Messages in this thread | | | Date | Tue, 12 Nov 2013 20:32:20 +0100 | Subject | Re: [PATCH] gpio: Add support for SX151x SPI GPIO Expanders | From | Linus Walleij <> |
| |
Gregory,
this is your driver, can you take a look at this patch?
Yours, Linus Walleij
On Thu, Oct 24, 2013 at 11:51 AM, Viktar Palstsiuk <viktar.palstsiuk@promwad.com> wrote:
> Isolated i2c specific parts and > added spi bindings for the sx151x devices. > > Signed-off-by: Viktar Palstsiuk <viktar.palstsiuk@promwad.com> > --- > drivers/gpio/Kconfig | 10 +- > drivers/gpio/gpio-sx150x.c | 267 +++++++++++++++++++++++++++++++++++++++------ > 2 files changed, 239 insertions(+), 38 deletions(-) > > diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig > index b6ed304..4d296ab 100644 > --- a/drivers/gpio/Kconfig > +++ b/drivers/gpio/Kconfig > @@ -465,15 +465,15 @@ config GPIO_RC5T583 > of RC5T583 device through standard gpio library. > > config GPIO_SX150X > - bool "Semtech SX150x I2C GPIO expander" > - depends on I2C=y > + bool "Semtech SX15xx I2C/SPI GPIO expander" > + depends on SPI_MASTER || I2C > default n > help > - Say yes here to provide support for Semtech SX150-series I2C > + Say yes here to provide support for Semtech SX15xx-series I2C/SPI > GPIO expanders. Compatible models include: > > - 8 bits: sx1508q > - 16 bits: sx1509q > + 8 bits: sx1508q, sx1511 > + 16 bits: sx1509q, sx1512 > > config GPIO_STMPE > bool "STMPE GPIOs" > diff --git a/drivers/gpio/gpio-sx150x.c b/drivers/gpio/gpio-sx150x.c > index d2983e9..bee1661 100644 > --- a/drivers/gpio/gpio-sx150x.c > +++ b/drivers/gpio/gpio-sx150x.c > @@ -22,9 +22,16 @@ > #include <linux/module.h> > #include <linux/mutex.h> > #include <linux/slab.h> > +#include <linux/spi/spi.h> > #include <linux/workqueue.h> > #include <linux/i2c/sx150x.h> > > +/** > + * SX15xx types supported by driver > + */ > +#define SX15_TYPE_8_GPIOS 0 > +#define SX15_TYPE_16_GPIOS 1 > + > #define NO_UPDATE_PENDING -1 > > struct sx150x_device_data { > @@ -43,9 +50,17 @@ struct sx150x_device_data { > u8 ngpios; > }; > > +struct sx150x_chip; > + > +struct sx15xx_ops { > + s32 (*read)(struct device *dev, u8 reg, u8 *val); > + s32 (*write)(struct device *dev, u8 reg, u8 val); > +}; > + > struct sx150x_chip { > struct gpio_chip gpio_chip; > - struct i2c_client *client; > + struct device *dev; > + const struct sx15xx_ops *ops; > const struct sx150x_device_data *dev_cfg; > int irq_summary; > int irq_base; > @@ -59,7 +74,7 @@ struct sx150x_chip { > }; > > static const struct sx150x_device_data sx150x_devices[] = { > - [0] = { /* sx1508q */ > + [SX15_TYPE_8_GPIOS] = { /* sx1508q */ > .reg_pullup = 0x03, > .reg_pulldn = 0x04, > .reg_drain = 0x05, > @@ -74,7 +89,7 @@ static const struct sx150x_device_data sx150x_devices[] = { > .reg_reset = 0x7d, > .ngpios = 8 > }, > - [1] = { /* sx1509q */ > + [SX15_TYPE_16_GPIOS] = { /* sx1509q */ > .reg_pullup = 0x07, > .reg_pulldn = 0x09, > .reg_drain = 0x0b, > @@ -91,37 +106,86 @@ static const struct sx150x_device_data sx150x_devices[] = { > }, > }; > > -static const struct i2c_device_id sx150x_id[] = { > - {"sx1508q", 0}, > - {"sx1509q", 1}, > - {} > -}; > -MODULE_DEVICE_TABLE(i2c, sx150x_id); > +#ifdef CONFIG_I2C > > -static s32 sx150x_i2c_write(struct i2c_client *client, u8 reg, u8 val) > +static s32 sx150x_i2c_write(struct device *dev, u8 reg, u8 val) > { > + struct i2c_client *client = to_i2c_client(dev); > s32 err = i2c_smbus_write_byte_data(client, reg, val); > > if (err < 0) > - dev_warn(&client->dev, > + dev_warn(dev, > "i2c write fail: can't write %02x to %02x: %d\n", > val, reg, err); > return err; > } > > -static s32 sx150x_i2c_read(struct i2c_client *client, u8 reg, u8 *val) > +static s32 sx150x_i2c_read(struct device *dev, u8 reg, u8 *val) > { > + struct i2c_client *client = to_i2c_client(dev); > s32 err = i2c_smbus_read_byte_data(client, reg); > > if (err >= 0) > *val = err; > else > - dev_warn(&client->dev, > + dev_warn(dev, > "i2c read fail: can't read from %02x: %d\n", > reg, err); > return err; > } > > +static const struct sx15xx_ops sx150x_ops = { > + .read = sx150x_i2c_read, > + .write = sx150x_i2c_write, > +}; > + > +#endif /* CONFIG_I2C */ > + > +#ifdef CONFIG_SPI_MASTER > + > +static s32 sx151x_spi_write(struct device *dev, u8 reg, u8 val) > +{ > + struct spi_device *spi = to_spi_device(dev); > + u8 tx[2]; > + s32 err; > + > + tx[0] = reg; > + tx[1] = val; > + > + err = spi_write(spi, tx, sizeof tx); > + > + if (err < 0) > + dev_warn(dev, > + "spi write fail: can't write %02x to %02x: %d\n", > + val, reg, err); > + return err; > +} > + > +static s32 sx151x_spi_read(struct device *dev, u8 reg, u8 *val) > +{ > + struct spi_device *spi = to_spi_device(dev); > + u8 tx[1], rx[1]; > + s32 err; > + > + tx[0] = reg | 0x80; > + err = spi_write_then_read(spi, tx, sizeof tx, rx, sizeof rx); > + > + if (err >= 0) > + *val = rx[0]; > + else > + dev_warn(dev, > + "spi read fail: can't read from %02x: %d\n", > + reg, err); > + return err; > +} > + > +static const struct sx15xx_ops sx151x_ops = { > + .read = sx151x_spi_read, > + .write = sx151x_spi_write, > +}; > + > +#endif /* CONFIG_SPI_MASTER */ > + > static inline bool offset_is_oscio(struct sx150x_chip *chip, unsigned offset) > { > return (chip->dev_cfg->ngpios == offset); > @@ -164,13 +228,12 @@ static s32 sx150x_write_cfg(struct sx150x_chip *chip, > s32 err; > > sx150x_find_cfg(offset, width, ®, &mask, &shift); > - err = sx150x_i2c_read(chip->client, reg, &data); > + err = chip->ops->read(chip->dev, reg, &data); > if (err < 0) > return err; > - > data &= ~mask; > data |= (val << shift) & mask; > - return sx150x_i2c_write(chip->client, reg, data); > + return chip->ops->write(chip->dev, reg, data); > } > > static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset) > @@ -182,7 +245,7 @@ static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset) > s32 err; > > sx150x_find_cfg(offset, 1, ®, &mask, &shift); > - err = sx150x_i2c_read(chip->client, reg, &data); > + err = chip->ops->read(chip->dev, reg, &data); > if (err >= 0) > err = (data & mask) != 0 ? 1 : 0; > > @@ -191,7 +254,7 @@ static int sx150x_get_io(struct sx150x_chip *chip, unsigned offset) > > static void sx150x_set_oscio(struct sx150x_chip *chip, int val) > { > - sx150x_i2c_write(chip->client, > + chip->ops->write(chip->dev, > chip->dev_cfg->reg_clock, > (val ? 0x1f : 0x10)); > } > @@ -361,13 +424,13 @@ static irqreturn_t sx150x_irq_thread_fn(int irq, void *dev_id) > int i; > > for (i = (chip->dev_cfg->ngpios / 8) - 1; i >= 0; --i) { > - err = sx150x_i2c_read(chip->client, > + err = chip->ops->read(chip->dev, > chip->dev_cfg->reg_irq_src - i, > &val); > if (err < 0) > continue; > > - sx150x_i2c_write(chip->client, > + chip->ops->write(chip->dev, > chip->dev_cfg->reg_irq_src - i, > val); > for (n = 0; n < 8; ++n) { > @@ -421,15 +484,17 @@ out: > } > > static void sx150x_init_chip(struct sx150x_chip *chip, > - struct i2c_client *client, > + struct device *dev, > + const struct sx15xx_ops *ops, > kernel_ulong_t driver_data, > struct sx150x_platform_data *pdata) > { > mutex_init(&chip->lock); > > - chip->client = client; > + chip->dev = dev; > + chip->ops = ops; > chip->dev_cfg = &sx150x_devices[driver_data]; > - chip->gpio_chip.label = client->name; > + chip->gpio_chip.label = dev_name(dev); > chip->gpio_chip.direction_input = sx150x_gpio_direction_input; > chip->gpio_chip.direction_output = sx150x_gpio_direction_output; > chip->gpio_chip.get = sx150x_gpio_get; > @@ -441,7 +506,7 @@ static void sx150x_init_chip(struct sx150x_chip *chip, > if (pdata->oscio_is_gpo) > ++chip->gpio_chip.ngpio; > > - chip->irq_chip.name = client->name; > + chip->irq_chip.name = dev_name(dev); > chip->irq_chip.irq_mask = sx150x_irq_mask; > chip->irq_chip.irq_unmask = sx150x_irq_unmask; > chip->irq_chip.irq_set_type = sx150x_irq_set_type; > @@ -462,7 +527,7 @@ static int sx150x_init_io(struct sx150x_chip *chip, u8 base, u16 cfg) > unsigned n; > > for (n = 0; err >= 0 && n < (chip->dev_cfg->ngpios / 8); ++n) > - err = sx150x_i2c_write(chip->client, base - n, cfg >> (n * 8)); > + err = chip->ops->write(chip->dev, base - n, cfg >> (n * 8)); > return err; > } > > @@ -470,13 +535,13 @@ static int sx150x_reset(struct sx150x_chip *chip) > { > int err; > > - err = i2c_smbus_write_byte_data(chip->client, > + err = chip->ops->write(chip->dev, > chip->dev_cfg->reg_reset, > 0x12); > if (err < 0) > return err; > > - err = i2c_smbus_write_byte_data(chip->client, > + err = chip->ops->write(chip->dev, > chip->dev_cfg->reg_reset, > 0x34); > return err; > @@ -493,7 +558,7 @@ static int sx150x_init_hw(struct sx150x_chip *chip, > return err; > } > > - err = sx150x_i2c_write(chip->client, > + err = chip->ops->write(chip->dev, > chip->dev_cfg->reg_misc, > 0x01); > if (err < 0) > @@ -548,7 +613,7 @@ static int sx150x_install_irq_chip(struct sx150x_chip *chip, > #endif > } > > - err = devm_request_threaded_irq(&chip->client->dev, > + err = devm_request_threaded_irq(chip->dev, > irq_summary, > NULL, > sx150x_irq_thread_fn, > @@ -574,6 +639,8 @@ static void sx150x_remove_irq_chip(struct sx150x_chip *chip) > } > } > > +#ifdef CONFIG_I2C > + > static int sx150x_probe(struct i2c_client *client, > const struct i2c_device_id *id) > { > @@ -595,7 +662,7 @@ static int sx150x_probe(struct i2c_client *client, > if (!chip) > return -ENOMEM; > > - sx150x_init_chip(chip, client, id->driver_data, pdata); > + sx150x_init_chip(chip, &client->dev, &sx150x_ops, id->driver_data, pdata); > rc = sx150x_init_hw(chip, pdata); > if (rc < 0) > return rc; > @@ -636,6 +703,13 @@ static int sx150x_remove(struct i2c_client *client) > return 0; > } > > +static const struct i2c_device_id sx150x_id[] = { > + {"sx1508q", SX15_TYPE_8_GPIOS}, > + {"sx1509q", SX15_TYPE_16_GPIOS}, > + {} > +}; > +MODULE_DEVICE_TABLE(i2c, sx150x_id); > + > static struct i2c_driver sx150x_driver = { > .driver = { > .name = "sx150x", > @@ -646,19 +720,146 @@ static struct i2c_driver sx150x_driver = { > .id_table = sx150x_id, > }; > > -static int __init sx150x_init(void) > +static int __init sx150x_i2c_init(void) > { > return i2c_add_driver(&sx150x_driver); > } > + > +static void __exit sx150x_i2c_exit(void) > +{ > + return i2c_del_driver(&sx150x_driver); > +} > + > +#else > + > +static int __init sx150x_i2c_init(void) { return 0; } > +static void sx150x_i2c_exit(void) { } > + > +#endif /* CONFIG_I2C */ > + > +#ifdef CONFIG_SPI_MASTER > + > +static int sx151x_probe(struct spi_device *spi) > +{ > + struct sx150x_platform_data *pdata; > + struct sx150x_chip *chip; > + int rc; > + > + pdata = dev_get_platdata(&spi->dev); > + if (!pdata) { > + return -EINVAL; > + } > + > + chip = devm_kzalloc(&spi->dev, > + sizeof(struct sx150x_chip), GFP_KERNEL); > + if (!chip) > + return -ENOMEM; > + > + sx150x_init_chip(chip, &spi->dev, &sx151x_ops, > + spi_get_device_id(spi)->driver_data, pdata); > + rc = sx150x_init_hw(chip, pdata); > + if (rc < 0) > + return rc; > + > + rc = gpiochip_add(&chip->gpio_chip); > + if (rc < 0) > + return rc; > + > + if (pdata->irq_summary >= 0) { > + rc = sx150x_install_irq_chip(chip, > + pdata->irq_summary, > + pdata->irq_base); > + if (rc < 0) > + goto probe_fail_post_gpiochip_add; > + } > + > + spi_set_drvdata(spi, chip); > + > + return 0; > +probe_fail_post_gpiochip_add: > + WARN_ON(gpiochip_remove(&chip->gpio_chip) < 0); > + return rc; > +} > + > +static int sx151x_remove(struct spi_device *spi) > +{ > + struct sx150x_chip *chip; > + int rc; > + > + chip = spi_get_drvdata(spi); > + rc = gpiochip_remove(&chip->gpio_chip); > + if (rc < 0) > + return rc; > + > + if (chip->irq_summary >= 0) > + sx150x_remove_irq_chip(chip); > + > + return 0; > +} > + > +static const struct spi_device_id sx151x_ids[] = { > + { "sx1511", SX15_TYPE_8_GPIOS }, > + { "sx1512", SX15_TYPE_16_GPIOS }, > + { }, > +}; > +MODULE_DEVICE_TABLE(spi, sx151x_ids); > + > +static struct spi_driver sx151x_driver = { > + .probe = sx151x_probe, > + .remove = sx151x_remove, > + .id_table = sx151x_ids, > + .driver = { > + .name = "sx151x", > + .owner = THIS_MODULE, > + }, > +}; > + > +static int __init sx151x_spi_init(void) > +{ > + return spi_register_driver(&sx151x_driver); > +} > + > +static void sx151x_spi_exit(void) > +{ > + spi_unregister_driver(&sx151x_driver); > +} > + > +#else > + > +static int __init sx151x_spi_init(void) { return 0; } > +static void sx151x_spi_exit(void) { } > + > +#endif /* CONFIG_SPI_MASTER */ > + > +static int __init sx150x_init(void) > +{ > + int ret; > + > + ret = sx151x_spi_init(); > + if (ret) > + goto spi_fail; > + > + ret = sx150x_i2c_init(); > + if (ret) > + goto i2c_fail; > + > + return 0; > + > + i2c_fail: > + sx151x_spi_exit(); > + spi_fail: > + return ret; > +} > subsys_initcall(sx150x_init); > > static void __exit sx150x_exit(void) > { > - return i2c_del_driver(&sx150x_driver); > + sx151x_spi_exit(); > + sx150x_i2c_exit(); > } > module_exit(sx150x_exit); > > MODULE_AUTHOR("Gregory Bean <gbean@codeaurora.org>"); > -MODULE_DESCRIPTION("Driver for Semtech SX150X I2C GPIO Expanders"); > +MODULE_DESCRIPTION("Driver for Semtech SX15xx I2C/SPI GPIO Expanders"); > MODULE_LICENSE("GPL v2"); > MODULE_ALIAS("i2c:sx150x"); > -- > 1.8.4.rc3 >
| |