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> | |
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 |