1eee0e5daSWolfram Sang // SPDX-License-Identifier: GPL-2.0
20b56e9a7SVivek Gautam /*
30b56e9a7SVivek Gautam * Renesas R-Car Gen2 PHY driver
40b56e9a7SVivek Gautam *
50b56e9a7SVivek Gautam * Copyright (C) 2014 Renesas Solutions Corp.
60b56e9a7SVivek Gautam * Copyright (C) 2014 Cogent Embedded, Inc.
7b7187e00SBiju Das * Copyright (C) 2019 Renesas Electronics Corp.
80b56e9a7SVivek Gautam */
90b56e9a7SVivek Gautam
100b56e9a7SVivek Gautam #include <linux/clk.h>
110b56e9a7SVivek Gautam #include <linux/delay.h>
120b56e9a7SVivek Gautam #include <linux/io.h>
130b56e9a7SVivek Gautam #include <linux/module.h>
140b56e9a7SVivek Gautam #include <linux/of.h>
150b56e9a7SVivek Gautam #include <linux/phy/phy.h>
160b56e9a7SVivek Gautam #include <linux/platform_device.h>
170b56e9a7SVivek Gautam #include <linux/spinlock.h>
180b56e9a7SVivek Gautam #include <linux/atomic.h>
190b56e9a7SVivek Gautam
200b56e9a7SVivek Gautam #define USBHS_LPSTS 0x02
210b56e9a7SVivek Gautam #define USBHS_UGCTRL 0x80
220b56e9a7SVivek Gautam #define USBHS_UGCTRL2 0x84
230b56e9a7SVivek Gautam #define USBHS_UGSTS 0x88 /* From technical update */
240b56e9a7SVivek Gautam
250b56e9a7SVivek Gautam /* Low Power Status register (LPSTS) */
260b56e9a7SVivek Gautam #define USBHS_LPSTS_SUSPM 0x4000
270b56e9a7SVivek Gautam
280b56e9a7SVivek Gautam /* USB General control register (UGCTRL) */
290b56e9a7SVivek Gautam #define USBHS_UGCTRL_CONNECT 0x00000004
300b56e9a7SVivek Gautam #define USBHS_UGCTRL_PLLRESET 0x00000001
310b56e9a7SVivek Gautam
320b56e9a7SVivek Gautam /* USB General control register 2 (UGCTRL2) */
330b56e9a7SVivek Gautam #define USBHS_UGCTRL2_USB2SEL 0x80000000
340b56e9a7SVivek Gautam #define USBHS_UGCTRL2_USB2SEL_PCI 0x00000000
350b56e9a7SVivek Gautam #define USBHS_UGCTRL2_USB2SEL_USB30 0x80000000
360b56e9a7SVivek Gautam #define USBHS_UGCTRL2_USB0SEL 0x00000030
370b56e9a7SVivek Gautam #define USBHS_UGCTRL2_USB0SEL_PCI 0x00000010
380b56e9a7SVivek Gautam #define USBHS_UGCTRL2_USB0SEL_HS_USB 0x00000030
39b7187e00SBiju Das #define USBHS_UGCTRL2_USB0SEL_USB20 0x00000010
40b7187e00SBiju Das #define USBHS_UGCTRL2_USB0SEL_HS_USB20 0x00000020
410b56e9a7SVivek Gautam
420b56e9a7SVivek Gautam /* USB General status register (UGSTS) */
430b56e9a7SVivek Gautam #define USBHS_UGSTS_LOCK 0x00000100 /* From technical update */
440b56e9a7SVivek Gautam
450b56e9a7SVivek Gautam #define PHYS_PER_CHANNEL 2
460b56e9a7SVivek Gautam
470b56e9a7SVivek Gautam struct rcar_gen2_phy {
480b56e9a7SVivek Gautam struct phy *phy;
490b56e9a7SVivek Gautam struct rcar_gen2_channel *channel;
500b56e9a7SVivek Gautam int number;
510b56e9a7SVivek Gautam u32 select_value;
520b56e9a7SVivek Gautam };
530b56e9a7SVivek Gautam
540b56e9a7SVivek Gautam struct rcar_gen2_channel {
550b56e9a7SVivek Gautam struct device_node *of_node;
560b56e9a7SVivek Gautam struct rcar_gen2_phy_driver *drv;
570b56e9a7SVivek Gautam struct rcar_gen2_phy phys[PHYS_PER_CHANNEL];
580b56e9a7SVivek Gautam int selected_phy;
590b56e9a7SVivek Gautam u32 select_mask;
600b56e9a7SVivek Gautam };
610b56e9a7SVivek Gautam
620b56e9a7SVivek Gautam struct rcar_gen2_phy_driver {
630b56e9a7SVivek Gautam void __iomem *base;
640b56e9a7SVivek Gautam struct clk *clk;
650b56e9a7SVivek Gautam spinlock_t lock;
660b56e9a7SVivek Gautam int num_channels;
670b56e9a7SVivek Gautam struct rcar_gen2_channel *channels;
680b56e9a7SVivek Gautam };
690b56e9a7SVivek Gautam
70b7187e00SBiju Das struct rcar_gen2_phy_data {
71b7187e00SBiju Das const struct phy_ops *gen2_phy_ops;
72b7187e00SBiju Das const u32 (*select_value)[PHYS_PER_CHANNEL];
73c9baab38SBiju Das const u32 num_channels;
74b7187e00SBiju Das };
75b7187e00SBiju Das
rcar_gen2_phy_init(struct phy * p)760b56e9a7SVivek Gautam static int rcar_gen2_phy_init(struct phy *p)
770b56e9a7SVivek Gautam {
780b56e9a7SVivek Gautam struct rcar_gen2_phy *phy = phy_get_drvdata(p);
790b56e9a7SVivek Gautam struct rcar_gen2_channel *channel = phy->channel;
800b56e9a7SVivek Gautam struct rcar_gen2_phy_driver *drv = channel->drv;
810b56e9a7SVivek Gautam unsigned long flags;
820b56e9a7SVivek Gautam u32 ugctrl2;
830b56e9a7SVivek Gautam
840b56e9a7SVivek Gautam /*
850b56e9a7SVivek Gautam * Try to acquire exclusive access to PHY. The first driver calling
860b56e9a7SVivek Gautam * phy_init() on a given channel wins, and all attempts to use another
870b56e9a7SVivek Gautam * PHY on this channel will fail until phy_exit() is called by the first
880b56e9a7SVivek Gautam * driver. Achieving this with cmpxcgh() should be SMP-safe.
890b56e9a7SVivek Gautam */
900b56e9a7SVivek Gautam if (cmpxchg(&channel->selected_phy, -1, phy->number) != -1)
910b56e9a7SVivek Gautam return -EBUSY;
920b56e9a7SVivek Gautam
930b56e9a7SVivek Gautam clk_prepare_enable(drv->clk);
940b56e9a7SVivek Gautam
950b56e9a7SVivek Gautam spin_lock_irqsave(&drv->lock, flags);
960b56e9a7SVivek Gautam ugctrl2 = readl(drv->base + USBHS_UGCTRL2);
970b56e9a7SVivek Gautam ugctrl2 &= ~channel->select_mask;
980b56e9a7SVivek Gautam ugctrl2 |= phy->select_value;
990b56e9a7SVivek Gautam writel(ugctrl2, drv->base + USBHS_UGCTRL2);
1000b56e9a7SVivek Gautam spin_unlock_irqrestore(&drv->lock, flags);
1010b56e9a7SVivek Gautam return 0;
1020b56e9a7SVivek Gautam }
1030b56e9a7SVivek Gautam
rcar_gen2_phy_exit(struct phy * p)1040b56e9a7SVivek Gautam static int rcar_gen2_phy_exit(struct phy *p)
1050b56e9a7SVivek Gautam {
1060b56e9a7SVivek Gautam struct rcar_gen2_phy *phy = phy_get_drvdata(p);
1070b56e9a7SVivek Gautam struct rcar_gen2_channel *channel = phy->channel;
1080b56e9a7SVivek Gautam
1090b56e9a7SVivek Gautam clk_disable_unprepare(channel->drv->clk);
1100b56e9a7SVivek Gautam
1110b56e9a7SVivek Gautam channel->selected_phy = -1;
1120b56e9a7SVivek Gautam
1130b56e9a7SVivek Gautam return 0;
1140b56e9a7SVivek Gautam }
1150b56e9a7SVivek Gautam
rcar_gen2_phy_power_on(struct phy * p)1160b56e9a7SVivek Gautam static int rcar_gen2_phy_power_on(struct phy *p)
1170b56e9a7SVivek Gautam {
1180b56e9a7SVivek Gautam struct rcar_gen2_phy *phy = phy_get_drvdata(p);
1190b56e9a7SVivek Gautam struct rcar_gen2_phy_driver *drv = phy->channel->drv;
1200b56e9a7SVivek Gautam void __iomem *base = drv->base;
1210b56e9a7SVivek Gautam unsigned long flags;
1220b56e9a7SVivek Gautam u32 value;
1230b56e9a7SVivek Gautam int err = 0, i;
1240b56e9a7SVivek Gautam
1250b56e9a7SVivek Gautam /* Skip if it's not USBHS */
1260b56e9a7SVivek Gautam if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB)
1270b56e9a7SVivek Gautam return 0;
1280b56e9a7SVivek Gautam
1290b56e9a7SVivek Gautam spin_lock_irqsave(&drv->lock, flags);
1300b56e9a7SVivek Gautam
1310b56e9a7SVivek Gautam /* Power on USBHS PHY */
1320b56e9a7SVivek Gautam value = readl(base + USBHS_UGCTRL);
1330b56e9a7SVivek Gautam value &= ~USBHS_UGCTRL_PLLRESET;
1340b56e9a7SVivek Gautam writel(value, base + USBHS_UGCTRL);
1350b56e9a7SVivek Gautam
1360b56e9a7SVivek Gautam value = readw(base + USBHS_LPSTS);
1370b56e9a7SVivek Gautam value |= USBHS_LPSTS_SUSPM;
1380b56e9a7SVivek Gautam writew(value, base + USBHS_LPSTS);
1390b56e9a7SVivek Gautam
1400b56e9a7SVivek Gautam for (i = 0; i < 20; i++) {
1410b56e9a7SVivek Gautam value = readl(base + USBHS_UGSTS);
1420b56e9a7SVivek Gautam if ((value & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) {
1430b56e9a7SVivek Gautam value = readl(base + USBHS_UGCTRL);
1440b56e9a7SVivek Gautam value |= USBHS_UGCTRL_CONNECT;
1450b56e9a7SVivek Gautam writel(value, base + USBHS_UGCTRL);
1460b56e9a7SVivek Gautam goto out;
1470b56e9a7SVivek Gautam }
1480b56e9a7SVivek Gautam udelay(1);
1490b56e9a7SVivek Gautam }
1500b56e9a7SVivek Gautam
1510b56e9a7SVivek Gautam /* Timed out waiting for the PLL lock */
1520b56e9a7SVivek Gautam err = -ETIMEDOUT;
1530b56e9a7SVivek Gautam
1540b56e9a7SVivek Gautam out:
1550b56e9a7SVivek Gautam spin_unlock_irqrestore(&drv->lock, flags);
1560b56e9a7SVivek Gautam
1570b56e9a7SVivek Gautam return err;
1580b56e9a7SVivek Gautam }
1590b56e9a7SVivek Gautam
rcar_gen2_phy_power_off(struct phy * p)1600b56e9a7SVivek Gautam static int rcar_gen2_phy_power_off(struct phy *p)
1610b56e9a7SVivek Gautam {
1620b56e9a7SVivek Gautam struct rcar_gen2_phy *phy = phy_get_drvdata(p);
1630b56e9a7SVivek Gautam struct rcar_gen2_phy_driver *drv = phy->channel->drv;
1640b56e9a7SVivek Gautam void __iomem *base = drv->base;
1650b56e9a7SVivek Gautam unsigned long flags;
1660b56e9a7SVivek Gautam u32 value;
1670b56e9a7SVivek Gautam
1680b56e9a7SVivek Gautam /* Skip if it's not USBHS */
1690b56e9a7SVivek Gautam if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB)
1700b56e9a7SVivek Gautam return 0;
1710b56e9a7SVivek Gautam
1720b56e9a7SVivek Gautam spin_lock_irqsave(&drv->lock, flags);
1730b56e9a7SVivek Gautam
1740b56e9a7SVivek Gautam /* Power off USBHS PHY */
1750b56e9a7SVivek Gautam value = readl(base + USBHS_UGCTRL);
1760b56e9a7SVivek Gautam value &= ~USBHS_UGCTRL_CONNECT;
1770b56e9a7SVivek Gautam writel(value, base + USBHS_UGCTRL);
1780b56e9a7SVivek Gautam
1790b56e9a7SVivek Gautam value = readw(base + USBHS_LPSTS);
1800b56e9a7SVivek Gautam value &= ~USBHS_LPSTS_SUSPM;
1810b56e9a7SVivek Gautam writew(value, base + USBHS_LPSTS);
1820b56e9a7SVivek Gautam
1830b56e9a7SVivek Gautam value = readl(base + USBHS_UGCTRL);
1840b56e9a7SVivek Gautam value |= USBHS_UGCTRL_PLLRESET;
1850b56e9a7SVivek Gautam writel(value, base + USBHS_UGCTRL);
1860b56e9a7SVivek Gautam
1870b56e9a7SVivek Gautam spin_unlock_irqrestore(&drv->lock, flags);
1880b56e9a7SVivek Gautam
1890b56e9a7SVivek Gautam return 0;
1900b56e9a7SVivek Gautam }
1910b56e9a7SVivek Gautam
rz_g1c_phy_power_on(struct phy * p)192b7187e00SBiju Das static int rz_g1c_phy_power_on(struct phy *p)
193b7187e00SBiju Das {
194b7187e00SBiju Das struct rcar_gen2_phy *phy = phy_get_drvdata(p);
195b7187e00SBiju Das struct rcar_gen2_phy_driver *drv = phy->channel->drv;
196b7187e00SBiju Das void __iomem *base = drv->base;
197b7187e00SBiju Das unsigned long flags;
198b7187e00SBiju Das u32 value;
199b7187e00SBiju Das
200b7187e00SBiju Das spin_lock_irqsave(&drv->lock, flags);
201b7187e00SBiju Das
202b7187e00SBiju Das /* Power on USBHS PHY */
203b7187e00SBiju Das value = readl(base + USBHS_UGCTRL);
204b7187e00SBiju Das value &= ~USBHS_UGCTRL_PLLRESET;
205b7187e00SBiju Das writel(value, base + USBHS_UGCTRL);
206b7187e00SBiju Das
207b7187e00SBiju Das /* As per the data sheet wait 340 micro sec for power stable */
208b7187e00SBiju Das udelay(340);
209b7187e00SBiju Das
210b7187e00SBiju Das if (phy->select_value == USBHS_UGCTRL2_USB0SEL_HS_USB20) {
211b7187e00SBiju Das value = readw(base + USBHS_LPSTS);
212b7187e00SBiju Das value |= USBHS_LPSTS_SUSPM;
213b7187e00SBiju Das writew(value, base + USBHS_LPSTS);
214b7187e00SBiju Das }
215b7187e00SBiju Das
216b7187e00SBiju Das spin_unlock_irqrestore(&drv->lock, flags);
217b7187e00SBiju Das
218b7187e00SBiju Das return 0;
219b7187e00SBiju Das }
220b7187e00SBiju Das
rz_g1c_phy_power_off(struct phy * p)221b7187e00SBiju Das static int rz_g1c_phy_power_off(struct phy *p)
222b7187e00SBiju Das {
223b7187e00SBiju Das struct rcar_gen2_phy *phy = phy_get_drvdata(p);
224b7187e00SBiju Das struct rcar_gen2_phy_driver *drv = phy->channel->drv;
225b7187e00SBiju Das void __iomem *base = drv->base;
226b7187e00SBiju Das unsigned long flags;
227b7187e00SBiju Das u32 value;
228b7187e00SBiju Das
229b7187e00SBiju Das spin_lock_irqsave(&drv->lock, flags);
230b7187e00SBiju Das /* Power off USBHS PHY */
231b7187e00SBiju Das if (phy->select_value == USBHS_UGCTRL2_USB0SEL_HS_USB20) {
232b7187e00SBiju Das value = readw(base + USBHS_LPSTS);
233b7187e00SBiju Das value &= ~USBHS_LPSTS_SUSPM;
234b7187e00SBiju Das writew(value, base + USBHS_LPSTS);
235b7187e00SBiju Das }
236b7187e00SBiju Das
237b7187e00SBiju Das value = readl(base + USBHS_UGCTRL);
238b7187e00SBiju Das value |= USBHS_UGCTRL_PLLRESET;
239b7187e00SBiju Das writel(value, base + USBHS_UGCTRL);
240b7187e00SBiju Das
241b7187e00SBiju Das spin_unlock_irqrestore(&drv->lock, flags);
242b7187e00SBiju Das
243b7187e00SBiju Das return 0;
244b7187e00SBiju Das }
245b7187e00SBiju Das
2460b56e9a7SVivek Gautam static const struct phy_ops rcar_gen2_phy_ops = {
2470b56e9a7SVivek Gautam .init = rcar_gen2_phy_init,
2480b56e9a7SVivek Gautam .exit = rcar_gen2_phy_exit,
2490b56e9a7SVivek Gautam .power_on = rcar_gen2_phy_power_on,
2500b56e9a7SVivek Gautam .power_off = rcar_gen2_phy_power_off,
2510b56e9a7SVivek Gautam .owner = THIS_MODULE,
2520b56e9a7SVivek Gautam };
2530b56e9a7SVivek Gautam
254b7187e00SBiju Das static const struct phy_ops rz_g1c_phy_ops = {
255b7187e00SBiju Das .init = rcar_gen2_phy_init,
256b7187e00SBiju Das .exit = rcar_gen2_phy_exit,
257b7187e00SBiju Das .power_on = rz_g1c_phy_power_on,
258b7187e00SBiju Das .power_off = rz_g1c_phy_power_off,
259b7187e00SBiju Das .owner = THIS_MODULE,
260b7187e00SBiju Das };
261b7187e00SBiju Das
262b7187e00SBiju Das static const u32 pci_select_value[][PHYS_PER_CHANNEL] = {
263b7187e00SBiju Das [0] = { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB },
264b7187e00SBiju Das [2] = { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 },
265b7187e00SBiju Das };
266b7187e00SBiju Das
267b7187e00SBiju Das static const u32 usb20_select_value[][PHYS_PER_CHANNEL] = {
268b7187e00SBiju Das { USBHS_UGCTRL2_USB0SEL_USB20, USBHS_UGCTRL2_USB0SEL_HS_USB20 },
269b7187e00SBiju Das };
270b7187e00SBiju Das
271b7187e00SBiju Das static const struct rcar_gen2_phy_data rcar_gen2_usb_phy_data = {
272b7187e00SBiju Das .gen2_phy_ops = &rcar_gen2_phy_ops,
273b7187e00SBiju Das .select_value = pci_select_value,
274c9baab38SBiju Das .num_channels = ARRAY_SIZE(pci_select_value),
275b7187e00SBiju Das };
276b7187e00SBiju Das
277b7187e00SBiju Das static const struct rcar_gen2_phy_data rz_g1c_usb_phy_data = {
278b7187e00SBiju Das .gen2_phy_ops = &rz_g1c_phy_ops,
279b7187e00SBiju Das .select_value = usb20_select_value,
280c9baab38SBiju Das .num_channels = ARRAY_SIZE(usb20_select_value),
281b7187e00SBiju Das };
282b7187e00SBiju Das
2830b56e9a7SVivek Gautam static const struct of_device_id rcar_gen2_phy_match_table[] = {
284b7187e00SBiju Das {
285b7187e00SBiju Das .compatible = "renesas,usb-phy-r8a77470",
286b7187e00SBiju Das .data = &rz_g1c_usb_phy_data,
287b7187e00SBiju Das },
288b7187e00SBiju Das {
289b7187e00SBiju Das .compatible = "renesas,usb-phy-r8a7790",
290b7187e00SBiju Das .data = &rcar_gen2_usb_phy_data,
291b7187e00SBiju Das },
292b7187e00SBiju Das {
293b7187e00SBiju Das .compatible = "renesas,usb-phy-r8a7791",
294b7187e00SBiju Das .data = &rcar_gen2_usb_phy_data,
295b7187e00SBiju Das },
296b7187e00SBiju Das {
297b7187e00SBiju Das .compatible = "renesas,usb-phy-r8a7794",
298b7187e00SBiju Das .data = &rcar_gen2_usb_phy_data,
299b7187e00SBiju Das },
300b7187e00SBiju Das {
301b7187e00SBiju Das .compatible = "renesas,rcar-gen2-usb-phy",
302b7187e00SBiju Das .data = &rcar_gen2_usb_phy_data,
303b7187e00SBiju Das },
304b7187e00SBiju Das { /* sentinel */ },
3050b56e9a7SVivek Gautam };
3060b56e9a7SVivek Gautam MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table);
3070b56e9a7SVivek Gautam
rcar_gen2_phy_xlate(struct device * dev,struct of_phandle_args * args)3080b56e9a7SVivek Gautam static struct phy *rcar_gen2_phy_xlate(struct device *dev,
3090b56e9a7SVivek Gautam struct of_phandle_args *args)
3100b56e9a7SVivek Gautam {
3110b56e9a7SVivek Gautam struct rcar_gen2_phy_driver *drv;
3120b56e9a7SVivek Gautam struct device_node *np = args->np;
3130b56e9a7SVivek Gautam int i;
3140b56e9a7SVivek Gautam
3150b56e9a7SVivek Gautam drv = dev_get_drvdata(dev);
3160b56e9a7SVivek Gautam if (!drv)
3170b56e9a7SVivek Gautam return ERR_PTR(-EINVAL);
3180b56e9a7SVivek Gautam
3190b56e9a7SVivek Gautam for (i = 0; i < drv->num_channels; i++) {
3200b56e9a7SVivek Gautam if (np == drv->channels[i].of_node)
3210b56e9a7SVivek Gautam break;
3220b56e9a7SVivek Gautam }
3230b56e9a7SVivek Gautam
3240b56e9a7SVivek Gautam if (i >= drv->num_channels || args->args[0] >= 2)
3250b56e9a7SVivek Gautam return ERR_PTR(-ENODEV);
3260b56e9a7SVivek Gautam
3270b56e9a7SVivek Gautam return drv->channels[i].phys[args->args[0]].phy;
3280b56e9a7SVivek Gautam }
3290b56e9a7SVivek Gautam
3300b56e9a7SVivek Gautam static const u32 select_mask[] = {
3310b56e9a7SVivek Gautam [0] = USBHS_UGCTRL2_USB0SEL,
3320b56e9a7SVivek Gautam [2] = USBHS_UGCTRL2_USB2SEL,
3330b56e9a7SVivek Gautam };
3340b56e9a7SVivek Gautam
rcar_gen2_phy_probe(struct platform_device * pdev)3350b56e9a7SVivek Gautam static int rcar_gen2_phy_probe(struct platform_device *pdev)
3360b56e9a7SVivek Gautam {
3370b56e9a7SVivek Gautam struct device *dev = &pdev->dev;
3380b56e9a7SVivek Gautam struct rcar_gen2_phy_driver *drv;
3390b56e9a7SVivek Gautam struct phy_provider *provider;
3400b56e9a7SVivek Gautam struct device_node *np;
3410b56e9a7SVivek Gautam void __iomem *base;
3420b56e9a7SVivek Gautam struct clk *clk;
343b7187e00SBiju Das const struct rcar_gen2_phy_data *data;
3440b56e9a7SVivek Gautam int i = 0;
3450b56e9a7SVivek Gautam
3460b56e9a7SVivek Gautam if (!dev->of_node) {
3470b56e9a7SVivek Gautam dev_err(dev,
3480b56e9a7SVivek Gautam "This driver is required to be instantiated from device tree\n");
3490b56e9a7SVivek Gautam return -EINVAL;
3500b56e9a7SVivek Gautam }
3510b56e9a7SVivek Gautam
3520b56e9a7SVivek Gautam clk = devm_clk_get(dev, "usbhs");
3530b56e9a7SVivek Gautam if (IS_ERR(clk)) {
3540b56e9a7SVivek Gautam dev_err(dev, "Can't get USBHS clock\n");
3550b56e9a7SVivek Gautam return PTR_ERR(clk);
3560b56e9a7SVivek Gautam }
3570b56e9a7SVivek Gautam
358*0b5604afSChunfeng Yun base = devm_platform_ioremap_resource(pdev, 0);
3590b56e9a7SVivek Gautam if (IS_ERR(base))
3600b56e9a7SVivek Gautam return PTR_ERR(base);
3610b56e9a7SVivek Gautam
3620b56e9a7SVivek Gautam drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
3630b56e9a7SVivek Gautam if (!drv)
3640b56e9a7SVivek Gautam return -ENOMEM;
3650b56e9a7SVivek Gautam
3660b56e9a7SVivek Gautam spin_lock_init(&drv->lock);
3670b56e9a7SVivek Gautam
3680b56e9a7SVivek Gautam drv->clk = clk;
3690b56e9a7SVivek Gautam drv->base = base;
3700b56e9a7SVivek Gautam
371b7187e00SBiju Das data = of_device_get_match_data(dev);
372b7187e00SBiju Das if (!data)
373b7187e00SBiju Das return -EINVAL;
374b7187e00SBiju Das
3750b56e9a7SVivek Gautam drv->num_channels = of_get_child_count(dev->of_node);
3760b56e9a7SVivek Gautam drv->channels = devm_kcalloc(dev, drv->num_channels,
3770b56e9a7SVivek Gautam sizeof(struct rcar_gen2_channel),
3780b56e9a7SVivek Gautam GFP_KERNEL);
3790b56e9a7SVivek Gautam if (!drv->channels)
3800b56e9a7SVivek Gautam return -ENOMEM;
3810b56e9a7SVivek Gautam
3820b56e9a7SVivek Gautam for_each_child_of_node(dev->of_node, np) {
3830b56e9a7SVivek Gautam struct rcar_gen2_channel *channel = drv->channels + i;
3840b56e9a7SVivek Gautam u32 channel_num;
3850b56e9a7SVivek Gautam int error, n;
3860b56e9a7SVivek Gautam
3870b56e9a7SVivek Gautam channel->of_node = np;
3880b56e9a7SVivek Gautam channel->drv = drv;
3890b56e9a7SVivek Gautam channel->selected_phy = -1;
3900b56e9a7SVivek Gautam
3910b56e9a7SVivek Gautam error = of_property_read_u32(np, "reg", &channel_num);
392c9baab38SBiju Das if (error || channel_num >= data->num_channels) {
3930b56e9a7SVivek Gautam dev_err(dev, "Invalid \"reg\" property\n");
394d4a36e82SYoshihiro Shimoda of_node_put(np);
3950b56e9a7SVivek Gautam return error;
3960b56e9a7SVivek Gautam }
3970b56e9a7SVivek Gautam channel->select_mask = select_mask[channel_num];
3980b56e9a7SVivek Gautam
3990b56e9a7SVivek Gautam for (n = 0; n < PHYS_PER_CHANNEL; n++) {
4000b56e9a7SVivek Gautam struct rcar_gen2_phy *phy = &channel->phys[n];
4010b56e9a7SVivek Gautam
4020b56e9a7SVivek Gautam phy->channel = channel;
4030b56e9a7SVivek Gautam phy->number = n;
404b7187e00SBiju Das phy->select_value = data->select_value[channel_num][n];
4050b56e9a7SVivek Gautam
4060b56e9a7SVivek Gautam phy->phy = devm_phy_create(dev, NULL,
407b7187e00SBiju Das data->gen2_phy_ops);
4080b56e9a7SVivek Gautam if (IS_ERR(phy->phy)) {
4090b56e9a7SVivek Gautam dev_err(dev, "Failed to create PHY\n");
410d4a36e82SYoshihiro Shimoda of_node_put(np);
4110b56e9a7SVivek Gautam return PTR_ERR(phy->phy);
4120b56e9a7SVivek Gautam }
4130b56e9a7SVivek Gautam phy_set_drvdata(phy->phy, phy);
4140b56e9a7SVivek Gautam }
4150b56e9a7SVivek Gautam
4160b56e9a7SVivek Gautam i++;
4170b56e9a7SVivek Gautam }
4180b56e9a7SVivek Gautam
4190b56e9a7SVivek Gautam provider = devm_of_phy_provider_register(dev, rcar_gen2_phy_xlate);
4200b56e9a7SVivek Gautam if (IS_ERR(provider)) {
4210b56e9a7SVivek Gautam dev_err(dev, "Failed to register PHY provider\n");
4220b56e9a7SVivek Gautam return PTR_ERR(provider);
4230b56e9a7SVivek Gautam }
4240b56e9a7SVivek Gautam
4250b56e9a7SVivek Gautam dev_set_drvdata(dev, drv);
4260b56e9a7SVivek Gautam
4270b56e9a7SVivek Gautam return 0;
4280b56e9a7SVivek Gautam }
4290b56e9a7SVivek Gautam
4300b56e9a7SVivek Gautam static struct platform_driver rcar_gen2_phy_driver = {
4310b56e9a7SVivek Gautam .driver = {
4320b56e9a7SVivek Gautam .name = "phy_rcar_gen2",
4330b56e9a7SVivek Gautam .of_match_table = rcar_gen2_phy_match_table,
4340b56e9a7SVivek Gautam },
4350b56e9a7SVivek Gautam .probe = rcar_gen2_phy_probe,
4360b56e9a7SVivek Gautam };
4370b56e9a7SVivek Gautam
4380b56e9a7SVivek Gautam module_platform_driver(rcar_gen2_phy_driver);
4390b56e9a7SVivek Gautam
4400b56e9a7SVivek Gautam MODULE_LICENSE("GPL v2");
4410b56e9a7SVivek Gautam MODULE_DESCRIPTION("Renesas R-Car Gen2 PHY");
4420b56e9a7SVivek Gautam MODULE_AUTHOR("Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>");
443