From: lyz Date: Fri, 4 Jul 2014 03:56:19 +0000 (+0800) Subject: 3036: usb: add usb support X-Git-Tag: firefly_0821_release~4916^2~303 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=fc8d8215ceb2b9f679e7326a69c04d8b08c51578;p=firefly-linux-kernel-4.4.55.git 3036: usb: add usb support --- diff --git a/arch/arm/boot/dts/rk3036.dtsi b/arch/arm/boot/dts/rk3036.dtsi old mode 100644 new mode 100755 index a971bb204e66..836becc4e820 --- a/arch/arm/boot/dts/rk3036.dtsi +++ b/arch/arm/boot/dts/rk3036.dtsi @@ -361,5 +361,43 @@ "Mali_PP0_IRQ", "Mali_PP0_MMU_IRQ"; }; + dwc_control_usb: dwc-control-usb@20008000 { + compatible = "rockchip,rk3188-dwc-control-usb"; + interrupts = ; + interrupt-names = "otg_bvalid"; + //gpios = <&gpio0 GPIO_C0 GPIO_ACTIVE_LOW>, <&gpio3 GPIO_D5 GPIO_ACTIVE_LOW>; + clocks = <&clk_gates9 13>; + clock-names = "hclk_usb_peri"; + rockchip,remote_wakeup; + rockchip,usb_irq_wakeup; + + usb_bc{ + compatible = "rockchip,ctrl"; + rk_usb,bvalid = <0x14c 8 1>; + rk_usb,iddig = <0x14c 11 1>; + rk_usb,line = <0x14c 9 2>; + rk_usb,softctrl = <0x17c 0 1>; + rk_usb,opmode = <0x17c 2 2>; + rk_usb,xcvrsel = <0x17c 4 2>; + rk_usb,termsel = <0x118 6 1>; + }; + }; + usb0: usb@10180000 { + compatible = "rockchip,rk3188_usb20_otg"; + reg = <0x10180000 0x40000>; + interrupts = ; + clocks = <&clk_gates1 5>, <&clk_gates5 13>; + clock-names = "clk_usbphy0", "hclk_usb0"; + /*0 - Normal, 1 - Force Host, 2 - Force Device*/ + rockchip,usb-mode = <0>; + }; + + usb1: usb@101c0000 { + compatible = "rockchip,rk3188_usb20_host"; + reg = <0x101c0000 0x40000>; + interrupts = ; + clocks = <&clk_gates1 6>, <&clk_gates7 3>; + clock-names = "clk_usbphy1", "hclk_usb1"; + }; }; diff --git a/arch/arm/mach-rockchip/rk3036.c b/arch/arm/mach-rockchip/rk3036.c old mode 100644 new mode 100755 index 3dcd886fdf74..bba0701c6379 --- a/arch/arm/mach-rockchip/rk3036.c +++ b/arch/arm/mach-rockchip/rk3036.c @@ -86,7 +86,24 @@ static void __init rk3036_boot_mode_init(void) static void usb_uart_init(void) { - return; + u32 soc_status0 = readl_relaxed(RK_GRF_VIRT + RK3036_GRF_SOC_STATUS0); + writel_relaxed(0x34000000, RK_GRF_VIRT + RK3036_GRF_UOC1_CON4); +#ifdef CONFIG_RK_USB_UART + if (!(soc_status0 & (1 << 14)) && (soc_status0 & (1 << 17))) { + /* software control usb phy enable */ + writel_relaxed(0x007f0055, RK_GRF_VIRT + RK3036_GRF_UOC0_CON5); + writel_relaxed(0x34003000, RK_GRF_VIRT + RK3036_GRF_UOC1_CON4); + } +#endif +#ifdef RK_DEBUG_UART_VIRT + writel_relaxed(0x07, RK_DEBUG_UART_VIRT + 0x88); + writel_relaxed(0x07, RK_DEBUG_UART_VIRT + 0x88); + writel_relaxed(0x00, RK_DEBUG_UART_VIRT + 0x04); + writel_relaxed(0x83, RK_DEBUG_UART_VIRT + 0x0c); + writel_relaxed(0x0d, RK_DEBUG_UART_VIRT + 0x00); + writel_relaxed(0x00, RK_DEBUG_UART_VIRT + 0x04); + writel_relaxed(0x03, RK_DEBUG_UART_VIRT + 0x0c); +#endif //end of DEBUG_UART_BASE } static void __init rk3036_dt_map_io(void) diff --git a/drivers/usb/dwc_otg_310/Makefile b/drivers/usb/dwc_otg_310/Makefile index 9af238ce165b..f5dd7ae364c9 100755 --- a/drivers/usb/dwc_otg_310/Makefile +++ b/drivers/usb/dwc_otg_310/Makefile @@ -25,5 +25,5 @@ endif dwc_otg-objs += common_port/dwc_common_linux.o #objs relative to RK platform -dwc_otg-objs += usbdev_rk30.o usbdev_rk32.o usbdev_bc.o +dwc_otg-objs += usbdev_rk30.o usbdev_rk32.o usbdev_rk3036.o usbdev_bc.o obj-$(CONFIG_DWC_OTG_310) := dwc_otg.o diff --git a/drivers/usb/dwc_otg_310/dwc_otg_driver.c b/drivers/usb/dwc_otg_310/dwc_otg_driver.c index 74deafef62f1..5b9ceb1f94d9 100755 --- a/drivers/usb/dwc_otg_310/dwc_otg_driver.c +++ b/drivers/usb/dwc_otg_310/dwc_otg_driver.c @@ -86,6 +86,10 @@ static struct usb20otg_pdata_id usb20otg_pdata[] = { .name = "rk3288-usb20otg", .pdata = &usb20otg_pdata_rk3288, }, + { + .name = "rk3036-usb20otg", + .pdata = &usb20otg_pdata_rk3036, + }, {}, }; #endif @@ -100,6 +104,10 @@ static struct usb20host_pdata_id usb20host_pdata[] = { .name = "rk3288-usb20host", .pdata = &usb20host_pdata_rk3288, }, + { + .name = "rk3288-usb20host", + .pdata = &usb20host_pdata_rk3036, + }, {}, }; #endif @@ -972,6 +980,10 @@ static const struct of_device_id usb20_host_of_match[] = { .compatible = "rockchip,rk3288_usb20_host", .data = &usb20host_pdata[RK3288_USB_CTLR], }, + { + .compatible = "rockchip,rk3036_usb20_host", + .data = &usb20host_pdata[RK3036_USB_CTLR], + }, {}, }; @@ -1326,6 +1338,10 @@ static const struct of_device_id usb20_otg_of_match[] = { .compatible = "rockchip,rk3288_usb20_otg", .data = &usb20otg_pdata[RK3288_USB_CTLR], }, + { + .compatible = "rockchip,rk3036_usb20_otg", + .data = &usb20otg_pdata[RK3036_USB_CTLR], + }, { }, }; diff --git a/drivers/usb/dwc_otg_310/usbdev_grf_regs.h b/drivers/usb/dwc_otg_310/usbdev_grf_regs.h old mode 100644 new mode 100755 index 319581bf803f..8b4a72748b6f --- a/drivers/usb/dwc_otg_310/usbdev_grf_regs.h +++ b/drivers/usb/dwc_otg_310/usbdev_grf_regs.h @@ -8,6 +8,7 @@ typedef volatile struct tag_grf_uoc0_reg { u32 CON2; u32 CON3; u32 CON4; + u32 CON5; } GRF_UOC0_REG, *pGRF_UOC0_REG; typedef volatile struct tag_grf_uoc1_reg { @@ -20,6 +21,7 @@ typedef volatile struct tag_grf_uoc1_reg { u32 CON2; u32 CON3; u32 CON4; + u32 CON5; } GRF_UOC1_REG, *pGRF_UOC1_REG; typedef volatile struct tag_grf_uoc2_reg { diff --git a/drivers/usb/dwc_otg_310/usbdev_rk.h b/drivers/usb/dwc_otg_310/usbdev_rk.h index f79adac1563f..8f4389e10a85 100755 --- a/drivers/usb/dwc_otg_310/usbdev_rk.h +++ b/drivers/usb/dwc_otg_310/usbdev_rk.h @@ -21,6 +21,7 @@ #include #include #include +#include #include "usbdev_grf_regs.h" #include "usbdev_bc.h" @@ -40,6 +41,9 @@ #define USB_REMOTE_WAKEUP (6) #define USB_IRQ_WAKEUP (7) +#define UOC_HIWORD_UPDATE(val, mask, shift) \ + ((val) << (shift) | (mask) << ((shift) + 16)) + extern int rk_usb_charger_status; extern void rk_send_wakeup_key(void); /* rk3188 platform data */ @@ -52,6 +56,9 @@ extern struct dwc_otg_platform_data usb20host_pdata_rk3288; extern struct rkehci_platform_data rkhsic_pdata_rk3288; extern struct rkehci_platform_data rkehci_pdata_rk3288; extern struct rkehci_platform_data rkohci_pdata_rk3288; +/* rk3036 platform data */ +extern struct dwc_otg_platform_data usb20otg_pdata_rk3036; +extern struct dwc_otg_platform_data usb20host_pdata_rk3036; struct dwc_otg_platform_data { void *privdata; @@ -61,15 +68,16 @@ struct dwc_otg_platform_data { struct clk *busclk; struct clk *phyclk_480m; int phy_status; - void (*hw_init) (void); - void (*phy_suspend) (void *pdata, int suspend); - void (*soft_reset) (void); - void (*clock_init) (void *pdata); - void (*clock_enable) (void *pdata, int enable); - void (*power_enable) (int enable); - void (*dwc_otg_uart_mode) (void *pdata, int enter_usb_uart_mode); - void (*bc_detect_cb) (int bc_type); - int (*get_status) (int id); + + void (*hw_init)(void); + void (*phy_suspend)(void *pdata, int suspend); + void (*soft_reset)(void); + void (*clock_init)(void *pdata); + void (*clock_enable)(void *pdata, int enable); + void (*power_enable)(int enable); + void (*dwc_otg_uart_mode)(void *pdata, int enter_usb_uart_mode); + void (*bc_detect_cb)(int bc_type); + int (*get_status)(int id); }; struct rkehci_platform_data { @@ -79,12 +87,13 @@ struct rkehci_platform_data { struct clk *hsic_phy_12m; struct clk *phyclk; struct clk *ahbclk; - void (*hw_init) (void); - void (*clock_init) (void *pdata); - void (*clock_enable) (void *pdata, int enable); - void (*phy_suspend) (void *pdata, int suspend); - void (*soft_reset) (void); - int (*get_status) (int id); + + void (*hw_init)(void); + void (*clock_init)(void *pdata); + void (*clock_enable)(void *pdata, int enable); + void (*phy_suspend)(void *pdata, int suspend); + void (*soft_reset)(void); + int (*get_status)(int id); int clk_status; int phy_status; }; @@ -100,6 +109,7 @@ struct dwc_otg_control_usb { pGRF_SOC_STATUS2_RK3288 grf_soc_status2_rk3288; pGRF_SOC_STATUS19_RK3288 grf_soc_status19_rk3288; pGRF_SOC_STATUS21_RK3288 grf_soc_status21_rk3288; + struct gpio *host_gpios; struct gpio *otg_gpios; struct clk *hclk_usb_peri; @@ -114,6 +124,7 @@ struct dwc_otg_control_usb { enum { RK3188_USB_CTLR = 0, /* rk3188 chip usb */ RK3288_USB_CTLR, /* rk3288 chip usb */ + RK3036_USB_CTLR, /* rk3036 chip usb */ }; struct usb20otg_pdata_id { diff --git a/drivers/usb/dwc_otg_310/usbdev_rk3036.c b/drivers/usb/dwc_otg_310/usbdev_rk3036.c new file mode 100755 index 000000000000..d94e8d2611b6 --- /dev/null +++ b/drivers/usb/dwc_otg_310/usbdev_rk3036.c @@ -0,0 +1,598 @@ +#include "usbdev_rk.h" +#include "usbdev_grf_regs.h" +#include "dwc_otg_regs.h" +static struct dwc_otg_control_usb *control_usb; + +#ifdef CONFIG_USB20_OTG +static void usb20otg_hw_init(void) +{ + /* other haredware init,include: + * DRV_VBUS GPIO init */ + if (gpio_is_valid(control_usb->otg_gpios->gpio)) { + if (gpio_get_value(control_usb->otg_gpios->gpio)) + gpio_set_value(control_usb->otg_gpios->gpio, 0); + } +} + +static void usb20otg_phy_suspend(void *pdata, int suspend) +{ + struct dwc_otg_platform_data *usbpdata = pdata; + + if (suspend) { + /* enable soft control */ + writel(UOC_HIWORD_UPDATE(0x55, 0x7f, 0), + RK_GRF_VIRT + RK3036_GRF_UOC0_CON5); + usbpdata->phy_status = 1; + } else { + /* exit suspend */ + writel(UOC_HIWORD_UPDATE(0x0, 0x1, 0), + RK_GRF_VIRT + RK3036_GRF_UOC0_CON5); + usbpdata->phy_status = 0; + } +} + +static void usb20otg_soft_reset(void) +{ + writel(UOC_HIWORD_UPDATE(0x1, 0x3, 0), + RK_GRF_VIRT + RK3036_GRF_UOC0_CON5); + writel(UOC_HIWORD_UPDATE(0x1, 0x3, 0), + RK_GRF_VIRT + RK3036_GRF_UOC1_CON5); + /* cru_set_soft_reset(SOFT_RST_USBPOR, true); */ + writel(UOC_HIWORD_UPDATE(0x1, 0x1, 9), + RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON); + /* cru_set_soft_reset(SOFT_RST_UTMI0, true); */ + writel(UOC_HIWORD_UPDATE(0x1, 0x1, 7), + RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON); + /* cru_set_soft_reset(SOFT_RST_UTMI1, true); */ + writel(UOC_HIWORD_UPDATE(0x1, 0x1, 8), + RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON); + + udelay(15); + + writel(UOC_HIWORD_UPDATE(0x2, 0x3, 0), + RK_GRF_VIRT + RK3036_GRF_UOC0_CON5); + writel(UOC_HIWORD_UPDATE(0x2, 0x3, 0), + RK_GRF_VIRT + RK3036_GRF_UOC1_CON5); + + udelay(1500); + /* cru_set_soft_reset(SOFT_RST_USBPOR, false); */ + writel(UOC_HIWORD_UPDATE(0x0, 0x1, 9), + RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON); + udelay(2); + /* cru_set_soft_reset(SOFT_RST_UTMI0, false); */ + writel(UOC_HIWORD_UPDATE(0x0, 0x1, 7), + RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON); + /* cru_set_soft_reset(SOFT_RST_UTMI1, false); */ + writel(UOC_HIWORD_UPDATE(0x0, 0x1, 8), + RK_CRU_VIRT + RK3036_CRU_SOFTRST6_CON); + + /* ctrler reset */ + /* cru_set_soft_reset(SOFT_RST_OTGC0, true); */ + writel(UOC_HIWORD_UPDATE(0x1, 0x1, 7), + RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON); + /* cru_set_soft_reset(SOFT_RST_OTGC1, true); */ + writel(UOC_HIWORD_UPDATE(0x1, 0x1, 10), + RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON); + udelay(2); + + /* cru_set_soft_reset(SOFT_RST_USBOTG0, true); */ + writel(UOC_HIWORD_UPDATE(0x1, 0x1, 5), + RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON); + /* cru_set_soft_reset(SOFT_RST_USBOTG1, true); */ + writel(UOC_HIWORD_UPDATE(0x1, 0x1, 8), + RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON); + udelay(2); + /* + cru_set_soft_reset(SOFT_RST_OTGC0,false); + cru_set_soft_reset(SOFT_RST_OTGC1,false); + ru_set_soft_reset(SOFT_RST_USBOTG0,false); + cru_set_soft_reset(SOFT_RST_USBOTG1,false); + */ + writel(UOC_HIWORD_UPDATE(0x0, 0x1, 7), + RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON); + writel(UOC_HIWORD_UPDATE(0x0, 0x1, 10), + RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON); + writel(UOC_HIWORD_UPDATE(0x0, 0x1, 5), + RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON); + writel(UOC_HIWORD_UPDATE(0x0, 0x1, 8), + RK_CRU_VIRT + RK3036_CRU_SOFTRST4_CON); +} + +static void usb20otg_clock_init(void *pdata) +{ + struct dwc_otg_platform_data *usbpdata = pdata; + struct clk *ahbclk, *phyclk; + + ahbclk = devm_clk_get(usbpdata->dev, "hclk_usb0"); + if (IS_ERR(ahbclk)) { + dev_err(usbpdata->dev, "Failed to get hclk_usb0\n"); + return; + } + + phyclk = devm_clk_get(usbpdata->dev, "clk_usbphy0"); + if (IS_ERR(phyclk)) { + dev_err(usbpdata->dev, "Failed to get clk_usbphy0\n"); + return; + } + + usbpdata->phyclk = phyclk; + usbpdata->ahbclk = ahbclk; +} + +static void usb20otg_clock_enable(void *pdata, int enable) +{ + struct dwc_otg_platform_data *usbpdata = pdata; + + if (enable) { + clk_prepare_enable(usbpdata->ahbclk); + clk_prepare_enable(usbpdata->phyclk); + } else { + clk_disable_unprepare(usbpdata->ahbclk); + /* + clk_disable_unprepare(usbpdata->phyclk); + */ + } +} + +static int usb20otg_get_status(int id) +{ + int ret = -1; + u32 soc_status0 = readl(RK_GRF_VIRT + RK3036_GRF_CPU_STATUS0); + + switch (id) { + case USB_STATUS_BVABLID: + /* bvalid in grf */ + ret = soc_status0 & (0x1 << 8); + break; + case USB_STATUS_DPDM: + /* dpdm in grf */ + ret = soc_status0 & (0x3 << 9); + break; + case USB_STATUS_ID: + /* id in grf */ + ret = soc_status0 & (0x1 << 11); + break; + case USB_CHIP_ID: + ret = control_usb->chip_id; + break; + case USB_REMOTE_WAKEUP: + ret = control_usb->remote_wakeup; + break; + case USB_IRQ_WAKEUP: + ret = control_usb->usb_irq_wakeup; + break; + default: + break; + } + return ret; +} + +static void dwc_otg_uart_mode(void *pdata, int enter_usb_uart_mode) +{ +#ifdef CONFIG_RK_USB_UART + if (1 == enter_usb_uart_mode) { + /* bypass dm, enter uart mode */ + writel(UOC_HIWORD_UPDATE(0x3, 0x3, 12), + RK_GRF_VIRT + RK3036_GRF_UOC1_CON4); + } else if (0 == enter_usb_uart_mode) { + /* enter usb mode */ + writel(UOC_HIWORD_UPDATE(0x0, 0x3, 12), + RK_GRF_VIRT + RK3036_GRF_UOC1_CON4); + } +#endif +} + +static void usb20otg_power_enable(int enable) +{ + if (0 == enable) { + /* disable otg_drv power */ + if (gpio_is_valid(control_usb->otg_gpios->gpio)) + gpio_set_value(control_usb->otg_gpios->gpio, 0); + } else if (1 == enable) { + /* enable otg_drv power */ + if (gpio_is_valid(control_usb->otg_gpios->gpio)) + gpio_set_value(control_usb->otg_gpios->gpio, 1); + } +} + +struct dwc_otg_platform_data usb20otg_pdata_rk3036 = { + .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, + .power_enable = usb20otg_power_enable, + .dwc_otg_uart_mode = dwc_otg_uart_mode, + .bc_detect_cb = usb20otg_battery_charger_detect_cb, +}; +#endif + +#ifdef CONFIG_USB20_HOST +static void usb20host_hw_init(void) +{ + /* other haredware init,include: + * DRV_VBUS GPIO init */ + if (gpio_is_valid(control_usb->host_gpios->gpio)) { + if (!gpio_get_value(control_usb->host_gpios->gpio)) + gpio_set_value(control_usb->host_gpios->gpio, 1); + } +} + +static void usb20host_phy_suspend(void *pdata, int suspend) +{ + struct dwc_otg_platform_data *usbpdata = pdata; + + if (suspend) { + /* enable soft control */ + writel(UOC_HIWORD_UPDATE(0x1d5, 0x1ff, 0), + RK_GRF_VIRT + RK3036_GRF_UOC1_CON5); + usbpdata->phy_status = 1; + } else { + /* exit suspend */ + writel(UOC_HIWORD_UPDATE(0x0, 0x1, 0), + RK_GRF_VIRT + RK3036_GRF_UOC1_CON5); + usbpdata->phy_status = 0; + } +} + +static void usb20host_soft_reset(void) +{ + ; +} + +static void usb20host_clock_init(void *pdata) +{ + struct dwc_otg_platform_data *usbpdata = pdata; + struct clk *ahbclk, *phyclk; + + ahbclk = devm_clk_get(usbpdata->dev, "hclk_usb1"); + if (IS_ERR(ahbclk)) { + dev_err(usbpdata->dev, "Failed to get hclk_usb1\n"); + return; + } + + phyclk = devm_clk_get(usbpdata->dev, "clk_usbphy1"); + if (IS_ERR(phyclk)) { + dev_err(usbpdata->dev, "Failed to get clk_usbphy1\n"); + return; + } + + usbpdata->phyclk = phyclk; + usbpdata->ahbclk = ahbclk; +} + +static void usb20host_clock_enable(void *pdata, int enable) +{ + struct dwc_otg_platform_data *usbpdata = pdata; + + if (enable) { + clk_prepare_enable(usbpdata->ahbclk); + clk_prepare_enable(usbpdata->phyclk); + } else { + clk_disable_unprepare(usbpdata->ahbclk); + clk_disable_unprepare(usbpdata->phyclk); + } +} + +static int usb20host_get_status(int id) +{ + int ret = -1; + u32 soc_status0 = readl(RK_GRF_VIRT + RK3036_GRF_CPU_STATUS0); + + switch (id) { + case USB_STATUS_BVABLID: + /* bvalid in grf */ + ret = soc_status0 & (0x1 << 13); + break; + case USB_STATUS_DPDM: + /* dpdm in grf */ + ret = soc_status0 & (0x3 << 14); + break; + case USB_STATUS_ID: + /* id in grf */ + ret = 0; + break; + case USB_CHIP_ID: + ret = control_usb->chip_id; + break; + case USB_REMOTE_WAKEUP: + ret = control_usb->remote_wakeup; + break; + case USB_IRQ_WAKEUP: + ret = control_usb->usb_irq_wakeup; + break; + default: + break; + } + return ret; +} + +static void usb20host_power_enable(int enable) +{ + if (0 == enable) { + /* disable host_drv power */ + /* do not disable power in default */ + } else if (1 == enable) { + /* enable host_drv power */ + if (gpio_is_valid(control_usb->host_gpios->gpio)) + gpio_set_value(control_usb->host_gpios->gpio, 1); + } +} + +struct dwc_otg_platform_data usb20host_pdata_rk3036 = { + .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, + .power_enable = usb20host_power_enable, +}; +#endif + +#ifdef CONFIG_OF +static const struct of_device_id rk_usb_control_id_table[] = { + { + .compatible = "rockchip,rk3036-usb-control", + }, + {}, +}; +#endif +/********************************************************************* + rk3036 usb detections +*********************************************************************/ + +#define WAKE_LOCK_TIMEOUT (HZ * 10) +static inline void do_wakeup(struct work_struct *work) +{ + /* wake up the system */ + rk_send_wakeup_key(); +} + +static void usb_battery_charger_detect_work(struct work_struct *work) +{ + rk_usb_charger_status = usb_battery_charger_detect(0); +} + +/********** handler for bvalid irq **********/ +static irqreturn_t bvalid_irq_handler(int irq, void *dev_id) +{ + /* clear irq */ + writel(UOC_HIWORD_UPDATE(0x1, 0x1, 15), + RK_GRF_VIRT + RK3036_GRF_UOC0_CON5); +#ifdef CONFIG_RK_USB_UART + /* usb otg dp/dm switch to usb phy */ + dwc_otg_uart_mode(NULL, PHY_USB_MODE); +#endif + + if (control_usb->usb_irq_wakeup) { + wake_lock_timeout(&control_usb->usb_wakelock, + WAKE_LOCK_TIMEOUT); + schedule_delayed_work(&control_usb->usb_det_wakeup_work, + HZ / 10); + } + + rk_usb_charger_status = USB_BC_TYPE_SDP; + schedule_delayed_work(&control_usb->usb_charger_det_work, HZ / 10); + + return IRQ_HANDLED; +} + +/************* register usb detection irqs **************/ +static int otg_irq_detect_init(struct platform_device *pdev) +{ + int ret = 0; + int irq = 0; + + if (control_usb->usb_irq_wakeup) { + wake_lock_init(&control_usb->usb_wakelock, WAKE_LOCK_SUSPEND, + "usb_detect"); + INIT_DELAYED_WORK(&control_usb->usb_det_wakeup_work, do_wakeup); + } + + /*register otg_bvalid irq */ + irq = platform_get_irq_byname(pdev, "otg_bvalid"); + if ((irq > 0) && control_usb->usb_irq_wakeup) { + ret = request_irq(irq, bvalid_irq_handler, + 0, "otg_bvalid", NULL); + if (ret < 0) { + dev_err(&pdev->dev, "request_irq %d failed!\n", irq); + } else { + /* enable bvalid irq */ + writel(UOC_HIWORD_UPDATE(0x1, 0x1, 14), + RK_GRF_VIRT + RK3036_GRF_UOC0_CON5); + } + } + return ret; +} + +/********** end of rk3036 usb detections **********/ +static int rk_usb_control_probe(struct platform_device *pdev) +{ + int gpio, err; + struct device_node *np = pdev->dev.of_node; + int ret = 0; + + control_usb = + devm_kzalloc(&pdev->dev, sizeof(*control_usb), GFP_KERNEL); + if (!control_usb) { + dev_err(&pdev->dev, "unable to alloc memory for control usb\n"); + ret = -ENOMEM; + goto out; + } + + control_usb->chip_id = RK3036_USB_CTLR; + control_usb->remote_wakeup = of_property_read_bool(np, + "rockchip,remote_wakeup"); + control_usb->usb_irq_wakeup = of_property_read_bool(np, + "rockchip,usb_irq_wakeup"); + + INIT_DELAYED_WORK(&control_usb->usb_charger_det_work, + usb_battery_charger_detect_work); + + control_usb->host_gpios = + devm_kzalloc(&pdev->dev, sizeof(struct gpio), GFP_KERNEL); + if (!control_usb->host_gpios) { + dev_err(&pdev->dev, "unable to alloc memory for host_gpios\n"); + ret = -ENOMEM; + goto out; + } + + gpio = of_get_named_gpio(np, "host_drv_gpio", 0); + control_usb->host_gpios->gpio = gpio; + + if (!gpio_is_valid(gpio)) { + dev_err(&pdev->dev, "invalid host gpio%d\n", gpio); + } else { + err = devm_gpio_request(&pdev->dev, gpio, "host_drv_gpio"); + if (err) { + dev_err(&pdev->dev, + "failed to request GPIO%d for host_drv\n", + gpio); + ret = err; + goto out; + } + gpio_direction_output(control_usb->host_gpios->gpio, 1); + } + + control_usb->otg_gpios = + devm_kzalloc(&pdev->dev, sizeof(struct gpio), GFP_KERNEL); + if (!control_usb->otg_gpios) { + dev_err(&pdev->dev, "unable to alloc memory for otg_gpios\n"); + ret = -ENOMEM; + goto out; + } + + gpio = of_get_named_gpio(np, "otg_drv_gpio", 0); + control_usb->otg_gpios->gpio = gpio; + + if (!gpio_is_valid(gpio)) { + dev_err(&pdev->dev, "invalid otg gpio%d\n", gpio); + } else { + err = devm_gpio_request(&pdev->dev, gpio, "otg_drv_gpio"); + if (err) { + dev_err(&pdev->dev, + "failed to request GPIO%d for otg_drv\n", gpio); + ret = err; + goto out; + } + gpio_direction_output(control_usb->otg_gpios->gpio, 0); + } + +out: + return ret; +} + +static int rk_usb_control_remove(struct platform_device *pdev) +{ + return 0; +} + +static struct platform_driver rk_usb_control_driver = { + .probe = rk_usb_control_probe, + .remove = rk_usb_control_remove, + .driver = { + .name = "rk3036-usb-control", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(rk_usb_control_id_table), + }, +}; + +#ifdef CONFIG_OF + +static const struct of_device_id dwc_otg_control_usb_id_table[] = { + { + .compatible = "rockchip,rk3036-dwc-control-usb", + }, + {}, +}; + +#endif +static int dwc_otg_control_usb_probe(struct platform_device *pdev) +{ + struct clk *hclk_usb_peri; + int ret = 0; + + if (!control_usb) { + dev_err(&pdev->dev, "unable to alloc memory for control usb\n"); + ret = -ENOMEM; + goto err1; + } + + hclk_usb_peri = devm_clk_get(&pdev->dev, "hclk_usb_peri"); + if (IS_ERR(hclk_usb_peri)) { + dev_err(&pdev->dev, "Failed to get hclk_usb_peri\n"); + ret = -EINVAL; + goto err1; + } + + control_usb->hclk_usb_peri = hclk_usb_peri; + clk_prepare_enable(hclk_usb_peri); + +#ifdef CONFIG_USB20_OTG + if (usb20otg_get_status(USB_STATUS_BVABLID)) { + rk_usb_charger_status = USB_BC_TYPE_SDP; + schedule_delayed_work(&control_usb->usb_charger_det_work, + HZ / 10); + } +#endif + + ret = otg_irq_detect_init(pdev); + if (ret < 0) + goto err2; + + return 0; + +err2: + clk_disable_unprepare(hclk_usb_peri); +err1: + return ret; +} + +static int dwc_otg_control_usb_remove(struct platform_device *pdev) +{ + clk_disable_unprepare(control_usb->hclk_usb_peri); + return 0; +} + +static struct platform_driver dwc_otg_control_usb_driver = { + .probe = dwc_otg_control_usb_probe, + .remove = dwc_otg_control_usb_remove, + .driver = { + .name = "rk3036-dwc-control-usb", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(dwc_otg_control_usb_id_table), + }, +}; + +static int __init dwc_otg_control_usb_init(void) +{ + int retval = 0; + + retval |= platform_driver_register(&rk_usb_control_driver); + retval |= platform_driver_register(&dwc_otg_control_usb_driver); + return retval; +} + +subsys_initcall(dwc_otg_control_usb_init); + +static void __exit dwc_otg_control_usb_exit(void) +{ + platform_driver_unregister(&rk_usb_control_driver); + platform_driver_unregister(&dwc_otg_control_usb_driver); +} + +module_exit(dwc_otg_control_usb_exit); +MODULE_ALIAS("platform: dwc_control_usb"); +MODULE_AUTHOR("RockChip Inc."); +MODULE_DESCRIPTION("RockChip Control Module USB Driver"); +MODULE_LICENSE("GPL v2");