mirror of
https://github.com/torvalds/linux.git
synced 2024-12-02 09:01:34 +00:00
7bb8a0cf49
If the teardown callback failed in the gpio driver, it fails to free the
irq (if there is one). The device is removed anyhow. If later on the irq
triggers, all sorts of unpleasant things might happen (e.g. accessing
the struct adp5588_gpio which is already freed in the meantime or starting
i2c bus transfers for an unregistered device). Even before irq support was
added to this driver, exiting early was wrong; back then it failed to
unregister the gpiochip.
Fortunately these callbacks aren't used any more since at least blackfin
was removed in 2018. So just drop them.
Note that they are not removed from struct adp5588_gpio_platform_data
because the keyboard driver adp5588-keys.c also makes use of them.
(I didn't check if the callbacks might have been called twice, maybe there
is another reason hidden to better not call these functions.)
This patch is a preparation for making i2c remove callbacks return void.
Fixes: 80884094e3
("gpio: adp5588-gpio: new driver for ADP5588 GPIO expanders")
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Bartosz Golaszewski <brgl@bgdev.pl>
453 lines
11 KiB
C
453 lines
11 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* GPIO Chip driver for Analog Devices
|
|
* ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller
|
|
*
|
|
* Copyright 2009-2010 Analog Devices Inc.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/init.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/gpio/driver.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/of_device.h>
|
|
|
|
#include <linux/platform_data/adp5588.h>
|
|
|
|
#define DRV_NAME "adp5588-gpio"
|
|
|
|
/*
|
|
* Early pre 4.0 Silicon required to delay readout by at least 25ms,
|
|
* since the Event Counter Register updated 25ms after the interrupt
|
|
* asserted.
|
|
*/
|
|
#define WA_DELAYED_READOUT_REVID(rev) ((rev) < 4)
|
|
|
|
struct adp5588_gpio {
|
|
struct i2c_client *client;
|
|
struct gpio_chip gpio_chip;
|
|
struct mutex lock; /* protect cached dir, dat_out */
|
|
/* protect serialized access to the interrupt controller bus */
|
|
struct mutex irq_lock;
|
|
uint8_t dat_out[3];
|
|
uint8_t dir[3];
|
|
uint8_t int_lvl_low[3];
|
|
uint8_t int_lvl_high[3];
|
|
uint8_t int_en[3];
|
|
uint8_t irq_mask[3];
|
|
uint8_t int_input_en[3];
|
|
};
|
|
|
|
static int adp5588_gpio_read(struct i2c_client *client, u8 reg)
|
|
{
|
|
int ret = i2c_smbus_read_byte_data(client, reg);
|
|
|
|
if (ret < 0)
|
|
dev_err(&client->dev, "Read Error\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int adp5588_gpio_write(struct i2c_client *client, u8 reg, u8 val)
|
|
{
|
|
int ret = i2c_smbus_write_byte_data(client, reg, val);
|
|
|
|
if (ret < 0)
|
|
dev_err(&client->dev, "Write Error\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int adp5588_gpio_get_value(struct gpio_chip *chip, unsigned off)
|
|
{
|
|
struct adp5588_gpio *dev = gpiochip_get_data(chip);
|
|
unsigned bank = ADP5588_BANK(off);
|
|
unsigned bit = ADP5588_BIT(off);
|
|
int val;
|
|
|
|
mutex_lock(&dev->lock);
|
|
|
|
if (dev->dir[bank] & bit)
|
|
val = dev->dat_out[bank];
|
|
else
|
|
val = adp5588_gpio_read(dev->client, GPIO_DAT_STAT1 + bank);
|
|
|
|
mutex_unlock(&dev->lock);
|
|
|
|
return !!(val & bit);
|
|
}
|
|
|
|
static void adp5588_gpio_set_value(struct gpio_chip *chip,
|
|
unsigned off, int val)
|
|
{
|
|
unsigned bank, bit;
|
|
struct adp5588_gpio *dev = gpiochip_get_data(chip);
|
|
|
|
bank = ADP5588_BANK(off);
|
|
bit = ADP5588_BIT(off);
|
|
|
|
mutex_lock(&dev->lock);
|
|
if (val)
|
|
dev->dat_out[bank] |= bit;
|
|
else
|
|
dev->dat_out[bank] &= ~bit;
|
|
|
|
adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank,
|
|
dev->dat_out[bank]);
|
|
mutex_unlock(&dev->lock);
|
|
}
|
|
|
|
static int adp5588_gpio_direction_input(struct gpio_chip *chip, unsigned off)
|
|
{
|
|
int ret;
|
|
unsigned bank;
|
|
struct adp5588_gpio *dev = gpiochip_get_data(chip);
|
|
|
|
bank = ADP5588_BANK(off);
|
|
|
|
mutex_lock(&dev->lock);
|
|
dev->dir[bank] &= ~ADP5588_BIT(off);
|
|
ret = adp5588_gpio_write(dev->client, GPIO_DIR1 + bank, dev->dir[bank]);
|
|
mutex_unlock(&dev->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int adp5588_gpio_direction_output(struct gpio_chip *chip,
|
|
unsigned off, int val)
|
|
{
|
|
int ret;
|
|
unsigned bank, bit;
|
|
struct adp5588_gpio *dev = gpiochip_get_data(chip);
|
|
|
|
bank = ADP5588_BANK(off);
|
|
bit = ADP5588_BIT(off);
|
|
|
|
mutex_lock(&dev->lock);
|
|
dev->dir[bank] |= bit;
|
|
|
|
if (val)
|
|
dev->dat_out[bank] |= bit;
|
|
else
|
|
dev->dat_out[bank] &= ~bit;
|
|
|
|
ret = adp5588_gpio_write(dev->client, GPIO_DAT_OUT1 + bank,
|
|
dev->dat_out[bank]);
|
|
ret |= adp5588_gpio_write(dev->client, GPIO_DIR1 + bank,
|
|
dev->dir[bank]);
|
|
mutex_unlock(&dev->lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#ifdef CONFIG_GPIO_ADP5588_IRQ
|
|
|
|
static void adp5588_irq_bus_lock(struct irq_data *d)
|
|
{
|
|
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
|
struct adp5588_gpio *dev = gpiochip_get_data(gc);
|
|
|
|
mutex_lock(&dev->irq_lock);
|
|
}
|
|
|
|
/*
|
|
* genirq core code can issue chip->mask/unmask from atomic context.
|
|
* This doesn't work for slow busses where an access needs to sleep.
|
|
* bus_sync_unlock() is therefore called outside the atomic context,
|
|
* syncs the current irq mask state with the slow external controller
|
|
* and unlocks the bus.
|
|
*/
|
|
|
|
static void adp5588_irq_bus_sync_unlock(struct irq_data *d)
|
|
{
|
|
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
|
struct adp5588_gpio *dev = gpiochip_get_data(gc);
|
|
int i;
|
|
|
|
for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
|
|
if (dev->int_input_en[i]) {
|
|
mutex_lock(&dev->lock);
|
|
dev->dir[i] &= ~dev->int_input_en[i];
|
|
dev->int_input_en[i] = 0;
|
|
adp5588_gpio_write(dev->client, GPIO_DIR1 + i,
|
|
dev->dir[i]);
|
|
mutex_unlock(&dev->lock);
|
|
}
|
|
|
|
if (dev->int_en[i] ^ dev->irq_mask[i]) {
|
|
dev->int_en[i] = dev->irq_mask[i];
|
|
adp5588_gpio_write(dev->client, GPI_EM1 + i,
|
|
dev->int_en[i]);
|
|
}
|
|
}
|
|
|
|
mutex_unlock(&dev->irq_lock);
|
|
}
|
|
|
|
static void adp5588_irq_mask(struct irq_data *d)
|
|
{
|
|
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
|
struct adp5588_gpio *dev = gpiochip_get_data(gc);
|
|
|
|
dev->irq_mask[ADP5588_BANK(d->hwirq)] &= ~ADP5588_BIT(d->hwirq);
|
|
}
|
|
|
|
static void adp5588_irq_unmask(struct irq_data *d)
|
|
{
|
|
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
|
struct adp5588_gpio *dev = gpiochip_get_data(gc);
|
|
|
|
dev->irq_mask[ADP5588_BANK(d->hwirq)] |= ADP5588_BIT(d->hwirq);
|
|
}
|
|
|
|
static int adp5588_irq_set_type(struct irq_data *d, unsigned int type)
|
|
{
|
|
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
|
|
struct adp5588_gpio *dev = gpiochip_get_data(gc);
|
|
uint16_t gpio = d->hwirq;
|
|
unsigned bank, bit;
|
|
|
|
bank = ADP5588_BANK(gpio);
|
|
bit = ADP5588_BIT(gpio);
|
|
|
|
dev->int_lvl_low[bank] &= ~bit;
|
|
dev->int_lvl_high[bank] &= ~bit;
|
|
|
|
if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_HIGH)
|
|
dev->int_lvl_high[bank] |= bit;
|
|
|
|
if (type & IRQ_TYPE_EDGE_BOTH || type & IRQ_TYPE_LEVEL_LOW)
|
|
dev->int_lvl_low[bank] |= bit;
|
|
|
|
dev->int_input_en[bank] |= bit;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct irq_chip adp5588_irq_chip = {
|
|
.name = "adp5588",
|
|
.irq_mask = adp5588_irq_mask,
|
|
.irq_unmask = adp5588_irq_unmask,
|
|
.irq_bus_lock = adp5588_irq_bus_lock,
|
|
.irq_bus_sync_unlock = adp5588_irq_bus_sync_unlock,
|
|
.irq_set_type = adp5588_irq_set_type,
|
|
};
|
|
|
|
static irqreturn_t adp5588_irq_handler(int irq, void *devid)
|
|
{
|
|
struct adp5588_gpio *dev = devid;
|
|
int status = adp5588_gpio_read(dev->client, INT_STAT);
|
|
|
|
if (status & ADP5588_KE_INT) {
|
|
int ev_cnt = adp5588_gpio_read(dev->client, KEY_LCK_EC_STAT);
|
|
|
|
if (ev_cnt > 0) {
|
|
int i;
|
|
|
|
for (i = 0; i < (ev_cnt & ADP5588_KEC); i++) {
|
|
int key = adp5588_gpio_read(dev->client,
|
|
Key_EVENTA + i);
|
|
/* GPIN events begin at 97,
|
|
* bit 7 indicates logic level
|
|
*/
|
|
int gpio = (key & 0x7f) - 97;
|
|
int lvl = key & (1 << 7);
|
|
int bank = ADP5588_BANK(gpio);
|
|
int bit = ADP5588_BIT(gpio);
|
|
|
|
if ((lvl && dev->int_lvl_high[bank] & bit) ||
|
|
(!lvl && dev->int_lvl_low[bank] & bit))
|
|
handle_nested_irq(irq_find_mapping(
|
|
dev->gpio_chip.irq.domain, gpio));
|
|
}
|
|
}
|
|
}
|
|
|
|
adp5588_gpio_write(dev->client, INT_STAT, status); /* Status is W1C */
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
|
|
static int adp5588_irq_init_hw(struct gpio_chip *gc)
|
|
{
|
|
struct adp5588_gpio *dev = gpiochip_get_data(gc);
|
|
/* Enable IRQs after registering chip */
|
|
adp5588_gpio_write(dev->client, CFG,
|
|
ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_KE_IEN);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int adp5588_irq_setup(struct adp5588_gpio *dev)
|
|
{
|
|
struct i2c_client *client = dev->client;
|
|
int ret;
|
|
struct adp5588_gpio_platform_data *pdata =
|
|
dev_get_platdata(&client->dev);
|
|
struct gpio_irq_chip *girq;
|
|
|
|
adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC);
|
|
adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */
|
|
|
|
mutex_init(&dev->irq_lock);
|
|
|
|
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
|
NULL, adp5588_irq_handler, IRQF_ONESHOT
|
|
| IRQF_TRIGGER_FALLING | IRQF_SHARED,
|
|
dev_name(&client->dev), dev);
|
|
if (ret) {
|
|
dev_err(&client->dev, "failed to request irq %d\n",
|
|
client->irq);
|
|
return ret;
|
|
}
|
|
|
|
/* This will be registered in the call to devm_gpiochip_add_data() */
|
|
girq = &dev->gpio_chip.irq;
|
|
girq->chip = &adp5588_irq_chip;
|
|
/* This will let us handle the parent IRQ in the driver */
|
|
girq->parent_handler = NULL;
|
|
girq->num_parents = 0;
|
|
girq->parents = NULL;
|
|
girq->first = pdata ? pdata->irq_base : 0;
|
|
girq->default_type = IRQ_TYPE_NONE;
|
|
girq->handler = handle_simple_irq;
|
|
girq->init_hw = adp5588_irq_init_hw;
|
|
girq->threaded = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
#else
|
|
static int adp5588_irq_setup(struct adp5588_gpio *dev)
|
|
{
|
|
struct i2c_client *client = dev->client;
|
|
dev_warn(&client->dev, "interrupt support not compiled in\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif /* CONFIG_GPIO_ADP5588_IRQ */
|
|
|
|
static int adp5588_gpio_probe(struct i2c_client *client)
|
|
{
|
|
struct adp5588_gpio_platform_data *pdata =
|
|
dev_get_platdata(&client->dev);
|
|
struct adp5588_gpio *dev;
|
|
struct gpio_chip *gc;
|
|
int ret, i, revid;
|
|
unsigned int pullup_dis_mask = 0;
|
|
|
|
if (!i2c_check_functionality(client->adapter,
|
|
I2C_FUNC_SMBUS_BYTE_DATA)) {
|
|
dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
|
|
return -EIO;
|
|
}
|
|
|
|
dev = devm_kzalloc(&client->dev, sizeof(*dev), GFP_KERNEL);
|
|
if (!dev)
|
|
return -ENOMEM;
|
|
|
|
dev->client = client;
|
|
|
|
gc = &dev->gpio_chip;
|
|
gc->direction_input = adp5588_gpio_direction_input;
|
|
gc->direction_output = adp5588_gpio_direction_output;
|
|
gc->get = adp5588_gpio_get_value;
|
|
gc->set = adp5588_gpio_set_value;
|
|
gc->can_sleep = true;
|
|
gc->base = -1;
|
|
gc->parent = &client->dev;
|
|
|
|
if (pdata) {
|
|
gc->base = pdata->gpio_start;
|
|
gc->names = pdata->names;
|
|
pullup_dis_mask = pdata->pullup_dis_mask;
|
|
}
|
|
|
|
gc->ngpio = ADP5588_MAXGPIO;
|
|
gc->label = client->name;
|
|
gc->owner = THIS_MODULE;
|
|
|
|
mutex_init(&dev->lock);
|
|
|
|
ret = adp5588_gpio_read(dev->client, DEV_ID);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
revid = ret & ADP5588_DEVICE_ID_MASK;
|
|
|
|
for (i = 0, ret = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++) {
|
|
dev->dat_out[i] = adp5588_gpio_read(client, GPIO_DAT_OUT1 + i);
|
|
dev->dir[i] = adp5588_gpio_read(client, GPIO_DIR1 + i);
|
|
ret |= adp5588_gpio_write(client, KP_GPIO1 + i, 0);
|
|
ret |= adp5588_gpio_write(client, GPIO_PULL1 + i,
|
|
(pullup_dis_mask >> (8 * i)) & 0xFF);
|
|
ret |= adp5588_gpio_write(client, GPIO_INT_EN1 + i, 0);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (client->irq) {
|
|
if (WA_DELAYED_READOUT_REVID(revid)) {
|
|
dev_warn(&client->dev, "GPIO int not supported\n");
|
|
} else {
|
|
ret = adp5588_irq_setup(dev);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
ret = devm_gpiochip_add_data(&client->dev, &dev->gpio_chip, dev);
|
|
if (ret)
|
|
return ret;
|
|
|
|
i2c_set_clientdata(client, dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int adp5588_gpio_remove(struct i2c_client *client)
|
|
{
|
|
struct adp5588_gpio *dev = i2c_get_clientdata(client);
|
|
|
|
if (dev->client->irq)
|
|
free_irq(dev->client->irq, dev);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct i2c_device_id adp5588_gpio_id[] = {
|
|
{DRV_NAME, 0},
|
|
{}
|
|
};
|
|
MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id);
|
|
|
|
#ifdef CONFIG_OF
|
|
static const struct of_device_id adp5588_gpio_of_id[] = {
|
|
{ .compatible = "adi," DRV_NAME, },
|
|
{},
|
|
};
|
|
MODULE_DEVICE_TABLE(of, adp5588_gpio_of_id);
|
|
#endif
|
|
|
|
static struct i2c_driver adp5588_gpio_driver = {
|
|
.driver = {
|
|
.name = DRV_NAME,
|
|
.of_match_table = of_match_ptr(adp5588_gpio_of_id),
|
|
},
|
|
.probe_new = adp5588_gpio_probe,
|
|
.remove = adp5588_gpio_remove,
|
|
.id_table = adp5588_gpio_id,
|
|
};
|
|
|
|
module_i2c_driver(adp5588_gpio_driver);
|
|
|
|
MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
|
|
MODULE_DESCRIPTION("GPIO ADP5588 Driver");
|
|
MODULE_LICENSE("GPL");
|