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