USB: EHCI HCD (USB 2.0) support for rk3108/rk3168/rk3188
authorwlf <wulf@rock-chips.com>
Sat, 10 Aug 2013 03:01:07 +0000 (11:01 +0800)
committerwlf <wulf@rock-chips.com>
Sat, 10 Aug 2013 03:01:07 +0000 (11:01 +0800)
arch/arm/mach-rk3188/Kconfig
drivers/usb/dwc_otg/usbdev_rk.h
drivers/usb/dwc_otg/usbdev_rk30.c
drivers/usb/host/ehci-rk.c

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