1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * MDIO bus driver for the Xilinx TEMAC device |
4 | * |
5 | * Copyright (c) 2009 Secret Lab Technologies, Ltd. |
6 | */ |
7 | |
8 | #include <linux/io.h> |
9 | #include <linux/netdevice.h> |
10 | #include <linux/mutex.h> |
11 | #include <linux/phy.h> |
12 | #include <linux/of.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/platform_device.h> |
15 | #include <linux/slab.h> |
16 | #include <linux/of_mdio.h> |
17 | #include <linux/platform_data/xilinx-ll-temac.h> |
18 | |
19 | #include "ll_temac.h" |
20 | |
21 | /* --------------------------------------------------------------------- |
22 | * MDIO Bus functions |
23 | */ |
24 | static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg) |
25 | { |
26 | struct temac_local *lp = bus->priv; |
27 | u32 rc; |
28 | unsigned long flags; |
29 | |
30 | /* Write the PHY address to the MIIM Access Initiator register. |
31 | * When the transfer completes, the PHY register value will appear |
32 | * in the LSW0 register |
33 | */ |
34 | spin_lock_irqsave(lp->indirect_lock, flags); |
35 | temac_iow(lp, XTE_LSW0_OFFSET, (phy_id << 5) | reg); |
36 | rc = temac_indirect_in32_locked(lp, XTE_MIIMAI_OFFSET); |
37 | spin_unlock_irqrestore(lock: lp->indirect_lock, flags); |
38 | |
39 | dev_dbg(lp->dev, "temac_mdio_read(phy_id=%i, reg=%x) == %x\n" , |
40 | phy_id, reg, rc); |
41 | |
42 | return rc; |
43 | } |
44 | |
45 | static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) |
46 | { |
47 | struct temac_local *lp = bus->priv; |
48 | unsigned long flags; |
49 | |
50 | dev_dbg(lp->dev, "temac_mdio_write(phy_id=%i, reg=%x, val=%x)\n" , |
51 | phy_id, reg, val); |
52 | |
53 | /* First write the desired value into the write data register |
54 | * and then write the address into the access initiator register |
55 | */ |
56 | spin_lock_irqsave(lp->indirect_lock, flags); |
57 | temac_indirect_out32_locked(lp, XTE_MGTDR_OFFSET, value: val); |
58 | temac_indirect_out32_locked(lp, XTE_MIIMAI_OFFSET, value: (phy_id << 5) | reg); |
59 | spin_unlock_irqrestore(lock: lp->indirect_lock, flags); |
60 | |
61 | return 0; |
62 | } |
63 | |
64 | int temac_mdio_setup(struct temac_local *lp, struct platform_device *pdev) |
65 | { |
66 | struct ll_temac_platform_data *pdata = dev_get_platdata(dev: &pdev->dev); |
67 | struct device_node *np = dev_of_node(dev: &pdev->dev); |
68 | struct mii_bus *bus; |
69 | u32 bus_hz; |
70 | int clk_div; |
71 | int rc; |
72 | struct resource res; |
73 | |
74 | /* Get MDIO bus frequency (if specified) */ |
75 | bus_hz = 0; |
76 | if (np) |
77 | of_property_read_u32(np, propname: "clock-frequency" , out_value: &bus_hz); |
78 | else if (pdata) |
79 | bus_hz = pdata->mdio_clk_freq; |
80 | |
81 | /* Calculate a reasonable divisor for the clock rate */ |
82 | clk_div = 0x3f; /* worst-case default setting */ |
83 | if (bus_hz != 0) { |
84 | clk_div = bus_hz / (2500 * 1000 * 2) - 1; |
85 | if (clk_div < 1) |
86 | clk_div = 1; |
87 | if (clk_div > 0x3f) |
88 | clk_div = 0x3f; |
89 | } |
90 | |
91 | /* Enable the MDIO bus by asserting the enable bit and writing |
92 | * in the clock config |
93 | */ |
94 | temac_indirect_out32(lp, XTE_MC_OFFSET, value: 1 << 6 | clk_div); |
95 | |
96 | bus = devm_mdiobus_alloc(dev: &pdev->dev); |
97 | if (!bus) |
98 | return -ENOMEM; |
99 | |
100 | if (np) { |
101 | of_address_to_resource(dev: np, index: 0, r: &res); |
102 | snprintf(buf: bus->id, MII_BUS_ID_SIZE, fmt: "%.8llx" , |
103 | (unsigned long long)res.start); |
104 | } else if (pdata) { |
105 | snprintf(buf: bus->id, MII_BUS_ID_SIZE, fmt: "%.8llx" , |
106 | pdata->mdio_bus_id); |
107 | } |
108 | |
109 | bus->priv = lp; |
110 | bus->name = "Xilinx TEMAC MDIO" ; |
111 | bus->read = temac_mdio_read; |
112 | bus->write = temac_mdio_write; |
113 | bus->parent = lp->dev; |
114 | |
115 | lp->mii_bus = bus; |
116 | |
117 | rc = of_mdiobus_register(mdio: bus, np); |
118 | if (rc) |
119 | return rc; |
120 | |
121 | dev_dbg(lp->dev, "MDIO bus registered; MC:%x\n" , |
122 | temac_indirect_in32(lp, XTE_MC_OFFSET)); |
123 | return 0; |
124 | } |
125 | |
126 | void temac_mdio_teardown(struct temac_local *lp) |
127 | { |
128 | mdiobus_unregister(bus: lp->mii_bus); |
129 | } |
130 | |