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