brintos

brintos / linux-shallow public Read only

0
0
Text · 5.3 KiB · 8208874 Raw
223 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * GPIO based MDIO bitbang driver.4 * Supports OpenFirmware.5 *6 * Copyright (c) 2008 CSE Semaphore Belgium.7 *  by Laurent Pinchart <laurentp@cse-semaphore.com>8 *9 * Copyright (C) 2008, Paulius Zaleckas <paulius.zaleckas@teltonika.lt>10 *11 * Based on earlier work by12 *13 * Copyright (c) 2003 Intracom S.A.14 *  by Pantelis Antoniou <panto@intracom.gr>15 *16 * 2005 (c) MontaVista Software, Inc.17 * Vitaly Bordug <vbordug@ru.mvista.com>18 */19 20#include <linux/gpio/consumer.h>21#include <linux/interrupt.h>22#include <linux/mdio-bitbang.h>23#include <linux/mdio-gpio.h>24#include <linux/module.h>25#include <linux/of_mdio.h>26#include <linux/platform_data/mdio-gpio.h>27#include <linux/platform_device.h>28#include <linux/slab.h>29 30struct mdio_gpio_info {31	struct mdiobb_ctrl ctrl;32	struct gpio_desc *mdc, *mdio, *mdo;33};34 35static int mdio_gpio_get_data(struct device *dev,36			      struct mdio_gpio_info *bitbang)37{38	bitbang->mdc = devm_gpiod_get_index(dev, NULL, MDIO_GPIO_MDC,39					    GPIOD_OUT_LOW);40	if (IS_ERR(bitbang->mdc))41		return PTR_ERR(bitbang->mdc);42 43	bitbang->mdio = devm_gpiod_get_index(dev, NULL, MDIO_GPIO_MDIO,44					     GPIOD_IN);45	if (IS_ERR(bitbang->mdio))46		return PTR_ERR(bitbang->mdio);47 48	bitbang->mdo = devm_gpiod_get_index_optional(dev, NULL, MDIO_GPIO_MDO,49						     GPIOD_OUT_LOW);50	return PTR_ERR_OR_ZERO(bitbang->mdo);51}52 53static void mdio_dir(struct mdiobb_ctrl *ctrl, int dir)54{55	struct mdio_gpio_info *bitbang =56		container_of(ctrl, struct mdio_gpio_info, ctrl);57 58	if (bitbang->mdo) {59		/* Separate output pin. Always set its value to high60		 * when changing direction. If direction is input,61		 * assume the pin serves as pull-up. If direction is62		 * output, the default value is high.63		 */64		gpiod_set_value_cansleep(bitbang->mdo, 1);65		return;66	}67 68	if (dir)69		gpiod_direction_output(bitbang->mdio, 1);70	else71		gpiod_direction_input(bitbang->mdio);72}73 74static int mdio_get(struct mdiobb_ctrl *ctrl)75{76	struct mdio_gpio_info *bitbang =77		container_of(ctrl, struct mdio_gpio_info, ctrl);78 79	return gpiod_get_value_cansleep(bitbang->mdio);80}81 82static void mdio_set(struct mdiobb_ctrl *ctrl, int what)83{84	struct mdio_gpio_info *bitbang =85		container_of(ctrl, struct mdio_gpio_info, ctrl);86 87	if (bitbang->mdo)88		gpiod_set_value_cansleep(bitbang->mdo, what);89	else90		gpiod_set_value_cansleep(bitbang->mdio, what);91}92 93static void mdc_set(struct mdiobb_ctrl *ctrl, int what)94{95	struct mdio_gpio_info *bitbang =96		container_of(ctrl, struct mdio_gpio_info, ctrl);97 98	gpiod_set_value_cansleep(bitbang->mdc, what);99}100 101static const struct mdiobb_ops mdio_gpio_ops = {102	.owner = THIS_MODULE,103	.set_mdc = mdc_set,104	.set_mdio_dir = mdio_dir,105	.set_mdio_data = mdio_set,106	.get_mdio_data = mdio_get,107};108 109static struct mii_bus *mdio_gpio_bus_init(struct device *dev,110					  struct mdio_gpio_info *bitbang,111					  int bus_id)112{113	struct mdio_gpio_platform_data *pdata = dev_get_platdata(dev);114	struct mii_bus *new_bus;115 116	bitbang->ctrl.ops = &mdio_gpio_ops;117 118	new_bus = alloc_mdio_bitbang(&bitbang->ctrl);119	if (!new_bus)120		return NULL;121 122	new_bus->name = "GPIO Bitbanged MDIO";123	new_bus->parent = dev;124 125	if (bus_id != -1)126		snprintf(new_bus->id, sizeof(new_bus->id), "gpio-%x", bus_id);127	else128		strscpy(new_bus->id, "gpio", sizeof(new_bus->id));129 130	if (pdata) {131		new_bus->phy_mask = pdata->phy_mask;132		new_bus->phy_ignore_ta_mask = pdata->phy_ignore_ta_mask;133	}134 135	if (device_is_compatible(dev, "microchip,mdio-smi0")) {136		bitbang->ctrl.op_c22_read = 0;137		bitbang->ctrl.op_c22_write = 0;138		bitbang->ctrl.override_op_c22 = 1;139	}140 141	dev_set_drvdata(dev, new_bus);142 143	return new_bus;144}145 146static void mdio_gpio_bus_deinit(struct device *dev)147{148	struct mii_bus *bus = dev_get_drvdata(dev);149 150	free_mdio_bitbang(bus);151}152 153static void mdio_gpio_bus_destroy(struct device *dev)154{155	struct mii_bus *bus = dev_get_drvdata(dev);156 157	mdiobus_unregister(bus);158	mdio_gpio_bus_deinit(dev);159}160 161static int mdio_gpio_probe(struct platform_device *pdev)162{163	struct mdio_gpio_info *bitbang;164	struct mii_bus *new_bus;165	int ret, bus_id;166 167	bitbang = devm_kzalloc(&pdev->dev, sizeof(*bitbang), GFP_KERNEL);168	if (!bitbang)169		return -ENOMEM;170 171	ret = mdio_gpio_get_data(&pdev->dev, bitbang);172	if (ret)173		return ret;174 175	if (pdev->dev.of_node) {176		bus_id = of_alias_get_id(pdev->dev.of_node, "mdio-gpio");177		if (bus_id < 0) {178			dev_warn(&pdev->dev, "failed to get alias id\n");179			bus_id = 0;180		}181	} else {182		bus_id = pdev->id;183	}184 185	new_bus = mdio_gpio_bus_init(&pdev->dev, bitbang, bus_id);186	if (!new_bus)187		return -ENODEV;188 189	ret = of_mdiobus_register(new_bus, pdev->dev.of_node);190	if (ret)191		mdio_gpio_bus_deinit(&pdev->dev);192 193	return ret;194}195 196static void mdio_gpio_remove(struct platform_device *pdev)197{198	mdio_gpio_bus_destroy(&pdev->dev);199}200 201static const struct of_device_id mdio_gpio_of_match[] = {202	{ .compatible = "virtual,mdio-gpio", },203	{ .compatible = "microchip,mdio-smi0" },204	{ /* sentinel */ }205};206MODULE_DEVICE_TABLE(of, mdio_gpio_of_match);207 208static struct platform_driver mdio_gpio_driver = {209	.probe = mdio_gpio_probe,210	.remove_new = mdio_gpio_remove,211	.driver		= {212		.name	= "mdio-gpio",213		.of_match_table = mdio_gpio_of_match,214	},215};216 217module_platform_driver(mdio_gpio_driver);218 219MODULE_ALIAS("platform:mdio-gpio");220MODULE_AUTHOR("Laurent Pinchart, Paulius Zaleckas");221MODULE_LICENSE("GPL v2");222MODULE_DESCRIPTION("Generic driver for MDIO bus emulation using GPIO");223