* Re: [PATCH 0/2][resend] gpio: pcf857x: enable gpio_to_irq() [not found] <877gw2uis5.wl%kuninori.morimoto.gx@renesas.com> @ 2012-06-14 4:57 ` Kuninori Morimoto 2012-06-14 4:58 ` [PATCH 1/2][resend] gpio: pcf857x: share 8/16 bit access functions Kuninori Morimoto 2012-06-14 4:58 ` [PATCH 2/2][resend] gpio: pcf857x: enable gpio_to_irq() support Kuninori Morimoto 0 siblings, 2 replies; 11+ messages in thread From: Kuninori Morimoto @ 2012-06-14 4:57 UTC (permalink / raw) To: Grant Likely, Linus Walleij; +Cc: linux-kernel These patches add gpio_to_irq() support for gpio-pcf857x. Kuninori Morimoto (2): gpio: pcf857x: share 8/16 bit access functions gpio: pcf857x: enable gpio_to_irq() support I'm using pcf857x, and gpio_keys_polled driver, since pcf857x didn't support irq. pcf857x has some pins, but interrupt is only 1 pin. So, these patches add irq demux support on pcf857x driver, and enabled gpio_to_irq(). Now, I can use both gpio_keys / gpio_keys_polled These patches are based on latest linus/master branch ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/2][resend] gpio: pcf857x: share 8/16 bit access functions 2012-06-14 4:57 ` [PATCH 0/2][resend] gpio: pcf857x: enable gpio_to_irq() Kuninori Morimoto @ 2012-06-14 4:58 ` Kuninori Morimoto 2012-07-05 12:57 ` Linus Walleij 2012-06-14 4:58 ` [PATCH 2/2][resend] gpio: pcf857x: enable gpio_to_irq() support Kuninori Morimoto 1 sibling, 1 reply; 11+ messages in thread From: Kuninori Morimoto @ 2012-06-14 4:58 UTC (permalink / raw) To: Linus Walleij, Grant Likely; +Cc: Kuninori Morimoto, linux-kernel This patch adds 8/16 bit write/read functions, and shared gpio input/output/set/get functions. Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> --- drivers/gpio/gpio-pcf857x.c | 93 ++++++++++++++---------------------------- 1 files changed, 31 insertions(+), 62 deletions(-) diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 2d1de9e..076e236 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -61,61 +61,28 @@ struct pcf857x { struct i2c_client *client; struct mutex lock; /* protect 'out' */ unsigned out; /* software latch */ + + int (*write)(struct i2c_client *client, unsigned data); + int (*read)(struct i2c_client *client); }; /*-------------------------------------------------------------------------*/ /* Talk to 8-bit I/O expander */ -static int pcf857x_input8(struct gpio_chip *chip, unsigned offset) -{ - struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); - int status; - - mutex_lock(&gpio->lock); - gpio->out |= (1 << offset); - status = i2c_smbus_write_byte(gpio->client, gpio->out); - mutex_unlock(&gpio->lock); - - return status; -} - -static int pcf857x_get8(struct gpio_chip *chip, unsigned offset) -{ - struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); - s32 value; - - value = i2c_smbus_read_byte(gpio->client); - return (value < 0) ? 0 : (value & (1 << offset)); -} - -static int pcf857x_output8(struct gpio_chip *chip, unsigned offset, int value) +static int i2c_write_le8(struct i2c_client *client, unsigned data) { - struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); - unsigned bit = 1 << offset; - int status; - - mutex_lock(&gpio->lock); - if (value) - gpio->out |= bit; - else - gpio->out &= ~bit; - status = i2c_smbus_write_byte(gpio->client, gpio->out); - mutex_unlock(&gpio->lock); - - return status; + return i2c_smbus_write_byte(client, data); } -static void pcf857x_set8(struct gpio_chip *chip, unsigned offset, int value) +static int i2c_read_le8(struct i2c_client *client) { - pcf857x_output8(chip, offset, value); + return (int)i2c_smbus_read_byte(client); } -/*-------------------------------------------------------------------------*/ - /* Talk to 16-bit I/O expander */ -static int i2c_write_le16(struct i2c_client *client, u16 word) +static int i2c_write_le16(struct i2c_client *client, unsigned word) { u8 buf[2] = { word & 0xff, word >> 8, }; int status; @@ -135,29 +102,31 @@ static int i2c_read_le16(struct i2c_client *client) return (buf[1] << 8) | buf[0]; } -static int pcf857x_input16(struct gpio_chip *chip, unsigned offset) +/*-------------------------------------------------------------------------*/ + +static int pcf857x_input(struct gpio_chip *chip, unsigned offset) { struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); int status; mutex_lock(&gpio->lock); gpio->out |= (1 << offset); - status = i2c_write_le16(gpio->client, gpio->out); + status = gpio->write(gpio->client, gpio->out); mutex_unlock(&gpio->lock); return status; } -static int pcf857x_get16(struct gpio_chip *chip, unsigned offset) +static int pcf857x_get(struct gpio_chip *chip, unsigned offset) { struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); int value; - value = i2c_read_le16(gpio->client); + value = gpio->read(gpio->client); return (value < 0) ? 0 : (value & (1 << offset)); } -static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value) +static int pcf857x_output(struct gpio_chip *chip, unsigned offset, int value) { struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); unsigned bit = 1 << offset; @@ -168,15 +137,15 @@ static int pcf857x_output16(struct gpio_chip *chip, unsigned offset, int value) gpio->out |= bit; else gpio->out &= ~bit; - status = i2c_write_le16(gpio->client, gpio->out); + status = gpio->write(gpio->client, gpio->out); mutex_unlock(&gpio->lock); return status; } -static void pcf857x_set16(struct gpio_chip *chip, unsigned offset, int value) +static void pcf857x_set(struct gpio_chip *chip, unsigned offset, int value) { - pcf857x_output16(chip, offset, value); + pcf857x_output(chip, offset, value); } /*-------------------------------------------------------------------------*/ @@ -200,10 +169,15 @@ static int pcf857x_probe(struct i2c_client *client, mutex_init(&gpio->lock); - gpio->chip.base = pdata ? pdata->gpio_base : -1; - gpio->chip.can_sleep = 1; - gpio->chip.dev = &client->dev; - gpio->chip.owner = THIS_MODULE; + gpio->chip.base = pdata ? pdata->gpio_base : -1; + gpio->chip.can_sleep = 1; + gpio->chip.dev = &client->dev; + gpio->chip.owner = THIS_MODULE; + gpio->chip.get = pcf857x_get; + gpio->chip.set = pcf857x_set; + gpio->chip.direction_input = pcf857x_input; + gpio->chip.direction_output = pcf857x_output; + gpio->chip.ngpio = id->driver_data; /* NOTE: the OnSemi jlc1562b is also largely compatible with * these parts, notably for output. It has a low-resolution @@ -216,12 +190,9 @@ static int pcf857x_probe(struct i2c_client *client, * * NOTE: we don't distinguish here between *4 and *4a parts. */ - gpio->chip.ngpio = id->driver_data; if (gpio->chip.ngpio == 8) { - gpio->chip.direction_input = pcf857x_input8; - gpio->chip.get = pcf857x_get8; - gpio->chip.direction_output = pcf857x_output8; - gpio->chip.set = pcf857x_set8; + gpio->write = i2c_write_le8; + gpio->read = i2c_read_le8; if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) @@ -238,10 +209,8 @@ static int pcf857x_probe(struct i2c_client *client, * NOTE: we don't distinguish here between '75 and '75c parts. */ } else if (gpio->chip.ngpio == 16) { - gpio->chip.direction_input = pcf857x_input16; - gpio->chip.get = pcf857x_get16; - gpio->chip.direction_output = pcf857x_output16; - gpio->chip.set = pcf857x_set16; + gpio->write = i2c_write_le16; + gpio->read = i2c_read_le16; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) status = -EIO; -- 1.7.5.4 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 1/2][resend] gpio: pcf857x: share 8/16 bit access functions 2012-06-14 4:58 ` [PATCH 1/2][resend] gpio: pcf857x: share 8/16 bit access functions Kuninori Morimoto @ 2012-07-05 12:57 ` Linus Walleij 0 siblings, 0 replies; 11+ messages in thread From: Linus Walleij @ 2012-07-05 12:57 UTC (permalink / raw) To: Kuninori Morimoto Cc: Linus Walleij, Grant Likely, Kuninori Morimoto, linux-kernel On Thu, Jun 14, 2012 at 6:58 AM, Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> wrote: > This patch adds 8/16 bit write/read functions, > and shared gpio input/output/set/get functions. > > Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Applied. Thanks, Linus Walleij ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/2][resend] gpio: pcf857x: enable gpio_to_irq() support 2012-06-14 4:57 ` [PATCH 0/2][resend] gpio: pcf857x: enable gpio_to_irq() Kuninori Morimoto 2012-06-14 4:58 ` [PATCH 1/2][resend] gpio: pcf857x: share 8/16 bit access functions Kuninori Morimoto @ 2012-06-14 4:58 ` Kuninori Morimoto 2012-06-14 6:15 ` Paul Mundt 1 sibling, 1 reply; 11+ messages in thread From: Kuninori Morimoto @ 2012-06-14 4:58 UTC (permalink / raw) To: Linus Walleij, Grant Likely; +Cc: Kuninori Morimoto, linux-kernel pcf857x chip has some pins, but interrupt pin is only 1 pin. And gpiolib requests 1 interrupt per 1 gpio pin. Thus, this patch added demuxed irq to pcf857x driver, and enabled gpio_to_irq(). Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> --- drivers/gpio/gpio-pcf857x.c | 146 +++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/pcf857x.h | 3 + 2 files changed, 149 insertions(+), 0 deletions(-) diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 076e236..a51d4f5b 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -23,7 +23,12 @@ #include <linux/gpio.h> #include <linux/i2c.h> #include <linux/i2c/pcf857x.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> static const struct i2c_device_id pcf857x_id[] = { @@ -60,7 +65,13 @@ struct pcf857x { struct gpio_chip chip; struct i2c_client *client; struct mutex lock; /* protect 'out' */ + struct work_struct work; /* irq demux work */ + struct irq_domain *irq_domain; /* for irq demux */ + spinlock_t slock; /* protect irq demux */ unsigned out; /* software latch */ + unsigned status; /* current status */ + int irq; /* real irq number */ + int irq_demux_base; /* irq demux base */ int (*write)(struct i2c_client *client, unsigned data); int (*read)(struct i2c_client *client); @@ -150,6 +161,123 @@ static void pcf857x_set(struct gpio_chip *chip, unsigned offset, int value) /*-------------------------------------------------------------------------*/ +static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + + return irq_find_mapping(gpio->irq_domain, offset); +} + +static void pcf857x_irq_demux_work(struct work_struct *work) +{ + struct pcf857x *gpio = container_of(work, + struct pcf857x, + work); + int change, i, status; + unsigned long flags; + + status = gpio->read(gpio->client); + + spin_lock_irqsave(&gpio->slock, flags); + change = gpio->status ^ status; + + for (i = 0; i < gpio->chip.ngpio; i++) + if (change & BIT(i)) + generic_handle_irq(irq_find_mapping(gpio->irq_domain, i)); + + gpio->status = status; + spin_unlock_irqrestore(&gpio->slock, flags); +} + +static irqreturn_t pcf857x_irq_demux(int irq, void *data) +{ + struct pcf857x *gpio = data; + + /* + * pcf857x can't read/write data here, + * since i2c data access might go to sleep. + */ + schedule_work(&gpio->work); + + return IRQ_HANDLED; +} + +static void pcf857x_nothing(struct irq_data *data) +{ + /* do nothing */ +} + +static struct irq_chip pcf857x_irq_chip = { + .name = "pcf857x cascade", + .irq_mask = pcf857x_nothing, + .irq_unmask = pcf857x_nothing, +}; + +static int pcf857x_irq_domain_map(struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, + &pcf857x_irq_chip, + handle_level_irq); + return 0; +} + +static struct irq_domain_ops pcf857x_irq_domain_ops = { + .map = pcf857x_irq_domain_map, +}; + +static void pcf857x_irq_domain_cleanup(struct pcf857x *gpio) +{ + if (gpio->irq_demux_base > 0) + irq_free_descs(gpio->irq_demux_base, gpio->chip.ngpio); + + if (gpio->irq_domain) + irq_domain_remove(gpio->irq_domain); + + if (gpio->irq) + free_irq(gpio->irq, gpio); +} + +static int pcf857x_irq_domain_init(struct pcf857x *gpio, + struct pcf857x_platform_data *pdata, + struct device *dev) +{ + int status; + + /* create demuxed irq */ + gpio->irq_demux_base = irq_alloc_descs(-1, 0, gpio->chip.ngpio, 0); + if (gpio->irq_demux_base < 0) + return -ENODEV; + + gpio->irq_domain = irq_domain_add_legacy(dev->of_node, + gpio->chip.ngpio, + gpio->irq_demux_base, + 0, + &pcf857x_irq_domain_ops, + NULL); + if (!gpio->irq_domain) + goto fail; + + /* enable real irq */ + status = request_irq(pdata->irq, pcf857x_irq_demux, 0, + dev_name(dev), gpio); + if (status) + goto fail; + + /* enable gpio_to_irq() */ + INIT_WORK(&gpio->work, pcf857x_irq_demux_work); + gpio->chip.to_irq = pcf857x_to_irq; + gpio->irq = pdata->irq; + + return 0; + +fail: + pcf857x_irq_domain_cleanup(gpio); + return -EINVAL; +} + +/*-------------------------------------------------------------------------*/ + static int pcf857x_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -168,6 +296,7 @@ static int pcf857x_probe(struct i2c_client *client, return -ENOMEM; mutex_init(&gpio->lock); + spin_lock_init(&gpio->slock); gpio->chip.base = pdata ? pdata->gpio_base : -1; gpio->chip.can_sleep = 1; @@ -179,6 +308,15 @@ static int pcf857x_probe(struct i2c_client *client, gpio->chip.direction_output = pcf857x_output; gpio->chip.ngpio = id->driver_data; + /* enable gpio_to_irq() if platform has settings */ + if (pdata->irq) { + status = pcf857x_irq_domain_init(gpio, pdata, &client->dev); + if (status < 0) { + dev_err(&client->dev, "irq_domain init failed\n"); + goto fail; + } + } + /* NOTE: the OnSemi jlc1562b is also largely compatible with * these parts, notably for output. It has a low-resolution * DAC instead of pin change IRQs; and its inputs can be the @@ -248,6 +386,7 @@ static int pcf857x_probe(struct i2c_client *client, * all-ones reset state. Otherwise it flags pins to be driven low. */ gpio->out = pdata ? ~pdata->n_latch : ~0; + gpio->status = gpio->out; status = gpiochip_add(&gpio->chip); if (status < 0) @@ -278,6 +417,10 @@ static int pcf857x_probe(struct i2c_client *client, fail: dev_dbg(&client->dev, "probe error %d for '%s'\n", status, client->name); + + if (pdata->irq) + pcf857x_irq_domain_cleanup(gpio); + kfree(gpio); return status; } @@ -299,6 +442,9 @@ static int pcf857x_remove(struct i2c_client *client) } } + if (pdata->irq) + pcf857x_irq_domain_cleanup(gpio); + status = gpiochip_remove(&gpio->chip); if (status == 0) kfree(gpio); diff --git a/include/linux/i2c/pcf857x.h b/include/linux/i2c/pcf857x.h index 0767a2a..781e6bd 100644 --- a/include/linux/i2c/pcf857x.h +++ b/include/linux/i2c/pcf857x.h @@ -10,6 +10,7 @@ * @setup: optional callback issued once the GPIOs are valid * @teardown: optional callback issued before the GPIOs are invalidated * @context: optional parameter passed to setup() and teardown() + * @irq: optional interrupt number * * In addition to the I2C_BOARD_INFO() state appropriate to each chip, * the i2c_board_info used with the pcf875x driver must provide its @@ -39,6 +40,8 @@ struct pcf857x_platform_data { int gpio, unsigned ngpio, void *context); void *context; + + int irq; }; #endif /* __LINUX_PCF857X_H */ -- 1.7.5.4 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2][resend] gpio: pcf857x: enable gpio_to_irq() support 2012-06-14 4:58 ` [PATCH 2/2][resend] gpio: pcf857x: enable gpio_to_irq() support Kuninori Morimoto @ 2012-06-14 6:15 ` Paul Mundt 2012-06-14 7:06 ` Kuninori Morimoto 0 siblings, 1 reply; 11+ messages in thread From: Paul Mundt @ 2012-06-14 6:15 UTC (permalink / raw) To: Kuninori Morimoto Cc: Linus Walleij, Grant Likely, Kuninori Morimoto, linux-kernel On Wed, Jun 13, 2012 at 09:58:26PM -0700, Kuninori Morimoto wrote: > +static void pcf857x_nothing(struct irq_data *data) > +{ > + /* do nothing */ > +} > + > +static struct irq_chip pcf857x_irq_chip = { > + .name = "pcf857x cascade", > + .irq_mask = pcf857x_nothing, > + .irq_unmask = pcf857x_nothing, > +}; > + Just use dummy_irq_chip. > +static int pcf857x_irq_domain_init(struct pcf857x *gpio, > + struct pcf857x_platform_data *pdata, > + struct device *dev) > +{ > + int status; > + > + /* create demuxed irq */ > + gpio->irq_demux_base = irq_alloc_descs(-1, 0, gpio->chip.ngpio, 0); > + if (gpio->irq_demux_base < 0) > + return -ENODEV; > + > + gpio->irq_domain = irq_domain_add_legacy(dev->of_node, > + gpio->chip.ngpio, > + gpio->irq_demux_base, > + 0, > + &pcf857x_irq_domain_ops, > + NULL); Given that you don't actually care where the IRQs are mapped there's no need to bother with a legacy domain here, you can just use a linear one outright. At which point you can drop your separate irq_desc allocation/management. ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2][resend] gpio: pcf857x: enable gpio_to_irq() support 2012-06-14 6:15 ` Paul Mundt @ 2012-06-14 7:06 ` Kuninori Morimoto 2012-06-14 7:10 ` [PATCH 2/2 v2][resend] " Kuninori Morimoto 0 siblings, 1 reply; 11+ messages in thread From: Kuninori Morimoto @ 2012-06-14 7:06 UTC (permalink / raw) To: Paul Mundt; +Cc: Linus Walleij, Grant Likely, Kuninori Morimoto, linux-kernel Hi Paul Thank you for checking patch > > +static struct irq_chip pcf857x_irq_chip = { > > + .name = "pcf857x cascade", > > + .irq_mask = pcf857x_nothing, > > + .irq_unmask = pcf857x_nothing, > > +}; > > + > Just use dummy_irq_chip. Thanks. I didn't know that. OK. I fix it in v2 > > +static int pcf857x_irq_domain_init(struct pcf857x *gpio, > > + struct pcf857x_platform_data *pdata, > > + struct device *dev) > > +{ > > + int status; > > + > > + /* create demuxed irq */ > > + gpio->irq_demux_base = irq_alloc_descs(-1, 0, gpio->chip.ngpio, 0); > > + if (gpio->irq_demux_base < 0) > > + return -ENODEV; > > + > > + gpio->irq_domain = irq_domain_add_legacy(dev->of_node, > > + gpio->chip.ngpio, > > + gpio->irq_demux_base, > > + 0, > > + &pcf857x_irq_domain_ops, > > + NULL); > > Given that you don't actually care where the IRQs are mapped there's no > need to bother with a legacy domain here, you can just use a linear one > outright. At which point you can drop your separate irq_desc > allocation/management. OK. I use linear in v2 Best regards --- Kuninori Morimoto ^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 2/2 v2][resend] gpio: pcf857x: enable gpio_to_irq() support 2012-06-14 7:06 ` Kuninori Morimoto @ 2012-06-14 7:10 ` Kuninori Morimoto 2012-06-14 7:23 ` Paul Mundt 0 siblings, 1 reply; 11+ messages in thread From: Kuninori Morimoto @ 2012-06-14 7:10 UTC (permalink / raw) To: Linus Walleij, Grant Likely; +Cc: Paul Mundt, Kuninori Morimoto, linux-kernel pcf857x chip has some pins, but interrupt pin is only 1 pin. And gpiolib requests 1 interrupt per 1 gpio pin. Thus, this patch added demuxed irq to pcf857x driver, and enabled gpio_to_irq(). Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> --- v1 -> v2 - used dummy_irq_chip on irq_set_chip_and_handler() - used irq_domain_add_linear() instead of irq_domain_add_legacy() drivers/gpio/gpio-pcf857x.c | 124 +++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/pcf857x.h | 3 + 2 files changed, 127 insertions(+), 0 deletions(-) diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 076e236..7f3a25c 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -23,7 +23,12 @@ #include <linux/gpio.h> #include <linux/i2c.h> #include <linux/i2c/pcf857x.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> static const struct i2c_device_id pcf857x_id[] = { @@ -60,7 +65,12 @@ struct pcf857x { struct gpio_chip chip; struct i2c_client *client; struct mutex lock; /* protect 'out' */ + struct work_struct work; /* irq demux work */ + struct irq_domain *irq_domain; /* for irq demux */ + spinlock_t slock; /* protect irq demux */ unsigned out; /* software latch */ + unsigned status; /* current status */ + int irq; /* real irq number */ int (*write)(struct i2c_client *client, unsigned data); int (*read)(struct i2c_client *client); @@ -150,6 +160,102 @@ static void pcf857x_set(struct gpio_chip *chip, unsigned offset, int value) /*-------------------------------------------------------------------------*/ +static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + + return irq_create_mapping(gpio->irq_domain, offset); +} + +static void pcf857x_irq_demux_work(struct work_struct *work) +{ + struct pcf857x *gpio = container_of(work, + struct pcf857x, + work); + int change, i, status; + unsigned long flags; + + status = gpio->read(gpio->client); + + spin_lock_irqsave(&gpio->slock, flags); + change = gpio->status ^ status; + + for (i = 0; i < gpio->chip.ngpio; i++) + if (change & BIT(i)) + generic_handle_irq(irq_find_mapping(gpio->irq_domain, i)); + + gpio->status = status; + spin_unlock_irqrestore(&gpio->slock, flags); +} + +static irqreturn_t pcf857x_irq_demux(int irq, void *data) +{ + struct pcf857x *gpio = data; + + /* + * pcf857x can't read/write data here, + * since i2c data access might go to sleep. + */ + schedule_work(&gpio->work); + + return IRQ_HANDLED; +} + +static int pcf857x_irq_domain_map(struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, + &dummy_irq_chip, + handle_level_irq); + return 0; +} + +static struct irq_domain_ops pcf857x_irq_domain_ops = { + .map = pcf857x_irq_domain_map, +}; + +static void pcf857x_irq_domain_cleanup(struct pcf857x *gpio) +{ + if (gpio->irq_domain) + irq_domain_remove(gpio->irq_domain); + + if (gpio->irq) + free_irq(gpio->irq, gpio); +} + +static int pcf857x_irq_domain_init(struct pcf857x *gpio, + struct pcf857x_platform_data *pdata, + struct device *dev) +{ + int status; + + gpio->irq_domain = irq_domain_add_linear(dev->of_node, + gpio->chip.ngpio, + &pcf857x_irq_domain_ops, + NULL); + if (!gpio->irq_domain) + goto fail; + + /* enable real irq */ + status = request_irq(pdata->irq, pcf857x_irq_demux, 0, + dev_name(dev), gpio); + if (status) + goto fail; + + /* enable gpio_to_irq() */ + INIT_WORK(&gpio->work, pcf857x_irq_demux_work); + gpio->chip.to_irq = pcf857x_to_irq; + gpio->irq = pdata->irq; + + return 0; + +fail: + pcf857x_irq_domain_cleanup(gpio); + return -EINVAL; +} + +/*-------------------------------------------------------------------------*/ + static int pcf857x_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -168,6 +274,7 @@ static int pcf857x_probe(struct i2c_client *client, return -ENOMEM; mutex_init(&gpio->lock); + spin_lock_init(&gpio->slock); gpio->chip.base = pdata ? pdata->gpio_base : -1; gpio->chip.can_sleep = 1; @@ -179,6 +286,15 @@ static int pcf857x_probe(struct i2c_client *client, gpio->chip.direction_output = pcf857x_output; gpio->chip.ngpio = id->driver_data; + /* enable gpio_to_irq() if platform has settings */ + if (pdata->irq) { + status = pcf857x_irq_domain_init(gpio, pdata, &client->dev); + if (status < 0) { + dev_err(&client->dev, "irq_domain init failed\n"); + goto fail; + } + } + /* NOTE: the OnSemi jlc1562b is also largely compatible with * these parts, notably for output. It has a low-resolution * DAC instead of pin change IRQs; and its inputs can be the @@ -248,6 +364,7 @@ static int pcf857x_probe(struct i2c_client *client, * all-ones reset state. Otherwise it flags pins to be driven low. */ gpio->out = pdata ? ~pdata->n_latch : ~0; + gpio->status = gpio->out; status = gpiochip_add(&gpio->chip); if (status < 0) @@ -278,6 +395,10 @@ static int pcf857x_probe(struct i2c_client *client, fail: dev_dbg(&client->dev, "probe error %d for '%s'\n", status, client->name); + + if (pdata->irq) + pcf857x_irq_domain_cleanup(gpio); + kfree(gpio); return status; } @@ -299,6 +420,9 @@ static int pcf857x_remove(struct i2c_client *client) } } + if (pdata->irq) + pcf857x_irq_domain_cleanup(gpio); + status = gpiochip_remove(&gpio->chip); if (status == 0) kfree(gpio); diff --git a/include/linux/i2c/pcf857x.h b/include/linux/i2c/pcf857x.h index 0767a2a..781e6bd 100644 --- a/include/linux/i2c/pcf857x.h +++ b/include/linux/i2c/pcf857x.h @@ -10,6 +10,7 @@ * @setup: optional callback issued once the GPIOs are valid * @teardown: optional callback issued before the GPIOs are invalidated * @context: optional parameter passed to setup() and teardown() + * @irq: optional interrupt number * * In addition to the I2C_BOARD_INFO() state appropriate to each chip, * the i2c_board_info used with the pcf875x driver must provide its @@ -39,6 +40,8 @@ struct pcf857x_platform_data { int gpio, unsigned ngpio, void *context); void *context; + + int irq; }; #endif /* __LINUX_PCF857X_H */ -- 1.7.5.4 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2 v2][resend] gpio: pcf857x: enable gpio_to_irq() support 2012-06-14 7:10 ` [PATCH 2/2 v2][resend] " Kuninori Morimoto @ 2012-06-14 7:23 ` Paul Mundt 2012-06-14 7:59 ` Kuninori Morimoto 0 siblings, 1 reply; 11+ messages in thread From: Paul Mundt @ 2012-06-14 7:23 UTC (permalink / raw) To: Kuninori Morimoto Cc: Linus Walleij, Grant Likely, Kuninori Morimoto, linux-kernel On Thu, Jun 14, 2012 at 12:10:59AM -0700, Kuninori Morimoto wrote: > + for (i = 0; i < gpio->chip.ngpio; i++) > + if (change & BIT(i)) > + generic_handle_irq(irq_find_mapping(gpio->irq_domain, i)); > + You can probably get away with using for_each_set_bit() as well, which would simplify this a bit. Also, do you not have to worry about masking the parent IRQ while iterating through the dispatch loop? ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2 v2][resend] gpio: pcf857x: enable gpio_to_irq() support 2012-06-14 7:23 ` Paul Mundt @ 2012-06-14 7:59 ` Kuninori Morimoto 2012-06-15 2:40 ` [PATCH 2/2 v3][resend] " Kuninori Morimoto 0 siblings, 1 reply; 11+ messages in thread From: Kuninori Morimoto @ 2012-06-14 7:59 UTC (permalink / raw) To: Paul Mundt; +Cc: Linus Walleij, Grant Likely, Kuninori Morimoto, linux-kernel Hi Paul > On Thu, Jun 14, 2012 at 12:10:59AM -0700, Kuninori Morimoto wrote: > > + for (i = 0; i < gpio->chip.ngpio; i++) > > + if (change & BIT(i)) > > + generic_handle_irq(irq_find_mapping(gpio->irq_domain, i)); > > + > You can probably get away with using for_each_set_bit() as well, which > would simplify this a bit. > > Also, do you not have to worry about masking the parent IRQ while > iterating through the dispatch loop? This device automatically stops its IRQ until data was read. And it is using spin_lock_irqsave(), and it needs to save prev status. So, I guess masking parent IRQ is not needed. But not enough/good ? Best regards --- Kuninori Morimoto ^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2 v3][resend] gpio: pcf857x: enable gpio_to_irq() support 2012-06-14 7:59 ` Kuninori Morimoto @ 2012-06-15 2:40 ` Kuninori Morimoto 2012-07-05 13:00 ` Linus Walleij 0 siblings, 1 reply; 11+ messages in thread From: Kuninori Morimoto @ 2012-06-15 2:40 UTC (permalink / raw) To: Paul Mundt, Linus Walleij, Grant Likely; +Cc: Kuninori Morimoto, linux-kernel pcf857x chip has some pins, but interrupt pin is only 1 pin. And gpiolib requests 1 interrupt per 1 gpio pin. Thus, this patch added demuxed irq to pcf857x driver, and enabled gpio_to_irq(). Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> --- v2 -> v3 - used for_each_set_bit() drivers/gpio/gpio-pcf857x.c | 122 +++++++++++++++++++++++++++++++++++++++++++ include/linux/i2c/pcf857x.h | 3 + 2 files changed, 125 insertions(+), 0 deletions(-) diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c index 076e236..12e3e48 100644 --- a/drivers/gpio/gpio-pcf857x.c +++ b/drivers/gpio/gpio-pcf857x.c @@ -23,7 +23,12 @@ #include <linux/gpio.h> #include <linux/i2c.h> #include <linux/i2c/pcf857x.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/module.h> +#include <linux/spinlock.h> +#include <linux/workqueue.h> static const struct i2c_device_id pcf857x_id[] = { @@ -60,7 +65,12 @@ struct pcf857x { struct gpio_chip chip; struct i2c_client *client; struct mutex lock; /* protect 'out' */ + struct work_struct work; /* irq demux work */ + struct irq_domain *irq_domain; /* for irq demux */ + spinlock_t slock; /* protect irq demux */ unsigned out; /* software latch */ + unsigned status; /* current status */ + int irq; /* real irq number */ int (*write)(struct i2c_client *client, unsigned data); int (*read)(struct i2c_client *client); @@ -150,6 +160,100 @@ static void pcf857x_set(struct gpio_chip *chip, unsigned offset, int value) /*-------------------------------------------------------------------------*/ +static int pcf857x_to_irq(struct gpio_chip *chip, unsigned offset) +{ + struct pcf857x *gpio = container_of(chip, struct pcf857x, chip); + + return irq_create_mapping(gpio->irq_domain, offset); +} + +static void pcf857x_irq_demux_work(struct work_struct *work) +{ + struct pcf857x *gpio = container_of(work, + struct pcf857x, + work); + unsigned long change, i, status, flags; + + status = gpio->read(gpio->client); + + spin_lock_irqsave(&gpio->slock, flags); + + change = gpio->status ^ status; + for_each_set_bit(i, &change, gpio->chip.ngpio) + generic_handle_irq(irq_find_mapping(gpio->irq_domain, i)); + gpio->status = status; + + spin_unlock_irqrestore(&gpio->slock, flags); +} + +static irqreturn_t pcf857x_irq_demux(int irq, void *data) +{ + struct pcf857x *gpio = data; + + /* + * pcf857x can't read/write data here, + * since i2c data access might go to sleep. + */ + schedule_work(&gpio->work); + + return IRQ_HANDLED; +} + +static int pcf857x_irq_domain_map(struct irq_domain *domain, unsigned int virq, + irq_hw_number_t hw) +{ + irq_set_chip_and_handler(virq, + &dummy_irq_chip, + handle_level_irq); + return 0; +} + +static struct irq_domain_ops pcf857x_irq_domain_ops = { + .map = pcf857x_irq_domain_map, +}; + +static void pcf857x_irq_domain_cleanup(struct pcf857x *gpio) +{ + if (gpio->irq_domain) + irq_domain_remove(gpio->irq_domain); + + if (gpio->irq) + free_irq(gpio->irq, gpio); +} + +static int pcf857x_irq_domain_init(struct pcf857x *gpio, + struct pcf857x_platform_data *pdata, + struct device *dev) +{ + int status; + + gpio->irq_domain = irq_domain_add_linear(dev->of_node, + gpio->chip.ngpio, + &pcf857x_irq_domain_ops, + NULL); + if (!gpio->irq_domain) + goto fail; + + /* enable real irq */ + status = request_irq(pdata->irq, pcf857x_irq_demux, 0, + dev_name(dev), gpio); + if (status) + goto fail; + + /* enable gpio_to_irq() */ + INIT_WORK(&gpio->work, pcf857x_irq_demux_work); + gpio->chip.to_irq = pcf857x_to_irq; + gpio->irq = pdata->irq; + + return 0; + +fail: + pcf857x_irq_domain_cleanup(gpio); + return -EINVAL; +} + +/*-------------------------------------------------------------------------*/ + static int pcf857x_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -168,6 +272,7 @@ static int pcf857x_probe(struct i2c_client *client, return -ENOMEM; mutex_init(&gpio->lock); + spin_lock_init(&gpio->slock); gpio->chip.base = pdata ? pdata->gpio_base : -1; gpio->chip.can_sleep = 1; @@ -179,6 +284,15 @@ static int pcf857x_probe(struct i2c_client *client, gpio->chip.direction_output = pcf857x_output; gpio->chip.ngpio = id->driver_data; + /* enable gpio_to_irq() if platform has settings */ + if (pdata->irq) { + status = pcf857x_irq_domain_init(gpio, pdata, &client->dev); + if (status < 0) { + dev_err(&client->dev, "irq_domain init failed\n"); + goto fail; + } + } + /* NOTE: the OnSemi jlc1562b is also largely compatible with * these parts, notably for output. It has a low-resolution * DAC instead of pin change IRQs; and its inputs can be the @@ -248,6 +362,7 @@ static int pcf857x_probe(struct i2c_client *client, * all-ones reset state. Otherwise it flags pins to be driven low. */ gpio->out = pdata ? ~pdata->n_latch : ~0; + gpio->status = gpio->out; status = gpiochip_add(&gpio->chip); if (status < 0) @@ -278,6 +393,10 @@ static int pcf857x_probe(struct i2c_client *client, fail: dev_dbg(&client->dev, "probe error %d for '%s'\n", status, client->name); + + if (pdata->irq) + pcf857x_irq_domain_cleanup(gpio); + kfree(gpio); return status; } @@ -299,6 +418,9 @@ static int pcf857x_remove(struct i2c_client *client) } } + if (pdata->irq) + pcf857x_irq_domain_cleanup(gpio); + status = gpiochip_remove(&gpio->chip); if (status == 0) kfree(gpio); diff --git a/include/linux/i2c/pcf857x.h b/include/linux/i2c/pcf857x.h index 0767a2a..781e6bd 100644 --- a/include/linux/i2c/pcf857x.h +++ b/include/linux/i2c/pcf857x.h @@ -10,6 +10,7 @@ * @setup: optional callback issued once the GPIOs are valid * @teardown: optional callback issued before the GPIOs are invalidated * @context: optional parameter passed to setup() and teardown() + * @irq: optional interrupt number * * In addition to the I2C_BOARD_INFO() state appropriate to each chip, * the i2c_board_info used with the pcf875x driver must provide its @@ -39,6 +40,8 @@ struct pcf857x_platform_data { int gpio, unsigned ngpio, void *context); void *context; + + int irq; }; #endif /* __LINUX_PCF857X_H */ -- 1.7.5.4 ^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 2/2 v3][resend] gpio: pcf857x: enable gpio_to_irq() support 2012-06-15 2:40 ` [PATCH 2/2 v3][resend] " Kuninori Morimoto @ 2012-07-05 13:00 ` Linus Walleij 0 siblings, 0 replies; 11+ messages in thread From: Linus Walleij @ 2012-07-05 13:00 UTC (permalink / raw) To: Kuninori Morimoto, Paul Mundt Cc: Linus Walleij, Grant Likely, Kuninori Morimoto, linux-kernel On Fri, Jun 15, 2012 at 4:40 AM, Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> wrote: > pcf857x chip has some pins, but interrupt pin is only 1 pin. > And gpiolib requests 1 interrupt per 1 gpio pin. > > Thus, this patch added demuxed irq to pcf857x driver, > and enabled gpio_to_irq(). > > Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> > --- > v2 -> v3 > > - used for_each_set_bit() Applied this v3 version as nothing has been said since. Paul, scream if you don't like it. Yours, Linus Walleij ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2012-07-05 13:00 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <877gw2uis5.wl%kuninori.morimoto.gx@renesas.com>
2012-06-14 4:57 ` [PATCH 0/2][resend] gpio: pcf857x: enable gpio_to_irq() Kuninori Morimoto
2012-06-14 4:58 ` [PATCH 1/2][resend] gpio: pcf857x: share 8/16 bit access functions Kuninori Morimoto
2012-07-05 12:57 ` Linus Walleij
2012-06-14 4:58 ` [PATCH 2/2][resend] gpio: pcf857x: enable gpio_to_irq() support Kuninori Morimoto
2012-06-14 6:15 ` Paul Mundt
2012-06-14 7:06 ` Kuninori Morimoto
2012-06-14 7:10 ` [PATCH 2/2 v2][resend] " Kuninori Morimoto
2012-06-14 7:23 ` Paul Mundt
2012-06-14 7:59 ` Kuninori Morimoto
2012-06-15 2:40 ` [PATCH 2/2 v3][resend] " Kuninori Morimoto
2012-07-05 13:00 ` Linus Walleij
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox