diff options
Diffstat (limited to 'drivers/phy/renesas')
| -rw-r--r-- | drivers/phy/renesas/Kconfig | 7 | ||||
| -rw-r--r-- | drivers/phy/renesas/Makefile | 1 | ||||
| -rw-r--r-- | drivers/phy/renesas/phy-rcar-gen3-pcie.c | 2 | ||||
| -rw-r--r-- | drivers/phy/renesas/phy-rcar-gen3-usb2.c | 70 | ||||
| -rw-r--r-- | drivers/phy/renesas/phy-rcar-gen3-usb3.c | 2 | ||||
| -rw-r--r-- | drivers/phy/renesas/phy-rzg3e-usb3.c | 259 |
6 files changed, 322 insertions, 19 deletions
diff --git a/drivers/phy/renesas/Kconfig b/drivers/phy/renesas/Kconfig index e342eef0640b..16211072098e 100644 --- a/drivers/phy/renesas/Kconfig +++ b/drivers/phy/renesas/Kconfig @@ -40,3 +40,10 @@ config PHY_RCAR_GEN3_USB3 select GENERIC_PHY help Support for USB 3.0 PHY found on Renesas R-Car generation 3 SoCs. + +config PHY_RZ_G3E_USB3 + tristate "Renesas RZ/G3E USB 3.0 PHY driver" + depends on ARCH_RENESAS || COMPILE_TEST + select GENERIC_PHY + help + Support for USB 3.0 PHY found on Renesas RZ/G3E SoCs. diff --git a/drivers/phy/renesas/Makefile b/drivers/phy/renesas/Makefile index 8896d1919faa..0e98083f2f0c 100644 --- a/drivers/phy/renesas/Makefile +++ b/drivers/phy/renesas/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o obj-$(CONFIG_PHY_RCAR_GEN3_PCIE) += phy-rcar-gen3-pcie.o obj-$(CONFIG_PHY_RCAR_GEN3_USB2) += phy-rcar-gen3-usb2.o obj-$(CONFIG_PHY_RCAR_GEN3_USB3) += phy-rcar-gen3-usb3.o +obj-$(CONFIG_PHY_RZ_G3E_USB3) += phy-rzg3e-usb3.o diff --git a/drivers/phy/renesas/phy-rcar-gen3-pcie.c b/drivers/phy/renesas/phy-rcar-gen3-pcie.c index feca4cb2ff4d..c0e5a4ac82de 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-pcie.c +++ b/drivers/phy/renesas/phy-rcar-gen3-pcie.c @@ -128,7 +128,7 @@ error: static void rcar_gen3_phy_pcie_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); -}; +} static struct platform_driver rcar_gen3_phy_driver = { .driver = { diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 3f6b480e1092..582de10d5beb 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -132,9 +132,9 @@ struct rcar_gen3_chan { struct device *dev; /* platform_device's device */ const struct rcar_gen3_phy_drv_data *phy_data; struct extcon_dev *extcon; + struct reset_control *rstc; struct rcar_gen3_phy rphys[NUM_OF_PHYS]; struct regulator *vbus; - struct reset_control *rstc; struct work_struct work; spinlock_t lock; /* protects access to hardware and driver data structure. */ enum usb_dr_mode dr_mode; @@ -771,33 +771,32 @@ static enum usb_dr_mode rcar_gen3_get_dr_mode(struct device_node *np) return candidate; } +static void rcar_gen3_reset_assert(void *data) +{ + reset_control_assert(data); +} + static int rcar_gen3_phy_usb2_init_bus(struct rcar_gen3_chan *channel) { struct device *dev = channel->dev; int ret; u32 val; - channel->rstc = devm_reset_control_array_get_shared(dev); - if (IS_ERR(channel->rstc)) - return PTR_ERR(channel->rstc); + if (!channel->phy_data->init_bus) + return 0; ret = pm_runtime_resume_and_get(dev); if (ret) return ret; - ret = reset_control_deassert(channel->rstc); - if (ret) - goto rpm_put; - val = readl(channel->base + USB2_AHB_BUS_CTR); val &= ~USB2_AHB_BUS_CTR_MBL_MASK; val |= USB2_AHB_BUS_CTR_MBL_INCR4; writel(val, channel->base + USB2_AHB_BUS_CTR); -rpm_put: pm_runtime_put(dev); - return ret; + return 0; } static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) @@ -837,6 +836,18 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) } } + channel->rstc = devm_reset_control_array_get_optional_shared(dev); + if (IS_ERR(channel->rstc)) + return PTR_ERR(channel->rstc); + + ret = reset_control_deassert(channel->rstc); + if (ret) + return ret; + + ret = devm_add_action_or_reset(dev, rcar_gen3_reset_assert, channel->rstc); + if (ret) + return ret; + /* * devm_phy_create() will call pm_runtime_enable(&phy->dev); * And then, phy-core will manage runtime pm for this device. @@ -852,11 +863,9 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, channel); channel->dev = dev; - if (channel->phy_data->init_bus) { - ret = rcar_gen3_phy_usb2_init_bus(channel); - if (ret) - goto error; - } + ret = rcar_gen3_phy_usb2_init_bus(channel); + if (ret) + goto error; spin_lock_init(&channel->lock); for (i = 0; i < NUM_OF_PHYS; i++) { @@ -924,14 +933,41 @@ static void rcar_gen3_phy_usb2_remove(struct platform_device *pdev) if (channel->is_otg_channel) device_remove_file(&pdev->dev, &dev_attr_role); - reset_control_assert(channel->rstc); pm_runtime_disable(&pdev->dev); -}; +} + +static int rcar_gen3_phy_usb2_suspend(struct device *dev) +{ + struct rcar_gen3_chan *channel = dev_get_drvdata(dev); + + return reset_control_assert(channel->rstc); +} + +static int rcar_gen3_phy_usb2_resume(struct device *dev) +{ + struct rcar_gen3_chan *channel = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(channel->rstc); + if (ret) + return ret; + + ret = rcar_gen3_phy_usb2_init_bus(channel); + if (ret) + reset_control_assert(channel->rstc); + + return ret; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(rcar_gen3_phy_usb2_pm_ops, + rcar_gen3_phy_usb2_suspend, + rcar_gen3_phy_usb2_resume); static struct platform_driver rcar_gen3_phy_usb2_driver = { .driver = { .name = "phy_rcar_gen3_usb2", .of_match_table = rcar_gen3_phy_usb2_match_table, + .pm = pm_ptr(&rcar_gen3_phy_usb2_pm_ops), }, .probe = rcar_gen3_phy_usb2_probe, .remove = rcar_gen3_phy_usb2_remove, diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb3.c b/drivers/phy/renesas/phy-rcar-gen3-usb3.c index 5c267d148c90..0420f5b283ce 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb3.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb3.c @@ -202,7 +202,7 @@ error: static void rcar_gen3_phy_usb3_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); -}; +} static struct platform_driver rcar_gen3_phy_usb3_driver = { .driver = { diff --git a/drivers/phy/renesas/phy-rzg3e-usb3.c b/drivers/phy/renesas/phy-rzg3e-usb3.c new file mode 100644 index 000000000000..6b3453ea0004 --- /dev/null +++ b/drivers/phy/renesas/phy-rzg3e-usb3.c @@ -0,0 +1,259 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Renesas RZ/G3E USB3.0 PHY driver + * + * Copyright (C) 2025 Renesas Electronics Corporation + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/iopoll.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/reset.h> + +#define USB3_TEST_RESET 0x0000 +#define USB3_TEST_UTMICTRL2 0x0b04 +#define USB3_TEST_PRMCTRL5_R 0x0c10 +#define USB3_TEST_PRMCTRL6_R 0x0c14 + +#define USB3_TEST_RSTCTRL 0x1000 +#define USB3_TEST_CLKCTRL 0x1004 +#define USB3_TEST_RAMCTRL 0x100c +#define USB3_TEST_CREGCTRL 0x1010 +#define USB3_TEST_LANECONFIG0 0x1030 + +#define USB3_TEST_RESET_PORTRESET0_CTRL BIT(9) +#define USB3_TEST_RESET_SIDDQ BIT(3) +#define USB3_TEST_RESET_PHY_RESET BIT(2) +#define USB3_TEST_RESET_PORTRESET0 BIT(1) +#define USB3_TEST_RESET_RELEASE_OVERRIDE (0) + +#define USB3_TEST_UTMICTRL2_CTRL_MASK GENMASK(9, 8) +#define USB3_TEST_UTMICTRL2_MODE_MASK GENMASK(1, 0) + +#define USB3_TEST_PRMCTRL5_R_TXPREEMPAMPTUNE0_MASK GENMASK(2, 1) + +#define USB3_TEST_PRMCTRL6_R_OTGTUNE0_MASK GENMASK(2, 0) + +#define USB3_TEST_RSTCTRL_HARDRESET_ODEN BIT(9) +#define USB3_TEST_RSTCTRL_PIPERESET_ODEN BIT(8) +#define USB3_TEST_RSTCTRL_HARDRESET BIT(1) +#define USB3_TEST_RSTCTRL_PIPERESET BIT(0) +#define USB3_TEST_RSTCTRL_ASSERT \ + (USB3_TEST_RSTCTRL_HARDRESET_ODEN | USB3_TEST_RSTCTRL_PIPERESET_ODEN | \ + USB3_TEST_RSTCTRL_HARDRESET | USB3_TEST_RSTCTRL_PIPERESET) +#define USB3_TEST_RSTCTRL_RELEASE_HARDRESET \ + (USB3_TEST_RSTCTRL_HARDRESET_ODEN | USB3_TEST_RSTCTRL_PIPERESET_ODEN | \ + USB3_TEST_RSTCTRL_PIPERESET) +#define USB3_TEST_RSTCTRL_DEASSERT \ + (USB3_TEST_RSTCTRL_HARDRESET_ODEN | USB3_TEST_RSTCTRL_PIPERESET_ODEN) +#define USB3_TEST_RSTCTRL_RELEASE_OVERRIDE (0) + +#define USB3_TEST_CLKCTRL_MPLLA_SSC_EN BIT(2) + +#define USB3_TEST_RAMCTRL_SRAM_INIT_DONE BIT(2) +#define USB3_TEST_RAMCTRL_SRAM_EXT_LD_DONE BIT(0) + +#define USB3_TEST_CREGCTRL_PARA_SEL BIT(8) + +#define USB3_TEST_LANECONFIG0_DEFAULT (0xd) + +struct rz_usb3 { + void __iomem *base; + struct reset_control *rstc; + bool skip_reinit; +}; + +static void rzg3e_phy_usb2test_phy_init(void __iomem *base) +{ + u32 val; + + val = readl(base + USB3_TEST_UTMICTRL2); + val |= USB3_TEST_UTMICTRL2_CTRL_MASK | USB3_TEST_UTMICTRL2_MODE_MASK; + writel(val, base + USB3_TEST_UTMICTRL2); + + val = readl(base + USB3_TEST_PRMCTRL5_R); + val &= ~USB3_TEST_PRMCTRL5_R_TXPREEMPAMPTUNE0_MASK; + val |= FIELD_PREP(USB3_TEST_PRMCTRL5_R_TXPREEMPAMPTUNE0_MASK, 2); + writel(val, base + USB3_TEST_PRMCTRL5_R); + + val = readl(base + USB3_TEST_PRMCTRL6_R); + val &= ~USB3_TEST_PRMCTRL6_R_OTGTUNE0_MASK; + val |= FIELD_PREP(USB3_TEST_PRMCTRL6_R_OTGTUNE0_MASK, 7); + writel(val, base + USB3_TEST_PRMCTRL6_R); + + val = readl(base + USB3_TEST_RESET); + val &= ~USB3_TEST_RESET_SIDDQ; + val |= USB3_TEST_RESET_PORTRESET0_CTRL | USB3_TEST_RESET_PHY_RESET | + USB3_TEST_RESET_PORTRESET0; + writel(val, base + USB3_TEST_RESET); + fsleep(10); + + val &= ~(USB3_TEST_RESET_PHY_RESET | USB3_TEST_RESET_PORTRESET0); + writel(val, base + USB3_TEST_RESET); + fsleep(10); + + val = readl(base + USB3_TEST_UTMICTRL2); + val &= ~USB3_TEST_UTMICTRL2_CTRL_MASK; + writel(val, base + USB3_TEST_UTMICTRL2); + + writel(USB3_TEST_RESET_RELEASE_OVERRIDE, base + USB3_TEST_RESET); +} + +static int rzg3e_phy_usb3test_phy_init(void __iomem *base) +{ + int ret; + u32 val; + + writel(USB3_TEST_CREGCTRL_PARA_SEL, base + USB3_TEST_CREGCTRL); + writel(USB3_TEST_RSTCTRL_ASSERT, base + USB3_TEST_RSTCTRL); + fsleep(20); + + writel(USB3_TEST_CLKCTRL_MPLLA_SSC_EN, base + USB3_TEST_CLKCTRL); + writel(USB3_TEST_LANECONFIG0_DEFAULT, base + USB3_TEST_LANECONFIG0); + writel(USB3_TEST_RSTCTRL_RELEASE_HARDRESET, base + USB3_TEST_RSTCTRL); + + ret = readl_poll_timeout_atomic(base + USB3_TEST_RAMCTRL, val, + val & USB3_TEST_RAMCTRL_SRAM_INIT_DONE, 1, 10000); + if (ret) + return ret; + + writel(USB3_TEST_RSTCTRL_DEASSERT, base + USB3_TEST_RSTCTRL); + writel(USB3_TEST_RAMCTRL_SRAM_EXT_LD_DONE, base + USB3_TEST_RAMCTRL); + writel(USB3_TEST_RSTCTRL_RELEASE_OVERRIDE, base + USB3_TEST_RSTCTRL); + + return 0; +} + +static int rzg3e_phy_usb3_init_helper(void __iomem *base) +{ + rzg3e_phy_usb2test_phy_init(base); + + return rzg3e_phy_usb3test_phy_init(base); +} + +static int rzg3e_phy_usb3_init(struct phy *p) +{ + struct rz_usb3 *r = phy_get_drvdata(p); + int ret = 0; + + if (!r->skip_reinit) + ret = rzg3e_phy_usb3_init_helper(r->base); + + return ret; +} + +static const struct phy_ops rzg3e_phy_usb3_ops = { + .init = rzg3e_phy_usb3_init, + .owner = THIS_MODULE, +}; + +static int rzg3e_phy_usb3_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct phy_provider *provider; + struct rz_usb3 *r; + struct phy *phy; + int ret; + + r = devm_kzalloc(dev, sizeof(*r), GFP_KERNEL); + if (!r) + return -ENOMEM; + + r->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(r->base)) + return PTR_ERR(r->base); + + r->rstc = devm_reset_control_get_shared_deasserted(dev, NULL); + if (IS_ERR(r->rstc)) + return dev_err_probe(dev, PTR_ERR(r->rstc), "failed to get deasserted reset\n"); + + /* + * devm_phy_create() will call pm_runtime_enable(&phy->dev); + * And then, phy-core will manage runtime pm for this device. + */ + ret = devm_pm_runtime_enable(dev); + if (ret < 0) + return ret; + + phy = devm_phy_create(dev, NULL, &rzg3e_phy_usb3_ops); + if (IS_ERR(phy)) + return dev_err_probe(dev, PTR_ERR(phy), "failed to create USB3 PHY\n"); + + platform_set_drvdata(pdev, r); + phy_set_drvdata(phy, r); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) + return dev_err_probe(dev, PTR_ERR(provider), "failed to register PHY provider\n"); + + return 0; +} + +static int rzg3e_phy_usb3_suspend(struct device *dev) +{ + struct rz_usb3 *r = dev_get_drvdata(dev); + + pm_runtime_put(dev); + reset_control_assert(r->rstc); + r->skip_reinit = false; + + return 0; +} + +static int rzg3e_phy_usb3_resume(struct device *dev) +{ + struct rz_usb3 *r = dev_get_drvdata(dev); + int ret; + + ret = reset_control_deassert(r->rstc); + if (ret) + return ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + goto reset_assert; + + ret = rzg3e_phy_usb3_init_helper(r->base); + if (ret) + goto pm_put; + + r->skip_reinit = true; + + return 0; + +pm_put: + pm_runtime_put(dev); +reset_assert: + reset_control_assert(r->rstc); + return ret; +} + +static const struct dev_pm_ops rzg3e_phy_usb3_pm = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(rzg3e_phy_usb3_suspend, rzg3e_phy_usb3_resume) +}; + +static const struct of_device_id rzg3e_phy_usb3_match_table[] = { + { .compatible = "renesas,r9a09g047-usb3-phy" }, + { /* Sentinel */ } +}; + +MODULE_DEVICE_TABLE(of, rzg3e_phy_usb3_match_table); +static struct platform_driver rzg3e_phy_usb3_driver = { + .driver = { + .name = "phy_rzg3e_usb3", + .of_match_table = rzg3e_phy_usb3_match_table, + .pm = pm_sleep_ptr(&rzg3e_phy_usb3_pm), + }, + .probe = rzg3e_phy_usb3_probe, +}; +module_platform_driver(rzg3e_phy_usb3_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Renesas RZ/G3E USB3.0 PHY Driver"); +MODULE_AUTHOR("biju.das.jz@bp.renesas.com>"); |
