File Index
Files and directories
Sign in
FILE

gpio_mdio.c

File preview with syntax highlighting.

linux-6.19.14/arch/powerpc/platforms/pasemi/gpio_mdio.c C
  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		= 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");
~/ Read-only file preview.