diff --git a/drivers/reset/reset-rzg2l-usbphy-ctrl.c b/drivers/reset/reset-rzg2l-usbphy-ctrl.c index 9ce0c1f5d465..32bc268c9149 100644 --- a/drivers/reset/reset-rzg2l-usbphy-ctrl.c +++ b/drivers/reset/reset-rzg2l-usbphy-ctrl.c @@ -36,6 +36,7 @@ struct rzg2l_usbphy_ctrl_priv { struct reset_control *rstc; void __iomem *base; struct platform_device *vdev; + struct regmap_field *pwrrdy; spinlock_t lock; }; @@ -92,6 +93,19 @@ static int rzg2l_usbphy_ctrl_status(struct reset_controller_dev *rcdev, return !!(readl(priv->base + RESET) & port_mask); } +/* put pll and phy into reset state */ +static void rzg2l_usbphy_ctrl_init(struct rzg2l_usbphy_ctrl_priv *priv) +{ + unsigned long flags; + u32 val; + + spin_lock_irqsave(&priv->lock, flags); + val = readl(priv->base + RESET); + val |= RESET_SEL_PLLRESET | RESET_PLLRESET | PHY_RESET_PORT2 | PHY_RESET_PORT1; + writel(val, priv->base + RESET); + spin_unlock_irqrestore(&priv->lock, flags); +} + #define RZG2L_USBPHY_CTRL_PWRRDY 1 static const struct of_device_id rzg2l_usbphy_ctrl_match_table[] = { @@ -131,9 +145,9 @@ static void rzg2l_usbphy_ctrl_pwrrdy_off(void *data) rzg2l_usbphy_ctrl_set_pwrrdy(data, false); } -static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev) +static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev, + struct rzg2l_usbphy_ctrl_priv *priv) { - struct regmap_field *pwrrdy; struct reg_field field; struct regmap *regmap; const int *data; @@ -158,15 +172,15 @@ static int rzg2l_usbphy_ctrl_pwrrdy_init(struct device *dev) field.lsb = __ffs(args[1]); field.msb = __fls(args[1]); - pwrrdy = devm_regmap_field_alloc(dev, regmap, field); - if (IS_ERR(pwrrdy)) - return PTR_ERR(pwrrdy); + priv->pwrrdy = devm_regmap_field_alloc(dev, regmap, field); + if (IS_ERR(priv->pwrrdy)) + return PTR_ERR(priv->pwrrdy); ret = rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, true); if (ret) return ret; - return devm_add_action_or_reset(dev, rzg2l_usbphy_ctrl_pwrrdy_off, pwrrdy); + return devm_add_action_or_reset(dev, rzg2l_usbphy_ctrl_pwrrdy_off, priv->pwrrdy); } static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev) @@ -175,9 +189,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev) struct rzg2l_usbphy_ctrl_priv *priv; struct platform_device *vdev; struct regmap *regmap; - unsigned long flags; int error; - u32 val; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -191,7 +203,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev) if (IS_ERR(regmap)) return PTR_ERR(regmap); - error = rzg2l_usbphy_ctrl_pwrrdy_init(dev); + error = rzg2l_usbphy_ctrl_pwrrdy_init(dev, priv); if (error) return error; @@ -214,12 +226,7 @@ static int rzg2l_usbphy_ctrl_probe(struct platform_device *pdev) goto err_pm_disable_reset_deassert; } - /* put pll and phy into reset state */ - spin_lock_irqsave(&priv->lock, flags); - val = readl(priv->base + RESET); - val |= RESET_SEL_PLLRESET | RESET_PLLRESET | PHY_RESET_PORT2 | PHY_RESET_PORT1; - writel(val, priv->base + RESET); - spin_unlock_irqrestore(&priv->lock, flags); + rzg2l_usbphy_ctrl_init(priv); priv->rcdev.ops = &rzg2l_usbphy_ctrl_reset_ops; priv->rcdev.of_reset_n_cells = 1; @@ -266,10 +273,72 @@ static void rzg2l_usbphy_ctrl_remove(struct platform_device *pdev) reset_control_assert(priv->rstc); } +static int rzg2l_usbphy_ctrl_suspend(struct device *dev) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_drvdata(dev); + u32 val; + int ret; + + val = readl(priv->base + RESET); + if (!(val & PHY_RESET_PORT2) || !(val & PHY_RESET_PORT1)) + WARN(1, "Suspend with resets de-asserted\n"); + + pm_runtime_put_sync(dev); + + ret = reset_control_assert(priv->rstc); + if (ret) + goto rpm_resume; + + ret = rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, false); + if (ret) + goto reset_deassert; + + return 0; + +reset_deassert: + reset_control_deassert(priv->rstc); +rpm_resume: + pm_runtime_resume_and_get(dev); + return ret; +} + +static int rzg2l_usbphy_ctrl_resume(struct device *dev) +{ + struct rzg2l_usbphy_ctrl_priv *priv = dev_get_drvdata(dev); + int ret; + + ret = rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, true); + if (ret) + return ret; + + ret = reset_control_deassert(priv->rstc); + if (ret) + goto pwrrdy_off; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + goto reset_assert; + + rzg2l_usbphy_ctrl_init(priv); + + return 0; + +reset_assert: + reset_control_assert(priv->rstc); +pwrrdy_off: + rzg2l_usbphy_ctrl_set_pwrrdy(priv->pwrrdy, false); + return ret; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(rzg2l_usbphy_ctrl_pm_ops, + rzg2l_usbphy_ctrl_suspend, + rzg2l_usbphy_ctrl_resume); + static struct platform_driver rzg2l_usbphy_ctrl_driver = { .driver = { .name = "rzg2l_usbphy_ctrl", .of_match_table = rzg2l_usbphy_ctrl_match_table, + .pm = pm_ptr(&rzg2l_usbphy_ctrl_pm_ops), }, .probe = rzg2l_usbphy_ctrl_probe, .remove = rzg2l_usbphy_ctrl_remove,