diff options
Diffstat (limited to 'drivers/usb/phy')
-rw-r--r-- | drivers/usb/phy/Kconfig | 13 | ||||
-rw-r--r-- | drivers/usb/phy/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/phy/phy-am335x-control.c | 44 | ||||
-rw-r--r-- | drivers/usb/phy/phy-am335x.c | 44 | ||||
-rw-r--r-- | drivers/usb/phy/phy-fsl-usb.c | 96 | ||||
-rw-r--r-- | drivers/usb/phy/phy-fsl-usb.h | 4 | ||||
-rw-r--r-- | drivers/usb/phy/phy-fsm-usb.c | 72 | ||||
-rw-r--r-- | drivers/usb/phy/phy-fsm-usb.h | 179 | ||||
-rw-r--r-- | drivers/usb/phy/phy-generic.c | 108 | ||||
-rw-r--r-- | drivers/usb/phy/phy-generic.h | 7 | ||||
-rw-r--r-- | drivers/usb/phy/phy-rcar-gen2-usb.c | 248 | ||||
-rw-r--r-- | drivers/usb/phy/phy-tegra-usb.c | 2 | ||||
-rw-r--r-- | drivers/usb/phy/phy-twl6030-usb.c | 1 | ||||
-rw-r--r-- | drivers/usb/phy/phy.c | 2 |
14 files changed, 666 insertions, 155 deletions
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 64b8bef1919e..08e2f39027ec 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -194,6 +194,19 @@ config USB_RCAR_PHY To compile this driver as a module, choose M here: the module will be called phy-rcar-usb. +config USB_RCAR_GEN2_PHY + tristate "Renesas R-Car Gen2 USB PHY support" + depends on ARCH_R8A7790 || ARCH_R8A7791 || COMPILE_TEST + select USB_PHY + help + Say Y here to add support for the Renesas R-Car Gen2 USB PHY driver. + It is typically used to control internal USB PHY for USBHS, + and to configure shared USB channels 0 and 2. + This driver supports R8A7790 and R8A7791. + + To compile this driver as a module, choose M here: the + module will be called phy-rcar-gen2-usb. + config USB_ULPI bool "Generic ULPI Transceiver Driver" depends on ARM diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile index 9c3736109c2c..022c1da7fb78 100644 --- a/drivers/usb/phy/Makefile +++ b/drivers/usb/phy/Makefile @@ -27,5 +27,6 @@ obj-$(CONFIG_USB_MSM_OTG) += phy-msm-usb.o obj-$(CONFIG_USB_MV_OTG) += phy-mv-usb.o obj-$(CONFIG_USB_MXS_PHY) += phy-mxs-usb.o obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o +obj-$(CONFIG_USB_RCAR_GEN2_PHY) += phy-rcar-gen2-usb.o obj-$(CONFIG_USB_ULPI) += phy-ulpi.o obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c index 22cf07d62e4c..634f49acd20e 100644 --- a/drivers/usb/phy/phy-am335x-control.c +++ b/drivers/usb/phy/phy-am335x-control.c @@ -26,6 +26,41 @@ struct am335x_control_usb { #define USBPHY_OTGVDET_EN (1 << 19) #define USBPHY_OTGSESSEND_EN (1 << 20) +#define AM335X_PHY0_WK_EN (1 << 0) +#define AM335X_PHY1_WK_EN (1 << 8) + +static void am335x_phy_wkup(struct phy_control *phy_ctrl, u32 id, bool on) +{ + struct am335x_control_usb *usb_ctrl; + u32 val; + u32 reg; + + usb_ctrl = container_of(phy_ctrl, struct am335x_control_usb, phy_ctrl); + + switch (id) { + case 0: + reg = AM335X_PHY0_WK_EN; + break; + case 1: + reg = AM335X_PHY1_WK_EN; + break; + default: + WARN_ON(1); + return; + } + + spin_lock(&usb_ctrl->lock); + val = readl(usb_ctrl->wkup); + + if (on) + val |= reg; + else + val &= ~reg; + + writel(val, usb_ctrl->wkup); + spin_unlock(&usb_ctrl->lock); +} + static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on) { struct am335x_control_usb *usb_ctrl; @@ -59,6 +94,7 @@ static void am335x_phy_power(struct phy_control *phy_ctrl, u32 id, bool on) static const struct phy_control ctrl_am335x = { .phy_power = am335x_phy_power, + .phy_wkup = am335x_phy_wkup, }; static const struct of_device_id omap_control_usb_id_table[] = { @@ -117,6 +153,12 @@ static int am335x_control_usb_probe(struct platform_device *pdev) ctrl_usb->phy_reg = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(ctrl_usb->phy_reg)) return PTR_ERR(ctrl_usb->phy_reg); + + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wakeup"); + ctrl_usb->wkup = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(ctrl_usb->wkup)) + return PTR_ERR(ctrl_usb->wkup); + spin_lock_init(&ctrl_usb->lock); ctrl_usb->phy_ctrl = *phy_ctrl; @@ -129,7 +171,7 @@ static struct platform_driver am335x_control_driver = { .driver = { .name = "am335x-control-usb", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(omap_control_usb_id_table), + .of_match_table = omap_control_usb_id_table, }, }; diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index c4d614d1f173..6370e50649d7 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -53,21 +53,20 @@ static int am335x_phy_probe(struct platform_device *pdev) } ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, - USB_PHY_TYPE_USB2, 0, false, false); + USB_PHY_TYPE_USB2, 0, false); if (ret) return ret; ret = usb_add_phy_dev(&am_phy->usb_phy_gen.phy); if (ret) - goto err_add; + return ret; am_phy->usb_phy_gen.phy.init = am335x_init; am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown; platform_set_drvdata(pdev, am_phy); + return 0; -err_add: - usb_phy_gen_cleanup_phy(&am_phy->usb_phy_gen); return ret; } @@ -79,6 +78,40 @@ static int am335x_phy_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_RUNTIME + +static int am335x_phy_runtime_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct am335x_phy *am_phy = platform_get_drvdata(pdev); + + if (device_may_wakeup(dev)) + phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, true); + phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false); + return 0; +} + +static int am335x_phy_runtime_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct am335x_phy *am_phy = platform_get_drvdata(pdev); + + phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, true); + if (device_may_wakeup(dev)) + phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, false); + return 0; +} + +static const struct dev_pm_ops am335x_pm_ops = { + SET_RUNTIME_PM_OPS(am335x_phy_runtime_suspend, + am335x_phy_runtime_resume, NULL) +}; + +#define DEV_PM_OPS (&am335x_pm_ops) +#else +#define DEV_PM_OPS NULL +#endif + static const struct of_device_id am335x_phy_ids[] = { { .compatible = "ti,am335x-usb-phy" }, { } @@ -91,7 +124,8 @@ static struct platform_driver am335x_phy_driver = { .driver = { .name = "am335x-phy-driver", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(am335x_phy_ids), + .pm = DEV_PM_OPS, + .of_match_table = am335x_phy_ids, }, }; diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c index fa7c9f9628b5..7f3c73b967ce 100644 --- a/drivers/usb/phy/phy-fsl-usb.c +++ b/drivers/usb/phy/phy-fsl-usb.c @@ -134,7 +134,7 @@ int write_ulpi(u8 addr, u8 data) /* Operations that will be called from OTG Finite State Machine */ /* Charge vbus for vbus pulsing in SRP */ -void fsl_otg_chrg_vbus(int on) +void fsl_otg_chrg_vbus(struct otg_fsm *fsm, int on) { u32 tmp; @@ -170,7 +170,7 @@ void fsl_otg_dischrg_vbus(int on) } /* A-device driver vbus, controlled through PP bit in PORTSC */ -void fsl_otg_drv_vbus(int on) +void fsl_otg_drv_vbus(struct otg_fsm *fsm, int on) { u32 tmp; @@ -188,7 +188,7 @@ void fsl_otg_drv_vbus(int on) * Pull-up D+, signalling connect by periperal. Also used in * data-line pulsing in SRP */ -void fsl_otg_loc_conn(int on) +void fsl_otg_loc_conn(struct otg_fsm *fsm, int on) { u32 tmp; @@ -207,7 +207,7 @@ void fsl_otg_loc_conn(int on) * port. In host mode, controller will automatically send SOF. * Suspend will block the data on the port. */ -void fsl_otg_loc_sof(int on) +void fsl_otg_loc_sof(struct otg_fsm *fsm, int on) { u32 tmp; @@ -222,7 +222,7 @@ void fsl_otg_loc_sof(int on) } /* Start SRP pulsing by data-line pulsing, followed with v-bus pulsing. */ -void fsl_otg_start_pulse(void) +void fsl_otg_start_pulse(struct otg_fsm *fsm) { u32 tmp; @@ -235,7 +235,7 @@ void fsl_otg_start_pulse(void) fsl_otg_loc_conn(1); #endif - fsl_otg_add_timer(b_data_pulse_tmr); + fsl_otg_add_timer(fsm, b_data_pulse_tmr); } void b_data_pulse_end(unsigned long foo) @@ -252,14 +252,14 @@ void b_data_pulse_end(unsigned long foo) void fsl_otg_pulse_vbus(void) { srp_wait_done = 0; - fsl_otg_chrg_vbus(1); + fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 1); /* start the timer to end vbus charge */ - fsl_otg_add_timer(b_vbus_pulse_tmr); + fsl_otg_add_timer(&fsl_otg_dev->fsm, b_vbus_pulse_tmr); } void b_vbus_pulse_end(unsigned long foo) { - fsl_otg_chrg_vbus(0); + fsl_otg_chrg_vbus(&fsl_otg_dev->fsm, 0); /* * As USB3300 using the same a_sess_vld and b_sess_vld voltage @@ -267,7 +267,7 @@ void b_vbus_pulse_end(unsigned long foo) * residual voltage of vbus pulsing and A device pull up */ fsl_otg_dischrg_vbus(1); - fsl_otg_add_timer(b_srp_wait_tmr); + fsl_otg_add_timer(&fsl_otg_dev->fsm, b_srp_wait_tmr); } void b_srp_end(unsigned long foo) @@ -289,7 +289,7 @@ void a_wait_enum(unsigned long foo) { VDBG("a_wait_enum timeout\n"); if (!fsl_otg_dev->phy.otg->host->b_hnp_enable) - fsl_otg_add_timer(a_wait_enum_tmr); + fsl_otg_add_timer(&fsl_otg_dev->fsm, a_wait_enum_tmr); else otg_statemachine(&fsl_otg_dev->fsm); } @@ -375,8 +375,42 @@ void fsl_otg_uninit_timers(void) kfree(b_vbus_pulse_tmr); } +static struct fsl_otg_timer *fsl_otg_get_timer(enum otg_fsm_timer t) +{ + struct fsl_otg_timer *timer; + + /* REVISIT: use array of pointers to timers instead */ + switch (t) { + case A_WAIT_VRISE: + timer = a_wait_vrise_tmr; + break; + case A_WAIT_BCON: + timer = a_wait_vrise_tmr; + break; + case A_AIDL_BDIS: + timer = a_wait_vrise_tmr; + break; + case B_ASE0_BRST: + timer = a_wait_vrise_tmr; + break; + case B_SE0_SRP: + timer = a_wait_vrise_tmr; + break; + case B_SRP_FAIL: + timer = a_wait_vrise_tmr; + break; + case A_WAIT_ENUM: + timer = a_wait_vrise_tmr; + break; + default: + timer = NULL; + } + + return timer; +} + /* Add timer to timer list */ -void fsl_otg_add_timer(void *gtimer) +void fsl_otg_add_timer(struct otg_fsm *fsm, void *gtimer) { struct fsl_otg_timer *timer = gtimer; struct fsl_otg_timer *tmp_timer; @@ -394,8 +428,19 @@ void fsl_otg_add_timer(void *gtimer) list_add_tail(&timer->list, &active_timers); } +static void fsl_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t) +{ + struct fsl_otg_timer *timer; + + timer = fsl_otg_get_timer(t); + if (!timer) + return; + + fsl_otg_add_timer(fsm, timer); +} + /* Remove timer from the timer list; clear timeout status */ -void fsl_otg_del_timer(void *gtimer) +void fsl_otg_del_timer(struct otg_fsm *fsm, void *gtimer) { struct fsl_otg_timer *timer = gtimer; struct fsl_otg_timer *tmp_timer, *del_tmp; @@ -405,6 +450,17 @@ void fsl_otg_del_timer(void *gtimer) list_del(&timer->list); } +static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t) +{ + struct fsl_otg_timer *timer; + + timer = fsl_otg_get_timer(t); + if (!timer) + return; + + fsl_otg_del_timer(fsm, timer); +} + /* * Reduce timer count by 1, and find timeout conditions. * Called by fsl_otg 1ms timer interrupt @@ -468,7 +524,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on) retval = dev->driver->pm->resume(dev); if (fsm->id) { /* default-b */ - fsl_otg_drv_vbus(1); + fsl_otg_drv_vbus(fsm, 1); /* * Workaround: b_host can't driver * vbus, but PP in PORTSC needs to @@ -493,7 +549,7 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on) retval = dev->driver->pm->suspend(dev); if (fsm->id) /* default-b */ - fsl_otg_drv_vbus(0); + fsl_otg_drv_vbus(fsm, 0); } otg_dev->host_working = 0; } @@ -757,8 +813,8 @@ static struct otg_fsm_ops fsl_otg_ops = { .loc_sof = fsl_otg_loc_sof, .start_pulse = fsl_otg_start_pulse, - .add_timer = fsl_otg_add_timer, - .del_timer = fsl_otg_del_timer, + .add_timer = fsl_otg_fsm_add_timer, + .del_timer = fsl_otg_fsm_del_timer, .start_host = fsl_otg_start_host, .start_gadget = fsl_otg_start_gadget, @@ -1011,7 +1067,7 @@ static int show_fsl_usb2_otg_state(struct device *dev, "b_bus_suspend: %d\n" "b_conn: %d\n" "b_se0_srp: %d\n" - "b_sess_end: %d\n" + "b_ssend_srp: %d\n" "b_sess_vld: %d\n" "id: %d\n", fsm->a_bus_req, @@ -1026,7 +1082,7 @@ static int show_fsl_usb2_otg_state(struct device *dev, fsm->b_bus_suspend, fsm->b_conn, fsm->b_se0_srp, - fsm->b_sess_end, + fsm->b_ssend_srp, fsm->b_sess_vld, fsm->id); size -= t; @@ -1057,7 +1113,7 @@ static long fsl_otg_ioctl(struct file *file, unsigned int cmd, break; case SET_A_SUSPEND_REQ: - fsl_otg_dev->fsm.a_suspend_req = arg; + fsl_otg_dev->fsm.a_suspend_req_inf = arg; break; case SET_A_BUS_DROP: diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h index e1859b8ef567..7365170a2f23 100644 --- a/drivers/usb/phy/phy-fsl-usb.h +++ b/drivers/usb/phy/phy-fsl-usb.h @@ -401,6 +401,6 @@ struct fsl_otg_config { #define GET_A_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 8, int) #define GET_B_BUS_REQ _IOR(OTG_IOCTL_MAGIC, 9, int) -void fsl_otg_add_timer(void *timer); -void fsl_otg_del_timer(void *timer); +void fsl_otg_add_timer(struct otg_fsm *fsm, void *timer); +void fsl_otg_del_timer(struct otg_fsm *fsm, void *timer); void fsl_otg_pulse_vbus(void); diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c index 7f4596606e18..329c2d2f8595 100644 --- a/drivers/usb/phy/phy-fsm-usb.c +++ b/drivers/usb/phy/phy-fsm-usb.c @@ -41,17 +41,17 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol) fsm->protocol, protocol); /* stop old protocol */ if (fsm->protocol == PROTO_HOST) - ret = fsm->ops->start_host(fsm, 0); + ret = otg_start_host(fsm, 0); else if (fsm->protocol == PROTO_GADGET) - ret = fsm->ops->start_gadget(fsm, 0); + ret = otg_start_gadget(fsm, 0); if (ret) return ret; /* start new protocol */ if (protocol == PROTO_HOST) - ret = fsm->ops->start_host(fsm, 1); + ret = otg_start_host(fsm, 1); else if (protocol == PROTO_GADGET) - ret = fsm->ops->start_gadget(fsm, 1); + ret = otg_start_gadget(fsm, 1); if (ret) return ret; @@ -69,42 +69,50 @@ void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state) { switch (old_state) { case OTG_STATE_B_IDLE: - otg_del_timer(fsm, b_se0_srp_tmr); + otg_del_timer(fsm, B_SE0_SRP); fsm->b_se0_srp = 0; + fsm->adp_sns = 0; + fsm->adp_prb = 0; break; case OTG_STATE_B_SRP_INIT: + fsm->data_pulse = 0; fsm->b_srp_done = 0; break; case OTG_STATE_B_PERIPHERAL: break; case OTG_STATE_B_WAIT_ACON: - otg_del_timer(fsm, b_ase0_brst_tmr); + otg_del_timer(fsm, B_ASE0_BRST); fsm->b_ase0_brst_tmout = 0; break; case OTG_STATE_B_HOST: break; case OTG_STATE_A_IDLE: + fsm->adp_prb = 0; break; case OTG_STATE_A_WAIT_VRISE: - otg_del_timer(fsm, a_wait_vrise_tmr); + otg_del_timer(fsm, A_WAIT_VRISE); fsm->a_wait_vrise_tmout = 0; break; case OTG_STATE_A_WAIT_BCON: - otg_del_timer(fsm, a_wait_bcon_tmr); + otg_del_timer(fsm, A_WAIT_BCON); fsm->a_wait_bcon_tmout = 0; break; case OTG_STATE_A_HOST: - otg_del_timer(fsm, a_wait_enum_tmr); + otg_del_timer(fsm, A_WAIT_ENUM); break; case OTG_STATE_A_SUSPEND: - otg_del_timer(fsm, a_aidl_bdis_tmr); + otg_del_timer(fsm, A_AIDL_BDIS); fsm->a_aidl_bdis_tmout = 0; - fsm->a_suspend_req = 0; + fsm->a_suspend_req_inf = 0; break; case OTG_STATE_A_PERIPHERAL: + otg_del_timer(fsm, A_BIDL_ADIS); + fsm->a_bidl_adis_tmout = 0; break; case OTG_STATE_A_WAIT_VFALL: - otg_del_timer(fsm, a_wait_vrise_tmr); + otg_del_timer(fsm, A_WAIT_VFALL); + fsm->a_wait_vfall_tmout = 0; + otg_del_timer(fsm, A_WAIT_VRISE); break; case OTG_STATE_A_VBUS_ERR: break; @@ -127,14 +135,19 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) otg_chrg_vbus(fsm, 0); otg_loc_conn(fsm, 0); otg_loc_sof(fsm, 0); + /* + * Driver is responsible for starting ADP probing + * if ADP sensing times out. + */ + otg_start_adp_sns(fsm); otg_set_protocol(fsm, PROTO_UNDEF); - otg_add_timer(fsm, b_se0_srp_tmr); + otg_add_timer(fsm, B_SE0_SRP); break; case OTG_STATE_B_SRP_INIT: otg_start_pulse(fsm); otg_loc_sof(fsm, 0); otg_set_protocol(fsm, PROTO_UNDEF); - otg_add_timer(fsm, b_srp_fail_tmr); + otg_add_timer(fsm, B_SRP_FAIL); break; case OTG_STATE_B_PERIPHERAL: otg_chrg_vbus(fsm, 0); @@ -147,7 +160,7 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) otg_loc_conn(fsm, 0); otg_loc_sof(fsm, 0); otg_set_protocol(fsm, PROTO_HOST); - otg_add_timer(fsm, b_ase0_brst_tmr); + otg_add_timer(fsm, B_ASE0_BRST); fsm->a_bus_suspend = 0; break; case OTG_STATE_B_HOST: @@ -163,6 +176,7 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) otg_chrg_vbus(fsm, 0); otg_loc_conn(fsm, 0); otg_loc_sof(fsm, 0); + otg_start_adp_prb(fsm); otg_set_protocol(fsm, PROTO_HOST); break; case OTG_STATE_A_WAIT_VRISE: @@ -170,14 +184,14 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) otg_loc_conn(fsm, 0); otg_loc_sof(fsm, 0); otg_set_protocol(fsm, PROTO_HOST); - otg_add_timer(fsm, a_wait_vrise_tmr); + otg_add_timer(fsm, A_WAIT_VRISE); break; case OTG_STATE_A_WAIT_BCON: otg_drv_vbus(fsm, 1); otg_loc_conn(fsm, 0); otg_loc_sof(fsm, 0); otg_set_protocol(fsm, PROTO_HOST); - otg_add_timer(fsm, a_wait_bcon_tmr); + otg_add_timer(fsm, A_WAIT_BCON); break; case OTG_STATE_A_HOST: otg_drv_vbus(fsm, 1); @@ -188,15 +202,15 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) * When HNP is triggered while a_bus_req = 0, a_host will * suspend too fast to complete a_set_b_hnp_en */ - if (!fsm->a_bus_req || fsm->a_suspend_req) - otg_add_timer(fsm, a_wait_enum_tmr); + if (!fsm->a_bus_req || fsm->a_suspend_req_inf) + otg_add_timer(fsm, A_WAIT_ENUM); break; case OTG_STATE_A_SUSPEND: otg_drv_vbus(fsm, 1); otg_loc_conn(fsm, 0); otg_loc_sof(fsm, 0); otg_set_protocol(fsm, PROTO_HOST); - otg_add_timer(fsm, a_aidl_bdis_tmr); + otg_add_timer(fsm, A_AIDL_BDIS); break; case OTG_STATE_A_PERIPHERAL: @@ -204,12 +218,14 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state) otg_loc_sof(fsm, 0); otg_set_protocol(fsm, PROTO_GADGET); otg_drv_vbus(fsm, 1); + otg_add_timer(fsm, A_BIDL_ADIS); break; case OTG_STATE_A_WAIT_VFALL: otg_drv_vbus(fsm, 0); otg_loc_conn(fsm, 0); otg_loc_sof(fsm, 0); otg_set_protocol(fsm, PROTO_HOST); + otg_add_timer(fsm, A_WAIT_VFALL); break; case OTG_STATE_A_VBUS_ERR: otg_drv_vbus(fsm, 0); @@ -250,7 +266,8 @@ int otg_statemachine(struct otg_fsm *fsm) otg_set_state(fsm, OTG_STATE_A_IDLE); else if (fsm->b_sess_vld && fsm->otg->gadget) otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); - else if (fsm->b_bus_req && fsm->b_sess_end && fsm->b_se0_srp) + else if ((fsm->b_bus_req || fsm->adp_change || fsm->power_up) && + fsm->b_ssend_srp && fsm->b_se0_srp) otg_set_state(fsm, OTG_STATE_B_SRP_INIT); break; case OTG_STATE_B_SRP_INIT: @@ -277,13 +294,14 @@ int otg_statemachine(struct otg_fsm *fsm) case OTG_STATE_B_HOST: if (!fsm->id || !fsm->b_sess_vld) otg_set_state(fsm, OTG_STATE_B_IDLE); - else if (!fsm->b_bus_req || !fsm->a_conn) + else if (!fsm->b_bus_req || !fsm->a_conn || fsm->test_device) otg_set_state(fsm, OTG_STATE_B_PERIPHERAL); break; case OTG_STATE_A_IDLE: if (fsm->id) otg_set_state(fsm, OTG_STATE_B_IDLE); - else if (!fsm->a_bus_drop && (fsm->a_bus_req || fsm->a_srp_det)) + else if (!fsm->a_bus_drop && (fsm->a_bus_req || + fsm->a_srp_det || fsm->adp_change || fsm->power_up)) otg_set_state(fsm, OTG_STATE_A_WAIT_VRISE); break; case OTG_STATE_A_WAIT_VRISE: @@ -301,7 +319,7 @@ int otg_statemachine(struct otg_fsm *fsm) otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); break; case OTG_STATE_A_HOST: - if ((!fsm->a_bus_req || fsm->a_suspend_req) && + if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) && fsm->otg->host->b_hnp_enable) otg_set_state(fsm, OTG_STATE_A_SUSPEND); else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop) @@ -324,14 +342,14 @@ int otg_statemachine(struct otg_fsm *fsm) case OTG_STATE_A_PERIPHERAL: if (fsm->id || fsm->a_bus_drop) otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL); - else if (fsm->b_bus_suspend) + else if (fsm->a_bidl_adis_tmout || fsm->b_bus_suspend) otg_set_state(fsm, OTG_STATE_A_WAIT_BCON); else if (!fsm->a_vbus_vld) otg_set_state(fsm, OTG_STATE_A_VBUS_ERR); break; case OTG_STATE_A_WAIT_VFALL: - if (fsm->id || fsm->a_bus_req || (!fsm->a_sess_vld && - !fsm->b_conn)) + if (fsm->a_wait_vfall_tmout || fsm->id || fsm->a_bus_req || + (!fsm->a_sess_vld && !fsm->b_conn)) otg_set_state(fsm, OTG_STATE_A_IDLE); break; case OTG_STATE_A_VBUS_ERR: diff --git a/drivers/usb/phy/phy-fsm-usb.h b/drivers/usb/phy/phy-fsm-usb.h index fbe586206f33..7441b46a27f1 100644 --- a/drivers/usb/phy/phy-fsm-usb.h +++ b/drivers/usb/phy/phy-fsm-usb.h @@ -34,45 +34,76 @@ #define PROTO_HOST (1) #define PROTO_GADGET (2) +enum otg_fsm_timer { + /* Standard OTG timers */ + A_WAIT_VRISE, + A_WAIT_VFALL, + A_WAIT_BCON, + A_AIDL_BDIS, + B_ASE0_BRST, + A_BIDL_ADIS, + + /* Auxiliary timers */ + B_SE0_SRP, + B_SRP_FAIL, + A_WAIT_ENUM, + + NUM_OTG_FSM_TIMERS, +}; + /* OTG state machine according to the OTG spec */ struct otg_fsm { /* Input */ + int id; + int adp_change; + int power_up; + int test_device; + int a_bus_drop; + int a_bus_req; + int a_srp_det; + int a_vbus_vld; + int b_conn; int a_bus_resume; int a_bus_suspend; int a_conn; + int b_bus_req; + int b_se0_srp; + int b_ssend_srp; + int b_sess_vld; + /* Auxilary inputs */ int a_sess_vld; - int a_srp_det; - int a_vbus_vld; int b_bus_resume; int b_bus_suspend; - int b_conn; - int b_se0_srp; - int b_sess_end; - int b_sess_vld; - int id; + + /* Output */ + int data_pulse; + int drv_vbus; + int loc_conn; + int loc_sof; + int adp_prb; + int adp_sns; /* Internal variables */ int a_set_b_hnp_en; int b_srp_done; int b_hnp_enable; + int a_clr_err; + + /* Informative variables */ + int a_bus_drop_inf; + int a_bus_req_inf; + int a_clr_err_inf; + int b_bus_req_inf; + /* Auxilary informative variables */ + int a_suspend_req_inf; /* Timeout indicator for timers */ int a_wait_vrise_tmout; + int a_wait_vfall_tmout; int a_wait_bcon_tmout; int a_aidl_bdis_tmout; int b_ase0_brst_tmout; - - /* Informative variables */ - int a_bus_drop; - int a_bus_req; - int a_clr_err; - int a_suspend_req; - int b_bus_req; - - /* Output */ - int drv_vbus; - int loc_conn; - int loc_sof; + int a_bidl_adis_tmout; struct otg_fsm_ops *ops; struct usb_otg *otg; @@ -83,65 +114,123 @@ struct otg_fsm { }; struct otg_fsm_ops { - void (*chrg_vbus)(int on); - void (*drv_vbus)(int on); - void (*loc_conn)(int on); - void (*loc_sof)(int on); - void (*start_pulse)(void); - void (*add_timer)(void *timer); - void (*del_timer)(void *timer); + void (*chrg_vbus)(struct otg_fsm *fsm, int on); + void (*drv_vbus)(struct otg_fsm *fsm, int on); + void (*loc_conn)(struct otg_fsm *fsm, int on); + void (*loc_sof)(struct otg_fsm *fsm, int on); + void (*start_pulse)(struct otg_fsm *fsm); + void (*start_adp_prb)(struct otg_fsm *fsm); + void (*start_adp_sns)(struct otg_fsm *fsm); + void (*add_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer); + void (*del_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer); int (*start_host)(struct otg_fsm *fsm, int on); int (*start_gadget)(struct otg_fsm *fsm, int on); }; -static inline void otg_chrg_vbus(struct otg_fsm *fsm, int on) +static inline int otg_chrg_vbus(struct otg_fsm *fsm, int on) { - fsm->ops->chrg_vbus(on); + if (!fsm->ops->chrg_vbus) + return -EOPNOTSUPP; + fsm->ops->chrg_vbus(fsm, on); + return 0; } -static inline void otg_drv_vbus(struct otg_fsm *fsm, int on) +static inline int otg_drv_vbus(struct otg_fsm *fsm, int on) { + if (!fsm->ops->drv_vbus) + return -EOPNOTSUPP; if (fsm->drv_vbus != on) { fsm->drv_vbus = on; - fsm->ops->drv_vbus(on); + fsm->ops->drv_vbus(fsm, on); } + return 0; } -static inline void otg_loc_conn(struct otg_fsm *fsm, int on) +static inline int otg_loc_conn(struct otg_fsm *fsm, int on) { + if (!fsm->ops->loc_conn) + return -EOPNOTSUPP; if (fsm->loc_conn != on) { fsm->loc_conn = on; - fsm->ops->loc_conn(on); + fsm->ops->loc_conn(fsm, on); } + return 0; } -static inline void otg_loc_sof(struct otg_fsm *fsm, int on) +static inline int otg_loc_sof(struct otg_fsm *fsm, int on) { + if (!fsm->ops->loc_sof) + return -EOPNOTSUPP; if (fsm->loc_sof != on) { fsm->loc_sof = on; - fsm->ops->loc_sof(on); + fsm->ops->loc_sof(fsm, on); + } + return 0; +} + +static inline int otg_start_pulse(struct otg_fsm *fsm) +{ + if (!fsm->ops->start_pulse) + return -EOPNOTSUPP; + if (!fsm->data_pulse) { + fsm->data_pulse = 1; + fsm->ops->start_pulse(fsm); } + return 0; } -static inline void otg_start_pulse(struct otg_fsm *fsm) +static inline int otg_start_adp_prb(struct otg_fsm *fsm) { - fsm->ops->start_pulse(); + if (!fsm->ops->start_adp_prb) + return -EOPNOTSUPP; + if (!fsm->adp_prb) { + fsm->adp_sns = 0; + fsm->adp_prb = 1; + fsm->ops->start_adp_prb(fsm); + } + return 0; } -static inline void otg_add_timer(struct otg_fsm *fsm, void *timer) +static inline int otg_start_adp_sns(struct otg_fsm *fsm) { - fsm->ops->add_timer(timer); + if (!fsm->ops->start_adp_sns) + return -EOPNOTSUPP; + if (!fsm->adp_sns) { + fsm->adp_sns = 1; + fsm->ops->start_adp_sns(fsm); + } + return 0; } -static inline void otg_del_timer(struct otg_fsm *fsm, void *timer) +static inline int otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer) { - fsm->ops->del_timer(timer); + if (!fsm->ops->add_timer) + return -EOPNOTSUPP; + fsm->ops->add_timer(fsm, timer); + return 0; } -int otg_statemachine(struct otg_fsm *fsm); +static inline int otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer) +{ + if (!fsm->ops->del_timer) + return -EOPNOTSUPP; + fsm->ops->del_timer(fsm, timer); + return 0; +} -/* Defined by device specific driver, for different timer implementation */ -extern struct fsl_otg_timer *a_wait_vrise_tmr, *a_wait_bcon_tmr, - *a_aidl_bdis_tmr, *b_ase0_brst_tmr, *b_se0_srp_tmr, *b_srp_fail_tmr, - *a_wait_enum_tmr; +static inline int otg_start_host(struct otg_fsm *fsm, int on) +{ + if (!fsm->ops->start_host) + return -EOPNOTSUPP; + return fsm->ops->start_host(fsm, on); +} + +static inline int otg_start_gadget(struct otg_fsm *fsm, int on) +{ + if (!fsm->ops->start_gadget) + return -EOPNOTSUPP; + return fsm->ops->start_gadget(fsm, on); +} + +int otg_statemachine(struct otg_fsm *fsm); diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c index efe59f3f7fda..fce3a9e9bb5d 100644 --- a/drivers/usb/phy/phy-generic.c +++ b/drivers/usb/phy/phy-generic.c @@ -35,6 +35,9 @@ #include <linux/clk.h> #include <linux/regulator/consumer.h> #include <linux/of.h> +#include <linux/of_gpio.h> +#include <linux/gpio.h> +#include <linux/delay.h> #include "phy-generic.h" @@ -64,6 +67,23 @@ static int nop_set_suspend(struct usb_phy *x, int suspend) return 0; } +static void nop_reset_set(struct usb_phy_gen_xceiv *nop, int asserted) +{ + int value; + + if (!gpio_is_valid(nop->gpio_reset)) + return; + + value = asserted; + if (nop->reset_active_low) + value = !value; + + gpio_set_value_cansleep(nop->gpio_reset, value); + + if (!asserted) + usleep_range(10000, 20000); +} + int usb_gen_phy_init(struct usb_phy *phy) { struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); @@ -74,13 +94,10 @@ int usb_gen_phy_init(struct usb_phy *phy) } if (!IS_ERR(nop->clk)) - clk_enable(nop->clk); + clk_prepare_enable(nop->clk); - if (!IS_ERR(nop->reset)) { - /* De-assert RESET */ - if (regulator_enable(nop->reset)) - dev_err(phy->dev, "Failed to de-assert reset\n"); - } + /* De-assert RESET */ + nop_reset_set(nop, 0); return 0; } @@ -90,14 +107,11 @@ void usb_gen_phy_shutdown(struct usb_phy *phy) { struct usb_phy_gen_xceiv *nop = dev_get_drvdata(phy->dev); - if (!IS_ERR(nop->reset)) { - /* Assert RESET */ - if (regulator_disable(nop->reset)) - dev_err(phy->dev, "Failed to assert reset\n"); - } + /* Assert RESET */ + nop_reset_set(nop, 1); if (!IS_ERR(nop->clk)) - clk_disable(nop->clk); + clk_disable_unprepare(nop->clk); if (!IS_ERR(nop->vcc)) { if (regulator_disable(nop->vcc)) @@ -136,8 +150,7 @@ static int nop_set_host(struct usb_otg *otg, struct usb_bus *host) } int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, - enum usb_phy_type type, u32 clk_rate, bool needs_vcc, - bool needs_reset) + enum usb_phy_type type, u32 clk_rate, bool needs_vcc) { int err; @@ -160,14 +173,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, } } - if (!IS_ERR(nop->clk)) { - err = clk_prepare(nop->clk); - if (err) { - dev_err(dev, "Error preparing clock\n"); - return err; - } - } - nop->vcc = devm_regulator_get(dev, "vcc"); if (IS_ERR(nop->vcc)) { dev_dbg(dev, "Error getting vcc regulator: %ld\n", @@ -176,12 +181,22 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, return -EPROBE_DEFER; } - nop->reset = devm_regulator_get(dev, "reset"); - if (IS_ERR(nop->reset)) { - dev_dbg(dev, "Error getting reset regulator: %ld\n", - PTR_ERR(nop->reset)); - if (needs_reset) - return -EPROBE_DEFER; + if (gpio_is_valid(nop->gpio_reset)) { + unsigned long gpio_flags; + + /* Assert RESET */ + if (nop->reset_active_low) + gpio_flags = GPIOF_OUT_INIT_LOW; + else + gpio_flags = GPIOF_OUT_INIT_HIGH; + + err = devm_gpio_request_one(dev, nop->gpio_reset, + gpio_flags, dev_name(dev)); + if (err) { + dev_err(dev, "Error requesting RESET GPIO %d\n", + nop->gpio_reset); + return err; + } } nop->dev = dev; @@ -200,13 +215,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, } EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy); -void usb_phy_gen_cleanup_phy(struct usb_phy_gen_xceiv *nop) -{ - if (!IS_ERR(nop->clk)) - clk_unprepare(nop->clk); -} -EXPORT_SYMBOL_GPL(usb_phy_gen_cleanup_phy); - static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -217,31 +225,36 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) int err; u32 clk_rate = 0; bool needs_vcc = false; - bool needs_reset = false; + + nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL); + if (!nop) + return -ENOMEM; + + nop->reset_active_low = true; /* default behaviour */ if (dev->of_node) { struct device_node *node = dev->of_node; + enum of_gpio_flags flags; if (of_property_read_u32(node, "clock-frequency", &clk_rate)) clk_rate = 0; needs_vcc = of_property_read_bool(node, "vcc-supply"); - needs_reset = of_property_read_bool(node, "reset-supply"); + nop->gpio_reset = of_get_named_gpio_flags(node, "reset-gpios", + 0, &flags); + if (nop->gpio_reset == -EPROBE_DEFER) + return -EPROBE_DEFER; + + nop->reset_active_low = flags & OF_GPIO_ACTIVE_LOW; } else if (pdata) { type = pdata->type; clk_rate = pdata->clk_rate; needs_vcc = pdata->needs_vcc; - needs_reset = pdata->needs_reset; + nop->gpio_reset = pdata->gpio_reset; } - nop = devm_kzalloc(dev, sizeof(*nop), GFP_KERNEL); - if (!nop) - return -ENOMEM; - - - err = usb_phy_gen_create_phy(dev, nop, type, clk_rate, needs_vcc, - needs_reset); + err = usb_phy_gen_create_phy(dev, nop, type, clk_rate, needs_vcc); if (err) return err; @@ -252,15 +265,13 @@ static int usb_phy_gen_xceiv_probe(struct platform_device *pdev) if (err) { dev_err(&pdev->dev, "can't register transceiver, err: %d\n", err); - goto err_add; + return err; } platform_set_drvdata(pdev, nop); return 0; -err_add: - usb_phy_gen_cleanup_phy(nop); return err; } @@ -268,7 +279,6 @@ static int usb_phy_gen_xceiv_remove(struct platform_device *pdev) { struct usb_phy_gen_xceiv *nop = platform_get_drvdata(pdev); - usb_phy_gen_cleanup_phy(nop); usb_remove_phy(&nop->phy); return 0; diff --git a/drivers/usb/phy/phy-generic.h b/drivers/usb/phy/phy-generic.h index 61687d5a965b..d2a220d81734 100644 --- a/drivers/usb/phy/phy-generic.h +++ b/drivers/usb/phy/phy-generic.h @@ -6,15 +6,14 @@ struct usb_phy_gen_xceiv { struct device *dev; struct clk *clk; struct regulator *vcc; - struct regulator *reset; + int gpio_reset; + bool reset_active_low; }; int usb_gen_phy_init(struct usb_phy *phy); void usb_gen_phy_shutdown(struct usb_phy *phy); int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop, - enum usb_phy_type type, u32 clk_rate, bool needs_vcc, - bool needs_reset); -void usb_phy_gen_cleanup_phy(struct usb_phy_gen_xceiv *nop); + enum usb_phy_type type, u32 clk_rate, bool needs_vcc); #endif diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c new file mode 100644 index 000000000000..a99a6953f11c --- /dev/null +++ b/drivers/usb/phy/phy-rcar-gen2-usb.c @@ -0,0 +1,248 @@ +/* + * Renesas R-Car Gen2 USB phy driver + * + * Copyright (C) 2013 Renesas Solutions Corp. + * Copyright (C) 2013 Cogent Embedded, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_data/usb-rcar-gen2-phy.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/usb/otg.h> + +struct rcar_gen2_usb_phy_priv { + struct usb_phy phy; + void __iomem *base; + struct clk *clk; + spinlock_t lock; + int usecount; + u32 ugctrl2; +}; + +#define usb_phy_to_priv(p) container_of(p, struct rcar_gen2_usb_phy_priv, phy) + +/* Low Power Status register */ +#define USBHS_LPSTS_REG 0x02 +#define USBHS_LPSTS_SUSPM (1 << 14) + +/* USB General control register */ +#define USBHS_UGCTRL_REG 0x80 +#define USBHS_UGCTRL_CONNECT (1 << 2) +#define USBHS_UGCTRL_PLLRESET (1 << 0) + +/* USB General control register 2 */ +#define USBHS_UGCTRL2_REG 0x84 +#define USBHS_UGCTRL2_USB0_PCI (1 << 4) +#define USBHS_UGCTRL2_USB0_HS (3 << 4) +#define USBHS_UGCTRL2_USB2_PCI (0 << 31) +#define USBHS_UGCTRL2_USB2_SS (1 << 31) + +/* USB General status register */ +#define USBHS_UGSTS_REG 0x88 +#define USBHS_UGSTS_LOCK (3 << 8) + +/* Enable USBHS internal phy */ +static int __rcar_gen2_usbhs_phy_enable(void __iomem *base) +{ + u32 val; + int i; + + /* USBHS PHY power on */ + val = ioread32(base + USBHS_UGCTRL_REG); + val &= ~USBHS_UGCTRL_PLLRESET; + iowrite32(val, base + USBHS_UGCTRL_REG); + + val = ioread16(base + USBHS_LPSTS_REG); + val |= USBHS_LPSTS_SUSPM; + iowrite16(val, base + USBHS_LPSTS_REG); + + for (i = 0; i < 20; i++) { + val = ioread32(base + USBHS_UGSTS_REG); + if ((val & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) { + val = ioread32(base + USBHS_UGCTRL_REG); + val |= USBHS_UGCTRL_CONNECT; + iowrite32(val, base + USBHS_UGCTRL_REG); + return 0; + } + udelay(1); + } + + /* Timed out waiting for the PLL lock */ + return -ETIMEDOUT; +} + +/* Disable USBHS internal phy */ +static int __rcar_gen2_usbhs_phy_disable(void __iomem *base) +{ + u32 val; + + /* USBHS PHY power off */ + val = ioread32(base + USBHS_UGCTRL_REG); + val &= ~USBHS_UGCTRL_CONNECT; + iowrite32(val, base + USBHS_UGCTRL_REG); + + val = ioread16(base + USBHS_LPSTS_REG); + val &= ~USBHS_LPSTS_SUSPM; + iowrite16(val, base + USBHS_LPSTS_REG); + + val = ioread32(base + USBHS_UGCTRL_REG); + val |= USBHS_UGCTRL_PLLRESET; + iowrite32(val, base + USBHS_UGCTRL_REG); + return 0; +} + +/* Setup USB channels */ +static void __rcar_gen2_usb_phy_init(struct rcar_gen2_usb_phy_priv *priv) +{ + u32 val; + + clk_prepare_enable(priv->clk); + + /* Set USB channels in the USBHS UGCTRL2 register */ + val = ioread32(priv->base); + val &= ~(USBHS_UGCTRL2_USB0_HS | USBHS_UGCTRL2_USB2_SS); + val |= priv->ugctrl2; + iowrite32(val, priv->base); +} + +/* Shutdown USB channels */ +static void __rcar_gen2_usb_phy_shutdown(struct rcar_gen2_usb_phy_priv *priv) +{ + __rcar_gen2_usbhs_phy_disable(priv->base); + clk_disable_unprepare(priv->clk); +} + +static int rcar_gen2_usb_phy_set_suspend(struct usb_phy *phy, int suspend) +{ + struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); + unsigned long flags; + int retval; + + spin_lock_irqsave(&priv->lock, flags); + retval = suspend ? __rcar_gen2_usbhs_phy_disable(priv->base) : + __rcar_gen2_usbhs_phy_enable(priv->base); + spin_unlock_irqrestore(&priv->lock, flags); + return retval; +} + +static int rcar_gen2_usb_phy_init(struct usb_phy *phy) +{ + struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + /* + * Enable the clock and setup USB channels + * if it's the first user + */ + if (!priv->usecount++) + __rcar_gen2_usb_phy_init(priv); + spin_unlock_irqrestore(&priv->lock, flags); + return 0; +} + +static void rcar_gen2_usb_phy_shutdown(struct usb_phy *phy) +{ + struct rcar_gen2_usb_phy_priv *priv = usb_phy_to_priv(phy); + unsigned long flags; + + spin_lock_irqsave(&priv->lock, flags); + if (!priv->usecount) { + dev_warn(phy->dev, "Trying to disable phy with 0 usecount\n"); + goto out; + } + + /* Disable everything if it's the last user */ + if (!--priv->usecount) + __rcar_gen2_usb_phy_shutdown(priv); +out: + spin_unlock_irqrestore(&priv->lock, flags); +} + +static int rcar_gen2_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct rcar_gen2_phy_platform_data *pdata; + struct rcar_gen2_usb_phy_priv *priv; + struct resource *res; + void __iomem *base; + struct clk *clk; + int retval; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(dev, "No platform data\n"); + return -EINVAL; + } + + clk = devm_clk_get(&pdev->dev, "usbhs"); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "Can't get the clock\n"); + return PTR_ERR(clk); + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) { + dev_err(dev, "Memory allocation failed\n"); + return -ENOMEM; + } + + spin_lock_init(&priv->lock); + priv->clk = clk; + priv->base = base; + priv->ugctrl2 = pdata->chan0_pci ? + USBHS_UGCTRL2_USB0_PCI : USBHS_UGCTRL2_USB0_HS; + priv->ugctrl2 |= pdata->chan2_pci ? + USBHS_UGCTRL2_USB2_PCI : USBHS_UGCTRL2_USB2_SS; + priv->phy.dev = dev; + priv->phy.label = dev_name(dev); + priv->phy.init = rcar_gen2_usb_phy_init; + priv->phy.shutdown = rcar_gen2_usb_phy_shutdown; + priv->phy.set_suspend = rcar_gen2_usb_phy_set_suspend; + + retval = usb_add_phy(&priv->phy, USB_PHY_TYPE_USB2); + if (retval < 0) { + dev_err(dev, "Failed to add USB phy\n"); + return retval; + } + + platform_set_drvdata(pdev, priv); + + return retval; +} + +static int rcar_gen2_usb_phy_remove(struct platform_device *pdev) +{ + struct rcar_gen2_usb_phy_priv *priv = platform_get_drvdata(pdev); + + usb_remove_phy(&priv->phy); + + return 0; +} + +static struct platform_driver rcar_gen2_usb_phy_driver = { + .driver = { + .name = "usb_phy_rcar_gen2", + }, + .probe = rcar_gen2_usb_phy_probe, + .remove = rcar_gen2_usb_phy_remove, +}; + +module_platform_driver(rcar_gen2_usb_phy_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Renesas R-Car Gen2 USB phy"); +MODULE_AUTHOR("Valentine Barshak <valentine.barshak@cogentembedded.com>"); diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index e9cb1cb8abc7..82232acf1ab6 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -1090,7 +1090,7 @@ static struct platform_driver tegra_usb_phy_driver = { .driver = { .name = "tegra-phy", .owner = THIS_MODULE, - .of_match_table = of_match_ptr(tegra_usb_phy_id_table), + .of_match_table = tegra_usb_phy_id_table, }, }; module_platform_driver(tegra_usb_phy_driver); diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c index 16dbc9382678..30e8a61552d4 100644 --- a/drivers/usb/phy/phy-twl6030-usb.c +++ b/drivers/usb/phy/phy-twl6030-usb.c @@ -33,6 +33,7 @@ #include <linux/err.h> #include <linux/slab.h> #include <linux/delay.h> +#include <linux/of.h> /* usb register definitions */ #define USB_VENDOR_ID_LSB 0x00 diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index a9984c700d2c..1b74523e1fee 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -98,7 +98,7 @@ struct usb_phy *devm_usb_get_phy(struct device *dev, enum usb_phy_type type) ptr = devres_alloc(devm_usb_phy_release, sizeof(*ptr), GFP_KERNEL); if (!ptr) - return NULL; + return ERR_PTR(-ENOMEM); phy = usb_get_phy(type); if (!IS_ERR(phy)) { |