Commit | Line | Data |
---|---|---|
a5edeccb LP |
1 | /* |
2 | * OpenFirmware GPIO based MDIO bitbang driver. | |
3 | * | |
4 | * Copyright (c) 2008 CSE Semaphore Belgium. | |
5 | * by Laurent Pinchart <laurentp@cse-semaphore.com> | |
6 | * | |
7 | * Based on earlier work by | |
8 | * | |
9 | * Copyright (c) 2003 Intracom S.A. | |
10 | * by Pantelis Antoniou <panto@intracom.gr> | |
11 | * | |
12 | * 2005 (c) MontaVista Software, Inc. | |
13 | * Vitaly Bordug <vbordug@ru.mvista.com> | |
14 | * | |
15 | * This file is licensed under the terms of the GNU General Public License | |
16 | * version 2. This program is licensed "as is" without any warranty of any | |
17 | * kind, whether express or implied. | |
18 | */ | |
19 | ||
20 | #include <linux/module.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/init.h> | |
23 | #include <linux/interrupt.h> | |
24 | #include <linux/mdio-bitbang.h> | |
25 | #include <linux/of_gpio.h> | |
26 | #include <linux/of_platform.h> | |
27 | ||
28 | struct mdio_gpio_info { | |
29 | struct mdiobb_ctrl ctrl; | |
30 | int mdc, mdio; | |
31 | }; | |
32 | ||
33 | static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir) | |
34 | { | |
35 | struct mdio_gpio_info *bitbang = | |
36 | container_of(ctrl, struct mdio_gpio_info, ctrl); | |
37 | ||
38 | if (dir) | |
39 | gpio_direction_output(bitbang->mdio, 1); | |
40 | else | |
41 | gpio_direction_input(bitbang->mdio); | |
42 | } | |
43 | ||
44 | static int mdio_read(struct mdiobb_ctrl *ctrl) | |
45 | { | |
46 | struct mdio_gpio_info *bitbang = | |
47 | container_of(ctrl, struct mdio_gpio_info, ctrl); | |
48 | ||
49 | return gpio_get_value(bitbang->mdio); | |
50 | } | |
51 | ||
52 | static void mdio(struct mdiobb_ctrl *ctrl, int what) | |
53 | { | |
54 | struct mdio_gpio_info *bitbang = | |
55 | container_of(ctrl, struct mdio_gpio_info, ctrl); | |
56 | ||
57 | gpio_set_value(bitbang->mdio, what); | |
58 | } | |
59 | ||
60 | static void mdc(struct mdiobb_ctrl *ctrl, int what) | |
61 | { | |
62 | struct mdio_gpio_info *bitbang = | |
63 | container_of(ctrl, struct mdio_gpio_info, ctrl); | |
64 | ||
65 | gpio_set_value(bitbang->mdc, what); | |
66 | } | |
67 | ||
68 | static struct mdiobb_ops mdio_gpio_ops = { | |
69 | .owner = THIS_MODULE, | |
70 | .set_mdc = mdc, | |
71 | .set_mdio_dir = mdio_dir, | |
72 | .set_mdio_data = mdio, | |
73 | .get_mdio_data = mdio_read, | |
74 | }; | |
75 | ||
76 | static int __devinit mdio_ofgpio_bitbang_init(struct mii_bus *bus, | |
77 | struct device_node *np) | |
78 | { | |
79 | struct mdio_gpio_info *bitbang = bus->priv; | |
80 | ||
81 | bitbang->mdc = of_get_gpio(np, 0); | |
82 | bitbang->mdio = of_get_gpio(np, 1); | |
83 | ||
84 | if (bitbang->mdc < 0 || bitbang->mdio < 0) | |
85 | return -ENODEV; | |
86 | ||
87 | snprintf(bus->id, MII_BUS_ID_SIZE, "%x", bitbang->mdc); | |
88 | return 0; | |
89 | } | |
90 | ||
91 | static void __devinit add_phy(struct mii_bus *bus, struct device_node *np) | |
92 | { | |
93 | const u32 *data; | |
94 | int len, id, irq; | |
95 | ||
96 | data = of_get_property(np, "reg", &len); | |
97 | if (!data || len != 4) | |
98 | return; | |
99 | ||
100 | id = *data; | |
101 | bus->phy_mask &= ~(1 << id); | |
102 | ||
103 | irq = of_irq_to_resource(np, 0, NULL); | |
104 | if (irq != NO_IRQ) | |
105 | bus->irq[id] = irq; | |
106 | } | |
107 | ||
108 | static int __devinit mdio_ofgpio_probe(struct of_device *ofdev, | |
109 | const struct of_device_id *match) | |
110 | { | |
111 | struct device_node *np = NULL; | |
112 | struct mii_bus *new_bus; | |
113 | struct mdio_gpio_info *bitbang; | |
114 | int ret = -ENOMEM; | |
115 | int i; | |
116 | ||
117 | bitbang = kzalloc(sizeof(struct mdio_gpio_info), GFP_KERNEL); | |
118 | if (!bitbang) | |
119 | goto out; | |
120 | ||
121 | bitbang->ctrl.ops = &mdio_gpio_ops; | |
122 | ||
123 | new_bus = alloc_mdio_bitbang(&bitbang->ctrl); | |
124 | if (!new_bus) | |
298cf9be | 125 | goto out_free_bitbang; |
a5edeccb LP |
126 | |
127 | new_bus->name = "GPIO Bitbanged MII", | |
128 | ||
129 | ret = mdio_ofgpio_bitbang_init(new_bus, ofdev->node); | |
130 | if (ret) | |
131 | goto out_free_bus; | |
132 | ||
133 | new_bus->phy_mask = ~0; | |
134 | new_bus->irq = kmalloc(sizeof(int) * PHY_MAX_ADDR, GFP_KERNEL); | |
135 | if (!new_bus->irq) | |
136 | goto out_free_bus; | |
137 | ||
138 | for (i = 0; i < PHY_MAX_ADDR; i++) | |
139 | new_bus->irq[i] = -1; | |
140 | ||
141 | while ((np = of_get_next_child(ofdev->node, np))) | |
142 | if (!strcmp(np->type, "ethernet-phy")) | |
143 | add_phy(new_bus, np); | |
144 | ||
18ee49dd | 145 | new_bus->parent = &ofdev->dev; |
a5edeccb LP |
146 | dev_set_drvdata(&ofdev->dev, new_bus); |
147 | ||
148 | ret = mdiobus_register(new_bus); | |
149 | if (ret) | |
150 | goto out_free_irqs; | |
151 | ||
152 | return 0; | |
153 | ||
154 | out_free_irqs: | |
155 | dev_set_drvdata(&ofdev->dev, NULL); | |
156 | kfree(new_bus->irq); | |
157 | out_free_bus: | |
a5edeccb | 158 | free_mdio_bitbang(new_bus); |
298cf9be LB |
159 | out_free_bitbang: |
160 | kfree(bitbang); | |
a5edeccb LP |
161 | out: |
162 | return ret; | |
163 | } | |
164 | ||
165 | static int mdio_ofgpio_remove(struct of_device *ofdev) | |
166 | { | |
167 | struct mii_bus *bus = dev_get_drvdata(&ofdev->dev); | |
168 | struct mdio_gpio_info *bitbang = bus->priv; | |
169 | ||
170 | mdiobus_unregister(bus); | |
298cf9be | 171 | kfree(bus->irq); |
a5edeccb LP |
172 | free_mdio_bitbang(bus); |
173 | dev_set_drvdata(&ofdev->dev, NULL); | |
a5edeccb | 174 | kfree(bitbang); |
a5edeccb LP |
175 | |
176 | return 0; | |
177 | } | |
178 | ||
179 | static struct of_device_id mdio_ofgpio_match[] = { | |
180 | { | |
181 | .compatible = "virtual,mdio-gpio", | |
182 | }, | |
183 | {}, | |
184 | }; | |
185 | ||
186 | static struct of_platform_driver mdio_ofgpio_driver = { | |
187 | .name = "mdio-gpio", | |
188 | .match_table = mdio_ofgpio_match, | |
189 | .probe = mdio_ofgpio_probe, | |
190 | .remove = mdio_ofgpio_remove, | |
191 | }; | |
192 | ||
193 | static int mdio_ofgpio_init(void) | |
194 | { | |
195 | return of_register_platform_driver(&mdio_ofgpio_driver); | |
196 | } | |
197 | ||
198 | static void mdio_ofgpio_exit(void) | |
199 | { | |
200 | of_unregister_platform_driver(&mdio_ofgpio_driver); | |
201 | } | |
202 | ||
203 | module_init(mdio_ofgpio_init); | |
204 | module_exit(mdio_ofgpio_exit); |