From 49a1e1e7e4c20b952c9d6c36eb7b4d4101649118 Mon Sep 17 00:00:00 2001 From: wlf Date: Sat, 10 Aug 2013 11:01:07 +0800 Subject: [PATCH] USB: EHCI HCD (USB 2.0) support for rk3108/rk3168/rk3188 --- arch/arm/mach-rk3188/Kconfig | 2 + drivers/usb/dwc_otg/usbdev_rk.h | 11 +++ drivers/usb/dwc_otg/usbdev_rk30.c | 94 ++++++++++++++++++++++ drivers/usb/host/ehci-rk.c | 128 +++++++++++++++++++++++++----- 4 files changed, 214 insertions(+), 21 deletions(-) diff --git a/arch/arm/mach-rk3188/Kconfig b/arch/arm/mach-rk3188/Kconfig index 8eae9daa7bf6..2f51591d8490 100755 --- a/arch/arm/mach-rk3188/Kconfig +++ b/arch/arm/mach-rk3188/Kconfig @@ -4,9 +4,11 @@ choice config SOC_RK3188 bool "RK3188" + select USB_ARCH_HAS_EHCI if USB_SUPPORT config SOC_RK3188M bool "RK3188M" + select USB_ARCH_HAS_EHCI if USB_SUPPORT endchoice diff --git a/drivers/usb/dwc_otg/usbdev_rk.h b/drivers/usb/dwc_otg/usbdev_rk.h index 686ff71e035d..672fdd737a09 100755 --- a/drivers/usb/dwc_otg/usbdev_rk.h +++ b/drivers/usb/dwc_otg/usbdev_rk.h @@ -25,3 +25,14 @@ struct dwc_otg_platform_data { void (*dwc_otg_uart_mode)(void* pdata, int enter_usb_uart_mode); int (*get_status)(int id); }; + +struct rkehci_platform_data{ + struct clk* hclk_hsic; + struct clk* hsic_phy_480m; + struct clk* hsic_phy_12m; + void (*hw_init)(void); + void (*clock_init)(void* pdata); + void (*clock_enable)(void *pdata, int enable); + void (*soft_reset)(void); + int clk_status; +}; diff --git a/drivers/usb/dwc_otg/usbdev_rk30.c b/drivers/usb/dwc_otg/usbdev_rk30.c index f651f658afca..2f148ad302a1 100755 --- a/drivers/usb/dwc_otg/usbdev_rk30.c +++ b/drivers/usb/dwc_otg/usbdev_rk30.c @@ -21,9 +21,11 @@ #define USBGRF_UOC0_CON0 (GRF_REG_BASE+0x10c) #define USBGRF_UOC0_CON2 (GRF_REG_BASE+0x114) #define USBGRF_UOC0_CON3 (GRF_REG_BASE+0x118) +#define USBGRF_UOC1_CON0 (GRF_REG_BASE+0x11C) #define USBGRF_UOC1_CON2 (GRF_REG_BASE+0x124) #define USBGRF_UOC1_CON3 (GRF_REG_BASE+0x128) +#define USBGRF_UOC2_CON0 (GRF_REG_BASE+0x12C) #if defined(CONFIG_SOC_RK3066B) || defined(CONFIG_SOC_RK3108) #define RK3066B_HOST_DRV_VBUS RK30_PIN0_PD7 #define RK3066B_OTG_DRV_VBUS RK30_PIN0_PD6 @@ -480,6 +482,96 @@ struct platform_device device_usb20_host = { }, }; #endif +#ifdef CONFIG_USB_EHCI_RK +void rkehci_hw_init(void) +{ + unsigned int * phy_con0 = (unsigned int*)(USBGRF_UOC2_CON0); + unsigned int * phy_con1 = (unsigned int*)(USBGRF_UOC1_CON0); + unsigned int * phy_con2 = (unsigned int*)(USBGRF_UOC0_CON0); + // usb phy config init + // hsic phy config init, set hsicphy_txsrtune + *phy_con0 = ((0xf<<6)<<16)|(0xf<<6); + + // other haredware init + // set common_on, in suspend mode, otg/host PLL blocks remain powered +#ifdef CONFIG_ARCH_RK3188 + *phy_con1 = (1<<16)|0; +#else + *phy_con2 = (1<<16)|0; +#endif +} + +void rkehci_clock_init(void* pdata) +{ + struct rkehci_platform_data *usbpdata=pdata; + +#ifdef CONFIG_ARCH_RK3188 + struct clk *clk_otg, *clk_hs; + + /* By default, hsicphy_480m's parent is otg phy 480MHz clk + * rk3188 must use host phy 480MHz clk + */ + clk_hs = clk_get(NULL, "hsicphy_480m"); + clk_otg = clk_get(NULL, "otgphy1_480m"); + clk_set_parent(clk_hs, clk_otg); +#endif + + usbpdata->hclk_hsic = clk_get(NULL, "hclk_hsic"); + usbpdata->hsic_phy_480m = clk_get(NULL, "hsicphy_480m"); + usbpdata->hsic_phy_12m = clk_get(NULL, "hsicphy_12m"); +} + +void rkehci_clock_enable(void* pdata, int enable) +{ + struct rkehci_platform_data *usbpdata=pdata; + + if(enable == usbpdata->clk_status) + return; + + if(enable){ + clk_enable(usbpdata->hclk_hsic); + clk_enable(usbpdata->hsic_phy_480m); + clk_enable(usbpdata->hsic_phy_12m); + usbpdata->clk_status = 1; + }else{ + clk_disable(usbpdata->hsic_phy_12m); + clk_disable(usbpdata->hsic_phy_480m); + clk_disable(usbpdata->hclk_hsic); + usbpdata->clk_status = 0; + } +} + +void rkehci_soft_reset(void) +{ + unsigned int * phy_con0 = (unsigned int*)(USBGRF_UOC2_CON0); + + cru_set_soft_reset(SOFT_RST_HSICPHY, true); + udelay(12); + cru_set_soft_reset(SOFT_RST_HSICPHY, false); + mdelay(2); + + *phy_con0 = ((1<<10)<<16)|(1<<10); + udelay(2); + *phy_con0 = ((1<<10)<<16)|(0<<10); + udelay(2); + + cru_set_soft_reset(SOFT_RST_HSIC_AHB, true); + udelay(2); + cru_set_soft_reset(SOFT_RST_HSIC_AHB, false); + udelay(2); +} + +struct rkehci_platform_data rkehci_pdata = { + .hclk_hsic = NULL, + .hsic_phy_12m = NULL, + .hsic_phy_480m = NULL, + .clk_status = -1, + .hw_init = rkehci_hw_init, + .clock_init = rkehci_clock_init, + .clock_enable = rkehci_clock_enable, + .soft_reset = rkehci_soft_reset, +}; + static struct resource resources_hsusb_host[] = { { .start = IRQ_HSIC, @@ -500,8 +592,10 @@ struct platform_device device_hsusb_host = { .resource = resources_hsusb_host, .dev = { .coherent_dma_mask = 0xffffffff, + .platform_data = &rkehci_pdata, }, }; +#endif static int __init usbdev_init_devices(void) { diff --git a/drivers/usb/host/ehci-rk.c b/drivers/usb/host/ehci-rk.c index e5e8b7eb2342..649f55090059 100755 --- a/drivers/usb/host/ehci-rk.c +++ b/drivers/usb/host/ehci-rk.c @@ -27,8 +27,14 @@ #include #include +#include #include "ehci.h" +#include "../dwc_otg/usbdev_rk.h" +static int rkehci_status = 1; +static struct ehci_hcd *g_ehci; +#define EHCI_DEVICE_FILE "/sys/devices/platform/rk_hsusb_host/ehci_power" +#define EHCI_PRINT(x...) printk( KERN_INFO "EHCI: " x ) static struct hc_driver rk_hc_driver = { .description = hcd_name, @@ -76,33 +82,112 @@ static struct hc_driver rk_hc_driver = { .bus_resume = ehci_bus_resume, }; +static ssize_t ehci_power_show( struct device *_dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "%d\n", rkehci_status); +} +static ssize_t ehci_power_store( struct device *_dev, + struct device_attribute *attr, + const char *buf, size_t count ) +{ + uint32_t val = simple_strtoul(buf, NULL, 16); + struct usb_hcd *hcd = dev_get_drvdata(_dev); + struct ehci_hcd *ehci = hcd_to_ehci(hcd); + struct rkehci_platform_data *pldata = _dev->platform_data; + + printk("%s: %d setting to: %d\n", __func__, rkehci_status, val); + if(val == rkehci_status) + goto out; + + rkehci_status = val; + switch(val){ + case 0: //power down + ehci_port_power(ehci, 0); + writel_relaxed(0 ,hcd->regs +0xb0); + dsb(); + msleep(5); + usb_remove_hcd(hcd); + break; + case 1: // power on + pldata->soft_reset(); + usb_add_hcd(hcd, hcd->irq, IRQF_DISABLED | IRQF_SHARED); + + ehci_port_power(ehci, 1); + writel_relaxed(1 ,hcd->regs +0xb0); + writel_relaxed(0x1d4d ,hcd->regs +0x90); + writel_relaxed(0x4 ,hcd->regs +0xa0); + dsb(); + break; + default: + break; + } +out: + return count; +} +static DEVICE_ATTR(ehci_power, S_IRUGO|S_IWUSR, ehci_power_show, ehci_power_store); + +static ssize_t debug_show( struct device *_dev, + struct device_attribute *attr, char *buf) +{ + volatile uint32_t *addr; + + EHCI_PRINT("******** EHCI Capability Registers **********\n"); + addr = &g_ehci->caps->hc_capbase; + EHCI_PRINT("HCIVERSION / CAPLENGTH @0x%08x: 0x%08x\n", (uint32_t)addr, readl_relaxed(addr)); + addr = &g_ehci->caps->hcs_params; + EHCI_PRINT("HCSPARAMS @0x%08x: 0x%08x\n", (uint32_t)addr, readl_relaxed(addr)); + addr = &g_ehci->caps->hcc_params; + EHCI_PRINT("HCCPARAMS @0x%08x: 0x%08x\n", (uint32_t)addr, readl_relaxed(addr)); + EHCI_PRINT("********* EHCI Operational Registers *********\n"); + addr = &g_ehci->regs->command; + EHCI_PRINT("USBCMD @0x%08x: 0x%08x\n", (uint32_t)addr, readl_relaxed(addr)); + addr = &g_ehci->regs->status; + EHCI_PRINT("USBSTS @0x%08x: 0x%08x\n", (uint32_t)addr, readl_relaxed(addr)); + addr = &g_ehci->regs->intr_enable; + EHCI_PRINT("USBINTR @0x%08x: 0x%08x\n", (uint32_t)addr, readl_relaxed(addr)); + addr = &g_ehci->regs->frame_index; + EHCI_PRINT("FRINDEX @0x%08x: 0x%08x\n", (uint32_t)addr, readl_relaxed(addr)); + addr = &g_ehci->regs->segment; + EHCI_PRINT("CTRLDSSEGMENT @0x%08x: 0x%08x\n", (uint32_t)addr, readl_relaxed(addr)); + addr = &g_ehci->regs->frame_list; + EHCI_PRINT("PERIODICLISTBASE @0x%08x: 0x%08x\n", (uint32_t)addr, readl_relaxed(addr)); + addr = &g_ehci->regs->async_next; + EHCI_PRINT("ASYNCLISTADDR @0x%08x: 0x%08x\n", (uint32_t)addr, readl_relaxed(addr)); + addr = &g_ehci->regs->configured_flag; + EHCI_PRINT("CONFIGFLAG @0x%08x: 0x%08x\n", (uint32_t)addr, readl_relaxed(addr)); + addr = g_ehci->regs->port_status; + EHCI_PRINT("PORTSC @0x%08x: 0x%08x\n", (uint32_t)addr, readl_relaxed(addr)); + return sprintf(buf, "EHCI Registers Dump\n"); +} +static DEVICE_ATTR(debug_ehci, S_IRUGO, debug_show, NULL); + static int ehci_rk_probe(struct platform_device *pdev) { struct usb_hcd *hcd; struct ehci_hcd *ehci; struct resource *res; - struct clk *clk1; - struct clk *clk2; - struct clk *clk3; struct device *dev = &pdev->dev; + struct rkehci_platform_data *pldata = dev->platform_data; int ret; + int retval = 0; static u64 usb_dmamask = 0xffffffffUL; dev_dbg(&pdev->dev, "ehci_rk proble\n"); dev->dma_mask = &usb_dmamask; + retval = device_create_file(dev, &dev_attr_ehci_power); + retval = device_create_file(dev, &dev_attr_debug_ehci); hcd = usb_create_hcd(&rk_hc_driver, &pdev->dev, dev_name(&pdev->dev)); if (!hcd) { dev_err(&pdev->dev, "Unable to create HCD\n"); return -ENOMEM; } - clk1 = clk_get(NULL, "hclk_hsic"); - clk2 = clk_get(NULL, "hsicphy_480m"); - clk3 = clk_get(NULL, "hsicphy_12m"); - clk_enable(clk1); - clk_enable(clk2); - clk_enable(clk3); + + pldata->hw_init(); + pldata->clock_init(pldata); + pldata->clock_enable(pldata, 1); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { @@ -126,22 +211,23 @@ static int ehci_rk_probe(struct platform_device *pdev) goto put_hcd; } - ehci = hcd_to_ehci(hcd); - ehci->caps = hcd->regs; - ehci->regs = hcd->regs + 0x10; - printk("%s %p %p\n", __func__, ehci->caps, ehci->regs); + ehci = hcd_to_ehci(hcd); + ehci->caps = hcd->regs; + ehci->regs = hcd->regs + 0x10; + printk("%s %p %p\n", __func__, ehci->caps, ehci->regs); - dbg_hcs_params(ehci, "reset"); - dbg_hcc_params(ehci, "reset"); + dbg_hcs_params(ehci, "reset"); + dbg_hcc_params(ehci, "reset"); - ehci->hcs_params = readl(&ehci->caps->hcs_params); + ehci->hcs_params = readl(&ehci->caps->hcs_params); ret = usb_add_hcd(hcd, hcd->irq, IRQF_DISABLED | IRQF_SHARED); - if (ret) { - dev_err(&pdev->dev, "Failed to add USB HCD\n"); - goto unmap; - } - + if (ret) { + dev_err(&pdev->dev, "Failed to add USB HCD\n"); + goto unmap; + } + + g_ehci = ehci; ehci_port_power(ehci, 1); writel_relaxed(1 ,hcd->regs +0xb0); writel_relaxed(0x1d4d ,hcd->regs +0x90); -- 2.34.1