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