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 <olof@lixom.net>
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
27static void __iomem *gpio_regs;
28
29struct 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
37static inline void mdio_lo(struct mii_bus *bus)
38{
39	out_le32(gpio_regs+0x10, 1 << MDIO_PIN(bus));
40}
41
42static inline void mdio_hi(struct mii_bus *bus)
43{
44	out_le32(gpio_regs, 1 << MDIO_PIN(bus));
45}
46
47static inline void mdc_lo(struct mii_bus *bus)
48{
49	out_le32(gpio_regs+0x10, 1 << MDC_PIN(bus));
50}
51
52static inline void mdc_hi(struct mii_bus *bus)
53{
54	out_le32(gpio_regs, 1 << MDC_PIN(bus));
55}
56
57static 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
62static inline void mdio_tristate(struct mii_bus *bus)
63{
64	out_le32(gpio_regs+0x30, (1 << MDIO_PIN(bus)));
65}
66
67static inline int mdio_read(struct mii_bus *bus)
68{
69	return !!(in_le32(gpio_regs+0x40) & (1 << MDIO_PIN(bus)));
70}
71
72static 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). */
85static 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
115static 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
154static 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
200static int gpio_mdio_reset(struct mii_bus *bus)
201{
202	/*nothing here - dunno how to reset it*/
203	return 0;
204}
205
206
207static 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
254out_free_irq:
255	kfree(new_bus);
256out_free_priv:
257	kfree(priv);
258out:
259	return err;
260}
261
262
263static 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
276static const struct of_device_id gpio_mdio_match[] =
277{
278	{
279		.compatible      = "gpio-mdio",
280	},
281	{},
282};
283MODULE_DEVICE_TABLE(of, gpio_mdio_match);
284
285static struct platform_driver gpio_mdio_driver =
286{
287	.probe		= gpio_mdio_probe,
288	.remove_new	= gpio_mdio_remove,
289	.driver = {
290		.name = "gpio-mdio-bitbang",
291		.of_match_table = gpio_mdio_match,
292	},
293};
294
295static 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}
313module_init(gpio_mdio_init);
314
315static void __exit gpio_mdio_exit(void)
316{
317	platform_driver_unregister(&gpio_mdio_driver);
318	if (gpio_regs)
319		iounmap(gpio_regs);
320}
321module_exit(gpio_mdio_exit);
322
323MODULE_LICENSE("GPL");
324MODULE_AUTHOR("Olof Johansson <olof@lixom.net>");
325MODULE_DESCRIPTION("Driver for MDIO over GPIO on PA Semi PWRficient-based boards");
326