>From 1238b5c70d29762074bc71ac25623b958af9010a Mon Sep 17 00:00:00 2001 From: Clement Leger <clement.leger@xxxxxxxxx> Date: Wed, 8 Nov 2017 09:13:10 +0100 Subject: [PATCH] Net: add mdio_i2c driver (i2c to mdio bridge). Some phys (Marvells) can be connected using and i2c bus instead of a mdio bus. The communication format is then slightly different than standard mdio. This patch add a i2c to mdio bridge which allows to communicate with these phys. --- drivers/net/phy/Kconfig | 8 +++ drivers/net/phy/Makefile | 1 + drivers/net/phy/mdio-i2c.c | 148 ++++++++++++++++++++++++++++++++++++++++++ dts/Bindings/net/mdio-i2c.txt | 19 ++++++ 4 files changed, 176 insertions(+) create mode 100644 drivers/net/phy/mdio-i2c.c create mode 100644 dts/Bindings/net/mdio-i2c.txt diff --git a/drivers/net/phy/Kconfig b/drivers/net/phy/Kconfig index ea2e062..f058f38 100644 --- a/drivers/net/phy/Kconfig +++ b/drivers/net/phy/Kconfig @@ -60,6 +60,14 @@ config MDIO_BITBANG If in doubt, say N. +config MDIO_I2C + bool "Support for I2C based MDIO buses" + depends on I2C + ---help--- + Support I2C based PHYs. This provides a MDIO bus bridged + to I2C to allow PHYs connected in I2C mode to be accessed + using the existing infrastructure. + config MDIO_GPIO bool "Support for GPIO lib-based bitbanged MDIO buses" depends on MDIO_BITBANG && GPIOLIB diff --git a/drivers/net/phy/Makefile b/drivers/net/phy/Makefile index 13b8f65..9f61ae6 100644 --- a/drivers/net/phy/Makefile +++ b/drivers/net/phy/Makefile @@ -10,3 +10,4 @@ obj-$(CONFIG_SMSC_PHY) += smsc.o obj-$(CONFIG_MDIO_MVEBU) += mdio-mvebu.o obj-$(CONFIG_MDIO_BITBANG) += mdio-bitbang.o obj-$(CONFIG_MDIO_GPIO) += mdio-gpio.o +obj-$(CONFIG_MDIO_I2C) += mdio-i2c.o diff --git a/drivers/net/phy/mdio-i2c.c b/drivers/net/phy/mdio-i2c.c new file mode 100644 index 0000000..fcc5562 --- /dev/null +++ b/drivers/net/phy/mdio-i2c.c @@ -0,0 +1,148 @@ +/* + * MDIO I2C bridge + * + * Copyright (C) 2017 Clément Léger + * + * Based on code by Russell King from Linux + * Copyright (C) 2015 Russell King + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <common.h> +#include <driver.h> +#include <init.h> +#include <io.h> +#include <of.h> +#include <of_gpio.h> +#include <linux/phy.h> +#include <gpio.h> +#include <malloc.h> +#include <xfuncs.h> + +#include <i2c/i2c.h> + +/* + * I2C bus addresses 0x50 and 0x51 are normally an EEPROM, which is + * specified to be present in SFP modules. These correspond with PHY + * addresses 16 and 17. Disallow access to these "phy" addresses. + */ +static bool i2c_mii_valid_phy_id(int phy_id) +{ + return phy_id != 0x10 && phy_id != 0x11; +} + +static unsigned int i2c_mii_phy_addr(int phy_id) +{ + return phy_id + 0x40; +} + +static int i2c_mii_read(struct mii_bus *bus, int phy_id, int reg) +{ + struct i2c_adapter *i2c = bus->priv; + struct i2c_msg msgs[2]; + u8 data[2], dev_addr = reg; + int bus_addr, ret; + + if (!i2c_mii_valid_phy_id(phy_id)) + return 0xffff; + + bus_addr = i2c_mii_phy_addr(phy_id); + msgs[0].addr = bus_addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = &dev_addr; + msgs[1].addr = bus_addr; + msgs[1].flags = I2C_M_RD; + msgs[1].len = sizeof(data); + msgs[1].buf = data; + + ret = i2c_transfer(i2c, msgs, ARRAY_SIZE(msgs)); + if (ret != ARRAY_SIZE(msgs)) + return 0xffff; + + return data[0] << 8 | data[1]; +} + +static int i2c_mii_write(struct mii_bus *bus, int phy_id, int reg, u16 val) +{ + struct i2c_adapter *i2c = bus->priv; + struct i2c_msg msg; + int ret; + u8 data[3]; + + if (!i2c_mii_valid_phy_id(phy_id)) + return 0; + + data[0] = reg; + data[1] = val >> 8; + data[2] = val; + + msg.addr = i2c_mii_phy_addr(phy_id); + msg.flags = 0; + msg.len = 3; + msg.buf = data; + + ret = i2c_transfer(i2c, &msg, 1); + + return ret < 0 ? ret : 0; +} + +static int mdio_i2c_probe(struct device_d *dev) +{ + struct mii_bus *mii; + struct i2c_adapter *i2c_adap; + struct device_node *i2c_node; + + i2c_node = of_parse_phandle(dev->device_node, "i2c-bus", 0); + if (!i2c_node) { + dev_err(dev, "Missing i2c-bus property for mdio-i2c\n"); + return -EINVAL; + } + + i2c_adap = of_find_i2c_adapter_by_node(i2c_node); + if (!i2c_adap) { + dev_dbg(dev, "i2c bus not yet probed\n"); + return -EPROBE_DEFER; + } + + mii = xzalloc(sizeof(*mii)); + + mii->parent = dev; + mii->dev.device_node = dev->device_node; + + mii->parent = dev; + mii->read = i2c_mii_read; + mii->write = i2c_mii_write; + mii->priv = i2c_adap; + + dev->priv = mii; + + dev_info(dev, "I2C mdio register\n"); + + return mdiobus_register(mii); +} + +static void mdio_i2c_remove(struct device_d *dev) +{ + struct mii_bus *mii = dev->priv; + + mdiobus_unregister(mii); + kfree(mii); +} + +static const struct of_device_id i2c_mdio_dt_ids[] = { + { .compatible = "virtual,mdio-i2c", }, + { /* sentinel */ } +}; + +static struct driver_d mdio_i2c_driver = { + .name = "mdio-i2c", + .probe = mdio_i2c_probe, + .remove = mdio_i2c_remove, + .of_compatible = DRV_OF_COMPAT(i2c_mdio_dt_ids), +}; + +device_platform_driver(mdio_i2c_driver); diff --git a/dts/Bindings/net/mdio-i2c.txt b/dts/Bindings/net/mdio-i2c.txt new file mode 100644 index 0000000..64e20e1 --- /dev/null +++ b/dts/Bindings/net/mdio-i2c.txt @@ -0,0 +1,19 @@ +MDIO on I2C Bus +This compatible allows to use phy devices connected to an i2c bus +and expecting to be use with mdio protocol. + +Currently defined compatibles: +- virtual,mdio-i2c + +Required properties: +- i2c-bus: A phandle on the I2C bus acting as a MDIO bridge where phys + are connected to. + +Example: + +mdio0@i2c { + compatible = "virtual,mdio-i2c"; + #address-cells = <1>; + #size-cells = <0>; + i2c-bus = <&i2c_bus>; +}; -- 1.8.3.1 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox