Commit | Line | Data |
---|---|---|
48257c4f PA |
1 | /* |
2 | * Combined Ethernet driver for Motorola MPC8xx and MPC82xx. | |
3 | * | |
9b8ee8e7 | 4 | * Copyright (c) 2003 Intracom S.A. |
48257c4f | 5 | * by Pantelis Antoniou <panto@intracom.gr> |
9b8ee8e7 VB |
6 | * |
7 | * 2005 (c) MontaVista Software, Inc. | |
48257c4f PA |
8 | * Vitaly Bordug <vbordug@ru.mvista.com> |
9 | * | |
9b8ee8e7 VB |
10 | * This file is licensed under the terms of the GNU General Public License |
11 | * version 2. This program is licensed "as is" without any warranty of any | |
48257c4f PA |
12 | * kind, whether express or implied. |
13 | */ | |
14 | ||
48257c4f | 15 | #include <linux/module.h> |
48257c4f PA |
16 | #include <linux/ioport.h> |
17 | #include <linux/slab.h> | |
48257c4f | 18 | #include <linux/init.h> |
2b5b3a60 | 19 | #include <linux/interrupt.h> |
48257c4f PA |
20 | #include <linux/netdevice.h> |
21 | #include <linux/etherdevice.h> | |
48257c4f | 22 | #include <linux/mii.h> |
5b4b8454 | 23 | #include <linux/platform_device.h> |
2b5b3a60 | 24 | #include <linux/mdio-bitbang.h> |
aa73832c | 25 | #include <linux/of_mdio.h> |
976de6a8 | 26 | #include <linux/of_platform.h> |
48257c4f PA |
27 | |
28 | #include "fs_enet.h" | |
29 | ||
976de6a8 | 30 | struct bb_info { |
2b5b3a60 | 31 | struct mdiobb_ctrl ctrl; |
976de6a8 SW |
32 | __be32 __iomem *dir; |
33 | __be32 __iomem *dat; | |
34 | u32 mdio_msk; | |
35 | u32 mdc_msk; | |
976de6a8 SW |
36 | }; |
37 | ||
38 | /* FIXME: If any other users of GPIO crop up, then these will have to | |
39 | * have some sort of global synchronization to avoid races with other | |
40 | * pins on the same port. The ideal solution would probably be to | |
41 | * bind the ports to a GPIO driver, and have this be a client of it. | |
42 | */ | |
43 | static inline void bb_set(u32 __iomem *p, u32 m) | |
48257c4f | 44 | { |
976de6a8 | 45 | out_be32(p, in_be32(p) | m); |
48257c4f PA |
46 | } |
47 | ||
976de6a8 | 48 | static inline void bb_clr(u32 __iomem *p, u32 m) |
48257c4f | 49 | { |
976de6a8 | 50 | out_be32(p, in_be32(p) & ~m); |
48257c4f PA |
51 | } |
52 | ||
976de6a8 | 53 | static inline int bb_read(u32 __iomem *p, u32 m) |
48257c4f | 54 | { |
976de6a8 | 55 | return (in_be32(p) & m) != 0; |
48257c4f PA |
56 | } |
57 | ||
2b5b3a60 | 58 | static inline void mdio_dir(struct mdiobb_ctrl *ctrl, int dir) |
48257c4f | 59 | { |
2b5b3a60 | 60 | struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl); |
48257c4f | 61 | |
2b5b3a60 SW |
62 | if (dir) |
63 | bb_set(bitbang->dir, bitbang->mdio_msk); | |
64 | else | |
65 | bb_clr(bitbang->dir, bitbang->mdio_msk); | |
66 | ||
67 | /* Read back to flush the write. */ | |
68 | in_be32(bitbang->dir); | |
48257c4f PA |
69 | } |
70 | ||
2b5b3a60 | 71 | static inline int mdio_read(struct mdiobb_ctrl *ctrl) |
48257c4f | 72 | { |
2b5b3a60 | 73 | struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl); |
976de6a8 | 74 | return bb_read(bitbang->dat, bitbang->mdio_msk); |
48257c4f PA |
75 | } |
76 | ||
2b5b3a60 | 77 | static inline void mdio(struct mdiobb_ctrl *ctrl, int what) |
48257c4f | 78 | { |
2b5b3a60 SW |
79 | struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl); |
80 | ||
48257c4f | 81 | if (what) |
976de6a8 | 82 | bb_set(bitbang->dat, bitbang->mdio_msk); |
48257c4f | 83 | else |
976de6a8 | 84 | bb_clr(bitbang->dat, bitbang->mdio_msk); |
2b5b3a60 SW |
85 | |
86 | /* Read back to flush the write. */ | |
87 | in_be32(bitbang->dat); | |
48257c4f PA |
88 | } |
89 | ||
2b5b3a60 | 90 | static inline void mdc(struct mdiobb_ctrl *ctrl, int what) |
48257c4f | 91 | { |
2b5b3a60 SW |
92 | struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl); |
93 | ||
48257c4f | 94 | if (what) |
976de6a8 | 95 | bb_set(bitbang->dat, bitbang->mdc_msk); |
48257c4f | 96 | else |
976de6a8 | 97 | bb_clr(bitbang->dat, bitbang->mdc_msk); |
48257c4f | 98 | |
2b5b3a60 SW |
99 | /* Read back to flush the write. */ |
100 | in_be32(bitbang->dat); | |
48257c4f PA |
101 | } |
102 | ||
2b5b3a60 SW |
103 | static struct mdiobb_ops bb_ops = { |
104 | .owner = THIS_MODULE, | |
105 | .set_mdc = mdc, | |
106 | .set_mdio_dir = mdio_dir, | |
107 | .set_mdio_data = mdio, | |
108 | .get_mdio_data = mdio_read, | |
109 | }; | |
5b4b8454 | 110 | |
976de6a8 SW |
111 | static int __devinit fs_mii_bitbang_init(struct mii_bus *bus, |
112 | struct device_node *np) | |
48257c4f | 113 | { |
976de6a8 SW |
114 | struct resource res; |
115 | const u32 *data; | |
116 | int mdio_pin, mdc_pin, len; | |
117 | struct bb_info *bitbang = bus->priv; | |
48257c4f | 118 | |
976de6a8 SW |
119 | int ret = of_address_to_resource(np, 0, &res); |
120 | if (ret) | |
121 | return ret; | |
122 | ||
123 | if (res.end - res.start < 13) | |
124 | return -ENODEV; | |
125 | ||
126 | /* This should really encode the pin number as well, but all | |
127 | * we get is an int, and the odds of multiple bitbang mdio buses | |
128 | * is low enough that it's not worth going too crazy. | |
129 | */ | |
9d9326d3 | 130 | snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start); |
976de6a8 SW |
131 | |
132 | data = of_get_property(np, "fsl,mdio-pin", &len); | |
133 | if (!data || len != 4) | |
134 | return -ENODEV; | |
135 | mdio_pin = *data; | |
136 | ||
137 | data = of_get_property(np, "fsl,mdc-pin", &len); | |
138 | if (!data || len != 4) | |
139 | return -ENODEV; | |
140 | mdc_pin = *data; | |
141 | ||
142 | bitbang->dir = ioremap(res.start, res.end - res.start + 1); | |
143 | if (!bitbang->dir) | |
144 | return -ENOMEM; | |
145 | ||
146 | bitbang->dat = bitbang->dir + 4; | |
147 | bitbang->mdio_msk = 1 << (31 - mdio_pin); | |
148 | bitbang->mdc_msk = 1 << (31 - mdc_pin); | |
976de6a8 SW |
149 | |
150 | return 0; | |
151 | } | |
152 | ||
74888760 | 153 | static int __devinit fs_enet_mdio_probe(struct platform_device *ofdev) |
976de6a8 | 154 | { |
976de6a8 SW |
155 | struct mii_bus *new_bus; |
156 | struct bb_info *bitbang; | |
157 | int ret = -ENOMEM; | |
976de6a8 | 158 | |
976de6a8 SW |
159 | bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL); |
160 | if (!bitbang) | |
2b5b3a60 SW |
161 | goto out; |
162 | ||
163 | bitbang->ctrl.ops = &bb_ops; | |
164 | ||
165 | new_bus = alloc_mdio_bitbang(&bitbang->ctrl); | |
166 | if (!new_bus) | |
167 | goto out_free_priv; | |
976de6a8 | 168 | |
976de6a8 | 169 | new_bus->name = "CPM2 Bitbanged MII", |
976de6a8 | 170 | |
4eecb178 | 171 | ret = fs_mii_bitbang_init(new_bus, ofdev->dev.of_node); |
976de6a8 | 172 | if (ret) |
2b5b3a60 | 173 | goto out_free_bus; |
976de6a8 SW |
174 | |
175 | new_bus->phy_mask = ~0; | |
176 | new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); | |
177 | if (!new_bus->irq) | |
178 | goto out_unmap_regs; | |
179 | ||
18ee49dd | 180 | new_bus->parent = &ofdev->dev; |
976de6a8 SW |
181 | dev_set_drvdata(&ofdev->dev, new_bus); |
182 | ||
4eecb178 | 183 | ret = of_mdiobus_register(new_bus, ofdev->dev.of_node); |
976de6a8 SW |
184 | if (ret) |
185 | goto out_free_irqs; | |
186 | ||
187 | return 0; | |
188 | ||
189 | out_free_irqs: | |
190 | dev_set_drvdata(&ofdev->dev, NULL); | |
191 | kfree(new_bus->irq); | |
192 | out_unmap_regs: | |
193 | iounmap(bitbang->dir); | |
976de6a8 | 194 | out_free_bus: |
2b5b3a60 | 195 | free_mdio_bitbang(new_bus); |
298cf9be LB |
196 | out_free_priv: |
197 | kfree(bitbang); | |
976de6a8 SW |
198 | out: |
199 | return ret; | |
200 | } | |
201 | ||
2dc11581 | 202 | static int fs_enet_mdio_remove(struct platform_device *ofdev) |
976de6a8 SW |
203 | { |
204 | struct mii_bus *bus = dev_get_drvdata(&ofdev->dev); | |
205 | struct bb_info *bitbang = bus->priv; | |
206 | ||
207 | mdiobus_unregister(bus); | |
208 | dev_set_drvdata(&ofdev->dev, NULL); | |
209 | kfree(bus->irq); | |
298cf9be | 210 | free_mdio_bitbang(bus); |
976de6a8 SW |
211 | iounmap(bitbang->dir); |
212 | kfree(bitbang); | |
976de6a8 SW |
213 | |
214 | return 0; | |
215 | } | |
216 | ||
217 | static struct of_device_id fs_enet_mdio_bb_match[] = { | |
218 | { | |
219 | .compatible = "fsl,cpm2-mdio-bitbang", | |
220 | }, | |
221 | {}, | |
222 | }; | |
e72701ac | 223 | MODULE_DEVICE_TABLE(of, fs_enet_mdio_bb_match); |
976de6a8 | 224 | |
74888760 | 225 | static struct platform_driver fs_enet_bb_mdio_driver = { |
4018294b GL |
226 | .driver = { |
227 | .name = "fsl-bb-mdio", | |
228 | .owner = THIS_MODULE, | |
229 | .of_match_table = fs_enet_mdio_bb_match, | |
230 | }, | |
976de6a8 SW |
231 | .probe = fs_enet_mdio_probe, |
232 | .remove = fs_enet_mdio_remove, | |
233 | }; | |
234 | ||
2b5b3a60 | 235 | static int fs_enet_mdio_bb_init(void) |
976de6a8 | 236 | { |
74888760 | 237 | return platform_driver_register(&fs_enet_bb_mdio_driver); |
976de6a8 SW |
238 | } |
239 | ||
2b5b3a60 | 240 | static void fs_enet_mdio_bb_exit(void) |
976de6a8 | 241 | { |
74888760 | 242 | platform_driver_unregister(&fs_enet_bb_mdio_driver); |
976de6a8 SW |
243 | } |
244 | ||
245 | module_init(fs_enet_mdio_bb_init); | |
246 | module_exit(fs_enet_mdio_bb_exit); |