|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * Renesas R-Car Gen2 PHY driver | 
|  | * | 
|  | * Copyright (C) 2014 Renesas Solutions Corp. | 
|  | * Copyright (C) 2014 Cogent Embedded, Inc. | 
|  | * Copyright (C) 2019 Renesas Electronics Corp. | 
|  | */ | 
|  |  | 
|  | #include <linux/clk.h> | 
|  | #include <linux/delay.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/phy/phy.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/spinlock.h> | 
|  | #include <linux/atomic.h> | 
|  | #include <linux/of_device.h> | 
|  |  | 
|  | #define USBHS_LPSTS			0x02 | 
|  | #define USBHS_UGCTRL			0x80 | 
|  | #define USBHS_UGCTRL2			0x84 | 
|  | #define USBHS_UGSTS			0x88	/* From technical update */ | 
|  |  | 
|  | /* Low Power Status register (LPSTS) */ | 
|  | #define USBHS_LPSTS_SUSPM		0x4000 | 
|  |  | 
|  | /* USB General control register (UGCTRL) */ | 
|  | #define USBHS_UGCTRL_CONNECT		0x00000004 | 
|  | #define USBHS_UGCTRL_PLLRESET		0x00000001 | 
|  |  | 
|  | /* USB General control register 2 (UGCTRL2) */ | 
|  | #define USBHS_UGCTRL2_USB2SEL		0x80000000 | 
|  | #define USBHS_UGCTRL2_USB2SEL_PCI	0x00000000 | 
|  | #define USBHS_UGCTRL2_USB2SEL_USB30	0x80000000 | 
|  | #define USBHS_UGCTRL2_USB0SEL		0x00000030 | 
|  | #define USBHS_UGCTRL2_USB0SEL_PCI	0x00000010 | 
|  | #define USBHS_UGCTRL2_USB0SEL_HS_USB	0x00000030 | 
|  | #define USBHS_UGCTRL2_USB0SEL_USB20	0x00000010 | 
|  | #define USBHS_UGCTRL2_USB0SEL_HS_USB20	0x00000020 | 
|  |  | 
|  | /* USB General status register (UGSTS) */ | 
|  | #define USBHS_UGSTS_LOCK		0x00000100 /* From technical update */ | 
|  |  | 
|  | #define PHYS_PER_CHANNEL	2 | 
|  |  | 
|  | struct rcar_gen2_phy { | 
|  | struct phy *phy; | 
|  | struct rcar_gen2_channel *channel; | 
|  | int number; | 
|  | u32 select_value; | 
|  | }; | 
|  |  | 
|  | struct rcar_gen2_channel { | 
|  | struct device_node *of_node; | 
|  | struct rcar_gen2_phy_driver *drv; | 
|  | struct rcar_gen2_phy phys[PHYS_PER_CHANNEL]; | 
|  | int selected_phy; | 
|  | u32 select_mask; | 
|  | }; | 
|  |  | 
|  | struct rcar_gen2_phy_driver { | 
|  | void __iomem *base; | 
|  | struct clk *clk; | 
|  | spinlock_t lock; | 
|  | int num_channels; | 
|  | struct rcar_gen2_channel *channels; | 
|  | }; | 
|  |  | 
|  | struct rcar_gen2_phy_data { | 
|  | const struct phy_ops *gen2_phy_ops; | 
|  | const u32 (*select_value)[PHYS_PER_CHANNEL]; | 
|  | }; | 
|  |  | 
|  | static int rcar_gen2_phy_init(struct phy *p) | 
|  | { | 
|  | struct rcar_gen2_phy *phy = phy_get_drvdata(p); | 
|  | struct rcar_gen2_channel *channel = phy->channel; | 
|  | struct rcar_gen2_phy_driver *drv = channel->drv; | 
|  | unsigned long flags; | 
|  | u32 ugctrl2; | 
|  |  | 
|  | /* | 
|  | * Try to acquire exclusive access to PHY.  The first driver calling | 
|  | * phy_init()  on a given channel wins, and all attempts  to use another | 
|  | * PHY on this channel will fail until phy_exit() is called by the first | 
|  | * driver.   Achieving this with cmpxcgh() should be SMP-safe. | 
|  | */ | 
|  | if (cmpxchg(&channel->selected_phy, -1, phy->number) != -1) | 
|  | return -EBUSY; | 
|  |  | 
|  | clk_prepare_enable(drv->clk); | 
|  |  | 
|  | spin_lock_irqsave(&drv->lock, flags); | 
|  | ugctrl2 = readl(drv->base + USBHS_UGCTRL2); | 
|  | ugctrl2 &= ~channel->select_mask; | 
|  | ugctrl2 |= phy->select_value; | 
|  | writel(ugctrl2, drv->base + USBHS_UGCTRL2); | 
|  | spin_unlock_irqrestore(&drv->lock, flags); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int rcar_gen2_phy_exit(struct phy *p) | 
|  | { | 
|  | struct rcar_gen2_phy *phy = phy_get_drvdata(p); | 
|  | struct rcar_gen2_channel *channel = phy->channel; | 
|  |  | 
|  | clk_disable_unprepare(channel->drv->clk); | 
|  |  | 
|  | channel->selected_phy = -1; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int rcar_gen2_phy_power_on(struct phy *p) | 
|  | { | 
|  | struct rcar_gen2_phy *phy = phy_get_drvdata(p); | 
|  | struct rcar_gen2_phy_driver *drv = phy->channel->drv; | 
|  | void __iomem *base = drv->base; | 
|  | unsigned long flags; | 
|  | u32 value; | 
|  | int err = 0, i; | 
|  |  | 
|  | /* Skip if it's not USBHS */ | 
|  | if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB) | 
|  | return 0; | 
|  |  | 
|  | spin_lock_irqsave(&drv->lock, flags); | 
|  |  | 
|  | /* Power on USBHS PHY */ | 
|  | value = readl(base + USBHS_UGCTRL); | 
|  | value &= ~USBHS_UGCTRL_PLLRESET; | 
|  | writel(value, base + USBHS_UGCTRL); | 
|  |  | 
|  | value = readw(base + USBHS_LPSTS); | 
|  | value |= USBHS_LPSTS_SUSPM; | 
|  | writew(value, base + USBHS_LPSTS); | 
|  |  | 
|  | for (i = 0; i < 20; i++) { | 
|  | value = readl(base + USBHS_UGSTS); | 
|  | if ((value & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) { | 
|  | value = readl(base + USBHS_UGCTRL); | 
|  | value |= USBHS_UGCTRL_CONNECT; | 
|  | writel(value, base + USBHS_UGCTRL); | 
|  | goto out; | 
|  | } | 
|  | udelay(1); | 
|  | } | 
|  |  | 
|  | /* Timed out waiting for the PLL lock */ | 
|  | err = -ETIMEDOUT; | 
|  |  | 
|  | out: | 
|  | spin_unlock_irqrestore(&drv->lock, flags); | 
|  |  | 
|  | return err; | 
|  | } | 
|  |  | 
|  | static int rcar_gen2_phy_power_off(struct phy *p) | 
|  | { | 
|  | struct rcar_gen2_phy *phy = phy_get_drvdata(p); | 
|  | struct rcar_gen2_phy_driver *drv = phy->channel->drv; | 
|  | void __iomem *base = drv->base; | 
|  | unsigned long flags; | 
|  | u32 value; | 
|  |  | 
|  | /* Skip if it's not USBHS */ | 
|  | if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB) | 
|  | return 0; | 
|  |  | 
|  | spin_lock_irqsave(&drv->lock, flags); | 
|  |  | 
|  | /* Power off USBHS PHY */ | 
|  | value = readl(base + USBHS_UGCTRL); | 
|  | value &= ~USBHS_UGCTRL_CONNECT; | 
|  | writel(value, base + USBHS_UGCTRL); | 
|  |  | 
|  | value = readw(base + USBHS_LPSTS); | 
|  | value &= ~USBHS_LPSTS_SUSPM; | 
|  | writew(value, base + USBHS_LPSTS); | 
|  |  | 
|  | value = readl(base + USBHS_UGCTRL); | 
|  | value |= USBHS_UGCTRL_PLLRESET; | 
|  | writel(value, base + USBHS_UGCTRL); | 
|  |  | 
|  | spin_unlock_irqrestore(&drv->lock, flags); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int rz_g1c_phy_power_on(struct phy *p) | 
|  | { | 
|  | struct rcar_gen2_phy *phy = phy_get_drvdata(p); | 
|  | struct rcar_gen2_phy_driver *drv = phy->channel->drv; | 
|  | void __iomem *base = drv->base; | 
|  | unsigned long flags; | 
|  | u32 value; | 
|  |  | 
|  | spin_lock_irqsave(&drv->lock, flags); | 
|  |  | 
|  | /* Power on USBHS PHY */ | 
|  | value = readl(base + USBHS_UGCTRL); | 
|  | value &= ~USBHS_UGCTRL_PLLRESET; | 
|  | writel(value, base + USBHS_UGCTRL); | 
|  |  | 
|  | /* As per the data sheet wait 340 micro sec for power stable */ | 
|  | udelay(340); | 
|  |  | 
|  | if (phy->select_value == USBHS_UGCTRL2_USB0SEL_HS_USB20) { | 
|  | value = readw(base + USBHS_LPSTS); | 
|  | value |= USBHS_LPSTS_SUSPM; | 
|  | writew(value, base + USBHS_LPSTS); | 
|  | } | 
|  |  | 
|  | spin_unlock_irqrestore(&drv->lock, flags); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int rz_g1c_phy_power_off(struct phy *p) | 
|  | { | 
|  | struct rcar_gen2_phy *phy = phy_get_drvdata(p); | 
|  | struct rcar_gen2_phy_driver *drv = phy->channel->drv; | 
|  | void __iomem *base = drv->base; | 
|  | unsigned long flags; | 
|  | u32 value; | 
|  |  | 
|  | spin_lock_irqsave(&drv->lock, flags); | 
|  | /* Power off USBHS PHY */ | 
|  | if (phy->select_value == USBHS_UGCTRL2_USB0SEL_HS_USB20) { | 
|  | value = readw(base + USBHS_LPSTS); | 
|  | value &= ~USBHS_LPSTS_SUSPM; | 
|  | writew(value, base + USBHS_LPSTS); | 
|  | } | 
|  |  | 
|  | value = readl(base + USBHS_UGCTRL); | 
|  | value |= USBHS_UGCTRL_PLLRESET; | 
|  | writel(value, base + USBHS_UGCTRL); | 
|  |  | 
|  | spin_unlock_irqrestore(&drv->lock, flags); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct phy_ops rcar_gen2_phy_ops = { | 
|  | .init		= rcar_gen2_phy_init, | 
|  | .exit		= rcar_gen2_phy_exit, | 
|  | .power_on	= rcar_gen2_phy_power_on, | 
|  | .power_off	= rcar_gen2_phy_power_off, | 
|  | .owner		= THIS_MODULE, | 
|  | }; | 
|  |  | 
|  | static const struct phy_ops rz_g1c_phy_ops = { | 
|  | .init		= rcar_gen2_phy_init, | 
|  | .exit		= rcar_gen2_phy_exit, | 
|  | .power_on	= rz_g1c_phy_power_on, | 
|  | .power_off	= rz_g1c_phy_power_off, | 
|  | .owner		= THIS_MODULE, | 
|  | }; | 
|  |  | 
|  | static const u32 pci_select_value[][PHYS_PER_CHANNEL] = { | 
|  | [0]	= { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB }, | 
|  | [2]	= { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 }, | 
|  | }; | 
|  |  | 
|  | static const u32 usb20_select_value[][PHYS_PER_CHANNEL] = { | 
|  | { USBHS_UGCTRL2_USB0SEL_USB20, USBHS_UGCTRL2_USB0SEL_HS_USB20 }, | 
|  | }; | 
|  |  | 
|  | static const struct rcar_gen2_phy_data rcar_gen2_usb_phy_data = { | 
|  | .gen2_phy_ops = &rcar_gen2_phy_ops, | 
|  | .select_value = pci_select_value, | 
|  | }; | 
|  |  | 
|  | static const struct rcar_gen2_phy_data rz_g1c_usb_phy_data = { | 
|  | .gen2_phy_ops = &rz_g1c_phy_ops, | 
|  | .select_value = usb20_select_value, | 
|  | }; | 
|  |  | 
|  | static const struct of_device_id rcar_gen2_phy_match_table[] = { | 
|  | { | 
|  | .compatible = "renesas,usb-phy-r8a77470", | 
|  | .data = &rz_g1c_usb_phy_data, | 
|  | }, | 
|  | { | 
|  | .compatible = "renesas,usb-phy-r8a7790", | 
|  | .data = &rcar_gen2_usb_phy_data, | 
|  | }, | 
|  | { | 
|  | .compatible = "renesas,usb-phy-r8a7791", | 
|  | .data = &rcar_gen2_usb_phy_data, | 
|  | }, | 
|  | { | 
|  | .compatible = "renesas,usb-phy-r8a7794", | 
|  | .data = &rcar_gen2_usb_phy_data, | 
|  | }, | 
|  | { | 
|  | .compatible = "renesas,rcar-gen2-usb-phy", | 
|  | .data = &rcar_gen2_usb_phy_data, | 
|  | }, | 
|  | { /* sentinel */ }, | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table); | 
|  |  | 
|  | static struct phy *rcar_gen2_phy_xlate(struct device *dev, | 
|  | struct of_phandle_args *args) | 
|  | { | 
|  | struct rcar_gen2_phy_driver *drv; | 
|  | struct device_node *np = args->np; | 
|  | int i; | 
|  |  | 
|  | drv = dev_get_drvdata(dev); | 
|  | if (!drv) | 
|  | return ERR_PTR(-EINVAL); | 
|  |  | 
|  | for (i = 0; i < drv->num_channels; i++) { | 
|  | if (np == drv->channels[i].of_node) | 
|  | break; | 
|  | } | 
|  |  | 
|  | if (i >= drv->num_channels || args->args[0] >= 2) | 
|  | return ERR_PTR(-ENODEV); | 
|  |  | 
|  | return drv->channels[i].phys[args->args[0]].phy; | 
|  | } | 
|  |  | 
|  | static const u32 select_mask[] = { | 
|  | [0]	= USBHS_UGCTRL2_USB0SEL, | 
|  | [2]	= USBHS_UGCTRL2_USB2SEL, | 
|  | }; | 
|  |  | 
|  | static int rcar_gen2_phy_probe(struct platform_device *pdev) | 
|  | { | 
|  | struct device *dev = &pdev->dev; | 
|  | struct rcar_gen2_phy_driver *drv; | 
|  | struct phy_provider *provider; | 
|  | struct device_node *np; | 
|  | struct resource *res; | 
|  | void __iomem *base; | 
|  | struct clk *clk; | 
|  | const struct rcar_gen2_phy_data *data; | 
|  | int i = 0; | 
|  |  | 
|  | if (!dev->of_node) { | 
|  | dev_err(dev, | 
|  | "This driver is required to be instantiated from device tree\n"); | 
|  | return -EINVAL; | 
|  | } | 
|  |  | 
|  | clk = devm_clk_get(dev, "usbhs"); | 
|  | if (IS_ERR(clk)) { | 
|  | dev_err(dev, "Can't get USBHS 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); | 
|  |  | 
|  | drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); | 
|  | if (!drv) | 
|  | return -ENOMEM; | 
|  |  | 
|  | spin_lock_init(&drv->lock); | 
|  |  | 
|  | drv->clk = clk; | 
|  | drv->base = base; | 
|  |  | 
|  | data = of_device_get_match_data(dev); | 
|  | if (!data) | 
|  | return -EINVAL; | 
|  |  | 
|  | drv->num_channels = of_get_child_count(dev->of_node); | 
|  | drv->channels = devm_kcalloc(dev, drv->num_channels, | 
|  | sizeof(struct rcar_gen2_channel), | 
|  | GFP_KERNEL); | 
|  | if (!drv->channels) | 
|  | return -ENOMEM; | 
|  |  | 
|  | for_each_child_of_node(dev->of_node, np) { | 
|  | struct rcar_gen2_channel *channel = drv->channels + i; | 
|  | u32 channel_num; | 
|  | int error, n; | 
|  |  | 
|  | channel->of_node = np; | 
|  | channel->drv = drv; | 
|  | channel->selected_phy = -1; | 
|  |  | 
|  | error = of_property_read_u32(np, "reg", &channel_num); | 
|  | if (error || channel_num > 2) { | 
|  | dev_err(dev, "Invalid \"reg\" property\n"); | 
|  | return error; | 
|  | } | 
|  | channel->select_mask = select_mask[channel_num]; | 
|  |  | 
|  | for (n = 0; n < PHYS_PER_CHANNEL; n++) { | 
|  | struct rcar_gen2_phy *phy = &channel->phys[n]; | 
|  |  | 
|  | phy->channel = channel; | 
|  | phy->number = n; | 
|  | phy->select_value = data->select_value[channel_num][n]; | 
|  |  | 
|  | phy->phy = devm_phy_create(dev, NULL, | 
|  | data->gen2_phy_ops); | 
|  | if (IS_ERR(phy->phy)) { | 
|  | dev_err(dev, "Failed to create PHY\n"); | 
|  | return PTR_ERR(phy->phy); | 
|  | } | 
|  | phy_set_drvdata(phy->phy, phy); | 
|  | } | 
|  |  | 
|  | i++; | 
|  | } | 
|  |  | 
|  | provider = devm_of_phy_provider_register(dev, rcar_gen2_phy_xlate); | 
|  | if (IS_ERR(provider)) { | 
|  | dev_err(dev, "Failed to register PHY provider\n"); | 
|  | return PTR_ERR(provider); | 
|  | } | 
|  |  | 
|  | dev_set_drvdata(dev, drv); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct platform_driver rcar_gen2_phy_driver = { | 
|  | .driver = { | 
|  | .name		= "phy_rcar_gen2", | 
|  | .of_match_table	= rcar_gen2_phy_match_table, | 
|  | }, | 
|  | .probe	= rcar_gen2_phy_probe, | 
|  | }; | 
|  |  | 
|  | module_platform_driver(rcar_gen2_phy_driver); | 
|  |  | 
|  | MODULE_LICENSE("GPL v2"); | 
|  | MODULE_DESCRIPTION("Renesas R-Car Gen2 PHY"); | 
|  | MODULE_AUTHOR("Sergei Shtylyov <[email protected]>"); |