From: Jonathan Cameron <jic23@cam.ac.uk>
To: Jonathan McDowell <noodles@earth.li>
Cc: Grant Likely <grant.likely@secretlab.ca>,
LKML <linux-kernel@vger.kernel.org>
Subject: Re: [PATCH] Add support for VSC055 Enhanced Two-Wire Serial Backplane controller
Date: Fri, 06 May 2011 14:40:11 +0100 [thread overview]
Message-ID: <4DC3FA3B.5050201@cam.ac.uk> (raw)
In-Reply-To: <20110505220018.GF28362@earth.li>
On 05/05/11 23:00, Jonathan McDowell wrote:
> On Thu, May 05, 2011 at 10:03:31AM +0100, Jonathan Cameron wrote:
>> Sorry Jonathan and Grant. I accidentally sent this only to you and
>> not to lkml, hence the resend.
>
> That's my fault; I sent the initial revision to Grant and missed the
> lkml CC, then just bounced it to lkml.
>
>> Couple of suggestions for minor tidying up inline.
>
> Thanks. Things I've taken on board and updated:
>
> * Use u8, not uint8_t for 8 bit types.
> * Remove read_reg/write_reg wrappers and use i2c_smbus functions
> directly.
> * Fold container_of usage into struct vsc055_chip variable definition.
> * Use !! instead of ? 1 : 0.
> * Rename i2c_lock to reg_lock.
> * Move comment about cached status to correct location.
>
> Things I left:
>
> * pin/block - when they're used multiple times I think this is clearer
> and reflects how the data sheet talks about the GPIO lines.
> * Display version number - I think this is useful to indicate the
> device has been correctly found (I'd prefer it if there was a
> certain way to ID I2C devices).
Fair enough to both of those.
>
> -----
>
> Add support for the GPIOs on Maxim VSC055 I2C Enhanced Two-Wire Serial
> Backplane controller.
>
> This chip features 64 bits of user-definable, bidirectional GPIOs. It also
> has the ability to do PWM fan control output and LED flashing, but support
> for that functionality is not included at present.
>
> Signed-off-by: Jonathan McDowell <noodles@earth.li>
Acked-by: Jonathan Cameron <jic23@cam.ac.uk>
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index cc150db..4c5de65 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -255,6 +255,13 @@ config GPIO_TWL4030
> Say yes here to access the GPIO signals of various multi-function
> power management chips from Texas Instruments.
>
> +config GPIO_VSC055
> + tristate "VSC055 GPIOs"
> + depends on I2C
> + help
> + Say yes here to access the GPIOs found on the Maxim VSC055 Enhanced
> + Two-Wire Serial Backplane controller.
> +
> config GPIO_WM831X
> tristate "WM831x GPIOs"
> depends on MFD_WM831X
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index d2752b2..f82491d 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -42,4 +42,5 @@ obj-$(CONFIG_GPIO_RDC321X) += rdc321x-gpio.o
> obj-$(CONFIG_GPIO_JANZ_TTL) += janz-ttl.o
> obj-$(CONFIG_GPIO_SX150X) += sx150x.o
> obj-$(CONFIG_GPIO_VX855) += vx855_gpio.o
> +obj-$(CONFIG_GPIO_VSC055) += vsc055.o
> obj-$(CONFIG_GPIO_ML_IOH) += ml_ioh_gpio.o
> diff --git a/drivers/gpio/vsc055.c b/drivers/gpio/vsc055.c
> new file mode 100644
> index 0000000..1ca6ae0
> --- /dev/null
> +++ b/drivers/gpio/vsc055.c
> @@ -0,0 +1,283 @@
> +/*
> + * vsc055.c - Enhanced Two-Wire Serial Backplane Control with 64 I/O lines.
> + *
> + * Copyright (C) 2011 Jonathan McDowell <noodles@earth.li>
> + *
> + * Derived from drivers/gpio/pca953x.c
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 2 of the License.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/gpio.h>
> +#include <linux/i2c.h>
> +#include <linux/i2c/vsc055.h>
> +#include <linux/slab.h>
> +
> +#define VSC055_GPD0 0x00
> +#define VSC055_DDP0 0x10
> +#define VSC055_BCIS 0xF8
> +#define VSC055_BCT 0xFC
> +#define VSC055_VER 0xFF
> +
> +static const struct i2c_device_id vsc055_id[] = {
> + { "vsc055" },
> + { }
> +};
> +MODULE_DEVICE_TABLE(i2c, vsc055_id);
> +
> +struct vsc055_chip {
> + unsigned gpio_start;
> + u8 reg_output[8];
> + u8 reg_direction[8];
> + struct mutex reg_lock;
> +
> + struct i2c_client *client;
> + struct gpio_chip gpio_chip;
> + const char *const *names;
> +};
> +
> +static int vsc055_gpio_direction_input(struct gpio_chip *gc, unsigned off)
> +{
> + struct vsc055_chip *chip = container_of(gc, struct vsc055_chip, gpio_chip);
> + u8 reg_val;
> + int ret;
> + int block, pin;
> +
> + block = off >> 3;
> + pin = off & 7;
> +
> + mutex_lock(&chip->reg_lock);
> + reg_val = chip->reg_direction[block] | (1u << pin);
> + ret = i2c_smbus_write_byte_data(chip->client, VSC055_DDP0 + block, reg_val);
> + if (ret)
> + goto exit;
> +
> + chip->reg_direction[block] = reg_val;
> +exit:
> + mutex_unlock(&chip->reg_lock);
> + return ret;
> +}
> +
> +static int vsc055_gpio_direction_output(struct gpio_chip *gc,
> + unsigned off, int val)
> +{
> + struct vsc055_chip *chip = container_of(gc, struct vsc055_chip, gpio_chip);
> + u8 reg_val;
> + int ret;
> + int block, pin;
> +
> + block = off >> 3;
> + pin = off & 7;
> +
> + mutex_lock(&chip->reg_lock);
> + /* set output level */
> + if (val)
> + reg_val = chip->reg_output[block] | (1u << pin);
> + else
> + reg_val = chip->reg_output[block] & ~(1u << pin);
> +
> + ret = i2c_smbus_write_byte_data(chip->client, VSC055_GPD0 + block, reg_val);
> + if (ret)
> + goto exit;
> +
> + chip->reg_output[block] = reg_val;
> +
> + /* then direction */
> + reg_val = chip->reg_direction[block] & ~(1u << off);
> + ret = i2c_smbus_write_byte_data(chip->client, VSC055_DDP0 + block, reg_val);
> + if (ret)
> + goto exit;
> +
> + chip->reg_direction[block] = reg_val;
> + ret = 0;
> +exit:
> + mutex_unlock(&chip->reg_lock);
> + return ret;
> +}
> +
> +static int vsc055_gpio_get_value(struct gpio_chip *gc, unsigned off)
> +{
> + struct vsc055_chip *chip = container_of(gc, struct vsc055_chip, gpio_chip);
> + u8 reg_val;
> +
> + mutex_lock(&chip->reg_lock);
> + reg_val = i2c_smbus_read_byte_data(chip->client, VSC055_GPD0 + (off >> 3));
> + mutex_unlock(&chip->reg_lock);
> + if (reg_val < 0) {
> + dev_err(&chip->client->dev, "failed to read GPIO value.\n");
> + return 0;
> + }
> +
> + return !!(reg_val & (1u << (off & 7)));
> +}
> +
> +static void vsc055_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
> +{
> + struct vsc055_chip *chip = container_of(gc, struct vsc055_chip, gpio_chip);
> + u8 reg_val;
> + int ret;
> + int block, pin;
> +
> + block = off >> 3;
> + pin = off & 7;
> +
> + mutex_lock(&chip->reg_lock);
> + if (val)
> + reg_val = chip->reg_output[block] | (1u << pin);
> + else
> + reg_val = chip->reg_output[block] & ~(1u << pin);
> +
> + ret = i2c_smbus_write_byte_data(chip->client, VSC055_GPD0 + block, reg_val);
> + if (ret)
> + goto exit;
> +
> + chip->reg_output[block] = reg_val;
> +exit:
> + mutex_unlock(&chip->reg_lock);
> +}
> +
> +static void vsc055_setup_gpio(struct vsc055_chip *chip)
> +{
> + struct gpio_chip *gc = &chip->gpio_chip;;
> +
> + gc->direction_input = vsc055_gpio_direction_input;
> + gc->direction_output = vsc055_gpio_direction_output;
> + gc->get = vsc055_gpio_get_value;
> + gc->set = vsc055_gpio_set_value;
> + gc->can_sleep = 1;
> +
> + gc->base = chip->gpio_start;
> + gc->ngpio = 64;
> + gc->label = chip->client->name;
> + gc->dev = &chip->client->dev;
> + gc->owner = THIS_MODULE;
> + gc->names = chip->names;
> +}
> +
> +static int __devinit vsc055_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct vsc055_platform_data *pdata;
> + struct vsc055_chip *chip;
> + int ret;
> + int i;
> +
> + chip = kzalloc(sizeof(struct vsc055_chip), GFP_KERNEL);
> + if (chip == NULL)
> + return -ENOMEM;
> +
> + pdata = client->dev.platform_data;
> + if (pdata == NULL) {
> + dev_dbg(&client->dev, "no platform data\n");
> + ret = -EINVAL;
> + goto out_failed;
> + }
> +
> + chip->client = client;
> +
> + chip->gpio_start = pdata->gpio_base;
> +
> + chip->names = pdata->names;
> +
> + mutex_init(&chip->reg_lock);
> +
> + vsc055_setup_gpio(chip);
> +
> + /*
> + * Initialize cached registers from their original values.
> + * We can't share this chip with another i2c master.
> + */
> + for (i = 0; i < 8; i++) {
> + ret = i2c_smbus_read_byte_data(chip->client, VSC055_GPD0 + i);
> + if (ret < 0)
> + goto out_failed;
> + chip->reg_output[i] = ret;
> +
> + ret = i2c_smbus_read_byte_data(chip->client, VSC055_DDP0 + i);
> + if (ret < 0)
> + goto out_failed;
> + chip->reg_direction[i] = ret;
> + }
> +
> + ret = i2c_smbus_read_byte_data(chip->client, VSC055_VER);
> + if (ret < 0)
> + goto out_failed;
> + dev_info(&client->dev, "Found VSC055 revision %d.%d\n", ret >> 4, ret & 0xF);
> +
> + ret = gpiochip_add(&chip->gpio_chip);
> + if (ret)
> + goto out_failed;
> +
> + if (pdata->setup) {
> + ret = pdata->setup(client, chip->gpio_chip.base,
> + chip->gpio_chip.ngpio, pdata->context);
> + if (ret < 0)
> + dev_warn(&client->dev, "setup failed, %d\n", ret);
> + }
> +
> + i2c_set_clientdata(client, chip);
> + return 0;
> +
> +out_failed:
> + kfree(chip);
> + return ret;
> +}
> +
> +static int vsc055_remove(struct i2c_client *client)
> +{
> + struct vsc055_platform_data *pdata = client->dev.platform_data;
> + struct vsc055_chip *chip = i2c_get_clientdata(client);
> + int ret = 0;
> +
> + if (pdata->teardown) {
> + ret = pdata->teardown(client, chip->gpio_chip.base,
> + chip->gpio_chip.ngpio, pdata->context);
> + if (ret < 0) {
> + dev_err(&client->dev, "%s failed, %d\n",
> + "teardown", ret);
> + return ret;
> + }
> + }
> +
> + ret = gpiochip_remove(&chip->gpio_chip);
> + if (ret) {
> + dev_err(&client->dev, "%s failed, %d\n",
> + "gpiochip_remove()", ret);
> + return ret;
> + }
> +
> + kfree(chip);
> + return 0;
> +}
> +
> +static struct i2c_driver vsc055_driver = {
> + .driver = {
> + .name = "vsc055",
> + },
> + .probe = vsc055_probe,
> + .remove = vsc055_remove,
> + .id_table = vsc055_id,
> +};
> +
> +static int __init vsc055_init(void)
> +{
> + return i2c_add_driver(&vsc055_driver);
> +}
> +/* register after i2c postcore initcall and before
> + * subsys initcalls that may rely on these GPIOs
> + */
> +subsys_initcall(vsc055_init);
> +
> +static void __exit vsc055_exit(void)
> +{
> + i2c_del_driver(&vsc055_driver);
> +}
> +module_exit(vsc055_exit);
> +
> +MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>");
> +MODULE_DESCRIPTION("GPIO driver for VSC055");
> +MODULE_LICENSE("GPL");
> diff --git a/include/linux/i2c/vsc055.h b/include/linux/i2c/vsc055.h
> new file mode 100644
> index 0000000..dc1462b
> --- /dev/null
> +++ b/include/linux/i2c/vsc055.h
> @@ -0,0 +1,22 @@
> +#ifndef _LINUX_VSC055_H
> +#define _LINUX_VSC055_H
> +
> +#include <linux/types.h>
> +#include <linux/i2c.h>
> +
> +/* Platform data for the VSC055 64-bit I/O expander driver */
> +
> +struct vsc055_platform_data {
> + unsigned gpio_base; /* number of the first GPIO */
> + void *context; /* param to setup/teardown */
> +
> + int (*setup)(struct i2c_client *client,
> + unsigned gpio, unsigned ngpio,
> + void *context);
> + int (*teardown)(struct i2c_client *client,
> + unsigned gpio, unsigned ngpio,
> + void *context);
> + const char *const *names;
> +};
> +
> +#endif /* _LINUX_VSC055_H */
> -----
> J.
>
prev parent reply other threads:[~2011-05-06 13:37 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-05-05 4:12 [PATCH] Add support for VSC055 Enhanced Two-Wire Serial Backplane controller Jonathan McDowell
2011-05-05 9:03 ` Jonathan Cameron
2011-05-05 22:00 ` Jonathan McDowell
2011-05-06 13:40 ` Jonathan Cameron [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4DC3FA3B.5050201@cam.ac.uk \
--to=jic23@cam.ac.uk \
--cc=grant.likely@secretlab.ca \
--cc=linux-kernel@vger.kernel.org \
--cc=noodles@earth.li \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.