// SPDX-License-Identifier: GPL-2.0 /* * xHCI host controller toolkit driver for vrt vref * * Copyright (C) 2021 MediaTek Inc. * * Author: Zhanyong Wang */ #include #include #include #include #include "xhci-mtk.h" #include "xhci-mtk-test.h" #include "xhci-mtk-unusual.h" static ssize_t RG_USB20_VRT_VREF_SEL_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_VRT_VREF_SEL usage:\n"); cnt += sprintf(buf + cnt, " echo u2p index 3b011 > RG_USB20_VRT_VREF_SEL\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 3b101 > RG_USB20_VRT_VREF_SEL\n"); cnt += sprintf(buf + cnt, " port2 binding phy 0, tune 3b'010 as VRT_VREF value\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_VRT_VREF_SEL_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_VRT_VREF_SEL(%lu): %s\n", n, buf); words = sscanf(buf, "%i %i 3b%3[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_USBPHYACR1; addr = ioremap_nocache(io, 4); val = binary_write_width3(addr, SHFT_RG_USB20_VRT_VREF_SEL, 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_VRT_VREF_SEL);