/** * drivers/usb/host/sunxi_hci.c * (C) Copyright 2010-2015 * Allwinner Technology Co., Ltd. * yangnaitian, 2011-5-24, create this file * javen, 2011-7-18, add clock and power switch * * sunxi HCI Driver * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_AW_AXP) #include #endif #include "sunxi_hci.h" static u64 sunxi_hci_dmamask = DMA_BIT_MASK(64); static DEFINE_MUTEX(usb_passby_lock); static DEFINE_MUTEX(usb_vbus_lock); static DEFINE_MUTEX(usb_clock_lock); static DEFINE_MUTEX(usb_standby_lock); #ifndef CONFIG_OF static char *usbc_name[4] = {"usbc0", "usbc1", "usbc2", "usbc3"}; #endif static struct sunxi_hci_hcd sunxi_ohci0; static struct sunxi_hci_hcd sunxi_ohci1; static struct sunxi_hci_hcd sunxi_ohci2; static struct sunxi_hci_hcd sunxi_ohci3; static struct sunxi_hci_hcd sunxi_ehci0; static struct sunxi_hci_hcd sunxi_ehci1; static struct sunxi_hci_hcd sunxi_ehci2; static struct sunxi_hci_hcd sunxi_ehci3; static struct sunxi_hci_hcd sunxi_xhci; #define USBPHYC_REG_o_PHYCTL 0x0404 atomic_t usb1_set_vbus_cnt = ATOMIC_INIT(0); atomic_t usb2_set_vbus_cnt = ATOMIC_INIT(0); atomic_t usb3_set_vbus_cnt = ATOMIC_INIT(0); atomic_t usb4_set_vbus_cnt = ATOMIC_INIT(0); atomic_t usb1_enable_passly_cnt = ATOMIC_INIT(0); atomic_t usb2_enable_passly_cnt = ATOMIC_INIT(0); atomic_t usb3_enable_passly_cnt = ATOMIC_INIT(0); atomic_t usb4_enable_passly_cnt = ATOMIC_INIT(0); atomic_t usb_standby_cnt = ATOMIC_INIT(0); static s32 request_usb_regulator_io(struct sunxi_hci_hcd *sunxi_hci) { if (sunxi_hci->regulator_io != NULL) { sunxi_hci->regulator_io_hdle = regulator_get(NULL, sunxi_hci->regulator_io); if (IS_ERR(sunxi_hci->regulator_io_hdle)) { DMSG_PANIC("ERR: some error happen, %s,regulator_io_hdle fail to get regulator!", sunxi_hci->hci_name); sunxi_hci->regulator_io_hdle = NULL; return 0; } } if (sunxi_hci->hsic_flag) { if (sunxi_hci->hsic_regulator_io != NULL) { sunxi_hci->hsic_regulator_io_hdle = regulator_get(NULL, sunxi_hci->hsic_regulator_io); if (IS_ERR(sunxi_hci->hsic_regulator_io_hdle)) { DMSG_PANIC("ERR: some error happen, %s, hsic_regulator_io_hdle fail to get regulator!", sunxi_hci->hci_name); sunxi_hci->hsic_regulator_io_hdle = NULL; return 0; } } } return 0; } static s32 release_usb_regulator_io(struct sunxi_hci_hcd *sunxi_hci) { if (sunxi_hci->regulator_io != NULL) regulator_put(sunxi_hci->regulator_io_hdle); if (sunxi_hci->hsic_flag) { if (sunxi_hci->hsic_regulator_io != NULL) regulator_put(sunxi_hci->hsic_regulator_io_hdle); } return 0; } void __iomem *usb_phy_csr_add(struct sunxi_hci_hcd *sunxi_hci) { return (sunxi_hci->otg_vbase + SUNXI_OTG_PHY_CTRL); } void __iomem *usb_phy_csr_read(struct sunxi_hci_hcd *sunxi_hci) { switch (sunxi_hci->usbc_no) { case 0: return sunxi_hci->otg_vbase + SUNXI_OTG_PHY_STATUS; case 1: return sunxi_hci->usb_vbase + SUNXI_HCI_UTMI_PHY_STATUS; case 2: return sunxi_hci->usb_vbase + SUNXI_HCI_UTMI_PHY_STATUS; case 3: return sunxi_hci->usb_vbase + SUNXI_HCI_UTMI_PHY_STATUS; default: DMSG_PANIC("usb_phy_csr_read is failed in %d index\n", sunxi_hci->usbc_no); break; } return NULL; } void __iomem *usb_phy_csr_write(struct sunxi_hci_hcd *sunxi_hci) { switch (sunxi_hci->usbc_no) { case 0: return sunxi_hci->otg_vbase + SUNXI_OTG_PHY_CTRL; case 1: return sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL; case 2: return sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL; case 3: return sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL; default: DMSG_PANIC("usb_phy_csr_write is failed in %d index\n", sunxi_hci->usbc_no); break; } return NULL; } int usb_phyx_tp_write(struct sunxi_hci_hcd *sunxi_hci, int addr, int data, int len) { int temp = 0; int j = 0; int reg_value = 0; int reg_temp = 0; int dtmp = 0; if (sunxi_hci->otg_vbase == NULL) { DMSG_PANIC("%s,otg_vbase is null\n", __func__); return -1; } if (usb_phy_csr_add(sunxi_hci) == NULL) { DMSG_PANIC("%s,phy_csr_add is null\n", __func__); return -1; } if (usb_phy_csr_write(sunxi_hci) == NULL) { DMSG_PANIC("%s,phy_csr_write is null\n", __func__); return -1; } reg_value = USBC_Readl(sunxi_hci->otg_vbase + SUNXI_OTG_PHY_CFG); reg_temp = reg_value; reg_value |= 0x01; USBC_Writel(reg_value, (sunxi_hci->otg_vbase + SUNXI_OTG_PHY_CFG)); dtmp = data; for (j = 0; j < len; j++) { USBC_Writeb(addr + j, usb_phy_csr_add(sunxi_hci) + 1); temp = USBC_Readb(usb_phy_csr_write(sunxi_hci)); temp &= ~(0x1 << 0); USBC_Writeb(temp, usb_phy_csr_write(sunxi_hci)); temp = USBC_Readb(usb_phy_csr_add(sunxi_hci)); temp &= ~(0x1 << 7); temp |= (dtmp & 0x1) << 7; USBC_Writeb(temp, usb_phy_csr_add(sunxi_hci)); temp = USBC_Readb(usb_phy_csr_write(sunxi_hci)); temp |= (0x1 << 0); USBC_Writeb(temp, usb_phy_csr_write(sunxi_hci)); temp = USBC_Readb(usb_phy_csr_write(sunxi_hci)); temp &= ~(0x1 << 0); USBC_Writeb(temp, usb_phy_csr_write(sunxi_hci)); dtmp >>= 1; } USBC_Writel(reg_temp, (sunxi_hci->otg_vbase + SUNXI_OTG_PHY_CFG)); return 0; } EXPORT_SYMBOL(usb_phyx_tp_write); #if defined(CONFIG_ARCH_SUN8IW21) /*for new phy*/ static int usb_new_phyx_tp_write(struct sunxi_hci_hcd *sunxi_hci, int addr, int data, int len) { int temp = 0; int j = 0; int dtmp = 0; void __iomem *base; if (sunxi_hci->otg_vbase == NULL) { DMSG_PANIC("%s,otg_vbase is null\n", __func__); return -1; } if (usb_phy_csr_add(sunxi_hci) == NULL) { DMSG_PANIC("%s,phy_csr_add is null\n", __func__); return -1; } if (usb_phy_csr_write(sunxi_hci) == NULL) { DMSG_PANIC("%s,phy_csr_write is null\n", __func__); return -1; } /*device: 0x410(phy_ctl)*/ base = sunxi_hci->usb_vbase; dtmp = data; for (j = 0; j < len; j++) { temp = USBC_Readb(base + SUNXI_HCI_PHY_CTRL); temp |= (0x1 << 1); USBC_Writeb(temp, base + SUNXI_HCI_PHY_CTRL); USBC_Writeb(addr + j, base + SUNXI_HCI_PHY_CTRL + 1); temp = USBC_Readb(base + SUNXI_HCI_PHY_CTRL); temp &= ~(0x1 << 0); USBC_Writeb(temp, base + SUNXI_HCI_PHY_CTRL); temp = USBC_Readb(base + SUNXI_HCI_PHY_CTRL); temp &= ~(0x1 << 7); temp |= (dtmp & 0x1) << 7; USBC_Writeb(temp, base + SUNXI_HCI_PHY_CTRL); temp |= (0x1 << 0); USBC_Writeb(temp, base + SUNXI_HCI_PHY_CTRL); temp &= ~(0x1 << 0); USBC_Writeb(temp, base + SUNXI_HCI_PHY_CTRL); temp = USBC_Readb(base + SUNXI_HCI_PHY_CTRL); temp &= ~(0x1 << 1); USBC_Writeb(temp, base + SUNXI_HCI_PHY_CTRL); dtmp >>= 1; } return 0; } static int usb_new_phyx_tp_read(struct sunxi_hci_hcd *sunxi_hci, int addr, int len) { int temp = 0; int i = 0; int j = 0; int ret = 0; void __iomem *base; if (sunxi_hci->otg_vbase == NULL) { DMSG_PANIC("%s,otg_vbase is null\n", __func__); return -1; } if (usb_phy_csr_add(sunxi_hci) == NULL) { DMSG_PANIC("%s,phy_csr_add is null\n", __func__); return -1; } if (usb_phy_csr_read(sunxi_hci) == NULL) { DMSG_PANIC("%s,phy_csr_read is null\n", __func__); return -1; } base = sunxi_hci->usb_vbase; temp = USBC_Readb(base + SUNXI_HCI_PHY_CTRL); temp |= (0x1 << 1); USBC_Writeb(temp, base + SUNXI_HCI_PHY_CTRL); for (j = len; j > 0; j--) { USBC_Writeb((addr + j - 1), base + SUNXI_HCI_PHY_CTRL + 1); for (i = 0; i < 0x4; i++) ; temp = USBC_Readb(base + SUNXI_HCI_UTMI_PHY_STATUS); ret <<= 1; ret |= (temp & 0x1); } temp = USBC_Readb(base + SUNXI_HCI_PHY_CTRL); temp &= ~(0x1 << 1); USBC_Writeb(temp, base + SUNXI_HCI_PHY_CTRL); return ret; } void usb_new_phyx_write(struct sunxi_hci_hcd *sunxi_hci, u32 data) { u32 temp = 0, ptmp = 0, rtmp = 0; temp = data & PHY_RANGE_TRAN_MASK; temp >>= (2 + 4); ptmp = data & PHY_RANGE_PREE_MASK; ptmp >>= 4; rtmp = data & PHY_RANGE_RESI_MASK; /* tranceive data */ usb_new_phyx_tp_write(sunxi_hci, 0x60, temp, 0x4); DMSG_INFO("write to trancevie data: 0x%x\n", temp); usb_new_phyx_tp_write(sunxi_hci, 0x64, ptmp, 0x2); DMSG_INFO("write to preemphasis data: 0x%x\n", ptmp); usb_new_phyx_tp_write(sunxi_hci, 0x43, 0x0, 0x1); usb_new_phyx_tp_write(sunxi_hci, 0x41, 0x0, 0x1); usb_new_phyx_tp_write(sunxi_hci, 0x40, 0x0, 0x1); usb_new_phyx_tp_write(sunxi_hci, 0x44, rtmp, 0x4); DMSG_INFO("write to resistance data: 0x%x\n", rtmp); usb_new_phyx_tp_write(sunxi_hci, 0x43, 0x1, 0x1); } EXPORT_SYMBOL(usb_new_phyx_write); int usb_new_phyx_read(struct sunxi_hci_hcd *sunxi_hci) { u32 temp = 0, ptmp = 0, rtmp = 0, ret = 0; temp = usb_new_phyx_tp_read(sunxi_hci, 0x60, 0x4); ptmp = usb_new_phyx_tp_read(sunxi_hci, 0x64, 0x2); rtmp = usb_new_phyx_tp_read(sunxi_hci, 0x44, 0x4); DMSG_INFO("trancevie[9:6]:0x%x, preemphasis[5:4]:0x%x, resistance[3:0]:0x%x\n", temp, ptmp, rtmp); temp <<= (2 + 4); ptmp <<= 4; ret = temp | ptmp | rtmp; return ret; } EXPORT_SYMBOL(usb_new_phyx_read); static void usb_new_phy_init(struct sunxi_hci_hcd *sunxi_hci) { int value = 0; u32 efuse_val = 0; pr_debug("addr:%x,len:%x,value:%x\n", 0x03, 0x06, usb_new_phyx_tp_read(sunxi_hci, 0x03, 0x06)); //pll_prediv pr_debug("addr:%x,len:%x,value:%x\n", 0x16, 0x03, usb_new_phyx_tp_read(sunxi_hci, 0x16, 0x03)); //usbc_new_phyx_tp_write(regs, 0x1c, 0x07, 0x03); //pll_n pr_debug("addr:%x,len:%x,value:%x\n", 0x0b, 0x08, usb_new_phyx_tp_read(sunxi_hci, 0x0b, 0x08)); //usbc_new_phyx_tp_write(regs, 0x30, 0x0f, 0x0d); //pll_sts pr_debug("addr:%x,len:%x,value:%x\n", 0x09, 0x03, usb_new_phyx_tp_read(sunxi_hci, 0x09, 0x03)); sunxi_get_module_param_from_sid(&efuse_val, EFUSE_OFFSET, 4); pr_debug("efuse_val:0x%x\n", efuse_val); usb_new_phyx_tp_write(sunxi_hci, 0x03, 0x3f, 0x06); pr_debug("addr:%x,len:%x,value:%x\n", 0x03, 0x3f, usb_new_phyx_tp_read(sunxi_hci, 0x03, 0x06)); usb_new_phyx_tp_write(sunxi_hci, 0x1c, 0x03, 0x03); pr_debug("addr:%x,len:%x,value:%x\n", 0x1c, 0x03, usb_new_phyx_tp_read(sunxi_hci, 0x1c, 0x03)); if (efuse_val & SUNXI_HCI_PHY_EFUSE_ADJUST) { /* Already calibrate completely, don't have to distinguish iref mode and vref mode */ pr_debug("USB phy already calibrate completely\n"); switch (sunxi_hci->usbc_no) { case 0: value = (efuse_val & SUNXI_HCI_PHY_EFUSE_USB0TX) >> 9; usb_new_phyx_tp_write(sunxi_hci, 0x60, value, 0x04); break; default: pr_err("usb %d not exist!\n", sunxi_hci->usbc_no); break; } value = (efuse_val & SUNXI_HCI_PHY_EFUSE_RES) >> 5; usb_new_phyx_tp_write(sunxi_hci, 0x44, value, 0x04); pr_debug("addr:%x,len:%x,value:%x\n", 0x60, 0x04, usb_new_phyx_tp_read(sunxi_hci, 0x60, 0x04)); pr_debug("addr:%x,len:%x,value:%x\n", 0x44, 0x04, usb_new_phyx_tp_read(sunxi_hci, 0x44, 0x04)); } else { pr_debug("USB phy don't calibrate\n"); } pr_debug("addr:%x,len:%x,value:%x\n", 0x03, 0x06, usb_new_phyx_tp_read(sunxi_hci, 0x03, 0x06)); pr_debug("addr:%x,len:%x,value:%x\n", 0x16, 0x03, usb_new_phyx_tp_read(sunxi_hci, 0x16, 0x03)); pr_debug("addr:%x,len:%x,value:%x\n", 0x0b, 0x08, usb_new_phyx_tp_read(sunxi_hci, 0x0b, 0x08)); pr_debug("addr:%x,len:%x,value:%x\n", 0x09, 0x03, usb_new_phyx_tp_read(sunxi_hci, 0x09, 0x03)); } #endif int usb_phyx_write(struct sunxi_hci_hcd *sunxi_hci, int data) { int reg_value = 0; int temp = 0; int dtmp = 0; int ptmp = 0; temp = data; dtmp = data; ptmp = data; reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_PHY_TUNE); /*TXVREFTUNE + TXRISETUNE + TXPREEMPAMPTUNE + TXRESTUNE*/ reg_value &= ~((0xf << 8) | (0x3 << 4) | (0xf << 0)); temp &= ~((0xf << 4) | (0x3 << 8)); reg_value |= temp << 8; dtmp &= ~((0xf << 6) | (0xf << 0)); reg_value |= dtmp; data &= ~((0x3 << 4) | (0xf << 0)); reg_value |= data >> 6; USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_TUNE)); return 0; } EXPORT_SYMBOL(usb_phyx_write); int usb_phyx_read(struct sunxi_hci_hcd *sunxi_hci) { int reg_value = 0; int temp = 0; int ptmp = 0; int ret = 0; reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_PHY_TUNE); reg_value &= 0xf3f; ptmp = reg_value; temp = reg_value >> 8; ptmp &= ~((0xf << 8) | (0xf << 4)); ptmp <<= 6; reg_value &= ~((0xf << 8) | (0xf << 0)); ret = reg_value | ptmp | temp; DMSG_INFO("bit[3:0]VREF = 0x%x; bit[5:4]RISE = 0x%x; bit[7:6]PREEMPAMP = 0x%x; bit[9:8]RES = 0x%x\n", temp, reg_value >> 4, (ptmp >> 6) & 0x3, ((ptmp >> 6) & 0xc) >> 2); return ret; } EXPORT_SYMBOL(usb_phyx_read); int usb_phyx_tp_read(struct sunxi_hci_hcd *sunxi_hci, int addr, int len) { int temp = 0; int i = 0; int j = 0; int ret = 0; int reg_value = 0; int reg_temp = 0; if (sunxi_hci->otg_vbase == NULL) { DMSG_PANIC("%s,otg_vbase is null\n", __func__); return -1; } if (usb_phy_csr_add(sunxi_hci) == NULL) { DMSG_PANIC("%s,phy_csr_add is null\n", __func__); return -1; } if (usb_phy_csr_read(sunxi_hci) == NULL) { DMSG_PANIC("%s,phy_csr_read is null\n", __func__); return -1; } reg_value = USBC_Readl(sunxi_hci->otg_vbase + SUNXI_OTG_PHY_CFG); reg_temp = reg_value; reg_value |= 0x01; USBC_Writel(reg_value, (sunxi_hci->otg_vbase + SUNXI_OTG_PHY_CFG)); for (j = len; j > 0; j--) { USBC_Writeb((addr + j - 1), usb_phy_csr_add(sunxi_hci) + 1); for (i = 0; i < 0x4; i++) ; temp = USBC_Readb(usb_phy_csr_read(sunxi_hci)); ret <<= 1; ret |= (temp & 0x1); } USBC_Writel(reg_temp, (sunxi_hci->otg_vbase + SUNXI_OTG_PHY_CFG)); return ret; } EXPORT_SYMBOL(usb_phyx_tp_read); #if defined(CONFIG_ARCH_SUN8IW7) || defined(CONFIG_ARCH_SUN8IW12) \ || defined(CONFIG_ARCH_SUN8IW15) || defined(CONFIG_ARCH_SUN50IW3) \ || defined(CONFIG_ARCH_SUN50IW6) || defined(CONFIG_ARCH_SUN50IW9) \ || defined(CONFIG_ARCH_SUN8IW18) static void usb_hci_utmi_phy_tune(struct sunxi_hci_hcd *sunxi_hci, int mask, int offset, int val) { int reg_value = 0; reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_PHY_TUNE); reg_value &= ~mask; val = val << offset; val &= mask; reg_value |= val; USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_TUNE)); } #endif static void USBC_SelectPhyToHci(struct sunxi_hci_hcd *sunxi_hci) { int reg_value = 0; reg_value = USBC_Readl(sunxi_hci->otg_vbase + SUNXI_OTG_PHY_CFG); reg_value &= ~(0x01); USBC_Writel(reg_value, (sunxi_hci->otg_vbase + SUNXI_OTG_PHY_CFG)); } static void USBC_Clean_SIDDP(struct sunxi_hci_hcd *sunxi_hci) { int reg_value = 0; reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL); reg_value &= ~(0x01 << SUNXI_HCI_PHY_CTRL_SIDDQ); USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL)); } #if defined(CONFIG_ARCH_SUN50IW10) /*for common circuit*/ void sunxi_hci_common_set_rc_clk(struct sunxi_hci_hcd *sunxi_hci, int is_on) { int reg_value = 0; reg_value = USBC_Readl(sunxi_hci->usb_common_phy_config + SUNXI_USB_PMU_IRQ_ENABLE); if (is_on) reg_value |= 0x01 << SUNXI_HCI_RC16M_CLK_ENBALE; else reg_value &= ~(0x01 << SUNXI_HCI_RC16M_CLK_ENBALE); USBC_Writel(reg_value, (sunxi_hci->usb_common_phy_config + SUNXI_USB_PMU_IRQ_ENABLE)); } void sunxi_hci_common_switch_clk(struct sunxi_hci_hcd *sunxi_hci, int is_on) { int reg_value = 0; reg_value = USBC_Readl(sunxi_hci->usb_common_phy_config + SUNXI_USB_PMU_IRQ_ENABLE); if (is_on) reg_value |= 0x01 << 31; else reg_value &= ~(0x01 << 31); USBC_Writel(reg_value, (sunxi_hci->usb_common_phy_config + SUNXI_USB_PMU_IRQ_ENABLE)); } void sunxi_hci_common_set_rcgating(struct sunxi_hci_hcd *sunxi_hci, int is_on) { int reg_value = 0; reg_value = USBC_Readl(sunxi_hci->usb_common_phy_config + SUNXI_USB_PMU_IRQ_ENABLE); if (is_on) reg_value |= 0x01 << 3; else reg_value &= ~(0x01 << 3); USBC_Writel(reg_value, (sunxi_hci->usb_common_phy_config + SUNXI_USB_PMU_IRQ_ENABLE)); } void sunxi_hci_switch_clk(struct sunxi_hci_hcd *sunxi_hci, int is_on) { int val = 0; val = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_USB_PMU_IRQ_ENABLE); if (is_on) val |= 0x01 << 31; else val &= ~(0x01 << 31); USBC_Writel(val, (sunxi_hci->usb_vbase + SUNXI_USB_PMU_IRQ_ENABLE)); } void sunxi_hci_set_rcgating(struct sunxi_hci_hcd *sunxi_hci, int is_on) { int val = 0; val = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_USB_PMU_IRQ_ENABLE); if (is_on) val |= 0x01 << 3; else val &= ~(0x01 << 3); USBC_Writel(val, (sunxi_hci->usb_vbase + SUNXI_USB_PMU_IRQ_ENABLE)); } #endif void sunxi_hci_set_siddq(struct sunxi_hci_hcd *sunxi_hci, int is_on) { int reg_value = 0; reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL); if (is_on) reg_value |= 0x01 << SUNXI_HCI_PHY_CTRL_SIDDQ; else reg_value &= ~(0x01 << SUNXI_HCI_PHY_CTRL_SIDDQ); USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL)); } EXPORT_SYMBOL(sunxi_hci_set_siddq); void sunxi_hci_set_vc_cfg(struct sunxi_hci_hcd *sunxi_hci, int is_on) { int reg_value = 0; if (is_on) { /* must be set before host clk from ccu close */ /* com_tune bit[9] set to 1 when enter usb standby */ reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL); reg_value &= ~((0x01 << 7)|(0xff << 8)|(0x01 << 1)|(0x01 << 0)); /* clear phy vc cfg */ reg_value |= (0x01 << 7)|(0x39 << 8)|(0x01 << 1)|(0x0 << 0); /* bit[15:8] vc_addr */ USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL)); mdelay(10); reg_value &= ~((0x01 <<7)|(0xff << 8)|(0x01 << 1)|(0x01 << 0)); /* clear phy vc cfg */ reg_value |= (0x01 << 7)|(0x39 << 8)|(0x01 << 1)|(0x01 << 0); /* bit[15:8] vc_addr */ USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL)); /* com_tune bit[10] set to 1 when enter usb standby */ reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL); reg_value &= ~((0x01 << 7)|(0xff << 8)|(0x01 << 1)|(0x01 << 0)); /* clear phy vc cfg */ reg_value |= (0x01 << 7)|(0x3a << 8)|(0x01 << 1)|(0x0 << 0); /* bit[15:8] vc_addr */ USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL)); mdelay(10); reg_value &= ~((0x01 << 7)|(0xff << 8)|(0x01 << 1)|(0x01 << 0)); /* clear phy vc cfg */ reg_value |= (0x01 << 7)|(0x3a << 8)|(0x01 << 1)|(0x1 << 0); /* bit[15:8] vc_addr */ USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL)); reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL); reg_value &= ~(0x01 << 1); /* clear phy vc en */ USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL)); } else { /* cfg after switch to hclk from ccu */ /* com_tune bit[9] set to 0 when exit usb standby */ reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL); reg_value &= ~((0x01 << 7)|(0xff << 8)|(0x01 << 1)|(0x01 << 0)); /* clear phy vc cfg */ reg_value |= (0x0 << 7)|(0x39 << 8)|(0x01 << 1)|(0x0 << 0); /* bit[15:8] vc_addr */ USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL)); mdelay(10); reg_value &= ~((0x01 << 7)|(0xff << 8)|(0x01 << 1)|(0x01 << 0)); /* clear phy vc cfg */ reg_value |= (0x0 << 7)|(0x39 << 8)|(0x01 << 1)|(0x01 << 0); /* bit[15:8] vc_addr */ USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL)); /* com_tune bit[10] set to 0 when exit usb standby */ reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL); reg_value &= ~((0x01 << 7)|(0xff << 8)|(0x01 << 1)|(0x01 << 0)); /* clear phy vc cfg */ reg_value |= (0x0 << 7)|(0x3a << 8)|(0x01 << 1)|(0x0 << 0); /* bit[15:8] vc_addr */ USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL)); mdelay(10); reg_value &= ~((0x01 << 7)|(0xff << 8)|(0x01 << 1)|(0x01 << 0)); /* clear phy vc cfg */ reg_value |= (0x0 << 7)|(0x3a << 8)|(0x01 << 1)|(0x1 << 0); /* bit[15:8] vc_addr */ USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL)); reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL); reg_value &= ~(0x01 << 1); /* clear phy vc en */ USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_PHY_CTRL)); } } void sunxi_hci_set_clk(struct sunxi_hci_hcd *sunxi_hci, int is_on) { int reg_value = 0; if (is_on) { /* usb clk switch to rc16M */ reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_USB_CTRL); reg_value |= (0x01 << SUNXI_HCI_STANDBY_CLK_SEL); USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_USB_CTRL)); udelay(1); /* RC gen and rc clock ungated */ reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_USB_CTRL); reg_value |= ((0x01 << SUNXI_HCI_RC_CLK_GATING) | (0x01 << SUNXI_HCI_RC_GEN_ENABLE)); /* 0xc */ USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_USB_CTRL)); } else { /* RC disable and rc clock gated */ reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_USB_CTRL); reg_value &= ~((0x01 << SUNXI_HCI_RC_CLK_GATING) | (0x01 << SUNXI_HCI_RC_GEN_ENABLE)); /* 0xc */ USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_USB_CTRL)); udelay(1); /* usb clk switch to ccu clk */ reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_USB_CTRL); reg_value &= ~(0x01 << SUNXI_HCI_STANDBY_CLK_SEL); USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_USB_CTRL)); } } /* * Low-power mode USB standby helper functions. */ #ifdef SUNXI_USB_STANDBY_LOW_POW_MODE void sunxi_hci_set_wakeup_ctrl(struct sunxi_hci_hcd *sunxi_hci, int is_on) { int reg_value = 0; reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_HCI_CTRL_3); if (is_on) reg_value |= 0x01 << SUNXI_HCI_CTRL_3_REMOTE_WAKEUP; else reg_value &= ~(0x01 << SUNXI_HCI_CTRL_3_REMOTE_WAKEUP); USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_HCI_CTRL_3)); } void sunxi_hci_set_rc_clk(struct sunxi_hci_hcd *sunxi_hci, int is_on) { int reg_value = 0; reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_USB_PMU_IRQ_ENABLE); if (is_on) reg_value |= 0x01 << SUNXI_HCI_RC16M_CLK_ENBALE; else reg_value &= ~(0x01 << SUNXI_HCI_RC16M_CLK_ENBALE); USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_USB_PMU_IRQ_ENABLE)); } #if defined(CONFIG_ARCH_SUN50IW9) void sunxi_hci_set_standby_irq(struct sunxi_hci_hcd *sunxi_hci, int is_on) { int reg_value = 0; reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_USB_EHCI_TIME_INT); if (is_on) reg_value |= 0x01 << SUNXI_USB_EHCI_STANDBY_IRQ; else reg_value &= ~(0x01 << SUNXI_USB_EHCI_STANDBY_IRQ); USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_USB_EHCI_TIME_INT)); } #endif void sunxi_hci_clean_standby_irq(struct sunxi_hci_hcd *sunxi_hci) { int reg_value = 0; reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_USB_EHCI_TIME_INT); reg_value |= 0x01 << SUNXI_USB_EHCI_STANDBY_IRQ_STATUS; USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_USB_EHCI_TIME_INT)); } #endif static int open_clock(struct sunxi_hci_hcd *sunxi_hci, u32 ohci) { mutex_lock(&usb_clock_lock); /* * otg and hci share the same phy in fpga, * so need switch phy to hci here. * Notice: not need any more on new platforms. */ /* otg and hci0 Controller Shared phy in SUN50I */ if (sunxi_hci->usbc_no == HCI0_USBC_NO) USBC_SelectPhyToHci(sunxi_hci); #if defined(CONFIG_ARCH_SUN50IW11) if (sunxi_hci->usbc_no == HCI1_USBC_NO) USBC_SelectPhyToHci(sunxi_hci); #endif /* To fix hardware design issue. */ #if defined(CONFIG_ARCH_SUN8IW12) || defined(CONFIG_ARCH_SUN50IW3) \ || defined(CONFIG_ARCH_SUN50IW6) usb_hci_utmi_phy_tune(sunxi_hci, SUNXI_TX_RES_TUNE, SUNXI_TX_RES_TUNE_OFFSET, 0x3); usb_hci_utmi_phy_tune(sunxi_hci, SUNXI_TX_VREF_TUNE, SUNXI_TX_VREF_TUNE_OFFSET, 0xc); #endif #if defined(CONFIG_ARCH_SUN8IW18) usb_hci_utmi_phy_tune(sunxi_hci, SUNXI_TX_PREEMPAMP_TUNE, SUNXI_TX_PREEMPAMP_TUNE_OFFSET, 0x10); usb_hci_utmi_phy_tune(sunxi_hci, SUNXI_TX_VREF_TUNE, SUNXI_TX_VREF_TUNE_OFFSET, 0xc); #endif #if defined(CONFIG_ARCH_SUN8IW7) || defined(CONFIG_ARCH_SUN8IW15) usb_hci_utmi_phy_tune(sunxi_hci, SUNXI_TX_VREF_TUNE, SUNXI_TX_VREF_TUNE_OFFSET, 0x4); #endif #if defined(CONFIG_ARCH_SUN50IW9) usb_hci_utmi_phy_tune(sunxi_hci, SUNXI_TX_PREEMPAMP_TUNE, SUNXI_TX_PREEMPAMP_TUNE_OFFSET, 0x2); #endif if (sunxi_hci->ahb && sunxi_hci->mod_usbphy && !sunxi_hci->clk_is_open) { sunxi_hci->clk_is_open = 1; if (clk_prepare_enable(sunxi_hci->ahb)) DMSG_PANIC("ERR:try to prepare_enable %s_ahb failed!\n", sunxi_hci->hci_name); udelay(10); if (sunxi_hci->hsic_flag) { if (sunxi_hci->hsic_ctrl_flag) { if (sunxi_hci->hsic_enable_flag) { if (clk_prepare_enable(sunxi_hci->pll_hsic)) DMSG_PANIC("ERR:try to prepare_enable %s pll_hsic failed!\n", sunxi_hci->hci_name); if (clk_prepare_enable(sunxi_hci->clk_usbhsic12m)) DMSG_PANIC("ERR:try to prepare_enable %s clk_usbhsic12m failed!\n", sunxi_hci->hci_name); if (clk_prepare_enable(sunxi_hci->hsic_usbphy)) DMSG_PANIC("ERR:try to prepare_enable %s_hsic_usbphy failed!\n", sunxi_hci->hci_name); } } else { if (clk_prepare_enable(sunxi_hci->pll_hsic)) DMSG_PANIC("ERR:try to prepare_enable %s pll_hsic failed!\n", sunxi_hci->hci_name); if (clk_prepare_enable(sunxi_hci->clk_usbhsic12m)) DMSG_PANIC("ERR:try to prepare_enable %s clk_usbhsic12m failed!\n", sunxi_hci->hci_name); if (clk_prepare_enable(sunxi_hci->hsic_usbphy)) DMSG_PANIC("ERR:try to prepare_enable %s_hsic_usbphy failed!\n", sunxi_hci->hci_name); } } else { if (clk_prepare_enable(sunxi_hci->mod_usbphy)) DMSG_PANIC("ERR:try to prepare_enable %s_usbphy failed!\n", sunxi_hci->hci_name); #if defined(CONFIG_ARCH_SUN50IW10) if (sunxi_hci->ahb_usb1) clk_prepare_enable(sunxi_hci->ahb_usb1); if (sunxi_hci->mod_usbphy1) clk_prepare_enable(sunxi_hci->mod_usbphy1); #endif } udelay(10); } else { DMSG_PANIC("[%s]: wrn: open clock failed, (0x%p, 0x%p, %d, 0x%p)\n", sunxi_hci->hci_name, sunxi_hci->ahb, sunxi_hci->mod_usbphy, sunxi_hci->clk_is_open, sunxi_hci->mod_usb); } USBC_Clean_SIDDP(sunxi_hci); #if !defined(CONFIG_ARCH_SUN8IW12) && !defined(CONFIG_ARCH_SUN50IW3) \ && !defined(CONFIG_ARCH_SUN8IW6) && !defined(CONFIG_ARCH_SUN50IW6) \ && !defined(CONFIG_ARCH_SUN8IW15) && !defined(CONFIG_ARCH_SUN50IW8) \ && !defined(CONFIG_ARCH_SUN8IW18) && !defined(CONFIG_ARCH_SUN8IW16) \ && !defined(CONFIG_ARCH_SUN50IW9) && !defined(CONFIG_ARCH_SUN50IW10) \ && !defined(CONFIG_ARCH_SUN8IW19) && !defined(CONFIG_ARCH_SUN50IW11) \ && !defined(CONFIG_ARCH_SUN8IW21) usb_phyx_tp_write(sunxi_hci, 0x2a, 3, 2); #endif #if defined(CONFIG_ARCH_SUN8IW21) usb_new_phy_init(sunxi_hci); #endif mutex_unlock(&usb_clock_lock); return 0; } static int close_clock(struct sunxi_hci_hcd *sunxi_hci, u32 ohci) { if (sunxi_hci->ahb && sunxi_hci->mod_usbphy && sunxi_hci->clk_is_open) { sunxi_hci->clk_is_open = 0; if (sunxi_hci->hsic_flag) { if (sunxi_hci->hsic_ctrl_flag) { if (sunxi_hci->hsic_enable_flag) { clk_disable_unprepare(sunxi_hci->clk_usbhsic12m); clk_disable_unprepare(sunxi_hci->hsic_usbphy); clk_disable_unprepare(sunxi_hci->pll_hsic); } } else { clk_disable_unprepare(sunxi_hci->clk_usbhsic12m); clk_disable_unprepare(sunxi_hci->hsic_usbphy); clk_disable_unprepare(sunxi_hci->pll_hsic); } } else { clk_disable_unprepare(sunxi_hci->mod_usbphy); #if defined(CONFIG_ARCH_SUN50IW10) if (sunxi_hci->mod_usbphy1) clk_disable_unprepare(sunxi_hci->mod_usbphy1); if (sunxi_hci->ahb_usb1) clk_disable_unprepare(sunxi_hci->ahb_usb1); #endif } clk_disable_unprepare(sunxi_hci->ahb); udelay(10); } else { DMSG_PANIC("[%s]: wrn: open clock failed, (0x%p, 0x%p, %d, 0x%p)\n", sunxi_hci->hci_name, sunxi_hci->ahb, sunxi_hci->mod_usbphy, sunxi_hci->clk_is_open, sunxi_hci->mod_usb); } return 0; } static int usb_get_hsic_phy_ctrl(int value, int enable) { if (enable) { value |= (0x07<<8); value |= (0x01<<1); value |= (0x01<<0); value |= (0x01<<16); value |= (0x01<<20); } else { value &= ~(0x07<<8); value &= ~(0x01<<1); value &= ~(0x01<<0); value &= ~(0x01<<16); value &= ~(0x01<<20); } return value; } static void __usb_passby(struct sunxi_hci_hcd *sunxi_hci, u32 enable, atomic_t *usb_enable_passly_cnt) { unsigned long reg_value = 0; reg_value = USBC_Readl(sunxi_hci->usb_vbase + SUNXI_USB_PMU_IRQ_ENABLE); if (enable && (atomic_read(usb_enable_passly_cnt) == 0)) { if (sunxi_hci->hsic_flag) { reg_value = usb_get_hsic_phy_ctrl(reg_value, enable); } else { /* AHB Master interface INCR8 enable */ reg_value |= (1 << 10); /* AHB Master interface burst type INCR4 enable */ reg_value |= (1 << 9); /* AHB Master interface INCRX align enable */ reg_value |= (1 << 8); if (sunxi_hci->usbc_no == HCI0_USBC_NO) #ifdef SUNXI_USB_FPGA /* enable ULPI, disable UTMI */ reg_value |= (0 << 0); #else /* enable UTMI, disable ULPI */ reg_value |= (1 << 0); #endif else /* ULPI bypass enable */ reg_value |= (1 << 0); } } else if (!enable && (atomic_read(usb_enable_passly_cnt) == 1)) { if (sunxi_hci->hsic_flag) { reg_value = usb_get_hsic_phy_ctrl(reg_value, enable); } else { /* AHB Master interface INCR8 disable */ reg_value &= ~(1 << 10); /* AHB Master interface burst type INCR4 disable */ reg_value &= ~(1 << 9); /* AHB Master interface INCRX align disable */ reg_value &= ~(1 << 8); /* ULPI bypass disable */ reg_value &= ~(1 << 0); } } USBC_Writel(reg_value, (sunxi_hci->usb_vbase + SUNXI_USB_PMU_IRQ_ENABLE)); if (enable) atomic_add(1, usb_enable_passly_cnt); else atomic_sub(1, usb_enable_passly_cnt); } static void usb_passby(struct sunxi_hci_hcd *sunxi_hci, u32 enable) { spinlock_t lock; unsigned long flags = 0; mutex_lock(&usb_passby_lock); spin_lock_init(&lock); spin_lock_irqsave(&lock, flags); /* enable passby */ if (sunxi_hci->usbc_no == HCI0_USBC_NO) { __usb_passby(sunxi_hci, enable, &usb1_enable_passly_cnt); } else if (sunxi_hci->usbc_no == HCI1_USBC_NO) { __usb_passby(sunxi_hci, enable, &usb2_enable_passly_cnt); } else if (sunxi_hci->usbc_no == HCI2_USBC_NO) { __usb_passby(sunxi_hci, enable, &usb3_enable_passly_cnt); } else if (sunxi_hci->usbc_no == HCI3_USBC_NO) { __usb_passby(sunxi_hci, enable, &usb4_enable_passly_cnt); } else { DMSG_PANIC("EER: unknown usbc_no(%d)\n", sunxi_hci->usbc_no); spin_unlock_irqrestore(&lock, flags); mutex_unlock(&usb_passby_lock); return; } spin_unlock_irqrestore(&lock, flags); mutex_unlock(&usb_passby_lock); } static int alloc_pin(struct sunxi_hci_hcd *sunxi_hci) { u32 ret = 1; if (sunxi_hci->drv_vbus_gpio_valid) { ret = gpio_request(sunxi_hci->drv_vbus_gpio_set.gpio, NULL); if (ret != 0) { DMSG_PANIC("request %s gpio:%d\n", sunxi_hci->hci_name, sunxi_hci->drv_vbus_gpio_set.gpio); } else { gpio_direction_output(sunxi_hci->drv_vbus_gpio_set.gpio, 0); } } if (sunxi_hci->hsic_flag) { /* Marvell 4G HSIC ctrl */ if (sunxi_hci->usb_host_hsic_rdy_valid) { ret = gpio_request(sunxi_hci->usb_host_hsic_rdy.gpio, NULL); if (ret != 0) { DMSG_PANIC("ERR: gpio_request failed\n"); sunxi_hci->usb_host_hsic_rdy_valid = 0; } else { gpio_direction_output(sunxi_hci->usb_host_hsic_rdy.gpio, 0); } } /* SMSC usb3503 HSIC HUB ctrl */ if (sunxi_hci->usb_hsic_usb3503_flag) { if (sunxi_hci->usb_hsic_hub_connect_valid) { ret = gpio_request(sunxi_hci->usb_hsic_hub_connect.gpio, NULL); if (ret != 0) { DMSG_PANIC("ERR: gpio_request failed\n"); sunxi_hci->usb_hsic_hub_connect_valid = 0; } else { gpio_direction_output(sunxi_hci->usb_hsic_hub_connect.gpio, 1); } } if (sunxi_hci->usb_hsic_int_n_valid) { ret = gpio_request(sunxi_hci->usb_hsic_int_n.gpio, NULL); if (ret != 0) { DMSG_PANIC("ERR: gpio_request failed\n"); sunxi_hci->usb_hsic_int_n_valid = 0; } else { gpio_direction_output(sunxi_hci->usb_hsic_int_n.gpio, 1); } } msleep(20); if (sunxi_hci->usb_hsic_reset_n_valid) { ret = gpio_request(sunxi_hci->usb_hsic_reset_n.gpio, NULL); if (ret != 0) { DMSG_PANIC("ERR: gpio_request failed\n"); sunxi_hci->usb_hsic_reset_n_valid = 0; } else { gpio_direction_output(sunxi_hci->usb_hsic_reset_n.gpio, 1); } } /** * usb3503 device goto hub connect status * is need 100ms after reset */ msleep(100); } } return 0; } static void free_pin(struct sunxi_hci_hcd *sunxi_hci) { if (sunxi_hci->drv_vbus_gpio_valid) { gpio_free(sunxi_hci->drv_vbus_gpio_set.gpio); sunxi_hci->drv_vbus_gpio_valid = 0; } if (sunxi_hci->hsic_flag) { /* Marvell 4G HSIC ctrl */ if (sunxi_hci->usb_host_hsic_rdy_valid) { gpio_free(sunxi_hci->usb_host_hsic_rdy.gpio); sunxi_hci->usb_host_hsic_rdy_valid = 0; } /* SMSC usb3503 HSIC HUB ctrl */ if (sunxi_hci->usb_hsic_usb3503_flag) { if (sunxi_hci->usb_hsic_hub_connect_valid) { gpio_free(sunxi_hci->usb_hsic_hub_connect.gpio); sunxi_hci->usb_hsic_hub_connect_valid = 0; } if (sunxi_hci->usb_hsic_int_n_valid) { gpio_free(sunxi_hci->usb_hsic_int_n.gpio); sunxi_hci->usb_hsic_int_n_valid = 0; } if (sunxi_hci->usb_hsic_reset_n_valid) { gpio_free(sunxi_hci->usb_hsic_reset_n.gpio); sunxi_hci->usb_hsic_reset_n_valid = 0; } } } } void sunxi_set_host_hisc_rdy(struct sunxi_hci_hcd *sunxi_hci, int is_on) { if (sunxi_hci->usb_host_hsic_rdy_valid) { /* set config, output */ gpio_direction_output(sunxi_hci->usb_host_hsic_rdy.gpio, is_on); } } EXPORT_SYMBOL(sunxi_set_host_hisc_rdy); void sunxi_set_host_vbus(struct sunxi_hci_hcd *sunxi_hci, int is_on) { #if defined(CONFIG_SUNXI_REGULATOR_DT) int ret; if (sunxi_hci->supply) { if (is_on) { ret = regulator_enable(sunxi_hci->supply); if (ret) DMSG_PANIC("ERR: %s regulator enable failed\n", sunxi_hci->hci_name); } else { ret = regulator_disable(sunxi_hci->supply); if (ret) DMSG_PANIC("ERR: %s regulator force disable failed\n", sunxi_hci->hci_name); } } #else if (sunxi_hci->drv_vbus_type == USB_DRV_VBUS_TYPE_GIPO) { if (sunxi_hci->drv_vbus_gpio_valid) __gpio_set_value(sunxi_hci->drv_vbus_gpio_set.gpio, is_on); } else if (sunxi_hci->drv_vbus_type == USB_DRV_VBUS_TYPE_AXP) { #if defined(CONFIG_AW_AXP) axp_usb_vbus_output(is_on); #endif } #endif } EXPORT_SYMBOL(sunxi_set_host_vbus); static void __sunxi_set_vbus(struct sunxi_hci_hcd *sunxi_hci, int is_on) { #if defined(CONFIG_SUNXI_REGULATOR_DT) int ret; #endif /* set power flag */ sunxi_hci->power_flag = is_on; if ((sunxi_hci->regulator_io != NULL) && (sunxi_hci->regulator_io_hdle != NULL)) { if (is_on) { if (regulator_enable(sunxi_hci->regulator_io_hdle) < 0) DMSG_INFO("%s: regulator_enable fail\n", sunxi_hci->hci_name); } else { if (regulator_disable(sunxi_hci->regulator_io_hdle) < 0) DMSG_INFO("%s: regulator_disable fail\n", sunxi_hci->hci_name); } } if (sunxi_hci->hsic_flag) { if ((sunxi_hci->hsic_regulator_io != NULL) && (sunxi_hci->hsic_regulator_io_hdle != NULL)) { if (is_on) { if (regulator_enable(sunxi_hci->hsic_regulator_io_hdle) < 0) DMSG_INFO("%s: hsic_regulator_enable fail\n", sunxi_hci->hci_name); } else { if (regulator_disable(sunxi_hci->hsic_regulator_io_hdle) < 0) DMSG_INFO("%s: hsic_regulator_disable fail\n", sunxi_hci->hci_name); } } } #if defined(CONFIG_SUNXI_REGULATOR_DT) if (sunxi_hci->supply) { if (is_on) { ret = regulator_enable(sunxi_hci->supply); if (ret) DMSG_PANIC("ERR: %s regulator enable failed\n", sunxi_hci->hci_name); } else { ret = regulator_disable(sunxi_hci->supply); if (ret) DMSG_PANIC("ERR: %s regulator force disable failed\n", sunxi_hci->hci_name); } } #else if (sunxi_hci->drv_vbus_type == USB_DRV_VBUS_TYPE_GIPO) { if (sunxi_hci->drv_vbus_gpio_valid) __gpio_set_value(sunxi_hci->drv_vbus_gpio_set.gpio, is_on); } else if (sunxi_hci->drv_vbus_type == USB_DRV_VBUS_TYPE_AXP) { #if defined(CONFIG_AW_AXP) axp_usb_vbus_output(is_on); #endif } #endif } static void sunxi_set_vbus(struct sunxi_hci_hcd *sunxi_hci, int is_on) { DMSG_DEBUG("[%s]: sunxi_set_vbus cnt %d\n", sunxi_hci->hci_name, (sunxi_hci->usbc_no == 1) ? atomic_read(&usb1_set_vbus_cnt) : atomic_read(&usb2_set_vbus_cnt)); mutex_lock(&usb_vbus_lock); if (sunxi_hci->usbc_no == HCI0_USBC_NO) { if (is_on && (atomic_read(&usb1_set_vbus_cnt) == 0)) __sunxi_set_vbus(sunxi_hci, is_on); /* power on */ else if (!is_on && atomic_read(&usb1_set_vbus_cnt) == 1) __sunxi_set_vbus(sunxi_hci, is_on); /* power off */ if (is_on) atomic_add(1, &usb1_set_vbus_cnt); else atomic_sub(1, &usb1_set_vbus_cnt); } else if (sunxi_hci->usbc_no == HCI1_USBC_NO) { if (is_on && (atomic_read(&usb2_set_vbus_cnt) == 0)) __sunxi_set_vbus(sunxi_hci, is_on); /* power on */ else if (!is_on && atomic_read(&usb2_set_vbus_cnt) == 1) __sunxi_set_vbus(sunxi_hci, is_on); /* power off */ if (is_on) atomic_add(1, &usb2_set_vbus_cnt); else atomic_sub(1, &usb2_set_vbus_cnt); } else if (sunxi_hci->usbc_no == HCI2_USBC_NO) { if (is_on && (atomic_read(&usb3_set_vbus_cnt) == 0)) __sunxi_set_vbus(sunxi_hci, is_on); /* power on */ else if (!is_on && atomic_read(&usb3_set_vbus_cnt) == 1) __sunxi_set_vbus(sunxi_hci, is_on); /* power off */ if (is_on) atomic_add(1, &usb3_set_vbus_cnt); else atomic_sub(1, &usb3_set_vbus_cnt); } else if (sunxi_hci->usbc_no == HCI3_USBC_NO) { if (is_on && (atomic_read(&usb4_set_vbus_cnt) == 0)) __sunxi_set_vbus(sunxi_hci, is_on); /* power on */ else if (!is_on && atomic_read(&usb4_set_vbus_cnt) == 1) __sunxi_set_vbus(sunxi_hci, is_on); /* power off */ if (is_on) atomic_add(1, &usb4_set_vbus_cnt); else atomic_sub(1, &usb4_set_vbus_cnt); } else { DMSG_INFO("[%s]: sunxi_set_vbus no: %d\n", sunxi_hci->hci_name, sunxi_hci->usbc_no); } mutex_unlock(&usb_vbus_lock); } static int sunxi_get_hci_base(struct platform_device *pdev, struct sunxi_hci_hcd *sunxi_hci) { struct device_node *np = pdev->dev.of_node; struct resource res; int ret = 0; sunxi_hci->usb_vbase = of_iomap(np, 0); if (sunxi_hci->usb_vbase == NULL) { dev_err(&pdev->dev, "%s, can't get vbase resource\n", sunxi_hci->hci_name); return -EINVAL; } sunxi_hci->otg_vbase = of_iomap(np, 2); if (sunxi_hci->otg_vbase == NULL) { dev_err(&pdev->dev, "%s, can't get otg_vbase resource\n", sunxi_hci->hci_name); return -EINVAL; } #if defined(CONFIG_ARCH_SUN50IW10) sunxi_hci->prcm = of_iomap(np, 3); if (sunxi_hci->prcm == NULL) { dev_err(&pdev->dev, "%s, can't get prcm resource\n", sunxi_hci->hci_name); return -EINVAL; } if (sunxi_hci->usbc_no == HCI0_USBC_NO) { sunxi_hci->usb_common_phy_config = of_iomap(np, 4); if (sunxi_hci->usb_common_phy_config == NULL) { dev_err(&pdev->dev, "%s, can't get common phy resource\n", sunxi_hci->hci_name); return -EINVAL; } } #endif ret = of_address_to_resource(np, 0, &res); if (ret) dev_err(&pdev->dev, "could not get regs\n"); sunxi_hci->usb_base_res = &res; return 0; } static int sunxi_get_ohci_clock_src(struct platform_device *pdev, struct sunxi_hci_hcd *sunxi_hci) { struct device_node *np = pdev->dev.of_node; sunxi_hci->clk_usbohci12m = of_clk_get(np, 2); if (IS_ERR(sunxi_hci->clk_usbohci12m)) { sunxi_hci->clk_usbohci12m = NULL; DMSG_INFO("%s get usb clk_usbohci12m clk failed.\n", sunxi_hci->hci_name); } sunxi_hci->clk_hoscx2 = of_clk_get(np, 3); if (IS_ERR(sunxi_hci->clk_hoscx2)) { sunxi_hci->clk_hoscx2 = NULL; DMSG_INFO("%s get usb clk_hoscx2 clk failed.\n", sunxi_hci->hci_name); } sunxi_hci->clk_hosc = of_clk_get(np, 4); if (IS_ERR(sunxi_hci->clk_hosc)) { sunxi_hci->clk_hosc = NULL; DMSG_INFO("%s get usb clk_hosc failed.\n", sunxi_hci->hci_name); } sunxi_hci->clk_losc = of_clk_get(np, 5); if (IS_ERR(sunxi_hci->clk_losc)) { sunxi_hci->clk_losc = NULL; DMSG_INFO("%s get usb clk_losc clk failed.\n", sunxi_hci->hci_name); } return 0; } static int sunxi_get_hci_clock(struct platform_device *pdev, struct sunxi_hci_hcd *sunxi_hci) { struct device_node *np = pdev->dev.of_node; sunxi_hci->ahb = of_clk_get(np, 1); if (IS_ERR(sunxi_hci->ahb)) { sunxi_hci->ahb = NULL; DMSG_PANIC("ERR: %s get usb ahb_otg clk failed.\n", sunxi_hci->hci_name); return -EINVAL; } sunxi_hci->mod_usbphy = of_clk_get(np, 0); if (IS_ERR(sunxi_hci->mod_usbphy)) { sunxi_hci->mod_usbphy = NULL; DMSG_PANIC("ERR: %s get usb mod_usbphy failed.\n", sunxi_hci->hci_name); return -EINVAL; } if (sunxi_hci->hsic_flag) { sunxi_hci->hsic_usbphy = of_clk_get(np, 2); if (IS_ERR(sunxi_hci->hsic_usbphy)) { sunxi_hci->hsic_usbphy = NULL; DMSG_PANIC("ERR: %s get usb hsic_usbphy failed.\n", sunxi_hci->hci_name); } sunxi_hci->clk_usbhsic12m = of_clk_get(np, 3); if (IS_ERR(sunxi_hci->clk_usbhsic12m)) { sunxi_hci->clk_usbhsic12m = NULL; DMSG_PANIC("ERR: %s get usb clk_usbhsic12m failed.\n", sunxi_hci->hci_name); } sunxi_hci->pll_hsic = of_clk_get(np, 4); if (IS_ERR(sunxi_hci->pll_hsic)) { sunxi_hci->pll_hsic = NULL; DMSG_PANIC("ERR: %s get usb pll_hsic failed.\n", sunxi_hci->hci_name); } } #if defined(CONFIG_ARCH_SUN50IW10) if (!sunxi_hci->hsic_flag && sunxi_hci->usbc_no == HCI0_USBC_NO) { sunxi_hci->ahb_usb1 = of_clk_get(np, 2); if (IS_ERR(sunxi_hci->ahb_usb1)) { sunxi_hci->ahb_usb1 = NULL; DMSG_PANIC("ERR: %s get usb ahb_usb1 failed.\n", sunxi_hci->hci_name); return -EINVAL; } sunxi_hci->mod_usbphy1 = of_clk_get(np, 3); if (IS_ERR(sunxi_hci->mod_usbphy1)) { sunxi_hci->mod_usbphy1 = NULL; DMSG_PANIC("ERR: %s get usb mod_usbphy1 failed.\n", sunxi_hci->hci_name); return -EINVAL; } } #endif return 0; } static int get_usb_cfg(struct platform_device *pdev, struct sunxi_hci_hcd *sunxi_hci) { struct device_node *usbc_np = NULL; char np_name[10]; int ret = -1; sprintf(np_name, "usbc%d", sunxi_get_hci_num(pdev)); usbc_np = of_find_node_by_type(NULL, np_name); /* usbc enable */ ret = of_property_read_string(usbc_np, "status", &sunxi_hci->used_status); if (ret) { DMSG_PRINT("get %s used is fail, %d\n", sunxi_hci->hci_name, -ret); sunxi_hci->used = 0; } else if (!strcmp(sunxi_hci->used_status, "okay")) { sunxi_hci->used = 1; } else { sunxi_hci->used = 0; } /* usbc port type */ if (sunxi_hci->usbc_no == HCI0_USBC_NO) { ret = of_property_read_u32(usbc_np, KEY_USB_PORT_TYPE, &sunxi_hci->port_type); if (ret) DMSG_INFO("get usb_port_type is fail, %d\n", -ret); } sunxi_hci->hsic_flag = 0; if (sunxi_hci->usbc_no == HCI1_USBC_NO) { ret = of_property_read_u32(usbc_np, KEY_USB_HSIC_USBED, &sunxi_hci->hsic_flag); if (ret) sunxi_hci->hsic_flag = 0; if (sunxi_hci->hsic_flag) { if (!strncmp(sunxi_hci->hci_name, "ohci", strlen("ohci"))) { DMSG_PRINT("HSIC is no susport in %s, and to return\n", sunxi_hci->hci_name); sunxi_hci->used = 0; return 0; } /* hsic regulator_io */ ret = of_property_read_string(usbc_np, KEY_USB_HSIC_REGULATOR_IO, &sunxi_hci->hsic_regulator_io); if (ret) { DMSG_PRINT("get %s, hsic_regulator_io is fail, %d\n", sunxi_hci->hci_name, -ret); sunxi_hci->hsic_regulator_io = NULL; } else { if (!strcmp(sunxi_hci->hsic_regulator_io, "nocare")) { DMSG_PRINT("get %s, hsic_regulator_io is no nocare\n", sunxi_hci->hci_name); sunxi_hci->hsic_regulator_io = NULL; } } /* Marvell 4G HSIC ctrl */ ret = of_property_read_u32(usbc_np, KEY_USB_HSIC_CTRL, &sunxi_hci->hsic_ctrl_flag); if (ret) { DMSG_PRINT("get %s usb_hsic_ctrl is fail, %d\n", sunxi_hci->hci_name, -ret); sunxi_hci->hsic_ctrl_flag = 0; } if (sunxi_hci->hsic_ctrl_flag) { sunxi_hci->usb_host_hsic_rdy.gpio = of_get_named_gpio(usbc_np, KEY_USB_HSIC_RDY_GPIO, 0); if (gpio_is_valid(sunxi_hci->usb_host_hsic_rdy.gpio)) { sunxi_hci->usb_host_hsic_rdy_valid = 1; } else { sunxi_hci->usb_host_hsic_rdy_valid = 0; DMSG_PRINT("get %s drv_vbus_gpio is fail\n", sunxi_hci->hci_name); } } else { sunxi_hci->usb_host_hsic_rdy_valid = 0; } /* SMSC usb3503 HSIC HUB ctrl */ ret = of_property_read_u32(usbc_np, "usb_hsic_usb3503_flag", &sunxi_hci->usb_hsic_usb3503_flag); if (ret) { DMSG_PRINT("get %s usb_hsic_usb3503_flag is fail, %d\n", sunxi_hci->hci_name, -ret); sunxi_hci->usb_hsic_usb3503_flag = 0; } if (sunxi_hci->usb_hsic_usb3503_flag) { sunxi_hci->usb_hsic_hub_connect.gpio = of_get_named_gpio(usbc_np, "usb_hsic_hub_connect_gpio", 0); if (gpio_is_valid(sunxi_hci->usb_hsic_hub_connect.gpio)) { sunxi_hci->usb_hsic_hub_connect_valid = 1; } else { sunxi_hci->usb_hsic_hub_connect_valid = 0; DMSG_PRINT("get %s usb_hsic_hub_connect is fail\n", sunxi_hci->hci_name); } sunxi_hci->usb_hsic_int_n.gpio = of_get_named_gpio(usbc_np, "usb_hsic_int_n_gpio", 0); if (gpio_is_valid(sunxi_hci->usb_hsic_int_n.gpio)) { sunxi_hci->usb_hsic_int_n_valid = 1; } else { sunxi_hci->usb_hsic_int_n_valid = 0; DMSG_PRINT("get %s usb_hsic_int_n is fail\n", sunxi_hci->hci_name); } sunxi_hci->usb_hsic_reset_n.gpio = of_get_named_gpio(usbc_np, "usb_hsic_reset_n_gpio", 0); if (gpio_is_valid(sunxi_hci->usb_hsic_reset_n.gpio)) { sunxi_hci->usb_hsic_reset_n_valid = 1; } else { sunxi_hci->usb_hsic_reset_n_valid = 0; DMSG_PRINT("get %s usb_hsic_reset_n is fail\n", sunxi_hci->hci_name); } } else { sunxi_hci->usb_hsic_hub_connect_valid = 0; sunxi_hci->usb_hsic_int_n_valid = 0; sunxi_hci->usb_hsic_reset_n_valid = 0; } } else { sunxi_hci->hsic_ctrl_flag = 0; sunxi_hci->usb_host_hsic_rdy_valid = 0; sunxi_hci->usb_hsic_hub_connect_valid = 0; sunxi_hci->usb_hsic_int_n_valid = 0; sunxi_hci->usb_hsic_reset_n_valid = 0; } } /* usbc wakeup_suspend */ ret = of_property_read_u32(usbc_np, KEY_USB_WAKEUP_SUSPEND, &sunxi_hci->wakeup_suspend); if (ret) { DMSG_PRINT("get %s wakeup_suspend is fail, %d\n", sunxi_hci->hci_name, -ret); } #if !defined(CONFIG_SUNXI_REGULATOR_DT) /* usbc drv_vbus */ ret = of_property_read_string(usbc_np, KEY_USB_DRVVBUS_GPIO, &sunxi_hci->drv_vbus_name); if (ret) { DMSG_PRINT("get drv_vbus is fail, %d\n", -ret); sunxi_hci->drv_vbus_gpio_valid = 0; } else { if (strncmp(sunxi_hci->drv_vbus_name, "axp_ctrl", 8) == 0) { sunxi_hci->drv_vbus_type = USB_DRV_VBUS_TYPE_AXP; sunxi_hci->drv_vbus_gpio_valid = 0; } else { /* get drv vbus gpio */ sunxi_hci->drv_vbus_gpio_set.gpio = of_get_named_gpio(usbc_np, KEY_USB_DRVVBUS_GPIO, 0); if (gpio_is_valid(sunxi_hci->drv_vbus_gpio_set.gpio)) { sunxi_hci->drv_vbus_gpio_valid = 1; sunxi_hci->drv_vbus_type = USB_DRV_VBUS_TYPE_GIPO; } else { sunxi_hci->drv_vbus_gpio_valid = 0; } } } #endif /* usbc regulator_io */ ret = of_property_read_string(usbc_np, KEY_USB_REGULATOR_IO, &sunxi_hci->regulator_io); if (ret) { DMSG_PRINT("get %s, regulator_io is fail, %d\n", sunxi_hci->hci_name, -ret); sunxi_hci->regulator_io = NULL; } else { if (!strcmp(sunxi_hci->regulator_io, "nocare")) { DMSG_PRINT("get %s, regulator_io is no nocare\n", sunxi_hci->hci_name); sunxi_hci->regulator_io = NULL; } else { DMSG_PRINT("get %s, regulator_io is %s.\n", sunxi_hci->hci_name, sunxi_hci->regulator_io); } } /* wakeup-source */ if (of_property_read_bool(usbc_np, KEY_WAKEUP_SOURCE)) { sunxi_hci->wakeup_source_flag = 1; } else { DMSG_PRINT("get %s wakeup-source is fail.\n", sunxi_hci->hci_name); sunxi_hci->wakeup_source_flag = 0; } return 0; } int sunxi_get_hci_num(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; int ret = 0; int hci_num = 0; ret = of_property_read_u32(np, HCI_USBC_NO, &hci_num); if (ret) DMSG_PANIC("get hci_ctrl_num is fail, %d\n", -ret); return hci_num; } static int sunxi_get_hci_name(struct platform_device *pdev, struct sunxi_hci_hcd *sunxi_hci) { struct device_node *np = pdev->dev.of_node; sprintf(sunxi_hci->hci_name, "%s", np->name); return 0; } static int sunxi_get_hci_irq_no(struct platform_device *pdev, struct sunxi_hci_hcd *sunxi_hci) { sunxi_hci->irq_no = platform_get_irq(pdev, 0); return 0; } static int hci_wakeup_source_init(struct platform_device *pdev, struct sunxi_hci_hcd *sunxi_hci) { if (sunxi_hci->wakeup_source_flag && sunxi_hci->wakeup_suspend) { device_init_wakeup(&pdev->dev, true); dev_pm_set_wake_irq(&pdev->dev, sunxi_hci->irq_no); } else { DMSG_INFO("sunxi %s don't init wakeup source\n", sunxi_hci->hci_name); } return 0; } void enter_usb_standby(struct sunxi_hci_hcd *sunxi_hci) { mutex_lock(&usb_standby_lock); if (!atomic_read(&usb_standby_cnt)) { atomic_add(1, &usb_standby_cnt); } else { /*phy after ehci and ohci suspend.*/ #if defined(CONFIG_ARCH_SUN50IW10) if (sunxi_hci->usbc_no == HCI0_USBC_NO) { /*usb1 0x800 bit[2], enable rc16M*/ sunxi_hci_common_set_rc_clk(sunxi_hci, 1); /*usb1 0x800 bit[31], switch 16M*/ sunxi_hci_common_switch_clk(sunxi_hci, 1); /*usb1 0x800 bit[3], open 16M gating*/ sunxi_hci_common_set_rcgating(sunxi_hci, 1); } #endif #if defined(SUNXI_USB_STANDBY_LOW_POW_MODE) #if defined(SUNXI_USB_STANDBY_NEW_MODE) /*phy reg, offset:0x800 bit2, set 1, enable RC16M CLK*/ sunxi_hci_set_rc_clk(sunxi_hci, 1); #if defined(CONFIG_ARCH_SUN50IW9) /*enable standby irq*/ sunxi_hci_set_standby_irq(sunxi_hci, 1); #endif #endif /*phy reg, offset:0x808 bit3, set 1, remote enable*/ sunxi_hci_set_wakeup_ctrl(sunxi_hci, 1); /*phy reg, offset:0x810 bit3 set 1, clean siddq*/ sunxi_hci_set_siddq(sunxi_hci, 1); #if defined(CONFIG_ARCH_SUN8IW21) sunxi_hci_set_vc_cfg(sunxi_hci, 1); sunxi_hci_set_clk(sunxi_hci, 1); #endif #endif #if defined(CONFIG_ARCH_SUN50IW10) /*phy reg, offset:0x0 bit31, set 1*/ sunxi_hci_switch_clk(sunxi_hci, 1); /*phy reg, offset:0x0 bit3, set 1*/ sunxi_hci_set_rcgating(sunxi_hci, 1); #endif atomic_sub(1, &usb_standby_cnt); } mutex_unlock(&usb_standby_lock); } EXPORT_SYMBOL(enter_usb_standby); void exit_usb_standby(struct sunxi_hci_hcd *sunxi_hci) { mutex_lock(&usb_standby_lock); if (atomic_read(&usb_standby_cnt)) { atomic_sub(1, &usb_standby_cnt); } else { /*phy before ehci and ohci*/ #if defined(CONFIG_ARCH_SUN50IW10) if (sunxi_hci->usbc_no == HCI0_USBC_NO) { /*usb1 0x800 bit[3]*/ sunxi_hci_common_set_rcgating(sunxi_hci, 0); /*usb1 0x800 bit[31]*/ sunxi_hci_common_switch_clk(sunxi_hci, 0); /*usb1 0x800 bit[2]*/ sunxi_hci_common_set_rc_clk(sunxi_hci, 0); } /*phy reg, offset:0x0 bit3, set 0*/ sunxi_hci_set_rcgating(sunxi_hci, 0); /*phy reg, offset:0x0 bit31, set 0*/ sunxi_hci_switch_clk(sunxi_hci, 0); #endif #if defined(SUNXI_USB_STANDBY_LOW_POW_MODE) #if defined(CONFIG_ARCH_SUN8IW21) sunxi_hci_set_clk(sunxi_hci, 0); sunxi_hci_set_vc_cfg(sunxi_hci, 0); #endif /*phy reg, offset:0x810 bit3 set 0, enable siddq*/ sunxi_hci_set_siddq(sunxi_hci, 0); /*phy reg, offset:0x808 bit3, set 0, remote disable*/ sunxi_hci_set_wakeup_ctrl(sunxi_hci, 0); #if defined(SUNXI_USB_STANDBY_NEW_MODE) #if defined(CONFIG_ARCH_SUN50IW9) /*clear standby irq status*/ sunxi_hci_clean_standby_irq(sunxi_hci); /*disable standby irq */ sunxi_hci_set_standby_irq(sunxi_hci, 0); #endif /*phy reg, offset:0x0 bit2, set 0, disable rc clk*/ sunxi_hci_set_rc_clk(sunxi_hci, 0); #endif #endif atomic_add(1, &usb_standby_cnt); } mutex_unlock(&usb_standby_lock); } EXPORT_SYMBOL(exit_usb_standby); static int sunxi_get_hci_resource(struct platform_device *pdev, struct sunxi_hci_hcd *sunxi_hci, int usbc_no) { if (sunxi_hci == NULL) { dev_err(&pdev->dev, "sunxi_hci is NULL\n"); return -1; } memset(sunxi_hci, 0, sizeof(struct sunxi_hci_hcd)); sunxi_hci->usbc_no = usbc_no; sunxi_get_hci_name(pdev, sunxi_hci); get_usb_cfg(pdev, sunxi_hci); if (sunxi_hci->used == 0) { DMSG_INFO("sunxi %s is no enable\n", sunxi_hci->hci_name); return -1; } sunxi_get_hci_base(pdev, sunxi_hci); sunxi_get_hci_clock(pdev, sunxi_hci); sunxi_get_hci_irq_no(pdev, sunxi_hci); hci_wakeup_source_init(pdev, sunxi_hci); request_usb_regulator_io(sunxi_hci); sunxi_hci->open_clock = open_clock; sunxi_hci->close_clock = close_clock; sunxi_hci->set_power = sunxi_set_vbus; sunxi_hci->usb_passby = usb_passby; alloc_pin(sunxi_hci); pdev->dev.platform_data = sunxi_hci; return 0; } static int sunxi_hci_dump_reg_all(int usbc_type) { struct sunxi_hci_hcd *sunxi_hci, *sunxi_ehci, *sunxi_ohci; struct resource res, res1; int i; switch (usbc_type) { case SUNXI_USB_EHCI: for (i = 0; i < 4; i++) { switch (i) { case HCI0_USBC_NO: sunxi_hci = &sunxi_ehci0; break; case HCI1_USBC_NO: sunxi_hci = &sunxi_ehci1; break; case HCI2_USBC_NO: sunxi_hci = &sunxi_ehci2; break; case HCI3_USBC_NO: sunxi_hci = &sunxi_ehci3; break; } if (sunxi_hci->used) { of_address_to_resource(sunxi_hci->pdev->dev.of_node, 0, &res); DMSG_INFO("usbc%d, ehci, base[0x%08x]:\n", i, (unsigned int)res.start); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 4, 4, sunxi_hci->ehci_base, 0x58, false); DMSG_INFO("\n"); } } return 0; case SUNXI_USB_OHCI: for (i = 0; i < 4; i++) { switch (i) { case HCI0_USBC_NO: sunxi_hci = &sunxi_ohci0; break; case HCI1_USBC_NO: sunxi_hci = &sunxi_ohci1; break; case HCI2_USBC_NO: sunxi_hci = &sunxi_ohci2; break; case HCI3_USBC_NO: sunxi_hci = &sunxi_ohci3; break; } if (sunxi_hci->used) { of_address_to_resource(sunxi_hci->pdev->dev.of_node, 0, &res); DMSG_INFO("usbc%d, ohci, base[0x%08x]:\n", i, (unsigned int)(res.start + SUNXI_USB_OHCI_BASE_OFFSET)); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 4, 4, sunxi_hci->ohci_base, 0x58, false); DMSG_INFO("\n"); } } return 0; case SUNXI_USB_PHY: for (i = 0; i < 4; i++) { switch (i) { case HCI0_USBC_NO: sunxi_hci = &sunxi_ehci0; break; case HCI1_USBC_NO: sunxi_hci = &sunxi_ehci1; break; case HCI2_USBC_NO: sunxi_hci = &sunxi_ehci2; break; case HCI3_USBC_NO: sunxi_hci = &sunxi_ehci3; break; } if (sunxi_hci->used) { of_address_to_resource(sunxi_hci->pdev->dev.of_node, 0, &res); DMSG_INFO("usbc%d, phy, base[0x%08x]:\n", i, (unsigned int)(res.start + SUNXI_USB_PMU_IRQ_ENABLE)); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 4, 4, sunxi_hci->usb_vbase + SUNXI_USB_PMU_IRQ_ENABLE, 0x34, false); DMSG_INFO("\n"); } } return 0; case SUNXI_USB_ALL: for (i = 0; i < 4; i++) { switch (i) { case HCI0_USBC_NO: sunxi_ehci = &sunxi_ehci0; sunxi_ohci = &sunxi_ohci0; break; case HCI1_USBC_NO: sunxi_ehci = &sunxi_ehci1; sunxi_ohci = &sunxi_ohci1; break; case HCI2_USBC_NO: sunxi_ehci = &sunxi_ehci2; sunxi_ohci = &sunxi_ohci2; break; case HCI3_USBC_NO: sunxi_ehci = &sunxi_ehci3; sunxi_ohci = &sunxi_ohci3; break; } if (sunxi_ehci->used) { of_address_to_resource(sunxi_ehci->pdev->dev.of_node, 0, &res); of_address_to_resource(sunxi_ohci->pdev->dev.of_node, 0, &res1); DMSG_INFO("usbc%d, ehci, base[0x%08x]:\n", i, (unsigned int)res.start); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 4, 4, sunxi_ehci->ehci_base, 0x58, false); DMSG_INFO("usbc%d, ohci, base[0x%08x]:\n", i, (unsigned int)(res1.start + SUNXI_USB_OHCI_BASE_OFFSET)); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 4, 4, sunxi_ohci->ohci_base, 0x58, false); DMSG_INFO("usbc%d, phy, base[0x%08x]:\n", i, (unsigned int)(res.start + SUNXI_USB_PMU_IRQ_ENABLE)); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 4, 4, sunxi_ehci->usb_vbase + SUNXI_USB_PMU_IRQ_ENABLE, 0x34, false); DMSG_INFO("\n"); } } return 0; default: DMSG_PANIC("%s()%d invalid argument\n", __func__, __LINE__); return -EINVAL; } } static int sunxi_hci_dump_reg(int usbc_no, int usbc_type) { struct sunxi_hci_hcd *sunxi_hci, *sunxi_ehci, *sunxi_ohci; struct resource res, res1; switch (usbc_type) { case SUNXI_USB_EHCI: switch (usbc_no) { case HCI0_USBC_NO: sunxi_hci = &sunxi_ehci0; break; case HCI1_USBC_NO: sunxi_hci = &sunxi_ehci1; break; case HCI2_USBC_NO: sunxi_hci = &sunxi_ehci2; break; case HCI3_USBC_NO: sunxi_hci = &sunxi_ehci3; break; default: DMSG_PANIC("%s()%d invalid argument\n", __func__, __LINE__); return -EINVAL; } if (sunxi_hci->used) { of_address_to_resource(sunxi_hci->pdev->dev.of_node, 0, &res); DMSG_INFO("usbc%d, ehci, base[0x%08x]:\n", usbc_no, (unsigned int)res.start); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 4, 4, sunxi_hci->ehci_base, 0x58, false); DMSG_INFO("\n"); } else DMSG_PANIC("%s()%d usbc%d is disable\n", __func__, __LINE__, usbc_no); return 0; case SUNXI_USB_OHCI: switch (usbc_no) { case HCI0_USBC_NO: sunxi_hci = &sunxi_ohci0; break; case HCI1_USBC_NO: sunxi_hci = &sunxi_ohci1; break; case HCI2_USBC_NO: sunxi_hci = &sunxi_ohci2; break; case HCI3_USBC_NO: sunxi_hci = &sunxi_ohci3; break; default: DMSG_PANIC("%s()%d invalid argument\n", __func__, __LINE__); return -EINVAL; } if (sunxi_hci->used) { of_address_to_resource(sunxi_hci->pdev->dev.of_node, 0, &res); DMSG_INFO("usbc%d, ohci, base[0x%08x]:\n", usbc_no, (unsigned int)(res.start + SUNXI_USB_OHCI_BASE_OFFSET)); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 4, 4, sunxi_hci->ohci_base, 0x58, false); DMSG_INFO("\n"); } else DMSG_PANIC("%s()%d usbc%d is disable\n", __func__, __LINE__, usbc_no); return 0; case SUNXI_USB_PHY: switch (usbc_no) { case HCI0_USBC_NO: sunxi_hci = &sunxi_ehci0; break; case HCI1_USBC_NO: sunxi_hci = &sunxi_ehci1; break; case HCI2_USBC_NO: sunxi_hci = &sunxi_ehci2; break; case HCI3_USBC_NO: sunxi_hci = &sunxi_ehci3; break; default: DMSG_PANIC("%s()%d invalid argument\n", __func__, __LINE__); return -EINVAL; } if (sunxi_hci->used) { of_address_to_resource(sunxi_hci->pdev->dev.of_node, 0, &res); DMSG_INFO("usbc%d, phy, base[0x%08x]:\n", usbc_no, (unsigned int)(res.start + SUNXI_USB_PMU_IRQ_ENABLE)); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 4, 4, sunxi_hci->usb_vbase + SUNXI_USB_PMU_IRQ_ENABLE, 0x34, false); DMSG_INFO("\n"); } else DMSG_PANIC("%s()%d usbc%d is disable\n", __func__, __LINE__, usbc_no); return 0; case SUNXI_USB_ALL: switch (usbc_no) { case HCI0_USBC_NO: sunxi_ehci = &sunxi_ehci0; sunxi_ohci = &sunxi_ohci0; break; case HCI1_USBC_NO: sunxi_ehci = &sunxi_ehci1; sunxi_ohci = &sunxi_ohci1; break; case HCI2_USBC_NO: sunxi_ehci = &sunxi_ehci2; sunxi_ohci = &sunxi_ohci2; break; case HCI3_USBC_NO: sunxi_ehci = &sunxi_ehci3; sunxi_ohci = &sunxi_ohci3; break; default: DMSG_PANIC("%s()%d invalid argument\n", __func__, __LINE__); return -EINVAL; } if (sunxi_ehci->used) { of_address_to_resource(sunxi_ehci->pdev->dev.of_node, 0, &res); of_address_to_resource(sunxi_ohci->pdev->dev.of_node, 0, &res1); DMSG_INFO("usbc%d, ehci, base[0x%08x]:\n", usbc_no, (unsigned int)res.start); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 4, 4, sunxi_ehci->ehci_base, 0x58, false); DMSG_INFO("usbc%d, ohci, base[0x%08x]:\n", usbc_no, (unsigned int)(res1.start + SUNXI_USB_OHCI_BASE_OFFSET)); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 4, 4, sunxi_ohci->ohci_base, 0x58, false); DMSG_INFO("usbc%d, phy, base[0x%08x]:\n", usbc_no, (unsigned int)(res.start + SUNXI_USB_PMU_IRQ_ENABLE)); print_hex_dump(KERN_WARNING, "", DUMP_PREFIX_OFFSET, 4, 4, sunxi_ehci->usb_vbase + SUNXI_USB_PMU_IRQ_ENABLE, 0x34, false); DMSG_INFO("\n"); } else DMSG_PANIC("%s()%d usbc%d is disable\n", __func__, __LINE__, usbc_no); return 0; default: DMSG_PANIC("%s()%d invalid argument\n", __func__, __LINE__); return -EINVAL; } } int sunxi_hci_standby_completion(int usbc_type) { if (sunxi_ehci0.used) { if (usbc_type == SUNXI_USB_EHCI) { complete(&sunxi_ehci0.standby_complete); } else if (usbc_type == SUNXI_USB_OHCI) { complete(&sunxi_ohci0.standby_complete); } else { pr_err("%s()%d err usb type\n", __func__, __LINE__); return -1; } } if (sunxi_ehci1.used) { if (usbc_type == SUNXI_USB_EHCI) { complete(&sunxi_ehci1.standby_complete); } else if (usbc_type == SUNXI_USB_OHCI) { complete(&sunxi_ohci1.standby_complete); } else { pr_err("%s()%d err usb type\n", __func__, __LINE__); return -1; } } if (sunxi_ehci2.used) { if (usbc_type == SUNXI_USB_EHCI) { complete(&sunxi_ehci2.standby_complete); } else if (usbc_type == SUNXI_USB_OHCI) { complete(&sunxi_ohci2.standby_complete); } else { pr_err("%s()%d err usb type\n", __func__, __LINE__); return -1; } } if (sunxi_ehci3.used) { if (usbc_type == SUNXI_USB_EHCI) { complete(&sunxi_ehci3.standby_complete); } else if (usbc_type == SUNXI_USB_OHCI) { complete(&sunxi_ohci3.standby_complete); } else { pr_err("%s()%d err usb type\n", __func__, __LINE__); return -1; } } return 0; } EXPORT_SYMBOL(sunxi_hci_standby_completion); int exit_sunxi_hci(struct sunxi_hci_hcd *sunxi_hci) { release_usb_regulator_io(sunxi_hci); free_pin(sunxi_hci); return 0; } EXPORT_SYMBOL(exit_sunxi_hci); int init_sunxi_hci(struct platform_device *pdev, int usbc_type) { struct sunxi_hci_hcd *sunxi_hci = NULL; int usbc_no = 0; int hci_num = -1; int ret = -1; #ifdef CONFIG_OF pdev->dev.dma_mask = &sunxi_hci_dmamask; pdev->dev.coherent_dma_mask = DMA_BIT_MASK(64); #endif hci_num = sunxi_get_hci_num(pdev); if (usbc_type == SUNXI_USB_XHCI) { usbc_no = hci_num; sunxi_hci = &sunxi_xhci; } else { switch (hci_num) { case HCI0_USBC_NO: usbc_no = HCI0_USBC_NO; if (usbc_type == SUNXI_USB_EHCI) { sunxi_hci = &sunxi_ehci0; } else if (usbc_type == SUNXI_USB_OHCI) { sunxi_hci = &sunxi_ohci0; } else { dev_err(&pdev->dev, "get hci num fail: %d\n", hci_num); return -1; } break; case HCI1_USBC_NO: usbc_no = HCI1_USBC_NO; if (usbc_type == SUNXI_USB_EHCI) { sunxi_hci = &sunxi_ehci1; } else if (usbc_type == SUNXI_USB_OHCI) { sunxi_hci = &sunxi_ohci1; } else { dev_err(&pdev->dev, "get hci num fail: %d\n", hci_num); return -1; } break; case HCI2_USBC_NO: usbc_no = HCI2_USBC_NO; if (usbc_type == SUNXI_USB_EHCI) { sunxi_hci = &sunxi_ehci2; } else if (usbc_type == SUNXI_USB_OHCI) { sunxi_hci = &sunxi_ohci2; } else { dev_err(&pdev->dev, "get hci num fail: %d\n", hci_num); return -1; } break; case HCI3_USBC_NO: usbc_no = HCI3_USBC_NO; if (usbc_type == SUNXI_USB_EHCI) { sunxi_hci = &sunxi_ehci3; } else if (usbc_type == SUNXI_USB_OHCI) { sunxi_hci = &sunxi_ohci3; } else { dev_err(&pdev->dev, "get hci num fail: %d\n", hci_num); return -1; } break; default: dev_err(&pdev->dev, "get hci num fail: %d\n", hci_num); return -1; } } ret = sunxi_get_hci_resource(pdev, sunxi_hci, usbc_no); if (ret != 0) return ret; if (usbc_type == SUNXI_USB_OHCI) ret = sunxi_get_ohci_clock_src(pdev, sunxi_hci); return ret; } EXPORT_SYMBOL(init_sunxi_hci); static int __parse_hci_str(const char *buf, size_t size) { int ret = 0; unsigned long val; if (!buf) { pr_err("%s()%d invalid argument\n", __func__, __LINE__); return -1; } ret = kstrtoul(buf, 10, &val); if (ret) { pr_err("%s()%d failed to transfer\n", __func__, __LINE__); return -1; } return val; } static ssize_t ehci_reg_show(struct class *class, struct class_attribute *attr, char *buf) { sunxi_hci_dump_reg_all(SUNXI_USB_EHCI); return 0; } static ssize_t ehci_reg_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int usbc_no; usbc_no = __parse_hci_str(buf, count); if (usbc_no < 0) return -EINVAL; sunxi_hci_dump_reg(usbc_no, SUNXI_USB_EHCI); return count; } static ssize_t ohci_reg_show(struct class *class, struct class_attribute *attr, char *buf) { sunxi_hci_dump_reg_all(SUNXI_USB_OHCI); return 0; } static ssize_t ohci_reg_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int usbc_no; usbc_no = __parse_hci_str(buf, count); if (usbc_no < 0) return -EINVAL; sunxi_hci_dump_reg(usbc_no, SUNXI_USB_OHCI); return count; } static ssize_t phy_reg_show(struct class *class, struct class_attribute *attr, char *buf) { sunxi_hci_dump_reg_all(SUNXI_USB_PHY); return 0; } static ssize_t phy_reg_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int usbc_no; usbc_no = __parse_hci_str(buf, count); if (usbc_no < 0) return -EINVAL; sunxi_hci_dump_reg(usbc_no, SUNXI_USB_PHY); return count; } static ssize_t all_reg_show(struct class *class, struct class_attribute *attr, char *buf) { sunxi_hci_dump_reg_all(SUNXI_USB_ALL); return 0; } static ssize_t all_reg_store(struct class *class, struct class_attribute *attr, const char *buf, size_t count) { int usbc_no; usbc_no = __parse_hci_str(buf, count); if (usbc_no < 0) return -EINVAL; sunxi_hci_dump_reg(usbc_no, SUNXI_USB_ALL); return count; } static struct class_attribute hci_class_attrs[] = { __ATTR(ehci_reg, 0644, ehci_reg_show, ehci_reg_store), __ATTR(ohci_reg, 0644, ohci_reg_show, ohci_reg_store), __ATTR(phy_reg, 0644, phy_reg_show, phy_reg_store), __ATTR(all_reg, 0644, all_reg_show, all_reg_store), __ATTR_NULL, }; static struct class hci_class = { .name = "hci_reg", .owner = THIS_MODULE, .class_attrs = hci_class_attrs, }; static int __init init_sunxi_hci_class(void) { int ret = 0; ret = class_register(&hci_class); if (ret) { DMSG_PANIC("%s()%d register class fialed\n", __func__, __LINE__); return -1; } return 0; } static void __exit exit_sunxi_hci_class(void) { class_unregister(&hci_class); } late_initcall(init_sunxi_hci_class); module_exit(exit_sunxi_hci_class); MODULE_LICENSE("GPL");