On Wed, Dec 19, 2018 at 4:41 AM <tristram...@microchip.com> wrote: > > From: Tristram Ha <tristram...@microchip.com> > > Add KSZ9477 I2C driver support. The code ksz9477.c and ksz_common.c are > used together to generate the I2C driver. > > Signed-off-by: Tristram Ha <tristram...@microchip.com> > --- > v1 > - Return error code from i2c_transfer > - Change GPL license > - Change author > > drivers/net/dsa/microchip/Kconfig | 6 + > drivers/net/dsa/microchip/Makefile | 1 + > drivers/net/dsa/microchip/ksz9477_i2c.c | 196 > ++++++++++++++++++++++++++++++++ > drivers/net/dsa/microchip/ksz_i2c.h | 69 +++++++++++ > 4 files changed, 272 insertions(+) > create mode 100644 drivers/net/dsa/microchip/ksz9477_i2c.c > create mode 100644 drivers/net/dsa/microchip/ksz_i2c.h > > diff --git a/drivers/net/dsa/microchip/Kconfig > b/drivers/net/dsa/microchip/Kconfig > index bea29fd..fd94441 100644 > --- a/drivers/net/dsa/microchip/Kconfig > +++ b/drivers/net/dsa/microchip/Kconfig > @@ -9,6 +9,12 @@ menuconfig NET_DSA_MICROCHIP_KSZ9477 > help > This driver adds support for Microchip KSZ9477 switch chips. > > +config NET_DSA_MICROCHIP_KSZ9477_I2C > + tristate "KSZ9477 series I2C connected switch driver" > + depends on NET_DSA_MICROCHIP_KSZ9477 && I2C > + help > + Select to enable support for registering switches configured > through I2C. > + > config NET_DSA_MICROCHIP_KSZ9477_SPI > tristate "KSZ9477 series SPI connected switch driver" > depends on NET_DSA_MICROCHIP_KSZ9477 && SPI > diff --git a/drivers/net/dsa/microchip/Makefile > b/drivers/net/dsa/microchip/Makefile > index 3142c18..dbcc5db 100644 > --- a/drivers/net/dsa/microchip/Makefile > +++ b/drivers/net/dsa/microchip/Makefile > @@ -1,3 +1,4 @@ > obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ_COMMON) += ksz_common.o > obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477) += ksz9477.o > +obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_I2C) += ksz9477_i2c.o > obj-$(CONFIG_NET_DSA_MICROCHIP_KSZ9477_SPI) += ksz9477_spi.o > diff --git a/drivers/net/dsa/microchip/ksz9477_i2c.c > b/drivers/net/dsa/microchip/ksz9477_i2c.c > new file mode 100644 > index 0000000..29511e9 > --- /dev/null > +++ b/drivers/net/dsa/microchip/ksz9477_i2c.c > @@ -0,0 +1,196 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Microchip KSZ9477 series register access through I2C > + * > + * Copyright (C) 2018 Microchip Technology Inc. > + */ > + > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/i2c.h> > + > +#include "ksz_priv.h" > +#include "ksz_i2c.h" > + > +/* Enough to read all switch port registers. */ > +#define I2C_TX_BUF_LEN 0x100 > + > +static int ksz9477_i2c_read_reg(struct i2c_client *i2c, u32 reg, u8 *val, > + unsigned int len) > +{ > + struct i2c_msg msg[2]; > + int ret; > + > + val[0] = (u8)(reg >> 8); > + val[1] = (u8)reg; > + > + msg[0].addr = i2c->addr; > + msg[0].flags = 0; > + msg[0].len = 2; > + msg[0].buf = val; > + > + msg[1].addr = i2c->addr; > + msg[1].flags = I2C_M_RD; > + msg[1].len = len; > + msg[1].buf = &val[2]; > + > + ret = i2c_transfer(i2c->adapter, msg, 2); > + if (ret == 2) > + ret = 0; > + return ret; > +} > + > +static int ksz9477_i2c_write_reg(struct i2c_client *i2c, u32 reg, u8 *val, > + unsigned int len) > +{ > + struct i2c_msg msg; > + int ret; > + > + val[0] = (u8)(reg >> 8); > + val[1] = (u8)reg; > + > + msg.addr = i2c->addr; > + msg.flags = 0; > + msg.len = 2 + len; > + msg.buf = val; > + > + ret = i2c_transfer(i2c->adapter, &msg, 1); > + if (ret == 1) > + ret = 0; > + return ret; > +} > + > +static int ksz_i2c_read(struct ksz_device *dev, u32 reg, u8 *data, > + unsigned int len) > +{ > + struct i2c_client *i2c = dev->priv; > + int ret; > + > + ret = ksz9477_i2c_read_reg(i2c, reg, dev->txbuf, len); > + if (!ret) > + memcpy(data, &dev->txbuf[2], len); > + return ret; > +} > + > +static int ksz_i2c_write(struct ksz_device *dev, u32 reg, void *data, > + unsigned int len) > +{ > + struct i2c_client *i2c = dev->priv; > + > + if (len > I2C_TX_BUF_LEN) > + len = I2C_TX_BUF_LEN; > + memcpy(&dev->txbuf[2], data, len); > + return ksz9477_i2c_write_reg(i2c, reg, dev->txbuf, len); > +} > + > +static int ksz_i2c_read24(struct ksz_device *dev, u32 reg, u32 *val) > +{ > + int ret; > + > + *val = 0; > + ret = ksz_i2c_read(dev, reg, (u8 *)val, 3); > + if (!ret) { > + *val = be32_to_cpu(*val); > + /* convert to 24bit */ > + *val >>= 8; > + } > + > + return ret; > +} > + > +static int ksz_i2c_write24(struct ksz_device *dev, u32 reg, u32 value) > +{ > + /* make it to big endian 24bit from MSB */ > + value <<= 8; > + value = cpu_to_be32(value); > + return ksz_i2c_write(dev, reg, &value, 3); > +} > + > +static const struct ksz_io_ops ksz9477_i2c_ops = { > + .read8 = ksz_i2c_read8, > + .read16 = ksz_i2c_read16, > + .read24 = ksz_i2c_read24, > + .read32 = ksz_i2c_read32, > + .write8 = ksz_i2c_write8, > + .write16 = ksz_i2c_write16, > + .write24 = ksz_i2c_write24, > + .write32 = ksz_i2c_write32, > + .get = ksz_i2c_get, > + .set = ksz_i2c_set, > +}; > + > +static int ksz9477_i2c_probe(struct i2c_client *i2c, > + const struct i2c_device_id *i2c_id) > +{ > + struct ksz_device *dev; > + int ret; > + > + dev = ksz_switch_alloc(&i2c->dev, &ksz9477_i2c_ops, i2c); > + if (!dev) > + return -ENOMEM; > + > + if (i2c->dev.platform_data) > + dev->pdata = i2c->dev.platform_data; > + > + dev->txbuf = devm_kzalloc(dev->dev, 2 + I2C_TX_BUF_LEN, GFP_KERNEL); > + > + ret = ksz9477_switch_register(dev); > + > + /* Main DSA driver may not be started yet. */ > + if (ret) > + return ret; > + > + i2c_set_clientdata(i2c, dev); > + > + return 0; > +} > + > +static int ksz9477_i2c_remove(struct i2c_client *i2c) > +{ > + struct ksz_device *dev = i2c_get_clientdata(i2c); > + > + if (dev) > + ksz_switch_remove(dev); > + > + return 0; > +} > + > +static void ksz9477_i2c_shutdown(struct i2c_client *i2c) > +{ > + struct ksz_device *dev = i2c_get_clientdata(i2c); > + > + if (dev && dev->dev_ops->shutdown) > + dev->dev_ops->shutdown(dev); > +} > + > +static const struct i2c_device_id ksz9477_i2c_id[] = { > + { "ksz9477-switch", 0 }, > + {}, > +}; > + > +MODULE_DEVICE_TABLE(i2c, ksz9477_i2c_id); > + > +static const struct of_device_id ksz9477_dt_ids[] = { > + { .compatible = "microchip,ksz9477" }, > + { .compatible = "microchip,ksz9897" }, > + {}, > +}; > +MODULE_DEVICE_TABLE(of, ksz9477_dt_ids); > + > +static struct i2c_driver ksz9477_i2c_driver = { > + .driver = { > + .name = "ksz9477-switch", > + .owner = THIS_MODULE, > + .of_match_table = of_match_ptr(ksz9477_dt_ids), > + }, > + .probe = ksz9477_i2c_probe, > + .remove = ksz9477_i2c_remove, > + .shutdown = ksz9477_i2c_shutdown, > + .id_table = ksz9477_i2c_id, > +}; > + > +module_i2c_driver(ksz9477_i2c_driver); > + > +MODULE_AUTHOR("Tristram Ha <tristram...@microchip.com>"); > +MODULE_DESCRIPTION("Microchip KSZ9477 Series Switch I2C access Driver"); > +MODULE_LICENSE("GPL v2"); > diff --git a/drivers/net/dsa/microchip/ksz_i2c.h > b/drivers/net/dsa/microchip/ksz_i2c.h > new file mode 100644 > index 0000000..b9af0a8 > --- /dev/null > +++ b/drivers/net/dsa/microchip/ksz_i2c.h > @@ -0,0 +1,69 @@ > +/* SPDX-License-Identifier: GPL-2.0 > + * Microchip KSZ series I2C access common header > + * > + * Copyright (C) 2018 Microchip Technology Inc. > + * Tristram Ha <tristram...@microchip.com> > + */ > + > +#ifndef __KSZ_I2C_H > +#define __KSZ_I2C_H > + > +/* Chip dependent I2C access */ > +static int ksz_i2c_read(struct ksz_device *dev, u32 reg, u8 *data, > + unsigned int len); > +static int ksz_i2c_write(struct ksz_device *dev, u32 reg, void *data, > + unsigned int len); > + > +static int ksz_i2c_read8(struct ksz_device *dev, u32 reg, u8 *val) > +{ > + return ksz_i2c_read(dev, reg, val, 1); > +} > + > +static int ksz_i2c_read16(struct ksz_device *dev, u32 reg, u16 *val) > +{ > + int ret = ksz_i2c_read(dev, reg, (u8 *)val, 2); > + > + if (!ret) > + *val = be16_to_cpu(*val); > + > + return ret; > +} > + > +static int ksz_i2c_read32(struct ksz_device *dev, u32 reg, u32 *val) > +{ > + int ret = ksz_i2c_read(dev, reg, (u8 *)val, 4); > + > + if (!ret) > + *val = be32_to_cpu(*val); > + > + return ret; > +} > + > +static int ksz_i2c_write8(struct ksz_device *dev, u32 reg, u8 value) > +{ > + return ksz_i2c_write(dev, reg, &value, 1); > +} > + > +static int ksz_i2c_write16(struct ksz_device *dev, u32 reg, u16 value) > +{ > + value = cpu_to_be16(value); > + return ksz_i2c_write(dev, reg, &value, 2); > +} > + > +static int ksz_i2c_write32(struct ksz_device *dev, u32 reg, u32 value) > +{ > + value = cpu_to_be32(value); > + return ksz_i2c_write(dev, reg, &value, 4); > +} > + > +static int ksz_i2c_get(struct ksz_device *dev, u32 reg, void *data, size_t > len) > +{ > + return ksz_i2c_read(dev, reg, data, len); > +} > + > +static int ksz_i2c_set(struct ksz_device *dev, u32 reg, void *data, size_t > len) > +{ > + return ksz_i2c_write(dev, reg, data, len); > +} > + > +#endif > -- > 1.9.1 >
I don't know if at the end this is going to be changed together with spi etc to use regmap API but I tested this code as it is and it works. So you can add my Tested-by if you want: Tested-by: Sergio Paracuellos <sergio.paracuel...@gmail.com> Best regards, Sergio Paracuellos