| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (c) 2015 MediaTek Inc. |
| * Author: Zhanyong Wang <zhanyong.wang@mediatek.com> |
| * |
| */ |
| |
| #include <linux/module.h> |
| #include <linux/kernel.h> |
| #include <linux/init.h> |
| #include <linux/debugfs.h> |
| #include <linux/seq_file.h> |
| #include <linux/uaccess.h> |
| #include "xhci.h" |
| #include "xhci-mtk.h" |
| |
| struct xhci_usbxx_set { |
| unsigned int offset; |
| const char *name; |
| unsigned int width; |
| const char *comment; |
| }; |
| |
| #define XHCI_INIT_VALUE 0x0 |
| |
| /* |
| * Module name: ssusb_sifslv_ippc Base address: (+11203E00h) |
| * Address Name Width Register Function |
| */ |
| static struct xhci_usbxx_set ssusb_sifslv_ippc[] = { |
| { 0x00, "SSUSB_IP_PW_CTRL0", 32, "SSUSB IP Power/Clock Control Reg0" }, |
| { 0x04, "SSUSB_IP_PW_CTRL1", 32, "SSUSB IP Power/Clock Control Reg1" }, |
| { 0x08, "SSUSB_IP_PW_CTRL2", 32, "SSUSB IP Power/Clock Control Reg2" }, |
| { 0x0c, "SSUSB_IP_PW_CTRL3", 32, "SSUSB IP Power/Clock Control Reg3" }, |
| { 0x10, "SSUSB_IP_PW_STS1", 32, "SSUSB IP Power/Clock Status Reg1" }, |
| { 0x14, "SSUSB_IP_PW_STS2", 32, "SSUSB IP Power/Clock Status Reg2" }, |
| { 0x18, "SSUSB_OTG_STS", 32, "SSUSB OTG STATUS" }, |
| { 0x1c, "SSUSB_OTG_STS_CLR", 32, "SSUSB OTG STATUS CLEAR" }, |
| { 0x20, "SSUSB_IP_MAC_CAP", 32, "SSUSB IP MAC Capability Register" }, |
| { 0x24, "SSUSB_IP_XHCI_CAP", 32, "SSUSB IP xHCI Capability Register" }, |
| { 0x28, "SSUSB_IP_DEV_CAP", 32, "SSUSB IP Device Capability Reg" }, |
| { 0x2c, "SSUSB_OTG_INT_EN", 32, "SSUSB OTG INTERRUPT Enable" }, |
| { 0x30, "SSUSB_U3_CTRL_0P", 32, "SSUSB IP U3 Port 0 Control Reg" }, |
| { 0x38, "SSUSB_U3_CTRL_1P", 32, "SSUSB IP U3 Port 1 Control Reg" }, |
| { 0x40, "SSUSB_U3_CTRL_2P", 32, "SSUSB IP U3 Port 2 Control Reg" }, |
| { 0x48, "SSUSB_U3_CTRL_3P", 32, "SSUSB IP U3 Port 3 Control Reg" }, |
| { 0x50, "SSUSB_U2_CTRL_0P", 32, "SSUSB IP U2 Port 0 Control Reg" }, |
| { 0x58, "SSUSB_U2_CTRL_1P", 32, "SSUSB IP U2 Port 1 Control Reg" }, |
| { 0x60, "SSUSB_U2_CTRL_2P", 32, "SSUSB IP U2 Port 2 Control Reg" }, |
| { 0x68, "SSUSB_U2_CTRL_3P", 32, "SSUSB IP U2 Port 3 Control Reg" }, |
| { 0x70, "SSUSB_U2_CTRL_4P", 32, "SSUSB IP U2 Port 4 Control Reg" }, |
| { 0x78, "SSUSB_U2_CTRL_5P", 32, "SSUSB IP U2 Port 5 Control Reg" }, |
| { 0x7c, "SSUSB_U2_PHY_PLL", 32, "SSUSB U2 PHY PLL Control Register" }, |
| { 0x80, "SSUSB_DMA_CTRL", 32, "SSUSB DMA Control Register" }, |
| { 0x84, "SSUSB_MAC_CK_CTRL", 32, "SSUSB MAC Clock Control Register" }, |
| { 0x88, "SSUSB_CSR_CK_CTRL", 32, "" }, |
| { 0x8c, "SSUSB_REF_CK_CTRL", 32, "SSUSB Ref Clock Control Register" }, |
| { 0x90, "SSUSB_XHCI_CK_CTRL", 32, "SSUSB XHCI Clock Control Reg" }, |
| { 0x94, "SSUSB_XHCI_RST_CTRL", 32, "SSUSB XHCI Reset Control Reg" }, |
| { 0x98, "SSUSB_DEV_RST_CTRL", 32, "SSUSB Device Reset Control Reg" }, |
| { 0x9c, "SSUSB_SYS_CK_CTRL", 32, "SSUSB System Clock Control Reg" }, |
| { 0xa0, "SSUSB_HW_ID", 32, "SSUSB HW ID" }, |
| { 0xa4, "SSUSB_HW_SUB_ID", 32, "SSUSB HW SUB ID" }, |
| { 0xb0, "SSUSB_PRB_CTRL0", 32, "Probe Control Register 0" }, |
| { 0xb4, "SSUSB_PRB_CTRL1", 32, "Probe Control Register 1" }, |
| { 0xb8, "SSUSB_PRB_CTRL2", 32, "Probe Control Register 2" }, |
| { 0xbc, "SSUSB_PRB_CTRL3", 32, "Probe Control Register 3" }, |
| { 0xc0, "SSUSB_PRB_CTRL4", 32, "Probe Control Register 4" }, |
| { 0xc4, "SSUSB_PRB_CTRL5", 32, "Probe Control Register 5" }, |
| { 0xc8, "SSUSB_IP_SPARE0", 32, "SSUSB IP Spare Register0" }, |
| { 0xcc, "SSUSB_IP_SPARE1", 32, "SSUSB IP Spare Register1" }, |
| { 0xf8, "SSUSB_IP_SLV_TMOUT", 32, "SSUSB IP SLAVE TIMEOUT" }, |
| { 0, NULL, 0, NULL } |
| }; |
| |
| |
| /* |
| * Module name: ssusb2_xhci_exclude_port_csr Base address: (+11200000h) |
| * Address Name Width Register Function |
| */ |
| static struct xhci_usbxx_set ssusb2_xhci_exclude_port_csr[] = { |
| { 0x0000, "CAPLENGTH", 32, "Capability Register Length" }, |
| { 0x0004, "HCSPARAMS1", 32, "Structural Parameters 1" }, |
| { 0x0008, "HCSPARAMS2", 32, "Structural Parameters 2" }, |
| { 0x000c, "HCSPARAMS3", 32, "Structural Parameters 3" }, |
| { 0x0010, "HCCPARAMS", 32, "Capability Parameters" }, |
| { 0x0014, "DBSOFF", 32, "Doorbell Offset" }, |
| { 0x0018, "RTSOFF", 32, "Runtime Register Space Offset" }, |
| { 0x0020, "USBCMD", 32, "USB Command" }, |
| { 0x0024, "USBSTS", 32, "USB Status" }, |
| { 0x0028, "PAGESIZE", 32, "Page Size" }, |
| { 0x0034, "DNCTRL", 32, "Device Notification Control" }, |
| { 0x0038, "CRCR1", 32, "Command Ring Control 1" }, |
| { 0x003c, "CRCR2", 32, "Command Ring Control 2" }, |
| { 0x0050, "DCBAAP_LO", 32, "DCBA Array Pointer Lo" }, |
| { 0x0054, "DCBAAP_HI", 32, "DCBA Array Pointer High" }, |
| { 0x0058, "CONFIG", 32, "Configure" }, |
| { 0x0500, "SUPP_PTCL_REG1", 32, |
| "xHCI Supported Protocol Capability 1 Register" }, |
| { 0x0504, "SUPP_PTCL_REG2", 32, |
| "xHCI Supported Protocol Capability 2 Register" }, |
| { 0x0508, "SUPP_PTCL_REG3", 32, |
| "xHCI Supported Protocol Capability 3 Register" }, |
| { 0x0510, "SUPP_PTCL_REG4", 32, |
| "xHCI Supported Protocol Capability 4 Register" }, |
| { 0x0514, "SUPP_PTCL_REG5", 32, |
| "xHCI Supported Protocol Capability 5 Register" }, |
| { 0x0518, "SUPP_PTCL_REG6", 32, |
| "xHCI Supported Protocol Capability 6 Register" }, |
| { 0x0600, "MFINDEX", 32, "Microframe Index" }, |
| { 0x0620, "IMAN", 32, "Interrupter Management" }, |
| { 0x0624, "IMOD", 32, "Interrupter Moderation" }, |
| { 0x0628, "ERSTSZ", 32, "Event Ring Segment Table Size" }, |
| { 0x0630, "ERSTBA_LO", 32, "Event Ring Segment Table Base Addr Lo" }, |
| { 0x0634, "ERSTBA_HI", 32, "Event Ring Segment Table Base Addr Hi" }, |
| { 0x0638, "ERDP_LO", 32, "Event Ring Segment Table Base Addr Lo" }, |
| { 0x063c, "ERDP_HI", 32, "Event Ring Segment Table Base Addr Hi" }, |
| { 0x0800, "HOST_CMD_DB", 32, "Host Controller Doorbell Registers" }, |
| { 0x0804, "DEVICE1_DB", 32, "Device 1 Doorbell Registers" }, |
| { 0x0808, "DEVICE2_DB", 32, "Device 2 Doorbell Registers" }, |
| { 0x080c, "DEVICE3_DB", 32, "Device 3 Doorbell Registers" }, |
| { 0x0810, "DEVICE4_DB", 32, "Device 4 Doorbell Registers" }, |
| { 0x0814, "DEVICE5_DB", 32, "Device 5 Doorbell Registers" }, |
| { 0x0818, "DEVICE6_DB", 32, "Device 6 Doorbell Registers" }, |
| { 0x081c, "DEVICE7_DB", 32, "Device 7 Doorbell Registers" }, |
| { 0x0820, "DEVICE8_DB", 32, "Device 8 Doorbell Registers" }, |
| { 0x0824, "DEVICE9_DB", 32, "Device 9 Doorbell Registers" }, |
| { 0x0828, "DEVICE10_DB", 32, "Device 10 Doorbell Registers" }, |
| { 0x082c, "DEVICE11_DB", 32, "Device 11 Doorbell Registers" }, |
| { 0x0830, "DEVICE12_DB", 32, "Device 12 Doorbell Registers" }, |
| { 0x0834, "DEVICE13_DB", 32, "Device 13 Doorbell Registers" }, |
| { 0x0838, "DEVICE14_DB", 32, "Device 14 Doorbell Registers" }, |
| { 0x083c, "DEVICE15_DB", 32, "Device 15 Doorbell Registers" }, |
| { 0x0900, "HSRAM_DBGCTL", 32, "Host SRAM Debug Control Register" }, |
| { 0x0904, "HSRAM_DBGMODE", 32, "Host SRAM Debug Mode Register" }, |
| { 0x0908, "HSRAM_DBGSEL", 32, "Host SRAM Debug Select Register" }, |
| { 0x090c, "HSRAM_DBGADR", 32, "Host SRAM Debug Address Register" }, |
| { 0x0910, "HSRAM_DBGDR", 32, "Host SRAM Debug Data Register" }, |
| { 0x0920, "HSRAM_DELSEL_0", 32, "Host SRAM Delay Select 0" }, |
| { 0x0924, "HSRAM_DELSEL_1", 32, "Host SRAM Delay Select 1" }, |
| { 0x0930, "LS_EOF", 32, "Low Speed EOF Start Offset" }, |
| { 0x0934, "FS_EOF", 32, "Full Speed EOF Start Offset" }, |
| { 0x0938, "SYNC_HS_EOF", 32, "Sync High Speed EOF Start Offset" }, |
| { 0x093c, "SS_EOF", 32, "Super Speed EOF Start Offset" }, |
| { 0x0940, "SOF_OFFSET", 32, "SOF Offset" }, |
| { 0x0944, "HFCNTR_CFG", 32, "Host Frame Counter Configuration" }, |
| { 0x0948, "XACT3_CFG", 32, "Super Speed Transaction Configuration" }, |
| { 0x094c, "XACT2_CFG", 32, "USB2 Transaction Configuration" }, |
| { 0x0950, "HDMA_CFG", 32, "Host DMA Configuration" }, |
| { 0x0954, "ASYNC_HS_EOF", 32, "Async High Speed EOF Start Offset" }, |
| { 0x0958, "AXI_WR_DMA_CFG", 32, "AXI WR DMA configuration Reg." }, |
| { 0x095c, "AXI_RD_DMA_CFG", 32, "AXI RD DMA configuration Reg." }, |
| { 0x0960, "HSCH_CFG1", 32, "Host Scheduler Configuration Reg1" }, |
| { 0x0964, "CMD_CFG", 32, "Command Configuration" }, |
| { 0x0968, "EP_CFG", 32, "Endpoint Status Configuration" }, |
| { 0x096c, "EVT_CFG", 32, "Event Configuration" }, |
| { 0x0970, "TRBQ_CFG", 32, "TRBQ Configuration" }, |
| { 0x0974, "U3PORT_CFG", 32, "USB3 Port Configuration" }, |
| { 0x0978, "U2PORT_CFG", 32, "USB2 Port Configuration" }, |
| { 0x097c, "HSCH_CFG2", 32, "Host Scheduler Configuration Reg2" }, |
| { 0x0980, "SW_ERDY", 32, "Software ERDY" }, |
| { 0x09a0, "SLOT_EP_STS0", 32, "Slot and EP Resource Status0" }, |
| { 0x09a4, "SLOT_EP_STS1", 32, "Slot and EP Resource Status1" }, |
| { 0x09a8, "SLOT_EP_STS2", 32, "Slot and EP Resource Status2" }, |
| { 0x09b0, "RST_CTRL0", 32, "Host reset control Register 2" }, |
| { 0x09b4, "RST_CTRL1", 32, "Host reset control Register 3" }, |
| { 0x09f0, "SPARE0", 32, "Spare Register 0" }, |
| { 0x09f4, "SPARE1", 32, "Spare Register 1" }, |
| { 0, NULL, 0, NULL } |
| }; |
| /* |
| * Module name: ssusb_xhci_u3_port_csr Base address: (+11200420h) |
| * Address Name Width Register Function |
| */ |
| static struct xhci_usbxx_set ssusb_xhci_u3_port_csr[] = { |
| { 0x0420, "USB3_PORT_SC", 32, "USB3_PORT Port Status and Control" }, |
| { 0x0424, "USB3_PORT_PMSC", 32, "USB3_PORT PM Status and Control" }, |
| { 0x0428, "USB3_PORT_LI", 32, "USB3_PORT Link Info" }, |
| { 0, NULL, 0, NULL } |
| }; |
| |
| /* |
| * Module name: ssusb_xhci_u2_port_csr Base address: (+11200430h) |
| * Address Name Width Register Function |
| */ |
| static struct xhci_usbxx_set ssusb_xhci_u2_port_csr[] = { |
| { 0x0430, "USB2_PORT_SC", 32, "USB2_PORT Port Status and Control" }, |
| { 0x0434, "USB2_PORT_PMSC", 32, "USB2_PORT PM Status and Control" }, |
| { 0x0438, "USB2_PORT_LI", 32, "USB2_PORT Link Info" }, |
| { 0x043c, "USB2_PORT_HLPMC", 32, "USB2_PORT HW LPM Control Reg" }, |
| { 0, NULL, 0, NULL } |
| }; |
| |
| static inline unsigned int uffs(unsigned int x) |
| { |
| unsigned int r = 1; |
| |
| if (!x) |
| return 0; |
| |
| if (!(x & 0xffff)) { |
| x >>= 16; |
| r += 16; |
| } |
| if (!(x & 0xff)) { |
| x >>= 8; |
| r += 8; |
| } |
| if (!(x & 0xf)) { |
| x >>= 4; |
| r += 4; |
| } |
| if (!(x & 3)) { |
| x >>= 2; |
| r += 2; |
| } |
| if (!(x & 1)) { |
| x >>= 1; |
| r += 1; |
| } |
| |
| return r; |
| } |
| |
| #define IO_SET_FIELD(reg, field, val) \ |
| do { \ |
| unsigned int sb = uffs((unsigned int)field); \ |
| unsigned int tv = readl(reg); \ |
| if (sb) { \ |
| tv &= ~(field); \ |
| tv |= ((val) << (sb - 1)); \ |
| writel(tv, reg); \ |
| } \ |
| } while (0) |
| |
| #define IO_GET_FIELD(reg, field, val) \ |
| do { \ |
| unsigned int sb = uffs((unsigned int)field); \ |
| unsigned int tv = readl(reg); \ |
| if (sb) { \ |
| val = ((tv & (field)) >> (sb - 1)); \ |
| } else \ |
| val = tv; \ |
| } while (0) |
| |
| static void register_set_field(void __iomem *addr, unsigned int start_bit, |
| unsigned int len, unsigned int value) |
| { |
| unsigned long field; |
| |
| if (start_bit > 31 || len > 31 || (start_bit + len > 31)) |
| pr_info("[xhci][RMW] Invalid Register field range or length\n"); |
| else { |
| field = ((1 << len) - 1) << start_bit; |
| value &= (1 << len) - 1; |
| pr_info("[xhci][RMW]Original:0x%p (0x%x)\n", addr, readl(addr)); |
| IO_SET_FIELD(addr, field, value); |
| pr_info("[xhci][RMW]Modified:0x%p (0x%x)\n", addr, readl(addr)); |
| } |
| } |
| |
| static void register_get_field(void __iomem *address, unsigned int start_bit, |
| unsigned int len, unsigned int value) |
| { |
| unsigned long field; |
| |
| if (start_bit > 31 || len > 31 || (start_bit + len > 31)) |
| pr_info("[xhci][RMW]Invalid reg field range or length\n"); |
| else { |
| field = ((1 << len) - 1) << start_bit; |
| IO_GET_FIELD(address, field, value); |
| pr_info("[xhci][RMW]Reg:0x%p start_bit(%d)len(%d)(0x%x)\n", |
| address, start_bit, len, value); |
| } |
| } |
| |
| static int mtk_xhci_regdump_show(struct seq_file *s, void *unused) |
| { |
| int i; |
| int port; |
| int num_u3_ports = 1; |
| int num_u2_ports = 3; |
| struct xhci_usbxx_set *usbxxreg; |
| struct xhci_hcd *xhci = s->private; |
| struct usb_hcd *hcd = xhci_to_hcd(xhci); |
| struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); |
| void __iomem *iobase = xhci->main_hcd->regs; |
| void __iomem *ibase = mtk->ippc_regs; |
| void __iomem *portbase; |
| |
| seq_printf(s, |
| "\nMTK xHCI:(version:0x%X) (ippc_base: 0x%p, mac_base: 0x%p)\n", |
| HC_VERSION(readl(&xhci->cap_regs->hc_capbase)), ibase, iobase); |
| |
| if (!PTR_ERR(ibase)) |
| return 0; |
| |
| seq_puts(s, |
| "ssusb_sifslv_ippc Register Dump range [0x11203E00 ~ 0x11203F00)\n"); |
| for (i = 0; i < ARRAY_SIZE(ssusb_sifslv_ippc); i++) { |
| usbxxreg = &ssusb_sifslv_ippc[i]; |
| switch (usbxxreg->width) { |
| case 8: |
| seq_printf(s, "%-15s(0x%p): %02x\n", |
| usbxxreg->name, |
| ibase + usbxxreg->offset, |
| readb(ibase + usbxxreg->offset)); |
| break; |
| case 16: |
| seq_printf(s, "%-15s(0x%p): %04x\n", |
| usbxxreg->name, |
| ibase + usbxxreg->offset, |
| readw(ibase + usbxxreg->offset)); |
| break; |
| case 32: |
| seq_printf(s, "%-15s(0x%p): %08x\n", |
| usbxxreg->name, |
| ibase + usbxxreg->offset, |
| readl(ibase + usbxxreg->offset)); |
| break; |
| } |
| } |
| |
| if (!PTR_ERR(iobase)) |
| return 0; |
| |
| seq_puts(s, |
| "\nssusb_xhci_exclude_port_csr Reg range [0x11200000~0x11201000)\n"); |
| for (i = 0; i < ARRAY_SIZE(ssusb2_xhci_exclude_port_csr); i++) { |
| usbxxreg = &ssusb2_xhci_exclude_port_csr[i]; |
| switch (usbxxreg->width) { |
| case 8: |
| seq_printf(s, "%-15s(0x%p): 0x%02X\n", |
| usbxxreg->name, |
| iobase + usbxxreg->offset, |
| readb(iobase + usbxxreg->offset)); |
| break; |
| case 16: |
| seq_printf(s, "%-15s(0x%p): 0x%04X\n", |
| usbxxreg->name, |
| iobase + usbxxreg->offset, |
| readw(iobase + usbxxreg->offset)); |
| break; |
| case 32: |
| seq_printf(s, "%-15s(0x%p): 0x%08X\n", |
| usbxxreg->name, |
| iobase + usbxxreg->offset, |
| readl(iobase + usbxxreg->offset)); |
| break; |
| } |
| } |
| |
| seq_puts(s, |
| "\nssusb_xhci_u3_port_csr Regrange [0x11200420 ~ 0x11200430)\n"); |
| for (port = 0; port < num_u3_ports; port++) { |
| for (i = 0; i < ARRAY_SIZE(ssusb_xhci_u3_port_csr); i++) { |
| usbxxreg = &ssusb_xhci_u3_port_csr[i]; |
| portbase = iobase + usbxxreg->offset + 0x10*port; |
| switch (usbxxreg->width) { |
| case 8: |
| seq_printf(s, "%-15s(0x%p): 0x%02X\n", |
| usbxxreg->name, |
| portbase, |
| readb(portbase)); |
| break; |
| case 16: |
| seq_printf(s, "%-15s(0x%p): 0x%04X\n", |
| usbxxreg->name, |
| portbase, |
| readw(portbase)); |
| break; |
| case 32: |
| seq_printf(s, "%-15s(0x%p): 0x%08X\n", |
| usbxxreg->name, |
| portbase, |
| readl(portbase)); |
| break; |
| } |
| } |
| } |
| |
| seq_puts(s, |
| "\nssusb_xhci_u2_port_csr Regrange [0x11200430 ~ 0x11200440)\n"); |
| for (port = 0; port < num_u2_ports; port++) { |
| for (i = 0; i < ARRAY_SIZE(ssusb_xhci_u2_port_csr); i++) { |
| usbxxreg = &ssusb_xhci_u2_port_csr[i]; |
| portbase = iobase + usbxxreg->offset + 0x10*port; |
| switch (usbxxreg->width) { |
| case 8: |
| seq_printf(s, "%-15s(0x%p): 0x%02X\n", |
| usbxxreg->name, |
| portbase, |
| readb(portbase)); |
| break; |
| case 16: |
| seq_printf(s, "%-15s(0x%p): 0x%04X\n", |
| usbxxreg->name, |
| portbase, |
| readw(portbase)); |
| break; |
| case 32: |
| seq_printf(s, "%-15s(0x%p): 0x%08X\n", |
| usbxxreg->name, |
| portbase, |
| readl(portbase)); |
| break; |
| } |
| } |
| } |
| |
| |
| return 0; |
| } |
| |
| |
| static int mtk_xhci_regdump(struct inode *inode, struct file *file) |
| { |
| return single_open(file, mtk_xhci_regdump_show, inode->i_private); |
| } |
| |
| static const struct file_operations mtk_xhci_regdump_fops = { |
| .open = mtk_xhci_regdump, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = single_release, |
| }; |
| |
| static int mtk_xhci_debug_proc_show(struct seq_file *m, void *v) |
| { |
| seq_puts(m, "\n===mtk_xhci_debug help===\n"); |
| |
| seq_puts(m, "\n REGISTER control usage:\n"); |
| seq_puts(m, " write register: echo 1 0 [io] [val] > debug\n"); |
| seq_puts(m, " read register: echo 1 1 [io] > debug\n"); |
| seq_puts(m, " write mask: echo 1 2 [io] [bit] [len] [val] > debug\n"); |
| seq_puts(m, " read mask: echo 1 3 [io] [bit] [len] > debug\n"); |
| seq_puts(m, " dump all regiters echo 1 4 > debug\n"); |
| seq_puts(m, "=========================================\n\n"); |
| |
| return 0; |
| } |
| |
| static ssize_t mtk_xhci_debug_proc_write(struct file *file, |
| const char *buf, size_t count, loff_t *data) |
| { |
| int ret; |
| int cmd, p1, p3, p4, p5; |
| unsigned int reg_value; |
| int sscanf_num; |
| struct seq_file *s = file->private_data; |
| struct xhci_hcd *xhci = s->private; |
| struct usb_hcd *hcd = xhci_to_hcd(xhci); |
| struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); |
| void __iomem *iomem = NULL; |
| |
| unsigned int long long p2; |
| |
| p1 = p2 = p3 = p4 = p5 = -1; |
| |
| if (count == 0) |
| return -1; |
| |
| if (count > 255) |
| count = 255; |
| |
| ret = copy_from_user(mtk->cmd_buf, buf, count); |
| if (ret < 0) |
| return -1; |
| |
| mtk->cmd_buf[count] = '\0'; |
| pr_info("[xhci]debug received:%s\n", mtk->cmd_buf); |
| |
| sscanf_num = sscanf(mtk->cmd_buf, |
| "%x %x %llx %x %x %x", &cmd, &p1, &p2, &p3, &p4, &p5); |
| if (sscanf_num < 1) |
| return count; |
| |
| if (cmd == 0) |
| pr_info("[xhci] zone <0x%.8x> yet\n", p1); |
| else if (cmd == 1) { |
| iomem += p2; |
| if (p1 == 0) { |
| reg_value = p3; |
| pr_info("[xhci][Write]Original:0x%p (0x%08X)\n", |
| iomem, readl(iomem)); |
| |
| writel(reg_value, iomem); |
| |
| pr_info("[xhci][Write]Writed:0x%p (0x%08X)\n", |
| iomem, readl(iomem)); |
| } else if (p1 == 1) |
| pr_info("[xhci][Read]Register:0x%p (0x%08X)\n", |
| iomem, readl(iomem)); |
| else if (p1 == 2) |
| register_set_field(iomem, p3, p4, p5); |
| else if (p1 == 3) |
| register_get_field(iomem, p3, p4, p5); |
| else |
| pr_info("[xhci] todo\n"); |
| } |
| |
| return count; |
| } |
| |
| static int mtk_xhci_debug_proc_open(struct inode *inode, struct file *file) |
| { |
| return single_open(file, mtk_xhci_debug_proc_show, inode->i_private); |
| } |
| |
| static const struct file_operations mtk_xhci_debug_proc_fops = { |
| .open = mtk_xhci_debug_proc_open, |
| .write = mtk_xhci_debug_proc_write, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = single_release, |
| }; |
| |
| int mtk_xhci_init_debugfs(struct xhci_hcd *xhci) |
| { |
| int ret; |
| struct dentry *file; |
| struct dentry *root; |
| struct usb_hcd *hcd = xhci_to_hcd(xhci); |
| struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); |
| |
| root = debugfs_create_dir( |
| dev_driver_string(xhci_to_hcd(xhci)->self.controller), NULL); |
| if (!root) { |
| ret = -ENOMEM; |
| goto err0; |
| } |
| |
| file = debugfs_create_file("regdump", |
| 0444, root, xhci, &mtk_xhci_regdump_fops); |
| if (!file) { |
| ret = -ENOMEM; |
| goto err1; |
| } |
| |
| file = debugfs_create_file("debug", |
| 0644, root, xhci, &mtk_xhci_debug_proc_fops); |
| if (!file) { |
| ret = -ENOMEM; |
| goto err1; |
| } |
| |
| mtk->debugfs_root = root; |
| |
| return 0; |
| |
| err1: |
| debugfs_remove_recursive(root); |
| |
| err0: |
| return ret; |
| } |
| EXPORT_SYMBOL_GPL(mtk_xhci_init_debugfs); |
| |
| void mtk_xhci_exit_debugfs(struct xhci_hcd *xhci) |
| { |
| struct usb_hcd *hcd = xhci_to_hcd(xhci); |
| struct xhci_hcd_mtk *mtk = hcd_to_mtk(hcd); |
| |
| debugfs_remove_recursive(mtk->debugfs_root); |
| } |
| EXPORT_SYMBOL_GPL(mtk_xhci_exit_debugfs); |