]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
92744989 GL |
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_device.h> | |
9f1a1fca | 14 | #include <linux/of_address.h> |
5a0e3ad6 | 15 | #include <linux/slab.h> |
92744989 GL |
16 | #include <linux/of_mdio.h> |
17 | ||
18 | #include "ll_temac.h" | |
19 | ||
20 | /* --------------------------------------------------------------------- | |
21 | * MDIO Bus functions | |
22 | */ | |
23 | static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg) | |
24 | { | |
25 | struct temac_local *lp = bus->priv; | |
26 | u32 rc; | |
27 | ||
28 | /* Write the PHY address to the MIIM Access Initiator register. | |
29 | * When the transfer completes, the PHY register value will appear | |
30 | * in the LSW0 register */ | |
31 | mutex_lock(&lp->indirect_mutex); | |
32 | temac_iow(lp, XTE_LSW0_OFFSET, (phy_id << 5) | reg); | |
33 | rc = temac_indirect_in32(lp, XTE_MIIMAI_OFFSET); | |
34 | mutex_unlock(&lp->indirect_mutex); | |
35 | ||
36 | dev_dbg(lp->dev, "temac_mdio_read(phy_id=%i, reg=%x) == %x\n", | |
37 | phy_id, reg, rc); | |
38 | ||
39 | return rc; | |
40 | } | |
41 | ||
42 | static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) | |
43 | { | |
44 | struct temac_local *lp = bus->priv; | |
45 | ||
46 | dev_dbg(lp->dev, "temac_mdio_write(phy_id=%i, reg=%x, val=%x)\n", | |
47 | phy_id, reg, val); | |
48 | ||
49 | /* First write the desired value into the write data register | |
50 | * and then write the address into the access initiator register | |
51 | */ | |
52 | mutex_lock(&lp->indirect_mutex); | |
53 | temac_indirect_out32(lp, XTE_MGTDR_OFFSET, val); | |
54 | temac_indirect_out32(lp, XTE_MIIMAI_OFFSET, (phy_id << 5) | reg); | |
55 | mutex_unlock(&lp->indirect_mutex); | |
56 | ||
57 | return 0; | |
58 | } | |
59 | ||
60 | int temac_mdio_setup(struct temac_local *lp, struct device_node *np) | |
61 | { | |
62 | struct mii_bus *bus; | |
e734a42f | 63 | u32 bus_hz; |
92744989 | 64 | int clk_div; |
e734a42f | 65 | int rc; |
92744989 GL |
66 | struct resource res; |
67 | ||
68 | /* Calculate a reasonable divisor for the clock rate */ | |
69 | clk_div = 0x3f; /* worst-case default setting */ | |
e734a42f TK |
70 | if (of_property_read_u32(np, "clock-frequency", &bus_hz) == 0) { |
71 | clk_div = bus_hz / (2500 * 1000 * 2) - 1; | |
92744989 GL |
72 | if (clk_div < 1) |
73 | clk_div = 1; | |
74 | if (clk_div > 0x3f) | |
75 | clk_div = 0x3f; | |
76 | } | |
77 | ||
78 | /* Enable the MDIO bus by asserting the enable bit and writing | |
79 | * in the clock config */ | |
80 | mutex_lock(&lp->indirect_mutex); | |
81 | temac_indirect_out32(lp, XTE_MC_OFFSET, 1 << 6 | clk_div); | |
82 | mutex_unlock(&lp->indirect_mutex); | |
83 | ||
84 | bus = mdiobus_alloc(); | |
85 | if (!bus) | |
86 | return -ENOMEM; | |
87 | ||
88 | of_address_to_resource(np, 0, &res); | |
89 | snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", | |
90 | (unsigned long long)res.start); | |
91 | bus->priv = lp; | |
92 | bus->name = "Xilinx TEMAC MDIO"; | |
93 | bus->read = temac_mdio_read; | |
94 | bus->write = temac_mdio_write; | |
95 | bus->parent = lp->dev; | |
92744989 GL |
96 | |
97 | lp->mii_bus = bus; | |
98 | ||
99 | rc = of_mdiobus_register(bus, np); | |
100 | if (rc) | |
101 | goto err_register; | |
102 | ||
103 | mutex_lock(&lp->indirect_mutex); | |
104 | dev_dbg(lp->dev, "MDIO bus registered; MC:%x\n", | |
105 | temac_indirect_in32(lp, XTE_MC_OFFSET)); | |
106 | mutex_unlock(&lp->indirect_mutex); | |
107 | return 0; | |
108 | ||
109 | err_register: | |
110 | mdiobus_free(bus); | |
111 | return rc; | |
112 | } | |
113 | ||
114 | void temac_mdio_teardown(struct temac_local *lp) | |
115 | { | |
116 | mdiobus_unregister(lp->mii_bus); | |
92744989 GL |
117 | mdiobus_free(lp->mii_bus); |
118 | lp->mii_bus = NULL; | |
119 | } | |
120 |