diff options
author | Timur Tabi <timur@freescale.com> | 2012-08-20 05:26:39 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2012-08-24 12:42:42 -0400 |
commit | 9f35a7342cff0be72e3c038ea972e07662ca1ce8 (patch) | |
tree | dd964d775cae6d8405ed9e37d83b6633484ed2dc /drivers/net/ethernet/freescale | |
parent | 3afa6d00fb4f9712fbb44b63ba31f88b6f9239fe (diff) |
net/fsl: introduce Freescale 10G MDIO driver
Similar to fsl_pq_mdio.c, this driver is for the 10G MDIO controller on
Freescale Frame Manager Ethernet controllers.
Signed-off-by: Timur Tabi <timur@freescale.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/ethernet/freescale')
-rw-r--r-- | drivers/net/ethernet/freescale/Kconfig | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/ethernet/freescale/xgmac_mdio.c | 274 |
3 files changed, 282 insertions, 0 deletions
diff --git a/drivers/net/ethernet/freescale/Kconfig b/drivers/net/ethernet/freescale/Kconfig index 3574e1499dfc..feff51664dcf 100644 --- a/drivers/net/ethernet/freescale/Kconfig +++ b/drivers/net/ethernet/freescale/Kconfig | |||
@@ -62,6 +62,13 @@ config FSL_PQ_MDIO | |||
62 | ---help--- | 62 | ---help--- |
63 | This driver supports the MDIO bus used by the gianfar and UCC drivers. | 63 | This driver supports the MDIO bus used by the gianfar and UCC drivers. |
64 | 64 | ||
65 | config FSL_XGMAC_MDIO | ||
66 | tristate "Freescale XGMAC MDIO" | ||
67 | depends on FSL_SOC | ||
68 | select PHYLIB | ||
69 | ---help--- | ||
70 | This driver supports the MDIO bus on the Fman 10G Ethernet MACs. | ||
71 | |||
65 | config UCC_GETH | 72 | config UCC_GETH |
66 | tristate "Freescale QE Gigabit Ethernet" | 73 | tristate "Freescale QE Gigabit Ethernet" |
67 | depends on QUICC_ENGINE | 74 | depends on QUICC_ENGINE |
diff --git a/drivers/net/ethernet/freescale/Makefile b/drivers/net/ethernet/freescale/Makefile index 1752488c9ee5..3d1839afff65 100644 --- a/drivers/net/ethernet/freescale/Makefile +++ b/drivers/net/ethernet/freescale/Makefile | |||
@@ -9,6 +9,7 @@ ifeq ($(CONFIG_FEC_MPC52xx_MDIO),y) | |||
9 | endif | 9 | endif |
10 | obj-$(CONFIG_FS_ENET) += fs_enet/ | 10 | obj-$(CONFIG_FS_ENET) += fs_enet/ |
11 | obj-$(CONFIG_FSL_PQ_MDIO) += fsl_pq_mdio.o | 11 | obj-$(CONFIG_FSL_PQ_MDIO) += fsl_pq_mdio.o |
12 | obj-$(CONFIG_FSL_XGMAC_MDIO) += xgmac_mdio.o | ||
12 | obj-$(CONFIG_GIANFAR) += gianfar_driver.o | 13 | obj-$(CONFIG_GIANFAR) += gianfar_driver.o |
13 | obj-$(CONFIG_PTP_1588_CLOCK_GIANFAR) += gianfar_ptp.o | 14 | obj-$(CONFIG_PTP_1588_CLOCK_GIANFAR) += gianfar_ptp.o |
14 | gianfar_driver-objs := gianfar.o \ | 15 | gianfar_driver-objs := gianfar.o \ |
diff --git a/drivers/net/ethernet/freescale/xgmac_mdio.c b/drivers/net/ethernet/freescale/xgmac_mdio.c new file mode 100644 index 000000000000..1afb5ea2a984 --- /dev/null +++ b/drivers/net/ethernet/freescale/xgmac_mdio.c | |||
@@ -0,0 +1,274 @@ | |||
1 | /* | ||
2 | * QorIQ 10G MDIO Controller | ||
3 | * | ||
4 | * Copyright 2012 Freescale Semiconductor, Inc. | ||
5 | * | ||
6 | * Authors: Andy Fleming <afleming@freescale.com> | ||
7 | * Timur Tabi <timur@freescale.com> | ||
8 | * | ||
9 | * This file is licensed under the terms of the GNU General Public License | ||
10 | * version 2. This program is licensed "as is" without any warranty of any | ||
11 | * kind, whether express or implied. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/phy.h> | ||
19 | #include <linux/mdio.h> | ||
20 | #include <linux/of_platform.h> | ||
21 | #include <linux/of_mdio.h> | ||
22 | |||
23 | /* Number of microseconds to wait for a register to respond */ | ||
24 | #define TIMEOUT 1000 | ||
25 | |||
26 | struct tgec_mdio_controller { | ||
27 | __be32 reserved[12]; | ||
28 | __be32 mdio_stat; /* MDIO configuration and status */ | ||
29 | __be32 mdio_ctl; /* MDIO control */ | ||
30 | __be32 mdio_data; /* MDIO data */ | ||
31 | __be32 mdio_addr; /* MDIO address */ | ||
32 | } __packed; | ||
33 | |||
34 | #define MDIO_STAT_CLKDIV(x) (((x>>1) & 0xff) << 8) | ||
35 | #define MDIO_STAT_BSY (1 << 0) | ||
36 | #define MDIO_STAT_RD_ER (1 << 1) | ||
37 | #define MDIO_CTL_DEV_ADDR(x) (x & 0x1f) | ||
38 | #define MDIO_CTL_PORT_ADDR(x) ((x & 0x1f) << 5) | ||
39 | #define MDIO_CTL_PRE_DIS (1 << 10) | ||
40 | #define MDIO_CTL_SCAN_EN (1 << 11) | ||
41 | #define MDIO_CTL_POST_INC (1 << 14) | ||
42 | #define MDIO_CTL_READ (1 << 15) | ||
43 | |||
44 | #define MDIO_DATA(x) (x & 0xffff) | ||
45 | #define MDIO_DATA_BSY (1 << 31) | ||
46 | |||
47 | /* | ||
48 | * Wait untill the MDIO bus is free | ||
49 | */ | ||
50 | static int xgmac_wait_until_free(struct device *dev, | ||
51 | struct tgec_mdio_controller __iomem *regs) | ||
52 | { | ||
53 | uint32_t status; | ||
54 | |||
55 | /* Wait till the bus is free */ | ||
56 | status = spin_event_timeout( | ||
57 | !((in_be32(®s->mdio_stat)) & MDIO_STAT_BSY), TIMEOUT, 0); | ||
58 | if (!status) { | ||
59 | dev_err(dev, "timeout waiting for bus to be free\n"); | ||
60 | return -ETIMEDOUT; | ||
61 | } | ||
62 | |||
63 | return 0; | ||
64 | } | ||
65 | |||
66 | /* | ||
67 | * Wait till the MDIO read or write operation is complete | ||
68 | */ | ||
69 | static int xgmac_wait_until_done(struct device *dev, | ||
70 | struct tgec_mdio_controller __iomem *regs) | ||
71 | { | ||
72 | uint32_t status; | ||
73 | |||
74 | /* Wait till the MDIO write is complete */ | ||
75 | status = spin_event_timeout( | ||
76 | !((in_be32(®s->mdio_data)) & MDIO_DATA_BSY), TIMEOUT, 0); | ||
77 | if (!status) { | ||
78 | dev_err(dev, "timeout waiting for operation to complete\n"); | ||
79 | return -ETIMEDOUT; | ||
80 | } | ||
81 | |||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | /* | ||
86 | * Write value to the PHY for this device to the register at regnum,waiting | ||
87 | * until the write is done before it returns. All PHY configuration has to be | ||
88 | * done through the TSEC1 MIIM regs. | ||
89 | */ | ||
90 | static int xgmac_mdio_write(struct mii_bus *bus, int phy_id, int regnum, u16 value) | ||
91 | { | ||
92 | struct tgec_mdio_controller __iomem *regs = bus->priv; | ||
93 | uint16_t dev_addr = regnum >> 16; | ||
94 | int ret; | ||
95 | |||
96 | /* Setup the MII Mgmt clock speed */ | ||
97 | out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); | ||
98 | |||
99 | ret = xgmac_wait_until_free(&bus->dev, regs); | ||
100 | if (ret) | ||
101 | return ret; | ||
102 | |||
103 | /* Set the port and dev addr */ | ||
104 | out_be32(®s->mdio_ctl, | ||
105 | MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr)); | ||
106 | |||
107 | /* Set the register address */ | ||
108 | out_be32(®s->mdio_addr, regnum & 0xffff); | ||
109 | |||
110 | ret = xgmac_wait_until_free(&bus->dev, regs); | ||
111 | if (ret) | ||
112 | return ret; | ||
113 | |||
114 | /* Write the value to the register */ | ||
115 | out_be32(®s->mdio_data, MDIO_DATA(value)); | ||
116 | |||
117 | ret = xgmac_wait_until_done(&bus->dev, regs); | ||
118 | if (ret) | ||
119 | return ret; | ||
120 | |||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | /* | ||
125 | * Reads from register regnum in the PHY for device dev, returning the value. | ||
126 | * Clears miimcom first. All PHY configuration has to be done through the | ||
127 | * TSEC1 MIIM regs. | ||
128 | */ | ||
129 | static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum) | ||
130 | { | ||
131 | struct tgec_mdio_controller __iomem *regs = bus->priv; | ||
132 | uint16_t dev_addr = regnum >> 16; | ||
133 | uint32_t mdio_ctl; | ||
134 | uint16_t value; | ||
135 | int ret; | ||
136 | |||
137 | /* Setup the MII Mgmt clock speed */ | ||
138 | out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); | ||
139 | |||
140 | ret = xgmac_wait_until_free(&bus->dev, regs); | ||
141 | if (ret) | ||
142 | return ret; | ||
143 | |||
144 | /* Set the Port and Device Addrs */ | ||
145 | mdio_ctl = MDIO_CTL_PORT_ADDR(phy_id) | MDIO_CTL_DEV_ADDR(dev_addr); | ||
146 | out_be32(®s->mdio_ctl, mdio_ctl); | ||
147 | |||
148 | /* Set the register address */ | ||
149 | out_be32(®s->mdio_addr, regnum & 0xffff); | ||
150 | |||
151 | ret = xgmac_wait_until_free(&bus->dev, regs); | ||
152 | if (ret) | ||
153 | return ret; | ||
154 | |||
155 | /* Initiate the read */ | ||
156 | out_be32(®s->mdio_ctl, mdio_ctl | MDIO_CTL_READ); | ||
157 | |||
158 | ret = xgmac_wait_until_done(&bus->dev, regs); | ||
159 | if (ret) | ||
160 | return ret; | ||
161 | |||
162 | /* Return all Fs if nothing was there */ | ||
163 | if (in_be32(®s->mdio_stat) & MDIO_STAT_RD_ER) { | ||
164 | dev_err(&bus->dev, "MDIO read error\n"); | ||
165 | return 0xffff; | ||
166 | } | ||
167 | |||
168 | value = in_be32(®s->mdio_data) & 0xffff; | ||
169 | dev_dbg(&bus->dev, "read %04x\n", value); | ||
170 | |||
171 | return value; | ||
172 | } | ||
173 | |||
174 | /* Reset the MIIM registers, and wait for the bus to free */ | ||
175 | static int xgmac_mdio_reset(struct mii_bus *bus) | ||
176 | { | ||
177 | struct tgec_mdio_controller __iomem *regs = bus->priv; | ||
178 | int ret; | ||
179 | |||
180 | mutex_lock(&bus->mdio_lock); | ||
181 | |||
182 | /* Setup the MII Mgmt clock speed */ | ||
183 | out_be32(®s->mdio_stat, MDIO_STAT_CLKDIV(100)); | ||
184 | |||
185 | ret = xgmac_wait_until_free(&bus->dev, regs); | ||
186 | |||
187 | mutex_unlock(&bus->mdio_lock); | ||
188 | |||
189 | return ret; | ||
190 | } | ||
191 | |||
192 | static int __devinit xgmac_mdio_probe(struct platform_device *pdev) | ||
193 | { | ||
194 | struct device_node *np = pdev->dev.of_node; | ||
195 | struct mii_bus *bus; | ||
196 | struct resource res; | ||
197 | int ret; | ||
198 | |||
199 | ret = of_address_to_resource(np, 0, &res); | ||
200 | if (ret) { | ||
201 | dev_err(&pdev->dev, "could not obtain address\n"); | ||
202 | return ret; | ||
203 | } | ||
204 | |||
205 | bus = mdiobus_alloc_size(PHY_MAX_ADDR * sizeof(int)); | ||
206 | if (!bus) | ||
207 | return -ENOMEM; | ||
208 | |||
209 | bus->name = "Freescale XGMAC MDIO Bus"; | ||
210 | bus->read = xgmac_mdio_read; | ||
211 | bus->write = xgmac_mdio_write; | ||
212 | bus->reset = xgmac_mdio_reset; | ||
213 | bus->irq = bus->priv; | ||
214 | bus->parent = &pdev->dev; | ||
215 | snprintf(bus->id, MII_BUS_ID_SIZE, "%llx", (unsigned long long)res.start); | ||
216 | |||
217 | /* Set the PHY base address */ | ||
218 | bus->priv = of_iomap(np, 0); | ||
219 | if (!bus->priv) { | ||
220 | ret = -ENOMEM; | ||
221 | goto err_ioremap; | ||
222 | } | ||
223 | |||
224 | ret = of_mdiobus_register(bus, np); | ||
225 | if (ret) { | ||
226 | dev_err(&pdev->dev, "cannot register MDIO bus\n"); | ||
227 | goto err_registration; | ||
228 | } | ||
229 | |||
230 | dev_set_drvdata(&pdev->dev, bus); | ||
231 | |||
232 | return 0; | ||
233 | |||
234 | err_registration: | ||
235 | iounmap(bus->priv); | ||
236 | |||
237 | err_ioremap: | ||
238 | mdiobus_free(bus); | ||
239 | |||
240 | return ret; | ||
241 | } | ||
242 | |||
243 | static int __devexit xgmac_mdio_remove(struct platform_device *pdev) | ||
244 | { | ||
245 | struct mii_bus *bus = dev_get_drvdata(&pdev->dev); | ||
246 | |||
247 | mdiobus_unregister(bus); | ||
248 | iounmap(bus->priv); | ||
249 | mdiobus_free(bus); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static struct of_device_id xgmac_mdio_match[] = { | ||
255 | { | ||
256 | .compatible = "fsl,fman-xmdio", | ||
257 | }, | ||
258 | {}, | ||
259 | }; | ||
260 | MODULE_DEVICE_TABLE(of, xgmac_mdio_match); | ||
261 | |||
262 | static struct platform_driver xgmac_mdio_driver = { | ||
263 | .driver = { | ||
264 | .name = "fsl-fman_xmdio", | ||
265 | .of_match_table = xgmac_mdio_match, | ||
266 | }, | ||
267 | .probe = xgmac_mdio_probe, | ||
268 | .remove = xgmac_mdio_remove, | ||
269 | }; | ||
270 | |||
271 | module_platform_driver(xgmac_mdio_driver); | ||
272 | |||
273 | MODULE_DESCRIPTION("Freescale QorIQ 10G MDIO Controller"); | ||
274 | MODULE_LICENSE("GPL v2"); | ||