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