diff options
Diffstat (limited to 'drivers/net/ll_temac_mdio.c')
-rw-r--r-- | drivers/net/ll_temac_mdio.c | 120 |
1 files changed, 120 insertions, 0 deletions
diff --git a/drivers/net/ll_temac_mdio.c b/drivers/net/ll_temac_mdio.c new file mode 100644 index 000000000000..da0e462308d5 --- /dev/null +++ b/drivers/net/ll_temac_mdio.c | |||
@@ -0,0 +1,120 @@ | |||
1 | /* | ||
2 | * MDIO bus driver for the Xilinx TEMAC device | ||
3 | * | ||
4 | * Copyright (c) 2009 Secret Lab Technologies, Ltd. | ||
5 | */ | ||
6 | |||
7 | #include <linux/io.h> | ||
8 | #include <linux/netdevice.h> | ||
9 | #include <linux/mutex.h> | ||
10 | #include <linux/phy.h> | ||
11 | #include <linux/of.h> | ||
12 | #include <linux/of_device.h> | ||
13 | #include <linux/of_mdio.h> | ||
14 | |||
15 | #include "ll_temac.h" | ||
16 | |||
17 | /* --------------------------------------------------------------------- | ||
18 | * MDIO Bus functions | ||
19 | */ | ||
20 | static int temac_mdio_read(struct mii_bus *bus, int phy_id, int reg) | ||
21 | { | ||
22 | struct temac_local *lp = bus->priv; | ||
23 | u32 rc; | ||
24 | |||
25 | /* Write the PHY address to the MIIM Access Initiator register. | ||
26 | * When the transfer completes, the PHY register value will appear | ||
27 | * in the LSW0 register */ | ||
28 | mutex_lock(&lp->indirect_mutex); | ||
29 | temac_iow(lp, XTE_LSW0_OFFSET, (phy_id << 5) | reg); | ||
30 | rc = temac_indirect_in32(lp, XTE_MIIMAI_OFFSET); | ||
31 | mutex_unlock(&lp->indirect_mutex); | ||
32 | |||
33 | dev_dbg(lp->dev, "temac_mdio_read(phy_id=%i, reg=%x) == %x\n", | ||
34 | phy_id, reg, rc); | ||
35 | |||
36 | return rc; | ||
37 | } | ||
38 | |||
39 | static int temac_mdio_write(struct mii_bus *bus, int phy_id, int reg, u16 val) | ||
40 | { | ||
41 | struct temac_local *lp = bus->priv; | ||
42 | |||
43 | dev_dbg(lp->dev, "temac_mdio_write(phy_id=%i, reg=%x, val=%x)\n", | ||
44 | phy_id, reg, val); | ||
45 | |||
46 | /* First write the desired value into the write data register | ||
47 | * and then write the address into the access initiator register | ||
48 | */ | ||
49 | mutex_lock(&lp->indirect_mutex); | ||
50 | temac_indirect_out32(lp, XTE_MGTDR_OFFSET, val); | ||
51 | temac_indirect_out32(lp, XTE_MIIMAI_OFFSET, (phy_id << 5) | reg); | ||
52 | mutex_unlock(&lp->indirect_mutex); | ||
53 | |||
54 | return 0; | ||
55 | } | ||
56 | |||
57 | int temac_mdio_setup(struct temac_local *lp, struct device_node *np) | ||
58 | { | ||
59 | struct mii_bus *bus; | ||
60 | const u32 *bus_hz; | ||
61 | int clk_div; | ||
62 | int rc, size; | ||
63 | struct resource res; | ||
64 | |||
65 | /* Calculate a reasonable divisor for the clock rate */ | ||
66 | clk_div = 0x3f; /* worst-case default setting */ | ||
67 | bus_hz = of_get_property(np, "clock-frequency", &size); | ||
68 | if (bus_hz && size >= sizeof(*bus_hz)) { | ||
69 | clk_div = (*bus_hz) / (2500 * 1000 * 2) - 1; | ||
70 | if (clk_div < 1) | ||
71 | clk_div = 1; | ||
72 | if (clk_div > 0x3f) | ||
73 | clk_div = 0x3f; | ||
74 | } | ||
75 | |||
76 | /* Enable the MDIO bus by asserting the enable bit and writing | ||
77 | * in the clock config */ | ||
78 | mutex_lock(&lp->indirect_mutex); | ||
79 | temac_indirect_out32(lp, XTE_MC_OFFSET, 1 << 6 | clk_div); | ||
80 | mutex_unlock(&lp->indirect_mutex); | ||
81 | |||
82 | bus = mdiobus_alloc(); | ||
83 | if (!bus) | ||
84 | return -ENOMEM; | ||
85 | |||
86 | of_address_to_resource(np, 0, &res); | ||
87 | snprintf(bus->id, MII_BUS_ID_SIZE, "%.8llx", | ||
88 | (unsigned long long)res.start); | ||
89 | bus->priv = lp; | ||
90 | bus->name = "Xilinx TEMAC MDIO"; | ||
91 | bus->read = temac_mdio_read; | ||
92 | bus->write = temac_mdio_write; | ||
93 | bus->parent = lp->dev; | ||
94 | bus->irq = lp->mdio_irqs; /* preallocated IRQ table */ | ||
95 | |||
96 | lp->mii_bus = bus; | ||
97 | |||
98 | rc = of_mdiobus_register(bus, np); | ||
99 | if (rc) | ||
100 | goto err_register; | ||
101 | |||
102 | mutex_lock(&lp->indirect_mutex); | ||
103 | dev_dbg(lp->dev, "MDIO bus registered; MC:%x\n", | ||
104 | temac_indirect_in32(lp, XTE_MC_OFFSET)); | ||
105 | mutex_unlock(&lp->indirect_mutex); | ||
106 | return 0; | ||
107 | |||
108 | err_register: | ||
109 | mdiobus_free(bus); | ||
110 | return rc; | ||
111 | } | ||
112 | |||
113 | void temac_mdio_teardown(struct temac_local *lp) | ||
114 | { | ||
115 | mdiobus_unregister(lp->mii_bus); | ||
116 | kfree(lp->mii_bus->irq); | ||
117 | mdiobus_free(lp->mii_bus); | ||
118 | lp->mii_bus = NULL; | ||
119 | } | ||
120 | |||