]>
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> | |
2b5b3a60 | 18 | #include <linux/interrupt.h> |
48257c4f PA |
19 | #include <linux/netdevice.h> |
20 | #include <linux/etherdevice.h> | |
48257c4f | 21 | #include <linux/mii.h> |
5b4b8454 | 22 | #include <linux/platform_device.h> |
2b5b3a60 | 23 | #include <linux/mdio-bitbang.h> |
5af50730 | 24 | #include <linux/of_address.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 | |
1dd06ae8 | 111 | static int fs_mii_bitbang_init(struct mii_bus *bus, struct device_node *np) |
48257c4f | 112 | { |
976de6a8 SW |
113 | struct resource res; |
114 | const u32 *data; | |
115 | int mdio_pin, mdc_pin, len; | |
116 | struct bb_info *bitbang = bus->priv; | |
48257c4f | 117 | |
976de6a8 SW |
118 | int ret = of_address_to_resource(np, 0, &res); |
119 | if (ret) | |
120 | return ret; | |
121 | ||
28f65c11 | 122 | if (resource_size(&res) <= 13) |
976de6a8 SW |
123 | return -ENODEV; |
124 | ||
125 | /* This should really encode the pin number as well, but all | |
126 | * we get is an int, and the odds of multiple bitbang mdio buses | |
127 | * is low enough that it's not worth going too crazy. | |
128 | */ | |
9d9326d3 | 129 | snprintf(bus->id, MII_BUS_ID_SIZE, "%x", res.start); |
976de6a8 SW |
130 | |
131 | data = of_get_property(np, "fsl,mdio-pin", &len); | |
132 | if (!data || len != 4) | |
133 | return -ENODEV; | |
134 | mdio_pin = *data; | |
135 | ||
136 | data = of_get_property(np, "fsl,mdc-pin", &len); | |
137 | if (!data || len != 4) | |
138 | return -ENODEV; | |
139 | mdc_pin = *data; | |
140 | ||
28f65c11 | 141 | bitbang->dir = ioremap(res.start, resource_size(&res)); |
976de6a8 SW |
142 | if (!bitbang->dir) |
143 | return -ENOMEM; | |
144 | ||
145 | bitbang->dat = bitbang->dir + 4; | |
146 | bitbang->mdio_msk = 1 << (31 - mdio_pin); | |
147 | bitbang->mdc_msk = 1 << (31 - mdc_pin); | |
976de6a8 SW |
148 | |
149 | return 0; | |
150 | } | |
151 | ||
4f2c53ea | 152 | static int fs_enet_mdio_probe(struct platform_device *ofdev) |
976de6a8 | 153 | { |
976de6a8 SW |
154 | struct mii_bus *new_bus; |
155 | struct bb_info *bitbang; | |
156 | int ret = -ENOMEM; | |
976de6a8 | 157 | |
976de6a8 SW |
158 | bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL); |
159 | if (!bitbang) | |
2b5b3a60 SW |
160 | goto out; |
161 | ||
162 | bitbang->ctrl.ops = &bb_ops; | |
163 | ||
164 | new_bus = alloc_mdio_bitbang(&bitbang->ctrl); | |
165 | if (!new_bus) | |
166 | goto out_free_priv; | |
976de6a8 | 167 | |
976de6a8 | 168 | new_bus->name = "CPM2 Bitbanged MII", |
976de6a8 | 169 | |
4eecb178 | 170 | ret = fs_mii_bitbang_init(new_bus, ofdev->dev.of_node); |
976de6a8 | 171 | if (ret) |
2b5b3a60 | 172 | goto out_free_bus; |
976de6a8 SW |
173 | |
174 | new_bus->phy_mask = ~0; | |
976de6a8 | 175 | |
18ee49dd | 176 | new_bus->parent = &ofdev->dev; |
8513fbd8 | 177 | platform_set_drvdata(ofdev, new_bus); |
976de6a8 | 178 | |
4eecb178 | 179 | ret = of_mdiobus_register(new_bus, ofdev->dev.of_node); |
976de6a8 | 180 | if (ret) |
e7f4dc35 | 181 | goto out_unmap_regs; |
976de6a8 SW |
182 | |
183 | return 0; | |
184 | ||
976de6a8 SW |
185 | out_unmap_regs: |
186 | iounmap(bitbang->dir); | |
976de6a8 | 187 | out_free_bus: |
2b5b3a60 | 188 | free_mdio_bitbang(new_bus); |
298cf9be LB |
189 | out_free_priv: |
190 | kfree(bitbang); | |
976de6a8 SW |
191 | out: |
192 | return ret; | |
193 | } | |
194 | ||
2dc11581 | 195 | static int fs_enet_mdio_remove(struct platform_device *ofdev) |
976de6a8 | 196 | { |
8513fbd8 | 197 | struct mii_bus *bus = platform_get_drvdata(ofdev); |
976de6a8 SW |
198 | struct bb_info *bitbang = bus->priv; |
199 | ||
200 | mdiobus_unregister(bus); | |
298cf9be | 201 | free_mdio_bitbang(bus); |
976de6a8 SW |
202 | iounmap(bitbang->dir); |
203 | kfree(bitbang); | |
976de6a8 SW |
204 | |
205 | return 0; | |
206 | } | |
207 | ||
94e5a2a8 | 208 | static const struct of_device_id fs_enet_mdio_bb_match[] = { |
976de6a8 SW |
209 | { |
210 | .compatible = "fsl,cpm2-mdio-bitbang", | |
211 | }, | |
212 | {}, | |
213 | }; | |
e72701ac | 214 | MODULE_DEVICE_TABLE(of, fs_enet_mdio_bb_match); |
976de6a8 | 215 | |
74888760 | 216 | static struct platform_driver fs_enet_bb_mdio_driver = { |
4018294b GL |
217 | .driver = { |
218 | .name = "fsl-bb-mdio", | |
4018294b GL |
219 | .of_match_table = fs_enet_mdio_bb_match, |
220 | }, | |
976de6a8 SW |
221 | .probe = fs_enet_mdio_probe, |
222 | .remove = fs_enet_mdio_remove, | |
223 | }; | |
224 | ||
db62f684 | 225 | module_platform_driver(fs_enet_bb_mdio_driver); |