1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2b13ad8f4SJeff Kirsher /*
3b13ad8f4SJeff Kirsher * MDIO bus driver for the Xilinx TEMAC device
4b13ad8f4SJeff Kirsher *
5b13ad8f4SJeff Kirsher * Copyright (c) 2009 Secret Lab Technologies, Ltd.
6b13ad8f4SJeff Kirsher */
7b13ad8f4SJeff Kirsher
8b13ad8f4SJeff Kirsher #include <linux/io.h>
9b13ad8f4SJeff Kirsher #include <linux/netdevice.h>
10b13ad8f4SJeff Kirsher #include <linux/mutex.h>
11b13ad8f4SJeff Kirsher #include <linux/phy.h>
12b13ad8f4SJeff Kirsher #include <linux/of.h>
13b13ad8f4SJeff Kirsher #include <linux/of_address.h>
14*3d40aed8SRob Herring #include <linux/platform_device.h>
15b13ad8f4SJeff Kirsher #include <linux/slab.h>
16b13ad8f4SJeff Kirsher #include <linux/of_mdio.h>
178425c41dSEsben Haabendal #include <linux/platform_data/xilinx-ll-temac.h>
18b13ad8f4SJeff Kirsher
19b13ad8f4SJeff Kirsher #include "ll_temac.h"
20b13ad8f4SJeff Kirsher
21b13ad8f4SJeff Kirsher /* ---------------------------------------------------------------------
22b13ad8f4SJeff Kirsher * MDIO Bus functions
23b13ad8f4SJeff Kirsher */
temac_mdio_read(struct mii_bus * bus,int phy_id,int reg)24b13ad8f4SJeff Kirsher static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg)
25b13ad8f4SJeff Kirsher {
26b13ad8f4SJeff Kirsher struct temac_local *lp = bus->priv;
27b13ad8f4SJeff Kirsher u32 rc;
281bd33bf0SEsben Haabendal unsigned long flags;
29b13ad8f4SJeff Kirsher
30b13ad8f4SJeff Kirsher /* Write the PHY address to the MIIM Access Initiator register.
31b13ad8f4SJeff Kirsher * When the transfer completes, the PHY register value will appear
3275124116Shuangjunxian * in the LSW0 register
3375124116Shuangjunxian */
341bd33bf0SEsben Haabendal spin_lock_irqsave(lp->indirect_lock, flags);
35b13ad8f4SJeff Kirsher temac_iow(lp, XTE_LSW0_OFFSET, (phy_id << 5) | reg);
361bd33bf0SEsben Haabendal rc = temac_indirect_in32_locked(lp, XTE_MIIMAI_OFFSET);
371bd33bf0SEsben Haabendal spin_unlock_irqrestore(lp->indirect_lock, flags);
38b13ad8f4SJeff Kirsher
39b13ad8f4SJeff Kirsher dev_dbg(lp->dev, "temac_mdio_read(phy_id=%i, reg=%x) == %x\n",
40b13ad8f4SJeff Kirsher phy_id, reg, rc);
41b13ad8f4SJeff Kirsher
42b13ad8f4SJeff Kirsher return rc;
43b13ad8f4SJeff Kirsher }
44b13ad8f4SJeff Kirsher
temac_mdio_write(struct mii_bus * bus,int phy_id,int reg,u16 val)45b13ad8f4SJeff Kirsher static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val)
46b13ad8f4SJeff Kirsher {
47b13ad8f4SJeff Kirsher struct temac_local *lp = bus->priv;
481bd33bf0SEsben Haabendal unsigned long flags;
49b13ad8f4SJeff Kirsher
50b13ad8f4SJeff Kirsher dev_dbg(lp->dev, "temac_mdio_write(phy_id=%i, reg=%x, val=%x)\n",
51b13ad8f4SJeff Kirsher phy_id, reg, val);
52b13ad8f4SJeff Kirsher
53b13ad8f4SJeff Kirsher /* First write the desired value into the write data register
54b13ad8f4SJeff Kirsher * and then write the address into the access initiator register
55b13ad8f4SJeff Kirsher */
561bd33bf0SEsben Haabendal spin_lock_irqsave(lp->indirect_lock, flags);
571bd33bf0SEsben Haabendal temac_indirect_out32_locked(lp, XTE_MGTDR_OFFSET, val);
581bd33bf0SEsben Haabendal temac_indirect_out32_locked(lp, XTE_MIIMAI_OFFSET, (phy_id << 5) | reg);
591bd33bf0SEsben Haabendal spin_unlock_irqrestore(lp->indirect_lock, flags);
60b13ad8f4SJeff Kirsher
61b13ad8f4SJeff Kirsher return 0;
62b13ad8f4SJeff Kirsher }
63b13ad8f4SJeff Kirsher
temac_mdio_setup(struct temac_local * lp,struct platform_device * pdev)64a63625d2SEsben Haabendal int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev)
65b13ad8f4SJeff Kirsher {
668425c41dSEsben Haabendal struct ll_temac_platform_data *pdata = dev_get_platdata(&pdev->dev);
67a63625d2SEsben Haabendal struct device_node *np = dev_of_node(&pdev->dev);
68b13ad8f4SJeff Kirsher struct mii_bus *bus;
69e734a42fSTobias Klauser u32 bus_hz;
70b13ad8f4SJeff Kirsher int clk_div;
71e734a42fSTobias Klauser int rc;
72b13ad8f4SJeff Kirsher struct resource res;
73b13ad8f4SJeff Kirsher
748425c41dSEsben Haabendal /* Get MDIO bus frequency (if specified) */
758425c41dSEsben Haabendal bus_hz = 0;
768425c41dSEsben Haabendal if (np)
778425c41dSEsben Haabendal of_property_read_u32(np, "clock-frequency", &bus_hz);
788425c41dSEsben Haabendal else if (pdata)
798425c41dSEsben Haabendal bus_hz = pdata->mdio_clk_freq;
808425c41dSEsben Haabendal
81b13ad8f4SJeff Kirsher /* Calculate a reasonable divisor for the clock rate */
82b13ad8f4SJeff Kirsher clk_div = 0x3f; /* worst-case default setting */
838425c41dSEsben Haabendal if (bus_hz != 0) {
84e734a42fSTobias Klauser clk_div = bus_hz / (2500 * 1000 * 2) - 1;
85b13ad8f4SJeff Kirsher if (clk_div < 1)
86b13ad8f4SJeff Kirsher clk_div = 1;
87b13ad8f4SJeff Kirsher if (clk_div > 0x3f)
88b13ad8f4SJeff Kirsher clk_div = 0x3f;
89b13ad8f4SJeff Kirsher }
90b13ad8f4SJeff Kirsher
91b13ad8f4SJeff Kirsher /* Enable the MDIO bus by asserting the enable bit and writing
9275124116Shuangjunxian * in the clock config
9375124116Shuangjunxian */
94b13ad8f4SJeff Kirsher temac_indirect_out32(lp, XTE_MC_OFFSET, 1 << 6 | clk_div);
95b13ad8f4SJeff Kirsher
96a63625d2SEsben Haabendal bus = devm_mdiobus_alloc(&pdev->dev);
97b13ad8f4SJeff Kirsher if (!bus)
98b13ad8f4SJeff Kirsher return -ENOMEM;
99b13ad8f4SJeff Kirsher
1008425c41dSEsben Haabendal if (np) {
101b13ad8f4SJeff Kirsher of_address_to_resource(np, 0, &res);
102b13ad8f4SJeff Kirsher snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
103b13ad8f4SJeff Kirsher (unsigned long long)res.start);
104b52d031bSDan Carpenter } else if (pdata) {
1058425c41dSEsben Haabendal snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx",
1068425c41dSEsben Haabendal pdata->mdio_bus_id);
1078425c41dSEsben Haabendal }
1088425c41dSEsben Haabendal
109b13ad8f4SJeff Kirsher bus->priv = lp;
110b13ad8f4SJeff Kirsher bus->name = "Xilinx TEMAC MDIO";
111b13ad8f4SJeff Kirsher bus->read = temac_mdio_read;
112b13ad8f4SJeff Kirsher bus->write = temac_mdio_write;
113b13ad8f4SJeff Kirsher bus->parent = lp->dev;
114b13ad8f4SJeff Kirsher
115b13ad8f4SJeff Kirsher lp->mii_bus = bus;
116b13ad8f4SJeff Kirsher
117b13ad8f4SJeff Kirsher rc = of_mdiobus_register(bus, np);
118b13ad8f4SJeff Kirsher if (rc)
119a63625d2SEsben Haabendal return rc;
120b13ad8f4SJeff Kirsher
121b13ad8f4SJeff Kirsher dev_dbg(lp->dev, "MDIO bus registered; MC:%x\n",
122b13ad8f4SJeff Kirsher temac_indirect_in32(lp, XTE_MC_OFFSET));
123b13ad8f4SJeff Kirsher return 0;
124b13ad8f4SJeff Kirsher }
125b13ad8f4SJeff Kirsher
temac_mdio_teardown(struct temac_local * lp)126b13ad8f4SJeff Kirsher void temac_mdio_teardown(struct temac_local *lp)
127b13ad8f4SJeff Kirsher {
128b13ad8f4SJeff Kirsher mdiobus_unregister(lp->mii_bus);
129b13ad8f4SJeff Kirsher }
130