3036: usb: add usb support

This commit is contained in:
lyz
2014-07-04 11:56:19 +08:00
parent 8584ad2d02
commit fc8d8215ce
7 changed files with 699 additions and 17 deletions

38
arch/arm/boot/dts/rk3036.dtsi Normal file → Executable file
View File

@@ -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 = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
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 = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
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 = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&clk_gates1 6>, <&clk_gates7 3>;
clock-names = "clk_usbphy1", "hclk_usb1";
};
};

19
arch/arm/mach-rockchip/rk3036.c Normal file → Executable file
View File

@@ -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)

View File

@@ -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

View File

@@ -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],
},
{
},
};

2
drivers/usb/dwc_otg_310/usbdev_grf_regs.h Normal file → Executable file
View File

@@ -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 {

View File

@@ -21,6 +21,7 @@
#include <linux/rockchip/cru.h>
#include <linux/rockchip/grf.h>
#include <linux/rockchip/cpu.h>
#include <linux/rockchip/iomap.h>
#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 {

View File

@@ -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");