diff options
Diffstat (limited to 'drivers/usb/renesas_usbhs/common.c')
| -rw-r--r-- | drivers/usb/renesas_usbhs/common.c | 168 |
1 files changed, 114 insertions, 54 deletions
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index 96f3939a65e2..cf4a0367d6d6 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -11,7 +11,7 @@ #include <linux/gpio/consumer.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/of_device.h> +#include <linux/of.h> #include <linux/pm_runtime.h> #include <linux/reset.h> #include <linux/slab.h> @@ -312,8 +312,10 @@ static int usbhsc_clk_get(struct device *dev, struct usbhs_priv *priv) priv->clks[1] = of_clk_get(dev_of_node(dev), 1); if (PTR_ERR(priv->clks[1]) == -ENOENT) priv->clks[1] = NULL; - else if (IS_ERR(priv->clks[1])) + else if (IS_ERR(priv->clks[1])) { + clk_put(priv->clks[0]); return PTR_ERR(priv->clks[1]); + } return 0; } @@ -363,14 +365,14 @@ static void usbhsc_clk_disable_unprepare(struct usbhs_priv *priv) * platform default param */ -/* commonly used on old SH-Mobile SoCs */ +/* commonly used on old SH-Mobile and RZ/G2L family SoCs */ static struct renesas_usbhs_driver_pipe_config usbhsc_default_pipe[] = { RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false), - RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, false), - RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x18, false), - RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x28, true), - RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x38, true), + RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, true), + RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x28, true), RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x48, true), + RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x58, true), + RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x68, true), RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x04, false), RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x05, false), RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x06, false), @@ -566,6 +568,22 @@ static const struct of_device_id usbhs_of_match[] = { .data = &usbhs_rcar_gen3_with_pll_plat_info, }, { + .compatible = "renesas,usbhs-r9a07g043", + .data = &usbhs_rzg2l_plat_info, + }, + { + .compatible = "renesas,usbhs-r9a07g044", + .data = &usbhs_rzg2l_plat_info, + }, + { + .compatible = "renesas,usbhs-r9a07g054", + .data = &usbhs_rzg2l_plat_info, + }, + { + .compatible = "renesas,usbhs-r9a09g077", + .data = &usbhs_rzg2l_plat_info, + }, + { .compatible = "renesas,rcar-gen2-usbhs", .data = &usbhs_rcar_gen2_plat_info, }, @@ -581,7 +599,11 @@ static const struct of_device_id usbhs_of_match[] = { .compatible = "renesas,rza2-usbhs", .data = &usbhs_rza2_plat_info, }, - { }, + { + .compatible = "renesas,rzg2l-usbhs", + .data = &usbhs_rzg2l_plat_info, + }, + { } }; MODULE_DEVICE_TABLE(of, usbhs_of_match); @@ -595,16 +617,11 @@ static int usbhs_probe(struct platform_device *pdev) u32 tmp; int irq; - /* check device node */ - if (dev_of_node(dev)) - info = of_device_get_match_data(dev); - else - info = renesas_usbhs_get_info(pdev); - - /* check platform information */ + info = of_device_get_match_data(dev); if (!info) { - dev_err(dev, "no platform information\n"); - return -EINVAL; + info = dev_get_platdata(dev); + if (!info) + return dev_err_probe(dev, -EINVAL, "no platform info\n"); } /* platform data */ @@ -621,7 +638,7 @@ static int usbhs_probe(struct platform_device *pdev) if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - if (of_property_read_bool(dev_of_node(dev), "extcon")) { + if (of_property_present(dev_of_node(dev), "extcon")) { priv->edev = extcon_get_edev_by_phandle(dev, 0); if (IS_ERR(priv->edev)) return PTR_ERR(priv->edev); @@ -672,10 +689,29 @@ static int usbhs_probe(struct platform_device *pdev) INIT_DELAYED_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug); spin_lock_init(usbhs_priv_to_lock(priv)); + /* + * Acquire clocks and enable power management (PM) early in the + * probe process, as the driver accesses registers during + * initialization. Ensure the device is active before proceeding. + */ + pm_runtime_enable(dev); + + ret = usbhsc_clk_get(dev, priv); + if (ret) + goto probe_pm_disable; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + goto probe_clk_put; + + ret = usbhsc_clk_prepare_enable(priv); + if (ret) + goto probe_pm_put; + /* call pipe and module init */ ret = usbhs_pipe_probe(priv); if (ret < 0) - return ret; + goto probe_clk_dis_unprepare; ret = usbhs_fifo_probe(priv); if (ret < 0) @@ -685,19 +721,15 @@ static int usbhs_probe(struct platform_device *pdev) if (ret < 0) goto probe_end_fifo_exit; - /* dev_set_drvdata should be called after usbhs_mod_init */ + /* platform_set_drvdata() should be called after usbhs_mod_probe() */ platform_set_drvdata(pdev, priv); ret = reset_control_deassert(priv->rsts); if (ret) goto probe_fail_rst; - ret = usbhsc_clk_get(dev, priv); - if (ret) - goto probe_fail_clks; - /* - * deviece reset here because + * device reset here because * USB device might be used in boot loader. */ usbhs_sys_clock_ctrl(priv, 0); @@ -708,7 +740,7 @@ static int usbhs_probe(struct platform_device *pdev) if (ret) { dev_warn(dev, "USB function not selected (GPIO)\n"); ret = -ENOTSUPP; - goto probe_end_mod_exit; + goto probe_assert_rest; } } @@ -722,14 +754,19 @@ static int usbhs_probe(struct platform_device *pdev) ret = usbhs_platform_call(priv, hardware_init, pdev); if (ret < 0) { dev_err(dev, "platform init failed.\n"); - goto probe_end_mod_exit; + goto probe_assert_rest; } /* reset phy for connection */ usbhs_platform_call(priv, phy_reset, pdev); - /* power control */ - pm_runtime_enable(dev); + /* + * Disable the clocks that were enabled earlier in the probe path, + * and let the driver handle the clocks beyond this point. + */ + usbhsc_clk_disable_unprepare(priv); + pm_runtime_put(dev); + if (!usbhs_get_dparam(priv, runtime_pwctrl)) { usbhsc_power_ctrl(priv, 1); usbhs_mod_autonomy_mode(priv); @@ -746,9 +783,7 @@ static int usbhs_probe(struct platform_device *pdev) return ret; -probe_end_mod_exit: - usbhsc_clk_put(priv); -probe_fail_clks: +probe_assert_rest: reset_control_assert(priv->rsts); probe_fail_rst: usbhs_mod_remove(priv); @@ -756,38 +791,62 @@ probe_end_fifo_exit: usbhs_fifo_remove(priv); probe_end_pipe_exit: usbhs_pipe_remove(priv); +probe_clk_dis_unprepare: + usbhsc_clk_disable_unprepare(priv); +probe_pm_put: + pm_runtime_put(dev); +probe_clk_put: + usbhsc_clk_put(priv); +probe_pm_disable: + pm_runtime_disable(dev); dev_info(dev, "probe failed (%d)\n", ret); return ret; } -static int usbhs_remove(struct platform_device *pdev) +static void usbhs_remove(struct platform_device *pdev) { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); dev_dbg(&pdev->dev, "usb remove\n"); - /* power off */ - if (!usbhs_get_dparam(priv, runtime_pwctrl)) - usbhsc_power_ctrl(priv, 0); - - pm_runtime_disable(&pdev->dev); + flush_delayed_work(&priv->notify_hotplug_work); usbhs_platform_call(priv, hardware_exit, pdev); - usbhsc_clk_put(priv); reset_control_assert(priv->rsts); usbhs_mod_remove(priv); usbhs_fifo_remove(priv); usbhs_pipe_remove(priv); - return 0; + /* power off */ + if (!usbhs_get_dparam(priv, runtime_pwctrl)) + usbhsc_power_ctrl(priv, 0); + + usbhsc_clk_put(priv); + pm_runtime_disable(&pdev->dev); } -static __maybe_unused int usbhsc_suspend(struct device *dev) +static void usbhsc_restore(struct device *dev) +{ + struct usbhs_priv *priv = dev_get_drvdata(dev); + struct platform_device *pdev = usbhs_priv_to_pdev(priv); + + if (!usbhs_get_dparam(priv, runtime_pwctrl)) { + usbhsc_power_ctrl(priv, 1); + usbhs_mod_autonomy_mode(priv); + } + + usbhs_platform_call(priv, phy_reset, pdev); + + usbhsc_schedule_notify_hotplug(pdev); +} + +static int usbhsc_suspend(struct device *dev) { struct usbhs_priv *priv = dev_get_drvdata(dev); struct usbhs_mod *mod = usbhs_mod_get_current(priv); + int ret; if (mod) { usbhs_mod_call(priv, stop, priv); @@ -797,33 +856,34 @@ static __maybe_unused int usbhsc_suspend(struct device *dev) if (mod || !usbhs_get_dparam(priv, runtime_pwctrl)) usbhsc_power_ctrl(priv, 0); - return 0; + ret = reset_control_assert(priv->rsts); + if (ret) + usbhsc_restore(dev); + + return ret; } -static __maybe_unused int usbhsc_resume(struct device *dev) +static int usbhsc_resume(struct device *dev) { struct usbhs_priv *priv = dev_get_drvdata(dev); - struct platform_device *pdev = usbhs_priv_to_pdev(priv); - - if (!usbhs_get_dparam(priv, runtime_pwctrl)) { - usbhsc_power_ctrl(priv, 1); - usbhs_mod_autonomy_mode(priv); - } + int ret; - usbhs_platform_call(priv, phy_reset, pdev); + ret = reset_control_deassert(priv->rsts); + if (ret) + return ret; - usbhsc_schedule_notify_hotplug(pdev); + usbhsc_restore(dev); return 0; } -static SIMPLE_DEV_PM_OPS(usbhsc_pm_ops, usbhsc_suspend, usbhsc_resume); +static DEFINE_SIMPLE_DEV_PM_OPS(usbhsc_pm_ops, usbhsc_suspend, usbhsc_resume); static struct platform_driver renesas_usbhs_driver = { .driver = { .name = "renesas_usbhs", - .pm = &usbhsc_pm_ops, - .of_match_table = of_match_ptr(usbhs_of_match), + .pm = pm_sleep_ptr(&usbhsc_pm_ops), + .of_match_table = usbhs_of_match, }, .probe = usbhs_probe, .remove = usbhs_remove, |
