blob: 5041dbc39dc01963e3076a120ad04487b8c2daa9 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* xHCI host controller toolkit driver for pre-emphasic
*
* Copyright (C) 2021 MediaTek Inc.
*
* Author: Zhanyong Wang <zhanyong.wang@mediatek.com>
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/usb.h>
#include "xhci-mtk.h"
#include "xhci-mtk-test.h"
#include "xhci-mtk-unusual.h"
static ssize_t RG_USB20_PHY_REV_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
struct usb_hcd *hcd = mtk->hcd;
struct xhci_hcd *xhci = hcd_to_xhci(hcd);
struct device_node *node = dev->of_node;
ssize_t cnt = 0;
void __iomem *addr;
u32 val;
u32 i;
int ports;
char str[32];
int index = 0;
u32 io, length;
int ret;
ports = mtk->num_u3_ports + mtk->num_u2_ports;
cnt += sprintf(buf + cnt, " RG_USB20_PHY_REV usage:\n");
cnt += sprintf(buf + cnt,
" echo u2p index 2b00 > RG_USB20_PHY_REV\n");
if (mtk->num_u3_ports + 1 != ports)
cnt += sprintf(buf + cnt, " parameter: u2p: %i ~ %i\n",
mtk->num_u3_ports + 1, ports);
else
cnt += sprintf(buf + cnt, " parameter: u2p: %i\n",
mtk->num_u3_ports + 1);
if (mtk->num_u2_ports > 1)
cnt += sprintf(buf + cnt, " parameter: index: 0 ~ %i\n",
mtk->num_u2_ports);
else
cnt += sprintf(buf + cnt, " parameter: index: 0\n");
cnt += sprintf(buf + cnt, " e.g.: echo 2 0 2b10 > RG_USB20_PHY_REV\n");
cnt += sprintf(buf + cnt,
" port2 binding phy 0, enable 2b'10 as RG_USB20_PHY_REV\n");
cnt += sprintf(buf + cnt,
"\n=========current HQA setting check=========\n");
for (i = 1; i <= ports; i++) {
addr = &xhci->op_regs->port_status_base +
NUM_PORT_REGS * ((i - 1) & 0xff);
val = readl(addr);
if (i <= mtk->num_u3_ports) {
cnt += sprintf(buf + cnt,
"USB30 Port%i: 0x%08X\n", i, val);
} else {
cnt += sprintf(buf + cnt,
"USB20 Port%i: 0x%08X\n", i, val);
ret = query_phy_addr(node,
&index, &io, &length, PHY_TYPE_USB2);
if (ret && ret != -EACCES) {
if (ret == -EPERM)
cnt += sprintf(buf + cnt,
"USB20 Port%i (Phy%i: absent)\n",
i, index);
else
cnt += sprintf(buf + cnt,
"USB20 Port%i (Phy%i) failure %i\n",
i, index, ret);
continue;
}
cnt += sprintf(buf + cnt,
"USB20 Port%i (Phy%i:%sable): 0x%08X 0x%08X\n",
i, index, ret ? " dis" : " en", io, length);
addr = ioremap_nocache(io, length);
addr += (length != 0x100) ? 0x300 : 0;
HQA_INFORMACTION_COLLECTS();
iounmap(addr);
index ++;
}
}
if (mtk->hqa_pos) {
cnt += sprintf(buf + cnt, "%s", mtk->hqa_buf);
mtk->hqa_pos = 0;
}
return cnt;
}
static ssize_t RG_USB20_PHY_REV_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t n)
{
u32 val;
u32 io;
u32 length;
int ports;
int words;
int port;
int index;
int ret;
char *str = NULL;
void __iomem *addr;
struct xhci_hcd_mtk *mtk = dev_get_drvdata(dev);
struct device_node *node = dev->of_node;
ports = mtk->num_u3_ports + mtk->num_u2_ports;
mtk->hqa_pos = 0;
memset(mtk->hqa_buf, 0, mtk->hqa_size);
str = kzalloc(n, GFP_ATOMIC);
hqa_info(mtk, "RG_USB20_PHY_REV(%lu): %s\n", n, buf);
words = sscanf(buf, "%i %i 2b%2[0,1]", &port, &index, str);
if ((words != 3) ||
(port < mtk->num_u3_ports || port > ports)) {
hqa_info(mtk, "Check params(%i):\" %i %i %s\", Please!\n",
words, port, index, str);
ret = -EINVAL;
goto error;
}
hqa_info(mtk, " params: %i %i %s\n",
port, index, str);
ret = query_phy_addr(node, &index, &io, &length, PHY_TYPE_USB2);
if (ret && ret != -EACCES)
goto error;
io += (length != 0x100) ? 0x300 : 0;
io += USB20_PHY_USBPHYACR6;
addr = ioremap_nocache(io, 4);
val = binary_write_width2(addr, SHFT_RG_USB20_PHY_REV, str);
hqa_info(mtk, "Port%i(Phy%i)[0x%08X]: 0x%08X but 0x%08X\n",
port, index, io, val, readl(addr));
iounmap(addr);
ret = n;
error:
kfree(str);
return ret;
}
DEVICE_ATTR_RW(RG_USB20_PHY_REV);