X-Git-Url: http://plrg.eecs.uci.edu/git/?p=firefly-linux-kernel-4.4.55.git;a=blobdiff_plain;f=drivers%2Fusb%2Fdwc_otg_310%2Fusbdev_rk3126.c;h=2cf9c5a8fbf838e37801b2289a3b9eb9a0ddd77a;hp=a6f174cc322dbe44ac16c4c65945a120a38f4e16;hb=9a47b6de548a83d198e2a92b53fe735c62832515;hpb=1f49c03ee0d79c6941697d40dae7ff1dddf95b79 diff --git a/drivers/usb/dwc_otg_310/usbdev_rk3126.c b/drivers/usb/dwc_otg_310/usbdev_rk3126.c index a6f174cc322d..2cf9c5a8fbf8 100755 --- a/drivers/usb/dwc_otg_310/usbdev_rk3126.c +++ b/drivers/usb/dwc_otg_310/usbdev_rk3126.c @@ -1,3 +1,4 @@ +#ifdef CONFIG_ARM #include "usbdev_rk.h" #include "usbdev_grf_regs.h" #include "dwc_otg_regs.h" @@ -8,7 +9,10 @@ static void usb20otg_hw_init(void) { /* Turn off differential receiver in suspend mode */ writel(UOC_HIWORD_UPDATE(0, 1, 2), - RK_GRF_VIRT + RK312X_GRF_USBPHY1_CON6); + RK_GRF_VIRT + RK312X_GRF_USBPHY0_CON6); + /* Set disconnect detection trigger point to 600mv */ + writel(UOC_HIWORD_UPDATE(0, 0xf, 11), + RK_GRF_VIRT + RK312X_GRF_USBPHY0_CON7); /* other haredware init,include: * DRV_VBUS GPIO init */ if (gpio_is_valid(control_usb->otg_gpios->gpio)) { @@ -20,9 +24,10 @@ static void usb20otg_hw_init(void) 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), + writel(UOC_HIWORD_UPDATE(0x1d1, 0x1ff, 0), RK_GRF_VIRT + RK312X_GRF_UOC0_CON0); usbpdata->phy_status = 1; } else { @@ -189,13 +194,32 @@ static void usb20otg_power_enable(int enable) /* disable otg_drv power */ if (gpio_is_valid(control_usb->otg_gpios->gpio)) gpio_set_value(control_usb->otg_gpios->gpio, 0); + + rk_battery_charger_detect_cb(USB_OTG_POWER_OFF); } 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); + + if (!usb20otg_get_status(USB_STATUS_BVABLID)) + rk_battery_charger_detect_cb(USB_OTG_POWER_ON); + } +} +static void usb20otg_phy_power_down(int power_down) +{ + if (power_down == PHY_POWER_DOWN) { + if (control_usb->linestate_wakeup) { + /* enable otg0_linestate irq */ + writel(UOC_HIWORD_UPDATE(0x3, 0x3, 14), + RK_GRF_VIRT + RK312X_GRF_UOC1_CON5); + /* enable otg1_linestate irq */ + writel(UOC_HIWORD_UPDATE(0x3, 0x3, 12), + RK_GRF_VIRT + RK312X_GRF_UOC0_CON0); + } + } else if (power_down == PHY_POWER_UP) { + ; } } - struct dwc_otg_platform_data usb20otg_pdata_rk3126 = { .phyclk = NULL, .ahbclk = NULL, @@ -210,18 +234,19 @@ struct dwc_otg_platform_data usb20otg_pdata_rk3126 = { .power_enable = usb20otg_power_enable, .dwc_otg_uart_mode = dwc_otg_uart_mode, .bc_detect_cb = rk_battery_charger_detect_cb, + .phy_power_down = usb20otg_phy_power_down, }; #endif -#ifdef CONFIG_USB20_HOST +#if defined(CONFIG_USB20_HOST) || defined(CONFIG_USB_EHCI_RK) static void usb20host_hw_init(void) { - /* Switch to DWC HOST */ - writel(UOC_HIWORD_UPDATE(1, 1, 3), - RK_GRF_VIRT + RK312X_GRF_SOC_CON2); /* Turn off differential receiver in suspend mode */ writel(UOC_HIWORD_UPDATE(0, 1, 2), - RK_GRF_VIRT + RK312X_GRF_USBPHY0_CON6); + RK_GRF_VIRT + RK312X_GRF_USBPHY1_CON6); + /* Set disconnect detection trigger point to 600mv */ + writel(UOC_HIWORD_UPDATE(1, 0xf, 11), + RK_GRF_VIRT + RK312X_GRF_USBPHY1_CON7); /* other haredware init,include: * DRV_VBUS GPIO init */ if (gpio_is_valid(control_usb->host_gpios->gpio)) { @@ -382,6 +407,114 @@ struct dwc_otg_platform_data usb20host_pdata_rk3126 = { }; #endif +#ifdef CONFIG_USB_EHCI_RK +static void usb20ehci_phy_suspend(void *pdata, int suspend) +{ + struct rkehci_platform_data *usbpdata = pdata; + + if (suspend) { + /* enable soft control */ + writel(UOC_HIWORD_UPDATE(0x1d1, 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 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 */ + 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 usb20ehci_clock_init(void *pdata) +{ + struct rkehci_platform_data *usbpdata = pdata; + struct clk *ahbclk, *phyclk; + + ahbclk = devm_clk_get(usbpdata->dev, "hclk_host0"); + 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 usb20ehci_clock_enable(void *pdata, int enable) +{ + struct rkehci_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); + } +} + +struct rkehci_platform_data usb20ehci_pdata_rk3126 = { + .phyclk = NULL, + .ahbclk = NULL, + .phy_status = 0, + .hw_init = usb20host_hw_init, + .phy_suspend = usb20ehci_phy_suspend, + .soft_reset = usb20ehci_soft_reset, + .clock_init = usb20ehci_clock_init, + .clock_enable = usb20ehci_clock_enable, + .get_status = usb20host_get_status, +}; +#endif + +struct dwc_otg_platform_data usb20ohci_pdata_rk3126; + #ifdef CONFIG_OF static const struct of_device_id rk_usb_control_id_table[] = { { @@ -429,6 +562,53 @@ static irqreturn_t bvalid_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } +/********** Handler for linestate irq **********/ +static irqreturn_t otg0_linestate_irq_handler(int irq, void *dev_id) +{ + /* + * Here is a chip hwrdware bug, when disable/enable + * linestate irq bit the state machine will not reset + * So here have to add a delay to wait the linestate filter + * timer run out (linestate filter time had been set to 100us) + */ + udelay(200); + + /* clear and disable irq */ + writel(UOC_HIWORD_UPDATE(0x2, 0x3, 12), + RK_GRF_VIRT + RK312X_GRF_UOC0_CON0); + + + if (control_usb->usb_irq_wakeup) { + wake_lock_timeout(&control_usb->usb_wakelock, + WAKE_LOCK_TIMEOUT); + } + + return IRQ_HANDLED; +} + +static irqreturn_t otg1_linestate_irq_handler(int irq, void *dev_id) +{ + /* + * Here is a chip hwrdware bug, when disable/enable + * linestate irq bit the state machine will not reset + * So here have to add a delay to wait the linestate filter + * timer run out (linestate filter time had been set to 100us) + */ + udelay(200); + + /* clear and disable irq */ + writel(UOC_HIWORD_UPDATE(0x2, 0x3, 14), + RK_GRF_VIRT + RK312X_GRF_UOC1_CON5); + + + if (control_usb->usb_irq_wakeup) { + wake_lock_timeout(&control_usb->usb_wakelock, + WAKE_LOCK_TIMEOUT); + } + + return IRQ_HANDLED; +} + /************* register usb detection irqs **************/ static int otg_irq_detect_init(struct platform_device *pdev) { @@ -441,7 +621,7 @@ static int otg_irq_detect_init(struct platform_device *pdev) INIT_DELAYED_WORK(&control_usb->usb_det_wakeup_work, do_wakeup); } - /*register otg_bvalid irq */ + /* 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, @@ -454,7 +634,41 @@ static int otg_irq_detect_init(struct platform_device *pdev) RK_GRF_VIRT + RK312X_GRF_UOC0_CON0); } } - return ret; + + if (!control_usb->linestate_wakeup) + return 0; + + /* Set otg0&1_linestate_filter time to 100us */ + writel(UOC_HIWORD_UPDATE(0x0, 0xf, 6), RK_GRF_VIRT + 0x1a0); + + /* Register otg0_linestate irq */ + irq = platform_get_irq_byname(pdev, "otg0_linestate"); + if (irq > 0) { + ret = request_irq(irq, otg0_linestate_irq_handler, + 0, "otg0_linestate", NULL); + if (ret < 0) { + dev_err(&pdev->dev, "request_irq %d failed!\n", irq); + } else { + /* Clear otg0_linestate irq */ + writel(UOC_HIWORD_UPDATE(0x2, 0x3, 12), + RK_GRF_VIRT + RK312X_GRF_UOC0_CON0); + } + } + + /* Register otg1_linestate irq */ + irq = platform_get_irq_byname(pdev, "otg1_linestate"); + if (irq > 0) { + ret = request_irq(irq, otg1_linestate_irq_handler, + 0, "otg1_linestate", NULL); + if (ret < 0) { + dev_err(&pdev->dev, "request_irq %d failed!\n", irq); + } else { + /* Clear otg1_linestate irq */ + writel(UOC_HIWORD_UPDATE(0x2, 0x3, 14), + RK_GRF_VIRT + RK312X_GRF_UOC1_CON5); + } + } + return 0; } /********** end of rk3126 usb detections **********/ @@ -477,6 +691,8 @@ static int rk_usb_control_probe(struct platform_device *pdev) "rockchip,remote_wakeup"); control_usb->usb_irq_wakeup = of_property_read_bool(np, "rockchip,usb_irq_wakeup"); + control_usb->linestate_wakeup = of_property_read_bool(np, + "rockchip,linestate_wakeup"); INIT_DELAYED_WORK(&control_usb->usb_charger_det_work, usb_battery_charger_detect_work); @@ -636,4 +852,4 @@ MODULE_ALIAS("platform: dwc_control_usb"); MODULE_AUTHOR("RockChip Inc."); MODULE_DESCRIPTION("RockChip Control Module USB Driver"); MODULE_LICENSE("GPL v2"); - +#endif