xref: /openbmc/linux/drivers/net/mdio/mdio-xgene.c (revision 44a696de)
1a9770eacSAndrew Lunn // SPDX-License-Identifier: GPL-2.0+
2a9770eacSAndrew Lunn /* Applied Micro X-Gene SoC MDIO Driver
3a9770eacSAndrew Lunn  *
4a9770eacSAndrew Lunn  * Copyright (c) 2016, Applied Micro Circuits Corporation
5a9770eacSAndrew Lunn  * Author: Iyappan Subramanian <isubramanian@apm.com>
6a9770eacSAndrew Lunn  */
7a9770eacSAndrew Lunn 
8a9770eacSAndrew Lunn #include <linux/acpi.h>
9a9770eacSAndrew Lunn #include <linux/clk.h>
10a9770eacSAndrew Lunn #include <linux/device.h>
11a9770eacSAndrew Lunn #include <linux/efi.h>
12a9770eacSAndrew Lunn #include <linux/if_vlan.h>
13a9770eacSAndrew Lunn #include <linux/io.h>
14a9770eacSAndrew Lunn #include <linux/mdio/mdio-xgene.h>
15a9770eacSAndrew Lunn #include <linux/module.h>
16a9770eacSAndrew Lunn #include <linux/of_mdio.h>
171bf34366SCalvin Johnson #include <linux/of_net.h>
181bf34366SCalvin Johnson #include <linux/of_platform.h>
19a9770eacSAndrew Lunn #include <linux/phy.h>
201bf34366SCalvin Johnson #include <linux/prefetch.h>
21a9770eacSAndrew Lunn #include <net/ip.h>
22a9770eacSAndrew Lunn 
xgene_mdio_rd_mac(struct xgene_mdio_pdata * pdata,u32 rd_addr)23a9770eacSAndrew Lunn u32 xgene_mdio_rd_mac(struct xgene_mdio_pdata *pdata, u32 rd_addr)
24a9770eacSAndrew Lunn {
25a9770eacSAndrew Lunn 	void __iomem *addr, *rd, *cmd, *cmd_done;
26a9770eacSAndrew Lunn 	u32 done, rd_data = BUSY_MASK;
27a9770eacSAndrew Lunn 	u8 wait = 10;
28a9770eacSAndrew Lunn 
29a9770eacSAndrew Lunn 	addr = pdata->mac_csr_addr + MAC_ADDR_REG_OFFSET;
30a9770eacSAndrew Lunn 	rd = pdata->mac_csr_addr + MAC_READ_REG_OFFSET;
31a9770eacSAndrew Lunn 	cmd = pdata->mac_csr_addr + MAC_COMMAND_REG_OFFSET;
32a9770eacSAndrew Lunn 	cmd_done = pdata->mac_csr_addr + MAC_COMMAND_DONE_REG_OFFSET;
33a9770eacSAndrew Lunn 
34a9770eacSAndrew Lunn 	spin_lock(&pdata->mac_lock);
35a9770eacSAndrew Lunn 	iowrite32(rd_addr, addr);
36a9770eacSAndrew Lunn 	iowrite32(XGENE_ENET_RD_CMD, cmd);
37a9770eacSAndrew Lunn 
38a9770eacSAndrew Lunn 	while (!(done = ioread32(cmd_done)) && wait--)
39a9770eacSAndrew Lunn 		udelay(1);
40a9770eacSAndrew Lunn 
41a9770eacSAndrew Lunn 	if (done)
42a9770eacSAndrew Lunn 		rd_data = ioread32(rd);
43a9770eacSAndrew Lunn 
44a9770eacSAndrew Lunn 	iowrite32(0, cmd);
45a9770eacSAndrew Lunn 	spin_unlock(&pdata->mac_lock);
46a9770eacSAndrew Lunn 
47a9770eacSAndrew Lunn 	return rd_data;
48a9770eacSAndrew Lunn }
49a9770eacSAndrew Lunn EXPORT_SYMBOL(xgene_mdio_rd_mac);
50a9770eacSAndrew Lunn 
xgene_mdio_wr_mac(struct xgene_mdio_pdata * pdata,u32 wr_addr,u32 data)51a9770eacSAndrew Lunn void xgene_mdio_wr_mac(struct xgene_mdio_pdata *pdata, u32 wr_addr, u32 data)
52a9770eacSAndrew Lunn {
53a9770eacSAndrew Lunn 	void __iomem *addr, *wr, *cmd, *cmd_done;
54a9770eacSAndrew Lunn 	u8 wait = 10;
55a9770eacSAndrew Lunn 	u32 done;
56a9770eacSAndrew Lunn 
57a9770eacSAndrew Lunn 	addr = pdata->mac_csr_addr + MAC_ADDR_REG_OFFSET;
58a9770eacSAndrew Lunn 	wr = pdata->mac_csr_addr + MAC_WRITE_REG_OFFSET;
59a9770eacSAndrew Lunn 	cmd = pdata->mac_csr_addr + MAC_COMMAND_REG_OFFSET;
60a9770eacSAndrew Lunn 	cmd_done = pdata->mac_csr_addr + MAC_COMMAND_DONE_REG_OFFSET;
61a9770eacSAndrew Lunn 
62a9770eacSAndrew Lunn 	spin_lock(&pdata->mac_lock);
63a9770eacSAndrew Lunn 	iowrite32(wr_addr, addr);
64a9770eacSAndrew Lunn 	iowrite32(data, wr);
65a9770eacSAndrew Lunn 	iowrite32(XGENE_ENET_WR_CMD, cmd);
66a9770eacSAndrew Lunn 
67a9770eacSAndrew Lunn 	while (!(done = ioread32(cmd_done)) && wait--)
68a9770eacSAndrew Lunn 		udelay(1);
69a9770eacSAndrew Lunn 
70a9770eacSAndrew Lunn 	if (!done)
71a9770eacSAndrew Lunn 		pr_err("MCX mac write failed, addr: 0x%04x\n", wr_addr);
72a9770eacSAndrew Lunn 
73a9770eacSAndrew Lunn 	iowrite32(0, cmd);
74a9770eacSAndrew Lunn 	spin_unlock(&pdata->mac_lock);
75a9770eacSAndrew Lunn }
76a9770eacSAndrew Lunn EXPORT_SYMBOL(xgene_mdio_wr_mac);
77a9770eacSAndrew Lunn 
xgene_mdio_rgmii_read(struct mii_bus * bus,int phy_id,int reg)78a9770eacSAndrew Lunn int xgene_mdio_rgmii_read(struct mii_bus *bus, int phy_id, int reg)
79a9770eacSAndrew Lunn {
8004115debSWu Yunchuan 	struct xgene_mdio_pdata *pdata = bus->priv;
81a9770eacSAndrew Lunn 	u32 data, done;
82a9770eacSAndrew Lunn 	u8 wait = 10;
83a9770eacSAndrew Lunn 
84a9770eacSAndrew Lunn 	data = SET_VAL(PHY_ADDR, phy_id) | SET_VAL(REG_ADDR, reg);
85a9770eacSAndrew Lunn 	xgene_mdio_wr_mac(pdata, MII_MGMT_ADDRESS_ADDR, data);
86a9770eacSAndrew Lunn 	xgene_mdio_wr_mac(pdata, MII_MGMT_COMMAND_ADDR, READ_CYCLE_MASK);
87a9770eacSAndrew Lunn 	do {
88a9770eacSAndrew Lunn 		usleep_range(5, 10);
89a9770eacSAndrew Lunn 		done = xgene_mdio_rd_mac(pdata, MII_MGMT_INDICATORS_ADDR);
90a9770eacSAndrew Lunn 	} while ((done & BUSY_MASK) && wait--);
91a9770eacSAndrew Lunn 
92a9770eacSAndrew Lunn 	if (done & BUSY_MASK) {
93a9770eacSAndrew Lunn 		dev_err(&bus->dev, "MII_MGMT read failed\n");
94a9770eacSAndrew Lunn 		return -EBUSY;
95a9770eacSAndrew Lunn 	}
96a9770eacSAndrew Lunn 
97a9770eacSAndrew Lunn 	data = xgene_mdio_rd_mac(pdata, MII_MGMT_STATUS_ADDR);
98a9770eacSAndrew Lunn 	xgene_mdio_wr_mac(pdata, MII_MGMT_COMMAND_ADDR, 0);
99a9770eacSAndrew Lunn 
100a9770eacSAndrew Lunn 	return data;
101a9770eacSAndrew Lunn }
102a9770eacSAndrew Lunn EXPORT_SYMBOL(xgene_mdio_rgmii_read);
103a9770eacSAndrew Lunn 
xgene_mdio_rgmii_write(struct mii_bus * bus,int phy_id,int reg,u16 data)104a9770eacSAndrew Lunn int xgene_mdio_rgmii_write(struct mii_bus *bus, int phy_id, int reg, u16 data)
105a9770eacSAndrew Lunn {
10604115debSWu Yunchuan 	struct xgene_mdio_pdata *pdata = bus->priv;
107a9770eacSAndrew Lunn 	u32 val, done;
108a9770eacSAndrew Lunn 	u8 wait = 10;
109a9770eacSAndrew Lunn 
110a9770eacSAndrew Lunn 	val = SET_VAL(PHY_ADDR, phy_id) | SET_VAL(REG_ADDR, reg);
111a9770eacSAndrew Lunn 	xgene_mdio_wr_mac(pdata, MII_MGMT_ADDRESS_ADDR, val);
112a9770eacSAndrew Lunn 
113a9770eacSAndrew Lunn 	xgene_mdio_wr_mac(pdata, MII_MGMT_CONTROL_ADDR, data);
114a9770eacSAndrew Lunn 	do {
115a9770eacSAndrew Lunn 		usleep_range(5, 10);
116a9770eacSAndrew Lunn 		done = xgene_mdio_rd_mac(pdata, MII_MGMT_INDICATORS_ADDR);
117a9770eacSAndrew Lunn 	} while ((done & BUSY_MASK) && wait--);
118a9770eacSAndrew Lunn 
119a9770eacSAndrew Lunn 	if (done & BUSY_MASK) {
120a9770eacSAndrew Lunn 		dev_err(&bus->dev, "MII_MGMT write failed\n");
121a9770eacSAndrew Lunn 		return -EBUSY;
122a9770eacSAndrew Lunn 	}
123a9770eacSAndrew Lunn 
124a9770eacSAndrew Lunn 	return 0;
125a9770eacSAndrew Lunn }
126a9770eacSAndrew Lunn EXPORT_SYMBOL(xgene_mdio_rgmii_write);
127a9770eacSAndrew Lunn 
xgene_menet_rd_diag_csr(struct xgene_mdio_pdata * pdata,u32 offset)128a9770eacSAndrew Lunn static u32 xgene_menet_rd_diag_csr(struct xgene_mdio_pdata *pdata, u32 offset)
129a9770eacSAndrew Lunn {
130a9770eacSAndrew Lunn 	return ioread32(pdata->diag_csr_addr + offset);
131a9770eacSAndrew Lunn }
132a9770eacSAndrew Lunn 
xgene_menet_wr_diag_csr(struct xgene_mdio_pdata * pdata,u32 offset,u32 val)133a9770eacSAndrew Lunn static void xgene_menet_wr_diag_csr(struct xgene_mdio_pdata *pdata,
134a9770eacSAndrew Lunn 				    u32 offset, u32 val)
135a9770eacSAndrew Lunn {
136a9770eacSAndrew Lunn 	iowrite32(val, pdata->diag_csr_addr + offset);
137a9770eacSAndrew Lunn }
138a9770eacSAndrew Lunn 
xgene_enet_ecc_init(struct xgene_mdio_pdata * pdata)139a9770eacSAndrew Lunn static int xgene_enet_ecc_init(struct xgene_mdio_pdata *pdata)
140a9770eacSAndrew Lunn {
141a9770eacSAndrew Lunn 	u32 data;
142a9770eacSAndrew Lunn 	u8 wait = 10;
143a9770eacSAndrew Lunn 
144a9770eacSAndrew Lunn 	xgene_menet_wr_diag_csr(pdata, MENET_CFG_MEM_RAM_SHUTDOWN_ADDR, 0x0);
145a9770eacSAndrew Lunn 	do {
146a9770eacSAndrew Lunn 		usleep_range(100, 110);
147a9770eacSAndrew Lunn 		data = xgene_menet_rd_diag_csr(pdata, MENET_BLOCK_MEM_RDY_ADDR);
148a9770eacSAndrew Lunn 	} while ((data != 0xffffffff) && wait--);
149a9770eacSAndrew Lunn 
150a9770eacSAndrew Lunn 	if (data != 0xffffffff) {
151a9770eacSAndrew Lunn 		dev_err(pdata->dev, "Failed to release memory from shutdown\n");
152a9770eacSAndrew Lunn 		return -ENODEV;
153a9770eacSAndrew Lunn 	}
154a9770eacSAndrew Lunn 
155a9770eacSAndrew Lunn 	return 0;
156a9770eacSAndrew Lunn }
157a9770eacSAndrew Lunn 
xgene_gmac_reset(struct xgene_mdio_pdata * pdata)158a9770eacSAndrew Lunn static void xgene_gmac_reset(struct xgene_mdio_pdata *pdata)
159a9770eacSAndrew Lunn {
160a9770eacSAndrew Lunn 	xgene_mdio_wr_mac(pdata, MAC_CONFIG_1_ADDR, SOFT_RESET);
161a9770eacSAndrew Lunn 	xgene_mdio_wr_mac(pdata, MAC_CONFIG_1_ADDR, 0);
162a9770eacSAndrew Lunn }
163a9770eacSAndrew Lunn 
xgene_mdio_reset(struct xgene_mdio_pdata * pdata)164a9770eacSAndrew Lunn static int xgene_mdio_reset(struct xgene_mdio_pdata *pdata)
165a9770eacSAndrew Lunn {
166a9770eacSAndrew Lunn 	int ret;
167a9770eacSAndrew Lunn 
168a9770eacSAndrew Lunn 	if (pdata->dev->of_node) {
169a9770eacSAndrew Lunn 		clk_prepare_enable(pdata->clk);
170a9770eacSAndrew Lunn 		udelay(5);
171a9770eacSAndrew Lunn 		clk_disable_unprepare(pdata->clk);
172a9770eacSAndrew Lunn 		udelay(5);
173a9770eacSAndrew Lunn 		clk_prepare_enable(pdata->clk);
174a9770eacSAndrew Lunn 		udelay(5);
175a9770eacSAndrew Lunn 	} else {
176a9770eacSAndrew Lunn #ifdef CONFIG_ACPI
177a9770eacSAndrew Lunn 		acpi_evaluate_object(ACPI_HANDLE(pdata->dev),
178a9770eacSAndrew Lunn 				     "_RST", NULL, NULL);
179a9770eacSAndrew Lunn #endif
180a9770eacSAndrew Lunn 	}
181a9770eacSAndrew Lunn 
182a9770eacSAndrew Lunn 	ret = xgene_enet_ecc_init(pdata);
183a9770eacSAndrew Lunn 	if (ret) {
184a9770eacSAndrew Lunn 		if (pdata->dev->of_node)
185a9770eacSAndrew Lunn 			clk_disable_unprepare(pdata->clk);
186a9770eacSAndrew Lunn 		return ret;
187a9770eacSAndrew Lunn 	}
188a9770eacSAndrew Lunn 	xgene_gmac_reset(pdata);
189a9770eacSAndrew Lunn 
190a9770eacSAndrew Lunn 	return 0;
191a9770eacSAndrew Lunn }
192a9770eacSAndrew Lunn 
xgene_enet_rd_mdio_csr(void __iomem * base_addr,u32 offset,u32 * val)193a9770eacSAndrew Lunn static void xgene_enet_rd_mdio_csr(void __iomem *base_addr,
194a9770eacSAndrew Lunn 				   u32 offset, u32 *val)
195a9770eacSAndrew Lunn {
196a9770eacSAndrew Lunn 	void __iomem *addr = base_addr  + offset;
197a9770eacSAndrew Lunn 
198a9770eacSAndrew Lunn 	*val = ioread32(addr);
199a9770eacSAndrew Lunn }
200a9770eacSAndrew Lunn 
xgene_enet_wr_mdio_csr(void __iomem * base_addr,u32 offset,u32 val)201a9770eacSAndrew Lunn static void xgene_enet_wr_mdio_csr(void __iomem *base_addr,
202a9770eacSAndrew Lunn 				   u32 offset, u32 val)
203a9770eacSAndrew Lunn {
204a9770eacSAndrew Lunn 	void __iomem *addr = base_addr  + offset;
205a9770eacSAndrew Lunn 
206a9770eacSAndrew Lunn 	iowrite32(val, addr);
207a9770eacSAndrew Lunn }
208a9770eacSAndrew Lunn 
xgene_xfi_mdio_write(struct mii_bus * bus,int phy_id,int reg,u16 data)209a9770eacSAndrew Lunn static int xgene_xfi_mdio_write(struct mii_bus *bus, int phy_id,
210a9770eacSAndrew Lunn 				int reg, u16 data)
211a9770eacSAndrew Lunn {
212a9770eacSAndrew Lunn 	void __iomem *addr = (void __iomem *)bus->priv;
213a9770eacSAndrew Lunn 	int timeout = 100;
214a9770eacSAndrew Lunn 	u32 status, val;
215a9770eacSAndrew Lunn 
216a9770eacSAndrew Lunn 	val = SET_VAL(HSTPHYADX, phy_id) | SET_VAL(HSTREGADX, reg) |
217a9770eacSAndrew Lunn 	      SET_VAL(HSTMIIMWRDAT, data);
218a9770eacSAndrew Lunn 	xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, val);
219a9770eacSAndrew Lunn 
220a9770eacSAndrew Lunn 	val = HSTLDCMD | SET_VAL(HSTMIIMCMD, MIIM_CMD_LEGACY_WRITE);
221a9770eacSAndrew Lunn 	xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, val);
222a9770eacSAndrew Lunn 
223a9770eacSAndrew Lunn 	do {
224a9770eacSAndrew Lunn 		usleep_range(5, 10);
225a9770eacSAndrew Lunn 		xgene_enet_rd_mdio_csr(addr, MIIM_INDICATOR_ADDR, &status);
226a9770eacSAndrew Lunn 	} while ((status & BUSY_MASK) && timeout--);
227a9770eacSAndrew Lunn 
228a9770eacSAndrew Lunn 	xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, 0);
229a9770eacSAndrew Lunn 
230a9770eacSAndrew Lunn 	return 0;
231a9770eacSAndrew Lunn }
232a9770eacSAndrew Lunn 
xgene_xfi_mdio_read(struct mii_bus * bus,int phy_id,int reg)233a9770eacSAndrew Lunn static int xgene_xfi_mdio_read(struct mii_bus *bus, int phy_id, int reg)
234a9770eacSAndrew Lunn {
235a9770eacSAndrew Lunn 	void __iomem *addr = (void __iomem *)bus->priv;
236a9770eacSAndrew Lunn 	u32 data, status, val;
237a9770eacSAndrew Lunn 	int timeout = 100;
238a9770eacSAndrew Lunn 
239a9770eacSAndrew Lunn 	val = SET_VAL(HSTPHYADX, phy_id) | SET_VAL(HSTREGADX, reg);
240a9770eacSAndrew Lunn 	xgene_enet_wr_mdio_csr(addr, MIIM_FIELD_ADDR, val);
241a9770eacSAndrew Lunn 
242a9770eacSAndrew Lunn 	val = HSTLDCMD | SET_VAL(HSTMIIMCMD, MIIM_CMD_LEGACY_READ);
243a9770eacSAndrew Lunn 	xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, val);
244a9770eacSAndrew Lunn 
245a9770eacSAndrew Lunn 	do {
246a9770eacSAndrew Lunn 		usleep_range(5, 10);
247a9770eacSAndrew Lunn 		xgene_enet_rd_mdio_csr(addr, MIIM_INDICATOR_ADDR, &status);
248a9770eacSAndrew Lunn 	} while ((status & BUSY_MASK) && timeout--);
249a9770eacSAndrew Lunn 
250a9770eacSAndrew Lunn 	if (status & BUSY_MASK) {
251a9770eacSAndrew Lunn 		pr_err("XGENET_MII_MGMT write failed\n");
252a9770eacSAndrew Lunn 		return -EBUSY;
253a9770eacSAndrew Lunn 	}
254a9770eacSAndrew Lunn 
255a9770eacSAndrew Lunn 	xgene_enet_rd_mdio_csr(addr, MIIMRD_FIELD_ADDR, &data);
256a9770eacSAndrew Lunn 	xgene_enet_wr_mdio_csr(addr, MIIM_COMMAND_ADDR, 0);
257a9770eacSAndrew Lunn 
258a9770eacSAndrew Lunn 	return data;
259a9770eacSAndrew Lunn }
260a9770eacSAndrew Lunn 
xgene_enet_phy_register(struct mii_bus * bus,int phy_addr)261a9770eacSAndrew Lunn struct phy_device *xgene_enet_phy_register(struct mii_bus *bus, int phy_addr)
262a9770eacSAndrew Lunn {
263a9770eacSAndrew Lunn 	struct phy_device *phy_dev;
264a9770eacSAndrew Lunn 
265a9770eacSAndrew Lunn 	phy_dev = get_phy_device(bus, phy_addr, false);
266a9770eacSAndrew Lunn 	if (!phy_dev || IS_ERR(phy_dev))
267a9770eacSAndrew Lunn 		return NULL;
268a9770eacSAndrew Lunn 
269a9770eacSAndrew Lunn 	if (phy_device_register(phy_dev))
270a9770eacSAndrew Lunn 		phy_device_free(phy_dev);
271a9770eacSAndrew Lunn 
272a9770eacSAndrew Lunn 	return phy_dev;
273a9770eacSAndrew Lunn }
274a9770eacSAndrew Lunn EXPORT_SYMBOL(xgene_enet_phy_register);
275a9770eacSAndrew Lunn 
276a9770eacSAndrew Lunn #ifdef CONFIG_ACPI
acpi_register_phy(acpi_handle handle,u32 lvl,void * context,void ** ret)277a9770eacSAndrew Lunn static acpi_status acpi_register_phy(acpi_handle handle, u32 lvl,
278a9770eacSAndrew Lunn 				     void *context, void **ret)
279a9770eacSAndrew Lunn {
280a9770eacSAndrew Lunn 	struct mii_bus *mdio = context;
281a9770eacSAndrew Lunn 	struct acpi_device *adev;
282a9770eacSAndrew Lunn 	struct phy_device *phy_dev;
283a9770eacSAndrew Lunn 	const union acpi_object *obj;
284a9770eacSAndrew Lunn 	u32 phy_addr;
285a9770eacSAndrew Lunn 
28652dae93fSRafael J. Wysocki 	adev = acpi_fetch_acpi_dev(handle);
28752dae93fSRafael J. Wysocki 	if (!adev)
288a9770eacSAndrew Lunn 		return AE_OK;
289a9770eacSAndrew Lunn 
290a9770eacSAndrew Lunn 	if (acpi_dev_get_property(adev, "phy-channel", ACPI_TYPE_INTEGER, &obj))
291a9770eacSAndrew Lunn 		return AE_OK;
292a9770eacSAndrew Lunn 	phy_addr = obj->integer.value;
293a9770eacSAndrew Lunn 
294a9770eacSAndrew Lunn 	phy_dev = xgene_enet_phy_register(mdio, phy_addr);
295a9770eacSAndrew Lunn 	adev->driver_data = phy_dev;
296a9770eacSAndrew Lunn 
297a9770eacSAndrew Lunn 	return AE_OK;
298a9770eacSAndrew Lunn }
299a9770eacSAndrew Lunn #endif
300a9770eacSAndrew Lunn 
301a9770eacSAndrew Lunn static const struct of_device_id xgene_mdio_of_match[] = {
302a9770eacSAndrew Lunn 	{
303a9770eacSAndrew Lunn 		.compatible = "apm,xgene-mdio-rgmii",
304a9770eacSAndrew Lunn 		.data = (void *)XGENE_MDIO_RGMII
305a9770eacSAndrew Lunn 	},
306a9770eacSAndrew Lunn 	{
307a9770eacSAndrew Lunn 		.compatible = "apm,xgene-mdio-xfi",
308a9770eacSAndrew Lunn 		.data = (void *)XGENE_MDIO_XFI
309a9770eacSAndrew Lunn 	},
310a9770eacSAndrew Lunn 	{},
311a9770eacSAndrew Lunn };
312a9770eacSAndrew Lunn MODULE_DEVICE_TABLE(of, xgene_mdio_of_match);
313a9770eacSAndrew Lunn 
314a9770eacSAndrew Lunn #ifdef CONFIG_ACPI
315a9770eacSAndrew Lunn static const struct acpi_device_id xgene_mdio_acpi_match[] = {
316a9770eacSAndrew Lunn 	{ "APMC0D65", XGENE_MDIO_RGMII },
317a9770eacSAndrew Lunn 	{ "APMC0D66", XGENE_MDIO_XFI },
318a9770eacSAndrew Lunn 	{ }
319a9770eacSAndrew Lunn };
320a9770eacSAndrew Lunn 
321a9770eacSAndrew Lunn MODULE_DEVICE_TABLE(acpi, xgene_mdio_acpi_match);
322a9770eacSAndrew Lunn #endif
323a9770eacSAndrew Lunn 
324a9770eacSAndrew Lunn 
xgene_mdio_probe(struct platform_device * pdev)325a9770eacSAndrew Lunn static int xgene_mdio_probe(struct platform_device *pdev)
326a9770eacSAndrew Lunn {
327a9770eacSAndrew Lunn 	struct device *dev = &pdev->dev;
328a9770eacSAndrew Lunn 	struct mii_bus *mdio_bus;
329a9770eacSAndrew Lunn 	const struct of_device_id *of_id;
330a9770eacSAndrew Lunn 	struct xgene_mdio_pdata *pdata;
331a9770eacSAndrew Lunn 	void __iomem *csr_base;
332a9770eacSAndrew Lunn 	int mdio_id = 0, ret = 0;
333a9770eacSAndrew Lunn 
334a9770eacSAndrew Lunn 	of_id = of_match_device(xgene_mdio_of_match, &pdev->dev);
335a9770eacSAndrew Lunn 	if (of_id) {
336*f3add6deSJustin Stitt 		mdio_id = (uintptr_t)of_id->data;
337a9770eacSAndrew Lunn 	} else {
338a9770eacSAndrew Lunn #ifdef CONFIG_ACPI
339a9770eacSAndrew Lunn 		const struct acpi_device_id *acpi_id;
340a9770eacSAndrew Lunn 
341a9770eacSAndrew Lunn 		acpi_id = acpi_match_device(xgene_mdio_acpi_match, &pdev->dev);
342a9770eacSAndrew Lunn 		if (acpi_id)
343a9770eacSAndrew Lunn 			mdio_id = (enum xgene_mdio_id)acpi_id->driver_data;
344a9770eacSAndrew Lunn #endif
345a9770eacSAndrew Lunn 	}
346a9770eacSAndrew Lunn 
347a9770eacSAndrew Lunn 	if (!mdio_id)
348a9770eacSAndrew Lunn 		return -ENODEV;
349a9770eacSAndrew Lunn 
350a9770eacSAndrew Lunn 	pdata = devm_kzalloc(dev, sizeof(struct xgene_mdio_pdata), GFP_KERNEL);
351a9770eacSAndrew Lunn 	if (!pdata)
352a9770eacSAndrew Lunn 		return -ENOMEM;
353a9770eacSAndrew Lunn 	pdata->mdio_id = mdio_id;
354a9770eacSAndrew Lunn 	pdata->dev = dev;
355a9770eacSAndrew Lunn 
356a9770eacSAndrew Lunn 	csr_base = devm_platform_ioremap_resource(pdev, 0);
357a9770eacSAndrew Lunn 	if (IS_ERR(csr_base))
358a9770eacSAndrew Lunn 		return PTR_ERR(csr_base);
359a9770eacSAndrew Lunn 	pdata->mac_csr_addr = csr_base;
360a9770eacSAndrew Lunn 	pdata->mdio_csr_addr = csr_base + BLOCK_XG_MDIO_CSR_OFFSET;
361a9770eacSAndrew Lunn 	pdata->diag_csr_addr = csr_base + BLOCK_DIAG_CSR_OFFSET;
362a9770eacSAndrew Lunn 
363a9770eacSAndrew Lunn 	if (mdio_id == XGENE_MDIO_RGMII)
364a9770eacSAndrew Lunn 		spin_lock_init(&pdata->mac_lock);
365a9770eacSAndrew Lunn 
366a9770eacSAndrew Lunn 	if (dev->of_node) {
367a9770eacSAndrew Lunn 		pdata->clk = devm_clk_get(dev, NULL);
368a9770eacSAndrew Lunn 		if (IS_ERR(pdata->clk)) {
369a9770eacSAndrew Lunn 			dev_err(dev, "Unable to retrieve clk\n");
370a9770eacSAndrew Lunn 			return PTR_ERR(pdata->clk);
371a9770eacSAndrew Lunn 		}
372a9770eacSAndrew Lunn 	}
373a9770eacSAndrew Lunn 
374a9770eacSAndrew Lunn 	ret = xgene_mdio_reset(pdata);
375a9770eacSAndrew Lunn 	if (ret)
376a9770eacSAndrew Lunn 		return ret;
377a9770eacSAndrew Lunn 
378a9770eacSAndrew Lunn 	mdio_bus = mdiobus_alloc();
379a9770eacSAndrew Lunn 	if (!mdio_bus) {
380a9770eacSAndrew Lunn 		ret = -ENOMEM;
381a9770eacSAndrew Lunn 		goto out_clk;
382a9770eacSAndrew Lunn 	}
383a9770eacSAndrew Lunn 
384a9770eacSAndrew Lunn 	mdio_bus->name = "APM X-Gene MDIO bus";
385a9770eacSAndrew Lunn 
386a9770eacSAndrew Lunn 	if (mdio_id == XGENE_MDIO_RGMII) {
387a9770eacSAndrew Lunn 		mdio_bus->read = xgene_mdio_rgmii_read;
388a9770eacSAndrew Lunn 		mdio_bus->write = xgene_mdio_rgmii_write;
389a9770eacSAndrew Lunn 		mdio_bus->priv = (void __force *)pdata;
390a9770eacSAndrew Lunn 		snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s",
391a9770eacSAndrew Lunn 			 "xgene-mii-rgmii");
392a9770eacSAndrew Lunn 	} else {
393a9770eacSAndrew Lunn 		mdio_bus->read = xgene_xfi_mdio_read;
394a9770eacSAndrew Lunn 		mdio_bus->write = xgene_xfi_mdio_write;
395a9770eacSAndrew Lunn 		mdio_bus->priv = (void __force *)pdata->mdio_csr_addr;
396a9770eacSAndrew Lunn 		snprintf(mdio_bus->id, MII_BUS_ID_SIZE, "%s",
397a9770eacSAndrew Lunn 			 "xgene-mii-xfi");
398a9770eacSAndrew Lunn 	}
399a9770eacSAndrew Lunn 
400a9770eacSAndrew Lunn 	mdio_bus->parent = dev;
401a9770eacSAndrew Lunn 	platform_set_drvdata(pdev, pdata);
402a9770eacSAndrew Lunn 
403a9770eacSAndrew Lunn 	if (dev->of_node) {
404a9770eacSAndrew Lunn 		ret = of_mdiobus_register(mdio_bus, dev->of_node);
405a9770eacSAndrew Lunn 	} else {
406a9770eacSAndrew Lunn #ifdef CONFIG_ACPI
407a9770eacSAndrew Lunn 		/* Mask out all PHYs from auto probing. */
408a9770eacSAndrew Lunn 		mdio_bus->phy_mask = ~0;
409a9770eacSAndrew Lunn 		ret = mdiobus_register(mdio_bus);
410a9770eacSAndrew Lunn 		if (ret)
411a9770eacSAndrew Lunn 			goto out_mdiobus;
412a9770eacSAndrew Lunn 
413a9770eacSAndrew Lunn 		acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_HANDLE(dev), 1,
414a9770eacSAndrew Lunn 				    acpi_register_phy, NULL, mdio_bus, NULL);
415a9770eacSAndrew Lunn #endif
416a9770eacSAndrew Lunn 	}
417a9770eacSAndrew Lunn 
418a9770eacSAndrew Lunn 	if (ret)
419a9770eacSAndrew Lunn 		goto out_mdiobus;
420a9770eacSAndrew Lunn 
421a9770eacSAndrew Lunn 	pdata->mdio_bus = mdio_bus;
422a9770eacSAndrew Lunn 
423a9770eacSAndrew Lunn 	return 0;
424a9770eacSAndrew Lunn 
425a9770eacSAndrew Lunn out_mdiobus:
426a9770eacSAndrew Lunn 	mdiobus_free(mdio_bus);
427a9770eacSAndrew Lunn 
428a9770eacSAndrew Lunn out_clk:
429a9770eacSAndrew Lunn 	if (dev->of_node)
430a9770eacSAndrew Lunn 		clk_disable_unprepare(pdata->clk);
431a9770eacSAndrew Lunn 
432a9770eacSAndrew Lunn 	return ret;
433a9770eacSAndrew Lunn }
434a9770eacSAndrew Lunn 
xgene_mdio_remove(struct platform_device * pdev)435a9770eacSAndrew Lunn static int xgene_mdio_remove(struct platform_device *pdev)
436a9770eacSAndrew Lunn {
437a9770eacSAndrew Lunn 	struct xgene_mdio_pdata *pdata = platform_get_drvdata(pdev);
438a9770eacSAndrew Lunn 	struct mii_bus *mdio_bus = pdata->mdio_bus;
439a9770eacSAndrew Lunn 	struct device *dev = &pdev->dev;
440a9770eacSAndrew Lunn 
441a9770eacSAndrew Lunn 	mdiobus_unregister(mdio_bus);
442a9770eacSAndrew Lunn 	mdiobus_free(mdio_bus);
443a9770eacSAndrew Lunn 
444a9770eacSAndrew Lunn 	if (dev->of_node)
445a9770eacSAndrew Lunn 		clk_disable_unprepare(pdata->clk);
446a9770eacSAndrew Lunn 
447a9770eacSAndrew Lunn 	return 0;
448a9770eacSAndrew Lunn }
449a9770eacSAndrew Lunn 
450a9770eacSAndrew Lunn static struct platform_driver xgene_mdio_driver = {
451a9770eacSAndrew Lunn 	.driver = {
452a9770eacSAndrew Lunn 		.name = "xgene-mdio",
453a9770eacSAndrew Lunn 		.of_match_table = of_match_ptr(xgene_mdio_of_match),
454a9770eacSAndrew Lunn 		.acpi_match_table = ACPI_PTR(xgene_mdio_acpi_match),
455a9770eacSAndrew Lunn 	},
456a9770eacSAndrew Lunn 	.probe = xgene_mdio_probe,
457a9770eacSAndrew Lunn 	.remove = xgene_mdio_remove,
458a9770eacSAndrew Lunn };
459a9770eacSAndrew Lunn 
460a9770eacSAndrew Lunn module_platform_driver(xgene_mdio_driver);
461a9770eacSAndrew Lunn 
462a9770eacSAndrew Lunn MODULE_DESCRIPTION("APM X-Gene SoC MDIO driver");
463a9770eacSAndrew Lunn MODULE_AUTHOR("Iyappan Subramanian <isubramanian@apm.com>");
464a9770eacSAndrew Lunn MODULE_LICENSE("GPL");
465