From 522d0ceccf1d0f3bcf1c275e9e6a77d483688035 Mon Sep 17 00:00:00 2001 From: lyz Date: Wed, 30 Jul 2014 18:30:52 +0800 Subject: [PATCH] rk312x: add usb --- arch/arm/boot/dts/rk3126-sdk.dts | 8 + arch/arm/boot/dts/rk312x.dtsi | 41 ++ drivers/usb/dwc_otg_310/Makefile | 2 +- drivers/usb/dwc_otg_310/dwc_otg_driver.c | 16 + drivers/usb/dwc_otg_310/usbdev_rk.h | 5 + drivers/usb/dwc_otg_310/usbdev_rk3126.c | 608 +++++++++++++++++++++++ 6 files changed, 679 insertions(+), 1 deletion(-) create mode 100755 drivers/usb/dwc_otg_310/usbdev_rk3126.c diff --git a/arch/arm/boot/dts/rk3126-sdk.dts b/arch/arm/boot/dts/rk3126-sdk.dts index 24ef257c303a..cb3c4b3ecaa0 100755 --- a/arch/arm/boot/dts/rk3126-sdk.dts +++ b/arch/arm/boot/dts/rk3126-sdk.dts @@ -16,6 +16,14 @@ default-brightness-level = <128>; enable-gpios = <&gpio3 GPIO_C1 GPIO_ACTIVE_HIGH>; }; + usb_control { + compatible = "rockchip,rk3126-usb-control"; + host_drv_gpio = <&gpio2 GPIO_B4 GPIO_ACTIVE_LOW>; + otg_drv_gpio = <&gpio2 GPIO_B6 GPIO_ACTIVE_LOW>; + + rockchip,remote_wakeup; + rockchip,usb_irq_wakeup; + }; }; &nandc { diff --git a/arch/arm/boot/dts/rk312x.dtsi b/arch/arm/boot/dts/rk312x.dtsi index ef7d1c9a7b8e..1485704d82fe 100755 --- a/arch/arm/boot/dts/rk312x.dtsi +++ b/arch/arm/boot/dts/rk312x.dtsi @@ -541,6 +541,47 @@ status = "disabled"; }; + dwc_control_usb: dwc-control-usb@20008000 { + compatible = "rockchip,rk3126-dwc-control-usb"; + reg = <0x20008000 0x4>; + interrupts = ; + interrupt-names = "otg_bvalid"; + clocks = <&clk_gates9 13>; + clock-names = "hclk_usb_peri"; + rockchip,remote_wakeup; + rockchip,usb_irq_wakeup; + resets = <&reset RK3128_RST_USBPOR>; + reset-names = "usbphy_por"; + usb_bc{ + compatible = "inno,phy"; + regbase = &dwc_control_usb; + }; + }; + + usb0: usb@10180000 { + compatible = "rockchip,rk3126_usb20_otg"; + reg = <0x10180000 0x40000>; + interrupts = ; + clocks = <&clk_gates1 5>, <&clk_gates5 13>; + clock-names = "clk_usbphy0", "hclk_usb0"; + resets = <&reset RK3128_RST_USBOTG0>, <&reset RK3128_RST_USBOTG0>, + <&reset RK3128_RST_OTGC0>; + reset-names = "otg_ahb", "otg_phy", "otg_controller"; + /*0 - Normal, 1 - Force Host, 2 - Force Device*/ + rockchip,usb-mode = <0>; + }; + + usb1: usb@101c0000 { + compatible = "rockchip,rk3126_usb20_host"; + reg = <0x101c0000 0x40000>; + interrupts = ; + clocks = <&clk_gates1 6>, <&clk_gates7 3>; + clock-names = "clk_usbphy1", "hclk_usb1"; + resets = <&reset RK3128_RST_USBOTG1>, <&reset RK3128_RST_UTMI1>, + <&reset RK3128_RST_OTGC1>; + reset-names = "host_ahb", "host_phy", "host_controller"; + }; + fb: fb{ compatible = "rockchip,rk-fb"; rockchip,disp-mode = ; diff --git a/drivers/usb/dwc_otg_310/Makefile b/drivers/usb/dwc_otg_310/Makefile index f5dd7ae364c9..b1656829613b 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_rk3036.o usbdev_bc.o +dwc_otg-objs += usbdev_rk30.o usbdev_rk32.o usbdev_rk3036.o usbdev_rk3126.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 96671ef802a8..57034a80f4ee 100755 --- a/drivers/usb/dwc_otg_310/dwc_otg_driver.c +++ b/drivers/usb/dwc_otg_310/dwc_otg_driver.c @@ -90,6 +90,10 @@ static struct usb20otg_pdata_id usb20otg_pdata[] = { .name = "rk3036-usb20otg", .pdata = &usb20otg_pdata_rk3036, }, + { + .name = "rk3126-usb20otg", + .pdata = &usb20otg_pdata_rk3126, + }, {}, }; #endif @@ -108,6 +112,10 @@ static struct usb20host_pdata_id usb20host_pdata[] = { .name = "rk3036-usb20host", .pdata = &usb20host_pdata_rk3036, }, + { + .name = "rk3126-usb20host", + .pdata = &usb20host_pdata_rk3126, + }, {}, }; #endif @@ -984,6 +992,10 @@ static const struct of_device_id usb20_host_of_match[] = { .compatible = "rockchip,rk3036_usb20_host", .data = &usb20host_pdata[RK3036_USB_CTLR], }, + { + .compatible = "rockchip,rk3126_usb20_host", + .data = &usb20host_pdata[RK3126_USB_CTLR], + }, {}, }; @@ -1342,6 +1354,10 @@ static const struct of_device_id usb20_otg_of_match[] = { .compatible = "rockchip,rk3036_usb20_otg", .data = &usb20otg_pdata[RK3036_USB_CTLR], }, + { + .compatible = "rockchip,rk3126_usb20_otg", + .data = &usb20otg_pdata[RK3126_USB_CTLR], + }, { }, }; diff --git a/drivers/usb/dwc_otg_310/usbdev_rk.h b/drivers/usb/dwc_otg_310/usbdev_rk.h index c6d9e76d2694..06584dd516ca 100755 --- a/drivers/usb/dwc_otg_310/usbdev_rk.h +++ b/drivers/usb/dwc_otg_310/usbdev_rk.h @@ -67,6 +67,10 @@ 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; +/* rk3126 platform data */ +extern struct dwc_otg_platform_data usb20otg_pdata_rk3126; +extern struct dwc_otg_platform_data usb20host_pdata_rk3126; + struct dwc_otg_platform_data { void *privdata; @@ -131,6 +135,7 @@ enum { RK3188_USB_CTLR = 0, /* rk3188 chip usb */ RK3288_USB_CTLR, /* rk3288 chip usb */ RK3036_USB_CTLR, /* rk3036 chip usb */ + RK3126_USB_CTLR, }; struct usb20otg_pdata_id { diff --git a/drivers/usb/dwc_otg_310/usbdev_rk3126.c b/drivers/usb/dwc_otg_310/usbdev_rk3126.c new file mode 100755 index 000000000000..0c691438dc8a --- /dev/null +++ b/drivers/usb/dwc_otg_310/usbdev_rk3126.c @@ -0,0 +1,608 @@ +#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 + RK312X_GRF_UOC0_CON0); + usbpdata->phy_status = 1; + } else { + /* exit suspend */ + writel(UOC_HIWORD_UPDATE(0x0, 0x1, 0), + RK_GRF_VIRT + RK312X_GRF_UOC0_CON0); + usbpdata->phy_status = 0; + } +} + +static void usb20otg_soft_reset(void *pdata, enum rkusb_rst_flag rst_type) +{ + struct dwc_otg_platform_data *usbpdata = pdata; + struct reset_control *rst_otg_h, *rst_otg_p, *rst_otg_c; + + rst_otg_h = devm_reset_control_get(usbpdata->dev, "otg_ahb"); + rst_otg_p = devm_reset_control_get(usbpdata->dev, "otg_phy"); + rst_otg_c = devm_reset_control_get(usbpdata->dev, "otg_controller"); + if (IS_ERR(rst_otg_h) || IS_ERR(rst_otg_p) || IS_ERR(rst_otg_c)) { + dev_err(usbpdata->dev, "Fail to get reset control from dts\n"); + return; + } + + switch(rst_type) { + case RST_POR: + /* PHY reset */ + writel(UOC_HIWORD_UPDATE(0x1, 0x3, 0), + RK_GRF_VIRT + RK312X_GRF_UOC0_CON0); + reset_control_assert(rst_otg_p); + udelay(15); + writel(UOC_HIWORD_UPDATE(0x2, 0x3, 0), + RK_GRF_VIRT + RK312X_GRF_UOC0_CON0); + udelay(1500); + reset_control_deassert(rst_otg_p); + udelay(2); + + /* Controller reset */ + reset_control_assert(rst_otg_c); + reset_control_assert(rst_otg_h); + + udelay(2); + + reset_control_deassert(rst_otg_c); + reset_control_deassert(rst_otg_h); + break; + + default: + break; + } +} + +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 + RK312X_GRF_SOC_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 + RK312X_GRF_UOC1_CON4); + } else if (0 == enter_usb_uart_mode) { + /* enter usb mode */ + writel(UOC_HIWORD_UPDATE(0x0, 0x3, 12), + RK_GRF_VIRT + RK312X_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_rk3126 = { + .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 + RK312X_GRF_UOC1_CON5); + usbpdata->phy_status = 1; + } else { + /* exit suspend */ + writel(UOC_HIWORD_UPDATE(0x0, 0x1, 0), + RK_GRF_VIRT + RK312X_GRF_UOC1_CON5); + usbpdata->phy_status = 0; + } +} + +static void usb20host_soft_reset(void *pdata, enum rkusb_rst_flag rst_type) +{ + struct dwc_otg_platform_data *usbpdata = pdata; + struct reset_control *rst_host_h, *rst_host_p, *rst_host_c; + + rst_host_h = devm_reset_control_get(usbpdata->dev, "host_ahb"); + rst_host_p = devm_reset_control_get(usbpdata->dev, "host_phy"); + rst_host_c = devm_reset_control_get(usbpdata->dev, "host_controller"); + if (IS_ERR(rst_host_h) || IS_ERR(rst_host_p) || IS_ERR(rst_host_c)) { + dev_err(usbpdata->dev, "Fail to get reset control from dts\n"); + return; + } + + switch(rst_type) { + case RST_POR: + /* PHY reset */ + writel(UOC_HIWORD_UPDATE(0x1, 0x3, 0), + RK_GRF_VIRT + RK312X_GRF_UOC1_CON5); + reset_control_assert(rst_host_p); + udelay(15); + writel(UOC_HIWORD_UPDATE(0x2, 0x3, 0), + RK_GRF_VIRT + RK312X_GRF_UOC1_CON5); + + udelay(1500); + reset_control_deassert(rst_host_p); + + /* Controller reset */ + reset_control_assert(rst_host_c); + reset_control_assert(rst_host_h); + + udelay(5); + + reset_control_deassert(rst_host_c); + reset_control_deassert(rst_host_h); + break; + + default: + break; + } +} + +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 + RK312X_GRF_SOC_STATUS0); + + switch (id) { + case USB_STATUS_BVABLID: + /* bvalid in grf */ + ret = soc_status0 & (0x1 << 10); + break; + case USB_STATUS_DPDM: + /* dpdm in grf */ + ret = soc_status0 & (0x3 << 11); + 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_rk3126 = { + .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,rk3126-usb-control", + }, + {}, +}; +#endif +/********************************************************************* + rk3126 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 + RK312X_GRF_UOC0_CON0); +#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 + RK312X_GRF_UOC0_CON0); + } + } + return ret; +} + +/********** end of rk3126 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 = RK3126_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 = "rk3126-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,rk3126-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 = "rk3126-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"); + -- 2.34.1