2019-01-22 01:10:19 +07:00
|
|
|
// SPDX-License-Identifier: GPL-2.0
|
2008-05-26 16:53:21 +07:00
|
|
|
/*
|
2008-11-14 07:24:34 +07:00
|
|
|
* GPIO based MDIO bitbang driver.
|
|
|
|
* Supports OpenFirmware.
|
2008-05-26 16:53:21 +07:00
|
|
|
*
|
|
|
|
* Copyright (c) 2008 CSE Semaphore Belgium.
|
|
|
|
* by Laurent Pinchart <laurentp@cse-semaphore.com>
|
|
|
|
*
|
2008-11-14 07:24:34 +07:00
|
|
|
* Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
|
|
|
|
*
|
2008-05-26 16:53:21 +07:00
|
|
|
* Based on earlier work by
|
|
|
|
*
|
|
|
|
* Copyright (c) 2003 Intracom S.A.
|
|
|
|
* by Pantelis Antoniou <panto@intracom.gr>
|
|
|
|
*
|
|
|
|
* 2005 (c) MontaVista Software, Inc.
|
|
|
|
* Vitaly Bordug <vbordug@ru.mvista.com>
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/interrupt.h>
|
2008-11-14 07:24:34 +07:00
|
|
|
#include <linux/platform_device.h>
|
net: phy: mdio-gpio: Add platform_data support for phy_mask
It is sometimes necessary to instantiate a bit-banging MDIO bus as a
platform device, without the aid of device tree.
When device tree is being used, the bus is not scanned for devices,
only those devices which are in device tree are probed. Without device
tree, by default, all addresses on the bus are scanned. This may then
find a device which is not a PHY, e.g. a switch. And the switch may
have registers containing values which look like a PHY. So during the
scan, a PHY device is wrongly created.
After the bus has been registered, a search is made for
mdio_board_info structures which indicates devices on the bus, and the
driver which should be used for them. This is typically used to
instantiate Ethernet switches from platform drivers. However, if the
scanning of the bus has created a PHY device at the same location as
indicated into the board info for a switch, the switch device is not
created, since the address is already busy.
This can be avoided by setting the phy_mask of the mdio bus. This mask
prevents addresses on the bus being scanned.
v2
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-12-08 22:12:12 +07:00
|
|
|
#include <linux/platform_data/mdio-gpio.h>
|
2018-04-19 06:02:58 +07:00
|
|
|
#include <linux/mdio-bitbang.h>
|
|
|
|
#include <linux/mdio-gpio.h>
|
2018-04-19 06:02:59 +07:00
|
|
|
#include <linux/gpio/consumer.h>
|
2009-07-24 00:56:48 +07:00
|
|
|
#include <linux/of_mdio.h>
|
2008-05-26 16:53:21 +07:00
|
|
|
|
|
|
|
struct mdio_gpio_info {
|
|
|
|
struct mdiobb_ctrl ctrl;
|
2017-01-12 03:59:50 +07:00
|
|
|
struct gpio_desc *mdc, *mdio, *mdo;
|
2008-05-26 16:53:21 +07:00
|
|
|
};
|
|
|
|
|
2018-04-19 06:02:57 +07:00
|
|
|
static int mdio_gpio_get_data(struct device *dev,
|
|
|
|
struct mdio_gpio_info *bitbang)
|
2012-08-24 08:59:17 +07:00
|
|
|
{
|
2018-04-19 06:02:58 +07:00
|
|
|
bitbang->mdc = devm_gpiod_get_index(dev, NULL, MDIO_GPIO_MDC,
|
|
|
|
GPIOD_OUT_LOW);
|
2018-04-19 06:02:57 +07:00
|
|
|
if (IS_ERR(bitbang->mdc))
|
|
|
|
return PTR_ERR(bitbang->mdc);
|
2012-08-24 08:59:17 +07:00
|
|
|
|
2018-04-19 06:02:58 +07:00
|
|
|
bitbang->mdio = devm_gpiod_get_index(dev, NULL, MDIO_GPIO_MDIO,
|
|
|
|
GPIOD_IN);
|
2018-04-19 06:02:57 +07:00
|
|
|
if (IS_ERR(bitbang->mdio))
|
|
|
|
return PTR_ERR(bitbang->mdio);
|
2014-04-16 09:16:42 +07:00
|
|
|
|
2018-04-19 06:02:58 +07:00
|
|
|
bitbang->mdo = devm_gpiod_get_index_optional(dev, NULL, MDIO_GPIO_MDO,
|
2018-04-19 06:02:57 +07:00
|
|
|
GPIOD_OUT_LOW);
|
|
|
|
return PTR_ERR_OR_ZERO(bitbang->mdo);
|
2012-08-24 08:59:17 +07:00
|
|
|
}
|
|
|
|
|
2008-05-26 16:53:21 +07:00
|
|
|
static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)
|
|
|
|
{
|
|
|
|
struct mdio_gpio_info *bitbang =
|
|
|
|
container_of(ctrl, struct mdio_gpio_info, ctrl);
|
|
|
|
|
2014-04-16 09:16:42 +07:00
|
|
|
if (bitbang->mdo) {
|
|
|
|
/* Separate output pin. Always set its value to high
|
|
|
|
* when changing direction. If direction is input,
|
|
|
|
* assume the pin serves as pull-up. If direction is
|
|
|
|
* output, the default value is high.
|
|
|
|
*/
|
2018-11-16 14:38:36 +07:00
|
|
|
gpiod_set_value_cansleep(bitbang->mdo, 1);
|
2014-04-16 09:16:42 +07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-05-26 16:53:21 +07:00
|
|
|
if (dir)
|
2017-01-12 03:59:51 +07:00
|
|
|
gpiod_direction_output(bitbang->mdio, 1);
|
2008-05-26 16:53:21 +07:00
|
|
|
else
|
2017-01-12 03:59:50 +07:00
|
|
|
gpiod_direction_input(bitbang->mdio);
|
2008-05-26 16:53:21 +07:00
|
|
|
}
|
|
|
|
|
2008-11-14 07:24:34 +07:00
|
|
|
static int mdio_get(struct mdiobb_ctrl *ctrl)
|
2008-05-26 16:53:21 +07:00
|
|
|
{
|
|
|
|
struct mdio_gpio_info *bitbang =
|
|
|
|
container_of(ctrl, struct mdio_gpio_info, ctrl);
|
|
|
|
|
2018-11-16 14:38:36 +07:00
|
|
|
return gpiod_get_value_cansleep(bitbang->mdio);
|
2008-05-26 16:53:21 +07:00
|
|
|
}
|
|
|
|
|
2008-11-14 07:24:34 +07:00
|
|
|
static void mdio_set(struct mdiobb_ctrl *ctrl, int what)
|
2008-05-26 16:53:21 +07:00
|
|
|
{
|
|
|
|
struct mdio_gpio_info *bitbang =
|
|
|
|
container_of(ctrl, struct mdio_gpio_info, ctrl);
|
|
|
|
|
2014-04-16 09:16:42 +07:00
|
|
|
if (bitbang->mdo)
|
2018-11-16 14:38:36 +07:00
|
|
|
gpiod_set_value_cansleep(bitbang->mdo, what);
|
2014-04-16 09:16:42 +07:00
|
|
|
else
|
2018-11-16 14:38:36 +07:00
|
|
|
gpiod_set_value_cansleep(bitbang->mdio, what);
|
2008-05-26 16:53:21 +07:00
|
|
|
}
|
|
|
|
|
2008-11-14 07:24:34 +07:00
|
|
|
static void mdc_set(struct mdiobb_ctrl *ctrl, int what)
|
2008-05-26 16:53:21 +07:00
|
|
|
{
|
|
|
|
struct mdio_gpio_info *bitbang =
|
|
|
|
container_of(ctrl, struct mdio_gpio_info, ctrl);
|
|
|
|
|
2018-11-16 14:38:36 +07:00
|
|
|
gpiod_set_value_cansleep(bitbang->mdc, what);
|
2008-05-26 16:53:21 +07:00
|
|
|
}
|
|
|
|
|
2017-08-22 15:13:29 +07:00
|
|
|
static const struct mdiobb_ops mdio_gpio_ops = {
|
2008-05-26 16:53:21 +07:00
|
|
|
.owner = THIS_MODULE,
|
2008-11-14 07:24:34 +07:00
|
|
|
.set_mdc = mdc_set,
|
2008-05-26 16:53:21 +07:00
|
|
|
.set_mdio_dir = mdio_dir,
|
2008-11-14 07:24:34 +07:00
|
|
|
.set_mdio_data = mdio_set,
|
|
|
|
.get_mdio_data = mdio_get,
|
2008-05-26 16:53:21 +07:00
|
|
|
};
|
|
|
|
|
2012-12-03 21:24:14 +07:00
|
|
|
static struct mii_bus *mdio_gpio_bus_init(struct device *dev,
|
2018-04-19 06:02:56 +07:00
|
|
|
struct mdio_gpio_info *bitbang,
|
2012-12-06 21:30:56 +07:00
|
|
|
int bus_id)
|
2008-05-26 16:53:21 +07:00
|
|
|
{
|
net: phy: mdio-gpio: Add platform_data support for phy_mask
It is sometimes necessary to instantiate a bit-banging MDIO bus as a
platform device, without the aid of device tree.
When device tree is being used, the bus is not scanned for devices,
only those devices which are in device tree are probed. Without device
tree, by default, all addresses on the bus are scanned. This may then
find a device which is not a PHY, e.g. a switch. And the switch may
have registers containing values which look like a PHY. So during the
scan, a PHY device is wrongly created.
After the bus has been registered, a search is made for
mdio_board_info structures which indicates devices on the bus, and the
driver which should be used for them. This is typically used to
instantiate Ethernet switches from platform drivers. However, if the
scanning of the bus has created a PHY device at the same location as
indicated into the board info for a switch, the switch device is not
created, since the address is already busy.
This can be avoided by setting the phy_mask of the mdio bus. This mask
prevents addresses on the bus being scanned.
v2
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-12-08 22:12:12 +07:00
|
|
|
struct mdio_gpio_platform_data *pdata = dev_get_platdata(dev);
|
2008-05-26 16:53:21 +07:00
|
|
|
struct mii_bus *new_bus;
|
|
|
|
|
|
|
|
bitbang->ctrl.ops = &mdio_gpio_ops;
|
|
|
|
|
|
|
|
new_bus = alloc_mdio_bitbang(&bitbang->ctrl);
|
|
|
|
if (!new_bus)
|
2018-04-19 06:02:55 +07:00
|
|
|
return NULL;
|
2008-05-26 16:53:21 +07:00
|
|
|
|
2018-04-19 06:02:49 +07:00
|
|
|
new_bus->name = "GPIO Bitbanged MDIO";
|
2008-11-14 07:24:34 +07:00
|
|
|
new_bus->parent = dev;
|
|
|
|
|
2015-05-08 21:18:49 +07:00
|
|
|
if (bus_id != -1)
|
|
|
|
snprintf(new_bus->id, MII_BUS_ID_SIZE, "gpio-%x", bus_id);
|
|
|
|
else
|
|
|
|
strncpy(new_bus->id, "gpio", MII_BUS_ID_SIZE);
|
2008-11-14 07:24:34 +07:00
|
|
|
|
2018-12-08 22:12:13 +07:00
|
|
|
if (pdata) {
|
net: phy: mdio-gpio: Add platform_data support for phy_mask
It is sometimes necessary to instantiate a bit-banging MDIO bus as a
platform device, without the aid of device tree.
When device tree is being used, the bus is not scanned for devices,
only those devices which are in device tree are probed. Without device
tree, by default, all addresses on the bus are scanned. This may then
find a device which is not a PHY, e.g. a switch. And the switch may
have registers containing values which look like a PHY. So during the
scan, a PHY device is wrongly created.
After the bus has been registered, a search is made for
mdio_board_info structures which indicates devices on the bus, and the
driver which should be used for them. This is typically used to
instantiate Ethernet switches from platform drivers. However, if the
scanning of the bus has created a PHY device at the same location as
indicated into the board info for a switch, the switch device is not
created, since the address is already busy.
This can be avoided by setting the phy_mask of the mdio bus. This mask
prevents addresses on the bus being scanned.
v2
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-12-08 22:12:12 +07:00
|
|
|
new_bus->phy_mask = pdata->phy_mask;
|
2018-12-08 22:12:13 +07:00
|
|
|
new_bus->phy_ignore_ta_mask = pdata->phy_ignore_ta_mask;
|
|
|
|
}
|
net: phy: mdio-gpio: Add platform_data support for phy_mask
It is sometimes necessary to instantiate a bit-banging MDIO bus as a
platform device, without the aid of device tree.
When device tree is being used, the bus is not scanned for devices,
only those devices which are in device tree are probed. Without device
tree, by default, all addresses on the bus are scanned. This may then
find a device which is not a PHY, e.g. a switch. And the switch may
have registers containing values which look like a PHY. So during the
scan, a PHY device is wrongly created.
After the bus has been registered, a search is made for
mdio_board_info structures which indicates devices on the bus, and the
driver which should be used for them. This is typically used to
instantiate Ethernet switches from platform drivers. However, if the
scanning of the bus has created a PHY device at the same location as
indicated into the board info for a switch, the switch device is not
created, since the address is already busy.
This can be avoided by setting the phy_mask of the mdio bus. This mask
prevents addresses on the bus being scanned.
v2
Reviewed-by: Florian Fainelli <f.fainelli@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2018-12-08 22:12:12 +07:00
|
|
|
|
2008-11-14 07:24:34 +07:00
|
|
|
dev_set_drvdata(dev, new_bus);
|
2008-05-26 16:53:21 +07:00
|
|
|
|
2009-07-24 00:56:48 +07:00
|
|
|
return new_bus;
|
2008-05-26 16:53:21 +07:00
|
|
|
}
|
|
|
|
|
2009-11-17 05:47:33 +07:00
|
|
|
static void mdio_gpio_bus_deinit(struct device *dev)
|
2008-05-26 16:53:21 +07:00
|
|
|
{
|
2008-11-14 07:24:34 +07:00
|
|
|
struct mii_bus *bus = dev_get_drvdata(dev);
|
2008-05-26 16:53:21 +07:00
|
|
|
|
2009-07-24 00:56:48 +07:00
|
|
|
free_mdio_bitbang(bus);
|
2008-11-14 07:24:34 +07:00
|
|
|
}
|
|
|
|
|
2012-12-03 21:24:14 +07:00
|
|
|
static void mdio_gpio_bus_destroy(struct device *dev)
|
2009-07-24 00:56:48 +07:00
|
|
|
{
|
|
|
|
struct mii_bus *bus = dev_get_drvdata(dev);
|
|
|
|
|
|
|
|
mdiobus_unregister(bus);
|
|
|
|
mdio_gpio_bus_deinit(dev);
|
|
|
|
}
|
|
|
|
|
2012-12-03 21:24:14 +07:00
|
|
|
static int mdio_gpio_probe(struct platform_device *pdev)
|
2008-11-14 07:24:34 +07:00
|
|
|
{
|
2018-04-19 06:02:56 +07:00
|
|
|
struct mdio_gpio_info *bitbang;
|
2009-07-24 00:56:48 +07:00
|
|
|
struct mii_bus *new_bus;
|
2012-11-16 07:33:59 +07:00
|
|
|
int ret, bus_id;
|
2008-11-14 07:24:34 +07:00
|
|
|
|
2018-04-19 06:02:56 +07:00
|
|
|
bitbang = devm_kzalloc(&pdev->dev, sizeof(*bitbang), GFP_KERNEL);
|
|
|
|
if (!bitbang)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
2018-04-19 06:02:57 +07:00
|
|
|
ret = mdio_gpio_get_data(&pdev->dev, bitbang);
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2012-11-16 07:33:59 +07:00
|
|
|
if (pdev->dev.of_node) {
|
|
|
|
bus_id = of_alias_get_id(pdev->dev.of_node, "mdio-gpio");
|
2014-05-08 15:09:21 +07:00
|
|
|
if (bus_id < 0) {
|
|
|
|
dev_warn(&pdev->dev, "failed to get alias id\n");
|
|
|
|
bus_id = 0;
|
|
|
|
}
|
2012-11-16 07:33:59 +07:00
|
|
|
} else {
|
|
|
|
bus_id = pdev->id;
|
|
|
|
}
|
2012-08-24 08:59:17 +07:00
|
|
|
|
2018-04-19 06:02:57 +07:00
|
|
|
new_bus = mdio_gpio_bus_init(&pdev->dev, bitbang, bus_id);
|
2009-07-24 00:56:48 +07:00
|
|
|
if (!new_bus)
|
|
|
|
return -ENODEV;
|
|
|
|
|
2018-05-16 06:56:19 +07:00
|
|
|
ret = of_mdiobus_register(new_bus, pdev->dev.of_node);
|
2009-07-24 00:56:48 +07:00
|
|
|
if (ret)
|
|
|
|
mdio_gpio_bus_deinit(&pdev->dev);
|
|
|
|
|
|
|
|
return ret;
|
2008-11-14 07:24:34 +07:00
|
|
|
}
|
|
|
|
|
2012-12-03 21:24:14 +07:00
|
|
|
static int mdio_gpio_remove(struct platform_device *pdev)
|
2008-11-14 07:24:34 +07:00
|
|
|
{
|
|
|
|
mdio_gpio_bus_destroy(&pdev->dev);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-03-18 01:40:23 +07:00
|
|
|
static const struct of_device_id mdio_gpio_of_match[] = {
|
2012-08-24 08:59:17 +07:00
|
|
|
{ .compatible = "virtual,mdio-gpio", },
|
|
|
|
{ /* sentinel */ }
|
2008-05-26 16:53:21 +07:00
|
|
|
};
|
2015-09-18 23:16:29 +07:00
|
|
|
MODULE_DEVICE_TABLE(of, mdio_gpio_of_match);
|
2008-05-26 16:53:21 +07:00
|
|
|
|
2008-11-14 07:24:34 +07:00
|
|
|
static struct platform_driver mdio_gpio_driver = {
|
|
|
|
.probe = mdio_gpio_probe,
|
2012-12-03 21:24:14 +07:00
|
|
|
.remove = mdio_gpio_remove,
|
2008-11-14 07:24:34 +07:00
|
|
|
.driver = {
|
|
|
|
.name = "mdio-gpio",
|
2012-08-24 08:59:17 +07:00
|
|
|
.of_match_table = mdio_gpio_of_match,
|
2008-11-14 07:24:34 +07:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2013-03-20 08:41:31 +07:00
|
|
|
module_platform_driver(mdio_gpio_driver);
|
2008-05-26 16:53:21 +07:00
|
|
|
|
2008-11-14 07:24:34 +07:00
|
|
|
MODULE_ALIAS("platform:mdio-gpio");
|
|
|
|
MODULE_AUTHOR("Laurent Pinchart, Paulius Zaleckas");
|
2019-01-22 01:10:19 +07:00
|
|
|
MODULE_LICENSE("GPL v2");
|
2008-11-14 07:24:34 +07:00
|
|
|
MODULE_DESCRIPTION("Generic driver for MDIO bus emulation using GPIO");
|