Re: [PATCH v2 2/4] gpio: add viperboard gpio driver

From: Linus Walleij
Date: Wed Oct 24 2012 - 03:53:02 EST


On Tue, Oct 23, 2012 at 5:24 PM, Lars Poeschel <poeschel@xxxxxxxxxxx> wrote:
> On Tuesday 16 October 2012 at 19:11:10, Linus Walleij wrote:
>> On Tue, Oct 16, 2012 at 3:38 PM, Lars Poeschel <poeschel@xxxxxxxxxxx> wrote:
>> > I had a look at regmap. This is interesting. But there is no regmap_bus
>> > implementation for usb. Are you pointing me in this direction ? ;-)
>>
>> I was more thinking about whether it would be useful to use for this
>> MFD hub. So that for this MFD and it's children, you would use
>> regmap to:
>>
>> 1) Cache registers
>> 2) Marshall the register read/writes across USB
>>
>> I don't think it would be applicable for USB, as reading & writing
>> registers across USB is a pretty odd usecase. (IIRC using control
>> transfers
>> right?)
>
> Yes, right.
>
>> > And it is not guaranteed, that this "address" and "value"
>> > are at the same position for other usb adapters. And this is getting even
>> > worse, if I think of reading values out of the viperboard...
>>
>> I was mainly thinking about the Viperboard MFD complex.
>
> Ok, I tried to implement it (at least for the gpioa part of the driver).
> It does not work. I got stuck. There are three problems I could not solve:

So I'm looping in Mark Brown to get some hints whether the following
can be resolved easily or needs to be given up on...

> To be able to use the caching, I have to implement a volatile_reg function. I
> have to do this, because caching can only happen, when the gpio pin is an
> output. If it is an input, I want to read the pin - caching has to be turned
> off. So in my vprbrd_volatile_reg function I have to check if a pin is set to
> output and return false or true. I can not query a regmap register inside that
> function if a pin is an output or not, this would deadlock, because there is
> a mutex lock inside regmap, so I have to cache this outside of regmap.
> This is a bit strange, to use the cache, setup another own cache. (1st
> problem)
> I chose my per device structure struct vprbrd_gpio to hold this cache, but I
> can not reach it. You see in the patch, I try to container_of up to my
> structure. This does not work, because struct regmap is not public available.
> (2nd problem)

The idea is that regmap is self-contained. If you need to change
it's semantics the place to patch is in regmap itself.

> Setting the direction to output is an atomic set direction and set value.
> The register number is different from setting the value only. So after a
> successful call, I want to update the cache of output value using
> regcache_write. This is also not publicy available api. The only thing I could
> do is invalidate the whole cache using regcache_mark_dirty. (3rd problem)

Same thing.

> I attach my current working state here. This is NOT WORKING and only as
> reference.

So if you want to do this with regmap, I suggest the way forward would
be to go solve it at the root by implementing something like
drivers/base/regmap/regmap-usb-ctrl.c with your desired semantics
and using that.

However I *do* understand that requesting this may be a bit thick,
so I'm happy to accept the older patch with custom caching
now.

> Regards,
> Lars
> ---
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 8382dc8..48be4ba 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -636,4 +636,17 @@ config GPIO_MSIC
> Enable support for GPIO on intel MSIC controllers found in
> intel MID devices
>
> +comment "USB GPIO expanders:"
> +
> +config GPIO_VIPERBOARD
> + tristate "Viperboard GPIO a & b support"
> + depends on MFD_VIPERBOARD && USB
> + help
> + Say yes here to access the GPIO signals of Nano River
> + Technologies Viperboard. There are two GPIO chips on the
> + board: gpioa and gpiob.
> + See viperboard API specification and Nano
> + River Tech's viperboard.h for detailed meaning
> + of the module parameters.
> +
> endif
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 0ffaa84..71cc896 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -69,6 +69,7 @@ obj-$(CONFIG_GPIO_TPS65910) += gpio-tps65910.o
> obj-$(CONFIG_GPIO_TPS65912) += gpio-tps65912.o
> obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o
> obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
> +obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o
> obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o
> obj-$(CONFIG_GPIO_VT8500) += gpio-vt8500.o
> obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o
> diff --git a/drivers/gpio/gpio-viperboard.c b/drivers/gpio/gpio-viperboard.c
> new file mode 100644
> index 0000000..2dc6c9f
> --- /dev/null
> +++ b/drivers/gpio/gpio-viperboard.c
> @@ -0,0 +1,618 @@
> +/*
> + * Nano River Technologies viperboard GPIO lib driver
> + *
> + * (C) 2012 by Lemonage GmbH
> + * Author: Lars Poeschel <poeschel@xxxxxxxxxxx>
> + * All rights reserved.
> + *
> + * 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; either version 2 of the License, or (at your
> + * option) any later version.
> + *
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/errno.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +#include <linux/types.h>
> +#include <linux/mutex.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +
> +#include <linux/usb.h>
> +#include <linux/gpio.h>
> +
> +#include <linux/mfd/viperboard.h>
> +
> +#define VPRBRD_GPIOA_CLK_1MHZ 0
> +#define VPRBRD_GPIOA_CLK_100KHZ 1
> +#define VPRBRD_GPIOA_CLK_10KHZ 2
> +#define VPRBRD_GPIOA_CLK_1KHZ 3
> +#define VPRBRD_GPIOA_CLK_100HZ 4
> +#define VPRBRD_GPIOA_CLK_10HZ 5
> +
> +#define VPRBRD_GPIOA_FREQ_DEFAULT 1000
> +
> +#define VPRBRD_GPIOA_CMD_CONT 0x00
> +#define VPRBRD_GPIOA_CMD_PULSE 0x01
> +#define VPRBRD_GPIOA_CMD_PWM 0x02
> +#define VPRBRD_GPIOA_CMD_SETOUT 0x03
> +#define VPRBRD_GPIOA_CMD_SETIN 0x04
> +#define VPRBRD_GPIOA_CMD_SETINT 0x05
> +#define VPRBRD_GPIOA_CMD_GETIN 0x06
> +
> +#define VPRBRD_GPIOB_CMD_SETDIR 0x00
> +#define VPRBRD_GPIOB_CMD_SETVAL 0x01
> +
> +#define VPRBRD_GPIOA_NUM_PINS 16
> +#define VPRBRD_GPIOA_REG_VAL_BASE 0
> +#define VPRBRD_GPIOA_REG_VAL_MAX (1 * VPRBRD_GPIOA_NUM_PINS - 1)
> +#define VPRBRD_GPIOA_REG_DIR_BASE (VPRBRD_GPIOA_REG_VAL_MAX + 1)
> +#define VPRBRD_GPIOA_REG_DIR_MAX (2 * VPRBRD_GPIOA_NUM_PINS - 1)
> +#define VPRBRD_GPIOA_NUM_REGS (VPRBRD_GPIOA_REG_DIR_MAX + 1)
> +
> +struct vprbrd_gpioa_msg {
> + u8 cmd;
> + u8 clk;
> + u8 offset;
> + u8 t1;
> + u8 t2;
> + u8 invert;
> + u8 pwmlevel;
> + u8 outval;
> + u8 risefall;
> + u8 answer;
> + u8 __fill;
> +} __packed;
> +
> +struct vprbrd_gpiob_msg {
> + u8 cmd;
> + u16 val;
> + u16 mask;
> +} __packed;
> +
> +struct vprbrd_gpio {
> + struct gpio_chip gpioa; /* gpio a related things */
> + u32 gpioa_out;
> + u32 gpioa_val;
> + struct gpio_chip gpiob; /* gpio b related things */
> + u32 gpiob_out;
> + u32 gpiob_val;
> + struct vprbrd *vb;
> + struct regmap *regmap;
> +};
> +
> +/* gpioa sampling clock module parameter */
> +static unsigned char gpioa_clk;
> +static unsigned int gpioa_freq = VPRBRD_GPIOA_FREQ_DEFAULT;
> +module_param(gpioa_freq, uint, 0);
> +MODULE_PARM_DESC(gpioa_freq, "gpio a sampling freq in Hz (default is 1000Hz)"
> + "valid values: 10, 100, 1000, 10000, 100000, 1000000");
> +
> +/* ----- begin of gipo a chip -------------------------------------------- */
> +
> +static int regmap_vb_gpio_write(void *context, const void *data, size_t count)
> +{
> + struct vprbrd *vb = (struct vprbrd *)context;
> + struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
> + int ret;
> + const u8 reg = ((const u8 *)data)[0];
> + const u8 val = ((const u8 *)data)[1];
> +
> + dev_dbg(&vb->pdev.dev, "%s count:%i reg:%i, val:%i\n", __FUNCTION__, count, reg, val);
> +
> + if (count != 2)
> + return -ENOTSUPP;
> +
> + mutex_lock(&vb->lock);
> +
> + gamsg->clk = 0x00;
> + gamsg->t1 = 0x00;
> + gamsg->t2 = 0x00;
> + gamsg->invert = 0x00;
> + gamsg->pwmlevel = 0x00;
> + gamsg->outval = val;
> + gamsg->risefall = 0x00;
> + gamsg->answer = 0x00;
> + gamsg->__fill = 0x00;
> +
> + if (reg >= VPRBRD_GPIOA_REG_VAL_BASE &&
> + reg <= VPRBRD_GPIOA_REG_VAL_MAX) {
> + gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT;
> + gamsg->offset = reg;
> + gamsg->outval = val;
> + } else if (reg >= VPRBRD_GPIOA_REG_DIR_BASE &&
> + reg <= VPRBRD_GPIOA_REG_DIR_MAX) {
> + gamsg->offset = reg - VPRBRD_GPIOA_REG_DIR_BASE;
> + if (val > 0) {
> + gamsg->cmd = VPRBRD_GPIOA_CMD_SETIN;
> + gamsg->clk = gpioa_clk;
> + } else {
> + gamsg->cmd = VPRBRD_GPIOA_CMD_SETOUT;
> + }
> + }
> + ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
> + VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000,
> + 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
> + VPRBRD_USB_TIMEOUT_MS);
> +
> + mutex_unlock(&vb->lock);
> +
> + if (ret != sizeof(struct vprbrd_gpioa_msg))
> + return -EREMOTEIO;
> +
> + return 0;
> +}
> +
> +static int regmap_vb_gpio_read(void *context, const void *reg_buf,
> + size_t reg_size, void *val_buf, size_t val_size)
> +{
> + struct vprbrd *vb = (struct vprbrd *)context;
> + struct vprbrd_gpioa_msg *gamsg = (struct vprbrd_gpioa_msg *)vb->buf;
> + int ret, error = 0;
> + const u8 *reg8 = reg_buf;
> +
> + dev_dbg(&vb->pdev.dev, "%s reg_size:%i val_size:%i\n", __FUNCTION__, reg_size, val_size);
> +
> + if (val_size != 1 || reg_size != 1)
> + return -ENOTSUPP;
> +
> + mutex_lock(&vb->lock);
> +
> + gamsg->cmd = VPRBRD_GPIOA_CMD_GETIN;
> + gamsg->clk = 0x00;
> + gamsg->offset = reg8[0];
> + gamsg->t1 = 0x00;
> + gamsg->t2 = 0x00;
> + gamsg->invert = 0x00;
> + gamsg->pwmlevel = 0x00;
> + gamsg->outval = 0x00;
> + gamsg->risefall = 0x00;
> + gamsg->answer = 0x00;
> + gamsg->__fill = 0x00;
> +
> + ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
> + VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_OUT, 0x0000,
> + 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
> + VPRBRD_USB_TIMEOUT_MS);
> + if (ret != sizeof(struct vprbrd_gpioa_msg))
> + error = -EREMOTEIO;
> +
> + ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0),
> + VPRBRD_USB_REQUEST_GPIOA, VPRBRD_USB_TYPE_IN, 0x0000,
> + 0x0000, gamsg, sizeof(struct vprbrd_gpioa_msg),
> + VPRBRD_USB_TIMEOUT_MS);
> + *(u8 *)val_buf = gamsg->answer & 0x01;
> +
> + dev_dbg(&vb->pdev.dev, "%s gamsg->answer:%i val:%i\n", __FUNCTION__, gamsg->answer, *(u8 *)val_buf);
> +
> + mutex_unlock(&vb->lock);
> +
> + if (ret != sizeof(struct vprbrd_gpioa_msg))
> + error = -EREMOTEIO;
> +
> + if (error)
> + return error;
> +
> + return 0;
> +}
> +
> +static const struct regmap_bus regmap_vb_gpio = {
> + .write = regmap_vb_gpio_write,
> + .read = regmap_vb_gpio_read,
> +};
> +
> +static bool vprbrd_volatile_reg(struct device *device, unsigned int reg)
> +{
> + struct regmap *regmap = container_of(device, struct regmap, dev);
> + struct vprbrd_gpio *gpio =
> + container_of(regmap, struct vprbrd_gpio, regmap);
> +
> + dev_dbg(device, "%s reg:%i\n", __FUNCTION__, reg);
> +
> + /* if the pin is an output, we are not volatile and can cache */
> + if (gpio->gpioa_out & (1 << reg))
> + return false;
> +
> + return true;
> +}
> +
> +static struct reg_default vprbrd_regdefaults[] = {
> + {0x00, 0x00}, /* 0x00 - 0x0f pin values */
> + {0x01, 0x00},
> + {0x02, 0x00},
> + {0x03, 0x00},
> + {0x04, 0x00},
> + {0x05, 0x00},
> + {0x06, 0x00},
> + {0x07, 0x00},
> + {0x08, 0x00},
> + {0x09, 0x00},
> + {0x0a, 0x00},
> + {0x0b, 0x00},
> + {0x0c, 0x00},
> + {0x0d, 0x00},
> + {0x0e, 0x00},
> + {0x0f, 0x00},
> + {0x10, 0x00}, /* 0x10 - 0x1f pin direction */
> + {0x11, 0x00},
> + {0x12, 0x00},
> + {0x13, 0x00},
> + {0x14, 0x00},
> + {0x15, 0x00},
> + {0x16, 0x00},
> + {0x17, 0x00},
> + {0x18, 0x00},
> + {0x19, 0x00},
> + {0x1a, 0x00},
> + {0x1b, 0x00},
> + {0x1c, 0x00},
> + {0x1d, 0x00},
> + {0x1e, 0x00},
> + {0x1f, 0x00},
> +};
> +
> +static const struct regmap_config vprbrd_regmap_config = {
> + .name = "gpio_regmap",
> + .reg_bits = 8,
> + .val_bits = 8,
> + .volatile_reg = vprbrd_volatile_reg,
> + .max_register = VPRBRD_GPIOA_NUM_REGS,
> + .reg_defaults = vprbrd_regdefaults,
> + .num_reg_defaults = ARRAY_SIZE(vprbrd_regdefaults),
> + .cache_type = REGCACHE_RBTREE,
> +};
> +
> +static int vprbrd_gpioa_get(struct gpio_chip *chip,
> + unsigned offset)
> +{
> + int ret;
> + struct vprbrd_gpio *gpio =
> + container_of(chip, struct vprbrd_gpio, gpioa);
> + unsigned int val;
> +
> + ret = regmap_read(gpio->regmap, offset, &val);
> +
> + dev_dbg(chip->dev, "%s offset:%i val:%x ret:%i\n", __FUNCTION__, offset, val, ret);
> +
> + if (ret < 0)
> + return ret;
> + else
> + return val;
> +}
> +
> +static void vprbrd_gpioa_set(struct gpio_chip *chip,
> + unsigned offset, int value)
> +{
> + int ret;
> + struct vprbrd_gpio *gpio =
> + container_of(chip, struct vprbrd_gpio, gpioa);
> +
> + dev_dbg(chip->dev, "%s offset:%i, value:%i\n", __FUNCTION__, offset, value);
> +
> + ret = regmap_write(gpio->regmap, offset, value);
> + if (ret < 0)
> + dev_err(chip->dev, "usb error setting pin value\n");
> +}
> +
> +static int vprbrd_gpioa_direction_input(struct gpio_chip *chip,
> + unsigned offset)
> +{
> + int ret;
> + struct vprbrd_gpio *gpio =
> + container_of(chip, struct vprbrd_gpio, gpioa);
> +
> + dev_dbg(chip->dev, "%s offset:%i\n", __FUNCTION__, offset);
> +
> + ret = regmap_write(gpio->regmap,
> + offset + VPRBRD_GPIOA_REG_DIR_BASE, 0);
> +
> + if (ret < 0) {
> + dev_err(chip->dev, "usb error setting pin to output\n");
> + return ret;
> + }
> +
> + gpio->gpioa_out &= ~(1 << offset);
> +
> + return 0;
> +}
> +
> +static int vprbrd_gpioa_direction_output(struct gpio_chip *chip,
> + unsigned offset, int value)
> +{
> + int ret;
> + struct vprbrd_gpio *gpio =
> + container_of(chip, struct vprbrd_gpio, gpioa);
> +
> + dev_dbg(chip->dev, "%s offset:%i, value:%i\n", __FUNCTION__, offset, value);
> +
> + ret = regmap_write(gpio->regmap,
> + offset + VPRBRD_GPIOA_REG_DIR_BASE, value);
> +
> + if (ret < 0) {
> + dev_err(chip->dev, "usb error setting pin to output\n");
> + return ret;
> + }
> +
> + gpio->gpioa_out |= (1 << offset);
> + ret = regcache_write(gpio->regmap, offset, value);
> +
> + return 0;
> +}
> +
> +/* ----- end of gpio a chip ---------------------------------------------- */
> +
> +/* ----- begin of gipo b chip -------------------------------------------- */
> +
> +static int vprbrd_gpiob_setdir(struct vprbrd *vb, unsigned offset,
> + unsigned dir)
> +{
> + struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf;
> + int ret;
> +
> + gbmsg->cmd = VPRBRD_GPIOB_CMD_SETDIR;
> + gbmsg->val = cpu_to_be16(dir << offset);
> + gbmsg->mask = cpu_to_be16(0x0001 << offset);
> +
> + ret = usb_control_msg(vb->usb_dev, usb_sndctrlpipe(vb->usb_dev, 0),
> + VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT, 0x0000,
> + 0x0000, gbmsg, sizeof(struct vprbrd_gpiob_msg),
> + VPRBRD_USB_TIMEOUT_MS);
> +
> + if (ret != sizeof(struct vprbrd_gpiob_msg))
> + return -EREMOTEIO;
> +
> + return 0;
> +}
> +
> +static int vprbrd_gpiob_get(struct gpio_chip *chip,
> + unsigned offset)
> +{
> + int ret;
> + u16 val;
> + struct vprbrd_gpio *gpio =
> + container_of(chip, struct vprbrd_gpio, gpiob);
> + struct vprbrd *vb = gpio->vb;
> + struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf;
> +
> + /* if io is set to output, just return the saved value */
> + if (gpio->gpiob_out & (1 << offset))
> + return gpio->gpiob_val & (1 << offset);
> +
> + mutex_lock(&vb->lock);
> +
> + ret = usb_control_msg(vb->usb_dev, usb_rcvctrlpipe(vb->usb_dev, 0),
> + VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_IN, 0x0000,
> + 0x0000, gbmsg, sizeof(struct vprbrd_gpiob_msg),
> + VPRBRD_USB_TIMEOUT_MS);
> + val = gbmsg->val;
> +
> + mutex_unlock(&vb->lock);
> +
> + if (ret != sizeof(struct vprbrd_gpiob_msg))
> + return ret;
> +
> + /* cache the read values */
> + gpio->gpiob_val = be16_to_cpu(val);
> +
> + return (gpio->gpiob_val >> offset) & 0x1;
> +}
> +
> +static void vprbrd_gpiob_set(struct gpio_chip *chip,
> + unsigned offset, int value)
> +{
> + int ret;
> + struct vprbrd_gpio *gpio =
> + container_of(chip, struct vprbrd_gpio, gpiob);
> + struct vprbrd *vb = gpio->vb;
> + struct vprbrd_gpiob_msg *gbmsg = (struct vprbrd_gpiob_msg *)vb->buf;
> +
> + if (gpio->gpiob_out & (1 << offset)) {
> + if (value)
> + gpio->gpiob_val |= (1 << offset);
> + else
> + gpio->gpiob_val &= ~(1 << offset);
> +
> + mutex_lock(&vb->lock);
> +
> + gbmsg->cmd = VPRBRD_GPIOB_CMD_SETVAL;
> + gbmsg->val = cpu_to_be16(value << offset);
> + gbmsg->mask = cpu_to_be16(0x0001 << offset);
> +
> + ret = usb_control_msg(vb->usb_dev,
> + usb_sndctrlpipe(vb->usb_dev, 0),
> + VPRBRD_USB_REQUEST_GPIOB, VPRBRD_USB_TYPE_OUT,
> + 0x0000, 0x0000, gbmsg,
> + sizeof(struct vprbrd_gpiob_msg), VPRBRD_USB_TIMEOUT_MS);
> +
> + mutex_unlock(&vb->lock);
> +
> + if (ret != sizeof(struct vprbrd_gpiob_msg))
> + dev_err(chip->dev, "usb error setting pin value\n");
> + }
> +}
> +
> +static int vprbrd_gpiob_direction_input(struct gpio_chip *chip,
> + unsigned offset)
> +{
> + int ret;
> + struct vprbrd_gpio *gpio =
> + container_of(chip, struct vprbrd_gpio, gpiob);
> + struct vprbrd *vb = gpio->vb;
> +
> + gpio->gpiob_out &= ~(1 << offset);
> +
> + mutex_lock(&vb->lock);
> +
> + ret = vprbrd_gpiob_setdir(vb, offset, 0);
> +
> + mutex_unlock(&vb->lock);
> +
> + if (ret)
> + dev_err(chip->dev, "usb error setting pin to input\n");
> +
> + return ret;
> +}
> +
> +static int vprbrd_gpiob_direction_output(struct gpio_chip *chip,
> + unsigned offset, int value)
> +{
> + int ret;
> + struct vprbrd_gpio *gpio =
> + container_of(chip, struct vprbrd_gpio, gpiob);
> + struct vprbrd *vb = gpio->vb;
> +
> + gpio->gpiob_out |= (1 << offset);
> + if (value)
> + gpio->gpiob_val |= (1 << offset);
> + else
> + gpio->gpiob_val &= ~(1 << offset);
> +
> + mutex_lock(&vb->lock);
> +
> + ret = vprbrd_gpiob_setdir(vb, offset, 1);
> + if (ret)
> + dev_err(chip->dev, "usb error setting pin to output\n");
> +
> + mutex_unlock(&vb->lock);
> +
> + vprbrd_gpiob_set(chip, offset, value);
> +
> + return ret;
> +}
> +
> +/* ----- end of gpio b chip ---------------------------------------------- */
> +
> +static int __devinit vprbrd_gpio_probe(struct platform_device *pdev)
> +{
> + struct vprbrd *vb = dev_get_drvdata(pdev->dev.parent);
> + struct vprbrd_gpio *vb_gpio;
> + int ret, ret_err;
> +
> + vb_gpio = kzalloc(sizeof(*vb_gpio), GFP_KERNEL);
> + if (vb_gpio == NULL)
> + return -ENOMEM;
> +
> + vb_gpio->vb = vb;
> + /* registering gpio a */
> + vb_gpio->gpioa.label = "viperboard gpio a";
> + vb_gpio->gpioa.dev = &pdev->dev;
> + vb_gpio->gpioa.owner = THIS_MODULE;
> + vb_gpio->gpioa.base = -1;
> + vb_gpio->gpioa.ngpio = 16;
> + vb_gpio->gpioa.can_sleep = 1;
> + vb_gpio->gpioa.set = vprbrd_gpioa_set;
> + vb_gpio->gpioa.get = vprbrd_gpioa_get;
> + vb_gpio->gpioa.direction_input = vprbrd_gpioa_direction_input;
> + vb_gpio->gpioa.direction_output = vprbrd_gpioa_direction_output;
> + ret = gpiochip_add(&vb_gpio->gpioa);
> + if (ret < 0) {
> + dev_err(vb_gpio->gpioa.dev, "could not add gpio a");
> + goto err_gpioa;
> + }
> +
> + /* registering gpio b */
> + vb_gpio->gpiob.label = "viperboard gpio b";
> + vb_gpio->gpiob.dev = &pdev->dev;
> + vb_gpio->gpiob.owner = THIS_MODULE;
> + vb_gpio->gpiob.base = -1;
> + vb_gpio->gpiob.ngpio = 16;
> + vb_gpio->gpiob.can_sleep = 1;
> + vb_gpio->gpiob.set = vprbrd_gpiob_set;
> + vb_gpio->gpiob.get = vprbrd_gpiob_get;
> + vb_gpio->gpiob.direction_input = vprbrd_gpiob_direction_input;
> + vb_gpio->gpiob.direction_output = vprbrd_gpiob_direction_output;
> + ret = gpiochip_add(&vb_gpio->gpiob);
> + if (ret < 0) {
> + dev_err(vb_gpio->gpiob.dev, "could not add gpio b");
> + goto err_gpiob;
> + }
> +
> + platform_set_drvdata(pdev, vb_gpio);
> +
> + vb_gpio->regmap = devm_regmap_init(vb_gpio->gpioa.dev,
> + &regmap_vb_gpio, vb, &vprbrd_regmap_config);
> + if (IS_ERR(vb_gpio->regmap)) {
> + ret = PTR_ERR(vb_gpio->regmap);
> + dev_err(vb_gpio->gpioa.dev,
> + "regmap_init failed with err: %d\n", ret);
> + goto err_gpiob;
> + }
> +
> + return ret;
> +
> +err_gpiob:
> + ret_err = gpiochip_remove(&vb_gpio->gpioa);
> +
> +err_gpioa:
> + kfree(vb_gpio);
> + return ret;
> +}
> +
> +static int __devexit vprbrd_gpio_remove(struct platform_device *pdev)
> +{
> + struct vprbrd_gpio *vb_gpio = platform_get_drvdata(pdev);
> + int ret;
> +
> +/*
> + ret = regmap_exit(vbgpio->regmap);
> + if (ret == 0)
> +*/
> + ret = gpiochip_remove(&vb_gpio->gpiob);
> + if (ret == 0)
> + ret = gpiochip_remove(&vb_gpio->gpioa);
> + if (ret == 0)
> + kfree(vb_gpio);
> +
> + return ret;
> +}
> +
> +static struct platform_driver vprbrd_gpio_driver = {
> + .driver.name = "viperboard-gpio",
> + .driver.owner = THIS_MODULE,
> + .probe = vprbrd_gpio_probe,
> + .remove = __devexit_p(vprbrd_gpio_remove),
> +};
> +
> +static int __init vprbrd_gpio_init(void)
> +{
> + switch (gpioa_freq) {
> + case 1000000:
> + gpioa_clk = VPRBRD_GPIOA_CLK_1MHZ;
> + break;
> + case 100000:
> + gpioa_clk = VPRBRD_GPIOA_CLK_100KHZ;
> + break;
> + case 10000:
> + gpioa_clk = VPRBRD_GPIOA_CLK_10KHZ;
> + break;
> + case 1000:
> + gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ;
> + break;
> + case 100:
> + gpioa_clk = VPRBRD_GPIOA_CLK_100HZ;
> + break;
> + case 10:
> + gpioa_clk = VPRBRD_GPIOA_CLK_10HZ;
> + break;
> + default:
> + pr_warn("invalid gpioa_freq (%d)\n", gpioa_freq);
> + gpioa_clk = VPRBRD_GPIOA_CLK_1KHZ;
> + }
> +
> + return platform_driver_register(&vprbrd_gpio_driver);
> +}
> +subsys_initcall(vprbrd_gpio_init);
> +
> +static void __exit vprbrd_gpio_exit(void)
> +{
> + platform_driver_unregister(&vprbrd_gpio_driver);
> +}
> +module_exit(vprbrd_gpio_exit);
> +
> +MODULE_AUTHOR("Lars Poeschel <poeschel@xxxxxxxxxxx>");
> +MODULE_DESCRIPTION("GPIO driver for Nano River Techs Viperboard");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:viperboard-gpio");
> diff --git a/drivers/mfd/viperboard.c b/drivers/mfd/viperboard.c
> index de368b7..9ad06cd 100644
> --- a/drivers/mfd/viperboard.c
> +++ b/drivers/mfd/viperboard.c
> @@ -49,6 +49,9 @@ static void vprbrd_free(struct vprbrd *dev)
> }
>
> static struct mfd_cell vprbrd_devs[] = {
> + {
> + .name = "viperboard-gpio",
> + },
> };
>
> static int vprbrd_probe(struct usb_interface *interface,
> diff --git a/include/linux/mfd/viperboard.h b/include/linux/mfd/viperboard.h
> index 1c23c79..d3c9ad2 100644
> --- a/include/linux/mfd/viperboard.h
> +++ b/include/linux/mfd/viperboard.h
> @@ -44,6 +44,8 @@
> #define VPRBRD_USB_TIMEOUT_MS 100
> #define VPRBRD_USB_REQUEST_MAJOR 0xea
> #define VPRBRD_USB_REQUEST_MINOR 0xeb
> +#define VPRBRD_USB_REQUEST_GPIOA 0xed
> +#define VPRBRD_USB_REQUEST_GPIOB 0xdd
>
> struct __packed vprbrd_i2c_write_hdr {
> u8 cmd;


Yours,
Linus Walleij
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/