From 89f9ce3ace72b4333b43a98702d11f8acc628b82 Mon Sep 17 00:00:00 2001 From: lyz Date: Mon, 22 Jul 2013 19:16:32 +0800 Subject: [PATCH] usb: support for rk3026 --- arch/arm/plat-rk/usb_detect.c | 49 ++++ drivers/usb/dwc_otg/Makefile | 2 +- drivers/usb/dwc_otg/usbdev_rk3026.c | 418 ++++++++++++++++++++++++++++ 3 files changed, 468 insertions(+), 1 deletion(-) create mode 100755 drivers/usb/dwc_otg/usbdev_rk3026.c diff --git a/arch/arm/plat-rk/usb_detect.c b/arch/arm/plat-rk/usb_detect.c index 2671071a3f6b..3a0acd658ae8 100755 --- a/arch/arm/plat-rk/usb_detect.c +++ b/arch/arm/plat-rk/usb_detect.c @@ -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 +#include + +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 + diff --git a/drivers/usb/dwc_otg/Makefile b/drivers/usb/dwc_otg/Makefile index ad920c705604..2a27dfe6ab8c 100755 --- a/drivers/usb/dwc_otg/Makefile +++ b/drivers/usb/dwc_otg/Makefile @@ -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 index 000000000000..e5ceae23985c --- /dev/null +++ b/drivers/usb/dwc_otg/usbdev_rk3026.c @@ -0,0 +1,418 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#ifdef CONFIG_RK_CONFIG +#include +#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 + -- 2.34.1