aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorDavid Daney <ddaney@caviumnetworks.com>2010-11-19 07:13:18 -0500
committerDavid S. Miller <davem@davemloft.net>2010-11-22 11:34:23 -0500
commitcf41a51db89850033efc11c18a5257de810b5417 (patch)
treec6db1cf0b3bb293245dae7bf2930b4913b117eac /drivers
parent90600732d8b2fbc422bc9c57bdc73513d909367f (diff)
of/phylib: Use device tree properties to initialize Marvell PHYs.
Some aspects of PHY initialization are board dependent, things like indicator LED connections and some clocking modes cannot be determined by probing. The dev_flags element of struct phy_device can be used to control these things if an appropriate value can be passed from the Ethernet driver. We run into problems however if the PHY connections are specified by the device tree. There is no way for the Ethernet driver to know what flags it should pass. If we are using the device tree, the struct phy_device will be populated with the device tree node corresponding to the PHY, and we can extract extra configuration information from there. The next question is what should the format of that information be? It is highly device specific, and the device tree representation should not be tied to any arbitrary kernel defined constants. A straight forward representation is just to specify the exact bits that should be set using the "marvell,reg-init" property: phy5: ethernet-phy@5 { reg = <5>; compatible = "marvell,88e1149r"; marvell,reg-init = /* led[0]:1000, led[1]:100, led[2]:10, led[3]:tx */ <3 0x10 0 0x5777>, /* Reg 3,16 <- 0x5777 */ /* mix %:0, led[0123]:drive low off hiZ */ <3 0x11 0 0x00aa>, /* Reg 3,17 <- 0x00aa */ /* default blink periods. */ <3 0x12 0 0x4105>, /* Reg 3,18 <- 0x4105 */ /* led[4]:rx, led[5]:dplx, led[45]:drive low off hiZ */ <3 0x13 0 0x0a60>; /* Reg 3,19 <- 0x0a60 */ }; phy6: ethernet-phy@6 { reg = <6>; compatible = "marvell,88e1118"; marvell,reg-init = /* Fix rx and tx clock transition timing */ <2 0x15 0xffcf 0>, /* Reg 2,21 Clear bits 4, 5 */ /* Adjust LED drive. */ <3 0x11 0 0x442a>, /* Reg 3,17 <- 0442a */ /* irq, blink-activity, blink-link */ <3 0x10 0 0x0242>; /* Reg 3,16 <- 0x0242 */ }; The Marvell PHYs have a page select register at register 22 (0x16), we can specify any register by its page and register number. These are the first and second word. The third word contains a mask to be ANDed with the existing register value, and the fourth word is ORed with the result to yield the new register value. The new marvell_of_reg_init function leaves the page select register unchanged, so a call to it can be dropped into the .config_init functions without unduly affecting the state of the PHY. If CONFIG_OF_MDIO is not set, there is no of_node, or no "marvell,reg-init" property, the PHY initialization is unchanged. Signed-off-by: David Daney <ddaney@caviumnetworks.com> Cc: Grant Likely <grant.likely@secretlab.ca> Cc: Cyril Chemparathy <cyril@ti.com> Cc: David Daney <ddaney@caviumnetworks.com> Cc: Arnaud Patard <arnaud.patard@rtp-net.org> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Reviewed-by: Grant Likely <grant.likely@secretlab.ca> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/phy/marvell.c97
1 files changed, 97 insertions, 0 deletions
diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
index def19d7c53ef..e8b9c53c304b 100644
--- a/drivers/net/phy/marvell.c
+++ b/drivers/net/phy/marvell.c
@@ -30,6 +30,7 @@
30#include <linux/ethtool.h> 30#include <linux/ethtool.h>
31#include <linux/phy.h> 31#include <linux/phy.h>
32#include <linux/marvell_phy.h> 32#include <linux/marvell_phy.h>
33#include <linux/of.h>
33 34
34#include <asm/io.h> 35#include <asm/io.h>
35#include <asm/irq.h> 36#include <asm/irq.h>
@@ -187,6 +188,87 @@ static int marvell_config_aneg(struct phy_device *phydev)
187 return 0; 188 return 0;
188} 189}
189 190
191#ifdef CONFIG_OF_MDIO
192/*
193 * Set and/or override some configuration registers based on the
194 * marvell,reg-init property stored in the of_node for the phydev.
195 *
196 * marvell,reg-init = <reg-page reg mask value>,...;
197 *
198 * There may be one or more sets of <reg-page reg mask value>:
199 *
200 * reg-page: which register bank to use.
201 * reg: the register.
202 * mask: if non-zero, ANDed with existing register value.
203 * value: ORed with the masked value and written to the regiser.
204 *
205 */
206static int marvell_of_reg_init(struct phy_device *phydev)
207{
208 const __be32 *paddr;
209 int len, i, saved_page, current_page, page_changed, ret;
210
211 if (!phydev->dev.of_node)
212 return 0;
213
214 paddr = of_get_property(phydev->dev.of_node, "marvell,reg-init", &len);
215 if (!paddr || len < (4 * sizeof(*paddr)))
216 return 0;
217
218 saved_page = phy_read(phydev, MII_MARVELL_PHY_PAGE);
219 if (saved_page < 0)
220 return saved_page;
221 page_changed = 0;
222 current_page = saved_page;
223
224 ret = 0;
225 len /= sizeof(*paddr);
226 for (i = 0; i < len - 3; i += 4) {
227 u16 reg_page = be32_to_cpup(paddr + i);
228 u16 reg = be32_to_cpup(paddr + i + 1);
229 u16 mask = be32_to_cpup(paddr + i + 2);
230 u16 val_bits = be32_to_cpup(paddr + i + 3);
231 int val;
232
233 if (reg_page != current_page) {
234 current_page = reg_page;
235 page_changed = 1;
236 ret = phy_write(phydev, MII_MARVELL_PHY_PAGE, reg_page);
237 if (ret < 0)
238 goto err;
239 }
240
241 val = 0;
242 if (mask) {
243 val = phy_read(phydev, reg);
244 if (val < 0) {
245 ret = val;
246 goto err;
247 }
248 val &= mask;
249 }
250 val |= val_bits;
251
252 ret = phy_write(phydev, reg, val);
253 if (ret < 0)
254 goto err;
255
256 }
257err:
258 if (page_changed) {
259 i = phy_write(phydev, MII_MARVELL_PHY_PAGE, saved_page);
260 if (ret == 0)
261 ret = i;
262 }
263 return ret;
264}
265#else
266static int marvell_of_reg_init(struct phy_device *phydev)
267{
268 return 0;
269}
270#endif /* CONFIG_OF_MDIO */
271
190static int m88e1121_config_aneg(struct phy_device *phydev) 272static int m88e1121_config_aneg(struct phy_device *phydev)
191{ 273{
192 int err, oldpage, mscr; 274 int err, oldpage, mscr;
@@ -369,6 +451,9 @@ static int m88e1111_config_init(struct phy_device *phydev)
369 return err; 451 return err;
370 } 452 }
371 453
454 err = marvell_of_reg_init(phydev);
455 if (err < 0)
456 return err;
372 457
373 err = phy_write(phydev, MII_BMCR, BMCR_RESET); 458 err = phy_write(phydev, MII_BMCR, BMCR_RESET);
374 if (err < 0) 459 if (err < 0)
@@ -421,6 +506,10 @@ static int m88e1118_config_init(struct phy_device *phydev)
421 if (err < 0) 506 if (err < 0)
422 return err; 507 return err;
423 508
509 err = marvell_of_reg_init(phydev);
510 if (err < 0)
511 return err;
512
424 /* Reset address */ 513 /* Reset address */
425 err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0); 514 err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0);
426 if (err < 0) 515 if (err < 0)
@@ -447,6 +536,10 @@ static int m88e1149_config_init(struct phy_device *phydev)
447 if (err < 0) 536 if (err < 0)
448 return err; 537 return err;
449 538
539 err = marvell_of_reg_init(phydev);
540 if (err < 0)
541 return err;
542
450 /* Reset address */ 543 /* Reset address */
451 err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0); 544 err = phy_write(phydev, MII_MARVELL_PHY_PAGE, 0x0);
452 if (err < 0) 545 if (err < 0)
@@ -518,6 +611,10 @@ static int m88e1145_config_init(struct phy_device *phydev)
518 } 611 }
519 } 612 }
520 613
614 err = marvell_of_reg_init(phydev);
615 if (err < 0)
616 return err;
617
521 return 0; 618 return 0;
522} 619}
523 620