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