1ec21e2ecSJeff Kirsher /*
2ec21e2ecSJeff Kirsher * Combined Ethernet driver for Motorola MPC8xx and MPC82xx.
3ec21e2ecSJeff Kirsher *
4ec21e2ecSJeff Kirsher * Copyright (c) 2003 Intracom S.A.
5ec21e2ecSJeff Kirsher * by Pantelis Antoniou <panto@intracom.gr>
6ec21e2ecSJeff Kirsher *
7ec21e2ecSJeff Kirsher * 2005 (c) MontaVista Software, Inc.
8ec21e2ecSJeff Kirsher * Vitaly Bordug <vbordug@ru.mvista.com>
9ec21e2ecSJeff Kirsher *
10ec21e2ecSJeff Kirsher * This file is licensed under the terms of the GNU General Public License
11ec21e2ecSJeff Kirsher * version 2. This program is licensed "as is" without any warranty of any
12ec21e2ecSJeff Kirsher * kind, whether express or implied.
13ec21e2ecSJeff Kirsher */
14ec21e2ecSJeff Kirsher
15ec21e2ecSJeff Kirsher #include <linux/module.h>
16ec21e2ecSJeff Kirsher #include <linux/ioport.h>
17ec21e2ecSJeff Kirsher #include <linux/slab.h>
18ec21e2ecSJeff Kirsher #include <linux/interrupt.h>
19ec21e2ecSJeff Kirsher #include <linux/netdevice.h>
20ec21e2ecSJeff Kirsher #include <linux/etherdevice.h>
21ec21e2ecSJeff Kirsher #include <linux/mii.h>
22ec21e2ecSJeff Kirsher #include <linux/platform_device.h>
23ec21e2ecSJeff Kirsher #include <linux/mdio-bitbang.h>
245af50730SRob Herring #include <linux/of_address.h>
25ec21e2ecSJeff Kirsher #include <linux/of_mdio.h>
26ec21e2ecSJeff Kirsher #include <linux/of_platform.h>
27ec21e2ecSJeff Kirsher
28ec21e2ecSJeff Kirsher #include "fs_enet.h"
29ec21e2ecSJeff Kirsher
30ec21e2ecSJeff Kirsher struct bb_info {
31ec21e2ecSJeff Kirsher struct mdiobb_ctrl ctrl;
32ae9e78a9SChristophe Leroy u32 __iomem *dir;
33ae9e78a9SChristophe Leroy u32 __iomem *dat;
34ec21e2ecSJeff Kirsher u32 mdio_msk;
35ec21e2ecSJeff Kirsher u32 mdc_msk;
36ec21e2ecSJeff Kirsher };
37ec21e2ecSJeff Kirsher
38ec21e2ecSJeff Kirsher /* FIXME: If any other users of GPIO crop up, then these will have to
39ec21e2ecSJeff Kirsher * have some sort of global synchronization to avoid races with other
40ec21e2ecSJeff Kirsher * pins on the same port. The ideal solution would probably be to
41ec21e2ecSJeff Kirsher * bind the ports to a GPIO driver, and have this be a client of it.
42ec21e2ecSJeff Kirsher */
bb_set(u32 __iomem * p,u32 m)43ec21e2ecSJeff Kirsher static inline void bb_set(u32 __iomem *p, u32 m)
44ec21e2ecSJeff Kirsher {
45ec21e2ecSJeff Kirsher out_be32(p, in_be32(p) | m);
46ec21e2ecSJeff Kirsher }
47ec21e2ecSJeff Kirsher
bb_clr(u32 __iomem * p,u32 m)48ec21e2ecSJeff Kirsher static inline void bb_clr(u32 __iomem *p, u32 m)
49ec21e2ecSJeff Kirsher {
50ec21e2ecSJeff Kirsher out_be32(p, in_be32(p) & ~m);
51ec21e2ecSJeff Kirsher }
52ec21e2ecSJeff Kirsher
bb_read(u32 __iomem * p,u32 m)53ec21e2ecSJeff Kirsher static inline int bb_read(u32 __iomem *p, u32 m)
54ec21e2ecSJeff Kirsher {
55ec21e2ecSJeff Kirsher return (in_be32(p) & m) != 0;
56ec21e2ecSJeff Kirsher }
57ec21e2ecSJeff Kirsher
mdio_dir(struct mdiobb_ctrl * ctrl,int dir)58ec21e2ecSJeff Kirsher static inline void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)
59ec21e2ecSJeff Kirsher {
60ec21e2ecSJeff Kirsher struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
61ec21e2ecSJeff Kirsher
62ec21e2ecSJeff Kirsher if (dir)
63ec21e2ecSJeff Kirsher bb_set(bitbang->dir, bitbang->mdio_msk);
64ec21e2ecSJeff Kirsher else
65ec21e2ecSJeff Kirsher bb_clr(bitbang->dir, bitbang->mdio_msk);
66ec21e2ecSJeff Kirsher
67ec21e2ecSJeff Kirsher /* Read back to flush the write. */
68ec21e2ecSJeff Kirsher in_be32(bitbang->dir);
69ec21e2ecSJeff Kirsher }
70ec21e2ecSJeff Kirsher
mdio_read(struct mdiobb_ctrl * ctrl)71ec21e2ecSJeff Kirsher static inline int mdio_read(struct mdiobb_ctrl *ctrl)
72ec21e2ecSJeff Kirsher {
73ec21e2ecSJeff Kirsher struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
74ec21e2ecSJeff Kirsher return bb_read(bitbang->dat, bitbang->mdio_msk);
75ec21e2ecSJeff Kirsher }
76ec21e2ecSJeff Kirsher
mdio(struct mdiobb_ctrl * ctrl,int what)77ec21e2ecSJeff Kirsher static inline void mdio(struct mdiobb_ctrl *ctrl, int what)
78ec21e2ecSJeff Kirsher {
79ec21e2ecSJeff Kirsher struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
80ec21e2ecSJeff Kirsher
81ec21e2ecSJeff Kirsher if (what)
82ec21e2ecSJeff Kirsher bb_set(bitbang->dat, bitbang->mdio_msk);
83ec21e2ecSJeff Kirsher else
84ec21e2ecSJeff Kirsher bb_clr(bitbang->dat, bitbang->mdio_msk);
85ec21e2ecSJeff Kirsher
86ec21e2ecSJeff Kirsher /* Read back to flush the write. */
87ec21e2ecSJeff Kirsher in_be32(bitbang->dat);
88ec21e2ecSJeff Kirsher }
89ec21e2ecSJeff Kirsher
mdc(struct mdiobb_ctrl * ctrl,int what)90ec21e2ecSJeff Kirsher static inline void mdc(struct mdiobb_ctrl *ctrl, int what)
91ec21e2ecSJeff Kirsher {
92ec21e2ecSJeff Kirsher struct bb_info *bitbang = container_of(ctrl, struct bb_info, ctrl);
93ec21e2ecSJeff Kirsher
94ec21e2ecSJeff Kirsher if (what)
95ec21e2ecSJeff Kirsher bb_set(bitbang->dat, bitbang->mdc_msk);
96ec21e2ecSJeff Kirsher else
97ec21e2ecSJeff Kirsher bb_clr(bitbang->dat, bitbang->mdc_msk);
98ec21e2ecSJeff Kirsher
99ec21e2ecSJeff Kirsher /* Read back to flush the write. */
100ec21e2ecSJeff Kirsher in_be32(bitbang->dat);
101ec21e2ecSJeff Kirsher }
102ec21e2ecSJeff Kirsher
10394494733SBhumika Goyal static const struct mdiobb_ops bb_ops = {
104ec21e2ecSJeff Kirsher .owner = THIS_MODULE,
105ec21e2ecSJeff Kirsher .set_mdc = mdc,
106ec21e2ecSJeff Kirsher .set_mdio_dir = mdio_dir,
107ec21e2ecSJeff Kirsher .set_mdio_data = mdio,
108ec21e2ecSJeff Kirsher .get_mdio_data = mdio_read,
109ec21e2ecSJeff Kirsher };
110ec21e2ecSJeff Kirsher
fs_mii_bitbang_init(struct mii_bus * bus,struct device_node * np)1111dd06ae8SGreg Kroah-Hartman static int fs_mii_bitbang_init(struct mii_bus *bus, struct device_node *np)
112ec21e2ecSJeff Kirsher {
113ec21e2ecSJeff Kirsher struct resource res;
114ec21e2ecSJeff Kirsher const u32 *data;
115ec21e2ecSJeff Kirsher int mdio_pin, mdc_pin, len;
116ec21e2ecSJeff Kirsher struct bb_info *bitbang = bus->priv;
117ec21e2ecSJeff Kirsher
118ec21e2ecSJeff Kirsher int ret = of_address_to_resource(np, 0, &res);
119ec21e2ecSJeff Kirsher if (ret)
120ec21e2ecSJeff Kirsher return ret;
121ec21e2ecSJeff Kirsher
122ec21e2ecSJeff Kirsher if (resource_size(&res) <= 13)
123ec21e2ecSJeff Kirsher return -ENODEV;
124ec21e2ecSJeff Kirsher
125ec21e2ecSJeff Kirsher /* This should really encode the pin number as well, but all
126ec21e2ecSJeff Kirsher * we get is an int, and the odds of multiple bitbang mdio buses
127ec21e2ecSJeff Kirsher * is low enough that it's not worth going too crazy.
128ec21e2ecSJeff Kirsher */
129*159c6df9SSimon Horman snprintf(bus->id, MII_BUS_ID_SIZE, "%pa", &res.start);
130ec21e2ecSJeff Kirsher
131ec21e2ecSJeff Kirsher data = of_get_property(np, "fsl,mdio-pin", &len);
132ec21e2ecSJeff Kirsher if (!data || len != 4)
133ec21e2ecSJeff Kirsher return -ENODEV;
134ec21e2ecSJeff Kirsher mdio_pin = *data;
135ec21e2ecSJeff Kirsher
136ec21e2ecSJeff Kirsher data = of_get_property(np, "fsl,mdc-pin", &len);
137ec21e2ecSJeff Kirsher if (!data || len != 4)
138ec21e2ecSJeff Kirsher return -ENODEV;
139ec21e2ecSJeff Kirsher mdc_pin = *data;
140ec21e2ecSJeff Kirsher
141ec21e2ecSJeff Kirsher bitbang->dir = ioremap(res.start, resource_size(&res));
142ec21e2ecSJeff Kirsher if (!bitbang->dir)
143ec21e2ecSJeff Kirsher return -ENOMEM;
144ec21e2ecSJeff Kirsher
145ec21e2ecSJeff Kirsher bitbang->dat = bitbang->dir + 4;
146ec21e2ecSJeff Kirsher bitbang->mdio_msk = 1 << (31 - mdio_pin);
147ec21e2ecSJeff Kirsher bitbang->mdc_msk = 1 << (31 - mdc_pin);
148ec21e2ecSJeff Kirsher
149ec21e2ecSJeff Kirsher return 0;
150ec21e2ecSJeff Kirsher }
151ec21e2ecSJeff Kirsher
fs_enet_mdio_probe(struct platform_device * ofdev)1524f2c53eaSBill Pemberton static int fs_enet_mdio_probe(struct platform_device *ofdev)
153ec21e2ecSJeff Kirsher {
154ec21e2ecSJeff Kirsher struct mii_bus *new_bus;
155ec21e2ecSJeff Kirsher struct bb_info *bitbang;
156ec21e2ecSJeff Kirsher int ret = -ENOMEM;
157ec21e2ecSJeff Kirsher
158ec21e2ecSJeff Kirsher bitbang = kzalloc(sizeof(struct bb_info), GFP_KERNEL);
159ec21e2ecSJeff Kirsher if (!bitbang)
160ec21e2ecSJeff Kirsher goto out;
161ec21e2ecSJeff Kirsher
162ec21e2ecSJeff Kirsher bitbang->ctrl.ops = &bb_ops;
163ec21e2ecSJeff Kirsher
164ec21e2ecSJeff Kirsher new_bus = alloc_mdio_bitbang(&bitbang->ctrl);
165ec21e2ecSJeff Kirsher if (!new_bus)
166ec21e2ecSJeff Kirsher goto out_free_priv;
167ec21e2ecSJeff Kirsher
168ec21e2ecSJeff Kirsher new_bus->name = "CPM2 Bitbanged MII",
169ec21e2ecSJeff Kirsher
170ec21e2ecSJeff Kirsher ret = fs_mii_bitbang_init(new_bus, ofdev->dev.of_node);
171ec21e2ecSJeff Kirsher if (ret)
172ec21e2ecSJeff Kirsher goto out_free_bus;
173ec21e2ecSJeff Kirsher
174ec21e2ecSJeff Kirsher new_bus->phy_mask = ~0;
175ec21e2ecSJeff Kirsher
176ec21e2ecSJeff Kirsher new_bus->parent = &ofdev->dev;
1778513fbd8SJingoo Han platform_set_drvdata(ofdev, new_bus);
178ec21e2ecSJeff Kirsher
179ec21e2ecSJeff Kirsher ret = of_mdiobus_register(new_bus, ofdev->dev.of_node);
180ec21e2ecSJeff Kirsher if (ret)
181e7f4dc35SAndrew Lunn goto out_unmap_regs;
182ec21e2ecSJeff Kirsher
183ec21e2ecSJeff Kirsher return 0;
184ec21e2ecSJeff Kirsher
185ec21e2ecSJeff Kirsher out_unmap_regs:
186ec21e2ecSJeff Kirsher iounmap(bitbang->dir);
187ec21e2ecSJeff Kirsher out_free_bus:
188ec21e2ecSJeff Kirsher free_mdio_bitbang(new_bus);
189ec21e2ecSJeff Kirsher out_free_priv:
190ec21e2ecSJeff Kirsher kfree(bitbang);
191ec21e2ecSJeff Kirsher out:
192ec21e2ecSJeff Kirsher return ret;
193ec21e2ecSJeff Kirsher }
194ec21e2ecSJeff Kirsher
fs_enet_mdio_remove(struct platform_device * ofdev)195ead29c5eSUwe Kleine-König static void fs_enet_mdio_remove(struct platform_device *ofdev)
196ec21e2ecSJeff Kirsher {
1978513fbd8SJingoo Han struct mii_bus *bus = platform_get_drvdata(ofdev);
198ec21e2ecSJeff Kirsher struct bb_info *bitbang = bus->priv;
199ec21e2ecSJeff Kirsher
200ec21e2ecSJeff Kirsher mdiobus_unregister(bus);
201ec21e2ecSJeff Kirsher free_mdio_bitbang(bus);
202ec21e2ecSJeff Kirsher iounmap(bitbang->dir);
203ec21e2ecSJeff Kirsher kfree(bitbang);
204ec21e2ecSJeff Kirsher }
205ec21e2ecSJeff Kirsher
20694e5a2a8SFabian Frederick static const struct of_device_id fs_enet_mdio_bb_match[] = {
207ec21e2ecSJeff Kirsher {
208ec21e2ecSJeff Kirsher .compatible = "fsl,cpm2-mdio-bitbang",
209ec21e2ecSJeff Kirsher },
210ec21e2ecSJeff Kirsher {},
211ec21e2ecSJeff Kirsher };
212ec21e2ecSJeff Kirsher MODULE_DEVICE_TABLE(of, fs_enet_mdio_bb_match);
213ec21e2ecSJeff Kirsher
214ec21e2ecSJeff Kirsher static struct platform_driver fs_enet_bb_mdio_driver = {
215ec21e2ecSJeff Kirsher .driver = {
216ec21e2ecSJeff Kirsher .name = "fsl-bb-mdio",
217ec21e2ecSJeff Kirsher .of_match_table = fs_enet_mdio_bb_match,
218ec21e2ecSJeff Kirsher },
219ec21e2ecSJeff Kirsher .probe = fs_enet_mdio_probe,
220ead29c5eSUwe Kleine-König .remove_new = fs_enet_mdio_remove,
221ec21e2ecSJeff Kirsher };
222ec21e2ecSJeff Kirsher
223db62f684SAxel Lin module_platform_driver(fs_enet_bb_mdio_driver);
224445c6198SMichael Ellerman MODULE_LICENSE("GPL");
225