blob: 4e4c8daf44c308285201ecc051bc4e535e03036e [file] [log] [blame]
Laurent Pincharta5edecc2008-05-26 11:53:21 +02001/*
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +00002 * GPIO based MDIO bitbang driver.
3 * Supports OpenFirmware.
Laurent Pincharta5edecc2008-05-26 11:53:21 +02004 *
5 * Copyright (c) 2008 CSE Semaphore Belgium.
6 * by Laurent Pinchart <laurentp@cse-semaphore.com>
7 *
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +00008 * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
9 *
Laurent Pincharta5edecc2008-05-26 11:53:21 +020010 * Based on earlier work by
11 *
12 * Copyright (c) 2003 Intracom S.A.
13 * by Pantelis Antoniou <panto@intracom.gr>
14 *
15 * 2005 (c) MontaVista Software, Inc.
16 * Vitaly Bordug <vbordug@ru.mvista.com>
17 *
18 * This file is licensed under the terms of the GNU General Public License
19 * version 2. This program is licensed "as is" without any warranty of any
20 * kind, whether express or implied.
21 */
22
23#include <linux/module.h>
24#include <linux/slab.h>
Laurent Pincharta5edecc2008-05-26 11:53:21 +020025#include <linux/interrupt.h>
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +000026#include <linux/platform_device.h>
Andrew Lunnfb78a952018-04-19 01:02:58 +020027#include <linux/mdio-bitbang.h>
28#include <linux/mdio-gpio.h>
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +000029#include <linux/gpio.h>
Andrew Lunn0207dd12018-04-19 01:02:59 +020030#include <linux/gpio/consumer.h>
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +000031
Laurent Pincharta5edecc2008-05-26 11:53:21 +020032#include <linux/of_gpio.h>
Mark Waredacac4d2009-07-23 10:56:48 -070033#include <linux/of_mdio.h>
Laurent Pincharta5edecc2008-05-26 11:53:21 +020034
35struct mdio_gpio_info {
36 struct mdiobb_ctrl ctrl;
Guenter Roeck7e5fbd12017-01-11 12:59:50 -080037 struct gpio_desc *mdc, *mdio, *mdo;
Laurent Pincharta5edecc2008-05-26 11:53:21 +020038};
39
Andrew Lunn4029ea32018-04-19 01:02:57 +020040static int mdio_gpio_get_data(struct device *dev,
41 struct mdio_gpio_info *bitbang)
Srinivas Kandagatlae92bdf4b2012-08-24 01:59:17 +000042{
Andrew Lunnfb78a952018-04-19 01:02:58 +020043 bitbang->mdc = devm_gpiod_get_index(dev, NULL, MDIO_GPIO_MDC,
44 GPIOD_OUT_LOW);
Andrew Lunn4029ea32018-04-19 01:02:57 +020045 if (IS_ERR(bitbang->mdc))
46 return PTR_ERR(bitbang->mdc);
Srinivas Kandagatlae92bdf4b2012-08-24 01:59:17 +000047
Andrew Lunnfb78a952018-04-19 01:02:58 +020048 bitbang->mdio = devm_gpiod_get_index(dev, NULL, MDIO_GPIO_MDIO,
49 GPIOD_IN);
Andrew Lunn4029ea32018-04-19 01:02:57 +020050 if (IS_ERR(bitbang->mdio))
51 return PTR_ERR(bitbang->mdio);
Srinivas Kandagatlae92bdf4b2012-08-24 01:59:17 +000052
Andrew Lunnfb78a952018-04-19 01:02:58 +020053 bitbang->mdo = devm_gpiod_get_index_optional(dev, NULL, MDIO_GPIO_MDO,
Andrew Lunn4029ea32018-04-19 01:02:57 +020054 GPIOD_OUT_LOW);
55 return PTR_ERR_OR_ZERO(bitbang->mdo);
Srinivas Kandagatlae92bdf4b2012-08-24 01:59:17 +000056}
57
Laurent Pincharta5edecc2008-05-26 11:53:21 +020058static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)
59{
60 struct mdio_gpio_info *bitbang =
61 container_of(ctrl, struct mdio_gpio_info, ctrl);
62
Guenter Roeckf1d54c42014-04-15 19:16:42 -070063 if (bitbang->mdo) {
64 /* Separate output pin. Always set its value to high
65 * when changing direction. If direction is input,
66 * assume the pin serves as pull-up. If direction is
67 * output, the default value is high.
68 */
Guenter Roeck52aab182017-01-11 12:59:51 -080069 gpiod_set_value(bitbang->mdo, 1);
Guenter Roeckf1d54c42014-04-15 19:16:42 -070070 return;
71 }
72
Laurent Pincharta5edecc2008-05-26 11:53:21 +020073 if (dir)
Guenter Roeck52aab182017-01-11 12:59:51 -080074 gpiod_direction_output(bitbang->mdio, 1);
Laurent Pincharta5edecc2008-05-26 11:53:21 +020075 else
Guenter Roeck7e5fbd12017-01-11 12:59:50 -080076 gpiod_direction_input(bitbang->mdio);
Laurent Pincharta5edecc2008-05-26 11:53:21 +020077}
78
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +000079static int mdio_get(struct mdiobb_ctrl *ctrl)
Laurent Pincharta5edecc2008-05-26 11:53:21 +020080{
81 struct mdio_gpio_info *bitbang =
82 container_of(ctrl, struct mdio_gpio_info, ctrl);
83
Guenter Roeck52aab182017-01-11 12:59:51 -080084 return gpiod_get_value(bitbang->mdio);
Laurent Pincharta5edecc2008-05-26 11:53:21 +020085}
86
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +000087static void mdio_set(struct mdiobb_ctrl *ctrl, int what)
Laurent Pincharta5edecc2008-05-26 11:53:21 +020088{
89 struct mdio_gpio_info *bitbang =
90 container_of(ctrl, struct mdio_gpio_info, ctrl);
91
Guenter Roeckf1d54c42014-04-15 19:16:42 -070092 if (bitbang->mdo)
Guenter Roeck52aab182017-01-11 12:59:51 -080093 gpiod_set_value(bitbang->mdo, what);
Guenter Roeckf1d54c42014-04-15 19:16:42 -070094 else
Guenter Roeck52aab182017-01-11 12:59:51 -080095 gpiod_set_value(bitbang->mdio, what);
Laurent Pincharta5edecc2008-05-26 11:53:21 +020096}
97
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +000098static void mdc_set(struct mdiobb_ctrl *ctrl, int what)
Laurent Pincharta5edecc2008-05-26 11:53:21 +020099{
100 struct mdio_gpio_info *bitbang =
101 container_of(ctrl, struct mdio_gpio_info, ctrl);
102
Guenter Roeck52aab182017-01-11 12:59:51 -0800103 gpiod_set_value(bitbang->mdc, what);
Laurent Pincharta5edecc2008-05-26 11:53:21 +0200104}
105
Bhumika Goyal41a130f2017-08-22 13:43:29 +0530106static const struct mdiobb_ops mdio_gpio_ops = {
Laurent Pincharta5edecc2008-05-26 11:53:21 +0200107 .owner = THIS_MODULE,
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000108 .set_mdc = mdc_set,
Laurent Pincharta5edecc2008-05-26 11:53:21 +0200109 .set_mdio_dir = mdio_dir,
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000110 .set_mdio_data = mdio_set,
111 .get_mdio_data = mdio_get,
Laurent Pincharta5edecc2008-05-26 11:53:21 +0200112};
113
Bill Pemberton633d1592012-12-03 09:24:14 -0500114static struct mii_bus *mdio_gpio_bus_init(struct device *dev,
Andrew Lunnfb766422018-04-19 01:02:56 +0200115 struct mdio_gpio_info *bitbang,
Greg Kroah-Hartman1dd06ae2012-12-06 14:30:56 +0000116 int bus_id)
Laurent Pincharta5edecc2008-05-26 11:53:21 +0200117{
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000118 struct mii_bus *new_bus;
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000119
120 bitbang->ctrl.ops = &mdio_gpio_ops;
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000121
122 new_bus = alloc_mdio_bitbang(&bitbang->ctrl);
123 if (!new_bus)
Andrew Lunnc82fc482018-04-19 01:02:55 +0200124 return NULL;
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000125
Andrew Lunn712e5a5c2018-04-19 01:02:49 +0200126 new_bus->name = "GPIO Bitbanged MDIO";
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000127 new_bus->parent = dev;
128
Bert Vermeulen7c0c8262015-05-08 16:18:49 +0200129 if (bus_id != -1)
130 snprintf(new_bus->id, MII_BUS_ID_SIZE, "gpio-%x", bus_id);
131 else
132 strncpy(new_bus->id, "gpio", MII_BUS_ID_SIZE);
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000133
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000134 dev_set_drvdata(dev, new_bus);
135
Mark Waredacac4d2009-07-23 10:56:48 -0700136 return new_bus;
Mark Waredacac4d2009-07-23 10:56:48 -0700137}
138
Stephen Rothwellf99b4a02009-11-16 22:47:33 +0000139static void mdio_gpio_bus_deinit(struct device *dev)
Mark Waredacac4d2009-07-23 10:56:48 -0700140{
141 struct mii_bus *bus = dev_get_drvdata(dev);
Mark Waredacac4d2009-07-23 10:56:48 -0700142
Mark Waredacac4d2009-07-23 10:56:48 -0700143 free_mdio_bitbang(bus);
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000144}
145
Bill Pemberton633d1592012-12-03 09:24:14 -0500146static void mdio_gpio_bus_destroy(struct device *dev)
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000147{
148 struct mii_bus *bus = dev_get_drvdata(dev);
Laurent Pincharta5edecc2008-05-26 11:53:21 +0200149
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000150 mdiobus_unregister(bus);
Mark Waredacac4d2009-07-23 10:56:48 -0700151 mdio_gpio_bus_deinit(dev);
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000152}
Laurent Pincharta5edecc2008-05-26 11:53:21 +0200153
Bill Pemberton633d1592012-12-03 09:24:14 -0500154static int mdio_gpio_probe(struct platform_device *pdev)
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000155{
Andrew Lunnfb766422018-04-19 01:02:56 +0200156 struct mdio_gpio_info *bitbang;
Mark Waredacac4d2009-07-23 10:56:48 -0700157 struct mii_bus *new_bus;
Srinivas Kandagatla3272dd92012-11-16 00:33:59 +0000158 int ret, bus_id;
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000159
Andrew Lunnfb766422018-04-19 01:02:56 +0200160 bitbang = devm_kzalloc(&pdev->dev, sizeof(*bitbang), GFP_KERNEL);
161 if (!bitbang)
162 return -ENOMEM;
163
Andrew Lunn4029ea32018-04-19 01:02:57 +0200164 ret = mdio_gpio_get_data(&pdev->dev, bitbang);
165 if (ret)
166 return ret;
167
Srinivas Kandagatla3272dd92012-11-16 00:33:59 +0000168 if (pdev->dev.of_node) {
Srinivas Kandagatla3272dd92012-11-16 00:33:59 +0000169 bus_id = of_alias_get_id(pdev->dev.of_node, "mdio-gpio");
Johan Hovold7f52da52014-05-08 10:09:21 +0200170 if (bus_id < 0) {
171 dev_warn(&pdev->dev, "failed to get alias id\n");
172 bus_id = 0;
173 }
Srinivas Kandagatla3272dd92012-11-16 00:33:59 +0000174 } else {
Srinivas Kandagatla3272dd92012-11-16 00:33:59 +0000175 bus_id = pdev->id;
176 }
Srinivas Kandagatlae92bdf4b2012-08-24 01:59:17 +0000177
Andrew Lunn4029ea32018-04-19 01:02:57 +0200178 new_bus = mdio_gpio_bus_init(&pdev->dev, bitbang, bus_id);
Mark Waredacac4d2009-07-23 10:56:48 -0700179 if (!new_bus)
180 return -ENODEV;
181
Florian Fainelli00e798c2018-05-15 16:56:19 -0700182 ret = of_mdiobus_register(new_bus, pdev->dev.of_node);
Mark Waredacac4d2009-07-23 10:56:48 -0700183 if (ret)
184 mdio_gpio_bus_deinit(&pdev->dev);
185
186 return ret;
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000187}
188
Bill Pemberton633d1592012-12-03 09:24:14 -0500189static int mdio_gpio_remove(struct platform_device *pdev)
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000190{
191 mdio_gpio_bus_destroy(&pdev->dev);
192
Laurent Pincharta5edecc2008-05-26 11:53:21 +0200193 return 0;
194}
195
Fabian Frederickd8a7dad2015-03-17 19:40:23 +0100196static const struct of_device_id mdio_gpio_of_match[] = {
Srinivas Kandagatlae92bdf4b2012-08-24 01:59:17 +0000197 { .compatible = "virtual,mdio-gpio", },
198 { /* sentinel */ }
Laurent Pincharta5edecc2008-05-26 11:53:21 +0200199};
Luis de Bethencourt1ccb1412015-09-18 18:16:29 +0200200MODULE_DEVICE_TABLE(of, mdio_gpio_of_match);
Laurent Pincharta5edecc2008-05-26 11:53:21 +0200201
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000202static struct platform_driver mdio_gpio_driver = {
203 .probe = mdio_gpio_probe,
Bill Pemberton633d1592012-12-03 09:24:14 -0500204 .remove = mdio_gpio_remove,
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000205 .driver = {
206 .name = "mdio-gpio",
Srinivas Kandagatlae92bdf4b2012-08-24 01:59:17 +0000207 .of_match_table = mdio_gpio_of_match,
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000208 },
209};
210
Sachin Kamatf8e5fc82013-03-20 01:41:31 +0000211module_platform_driver(mdio_gpio_driver);
Paulius Zaleckasf004f3e2008-11-14 00:24:34 +0000212
213MODULE_ALIAS("platform:mdio-gpio");
214MODULE_AUTHOR("Laurent Pinchart, Paulius Zaleckas");
215MODULE_LICENSE("GPL");
216MODULE_DESCRIPTION("Generic driver for MDIO bus emulation using GPIO");