summaryrefslogtreecommitdiffstats
path: root/drivers/gpio/gpio-max732x.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpio/gpio-max732x.c')
-rw-r--r--drivers/gpio/gpio-max732x.c225
1 files changed, 161 insertions, 64 deletions
diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c
index 6c676225b886..a095b2393fe9 100644
--- a/drivers/gpio/gpio-max732x.c
+++ b/drivers/gpio/gpio-max732x.c
@@ -19,8 +19,10 @@
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/irqdomain.h>
#include <linux/i2c.h>
#include <linux/i2c/max732x.h>
+#include <linux/of.h>
/*
@@ -116,6 +118,22 @@ static const struct i2c_device_id max732x_id[] = {
};
MODULE_DEVICE_TABLE(i2c, max732x_id);
+#ifdef CONFIG_OF
+static const struct of_device_id max732x_of_table[] = {
+ { .compatible = "maxim,max7319" },
+ { .compatible = "maxim,max7320" },
+ { .compatible = "maxim,max7321" },
+ { .compatible = "maxim,max7322" },
+ { .compatible = "maxim,max7323" },
+ { .compatible = "maxim,max7324" },
+ { .compatible = "maxim,max7325" },
+ { .compatible = "maxim,max7326" },
+ { .compatible = "maxim,max7327" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max732x_of_table);
+#endif
+
struct max732x_chip {
struct gpio_chip gpio_chip;
@@ -132,16 +150,22 @@ struct max732x_chip {
uint8_t reg_out[2];
#ifdef CONFIG_GPIO_MAX732X_IRQ
- struct mutex irq_lock;
- int irq_base;
- uint8_t irq_mask;
- uint8_t irq_mask_cur;
- uint8_t irq_trig_raise;
- uint8_t irq_trig_fall;
- uint8_t irq_features;
+ struct irq_domain *irq_domain;
+ struct mutex irq_lock;
+ int irq_base;
+ uint8_t irq_mask;
+ uint8_t irq_mask_cur;
+ uint8_t irq_trig_raise;
+ uint8_t irq_trig_fall;
+ uint8_t irq_features;
#endif
};
+static inline struct max732x_chip *to_max732x(struct gpio_chip *gc)
+{
+ return container_of(gc, struct max732x_chip, gpio_chip);
+}
+
static int max732x_writeb(struct max732x_chip *chip, int group_a, uint8_t val)
{
struct i2c_client *client;
@@ -180,12 +204,10 @@ static inline int is_group_a(struct max732x_chip *chip, unsigned off)
static int max732x_gpio_get_value(struct gpio_chip *gc, unsigned off)
{
- struct max732x_chip *chip;
+ struct max732x_chip *chip = to_max732x(gc);
uint8_t reg_val;
int ret;
- chip = container_of(gc, struct max732x_chip, gpio_chip);
-
ret = max732x_readb(chip, is_group_a(chip, off), &reg_val);
if (ret < 0)
return 0;
@@ -193,18 +215,17 @@ static int max732x_gpio_get_value(struct gpio_chip *gc, unsigned off)
return reg_val & (1u << (off & 0x7));
}
-static void max732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
+static void max732x_gpio_set_mask(struct gpio_chip *gc, unsigned off, int mask,
+ int val)
{
- struct max732x_chip *chip;
- uint8_t reg_out, mask = 1u << (off & 0x7);
+ struct max732x_chip *chip = to_max732x(gc);
+ uint8_t reg_out;
int ret;
- chip = container_of(gc, struct max732x_chip, gpio_chip);
-
mutex_lock(&chip->lock);
reg_out = (off > 7) ? chip->reg_out[1] : chip->reg_out[0];
- reg_out = (val) ? reg_out | mask : reg_out & ~mask;
+ reg_out = (reg_out & ~mask) | (val & mask);
ret = max732x_writeb(chip, is_group_a(chip, off), reg_out);
if (ret < 0)
@@ -219,13 +240,31 @@ out:
mutex_unlock(&chip->lock);
}
+static void max732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
+{
+ unsigned base = off & ~0x7;
+ uint8_t mask = 1u << (off & 0x7);
+
+ max732x_gpio_set_mask(gc, base, mask, val << (off & 0x7));
+}
+
+static void max732x_gpio_set_multiple(struct gpio_chip *gc,
+ unsigned long *mask, unsigned long *bits)
+{
+ unsigned mask_lo = mask[0] & 0xff;
+ unsigned mask_hi = (mask[0] >> 8) & 0xff;
+
+ if (mask_lo)
+ max732x_gpio_set_mask(gc, 0, mask_lo, bits[0] & 0xff);
+ if (mask_hi)
+ max732x_gpio_set_mask(gc, 8, mask_hi, (bits[0] >> 8) & 0xff);
+}
+
static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
{
- struct max732x_chip *chip;
+ struct max732x_chip *chip = to_max732x(gc);
unsigned int mask = 1u << off;
- chip = container_of(gc, struct max732x_chip, gpio_chip);
-
if ((mask & chip->dir_input) == 0) {
dev_dbg(&chip->client->dev, "%s port %d is output only\n",
chip->client->name, off);
@@ -245,11 +284,9 @@ static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
static int max732x_gpio_direction_output(struct gpio_chip *gc,
unsigned off, int val)
{
- struct max732x_chip *chip;
+ struct max732x_chip *chip = to_max732x(gc);
unsigned int mask = 1u << off;
- chip = container_of(gc, struct max732x_chip, gpio_chip);
-
if ((mask & chip->dir_output) == 0) {
dev_dbg(&chip->client->dev, "%s port %d is input only\n",
chip->client->name, off);
@@ -321,24 +358,28 @@ static void max732x_irq_update_mask(struct max732x_chip *chip)
static int max732x_gpio_to_irq(struct gpio_chip *gc, unsigned off)
{
- struct max732x_chip *chip;
+ struct max732x_chip *chip = to_max732x(gc);
- chip = container_of(gc, struct max732x_chip, gpio_chip);
- return chip->irq_base + off;
+ 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)
{
struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
- chip->irq_mask_cur &= ~(1 << (d->irq - chip->irq_base));
+ chip->irq_mask_cur &= ~(1 << d->hwirq);
}
static void max732x_irq_unmask(struct irq_data *d)
{
struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
- chip->irq_mask_cur |= 1 << (d->irq - chip->irq_base);
+ chip->irq_mask_cur |= 1 << d->hwirq;
}
static void max732x_irq_bus_lock(struct irq_data *d)
@@ -352,15 +393,25 @@ static void max732x_irq_bus_lock(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);
+ uint16_t new_irqs;
+ uint16_t level;
max732x_irq_update_mask(chip);
+
+ new_irqs = chip->irq_trig_fall | chip->irq_trig_raise;
+ while (new_irqs) {
+ level = __ffs(new_irqs);
+ max732x_gpio_direction_input(&chip->gpio_chip, level);
+ new_irqs &= ~(1 << level);
+ }
+
mutex_unlock(&chip->irq_lock);
}
static int max732x_irq_set_type(struct irq_data *d, unsigned int type)
{
struct max732x_chip *chip = irq_data_get_irq_chip_data(d);
- uint16_t off = d->irq - chip->irq_base;
+ uint16_t off = d->hwirq;
uint16_t mask = 1 << off;
if (!(mask & chip->dir_input)) {
@@ -385,7 +436,7 @@ static int max732x_irq_set_type(struct irq_data *d, unsigned int type)
else
chip->irq_trig_raise &= ~mask;
- return max732x_gpio_direction_input(&chip->gpio_chip, off);
+ return 0;
}
static struct irq_chip max732x_irq_chip = {
@@ -441,7 +492,7 @@ static irqreturn_t max732x_irq_handler(int irq, void *devid)
do {
level = __ffs(pending);
- handle_nested_irq(level + chip->irq_base);
+ handle_nested_irq(irq_find_mapping(chip->irq_domain, level));
pending &= ~(1 << level);
} while (pending);
@@ -449,6 +500,44 @@ static irqreturn_t max732x_irq_handler(int irq, void *devid)
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,
const struct i2c_device_id *id)
{
@@ -457,28 +546,19 @@ static int max732x_irq_setup(struct max732x_chip *chip,
int has_irq = max732x_features[id->driver_data] >> 32;
int ret;
- if (pdata->irq_base && has_irq != INT_NONE) {
- int lvl;
-
- chip->irq_base = pdata->irq_base;
+ if (((pdata && pdata->irq_base) || client->irq)
+ && has_irq != INT_NONE) {
+ if (pdata)
+ chip->irq_base = pdata->irq_base;
chip->irq_features = has_irq;
mutex_init(&chip->irq_lock);
- for (lvl = 0; lvl < chip->gpio_chip.ngpio; lvl++) {
- int irq = lvl + chip->irq_base;
-
- if (!(chip->dir_input & (1 << lvl)))
- continue;
-
- irq_set_chip_data(irq, chip);
- irq_set_chip_and_handler(irq, &max732x_irq_chip,
- handle_edge_irq);
- irq_set_nested_thread(irq, 1);
-#ifdef CONFIG_ARM
- set_irq_flags(irq, IRQF_VALID);
-#else
- irq_set_noprobe(irq);
-#endif
+ chip->irq_domain = irq_domain_add_simple(client->dev.of_node,
+ chip->gpio_chip.ngpio, chip->irq_base,
+ &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,
@@ -498,15 +578,10 @@ static int max732x_irq_setup(struct max732x_chip *chip,
return 0;
out_failed:
- chip->irq_base = 0;
+ max732x_irq_teardown(chip);
return ret;
}
-static void max732x_irq_teardown(struct max732x_chip *chip)
-{
- if (chip->irq_base)
- free_irq(chip->client->irq, chip);
-}
#else /* CONFIG_GPIO_MAX732X_IRQ */
static int max732x_irq_setup(struct max732x_chip *chip,
const struct i2c_device_id *id)
@@ -515,7 +590,7 @@ static int max732x_irq_setup(struct max732x_chip *chip,
struct max732x_platform_data *pdata = dev_get_platdata(&client->dev);
int has_irq = max732x_features[id->driver_data] >> 32;
- if (pdata->irq_base && has_irq != INT_NONE)
+ if (((pdata && pdata->irq_base) || client->irq) && has_irq != INT_NONE)
dev_warn(&client->dev, "interrupt support not compiled in\n");
return 0;
@@ -562,6 +637,7 @@ static int max732x_setup_gpio(struct max732x_chip *chip,
if (chip->dir_output) {
gc->direction_output = max732x_gpio_direction_output;
gc->set = max732x_gpio_set_value;
+ gc->set_multiple = max732x_gpio_set_multiple;
}
gc->get = max732x_gpio_get_value;
gc->can_sleep = true;
@@ -574,28 +650,47 @@ static int max732x_setup_gpio(struct max732x_chip *chip,
return port;
}
+static struct max732x_platform_data *of_gpio_max732x(struct device *dev)
+{
+ struct max732x_platform_data *pdata;
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return NULL;
+
+ pdata->gpio_base = -1;
+
+ return pdata;
+}
+
static int max732x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct max732x_platform_data *pdata;
+ struct device_node *node;
struct max732x_chip *chip;
struct i2c_client *c;
uint16_t addr_a, addr_b;
int ret, nr_port;
pdata = dev_get_platdata(&client->dev);
- if (pdata == NULL) {
+ node = client->dev.of_node;
+
+ if (!pdata && node)
+ pdata = of_gpio_max732x(&client->dev);
+
+ if (!pdata) {
dev_dbg(&client->dev, "no platform data\n");
return -EINVAL;
}
- chip = devm_kzalloc(&client->dev, sizeof(struct max732x_chip),
- GFP_KERNEL);
+ chip = devm_kzalloc(&client->dev, sizeof(*chip), GFP_KERNEL);
if (chip == NULL)
return -ENOMEM;
chip->client = client;
nr_port = max732x_setup_gpio(chip, id, pdata->gpio_base);
+ chip->gpio_chip.dev = &client->dev;
addr_a = (client->addr & 0x0f) | 0x60;
addr_b = (client->addr & 0x0f) | 0x50;
@@ -643,7 +738,7 @@ static int max732x_probe(struct i2c_client *client,
if (ret)
goto out_failed;
- if (pdata->setup) {
+ if (pdata && pdata->setup) {
ret = pdata->setup(client, chip->gpio_chip.base,
chip->gpio_chip.ngpio, pdata->context);
if (ret < 0)
@@ -664,9 +759,10 @@ static int max732x_remove(struct i2c_client *client)
{
struct max732x_platform_data *pdata = dev_get_platdata(&client->dev);
struct max732x_chip *chip = i2c_get_clientdata(client);
- int ret;
- if (pdata->teardown) {
+ if (pdata && pdata->teardown) {
+ int ret;
+
ret = pdata->teardown(client, chip->gpio_chip.base,
chip->gpio_chip.ngpio, pdata->context);
if (ret < 0) {
@@ -689,8 +785,9 @@ static int max732x_remove(struct i2c_client *client)
static struct i2c_driver max732x_driver = {
.driver = {
- .name = "max732x",
- .owner = THIS_MODULE,
+ .name = "max732x",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(max732x_of_table),
},
.probe = max732x_probe,
.remove = max732x_remove,