Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 984f6643 authored by Linus Walleij's avatar Linus Walleij
Browse files

gpio: max732x: convert to GPIOLIB_IRQCHIP



Take a sweep to bring the irq support for the MAX732x expanders
into the gpiolib core to cut down on duplicated code.

Only compile tested! I need some feedback from people using this
expander with interrupts to tell me if things go right or
wrong when I do this.

Cc: Semen Protsenko <semen.protsenko@globallogic.com>
Cc: Mans Rullgard <mans@mansr.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent fd9c963c
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -543,7 +543,6 @@ config GPIO_MAX7300
config GPIO_MAX732X
config GPIO_MAX732X
	tristate "MAX7319, MAX7320-7327 I2C Port Expanders"
	tristate "MAX7319, MAX7320-7327 I2C Port Expanders"
	depends on I2C
	depends on I2C
	select IRQ_DOMAIN
	help
	help
	  Say yes here to support the MAX7319, MAX7320-7327 series of I2C
	  Say yes here to support the MAX7319, MAX7320-7327 series of I2C
	  Port Expanders. Each IO port on these chips has a fixed role of
	  Port Expanders. Each IO port on these chips has a fixed role of
@@ -563,6 +562,7 @@ config GPIO_MAX732X
config GPIO_MAX732X_IRQ
config GPIO_MAX732X_IRQ
	bool "Interrupt controller support for MAX732x"
	bool "Interrupt controller support for MAX732x"
	depends on GPIO_MAX732X=y
	depends on GPIO_MAX732X=y
	select GPIOLIB_IRQCHIP
	help
	help
	  Say yes here to enable the max732x to be used as an interrupt
	  Say yes here to enable the max732x to be used as an interrupt
	  controller. It requires the driver to be built in the kernel.
	  controller. It requires the driver to be built in the kernel.
+42 −92
Original line number Original line Diff line number Diff line
@@ -4,6 +4,7 @@
 *  Copyright (C) 2007 Marvell International Ltd.
 *  Copyright (C) 2007 Marvell International Ltd.
 *  Copyright (C) 2008 Jack Ren <jack.ren@marvell.com>
 *  Copyright (C) 2008 Jack Ren <jack.ren@marvell.com>
 *  Copyright (C) 2008 Eric Miao <eric.miao@marvell.com>
 *  Copyright (C) 2008 Eric Miao <eric.miao@marvell.com>
 *  Copyright (C) 2015 Linus Walleij <linus.walleij@linaro.org>
 *
 *
 *  Derived from drivers/gpio/pca953x.c
 *  Derived from drivers/gpio/pca953x.c
 *
 *
@@ -16,10 +17,8 @@
#include <linux/init.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irqdomain.h>
#include <linux/i2c.h>
#include <linux/i2c.h>
#include <linux/i2c/max732x.h>
#include <linux/i2c/max732x.h>
#include <linux/of.h>
#include <linux/of.h>
@@ -150,9 +149,7 @@ struct max732x_chip {
	uint8_t		reg_out[2];
	uint8_t		reg_out[2];


#ifdef CONFIG_GPIO_MAX732X_IRQ
#ifdef CONFIG_GPIO_MAX732X_IRQ
	struct irq_domain	*irq_domain;
	struct mutex		irq_lock;
	struct mutex		irq_lock;
	int			irq_base;
	uint8_t			irq_mask;
	uint8_t			irq_mask;
	uint8_t			irq_mask_cur;
	uint8_t			irq_mask_cur;
	uint8_t			irq_trig_raise;
	uint8_t			irq_trig_raise;
@@ -356,35 +353,26 @@ static void max732x_irq_update_mask(struct max732x_chip *chip)
	mutex_unlock(&chip->lock);
	mutex_unlock(&chip->lock);
}
}


static int max732x_gpio_to_irq(struct gpio_chip *gc, unsigned off)
{
	struct max732x_chip *chip = to_max732x(gc);

	if (chip->irq_domain) {
		return irq_create_mapping(chip->irq_domain,
				chip->irq_base + off);
	} else {
		return -ENXIO;
	}
}

static void max732x_irq_mask(struct irq_data *d)
static void max732x_irq_mask(struct irq_data *d)
{
{
	struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct max732x_chip *chip = to_max732x(gc);


	chip->irq_mask_cur &= ~(1 << d->hwirq);
	chip->irq_mask_cur &= ~(1 << d->hwirq);
}
}


static void max732x_irq_unmask(struct irq_data *d)
static void max732x_irq_unmask(struct irq_data *d)
{
{
	struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct max732x_chip *chip = to_max732x(gc);


	chip->irq_mask_cur |= 1 << d->hwirq;
	chip->irq_mask_cur |= 1 << d->hwirq;
}
}


static void max732x_irq_bus_lock(struct irq_data *d)
static void max732x_irq_bus_lock(struct irq_data *d)
{
{
	struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct max732x_chip *chip = to_max732x(gc);


	mutex_lock(&chip->irq_lock);
	mutex_lock(&chip->irq_lock);
	chip->irq_mask_cur = chip->irq_mask;
	chip->irq_mask_cur = chip->irq_mask;
@@ -392,7 +380,8 @@ static void max732x_irq_bus_lock(struct irq_data *d)


static void max732x_irq_bus_sync_unlock(struct irq_data *d)
static void max732x_irq_bus_sync_unlock(struct irq_data *d)
{
{
	struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct max732x_chip *chip = to_max732x(gc);
	uint16_t new_irqs;
	uint16_t new_irqs;
	uint16_t level;
	uint16_t level;


@@ -410,7 +399,8 @@ static void max732x_irq_bus_sync_unlock(struct irq_data *d)


static int max732x_irq_set_type(struct irq_data *d, unsigned int type)
static int max732x_irq_set_type(struct irq_data *d, unsigned int type)
{
{
	struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
	struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
	struct max732x_chip *chip = to_max732x(gc);
	uint16_t off = d->hwirq;
	uint16_t off = d->hwirq;
	uint16_t mask = 1 << off;
	uint16_t mask = 1 << off;


@@ -492,7 +482,8 @@ static irqreturn_t max732x_irq_handler(int irq, void *devid)


	do {
	do {
		level = __ffs(pending);
		level = __ffs(pending);
		handle_nested_irq(irq_find_mapping(chip->irq_domain, level));
		handle_nested_irq(irq_find_mapping(chip->gpio_chip.irqdomain,
						   level));


		pending &= ~(1 << level);
		pending &= ~(1 << level);
	} while (pending);
	} while (pending);
@@ -500,68 +491,24 @@ static irqreturn_t max732x_irq_handler(int irq, void *devid)
	return IRQ_HANDLED;
	return IRQ_HANDLED;
}
}


static int max732x_irq_map(struct irq_domain *h, unsigned int virq,
		irq_hw_number_t hw)
{
	struct max732x_chip *chip = h->host_data;

	if (!(chip->dir_input & (1 << hw))) {
		dev_err(&chip->client->dev,
				"Attempt to map output line as IRQ line: %lu\n",
				hw);
		return -EPERM;
	}

	irq_set_chip_data(virq, chip);
	irq_set_chip_and_handler(virq, &max732x_irq_chip,
			handle_edge_irq);
	irq_set_nested_thread(virq, 1);
#ifdef CONFIG_ARM
	/* ARM needs us to explicitly flag the IRQ as valid
	 * and will set them noprobe when we do so. */
	set_irq_flags(virq, IRQF_VALID);
#else
	irq_set_noprobe(virq);
#endif

	return 0;
}

static struct irq_domain_ops max732x_irq_domain_ops = {
	.map	= max732x_irq_map,
	.xlate	= irq_domain_xlate_twocell,
};

static void max732x_irq_teardown(struct max732x_chip *chip)
{
	if (chip->client->irq && chip->irq_domain)
		irq_domain_remove(chip->irq_domain);
}

static int max732x_irq_setup(struct max732x_chip *chip,
static int max732x_irq_setup(struct max732x_chip *chip,
			     const struct i2c_device_id *id)
			     const struct i2c_device_id *id)
{
{
	struct i2c_client *client = chip->client;
	struct i2c_client *client = chip->client;
	struct max732x_platform_data *pdata = dev_get_platdata(&client->dev);
	struct max732x_platform_data *pdata = dev_get_platdata(&client->dev);
	int has_irq = max732x_features[id->driver_data] >> 32;
	int has_irq = max732x_features[id->driver_data] >> 32;
	int irq_base = 0;
	int ret;
	int ret;


	if (((pdata && pdata->irq_base) || client->irq)
	if (((pdata && pdata->irq_base) || client->irq)
			&& has_irq != INT_NONE) {
			&& has_irq != INT_NONE) {
		if (pdata)
		if (pdata)
			chip->irq_base = pdata->irq_base;
			irq_base = pdata->irq_base;
		chip->irq_features = has_irq;
		chip->irq_features = has_irq;
		mutex_init(&chip->irq_lock);
		mutex_init(&chip->irq_lock);


		chip->irq_domain = irq_domain_add_simple(client->dev.of_node,
		ret = devm_request_threaded_irq(&client->dev,
				chip->gpio_chip.ngpio, chip->irq_base,
					client->irq,
				&max732x_irq_domain_ops, chip);
		if (!chip->irq_domain) {
			dev_err(&client->dev, "Failed to create IRQ domain\n");
			return -ENOMEM;
		}

		ret = request_threaded_irq(client->irq,
					NULL,
					NULL,
					max732x_irq_handler,
					max732x_irq_handler,
					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
					IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
@@ -569,17 +516,25 @@ static int max732x_irq_setup(struct max732x_chip *chip,
		if (ret) {
		if (ret) {
			dev_err(&client->dev, "failed to request irq %d\n",
			dev_err(&client->dev, "failed to request irq %d\n",
				client->irq);
				client->irq);
			goto out_failed;
			return ret;
		}
		}

		ret =  gpiochip_irqchip_add(&chip->gpio_chip,
		chip->gpio_chip.to_irq = max732x_gpio_to_irq;
					    &max732x_irq_chip,
					    irq_base,
					    handle_edge_irq,
					    IRQ_TYPE_NONE);
		if (ret) {
			dev_err(&client->dev,
				"could not connect irqchip to gpiochip\n");
			return ret;
		}
		gpiochip_set_chained_irqchip(&chip->gpio_chip,
					     &max732x_irq_chip,
					     client->irq,
					     NULL);
	}
	}


	return 0;
	return 0;

out_failed:
	max732x_irq_teardown(chip);
	return ret;
}
}


#else /* CONFIG_GPIO_MAX732X_IRQ */
#else /* CONFIG_GPIO_MAX732X_IRQ */
@@ -595,10 +550,6 @@ static int max732x_irq_setup(struct max732x_chip *chip,


	return 0;
	return 0;
}
}

static void max732x_irq_teardown(struct max732x_chip *chip)
{
}
#endif
#endif


static int max732x_setup_gpio(struct max732x_chip *chip,
static int max732x_setup_gpio(struct max732x_chip *chip,
@@ -730,13 +681,15 @@ static int max732x_probe(struct i2c_client *client,
	if (nr_port > 8)
	if (nr_port > 8)
		max732x_readb(chip, is_group_a(chip, 8), &chip->reg_out[1]);
		max732x_readb(chip, is_group_a(chip, 8), &chip->reg_out[1]);


	ret = max732x_irq_setup(chip, id);
	ret = gpiochip_add(&chip->gpio_chip);
	if (ret)
	if (ret)
		goto out_failed;
		goto out_failed;


	ret = gpiochip_add(&chip->gpio_chip);
	ret = max732x_irq_setup(chip, id);
	if (ret)
	if (ret) {
		gpiochip_remove(&chip->gpio_chip);
		goto out_failed;
		goto out_failed;
	}


	if (pdata && pdata->setup) {
	if (pdata && pdata->setup) {
		ret = pdata->setup(client, chip->gpio_chip.base,
		ret = pdata->setup(client, chip->gpio_chip.base,
@@ -751,7 +704,6 @@ static int max732x_probe(struct i2c_client *client,
out_failed:
out_failed:
	if (chip->client_dummy)
	if (chip->client_dummy)
		i2c_unregister_device(chip->client_dummy);
		i2c_unregister_device(chip->client_dummy);
	max732x_irq_teardown(chip);
	return ret;
	return ret;
}
}


@@ -774,8 +726,6 @@ static int max732x_remove(struct i2c_client *client)


	gpiochip_remove(&chip->gpio_chip);
	gpiochip_remove(&chip->gpio_chip);


	max732x_irq_teardown(chip);

	/* unregister any dummy i2c_client */
	/* unregister any dummy i2c_client */
	if (chip->client_dummy)
	if (chip->client_dummy)
		i2c_unregister_device(chip->client_dummy);
		i2c_unregister_device(chip->client_dummy);