Commit | Line | Data |
---|---|---|
8a3b7a25 | 1 | /* |
2 | * MDIO bus driver for the Xilinx Axi Ethernet device | |
3 | * | |
4 | * Copyright (c) 2009 Secret Lab Technologies, Ltd. | |
59a54f30 MS |
5 | * Copyright (c) 2010 - 2011 Michal Simek <monstr@monstr.eu> |
6 | * Copyright (c) 2010 - 2011 PetaLogix | |
7 | * Copyright (c) 2010 - 2012 Xilinx, Inc. All rights reserved. | |
8a3b7a25 | 8 | */ |
9 | ||
10 | #include <linux/of_address.h> | |
11 | #include <linux/of_mdio.h> | |
12 | #include <linux/jiffies.h> | |
13 | ||
14 | #include "xilinx_axienet.h" | |
15 | ||
16 | #define MAX_MDIO_FREQ 2500000 /* 2.5 MHz */ | |
17 | #define DEFAULT_CLOCK_DIVISOR XAE_MDIO_DIV_DFT | |
18 | ||
19 | /* Wait till MDIO interface is ready to accept a new transaction.*/ | |
20 | int axienet_mdio_wait_until_ready(struct axienet_local *lp) | |
21 | { | |
9f8b93cb | 22 | unsigned long end = jiffies + 2; |
8a3b7a25 | 23 | while (!(axienet_ior(lp, XAE_MDIO_MCR_OFFSET) & |
24 | XAE_MDIO_MCR_READY_MASK)) { | |
3aeea53f | 25 | if (time_before_eq(end, jiffies)) { |
8a3b7a25 | 26 | WARN_ON(1); |
27 | return -ETIMEDOUT; | |
28 | } | |
29 | udelay(1); | |
30 | } | |
31 | return 0; | |
32 | } | |
33 | ||
34 | /** | |
35 | * axienet_mdio_read - MDIO interface read function | |
36 | * @bus: Pointer to mii bus structure | |
37 | * @phy_id: Address of the PHY device | |
38 | * @reg: PHY register to read | |
39 | * | |
40 | * returns: The register contents on success, -ETIMEDOUT on a timeout | |
41 | * | |
42 | * Reads the contents of the requested register from the requested PHY | |
43 | * address by first writing the details into MCR register. After a while | |
44 | * the register MRD is read to obtain the PHY register content. | |
45 | */ | |
46 | static int axienet_mdio_read(struct mii_bus *bus, int phy_id, int reg) | |
47 | { | |
48 | u32 rc; | |
49 | int ret; | |
50 | struct axienet_local *lp = bus->priv; | |
51 | ||
52 | ret = axienet_mdio_wait_until_ready(lp); | |
53 | if (ret < 0) | |
54 | return ret; | |
55 | ||
56 | axienet_iow(lp, XAE_MDIO_MCR_OFFSET, | |
57 | (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) & | |
58 | XAE_MDIO_MCR_PHYAD_MASK) | | |
59 | ((reg << XAE_MDIO_MCR_REGAD_SHIFT) & | |
60 | XAE_MDIO_MCR_REGAD_MASK) | | |
61 | XAE_MDIO_MCR_INITIATE_MASK | | |
62 | XAE_MDIO_MCR_OP_READ_MASK)); | |
63 | ||
64 | ret = axienet_mdio_wait_until_ready(lp); | |
65 | if (ret < 0) | |
66 | return ret; | |
67 | ||
68 | rc = axienet_ior(lp, XAE_MDIO_MRD_OFFSET) & 0x0000FFFF; | |
69 | ||
70 | dev_dbg(lp->dev, "axienet_mdio_read(phy_id=%i, reg=%x) == %x\n", | |
71 | phy_id, reg, rc); | |
72 | ||
73 | return rc; | |
74 | } | |
75 | ||
76 | /** | |
77 | * axienet_mdio_write - MDIO interface write function | |
78 | * @bus: Pointer to mii bus structure | |
79 | * @phy_id: Address of the PHY device | |
80 | * @reg: PHY register to write to | |
81 | * @val: Value to be written into the register | |
82 | * | |
83 | * returns: 0 on success, -ETIMEDOUT on a timeout | |
84 | * | |
85 | * Writes the value to the requested register by first writing the value | |
86 | * into MWD register. The the MCR register is then appropriately setup | |
87 | * to finish the write operation. | |
88 | */ | |
89 | static int axienet_mdio_write(struct mii_bus *bus, int phy_id, int reg, | |
90 | u16 val) | |
91 | { | |
92 | int ret; | |
93 | struct axienet_local *lp = bus->priv; | |
94 | ||
95 | dev_dbg(lp->dev, "axienet_mdio_write(phy_id=%i, reg=%x, val=%x)\n", | |
96 | phy_id, reg, val); | |
97 | ||
98 | ret = axienet_mdio_wait_until_ready(lp); | |
99 | if (ret < 0) | |
100 | return ret; | |
101 | ||
102 | axienet_iow(lp, XAE_MDIO_MWD_OFFSET, (u32) val); | |
103 | axienet_iow(lp, XAE_MDIO_MCR_OFFSET, | |
104 | (((phy_id << XAE_MDIO_MCR_PHYAD_SHIFT) & | |
105 | XAE_MDIO_MCR_PHYAD_MASK) | | |
106 | ((reg << XAE_MDIO_MCR_REGAD_SHIFT) & | |
107 | XAE_MDIO_MCR_REGAD_MASK) | | |
108 | XAE_MDIO_MCR_INITIATE_MASK | | |
109 | XAE_MDIO_MCR_OP_WRITE_MASK)); | |
110 | ||
111 | ret = axienet_mdio_wait_until_ready(lp); | |
112 | if (ret < 0) | |
113 | return ret; | |
114 | return 0; | |
115 | } | |
116 | ||
117 | /** | |
118 | * axienet_mdio_setup - MDIO setup function | |
119 | * @lp: Pointer to axienet local data structure. | |
120 | * @np: Pointer to device node | |
121 | * | |
122 | * returns: 0 on success, -ETIMEDOUT on a timeout, -ENOMEM when | |
123 | * mdiobus_alloc (to allocate memory for mii bus structure) fails. | |
124 | * | |
125 | * Sets up the MDIO interface by initializing the MDIO clock and enabling the | |
126 | * MDIO interface in hardware. Register the MDIO interface. | |
127 | **/ | |
128 | int axienet_mdio_setup(struct axienet_local *lp, struct device_node *np) | |
129 | { | |
130 | int ret; | |
131 | u32 clk_div, host_clock; | |
132 | u32 *property_p; | |
133 | struct mii_bus *bus; | |
134 | struct resource res; | |
135 | struct device_node *np1; | |
136 | ||
137 | /* clk_div can be calculated by deriving it from the equation: | |
138 | * fMDIO = fHOST / ((1 + clk_div) * 2) | |
139 | * | |
140 | * Where fMDIO <= 2500000, so we get: | |
141 | * fHOST / ((1 + clk_div) * 2) <= 2500000 | |
142 | * | |
143 | * Then we get: | |
144 | * 1 / ((1 + clk_div) * 2) <= (2500000 / fHOST) | |
145 | * | |
146 | * Then we get: | |
147 | * 1 / (1 + clk_div) <= ((2500000 * 2) / fHOST) | |
148 | * | |
149 | * Then we get: | |
150 | * 1 / (1 + clk_div) <= (5000000 / fHOST) | |
151 | * | |
152 | * So: | |
153 | * (1 + clk_div) >= (fHOST / 5000000) | |
154 | * | |
155 | * And finally: | |
156 | * clk_div >= (fHOST / 5000000) - 1 | |
157 | * | |
158 | * fHOST can be read from the flattened device tree as property | |
159 | * "clock-frequency" from the CPU | |
160 | */ | |
161 | ||
162 | np1 = of_find_node_by_name(NULL, "cpu"); | |
163 | if (!np1) { | |
164 | printk(KERN_WARNING "%s(): Could not find CPU device node.", | |
165 | __func__); | |
166 | printk(KERN_WARNING "Setting MDIO clock divisor to " | |
167 | "default %d\n", DEFAULT_CLOCK_DIVISOR); | |
168 | clk_div = DEFAULT_CLOCK_DIVISOR; | |
169 | goto issue; | |
170 | } | |
171 | property_p = (u32 *) of_get_property(np1, "clock-frequency", NULL); | |
172 | if (!property_p) { | |
173 | printk(KERN_WARNING "%s(): Could not find CPU property: " | |
174 | "clock-frequency.", __func__); | |
175 | printk(KERN_WARNING "Setting MDIO clock divisor to " | |
176 | "default %d\n", DEFAULT_CLOCK_DIVISOR); | |
177 | clk_div = DEFAULT_CLOCK_DIVISOR; | |
282a1dff | 178 | of_node_put(np1); |
8a3b7a25 | 179 | goto issue; |
180 | } | |
181 | ||
182 | host_clock = be32_to_cpup(property_p); | |
183 | clk_div = (host_clock / (MAX_MDIO_FREQ * 2)) - 1; | |
184 | /* If there is any remainder from the division of | |
185 | * fHOST / (MAX_MDIO_FREQ * 2), then we need to add | |
186 | * 1 to the clock divisor or we will surely be above 2.5 MHz */ | |
187 | if (host_clock % (MAX_MDIO_FREQ * 2)) | |
188 | clk_div++; | |
189 | ||
190 | printk(KERN_DEBUG "%s(): Setting MDIO clock divisor to %u based " | |
191 | "on %u Hz host clock.\n", __func__, clk_div, host_clock); | |
192 | ||
193 | of_node_put(np1); | |
194 | issue: | |
195 | axienet_iow(lp, XAE_MDIO_MC_OFFSET, | |
196 | (((u32) clk_div) | XAE_MDIO_MC_MDIOEN_MASK)); | |
197 | ||
198 | ret = axienet_mdio_wait_until_ready(lp); | |
199 | if (ret < 0) | |
200 | return ret; | |
201 | ||
202 | bus = mdiobus_alloc(); | |
203 | if (!bus) | |
204 | return -ENOMEM; | |
205 | ||
206 | np1 = of_get_parent(lp->phy_node); | |
207 | of_address_to_resource(np1, 0, &res); | |
208 | snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", | |
209 | (unsigned long long) res.start); | |
210 | ||
211 | bus->priv = lp; | |
212 | bus->name = "Xilinx Axi Ethernet MDIO"; | |
213 | bus->read = axienet_mdio_read; | |
214 | bus->write = axienet_mdio_write; | |
215 | bus->parent = lp->dev; | |
216 | bus->irq = lp->mdio_irqs; /* preallocated IRQ table */ | |
217 | lp->mii_bus = bus; | |
218 | ||
219 | ret = of_mdiobus_register(bus, np1); | |
220 | if (ret) { | |
221 | mdiobus_free(bus); | |
222 | return ret; | |
223 | } | |
224 | return 0; | |
225 | } | |
226 | ||
227 | /** | |
228 | * axienet_mdio_teardown - MDIO remove function | |
229 | * @lp: Pointer to axienet local data structure. | |
230 | * | |
231 | * Unregisters the MDIO and frees any associate memory for mii bus. | |
232 | */ | |
233 | void axienet_mdio_teardown(struct axienet_local *lp) | |
234 | { | |
235 | mdiobus_unregister(lp->mii_bus); | |
236 | kfree(lp->mii_bus->irq); | |
237 | mdiobus_free(lp->mii_bus); | |
238 | lp->mii_bus = NULL; | |
239 | } |