1 /*
2  * Copyright (C) 2006-2007 PA Semi, Inc
3  *
4  * Author: Olof Johansson, PA Semi
5  *
6  * Maintained by: Olof Johansson <olof@lixom.net>
7  *
8  * Based on drivers/net/fs_enet/mii-bitbang.c.
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
22  */
23 
24 #include <linux/io.h>
25 #include <linux/module.h>
26 #include <linux/types.h>
27 #include <linux/sched.h>
28 #include <linux/errno.h>
29 #include <linux/ioport.h>
30 #include <linux/interrupt.h>
31 #include <linux/phy.h>
32 #include <linux/of_mdio.h>
33 #include <linux/of_platform.h>
34 
35 #define DELAY 1
36 
37 static void __iomem *gpio_regs;
38 
39 struct gpio_priv {
40 	int mdc_pin;
41 	int mdio_pin;
42 	int mdio_irqs[PHY_MAX_ADDR];
43 };
44 
45 #define MDC_PIN(bus)	(((struct gpio_priv *)bus->priv)->mdc_pin)
46 #define MDIO_PIN(bus)	(((struct gpio_priv *)bus->priv)->mdio_pin)
47 
48 static inline void mdio_lo(struct mii_bus *bus)
49 {
50 	out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus));
51 }
52 
53 static inline void mdio_hi(struct mii_bus *bus)
54 {
55 	out_le32(gpio_regs, 1 << MDIO_PIN(bus));
56 }
57 
58 static inline void mdc_lo(struct mii_bus *bus)
59 {
60 	out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus));
61 }
62 
63 static inline void mdc_hi(struct mii_bus *bus)
64 {
65 	out_le32(gpio_regs, 1 << MDC_PIN(bus));
66 }
67 
68 static inline void mdio_active(struct mii_bus *bus)
69 {
70 	out_le32(gpio_regs+0x20, (1 << MDC_PIN(bus)) | (1 << MDIO_PIN(bus)));
71 }
72 
73 static inline void mdio_tristate(struct mii_bus *bus)
74 {
75 	out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus)));
76 }
77 
78 static inline int mdio_read(struct mii_bus *bus)
79 {
80 	return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus)));
81 }
82 
83 static void clock_out(struct mii_bus *bus, int bit)
84 {
85 	if (bit)
86 		mdio_hi(bus);
87 	else
88 		mdio_lo(bus);
89 	udelay(DELAY);
90 	mdc_hi(bus);
91 	udelay(DELAY);
92 	mdc_lo(bus);
93 }
94 
95 /* Utility to send the preamble, address, and register (common to read and write). */
96 static void bitbang_pre(struct mii_bus *bus, int read, u8 addr, u8 reg)
97 {
98 	int i;
99 
100 	/* CFE uses a really long preamble (40 bits). We'll do the same. */
101 	mdio_active(bus);
102 	for (i = 0; i < 40; i++) {
103 		clock_out(bus, 1);
104 	}
105 
106 	/* send the start bit (01) and the read opcode (10) or write (10) */
107 	clock_out(bus, 0);
108 	clock_out(bus, 1);
109 
110 	clock_out(bus, read);
111 	clock_out(bus, !read);
112 
113 	/* send the PHY address */
114 	for (i = 0; i < 5; i++) {
115 		clock_out(bus, (addr & 0x10) != 0);
116 		addr <<= 1;
117 	}
118 
119 	/* send the register address */
120 	for (i = 0; i < 5; i++) {
121 		clock_out(bus, (reg & 0x10) != 0);
122 		reg <<= 1;
123 	}
124 }
125 
126 static int gpio_mdio_read(struct mii_bus *bus, int phy_id, int location)
127 {
128 	u16 rdreg;
129 	int ret, i;
130 	u8 addr = phy_id & 0xff;
131 	u8 reg = location & 0xff;
132 
133 	bitbang_pre(bus, 1, addr, reg);
134 
135 	/* tri-state our MDIO I/O pin so we can read */
136 	mdio_tristate(bus);
137 	udelay(DELAY);
138 	mdc_hi(bus);
139 	udelay(DELAY);
140 	mdc_lo(bus);
141 
142 	/* read 16 bits of register data, MSB first */
143 	rdreg = 0;
144 	for (i = 0; i < 16; i++) {
145 		mdc_lo(bus);
146 		udelay(DELAY);
147 		mdc_hi(bus);
148 		udelay(DELAY);
149 		mdc_lo(bus);
150 		udelay(DELAY);
151 		rdreg <<= 1;
152 		rdreg |= mdio_read(bus);
153 	}
154 
155 	mdc_hi(bus);
156 	udelay(DELAY);
157 	mdc_lo(bus);
158 	udelay(DELAY);
159 
160 	ret = rdreg;
161 
162 	return ret;
163 }
164 
165 static int gpio_mdio_write(struct mii_bus *bus, int phy_id, int location, u16 val)
166 {
167 	int i;
168 
169 	u8 addr = phy_id & 0xff;
170 	u8 reg = location & 0xff;
171 	u16 value = val & 0xffff;
172 
173 	bitbang_pre(bus, 0, addr, reg);
174 
175 	/* send the turnaround (10) */
176 	mdc_lo(bus);
177 	mdio_hi(bus);
178 	udelay(DELAY);
179 	mdc_hi(bus);
180 	udelay(DELAY);
181 	mdc_lo(bus);
182 	mdio_lo(bus);
183 	udelay(DELAY);
184 	mdc_hi(bus);
185 	udelay(DELAY);
186 
187 	/* write 16 bits of register data, MSB first */
188 	for (i = 0; i < 16; i++) {
189 		mdc_lo(bus);
190 		if (value & 0x8000)
191 			mdio_hi(bus);
192 		else
193 			mdio_lo(bus);
194 		udelay(DELAY);
195 		mdc_hi(bus);
196 		udelay(DELAY);
197 		value <<= 1;
198 	}
199 
200 	/*
201 	 * Tri-state the MDIO line.
202 	 */
203 	mdio_tristate(bus);
204 	mdc_lo(bus);
205 	udelay(DELAY);
206 	mdc_hi(bus);
207 	udelay(DELAY);
208 	return 0;
209 }
210 
211 static int gpio_mdio_reset(struct mii_bus *bus)
212 {
213 	/*nothing here - dunno how to reset it*/
214 	return 0;
215 }
216 
217 
218 static int __devinit gpio_mdio_probe(struct of_device *ofdev,
219 				     const struct of_device_id *match)
220 {
221 	struct device *dev = &ofdev->dev;
222 	struct device_node *np = ofdev->node;
223 	struct mii_bus *new_bus;
224 	struct gpio_priv *priv;
225 	const unsigned int *prop;
226 	int err;
227 
228 	err = -ENOMEM;
229 	priv = kzalloc(sizeof(struct gpio_priv), GFP_KERNEL);
230 	if (!priv)
231 		goto out;
232 
233 	new_bus = mdiobus_alloc();
234 
235 	if (!new_bus)
236 		goto out_free_priv;
237 
238 	new_bus->name = "pasemi gpio mdio bus";
239 	new_bus->read = &gpio_mdio_read;
240 	new_bus->write = &gpio_mdio_write;
241 	new_bus->reset = &gpio_mdio_reset;
242 
243 	prop = of_get_property(np, "reg", NULL);
244 	snprintf(new_bus->id, MII_BUS_ID_SIZE, "%x", *prop);
245 	new_bus->priv = priv;
246 
247 	new_bus->irq = priv->mdio_irqs;
248 
249 	prop = of_get_property(np, "mdc-pin", NULL);
250 	priv->mdc_pin = *prop;
251 
252 	prop = of_get_property(np, "mdio-pin", NULL);
253 	priv->mdio_pin = *prop;
254 
255 	new_bus->parent = dev;
256 	dev_set_drvdata(dev, new_bus);
257 
258 	err = of_mdiobus_register(new_bus, np);
259 
260 	if (err != 0) {
261 		printk(KERN_ERR "%s: Cannot register as MDIO bus, err %d\n",
262 				new_bus->name, err);
263 		goto out_free_irq;
264 	}
265 
266 	return 0;
267 
268 out_free_irq:
269 	kfree(new_bus);
270 out_free_priv:
271 	kfree(priv);
272 out:
273 	return err;
274 }
275 
276 
277 static int gpio_mdio_remove(struct of_device *dev)
278 {
279 	struct mii_bus *bus = dev_get_drvdata(&dev->dev);
280 
281 	mdiobus_unregister(bus);
282 
283 	dev_set_drvdata(&dev->dev, NULL);
284 
285 	kfree(bus->priv);
286 	bus->priv = NULL;
287 	mdiobus_free(bus);
288 
289 	return 0;
290 }
291 
292 static struct of_device_id gpio_mdio_match[] =
293 {
294 	{
295 		.compatible      = "gpio-mdio",
296 	},
297 	{},
298 };
299 MODULE_DEVICE_TABLE(of, gpio_mdio_match);
300 
301 static struct of_platform_driver gpio_mdio_driver =
302 {
303 	.match_table	= gpio_mdio_match,
304 	.probe		= gpio_mdio_probe,
305 	.remove		= gpio_mdio_remove,
306 	.driver		= {
307 		.name	= "gpio-mdio-bitbang",
308 	},
309 };
310 
311 int gpio_mdio_init(void)
312 {
313 	struct device_node *np;
314 
315 	np = of_find_compatible_node(NULL, NULL, "1682m-gpio");
316 	if (!np)
317 		np = of_find_compatible_node(NULL, NULL,
318 					     "pasemi,pwrficient-gpio");
319 	if (!np)
320 		return -ENODEV;
321 	gpio_regs = of_iomap(np, 0);
322 	of_node_put(np);
323 
324 	if (!gpio_regs)
325 		return -ENODEV;
326 
327 	return of_register_platform_driver(&gpio_mdio_driver);
328 }
329 module_init(gpio_mdio_init);
330 
331 void gpio_mdio_exit(void)
332 {
333 	of_unregister_platform_driver(&gpio_mdio_driver);
334 	if (gpio_regs)
335 		iounmap(gpio_regs);
336 }
337 module_exit(gpio_mdio_exit);
338 
339 MODULE_LICENSE("GPL");
340 MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
341 MODULE_DESCRIPTION("Driver for MDIO over GPIO on PA Semi PWRficient-based boards");
342