mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 13:44:15 +08:00
c4941e072e
commit 3de07e5aaf290a0b58919 ("drivers: gpio: use devm_kzalloc") introduce devm_kzalloc() for allocating the memory. In this case kfree is not required to use. Above commit id introduced the bug that kfree is not removed from the remove function. This patch fix the above bug. Signed-off-by: Varka Bhadram <varkab@cdac.in> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
498 lines
12 KiB
C
498 lines
12 KiB
C
/*
|
|
* GPIO Chip driver for Analog Devices
|
|
* ADP5588/ADP5587 I/O Expander and QWERTY Keypad Controller
|
|
*
|
|
* Copyright 2009-2010 Analog Devices Inc.
|
|
*
|
|
* Licensed under the GPL-2 or later.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/init.h>
|
|
#include <linux/i2c.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/irq.h>
|
|
|
|
#include <linux/i2c/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;
|
|
unsigned gpio_start;
|
|
unsigned irq_base;
|
|
uint8_t dat_out[3];
|
|
uint8_t dir[3];
|
|
uint8_t int_lvl[3];
|
|
uint8_t int_en[3];
|
|
uint8_t irq_mask[3];
|
|
uint8_t irq_stat[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 =
|
|
container_of(chip, struct adp5588_gpio, gpio_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 =
|
|
container_of(chip, struct adp5588_gpio, gpio_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 =
|
|
container_of(chip, struct adp5588_gpio, gpio_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 =
|
|
container_of(chip, struct adp5588_gpio, gpio_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 int adp5588_gpio_to_irq(struct gpio_chip *chip, unsigned off)
|
|
{
|
|
struct adp5588_gpio *dev =
|
|
container_of(chip, struct adp5588_gpio, gpio_chip);
|
|
return dev->irq_base + off;
|
|
}
|
|
|
|
static void adp5588_irq_bus_lock(struct irq_data *d)
|
|
{
|
|
struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
|
|
|
|
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 adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
|
|
int i;
|
|
|
|
for (i = 0; i <= ADP5588_BANK(ADP5588_MAXGPIO); i++)
|
|
if (dev->int_en[i] ^ dev->irq_mask[i]) {
|
|
dev->int_en[i] = dev->irq_mask[i];
|
|
adp5588_gpio_write(dev->client, GPIO_INT_EN1 + i,
|
|
dev->int_en[i]);
|
|
}
|
|
|
|
mutex_unlock(&dev->irq_lock);
|
|
}
|
|
|
|
static void adp5588_irq_mask(struct irq_data *d)
|
|
{
|
|
struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
|
|
unsigned gpio = d->irq - dev->irq_base;
|
|
|
|
dev->irq_mask[ADP5588_BANK(gpio)] &= ~ADP5588_BIT(gpio);
|
|
}
|
|
|
|
static void adp5588_irq_unmask(struct irq_data *d)
|
|
{
|
|
struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
|
|
unsigned gpio = d->irq - dev->irq_base;
|
|
|
|
dev->irq_mask[ADP5588_BANK(gpio)] |= ADP5588_BIT(gpio);
|
|
}
|
|
|
|
static int adp5588_irq_set_type(struct irq_data *d, unsigned int type)
|
|
{
|
|
struct adp5588_gpio *dev = irq_data_get_irq_chip_data(d);
|
|
uint16_t gpio = d->irq - dev->irq_base;
|
|
unsigned bank, bit;
|
|
|
|
if ((type & IRQ_TYPE_EDGE_BOTH)) {
|
|
dev_err(&dev->client->dev, "irq %d: unsupported type %d\n",
|
|
d->irq, type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
bank = ADP5588_BANK(gpio);
|
|
bit = ADP5588_BIT(gpio);
|
|
|
|
if (type & IRQ_TYPE_LEVEL_HIGH)
|
|
dev->int_lvl[bank] |= bit;
|
|
else if (type & IRQ_TYPE_LEVEL_LOW)
|
|
dev->int_lvl[bank] &= ~bit;
|
|
else
|
|
return -EINVAL;
|
|
|
|
adp5588_gpio_direction_input(&dev->gpio_chip, gpio);
|
|
adp5588_gpio_write(dev->client, GPIO_INT_LVL1 + bank,
|
|
dev->int_lvl[bank]);
|
|
|
|
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 int adp5588_gpio_read_intstat(struct i2c_client *client, u8 *buf)
|
|
{
|
|
int ret = i2c_smbus_read_i2c_block_data(client, GPIO_INT_STAT1, 3, buf);
|
|
|
|
if (ret < 0)
|
|
dev_err(&client->dev, "Read INT_STAT Error\n");
|
|
|
|
return ret;
|
|
}
|
|
|
|
static irqreturn_t adp5588_irq_handler(int irq, void *devid)
|
|
{
|
|
struct adp5588_gpio *dev = devid;
|
|
unsigned status, bank, bit, pending;
|
|
int ret;
|
|
status = adp5588_gpio_read(dev->client, INT_STAT);
|
|
|
|
if (status & ADP5588_GPI_INT) {
|
|
ret = adp5588_gpio_read_intstat(dev->client, dev->irq_stat);
|
|
if (ret < 0)
|
|
memset(dev->irq_stat, 0, ARRAY_SIZE(dev->irq_stat));
|
|
|
|
for (bank = 0, bit = 0; bank <= ADP5588_BANK(ADP5588_MAXGPIO);
|
|
bank++, bit = 0) {
|
|
pending = dev->irq_stat[bank] & dev->irq_mask[bank];
|
|
|
|
while (pending) {
|
|
if (pending & (1 << bit)) {
|
|
handle_nested_irq(dev->irq_base +
|
|
(bank << 3) + bit);
|
|
pending &= ~(1 << bit);
|
|
|
|
}
|
|
bit++;
|
|
}
|
|
}
|
|
}
|
|
|
|
adp5588_gpio_write(dev->client, INT_STAT, status); /* Status is W1C */
|
|
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int adp5588_irq_setup(struct adp5588_gpio *dev)
|
|
{
|
|
struct i2c_client *client = dev->client;
|
|
struct adp5588_gpio_platform_data *pdata =
|
|
dev_get_platdata(&client->dev);
|
|
unsigned gpio;
|
|
int ret;
|
|
|
|
adp5588_gpio_write(client, CFG, ADP5588_AUTO_INC);
|
|
adp5588_gpio_write(client, INT_STAT, -1); /* status is W1C */
|
|
adp5588_gpio_read_intstat(client, dev->irq_stat); /* read to clear */
|
|
|
|
dev->irq_base = pdata->irq_base;
|
|
mutex_init(&dev->irq_lock);
|
|
|
|
for (gpio = 0; gpio < dev->gpio_chip.ngpio; gpio++) {
|
|
int irq = gpio + dev->irq_base;
|
|
irq_set_chip_data(irq, dev);
|
|
irq_set_chip_and_handler(irq, &adp5588_irq_chip,
|
|
handle_level_irq);
|
|
irq_set_nested_thread(irq, 1);
|
|
#ifdef CONFIG_ARM
|
|
/*
|
|
* ARM needs us to explicitly flag the IRQ as VALID,
|
|
* once we do so, it will also set the noprobe.
|
|
*/
|
|
set_irq_flags(irq, IRQF_VALID);
|
|
#else
|
|
irq_set_noprobe(irq);
|
|
#endif
|
|
}
|
|
|
|
ret = request_threaded_irq(client->irq,
|
|
NULL,
|
|
adp5588_irq_handler,
|
|
IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
|
|
dev_name(&client->dev), dev);
|
|
if (ret) {
|
|
dev_err(&client->dev, "failed to request irq %d\n",
|
|
client->irq);
|
|
goto out;
|
|
}
|
|
|
|
dev->gpio_chip.to_irq = adp5588_gpio_to_irq;
|
|
adp5588_gpio_write(client, CFG,
|
|
ADP5588_AUTO_INC | ADP5588_INT_CFG | ADP5588_GPI_INT);
|
|
|
|
return 0;
|
|
|
|
out:
|
|
dev->irq_base = 0;
|
|
return ret;
|
|
}
|
|
|
|
static void adp5588_irq_teardown(struct adp5588_gpio *dev)
|
|
{
|
|
if (dev->irq_base)
|
|
free_irq(dev->client->irq, dev);
|
|
}
|
|
|
|
#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;
|
|
}
|
|
|
|
static void adp5588_irq_teardown(struct adp5588_gpio *dev)
|
|
{
|
|
}
|
|
#endif /* CONFIG_GPIO_ADP5588_IRQ */
|
|
|
|
static int adp5588_gpio_probe(struct i2c_client *client,
|
|
const struct i2c_device_id *id)
|
|
{
|
|
struct adp5588_gpio_platform_data *pdata =
|
|
dev_get_platdata(&client->dev);
|
|
struct adp5588_gpio *dev;
|
|
struct gpio_chip *gc;
|
|
int ret, i, revid;
|
|
|
|
if (!pdata) {
|
|
dev_err(&client->dev, "missing platform data\n");
|
|
return -ENODEV;
|
|
}
|
|
|
|
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 = pdata->gpio_start;
|
|
gc->ngpio = ADP5588_MAXGPIO;
|
|
gc->label = client->name;
|
|
gc->owner = THIS_MODULE;
|
|
gc->names = pdata->names;
|
|
|
|
mutex_init(&dev->lock);
|
|
|
|
ret = adp5588_gpio_read(dev->client, DEV_ID);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
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,
|
|
(pdata->pullup_dis_mask >> (8 * i)) & 0xFF);
|
|
ret |= adp5588_gpio_write(client, GPIO_INT_EN1 + i, 0);
|
|
if (ret)
|
|
goto err;
|
|
}
|
|
|
|
if (pdata->irq_base) {
|
|
if (WA_DELAYED_READOUT_REVID(revid)) {
|
|
dev_warn(&client->dev, "GPIO int not supported\n");
|
|
} else {
|
|
ret = adp5588_irq_setup(dev);
|
|
if (ret)
|
|
goto err;
|
|
}
|
|
}
|
|
|
|
ret = gpiochip_add(&dev->gpio_chip);
|
|
if (ret)
|
|
goto err_irq;
|
|
|
|
dev_info(&client->dev, "IRQ Base: %d Rev.: %d\n",
|
|
pdata->irq_base, revid);
|
|
|
|
if (pdata->setup) {
|
|
ret = pdata->setup(client, gc->base, gc->ngpio, pdata->context);
|
|
if (ret < 0)
|
|
dev_warn(&client->dev, "setup failed, %d\n", ret);
|
|
}
|
|
|
|
i2c_set_clientdata(client, dev);
|
|
|
|
return 0;
|
|
|
|
err_irq:
|
|
adp5588_irq_teardown(dev);
|
|
err:
|
|
return ret;
|
|
}
|
|
|
|
static int adp5588_gpio_remove(struct i2c_client *client)
|
|
{
|
|
struct adp5588_gpio_platform_data *pdata =
|
|
dev_get_platdata(&client->dev);
|
|
struct adp5588_gpio *dev = i2c_get_clientdata(client);
|
|
int ret;
|
|
|
|
if (pdata->teardown) {
|
|
ret = pdata->teardown(client,
|
|
dev->gpio_chip.base, dev->gpio_chip.ngpio,
|
|
pdata->context);
|
|
if (ret < 0) {
|
|
dev_err(&client->dev, "teardown failed %d\n", ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (dev->irq_base)
|
|
free_irq(dev->client->irq, dev);
|
|
|
|
gpiochip_remove(&dev->gpio_chip);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct i2c_device_id adp5588_gpio_id[] = {
|
|
{DRV_NAME, 0},
|
|
{}
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id);
|
|
|
|
static struct i2c_driver adp5588_gpio_driver = {
|
|
.driver = {
|
|
.name = DRV_NAME,
|
|
},
|
|
.probe = adp5588_gpio_probe,
|
|
.remove = adp5588_gpio_remove,
|
|
.id_table = adp5588_gpio_id,
|
|
};
|
|
|
|
module_i2c_driver(adp5588_gpio_driver);
|
|
|
|
MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
|
|
MODULE_DESCRIPTION("GPIO ADP5588 Driver");
|
|
MODULE_LICENSE("GPL");
|