usb: support for rk3026
authorlyz <lyz@rock-chips.com>
Mon, 22 Jul 2013 11:16:32 +0000 (19:16 +0800)
committerlyz <lyz@rock-chips.com>
Mon, 22 Jul 2013 11:18:55 +0000 (19:18 +0800)
arch/arm/plat-rk/usb_detect.c
drivers/usb/dwc_otg/Makefile
drivers/usb/dwc_otg/usbdev_rk3026.c [new file with mode: 0755]

index 2671071a3f6b1fea25fdf6290aff31c5a08f280e..3a0acd658ae85739dd497cea7519d03c3531f906 100755 (executable)
@@ -147,3 +147,52 @@ static int __init bvalid_init(void)
 }
 late_initcall(bvalid_init);
 #endif
+
+#if defined(IRQ_OTG0_ID) && defined(CONFIG_ARCH_RK3026)
+#include <linux/io.h>
+#include <mach/iomux.h>
+
+static irqreturn_t otg_id_irq_handler(int irq, void *dev_id)
+{
+    unsigned int uoc_con;
+       /* clear irq */
+    uoc_con = readl_relaxed(RK2928_GRF_BASE + GRF_UOC_CON);
+
+#ifdef CONFIG_RK_USB_UART
+       /* to do @lyz*/
+#endif
+    
+    if(uoc_con & (1<<1)) //id rise 
+    {
+        writel_relaxed((0x1 << 16) | 0x1, RK2928_GRF_BASE + GRF_UOC_CON);;//clear id rise irq pandding
+    }
+    if(uoc_con & (1<<3))//id fall
+    { 
+        writel_relaxed((0x3 << 16) | 0x3, RK2928_GRF_BASE + GRF_UOC_CON);;//clear id fall irq pandding
+    }
+       rk28_send_wakeup_key();
+       return IRQ_HANDLED;
+}
+
+static int __init otg_id_irq_init(void)
+{
+       int ret;
+       int irq = IRQ_OTG0_ID;
+
+       ret = request_irq(irq, otg_id_irq_handler, 0, "otg_id_change", NULL);
+       if (ret < 0) {
+               pr_err("%s: request_irq(%d) failed\n", __func__, irq);
+               return ret;
+       }
+
+       /* clear & enable otg change irq */
+       /* for rk3026 enable and clear id_fall_irq & id_rise_irq*/
+       writel_relaxed((0xf << 16) | 0xf, RK2928_GRF_BASE + GRF_UOC_CON);
+
+       enable_irq_wake(irq);
+
+       return 0;
+}
+late_initcall(otg_id_irq_init);
+#endif
+
index ad920c705604d444d8d59949fb4fc823f56cdbaa..2a27dfe6ab8cdb821a7f9be714e578fa93e128bb 100755 (executable)
@@ -29,6 +29,6 @@ dwc_otg-objs    := dwc_otg_driver.o dwc_otg_attr.o
 dwc_otg-objs    += dwc_otg_cil.o dwc_otg_cil_intr.o
 dwc_otg-objs    += dwc_otg_pcd.o dwc_otg_pcd_intr.o
 dwc_otg-objs    += dwc_otg_hcd.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o
-dwc_otg-objs   += usbdev_rk30.o usbdev_rk2928.o
+dwc_otg-objs   += usbdev_rk30.o usbdev_rk2928.o usbdev_rk3026.o
 
 obj-$(CONFIG_DWC_OTG) := dwc_otg.o
diff --git a/drivers/usb/dwc_otg/usbdev_rk3026.c b/drivers/usb/dwc_otg/usbdev_rk3026.c
new file mode 100755 (executable)
index 0000000..e5ceae2
--- /dev/null
@@ -0,0 +1,418 @@
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/clk.h>
+
+#include <mach/irqs.h>
+#include <mach/gpio.h>
+#include <mach/iomux.h>
+#include <mach/cru.h>
+#ifdef CONFIG_RK_CONFIG
+#include <mach/config.h>
+#endif
+
+#include "usbdev_rk.h"
+#include "dwc_otg_regs.h"
+#ifdef CONFIG_ARCH_RK3026
+
+#define GRF_REG_BASE RK2928_GRF_BASE
+#define USBOTG_SIZE    RK2928_USBOTG20_SIZE
+
+#define USBGRF_SOC_STATUS0     (GRF_REG_BASE+0x14c)
+
+#define USBGRF_UOC0_CON0       (GRF_REG_BASE+0x17c)
+#define USBGRF_UOC1_CON0    (GRF_REG_BASE+0X190)
+#define USBGRF_UOC1_CON1       (GRF_REG_BASE+0x194)
+
+
+int dwc_otg_check_dpdm(void)
+{
+       static uint8_t * reg_base = 0;
+    volatile unsigned int * otg_dctl;
+    volatile unsigned int * otg_gotgctl;
+    volatile unsigned int * otg_hprt0;
+    int bus_status = 0;
+    unsigned int * otg_phy_con0 = (unsigned int*)(USBGRF_UOC0_CON0) ;
+    
+    *(unsigned int*)(RK2928_CRU_BASE+0x120) = ((7<<5)<<16)|(7<<5);    // otg0 phy clkgate
+    udelay(3);
+    *(unsigned int*)(RK2928_CRU_BASE+0x120) = ((7<<5)<<16)|(0<<5);    // otg0 phy clkgate
+    dsb();
+    *(unsigned int*)(RK2928_CRU_BASE+0xd4) = ((1<<5)<<16);    // otg0 phy clkgate
+    *(unsigned int*)(RK2928_CRU_BASE+0xe4) = ((1<<13)<<16);   // otg0 hclk clkgate
+    *(unsigned int*)(RK2928_CRU_BASE+0xf4) = ((3<<10)<<16);    // hclk usb clkgat
+   
+    // exit phy suspend 
+        *otg_phy_con0 = ((0x01<<0)<<16);  
+    
+    // soft connect
+    if(reg_base == 0){
+        reg_base = ioremap(RK2928_USBOTG20_PHYS,USBOTG_SIZE);
+        if(!reg_base){
+            bus_status = -1;
+            goto out;
+        }
+    }
+    mdelay(105);
+    //printk("regbase %p 0x%x, otg_phy_con%p, 0x%x\n",
+    //    reg_base, *(reg_base), otg_phy_con1, *otg_phy_con1);
+    otg_dctl = (unsigned int * )(reg_base+0x804);
+    otg_gotgctl = (unsigned int * )(reg_base);
+    otg_hprt0 = (unsigned int * )(reg_base + DWC_OTG_HOST_PORT_REGS_OFFSET);
+    if(*otg_gotgctl &(1<<19)){
+        bus_status = 1;
+        //*(unsigned int*)(GRF_REG_BASE + GRF_UOC0_CON0) = 0x10000000;//exit usbphy io hi-z state ***NO NEED FOR 
+        *otg_dctl &= ~(0x01<<1);//exit soft-disconnect mode
+        mdelay(1);    // delay about 1ms
+    // check dp,dm
+        if((*otg_hprt0 & 0xc00)==0xc00)//check hprt[11:10] 
+            bus_status = 2;
+        //*(unsigned int*)(GRF_REG_BASE + GRF_UOC0_CON0) = 0x10001000;
+    }
+out:
+    return bus_status;
+}
+
+EXPORT_SYMBOL(dwc_otg_check_dpdm);
+
+
+#ifdef CONFIG_USB20_OTG
+/*DWC_OTG*/
+static struct resource usb20_otg_resource[] = {
+       {
+               .start = IRQ_USB_OTG,
+               .end   = IRQ_USB_OTG,
+               .flags = IORESOURCE_IRQ,
+       },
+       {
+               .start = RK2928_USBOTG20_PHYS,
+               .end   = RK2928_USBOTG20_PHYS + RK2928_USBOTG20_SIZE - 1,
+               .flags = IORESOURCE_MEM,
+       },
+
+};
+
+void usb20otg_hw_init(void)
+{
+#ifndef CONFIG_USB20_HOST
+    // close USB 2.0 HOST phy and clock
+    unsigned int * otg_phy_con1 = (unsigned int*)(USBGRF_UOC1_CON1);
+    *otg_phy_con1 = 0x1D5 |(0x1ff<<16);   // enter suspend.
+#endif
+    // usb phy config init
+    
+    // other hardware init
+#ifdef CONFIG_RK_CONFIG
+    otg_drv_init(0);
+#else
+    rk30_mux_api_set(GPIO3_C1, 1);  
+#endif
+}
+void usb20otg_phy_suspend(void* pdata, int suspend)
+{
+    struct dwc_otg_platform_data *usbpdata=pdata;
+    unsigned int * otg_phy_con1 = (unsigned int*)(USBGRF_UOC0_CON0);
+    if(suspend){
+        *otg_phy_con1 = 0x55 |(0x7f<<16);   // enter suspend.
+        usbpdata->phy_status = 1;
+    }
+    else{
+        *otg_phy_con1 = (0x01<<16);    // exit suspend.
+        usbpdata->phy_status = 0;
+    }
+}
+void usb20otg_soft_reset(void)
+{
+#if 0 //@lyz todo for 3028 phy
+    printk("~~~~~~~~~~usb20otg_soft_reset\n");
+    //phy reset
+    *(unsigned int*)(USBGRF_UOC0_CON0) = 0x00030001;
+    *(unsigned int*)(USBGRF_UOC1_CON1) = 0x00030001;
+
+
+    cru_set_soft_reset(SOFT_RST_USBPOR, true);
+
+    cru_set_soft_reset(SOFT_RST_UTMI0, true);
+    cru_set_soft_reset(SOFT_RST_UTMI1, true);
+
+    udelay(15);
+    
+    *(unsigned int*)(USBGRF_UOC0_CON0) = 0x00030002; 
+    *(unsigned int*)(USBGRF_UOC1_CON1) = 0x00030002;
+
+    udelay(1500);
+    cru_set_soft_reset(SOFT_RST_USBPOR, false);
+    udelay(2);
+    cru_set_soft_reset(SOFT_RST_UTMI0, false);
+    cru_set_soft_reset(SOFT_RST_UTMI1, false);
+
+    //ctrler reset
+    cru_set_soft_reset(SOFT_RST_OTGC0, true);
+    cru_set_soft_reset(SOFT_RST_OTGC1, true);
+    udelay(2);
+
+    cru_set_soft_reset(SOFT_RST_USBOTG0, true);
+    cru_set_soft_reset(SOFT_RST_USBOTG1, true);
+    udelay(2);
+    
+    cru_set_soft_reset(SOFT_RST_OTGC0,false);
+    cru_set_soft_reset(SOFT_RST_OTGC1,false);
+    cru_set_soft_reset(SOFT_RST_USBOTG0,false);
+    cru_set_soft_reset(SOFT_RST_USBOTG1,false);
+#endif
+}
+void usb20otg_clock_init(void* pdata)
+{
+    struct dwc_otg_platform_data *usbpdata=pdata;
+    struct clk* ahbclk,*phyclk;
+    ahbclk = clk_get(NULL, "hclk_otg0");
+    phyclk = clk_get(NULL, "otgphy0");
+       usbpdata->phyclk = phyclk;
+       usbpdata->ahbclk = ahbclk;
+}
+void usb20otg_clock_enable(void* pdata, int enable)
+{
+    struct dwc_otg_platform_data *usbpdata=pdata;
+    #if 1
+    if(enable){
+        clk_enable(usbpdata->ahbclk);
+        clk_enable(usbpdata->phyclk);
+    }
+    else{
+   // clk_disable(usbpdata->phyclk);   /* otg/host20 use the same phyclk, so can't disable phyclk in case host20 is used.*/ 
+        clk_disable(usbpdata->ahbclk);    
+    }
+    #endif
+}
+int usb20otg_get_status(int id)
+{
+    int ret = -1;
+    unsigned int usbgrf_status = *(unsigned int*)(USBGRF_SOC_STATUS0);
+    unsigned int uoc1_con0 = *(unsigned int*)(USBGRF_UOC1_CON0);
+    switch(id)
+    {
+        case USB_STATUS_BVABLID:
+            // bvalid in grf
+            ret = (usbgrf_status &(1<<7));
+            break;
+        case USB_STATUS_DPDM:
+            // dpdm in grf
+            ret = (usbgrf_status &(3<<8));
+            break;
+        case USB_STATUS_ID:
+            // id in grf
+            ret = (usbgrf_status &(1<<10));
+            break;
+        case USB_STATUS_UARTMODE:
+            // usb_uart_mode in grf
+            ret = (uoc1_con0 &(1<<13));
+        default:
+            break;
+    }
+    return ret;
+}
+void dwc_otg_uart_mode(void* pdata, int enter_usb_uart_mode)
+{
+#ifdef CONFIG_RK_USB_UART
+    //struct dwc_otg_platform_data *usbpdata=pdata;//1:uart 0:usb
+    unsigned int * otg_phy_con0 = (unsigned int*)(USBGRF_UOC1_CON0);
+    //printk("usb_uart_mode = %d,enter_usb_uart_mode = %d\n",otg_phy_con1,enter_usb_uart_mode);
+    if(1 == enter_usb_uart_mode)   //uart mode
+    {
+        *otg_phy_con0 = (0x03 << 12 | (0x03<<(16+12)));//bypass dm
+        //printk("phy enter uart mode USBGRF_UOC1_CON0 = %08x\n",*otg_phy_con1);
+        
+    }
+    if(0 == enter_usb_uart_mode)   //usb mode
+    {   
+        *otg_phy_con0 = (0x03<<(12+16)); //bypass dm disable 
+        //printk("phy enter usb mode USBGRF_UOC1_CON0 = %8x\n",*otg_phy_con1);
+    }
+#endif
+}
+
+void usb20otg_power_enable(int enable)
+{
+#ifdef CONFIG_RK_CONFIG
+        if(enable)
+                otg_drv_on();
+        else
+                otg_drv_off();
+#endif
+}
+struct dwc_otg_platform_data usb20otg_pdata = {
+    .phyclk = NULL,
+    .ahbclk = NULL,
+    .busclk = NULL,
+    .phy_status = 0,
+    .hw_init=usb20otg_hw_init,
+    .phy_suspend=usb20otg_phy_suspend,
+    .soft_reset=usb20otg_soft_reset,
+    .clock_init=usb20otg_clock_init,
+    .clock_enable=usb20otg_clock_enable,
+    .get_status=usb20otg_get_status,
+    .dwc_otg_uart_mode=dwc_otg_uart_mode,
+};
+
+struct platform_device device_usb20_otg = {
+       .name             = "usb20_otg",
+       .id               = -1,
+       .num_resources    = ARRAY_SIZE(usb20_otg_resource),
+       .resource         = usb20_otg_resource,
+       .dev            = {
+               .platform_data  = &usb20otg_pdata,
+       },
+};
+#endif
+#ifdef CONFIG_USB20_HOST
+static struct resource usb20_host_resource[] = {
+    {
+        .start = IRQ_USB_HOST,
+        .end   = IRQ_USB_HOST,
+        .flags = IORESOURCE_IRQ,
+    },
+    {
+        .start = RK2928_USBHOST20_PHYS,
+        .end   = RK2928_USBHOST20_PHYS + RK2928_USBHOST20_SIZE - 1,
+        .flags = IORESOURCE_MEM,
+    },
+
+};
+
+void usb20host_hw_init(void)
+{
+    // usb phy config init
+
+    // other haredware init
+#ifdef CONFIG_RK_CONFIG
+    host_drv_init(1);
+#endif
+}
+void usb20host_phy_suspend(void* pdata, int suspend)
+{
+    struct dwc_otg_platform_data *usbpdata=pdata;
+    unsigned int * otg_phy_con1 = (unsigned int*)(USBGRF_UOC1_CON1);
+
+    if(suspend){
+        //*otg_phy_con2 = (1 << 12 | 1 << (12+16));//host io set to High-Z state ***NO NEED FOR 3026
+        *otg_phy_con1 = 0x1D5 |(0x1ff<<16);   // enter suspend.
+        usbpdata->phy_status = 1;
+    }
+    else{
+        //*otg_phy_con2 = (1 << 12+16);//host io exit High-Z state ***NO NEED FOR 3026
+        *otg_phy_con1 = (0x01<<16);    // exit suspend.
+        usbpdata->phy_status = 0;
+    }
+}
+void usb20host_soft_reset(void)
+{
+#if 0
+    cru_set_soft_reset(SOFT_RST_USBOTG1, true);
+    //cru_set_soft_reset(SOFT_RST_USBPHY1, true);
+    cru_set_soft_reset(SOFT_RST_OTGC1, true);
+    
+    udelay(1);
+
+    cru_set_soft_reset(SOFT_RST_USBOTG1, false);
+    //cru_set_soft_reset(SOFT_RST_USBPHY1, false);
+    cru_set_soft_reset(SOFT_RST_OTGC1, false);
+    mdelay(1);
+#endif
+}
+void usb20host_clock_init(void* pdata)
+{
+    struct dwc_otg_platform_data *usbpdata=pdata;
+    struct clk* ahbclk,*phyclk;
+    ahbclk = clk_get(NULL, "hclk_otg1");
+    phyclk = clk_get(NULL, "otgphy1");
+       usbpdata->phyclk = phyclk;
+       usbpdata->ahbclk = ahbclk;
+}
+void usb20host_clock_enable(void* pdata, int enable)
+{
+    struct dwc_otg_platform_data *usbpdata=pdata;
+    #if 1
+    if(enable){
+        clk_enable(usbpdata->ahbclk);
+        clk_enable(usbpdata->phyclk);
+    }
+    else{
+        clk_disable(usbpdata->phyclk);
+        clk_disable(usbpdata->ahbclk);
+    }
+    #endif
+}
+int usb20host_get_status(int id)
+{
+    int ret = -1;
+    unsigned int usbgrf_status = *(unsigned int*)(USBGRF_SOC_STATUS0);
+    switch(id)
+    {
+        case USB_STATUS_BVABLID:
+            // bvalid in grf
+            ret = (usbgrf_status &(1<<12));
+            break;
+        case USB_STATUS_DPDM:
+            // dpdm in grf
+            ret = (usbgrf_status &(3<<13));
+            break;
+        case USB_STATUS_ID:
+            // id in grf
+            ret = 0;
+            break;
+        default:
+            break;
+    }
+    return ret;
+}
+void usb20host_power_enable(int enable)
+{
+#ifdef CONFIG_RK_CONFIG
+        if(enable)
+                host_drv_on();
+        else
+                host_drv_off();
+#endif
+}
+struct dwc_otg_platform_data usb20host_pdata = {
+    .phyclk = NULL,
+    .ahbclk = NULL,
+    .busclk = NULL,
+    .phy_status = 0,
+    .hw_init=usb20host_hw_init,
+    .phy_suspend=usb20host_phy_suspend,
+    .soft_reset=usb20host_soft_reset,
+    .clock_init=usb20host_clock_init,
+    .clock_enable=usb20host_clock_enable,
+    .get_status=usb20host_get_status,
+};
+
+struct platform_device device_usb20_host = {
+    .name             = "usb20_host",
+    .id               = -1,
+    .num_resources    = ARRAY_SIZE(usb20_host_resource),
+    .resource         = usb20_host_resource,
+    .dev               = {
+               .platform_data  = &usb20host_pdata,
+       },
+};
+#endif
+static int __init usbdev_init_devices(void)
+{
+        int ret = 0;
+#ifdef CONFIG_USB20_OTG
+       ret = platform_device_register(&device_usb20_otg);
+        if(ret < 0){
+                printk("%s: platform_device_register(usb20_otg) failed\n", __func__);
+                return ret;
+        }
+#endif
+#ifdef CONFIG_USB20_HOST
+       ret = platform_device_register(&device_usb20_host);
+#endif
+        return ret;
+}
+arch_initcall(usbdev_init_devices);
+#endif
+