162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Renesas R-Car Gen2 PHY driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2014 Renesas Solutions Corp. 662306a36Sopenharmony_ci * Copyright (C) 2014 Cogent Embedded, Inc. 762306a36Sopenharmony_ci * Copyright (C) 2019 Renesas Electronics Corp. 862306a36Sopenharmony_ci */ 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/clk.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include <linux/module.h> 1462306a36Sopenharmony_ci#include <linux/of.h> 1562306a36Sopenharmony_ci#include <linux/phy/phy.h> 1662306a36Sopenharmony_ci#include <linux/platform_device.h> 1762306a36Sopenharmony_ci#include <linux/spinlock.h> 1862306a36Sopenharmony_ci#include <linux/atomic.h> 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#define USBHS_LPSTS 0x02 2162306a36Sopenharmony_ci#define USBHS_UGCTRL 0x80 2262306a36Sopenharmony_ci#define USBHS_UGCTRL2 0x84 2362306a36Sopenharmony_ci#define USBHS_UGSTS 0x88 /* From technical update */ 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci/* Low Power Status register (LPSTS) */ 2662306a36Sopenharmony_ci#define USBHS_LPSTS_SUSPM 0x4000 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* USB General control register (UGCTRL) */ 2962306a36Sopenharmony_ci#define USBHS_UGCTRL_CONNECT 0x00000004 3062306a36Sopenharmony_ci#define USBHS_UGCTRL_PLLRESET 0x00000001 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci/* USB General control register 2 (UGCTRL2) */ 3362306a36Sopenharmony_ci#define USBHS_UGCTRL2_USB2SEL 0x80000000 3462306a36Sopenharmony_ci#define USBHS_UGCTRL2_USB2SEL_PCI 0x00000000 3562306a36Sopenharmony_ci#define USBHS_UGCTRL2_USB2SEL_USB30 0x80000000 3662306a36Sopenharmony_ci#define USBHS_UGCTRL2_USB0SEL 0x00000030 3762306a36Sopenharmony_ci#define USBHS_UGCTRL2_USB0SEL_PCI 0x00000010 3862306a36Sopenharmony_ci#define USBHS_UGCTRL2_USB0SEL_HS_USB 0x00000030 3962306a36Sopenharmony_ci#define USBHS_UGCTRL2_USB0SEL_USB20 0x00000010 4062306a36Sopenharmony_ci#define USBHS_UGCTRL2_USB0SEL_HS_USB20 0x00000020 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* USB General status register (UGSTS) */ 4362306a36Sopenharmony_ci#define USBHS_UGSTS_LOCK 0x00000100 /* From technical update */ 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci#define PHYS_PER_CHANNEL 2 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistruct rcar_gen2_phy { 4862306a36Sopenharmony_ci struct phy *phy; 4962306a36Sopenharmony_ci struct rcar_gen2_channel *channel; 5062306a36Sopenharmony_ci int number; 5162306a36Sopenharmony_ci u32 select_value; 5262306a36Sopenharmony_ci}; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistruct rcar_gen2_channel { 5562306a36Sopenharmony_ci struct device_node *of_node; 5662306a36Sopenharmony_ci struct rcar_gen2_phy_driver *drv; 5762306a36Sopenharmony_ci struct rcar_gen2_phy phys[PHYS_PER_CHANNEL]; 5862306a36Sopenharmony_ci int selected_phy; 5962306a36Sopenharmony_ci u32 select_mask; 6062306a36Sopenharmony_ci}; 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_cistruct rcar_gen2_phy_driver { 6362306a36Sopenharmony_ci void __iomem *base; 6462306a36Sopenharmony_ci struct clk *clk; 6562306a36Sopenharmony_ci spinlock_t lock; 6662306a36Sopenharmony_ci int num_channels; 6762306a36Sopenharmony_ci struct rcar_gen2_channel *channels; 6862306a36Sopenharmony_ci}; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistruct rcar_gen2_phy_data { 7162306a36Sopenharmony_ci const struct phy_ops *gen2_phy_ops; 7262306a36Sopenharmony_ci const u32 (*select_value)[PHYS_PER_CHANNEL]; 7362306a36Sopenharmony_ci const u32 num_channels; 7462306a36Sopenharmony_ci}; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int rcar_gen2_phy_init(struct phy *p) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci struct rcar_gen2_phy *phy = phy_get_drvdata(p); 7962306a36Sopenharmony_ci struct rcar_gen2_channel *channel = phy->channel; 8062306a36Sopenharmony_ci struct rcar_gen2_phy_driver *drv = channel->drv; 8162306a36Sopenharmony_ci unsigned long flags; 8262306a36Sopenharmony_ci u32 ugctrl2; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci /* 8562306a36Sopenharmony_ci * Try to acquire exclusive access to PHY. The first driver calling 8662306a36Sopenharmony_ci * phy_init() on a given channel wins, and all attempts to use another 8762306a36Sopenharmony_ci * PHY on this channel will fail until phy_exit() is called by the first 8862306a36Sopenharmony_ci * driver. Achieving this with cmpxcgh() should be SMP-safe. 8962306a36Sopenharmony_ci */ 9062306a36Sopenharmony_ci if (cmpxchg(&channel->selected_phy, -1, phy->number) != -1) 9162306a36Sopenharmony_ci return -EBUSY; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci clk_prepare_enable(drv->clk); 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci spin_lock_irqsave(&drv->lock, flags); 9662306a36Sopenharmony_ci ugctrl2 = readl(drv->base + USBHS_UGCTRL2); 9762306a36Sopenharmony_ci ugctrl2 &= ~channel->select_mask; 9862306a36Sopenharmony_ci ugctrl2 |= phy->select_value; 9962306a36Sopenharmony_ci writel(ugctrl2, drv->base + USBHS_UGCTRL2); 10062306a36Sopenharmony_ci spin_unlock_irqrestore(&drv->lock, flags); 10162306a36Sopenharmony_ci return 0; 10262306a36Sopenharmony_ci} 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_cistatic int rcar_gen2_phy_exit(struct phy *p) 10562306a36Sopenharmony_ci{ 10662306a36Sopenharmony_ci struct rcar_gen2_phy *phy = phy_get_drvdata(p); 10762306a36Sopenharmony_ci struct rcar_gen2_channel *channel = phy->channel; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci clk_disable_unprepare(channel->drv->clk); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci channel->selected_phy = -1; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci return 0; 11462306a36Sopenharmony_ci} 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistatic int rcar_gen2_phy_power_on(struct phy *p) 11762306a36Sopenharmony_ci{ 11862306a36Sopenharmony_ci struct rcar_gen2_phy *phy = phy_get_drvdata(p); 11962306a36Sopenharmony_ci struct rcar_gen2_phy_driver *drv = phy->channel->drv; 12062306a36Sopenharmony_ci void __iomem *base = drv->base; 12162306a36Sopenharmony_ci unsigned long flags; 12262306a36Sopenharmony_ci u32 value; 12362306a36Sopenharmony_ci int err = 0, i; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci /* Skip if it's not USBHS */ 12662306a36Sopenharmony_ci if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB) 12762306a36Sopenharmony_ci return 0; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci spin_lock_irqsave(&drv->lock, flags); 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci /* Power on USBHS PHY */ 13262306a36Sopenharmony_ci value = readl(base + USBHS_UGCTRL); 13362306a36Sopenharmony_ci value &= ~USBHS_UGCTRL_PLLRESET; 13462306a36Sopenharmony_ci writel(value, base + USBHS_UGCTRL); 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci value = readw(base + USBHS_LPSTS); 13762306a36Sopenharmony_ci value |= USBHS_LPSTS_SUSPM; 13862306a36Sopenharmony_ci writew(value, base + USBHS_LPSTS); 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci for (i = 0; i < 20; i++) { 14162306a36Sopenharmony_ci value = readl(base + USBHS_UGSTS); 14262306a36Sopenharmony_ci if ((value & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) { 14362306a36Sopenharmony_ci value = readl(base + USBHS_UGCTRL); 14462306a36Sopenharmony_ci value |= USBHS_UGCTRL_CONNECT; 14562306a36Sopenharmony_ci writel(value, base + USBHS_UGCTRL); 14662306a36Sopenharmony_ci goto out; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci udelay(1); 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Timed out waiting for the PLL lock */ 15262306a36Sopenharmony_ci err = -ETIMEDOUT; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ciout: 15562306a36Sopenharmony_ci spin_unlock_irqrestore(&drv->lock, flags); 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci return err; 15862306a36Sopenharmony_ci} 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistatic int rcar_gen2_phy_power_off(struct phy *p) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct rcar_gen2_phy *phy = phy_get_drvdata(p); 16362306a36Sopenharmony_ci struct rcar_gen2_phy_driver *drv = phy->channel->drv; 16462306a36Sopenharmony_ci void __iomem *base = drv->base; 16562306a36Sopenharmony_ci unsigned long flags; 16662306a36Sopenharmony_ci u32 value; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* Skip if it's not USBHS */ 16962306a36Sopenharmony_ci if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB) 17062306a36Sopenharmony_ci return 0; 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci spin_lock_irqsave(&drv->lock, flags); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Power off USBHS PHY */ 17562306a36Sopenharmony_ci value = readl(base + USBHS_UGCTRL); 17662306a36Sopenharmony_ci value &= ~USBHS_UGCTRL_CONNECT; 17762306a36Sopenharmony_ci writel(value, base + USBHS_UGCTRL); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci value = readw(base + USBHS_LPSTS); 18062306a36Sopenharmony_ci value &= ~USBHS_LPSTS_SUSPM; 18162306a36Sopenharmony_ci writew(value, base + USBHS_LPSTS); 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci value = readl(base + USBHS_UGCTRL); 18462306a36Sopenharmony_ci value |= USBHS_UGCTRL_PLLRESET; 18562306a36Sopenharmony_ci writel(value, base + USBHS_UGCTRL); 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_ci spin_unlock_irqrestore(&drv->lock, flags); 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci return 0; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_cistatic int rz_g1c_phy_power_on(struct phy *p) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci struct rcar_gen2_phy *phy = phy_get_drvdata(p); 19562306a36Sopenharmony_ci struct rcar_gen2_phy_driver *drv = phy->channel->drv; 19662306a36Sopenharmony_ci void __iomem *base = drv->base; 19762306a36Sopenharmony_ci unsigned long flags; 19862306a36Sopenharmony_ci u32 value; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci spin_lock_irqsave(&drv->lock, flags); 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci /* Power on USBHS PHY */ 20362306a36Sopenharmony_ci value = readl(base + USBHS_UGCTRL); 20462306a36Sopenharmony_ci value &= ~USBHS_UGCTRL_PLLRESET; 20562306a36Sopenharmony_ci writel(value, base + USBHS_UGCTRL); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci /* As per the data sheet wait 340 micro sec for power stable */ 20862306a36Sopenharmony_ci udelay(340); 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (phy->select_value == USBHS_UGCTRL2_USB0SEL_HS_USB20) { 21162306a36Sopenharmony_ci value = readw(base + USBHS_LPSTS); 21262306a36Sopenharmony_ci value |= USBHS_LPSTS_SUSPM; 21362306a36Sopenharmony_ci writew(value, base + USBHS_LPSTS); 21462306a36Sopenharmony_ci } 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci spin_unlock_irqrestore(&drv->lock, flags); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci return 0; 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_cistatic int rz_g1c_phy_power_off(struct phy *p) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci struct rcar_gen2_phy *phy = phy_get_drvdata(p); 22462306a36Sopenharmony_ci struct rcar_gen2_phy_driver *drv = phy->channel->drv; 22562306a36Sopenharmony_ci void __iomem *base = drv->base; 22662306a36Sopenharmony_ci unsigned long flags; 22762306a36Sopenharmony_ci u32 value; 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci spin_lock_irqsave(&drv->lock, flags); 23062306a36Sopenharmony_ci /* Power off USBHS PHY */ 23162306a36Sopenharmony_ci if (phy->select_value == USBHS_UGCTRL2_USB0SEL_HS_USB20) { 23262306a36Sopenharmony_ci value = readw(base + USBHS_LPSTS); 23362306a36Sopenharmony_ci value &= ~USBHS_LPSTS_SUSPM; 23462306a36Sopenharmony_ci writew(value, base + USBHS_LPSTS); 23562306a36Sopenharmony_ci } 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci value = readl(base + USBHS_UGCTRL); 23862306a36Sopenharmony_ci value |= USBHS_UGCTRL_PLLRESET; 23962306a36Sopenharmony_ci writel(value, base + USBHS_UGCTRL); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci spin_unlock_irqrestore(&drv->lock, flags); 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return 0; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic const struct phy_ops rcar_gen2_phy_ops = { 24762306a36Sopenharmony_ci .init = rcar_gen2_phy_init, 24862306a36Sopenharmony_ci .exit = rcar_gen2_phy_exit, 24962306a36Sopenharmony_ci .power_on = rcar_gen2_phy_power_on, 25062306a36Sopenharmony_ci .power_off = rcar_gen2_phy_power_off, 25162306a36Sopenharmony_ci .owner = THIS_MODULE, 25262306a36Sopenharmony_ci}; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic const struct phy_ops rz_g1c_phy_ops = { 25562306a36Sopenharmony_ci .init = rcar_gen2_phy_init, 25662306a36Sopenharmony_ci .exit = rcar_gen2_phy_exit, 25762306a36Sopenharmony_ci .power_on = rz_g1c_phy_power_on, 25862306a36Sopenharmony_ci .power_off = rz_g1c_phy_power_off, 25962306a36Sopenharmony_ci .owner = THIS_MODULE, 26062306a36Sopenharmony_ci}; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_cistatic const u32 pci_select_value[][PHYS_PER_CHANNEL] = { 26362306a36Sopenharmony_ci [0] = { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB }, 26462306a36Sopenharmony_ci [2] = { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 }, 26562306a36Sopenharmony_ci}; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic const u32 usb20_select_value[][PHYS_PER_CHANNEL] = { 26862306a36Sopenharmony_ci { USBHS_UGCTRL2_USB0SEL_USB20, USBHS_UGCTRL2_USB0SEL_HS_USB20 }, 26962306a36Sopenharmony_ci}; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_cistatic const struct rcar_gen2_phy_data rcar_gen2_usb_phy_data = { 27262306a36Sopenharmony_ci .gen2_phy_ops = &rcar_gen2_phy_ops, 27362306a36Sopenharmony_ci .select_value = pci_select_value, 27462306a36Sopenharmony_ci .num_channels = ARRAY_SIZE(pci_select_value), 27562306a36Sopenharmony_ci}; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic const struct rcar_gen2_phy_data rz_g1c_usb_phy_data = { 27862306a36Sopenharmony_ci .gen2_phy_ops = &rz_g1c_phy_ops, 27962306a36Sopenharmony_ci .select_value = usb20_select_value, 28062306a36Sopenharmony_ci .num_channels = ARRAY_SIZE(usb20_select_value), 28162306a36Sopenharmony_ci}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_cistatic const struct of_device_id rcar_gen2_phy_match_table[] = { 28462306a36Sopenharmony_ci { 28562306a36Sopenharmony_ci .compatible = "renesas,usb-phy-r8a77470", 28662306a36Sopenharmony_ci .data = &rz_g1c_usb_phy_data, 28762306a36Sopenharmony_ci }, 28862306a36Sopenharmony_ci { 28962306a36Sopenharmony_ci .compatible = "renesas,usb-phy-r8a7790", 29062306a36Sopenharmony_ci .data = &rcar_gen2_usb_phy_data, 29162306a36Sopenharmony_ci }, 29262306a36Sopenharmony_ci { 29362306a36Sopenharmony_ci .compatible = "renesas,usb-phy-r8a7791", 29462306a36Sopenharmony_ci .data = &rcar_gen2_usb_phy_data, 29562306a36Sopenharmony_ci }, 29662306a36Sopenharmony_ci { 29762306a36Sopenharmony_ci .compatible = "renesas,usb-phy-r8a7794", 29862306a36Sopenharmony_ci .data = &rcar_gen2_usb_phy_data, 29962306a36Sopenharmony_ci }, 30062306a36Sopenharmony_ci { 30162306a36Sopenharmony_ci .compatible = "renesas,rcar-gen2-usb-phy", 30262306a36Sopenharmony_ci .data = &rcar_gen2_usb_phy_data, 30362306a36Sopenharmony_ci }, 30462306a36Sopenharmony_ci { /* sentinel */ }, 30562306a36Sopenharmony_ci}; 30662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table); 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic struct phy *rcar_gen2_phy_xlate(struct device *dev, 30962306a36Sopenharmony_ci struct of_phandle_args *args) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct rcar_gen2_phy_driver *drv; 31262306a36Sopenharmony_ci struct device_node *np = args->np; 31362306a36Sopenharmony_ci int i; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci drv = dev_get_drvdata(dev); 31662306a36Sopenharmony_ci if (!drv) 31762306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci for (i = 0; i < drv->num_channels; i++) { 32062306a36Sopenharmony_ci if (np == drv->channels[i].of_node) 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci if (i >= drv->num_channels || args->args[0] >= 2) 32562306a36Sopenharmony_ci return ERR_PTR(-ENODEV); 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci return drv->channels[i].phys[args->args[0]].phy; 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic const u32 select_mask[] = { 33162306a36Sopenharmony_ci [0] = USBHS_UGCTRL2_USB0SEL, 33262306a36Sopenharmony_ci [2] = USBHS_UGCTRL2_USB2SEL, 33362306a36Sopenharmony_ci}; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic int rcar_gen2_phy_probe(struct platform_device *pdev) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci struct device *dev = &pdev->dev; 33862306a36Sopenharmony_ci struct rcar_gen2_phy_driver *drv; 33962306a36Sopenharmony_ci struct phy_provider *provider; 34062306a36Sopenharmony_ci struct device_node *np; 34162306a36Sopenharmony_ci void __iomem *base; 34262306a36Sopenharmony_ci struct clk *clk; 34362306a36Sopenharmony_ci const struct rcar_gen2_phy_data *data; 34462306a36Sopenharmony_ci int i = 0; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci if (!dev->of_node) { 34762306a36Sopenharmony_ci dev_err(dev, 34862306a36Sopenharmony_ci "This driver is required to be instantiated from device tree\n"); 34962306a36Sopenharmony_ci return -EINVAL; 35062306a36Sopenharmony_ci } 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci clk = devm_clk_get(dev, "usbhs"); 35362306a36Sopenharmony_ci if (IS_ERR(clk)) { 35462306a36Sopenharmony_ci dev_err(dev, "Can't get USBHS clock\n"); 35562306a36Sopenharmony_ci return PTR_ERR(clk); 35662306a36Sopenharmony_ci } 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci base = devm_platform_ioremap_resource(pdev, 0); 35962306a36Sopenharmony_ci if (IS_ERR(base)) 36062306a36Sopenharmony_ci return PTR_ERR(base); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL); 36362306a36Sopenharmony_ci if (!drv) 36462306a36Sopenharmony_ci return -ENOMEM; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci spin_lock_init(&drv->lock); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci drv->clk = clk; 36962306a36Sopenharmony_ci drv->base = base; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci data = of_device_get_match_data(dev); 37262306a36Sopenharmony_ci if (!data) 37362306a36Sopenharmony_ci return -EINVAL; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci drv->num_channels = of_get_child_count(dev->of_node); 37662306a36Sopenharmony_ci drv->channels = devm_kcalloc(dev, drv->num_channels, 37762306a36Sopenharmony_ci sizeof(struct rcar_gen2_channel), 37862306a36Sopenharmony_ci GFP_KERNEL); 37962306a36Sopenharmony_ci if (!drv->channels) 38062306a36Sopenharmony_ci return -ENOMEM; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci for_each_child_of_node(dev->of_node, np) { 38362306a36Sopenharmony_ci struct rcar_gen2_channel *channel = drv->channels + i; 38462306a36Sopenharmony_ci u32 channel_num; 38562306a36Sopenharmony_ci int error, n; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci channel->of_node = np; 38862306a36Sopenharmony_ci channel->drv = drv; 38962306a36Sopenharmony_ci channel->selected_phy = -1; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci error = of_property_read_u32(np, "reg", &channel_num); 39262306a36Sopenharmony_ci if (error || channel_num >= data->num_channels) { 39362306a36Sopenharmony_ci dev_err(dev, "Invalid \"reg\" property\n"); 39462306a36Sopenharmony_ci of_node_put(np); 39562306a36Sopenharmony_ci return error; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci channel->select_mask = select_mask[channel_num]; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci for (n = 0; n < PHYS_PER_CHANNEL; n++) { 40062306a36Sopenharmony_ci struct rcar_gen2_phy *phy = &channel->phys[n]; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci phy->channel = channel; 40362306a36Sopenharmony_ci phy->number = n; 40462306a36Sopenharmony_ci phy->select_value = data->select_value[channel_num][n]; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci phy->phy = devm_phy_create(dev, NULL, 40762306a36Sopenharmony_ci data->gen2_phy_ops); 40862306a36Sopenharmony_ci if (IS_ERR(phy->phy)) { 40962306a36Sopenharmony_ci dev_err(dev, "Failed to create PHY\n"); 41062306a36Sopenharmony_ci of_node_put(np); 41162306a36Sopenharmony_ci return PTR_ERR(phy->phy); 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci phy_set_drvdata(phy->phy, phy); 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci i++; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci provider = devm_of_phy_provider_register(dev, rcar_gen2_phy_xlate); 42062306a36Sopenharmony_ci if (IS_ERR(provider)) { 42162306a36Sopenharmony_ci dev_err(dev, "Failed to register PHY provider\n"); 42262306a36Sopenharmony_ci return PTR_ERR(provider); 42362306a36Sopenharmony_ci } 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci dev_set_drvdata(dev, drv); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic struct platform_driver rcar_gen2_phy_driver = { 43162306a36Sopenharmony_ci .driver = { 43262306a36Sopenharmony_ci .name = "phy_rcar_gen2", 43362306a36Sopenharmony_ci .of_match_table = rcar_gen2_phy_match_table, 43462306a36Sopenharmony_ci }, 43562306a36Sopenharmony_ci .probe = rcar_gen2_phy_probe, 43662306a36Sopenharmony_ci}; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cimodule_platform_driver(rcar_gen2_phy_driver); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 44162306a36Sopenharmony_ciMODULE_DESCRIPTION("Renesas R-Car Gen2 PHY"); 44262306a36Sopenharmony_ciMODULE_AUTHOR("Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>"); 443