mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-08 20:07:46 +09:00
3036: usb: add usb support
This commit is contained in:
38
arch/arm/boot/dts/rk3036.dtsi
Normal file → Executable file
38
arch/arm/boot/dts/rk3036.dtsi
Normal file → Executable 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
19
arch/arm/mach-rockchip/rk3036.c
Normal file → Executable 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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
2
drivers/usb/dwc_otg_310/usbdev_grf_regs.h
Normal file → Executable 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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
598
drivers/usb/dwc_otg_310/usbdev_rk3036.c
Executable file
598
drivers/usb/dwc_otg_310/usbdev_rk3036.c
Executable 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");
|
||||
Reference in New Issue
Block a user