diff options
Diffstat (limited to 'drivers/usb/renesas_usbhs/common.c')
| -rw-r--r-- | drivers/usb/renesas_usbhs/common.c | 529 |
1 files changed, 390 insertions, 139 deletions
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index cfd205036aba..cf4a0367d6d6 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -1,26 +1,25 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * Renesas USB driver * * Copyright (C) 2011 Renesas Solutions Corp. + * Copyright (C) 2019 Renesas Electronics Corporation * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ +#include <linux/clk.h> #include <linux/err.h> +#include <linux/gpio/consumer.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/pm_runtime.h> +#include <linux/reset.h> #include <linux/slab.h> #include <linux/sysfs.h> #include "common.h" +#include "rcar2.h" +#include "rcar3.h" +#include "rza.h" /* * image of renesas_usbhs @@ -44,15 +43,6 @@ * | .... | +-----------+ */ - -#define USBHSF_RUNTIME_PWCTRL (1 << 0) - -/* status */ -#define usbhsc_flags_init(p) do {(p)->flags = 0; } while (0) -#define usbhsc_flags_set(p, b) ((p)->flags |= (b)) -#define usbhsc_flags_clr(p, b) ((p)->flags &= ~(b)) -#define usbhsc_flags_has(p, b) ((p)->flags & (b)) - /* * platform call back * @@ -62,8 +52,8 @@ */ #define usbhs_platform_call(priv, func, args...)\ (!(priv) ? -ENODEV : \ - !((priv)->pfunc.func) ? 0 : \ - (priv)->pfunc.func(args)) + !((priv)->pfunc->func) ? 0 : \ + (priv)->pfunc->func(args)) /* * common functions @@ -93,6 +83,11 @@ struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev) return dev_get_drvdata(&pdev->dev); } +int usbhs_get_id_as_gadget(struct platform_device *pdev) +{ + return USBHS_GADGET; +} + /* * syscfg functions */ @@ -105,10 +100,6 @@ void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable) { u16 mask = DCFM | DRPD | DPRPU | HSE | USBE; u16 val = DCFM | DRPD | HSE | USBE; - int has_otg = usbhs_get_dparam(priv, has_otg); - - if (has_otg) - usbhs_bset(priv, DVSTCTR, (EXTLP | PWEN), (EXTLP | PWEN)); /* * if enable @@ -122,13 +113,21 @@ void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable) void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable) { u16 mask = DCFM | DRPD | DPRPU | HSE | USBE; - u16 val = DPRPU | HSE | USBE; + u16 val = HSE | USBE; + + /* CNEN bit is required for function operation */ + if (usbhs_get_dparam(priv, has_cnen)) { + mask |= CNEN; + val |= CNEN; + } /* * if enable * * - select Function mode - * - D+ Line Pull-up + * - D+ Line Pull-up is disabled + * When D+ Line Pull-up is enabled, + * calling usbhs_sys_function_pullup(,1) */ usbhs_bset(priv, SYSCFG, mask, enable ? val : 0); } @@ -162,17 +161,17 @@ void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) req->bRequest = (val >> 8) & 0xFF; req->bRequestType = (val >> 0) & 0xFF; - req->wValue = usbhs_read(priv, USBVAL); - req->wIndex = usbhs_read(priv, USBINDX); - req->wLength = usbhs_read(priv, USBLENG); + req->wValue = cpu_to_le16(usbhs_read(priv, USBVAL)); + req->wIndex = cpu_to_le16(usbhs_read(priv, USBINDX)); + req->wLength = cpu_to_le16(usbhs_read(priv, USBLENG)); } void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req) { usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType); - usbhs_write(priv, USBVAL, req->wValue); - usbhs_write(priv, USBINDX, req->wIndex); - usbhs_write(priv, USBLENG, req->wLength); + usbhs_write(priv, USBVAL, le16_to_cpu(req->wValue)); + usbhs_write(priv, USBINDX, le16_to_cpu(req->wIndex)); + usbhs_write(priv, USBLENG, le16_to_cpu(req->wLength)); usbhs_bset(priv, DCPCTR, SUREQ, SUREQ); } @@ -270,6 +269,16 @@ int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, } /* + * interrupt functions + */ +void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit) +{ + u16 pipe_mask = (u16)GENMASK(usbhs_get_dparam(priv, pipe_size), 0); + + usbhs_write(priv, sts_reg, ~(1 << bit) & pipe_mask); +} + +/* * local functions */ static void usbhsc_set_buswait(struct usbhs_priv *priv) @@ -281,20 +290,113 @@ static void usbhsc_set_buswait(struct usbhs_priv *priv) usbhs_bset(priv, BUSWAIT, 0x000F, wait); } +static bool usbhsc_is_multi_clks(struct usbhs_priv *priv) +{ + return priv->dparam.multi_clks; +} + +static int usbhsc_clk_get(struct device *dev, struct usbhs_priv *priv) +{ + if (!usbhsc_is_multi_clks(priv)) + return 0; + + /* The first clock should exist */ + priv->clks[0] = of_clk_get(dev_of_node(dev), 0); + if (IS_ERR(priv->clks[0])) + return PTR_ERR(priv->clks[0]); + + /* + * To backward compatibility with old DT, this driver checks the return + * value if it's -ENOENT or not. + */ + 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])) { + clk_put(priv->clks[0]); + return PTR_ERR(priv->clks[1]); + } + + return 0; +} + +static void usbhsc_clk_put(struct usbhs_priv *priv) +{ + int i; + + if (!usbhsc_is_multi_clks(priv)) + return; + + for (i = 0; i < ARRAY_SIZE(priv->clks); i++) + clk_put(priv->clks[i]); +} + +static int usbhsc_clk_prepare_enable(struct usbhs_priv *priv) +{ + int i, ret; + + if (!usbhsc_is_multi_clks(priv)) + return 0; + + for (i = 0; i < ARRAY_SIZE(priv->clks); i++) { + ret = clk_prepare_enable(priv->clks[i]); + if (ret) { + while (--i >= 0) + clk_disable_unprepare(priv->clks[i]); + return ret; + } + } + + return ret; +} + +static void usbhsc_clk_disable_unprepare(struct usbhs_priv *priv) +{ + int i; + + if (!usbhsc_is_multi_clks(priv)) + return; + + for (i = 0; i < ARRAY_SIZE(priv->clks); i++) + clk_disable_unprepare(priv->clks[i]); +} + /* * platform default param */ -static u32 usbhsc_default_pipe_type[] = { - USB_ENDPOINT_XFER_CONTROL, - USB_ENDPOINT_XFER_ISOC, - USB_ENDPOINT_XFER_ISOC, - USB_ENDPOINT_XFER_BULK, - USB_ENDPOINT_XFER_BULK, - USB_ENDPOINT_XFER_BULK, - USB_ENDPOINT_XFER_INT, - USB_ENDPOINT_XFER_INT, - USB_ENDPOINT_XFER_INT, - USB_ENDPOINT_XFER_INT, + +/* 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, 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), + RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x07, false), +}; + +/* commonly used on newer SH-Mobile and R-Car SoCs */ +static struct renesas_usbhs_driver_pipe_config usbhsc_new_pipe[] = { + RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false), + 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), + RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x78, true), + RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x88, true), + RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x98, true), + RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xa8, true), + RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xb8, true), + RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xc8, true), + RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xd8, true), }; /* @@ -309,6 +411,10 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) /* enable PM */ pm_runtime_get_sync(dev); + /* enable clks */ + if (usbhsc_clk_prepare_enable(priv)) + return; + /* enable platform power */ usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable); @@ -321,6 +427,9 @@ static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable) /* disable platform power */ usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable); + /* disable clks */ + usbhsc_clk_disable_unprepare(priv); + /* disable PM */ pm_runtime_put_sync(dev); } @@ -335,12 +444,13 @@ static void usbhsc_hotplug(struct usbhs_priv *priv) struct usbhs_mod *mod = usbhs_mod_get_current(priv); int id; int enable; + int cable; int ret; /* * get vbus status from platform */ - enable = usbhs_platform_call(priv, get_vbus, pdev); + enable = usbhs_mod_info_call(priv, get_vbus, pdev); /* * get id from platform @@ -348,6 +458,16 @@ static void usbhsc_hotplug(struct usbhs_priv *priv) id = usbhs_platform_call(priv, get_id, pdev); if (enable && !mod) { + if (priv->edev) { + cable = extcon_get_state(priv->edev, EXTCON_USB_HOST); + if ((cable > 0 && id != USBHS_HOST) || + (!cable && id != USBHS_GADGET)) { + dev_info(&pdev->dev, + "USB cable plugged in doesn't match the selected role!\n"); + return; + } + } + ret = usbhs_mod_change(priv, id); if (ret < 0) return; @@ -355,7 +475,7 @@ static void usbhsc_hotplug(struct usbhs_priv *priv) dev_dbg(&pdev->dev, "%s enable\n", __func__); /* power on */ - if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + if (usbhs_get_dparam(priv, runtime_pwctrl)) usbhsc_power_ctrl(priv, enable); /* bus init */ @@ -375,7 +495,7 @@ static void usbhsc_hotplug(struct usbhs_priv *priv) usbhsc_bus_init(priv); /* power off */ - if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + if (usbhs_get_dparam(priv, runtime_pwctrl)) usbhsc_power_ctrl(priv, enable); usbhs_mod_change(priv, -1); @@ -396,7 +516,7 @@ static void usbhsc_notify_hotplug(struct work_struct *work) usbhsc_hotplug(priv); } -static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) +int usbhsc_schedule_notify_hotplug(struct platform_device *pdev) { struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev); int delay = usbhs_get_dparam(priv, detection_delay); @@ -414,81 +534,184 @@ static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev) /* * platform functions */ +static const struct of_device_id usbhs_of_match[] = { + { + .compatible = "renesas,usbhs-r8a774c0", + .data = &usbhs_rcar_gen3_with_pll_plat_info, + }, + { + .compatible = "renesas,usbhs-r8a7790", + .data = &usbhs_rcar_gen2_plat_info, + }, + { + .compatible = "renesas,usbhs-r8a7791", + .data = &usbhs_rcar_gen2_plat_info, + }, + { + .compatible = "renesas,usbhs-r8a7794", + .data = &usbhs_rcar_gen2_plat_info, + }, + { + .compatible = "renesas,usbhs-r8a7795", + .data = &usbhs_rcar_gen3_plat_info, + }, + { + .compatible = "renesas,usbhs-r8a7796", + .data = &usbhs_rcar_gen3_plat_info, + }, + { + .compatible = "renesas,usbhs-r8a77990", + .data = &usbhs_rcar_gen3_with_pll_plat_info, + }, + { + .compatible = "renesas,usbhs-r8a77995", + .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, + }, + { + .compatible = "renesas,rcar-gen3-usbhs", + .data = &usbhs_rcar_gen3_plat_info, + }, + { + .compatible = "renesas,rza1-usbhs", + .data = &usbhs_rza1_plat_info, + }, + { + .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); + static int usbhs_probe(struct platform_device *pdev) { - struct renesas_usbhs_platform_info *info = pdev->dev.platform_data; - struct renesas_usbhs_driver_callback *dfunc; + const struct renesas_usbhs_platform_info *info; struct usbhs_priv *priv; - struct resource *res, *irq_res; + struct device *dev = &pdev->dev; + struct gpio_desc *gpiod; int ret; - - /* check platform information */ - if (!info || - !info->platform_callback.get_id) { - dev_err(&pdev->dev, "no platform information\n"); - return -EINVAL; + u32 tmp; + int irq; + + info = of_device_get_match_data(dev); + if (!info) { + info = dev_get_platdata(dev); + if (!info) + return dev_err_probe(dev, -EINVAL, "no platform info\n"); } /* platform data */ - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res || !irq_res) { - dev_err(&pdev->dev, "Not enough Renesas USB platform resources.\n"); - return -ENODEV; - } + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; /* usb private data */ - priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); - if (!priv) { - dev_err(&pdev->dev, "Could not allocate priv\n"); + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) return -ENOMEM; - } - priv->base = devm_ioremap_resource(&pdev->dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); + 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); + } + + priv->rsts = devm_reset_control_array_get_optional_shared(dev); + if (IS_ERR(priv->rsts)) + return PTR_ERR(priv->rsts); + /* * care platform info */ - memcpy(&priv->pfunc, - &info->platform_callback, - sizeof(struct renesas_usbhs_platform_callback)); - memcpy(&priv->dparam, - &info->driver_param, - sizeof(struct renesas_usbhs_driver_param)); - /* set driver callback functions for platform */ - dfunc = &info->driver_callback; - dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug; + priv->dparam = info->driver_param; + + if (!info->platform_callback.get_id) { + dev_err(dev, "no platform callbacks\n"); + return -EINVAL; + } + priv->pfunc = &info->platform_callback; /* set default param if platform doesn't have */ - if (!priv->dparam.pipe_type) { - priv->dparam.pipe_type = usbhsc_default_pipe_type; - priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type); + if (usbhs_get_dparam(priv, has_new_pipe_configs)) { + priv->dparam.pipe_configs = usbhsc_new_pipe; + priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_new_pipe); + } else if (!priv->dparam.pipe_configs) { + priv->dparam.pipe_configs = usbhsc_default_pipe; + priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe); } if (!priv->dparam.pio_dma_border) priv->dparam.pio_dma_border = 64; /* 64byte */ + if (!of_property_read_u32(dev_of_node(dev), "renesas,buswait", &tmp)) + priv->dparam.buswait_bwait = tmp; + gpiod = devm_gpiod_get_optional(dev, "renesas,enable", GPIOD_IN); + if (IS_ERR(gpiod)) + return PTR_ERR(gpiod); /* FIXME */ /* runtime power control ? */ - if (priv->pfunc.get_vbus) - usbhsc_flags_set(priv, USBHSF_RUNTIME_PWCTRL); + if (priv->pfunc->get_vbus) + usbhs_get_dparam(priv, runtime_pwctrl) = 1; /* * priv settings */ - priv->irq = irq_res->start; - if (irq_res->flags & IORESOURCE_IRQ_SHAREABLE) - priv->irqflags = IRQF_SHARED; + priv->irq = irq; priv->pdev = 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) @@ -498,15 +721,29 @@ 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 */ - dev_set_drvdata(&pdev->dev, priv); + /* 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; /* - * deviece reset here because + * device reset here because * USB device might be used in boot loader. */ usbhs_sys_clock_ctrl(priv, 0); + /* check GPIO determining if USB function should be enabled */ + if (gpiod) { + ret = !gpiod_get_value(gpiod); + if (ret) { + dev_warn(dev, "USB function not selected (GPIO)\n"); + ret = -ENOTSUPP; + goto probe_assert_rest; + } + } + /* * platform call * @@ -516,123 +753,137 @@ static int usbhs_probe(struct platform_device *pdev) */ ret = usbhs_platform_call(priv, hardware_init, pdev); if (ret < 0) { - dev_err(&pdev->dev, "platform prove failed.\n"); - goto probe_end_mod_exit; + dev_err(dev, "platform init failed.\n"); + goto probe_assert_rest; } /* reset phy for connection */ usbhs_platform_call(priv, phy_reset, pdev); - /* power control */ - pm_runtime_enable(&pdev->dev); - if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) { + /* + * 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); + } else { + usbhs_mod_non_autonomy_mode(priv); } /* * manual call notify_hotplug for cold plug */ - ret = usbhsc_drvcllbck_notify_hotplug(pdev); - if (ret < 0) - goto probe_end_call_remove; + usbhsc_schedule_notify_hotplug(pdev); - dev_info(&pdev->dev, "probed\n"); + dev_info(dev, "probed\n"); return ret; -probe_end_call_remove: - usbhs_platform_call(priv, hardware_exit, pdev); -probe_end_mod_exit: +probe_assert_rest: + reset_control_assert(priv->rsts); +probe_fail_rst: usbhs_mod_remove(priv); 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(&pdev->dev, "probe failed\n"); + 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); - struct renesas_usbhs_platform_info *info = pdev->dev.platform_data; - struct renesas_usbhs_driver_callback *dfunc = &info->driver_callback; dev_dbg(&pdev->dev, "usb remove\n"); - dfunc->notify_hotplug = NULL; + flush_delayed_work(&priv->notify_hotplug_work); + + usbhs_platform_call(priv, hardware_exit, pdev); + reset_control_assert(priv->rsts); + usbhs_mod_remove(priv); + usbhs_fifo_remove(priv); + usbhs_pipe_remove(priv); /* power off */ - if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + if (!usbhs_get_dparam(priv, runtime_pwctrl)) usbhsc_power_ctrl(priv, 0); + usbhsc_clk_put(priv); pm_runtime_disable(&pdev->dev); +} - usbhs_platform_call(priv, hardware_exit, pdev); - usbhs_mod_remove(priv); - usbhs_fifo_remove(priv); - usbhs_pipe_remove(priv); +static void usbhsc_restore(struct device *dev) +{ + struct usbhs_priv *priv = dev_get_drvdata(dev); + struct platform_device *pdev = usbhs_priv_to_pdev(priv); - return 0; + 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); usbhs_mod_change(priv, -1); } - if (mod || !usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) + 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 int usbhsc_resume(struct device *dev) { struct usbhs_priv *priv = dev_get_drvdata(dev); - struct platform_device *pdev = usbhs_priv_to_pdev(priv); - - if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) - usbhsc_power_ctrl(priv, 1); - - usbhs_platform_call(priv, phy_reset, pdev); + int ret; - usbhsc_drvcllbck_notify_hotplug(pdev); + ret = reset_control_deassert(priv->rsts); + if (ret) + return ret; - return 0; -} + usbhsc_restore(dev); -static int usbhsc_runtime_nop(struct device *dev) -{ - /* Runtime PM callback shared between ->runtime_suspend() - * and ->runtime_resume(). Simply returns success. - * - * This driver re-initializes all registers after - * pm_runtime_get_sync() anyway so there is no need - * to save and restore registers here. - */ return 0; } -static const struct dev_pm_ops usbhsc_pm_ops = { - .suspend = usbhsc_suspend, - .resume = usbhsc_resume, - .runtime_suspend = usbhsc_runtime_nop, - .runtime_resume = usbhsc_runtime_nop, -}; +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, + .pm = pm_sleep_ptr(&usbhsc_pm_ops), + .of_match_table = usbhs_of_match, }, .probe = usbhs_probe, .remove = usbhs_remove, |
