clock-names = "clk_crypto", "sclk_crypto", "mclk_crypto";
status = "okay";
};
+ dwc_control_usb: dwc-control-usb {
+ compatible = "rockchip,rk3368-dwc-control-usb";
+ rockchip,grf = <&grf>;
+ interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "otg_id", "otg_bvalid",
+ "otg_linestate", "host0_linestate";
+ clocks = <&clk_gates20 6>, <&usbphy_480m>;
+ clock-names = "hclk_usb_peri", "usbphy_480m";
+ //resets = <&reset RK3128_RST_USBPOR>;
+ //reset-names = "usbphy_por";
+ usb_bc{
+ compatible = "inno,phy";
+ regbase = &dwc_control_usb;
+ rk_usb,bvalid = <0x04b 23 1>;
+ rk_usb,iddig = <0x04b 26 1>;
+ rk_usb,vdmsrcen = <0x718 12 1>;
+ rk_usb,vdpsrcen = <0x718 11 1>;
+ rk_usb,rdmpden = <0x718 10 1>;
+ rk_usb,idpsrcen = <0x718 9 1>;
+ rk_usb,idmsinken = <0x718 8 1>;
+ rk_usb,idpsinken = <0x718 7 1>;
+ rk_usb,dpattach = <0x4b8 31 1>;
+ rk_usb,cpdet = <0x4b8 30 1>;
+ rk_usb,dcpattach = <0x4b8 29 1>;
+ };
+ };
+
+ usb0: usb@ff580000 {
+ compatible = "rockchip,rk3368_usb20_otg";
+ reg = <0x0 0xff580000 0x0 0x40000>;
+ interrupts = <GIC_SPI 23 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 1>, <&clk_gates20 1>;
+ clock-names = "clk_usbphy0", "hclk_otg";
+ resets = <&reset RK3368_SRST_USBOTG0_H>, <&reset RK3368_SRST_USBOTGPHY0>,
+ <&reset RK3368_SRST_USBOTGC0>;
+ reset-names = "otg_ahb", "otg_phy", "otg_controller";
+ /*0 - Normal, 1 - Force Host, 2 - Force Device*/
+ rockchip,usb-mode = <0>;
+ };
+
+ usb_ehci: usb@ff500000 {
+ compatible = "generic-ehci";
+ reg = <0x0 0xff500000 0x0 0x20000>;
+ interrupts = <GIC_SPI 24 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 1>, <&clk_gates20 3>;
+ clock-names = "clk_usbphy0", "hclk_ehci";
+ //resets = <&reset RK3288_SOFT_RST_USBHOST0_H>, <&reset RK3288_SOFT_RST_USBHOST0PHY>,
+ // <&reset RK3288_SOFT_RST_USBHOST0C>, <&reset RK3288_SOFT_RST_USB_HOST0>;
+ //reset-names = "ehci_ahb", "ehci_phy", "ehci_controller", "ehci";
+ };
+
+ usb_ohci: usb@ff520000 {
+ compatible = "generic-ohci";
+ reg = <0x0 0xff520000 0x0 0x20000>;
+ interrupts = <GIC_SPI 25 IRQ_TYPE_LEVEL_HIGH>;
+ clocks = <&clk_gates8 1>, <&clk_gates20 3>;
+ clock-names = "clk_usbphy0", "hclk_ohci";
+ };
+
+ usb_hsic: usb@ff5c0000 {
+ compatible = "rockchip,rk3288_rk_hsic_host";
+ reg = <0x0 0xff5c0000 0x0 0x40000>;
+ interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
+/*
+ clocks = <&hsicphy_480m>, <&clk_gates7 8>,
+ <&hsicphy_12m>, <&usbphy_480m>,
+ <&otgphy1_480m>, <&otgphy2_480m>;
+ clock-names = "hsicphy_480m", "hclk_hsic",
+ "hsicphy_12m", "usbphy_480m",
+ "hsic_usbphy1", "hsic_usbphy2";
+ resets = <&reset RK3288_SOFT_RST_HSIC>, <&reset RK3288_SOFT_RST_HSIC_AUX>,
+ <&reset RK3288_SOFT_RST_HSICPHY>;
+ reset-names = "hsic_ahb", "hsic_aux", "hsic_phy";
+*/
+ status = "disabled";
+ };
pinctrl: pinctrl {
compatible = "rockchip,rk3368-pinctrl";
of_match_device(of_match_ptr(usb20_host_of_match), &_dev->dev);
if (match && match->data) {
- dev->platform_data = (void *)match->data;
+ pldata = (void *)match->data;
+ pldata->dev = dev;
} else {
- dev_err(dev, "usb20host match failed\n");
+ dev_err(dev, "usb20otg match failed\n");
return -EINVAL;
}
- pldata = dev->platform_data;
- pldata->dev = dev;
-
if (!node) {
dev_err(dev, "device node not found\n");
return -EINVAL;
retval = -ENOMEM;
goto clk_disable;
}
- dev_dbg(&_dev->dev, "base=0x%08x\n",
- (unsigned)dwc_otg_device->os_dep.base);
+ dev_dbg(&_dev->dev, "base=0x%p\n", dwc_otg_device->os_dep.base);
/* Set device flags indicating whether the HCD supports DMA. */
if (!_dev->dev.dma_mask)
static void dwc_otg_driver_shutdown(struct platform_device *_dev)
{
struct device *dev = &_dev->dev;
- struct dwc_otg_platform_data *pldata = dev->platform_data;
dwc_otg_device_t *otg_dev = dev->platform_data;
dwc_otg_core_if_t *core_if = otg_dev->core_if;
+ struct dwc_otg_platform_data *pldata = otg_dev->pldata;
dctl_data_t dctl = {.d32 = 0 };
DWC_PRINTF("%s: disconnect USB %s mode\n", __func__,
dwc_otg_is_host_mode(core_if) ? "host" : "device");
- if( pldata->dwc_otg_uart_mode != NULL)
- pldata->dwc_otg_uart_mode( pldata, PHY_USB_MODE);
- if(pldata->phy_suspend != NULL)
- pldata->phy_suspend(pldata, USB_PHY_ENABLED);
+ if (pldata->dwc_otg_uart_mode != NULL)
+ pldata->dwc_otg_uart_mode(pldata, PHY_USB_MODE);
+ if (pldata->phy_suspend != NULL)
+ pldata->phy_suspend(pldata, USB_PHY_ENABLED);
if (dwc_otg_is_host_mode(core_if)) {
if (core_if->hcd_cb && core_if->hcd_cb->stop)
core_if->hcd_cb->stop(core_if->hcd_cb_p);
.compatible = "rockchip,rk3126_usb20_otg",
.data = &usb20otg_pdata_rk3126,
},
+ {
+ .compatible = "rockchip,rk3368_usb20_otg",
+ .data = &usb20otg_pdata_rk3368,
+ },
{ },
};
const struct of_device_id *match =
of_match_device(of_match_ptr(usb20_otg_of_match), &_dev->dev);
- if (match) {
- dev->platform_data = (void *)match->data;
+ if (match && match->data) {
+ pldata = (void *)match->data;
+ pldata->dev = dev;
} else {
dev_err(dev, "usb20otg match failed\n");
return -EINVAL;
}
- pldata = dev->platform_data;
- pldata->dev = dev;
-
if (!node) {
dev_err(dev, "device node not found\n");
return -EINVAL;
}
- /*todo : move to usbdev_rk-XX.c */
+
if (pldata->hw_init)
pldata->hw_init();
retval = -ENOMEM;
goto clk_disable;
}
- dev_dbg(&_dev->dev, "base=0x%08x\n",
- (unsigned)dwc_otg_device->os_dep.base);
+ dev_dbg(&_dev->dev, "base=0x%p\n", dwc_otg_device->os_dep.base);
/* Set device flags indicating whether the HCD supports DMA. */
if (!_dev->dev.dma_mask)
--- /dev/null
+#include "usbdev_rk.h"
+#include "usbdev_grf_regs.h"
+#include "dwc_otg_regs.h"
+static struct dwc_otg_control_usb *control_usb;
+
+static u32 uoc_read(u32 reg)
+{
+ unsigned int val;
+
+ regmap_read(control_usb->grf, reg, &val);
+ return val;
+}
+
+static void uoc_write(u32 value, u32 reg)
+{
+ regmap_write(control_usb->grf, reg, value);
+}
+
+#ifdef CONFIG_USB20_OTG
+static void usb20otg_hw_init(void)
+{
+ /* Turn off differential receiver in suspend mode */
+ uoc_write(UOC_HIWORD_UPDATE(0, 1, 2), 0x798);
+
+ /* other haredware init,include:
+ * DRV_VBUS GPIO init */
+ if (gpio_is_valid(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 */
+ uoc_write(UOC_HIWORD_UPDATE(0x55, 0x7f, 0), 0x700);
+ usbpdata->phy_status = 1;
+ } else {
+ /* exit suspend */
+ uoc_write(UOC_HIWORD_UPDATE(0x0, 0x1, 0), 0x700);
+ 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 */
+ uoc_write(UOC_HIWORD_UPDATE(0x1, 0x3, 0), 0x700);
+ reset_control_assert(rst_otg_p);
+ udelay(15);
+ uoc_write(UOC_HIWORD_UPDATE(0x2, 0x3, 0), 0x700);
+ 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_otg");
+ 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_status15 = uoc_read(0x4bc);
+
+ switch (id) {
+ case USB_STATUS_BVABLID:
+ /* bvalid in grf */
+ ret = soc_status15 & (0x1 << 23);
+ break;
+ case USB_STATUS_DPDM:
+ /* dpdm in grf */
+ ret = soc_status15 & (0x3 << 24);
+ break;
+ case USB_STATUS_ID:
+ /* id in grf */
+ ret = soc_status15 & (0x1 << 26);
+ 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;
+}
+
+#ifdef CONFIG_RK_USB_UART
+/**
+ * dwc_otg_uart_enabled - check if a usb-uart bypass func is enabled in DT
+ *
+ * Returns true if the status property of node "usb_uart" is set to "okay"
+ * or "ok", if this property is absent it will use the default status "ok"
+ * 0 otherwise
+ */
+static bool dwc_otg_uart_enabled(void)
+{
+ return false;
+}
+
+static void dwc_otg_uart_mode(void *pdata, int enter_usb_uart_mode)
+{
+}
+#else
+static void dwc_otg_uart_mode(void *pdata, int enter_usb_uart_mode)
+{
+}
+#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_rk3368 = {
+ .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 = rk_battery_charger_detect_cb, */
+};
+#endif
+
+#ifdef CONFIG_USB_EHCI_RK
+static void usb20ehci_hw_init(void)
+{
+ /* Turn off differential receiver in suspend mode */
+ uoc_write(UOC_HIWORD_UPDATE(0, 1, 2), 0x7b8);
+ /* Set disconnect detection trigger point to 600mv */
+ uoc_write(UOC_HIWORD_UPDATE(1, 0xf, 11), 0x7bc);
+
+ /* 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 usb20ehci_phy_suspend(void *pdata, int suspend)
+{
+ struct rkehci_platform_data *usbpdata = pdata;
+
+ if (suspend) {
+ /* enable soft control */
+ uoc_write(UOC_HIWORD_UPDATE(0x1d5, 0x1ff, 0), 0x728);
+ usbpdata->phy_status = 1;
+ } else {
+ /* exit suspend */
+ uoc_write(UOC_HIWORD_UPDATE(0x0, 0x1, 0), 0x728);
+ usbpdata->phy_status = 0;
+ }
+}
+
+static void usb20ehci_soft_reset(void *pdata, enum rkusb_rst_flag rst_type)
+{
+ struct rkehci_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 */
+ uoc_write(UOC_HIWORD_UPDATE(0x1, 0x3, 0), 0x728);
+ reset_control_assert(rst_host_p);
+ udelay(15);
+ uoc_write(UOC_HIWORD_UPDATE(0x2, 0x3, 0), 0x728);
+
+ 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 usb20ehci_clock_init(void *pdata)
+{
+}
+
+static void usb20ehci_clock_enable(void *pdata, int enable)
+{
+}
+
+static int usb20ehci_get_status(int id)
+{
+ /* For HOST port in rk336x can not get any info from GRF */
+ return -ENOENT;
+}
+
+struct rkehci_platform_data usb20ehci_pdata_rk3368 = {
+ .phyclk = NULL,
+ .ahbclk = NULL,
+ .phy_status = 0,
+ .hw_init = usb20ehci_hw_init,
+ .phy_suspend = usb20ehci_phy_suspend,
+ .soft_reset = usb20ehci_soft_reset,
+ .clock_init = usb20ehci_clock_init,
+ .clock_enable = usb20ehci_clock_enable,
+ .get_status = usb20ehci_get_status,
+};
+#endif
+
+struct dwc_otg_platform_data usb20ohci_pdata_rk3368;
+
+#ifdef CONFIG_OF
+static const struct of_device_id rk_usb_control_id_table[] = {
+ {
+ .compatible = "rockchip,rk3368-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_battery_charger_detect_cb(usb_battery_charger_detect(1)); */
+}
+
+/********** handler for bvalid irq **********/
+static irqreturn_t bvalid_irq_handler(int irq, void *dev_id)
+{
+ /* clear irq */
+ uoc_write(UOC_HIWORD_UPDATE(0x1, 0x1, 3), 0x690);
+#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);
+ }
+
+ 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;
+
+ wake_lock_init(&control_usb->usb_wakelock, WAKE_LOCK_SUSPEND,
+ "usb_detect");
+ INIT_DELAYED_WORK(&control_usb->usb_det_wakeup_work, do_wakeup);
+#if 0
+ /*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 */
+ uoc_write(UOC_HIWORD_UPDATE(0x1, 0x1, 3), 0x680);
+ }
+ }
+#endif
+ return 0;
+}
+
+/********** end of usb detections **********/
+#ifdef CONFIG_OF
+static const struct of_device_id dwc_otg_control_usb_id_table[] = {
+ {
+ .compatible = "rockchip,rk3368-dwc-control-usb",
+ },
+ {},
+};
+#endif
+static int dwc_otg_control_usb_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct clk *hclk_usb_peri;
+ struct regmap *grf;
+ int gpio, ret = 0;
+
+ control_usb = devm_kzalloc(dev, sizeof(*control_usb), GFP_KERNEL);
+ if (!control_usb) {
+ dev_err(&pdev->dev, "Unable to alloc memory for control usb\n");
+ return -ENOMEM;
+ }
+
+ /* Init regmap GRF */
+ grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
+ if (IS_ERR(grf)) {
+ dev_err(&pdev->dev, "Missing rockchip,grf property\n");
+ return PTR_ERR(grf);
+ }
+ control_usb->grf = grf;
+
+ /* Init Vbus-drv GPIOs */
+ 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");
+ return -ENOMEM;
+ }
+
+ gpio = of_get_named_gpio(np, "host_drv_gpio", 0);
+ control_usb->host_gpios->gpio = gpio;
+
+ if (gpio_is_valid(gpio)) {
+ if (devm_gpio_request(&pdev->dev, gpio, "usb_host_drv")) {
+ dev_err(&pdev->dev,
+ "Failed to request GPIO%d for host_drv\n",
+ gpio);
+ return -EINVAL;
+ }
+ 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");
+ return -ENOMEM;
+ }
+
+ gpio = of_get_named_gpio(np, "otg_drv_gpio", 0);
+ control_usb->otg_gpios->gpio = gpio;
+
+ if (gpio_is_valid(gpio)) {
+ if (devm_gpio_request(&pdev->dev, gpio, "usb_otg_drv")) {
+ dev_err(&pdev->dev,
+ "failed to request GPIO%d for otg_drv\n", gpio);
+ return -EINVAL;
+ }
+ gpio_direction_output(control_usb->otg_gpios->gpio, 0);
+ }
+
+
+ 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 hclk_usb_peri */
+ 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");
+ return PTR_ERR(hclk_usb_peri);
+ }
+ control_usb->hclk_usb_peri = hclk_usb_peri;
+ clk_prepare_enable(hclk_usb_peri);
+
+#ifdef CONFIG_USB20_OTG
+ INIT_DELAYED_WORK(&control_usb->usb_charger_det_work,
+ usb_battery_charger_detect_work);
+
+ if (usb20otg_get_status(USB_STATUS_BVABLID))
+ schedule_delayed_work(&control_usb->usb_charger_det_work,
+ HZ / 10);
+#endif
+
+ ret = otg_irq_detect_init(pdev);
+ if (ret < 0)
+ goto err;
+
+ return 0;
+
+err:
+ clk_disable_unprepare(hclk_usb_peri);
+ 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 = "rk3368-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(&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(&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");