From 7b58696d9a8415576317615c63e9899797026f17 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Oct 2020 17:25:28 +0300 Subject: gpiolib: Extract gpiod_not_found() helper Several places in the code are using same idiom, i.e. IS_ERR(desc) && PTR_ERR(desc) == -ENOENT which meaning is GPIO description is not found. For better readability extract gpiod_not_found() helper and use it. Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-devres.c | 8 +++----- drivers/gpio/gpiolib-of.c | 12 ++++++------ drivers/gpio/gpiolib.c | 12 +++++------- drivers/gpio/gpiolib.h | 2 ++ 4 files changed, 16 insertions(+), 18 deletions(-) diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 7dbce4c4ebdf..174f88d5ec17 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -246,10 +246,8 @@ struct gpio_desc *__must_check devm_gpiod_get_index_optional(struct device *dev, struct gpio_desc *desc; desc = devm_gpiod_get_index(dev, con_id, index, flags); - if (IS_ERR(desc)) { - if (PTR_ERR(desc) == -ENOENT) - return NULL; - } + if (gpiod_not_found(desc)) + return NULL; return desc; } @@ -308,7 +306,7 @@ devm_gpiod_get_array_optional(struct device *dev, const char *con_id, struct gpio_descs *descs; descs = devm_gpiod_get_array(dev, con_id, flags); - if (PTR_ERR(descs) == -ENOENT) + if (gpiod_not_found(descs)) return NULL; return descs; diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 2f895a2b8411..9b223520d24b 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -509,31 +509,31 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, desc = of_get_named_gpiod_flags(dev->of_node, prop_name, idx, &of_flags); - if (!IS_ERR(desc) || PTR_ERR(desc) != -ENOENT) + if (!gpiod_not_found(desc)) break; } - if (PTR_ERR(desc) == -ENOENT) { + if (gpiod_not_found(desc)) { /* Special handling for SPI GPIOs if used */ desc = of_find_spi_gpio(dev, con_id, &of_flags); } - if (PTR_ERR(desc) == -ENOENT) { + if (gpiod_not_found(desc)) { /* This quirk looks up flags and all */ desc = of_find_spi_cs_gpio(dev, con_id, idx, flags); if (!IS_ERR(desc)) return desc; } - if (PTR_ERR(desc) == -ENOENT) { + if (gpiod_not_found(desc)) { /* Special handling for regulator GPIOs if used */ desc = of_find_regulator_gpio(dev, con_id, &of_flags); } - if (PTR_ERR(desc) == -ENOENT) + if (gpiod_not_found(desc)) desc = of_find_arizona_gpio(dev, con_id, &of_flags); - if (PTR_ERR(desc) == -ENOENT) + if (gpiod_not_found(desc)) desc = of_find_usb_gpio(dev, con_id, &of_flags); if (IS_ERR(desc)) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 3cdf9effc13a..42fd6f3d6191 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3767,7 +3767,7 @@ struct gpio_desc *fwnode_gpiod_get_index(struct fwnode_handle *fwnode, desc = fwnode_get_named_gpiod(fwnode, prop_name, index, flags, label); - if (!IS_ERR(desc) || (PTR_ERR(desc) != -ENOENT)) + if (!gpiod_not_found(desc)) break; } @@ -3943,7 +3943,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, * Either we are not using DT or ACPI, or their lookup did not return * a result. In that case, use platform lookup as a fallback. */ - if (!desc || desc == ERR_PTR(-ENOENT)) { + if (!desc || gpiod_not_found(desc)) { dev_dbg(dev, "using lookup tables for GPIO lookup\n"); desc = gpiod_find(dev, con_id, idx, &lookupflags); } @@ -4078,10 +4078,8 @@ struct gpio_desc *__must_check gpiod_get_index_optional(struct device *dev, struct gpio_desc *desc; desc = gpiod_get_index(dev, con_id, index, flags); - if (IS_ERR(desc)) { - if (PTR_ERR(desc) == -ENOENT) - return NULL; - } + if (gpiod_not_found(desc)) + return NULL; return desc; } @@ -4283,7 +4281,7 @@ struct gpio_descs *__must_check gpiod_get_array_optional(struct device *dev, struct gpio_descs *descs; descs = gpiod_get_array(dev, con_id, flags); - if (PTR_ERR(descs) == -ENOENT) + if (gpiod_not_found(descs)) return NULL; return descs; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index b674b5bb980e..16bc5731673c 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -130,6 +130,8 @@ struct gpio_desc { #endif }; +#define gpiod_not_found(desc) (IS_ERR(desc) && PTR_ERR(desc) == -ENOENT) + int gpiod_request(struct gpio_desc *desc, const char *label); void gpiod_free(struct gpio_desc *desc); int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, -- cgit v1.2.3 From 3ffb7c45d193c771d7fc7722be0a45bc196f25f9 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Wed, 14 Oct 2020 14:29:21 +0800 Subject: gpiolib: cdev: document that line eflags are shared The line.eflags field is shared so document this fact and highlight it throughout using READ_ONCE() and WRITE_ONCE() accessors. Also use a local copy of the eflags in edge_irq_thread() to ensure consistent control flow even if eflags changes. This is only a defensive measure as edge_irq_thread() is currently disabled when the eflags are changed. Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index e9faeaf65d14..519ecfa40a3b 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -428,6 +428,12 @@ struct line { */ struct linereq *req; unsigned int irq; + /* + * eflags is set by edge_detector_setup(), edge_detector_stop() and + * edge_detector_update(), which are themselves mutually exclusive, + * and is accessed by edge_irq_thread() and debounce_work_func(), + * which can both live with a slightly stale value. + */ u64 eflags; /* * timestamp_ns and req_seqno are accessed only by @@ -534,6 +540,7 @@ static irqreturn_t edge_irq_thread(int irq, void *p) struct line *line = p; struct linereq *lr = line->req; struct gpio_v2_line_event le; + u64 eflags; /* Do not leak kernel stack to userspace */ memset(&le, 0, sizeof(le)); @@ -552,8 +559,9 @@ static irqreturn_t edge_irq_thread(int irq, void *p) } line->timestamp_ns = 0; - if (line->eflags == (GPIO_V2_LINE_FLAG_EDGE_RISING | - GPIO_V2_LINE_FLAG_EDGE_FALLING)) { + eflags = READ_ONCE(line->eflags); + if (eflags == (GPIO_V2_LINE_FLAG_EDGE_RISING | + GPIO_V2_LINE_FLAG_EDGE_FALLING)) { int level = gpiod_get_value_cansleep(line->desc); if (level) @@ -562,10 +570,10 @@ static irqreturn_t edge_irq_thread(int irq, void *p) else /* Emit high-to-low event */ le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - } else if (line->eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) { + } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) { /* Emit low-to-high event */ le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - } else if (line->eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) { + } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) { /* Emit high-to-low event */ le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; } else { @@ -634,6 +642,7 @@ static void debounce_work_func(struct work_struct *work) struct line *line = container_of(work, struct line, work.work); struct linereq *lr; int level; + u64 eflags; level = gpiod_get_raw_value_cansleep(line->desc); if (level < 0) { @@ -647,7 +656,8 @@ static void debounce_work_func(struct work_struct *work) WRITE_ONCE(line->level, level); /* -- edge detection -- */ - if (!line->eflags) + eflags = READ_ONCE(line->eflags); + if (!eflags) return; /* switch from physical level to logical - if they differ */ @@ -655,8 +665,8 @@ static void debounce_work_func(struct work_struct *work) level = !level; /* ignore edges that are not being monitored */ - if (((line->eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) && !level) || - ((line->eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) && level)) + if (((eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) && !level) || + ((eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) && level)) return; /* Do not leak kernel stack to userspace */ @@ -755,7 +765,7 @@ static void edge_detector_stop(struct line *line) cancel_delayed_work_sync(&line->work); WRITE_ONCE(line->sw_debounced, 0); - line->eflags = 0; + WRITE_ONCE(line->eflags, 0); /* do not change line->level - see comment in debounced_value() */ } @@ -774,7 +784,7 @@ static int edge_detector_setup(struct line *line, if (ret) return ret; } - line->eflags = eflags; + WRITE_ONCE(line->eflags, eflags); if (gpio_v2_line_config_debounced(lc, line_idx)) { debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx); ret = debounce_setup(line, debounce_period_us); @@ -817,13 +827,13 @@ static int edge_detector_update(struct line *line, unsigned int debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx); - if ((line->eflags == eflags) && !polarity_change && + if ((READ_ONCE(line->eflags) == eflags) && !polarity_change && (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us)) return 0; /* sw debounced and still will be...*/ if (debounce_period_us && READ_ONCE(line->sw_debounced)) { - line->eflags = eflags; + WRITE_ONCE(line->eflags, eflags); WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); return 0; } -- cgit v1.2.3 From 40941954f6ce1d8b92a37fc35a28f83632deda8b Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 22 Oct 2020 19:58:47 +0300 Subject: gpiolib: of: Use named item for enum gpiod_flags variable Use named item instead of plain integer for enum gpiod_flags to make it clear that even 0 has its own meaning. Cc: Mika Westerberg Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 9b223520d24b..b4a71119a4b0 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -593,7 +593,7 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, xlate_flags = 0; *lflags = GPIO_LOOKUP_FLAGS_DEFAULT; - *dflags = 0; + *dflags = GPIOD_ASIS; ret = of_property_read_u32(chip_np, "#gpio-cells", &tmp); if (ret) -- cgit v1.2.3 From 8bbff39c6c6c86405fe023ccf50eeb44feacbde9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Oct 2020 14:25:36 +0300 Subject: gpiolib: Unify expectations about ->request() returned value Half of the code in the GPIO library is written in an expectation that any non-zero value returned from the ->request() callback is an error code, while some code checks only for negative values. Unify expectations about ->request() returned value to be non-zero for an error and 0 for the success. Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-sysfs.c | 2 +- drivers/gpio/gpiolib.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 728f6c687182..26c5466b8179 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -476,7 +476,7 @@ static ssize_t export_store(struct class *class, */ status = gpiod_request(desc, "sysfs"); - if (status < 0) { + if (status) { if (status == -EPROBE_DEFER) status = -ENODEV; goto done; diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 42fd6f3d6191..20e3eb74b5cb 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1985,7 +1985,7 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) ret = -EINVAL; spin_lock_irqsave(&gpio_lock, flags); - if (ret < 0) { + if (ret) { desc_set_label(desc, NULL); kfree_const(label); clear_bit(FLAG_REQUESTED, &desc->flags); @@ -2051,7 +2051,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label) if (try_module_get(gdev->owner)) { ret = gpiod_request_commit(desc, label); - if (ret < 0) + if (ret) module_put(gdev->owner); else get_device(&gdev->dev); @@ -3958,7 +3958,7 @@ struct gpio_desc *__must_check gpiod_get_index(struct device *dev, * the device name as label */ ret = gpiod_request(desc, con_id ? con_id : devname); - if (ret < 0) { + if (ret) { if (ret == -EBUSY && flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) { /* * This happens when there are several consumers for -- cgit v1.2.3 From 95d9f84fca1ef1bbbc2be6313e29181c7f0de99f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 21 Oct 2020 14:25:37 +0300 Subject: gpiolib: split error path in gpiod_request_commit() For better maintenance and micro optimization split error path in the gpiod_request_commit(). Signed-off-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 20e3eb74b5cb..a72d6293435f 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1968,11 +1968,9 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { desc_set_label(desc, label ? : "?"); - ret = 0; } else { - kfree_const(label); ret = -EBUSY; - goto done; + goto out_free_unlock; } if (gc->request) { @@ -1987,9 +1985,8 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) if (ret) { desc_set_label(desc, NULL); - kfree_const(label); clear_bit(FLAG_REQUESTED, &desc->flags); - goto done; + goto out_free_unlock; } } if (gc->get_direction) { @@ -1998,8 +1995,12 @@ static int gpiod_request_commit(struct gpio_desc *desc, const char *label) gpiod_get_direction(desc); spin_lock_irqsave(&gpio_lock, flags); } -done: spin_unlock_irqrestore(&gpio_lock, flags); + return 0; + +out_free_unlock: + spin_unlock_irqrestore(&gpio_lock, flags); + kfree_const(label); return ret; } -- cgit v1.2.3 From 11b3de087a1cae288f2cf345457bc7a7f97c7aa3 Mon Sep 17 00:00:00 2001 From: Mike Looijmans Date: Mon, 26 Oct 2020 17:32:43 +0100 Subject: dt-bindings: gpio: pca953x: Add support for the NXP PCAL9554B/C The NXP PCAL9554B is a variant of the PCA953x GPIO expander, with 8 GPIOs, latched interrupts and some advanced configuration options. The "C" version only differs in I2C address. This adds the entry to the devicetree bindings. Signed-off-by: Mike Looijmans Acked-by: Rob Herring Reviewed-by: Linus Walleij Signed-off-by: Bartosz Golaszewski --- Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml index 183ec23eda39..f5ee23c2df60 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml @@ -48,6 +48,7 @@ properties: - nxp,pcal6416 - nxp,pcal6524 - nxp,pcal9535 + - nxp,pcal9554b - nxp,pcal9555a - onnn,cat9554 - onnn,pca9654 -- cgit v1.2.3 From 9ef6293c0659de3fa7981e795e70786c89610374 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Fri, 9 Oct 2020 21:43:59 +0300 Subject: gpiolib: Use proper type for bias enumerator in gpio_set_bias() First of all, bias has a special type as being a part of enum pin_config_param. Second, 0 is also defined bias which is equivalent to BUS_HOLD. Taking into account above, change type of bias variable and refactor gpio_set_bias() in a way that it doesn't use BUS_HOLD as a place holder. Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20201009184359.16427-1-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 3cdf9effc13a..3b23a0ca77dd 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2256,8 +2256,8 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) static int gpio_set_bias(struct gpio_desc *desc) { - int bias = 0; - int ret = 0; + enum pin_config_param bias; + int ret; if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) bias = PIN_CONFIG_BIAS_DISABLE; @@ -2265,12 +2265,13 @@ static int gpio_set_bias(struct gpio_desc *desc) bias = PIN_CONFIG_BIAS_PULL_UP; else if (test_bit(FLAG_PULL_DOWN, &desc->flags)) bias = PIN_CONFIG_BIAS_PULL_DOWN; + else + return 0; + + ret = gpio_set_config(desc, bias); + if (ret != -ENOTSUPP) + return ret; - if (bias) { - ret = gpio_set_config(desc, bias); - if (ret != -ENOTSUPP) - return ret; - } return 0; } -- cgit v1.2.3 From 163d1719d30f137c5118b8cbcd8db73b0a580793 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 14 Oct 2020 13:33:15 +0300 Subject: gpiolib: Switch to use compat_need_64bit_alignment_fixup() helper Use the new compat_need_64bit_alignment_fixup() helper to avoid ugly ifdeffery in IOCTL compatibility code. Signed-off-by: Andy Shevchenko Tested-by: Kent Gibson Depends-on: 527c412519eb ("compat: add a compat_need_64bit_alignment_fixup() helper") Link: https://lore.kernel.org/r/20201014103315.82662-1-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-cdev.c | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index e9faeaf65d14..192721f829a3 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -1479,21 +1479,10 @@ static __poll_t lineevent_poll(struct file *file, return events; } -static ssize_t lineevent_get_size(void) -{ -#if defined(CONFIG_X86_64) && !defined(CONFIG_UML) - /* i386 has no padding after 'id' */ - if (in_ia32_syscall()) { - struct compat_gpioeevent_data { - compat_u64 timestamp; - u32 id; - }; - - return sizeof(struct compat_gpioeevent_data); - } -#endif - return sizeof(struct gpioevent_data); -} +struct compat_gpioeevent_data { + compat_u64 timestamp; + u32 id; +}; static ssize_t lineevent_read(struct file *file, char __user *buf, @@ -1515,7 +1504,10 @@ static ssize_t lineevent_read(struct file *file, * actual sizeof() and pass this as an argument to copy_to_user() to * drop unneeded bytes from the output. */ - ge_size = lineevent_get_size(); + if (compat_need_64bit_alignment_fixup()) + ge_size = sizeof(struct compat_gpioeevent_data); + else + ge_size = sizeof(struct gpioevent_data); if (count < ge_size) return -EINVAL; -- cgit v1.2.3 From f1f37abbe6fc2b1242f78157db76e48dbf9518ee Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 19 Oct 2020 15:40:46 +0200 Subject: gpio: Retire the explicit gpio irqchip code Now that all gpiolib irqchip users have been over to use the irqchip template, we can finally retire the old code path and leave just one way in to the irqchip: set up the template when registering the gpio_chip. For a while we had two code paths for this which was a bit confusing. This brings this work to a conclusion, there is now one way of doing this. Signed-off-by: Linus Walleij Reviewed-by: Andy Shevchenko Cc: Thierry Reding Link: https://lore.kernel.org/r/20201019134046.65101-1-linus.walleij@linaro.org --- Documentation/driver-api/gpio/driver.rst | 63 ++++++++----- drivers/gpio/TODO | 49 ---------- drivers/gpio/gpiolib.c | 153 ------------------------------- include/linux/gpio/driver.h | 71 -------------- 4 files changed, 42 insertions(+), 294 deletions(-) diff --git a/Documentation/driver-api/gpio/driver.rst b/Documentation/driver-api/gpio/driver.rst index 072a7455044e..65d708093b71 100644 --- a/Documentation/driver-api/gpio/driver.rst +++ b/Documentation/driver-api/gpio/driver.rst @@ -416,7 +416,8 @@ The preferred way to set up the helpers is to fill in the struct gpio_irq_chip inside struct gpio_chip before adding the gpio_chip. If you do this, the additional irq_chip will be set up by gpiolib at the same time as setting up the rest of the GPIO functionality. The following -is a typical example of a cascaded interrupt handler using gpio_irq_chip: +is a typical example of a chained cascaded interrupt handler using +the gpio_irq_chip: .. code-block:: c @@ -452,7 +453,46 @@ is a typical example of a cascaded interrupt handler using gpio_irq_chip: return devm_gpiochip_add_data(dev, &g->gc, g); -The helper support using hierarchical interrupt controllers as well. +The helper supports using threaded interrupts as well. Then you just request +the interrupt separately and go with it: + +.. code-block:: c + + /* Typical state container with dynamic irqchip */ + struct my_gpio { + struct gpio_chip gc; + struct irq_chip irq; + }; + + int irq; /* from platform etc */ + struct my_gpio *g; + struct gpio_irq_chip *girq; + + /* Set up the irqchip dynamically */ + g->irq.name = "my_gpio_irq"; + g->irq.irq_ack = my_gpio_ack_irq; + g->irq.irq_mask = my_gpio_mask_irq; + g->irq.irq_unmask = my_gpio_unmask_irq; + g->irq.irq_set_type = my_gpio_set_irq_type; + + ret = devm_request_threaded_irq(dev, irq, NULL, + irq_thread_fn, IRQF_ONESHOT, "my-chip", g); + if (ret < 0) + return ret; + + /* Get a pointer to the gpio_irq_chip */ + girq = &g->gc.irq; + girq->chip = &g->irq; + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_bad_irq; + + return devm_gpiochip_add_data(dev, &g->gc, g); + +The helper supports using hierarchical interrupt controllers as well. In this case the typical set-up will look like this: .. code-block:: c @@ -493,25 +533,6 @@ the parent hardware irq from a child (i.e. this gpio chip) hardware irq. As always it is good to look at examples in the kernel tree for advice on how to find the required pieces. -The old way of adding irqchips to gpiochips after registration is also still -available but we try to move away from this: - -- DEPRECATED: gpiochip_irqchip_add(): adds a chained cascaded irqchip to a - gpiochip. It will pass the struct gpio_chip* for the chip to all IRQ - callbacks, so the callbacks need to embed the gpio_chip in its state - container and obtain a pointer to the container using container_of(). - (See Documentation/driver-api/driver-model/design-patterns.rst) - -- gpiochip_irqchip_add_nested(): adds a nested cascaded irqchip to a gpiochip, - as discussed above regarding different types of cascaded irqchips. The - cascaded irq has to be handled by a threaded interrupt handler. - Apart from that it works exactly like the chained irqchip. - -- gpiochip_set_nested_irqchip(): sets up a nested cascaded irq handler for a - gpio_chip from a parent IRQ. As the parent IRQ has usually been - explicitly requested by the driver, this does very little more than - mark all the child IRQs as having the other IRQ as parent. - If there is a need to exclude certain GPIO lines from the IRQ domain handled by these helpers, we can set .irq.need_valid_mask of the gpiochip before devm_gpiochip_add_data() or gpiochip_add_data() is called. This allocates an diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index e560e45e84f8..cd04e0b60159 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -129,58 +129,9 @@ GPIOLIB irqchip The GPIOLIB irqchip is a helper irqchip for "simple cases" that should try to cover any generic kind of irqchip cascaded from a GPIO. -- Convert all the GPIOLIB_IRQCHIP users to pass an irqchip template, - parent and flags before calling [devm_]gpiochip_add[_data](). - Currently we set up the irqchip after setting up the gpiochip - using gpiochip_irqchip_add() and gpiochip_set_[chained|nested]_irqchip(). - This is too complex, so convert all users over to just set up - the irqchip before registering the gpio_chip, typical example: - - /* Typical state container with dynamic irqchip */ - struct my_gpio { - struct gpio_chip gc; - struct irq_chip irq; - }; - - int irq; /* from platform etc */ - struct my_gpio *g; - struct gpio_irq_chip *girq; - - /* Set up the irqchip dynamically */ - g->irq.name = "my_gpio_irq"; - g->irq.irq_ack = my_gpio_ack_irq; - g->irq.irq_mask = my_gpio_mask_irq; - g->irq.irq_unmask = my_gpio_unmask_irq; - g->irq.irq_set_type = my_gpio_set_irq_type; - - /* Get a pointer to the gpio_irq_chip */ - girq = &g->gc.irq; - girq->chip = &g->irq; - girq->parent_handler = ftgpio_gpio_irq_handler; - girq->num_parents = 1; - girq->parents = devm_kcalloc(dev, 1, sizeof(*girq->parents), - GFP_KERNEL); - if (!girq->parents) - return -ENOMEM; - girq->default_type = IRQ_TYPE_NONE; - girq->handler = handle_bad_irq; - girq->parents[0] = irq; - - When this is done, we will delete the old APIs for instatiating - GPIOLIB_IRQCHIP and simplify the code. - - Look over and identify any remaining easily converted drivers and dry-code conversions to gpiolib irqchip for maintainers to test -- Drop gpiochip_set_chained_irqchip() when all the chained irqchips - have been converted to the above infrastructure. - -- Add more infrastructure to make it possible to also pass a threaded - irqchip in struct gpio_irq_chip. - -- Drop gpiochip_irqchip_add_nested() when all the chained irqchips - have been converted to the above infrastructure. - Increase integration with pin control diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 3b23a0ca77dd..8e29a60c3697 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -924,67 +924,6 @@ bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, } EXPORT_SYMBOL_GPL(gpiochip_irqchip_irq_valid); -/** - * gpiochip_set_cascaded_irqchip() - connects a cascaded irqchip to a gpiochip - * @gc: the gpiochip to set the irqchip chain to - * @parent_irq: the irq number corresponding to the parent IRQ for this - * cascaded irqchip - * @parent_handler: the parent interrupt handler for the accumulated IRQ - * coming out of the gpiochip. If the interrupt is nested rather than - * cascaded, pass NULL in this handler argument - */ -static void gpiochip_set_cascaded_irqchip(struct gpio_chip *gc, - unsigned int parent_irq, - irq_flow_handler_t parent_handler) -{ - struct gpio_irq_chip *girq = &gc->irq; - struct device *dev = &gc->gpiodev->dev; - - if (!girq->domain) { - chip_err(gc, "called %s before setting up irqchip\n", - __func__); - return; - } - - if (parent_handler) { - if (gc->can_sleep) { - chip_err(gc, - "you cannot have chained interrupts on a chip that may sleep\n"); - return; - } - girq->parents = devm_kcalloc(dev, 1, - sizeof(*girq->parents), - GFP_KERNEL); - if (!girq->parents) { - chip_err(gc, "out of memory allocating parent IRQ\n"); - return; - } - girq->parents[0] = parent_irq; - girq->num_parents = 1; - /* - * The parent irqchip is already using the chip_data for this - * irqchip, so our callbacks simply use the handler_data. - */ - irq_set_chained_handler_and_data(parent_irq, parent_handler, - gc); - } -} - -/** - * gpiochip_set_nested_irqchip() - connects a nested irqchip to a gpiochip - * @gc: the gpiochip to set the irqchip nested handler to - * @irqchip: the irqchip to nest to the gpiochip - * @parent_irq: the irq number corresponding to the parent IRQ for this - * nested irqchip - */ -void gpiochip_set_nested_irqchip(struct gpio_chip *gc, - struct irq_chip *irqchip, - unsigned int parent_irq) -{ - gpiochip_set_cascaded_irqchip(gc, parent_irq, NULL); -} -EXPORT_SYMBOL_GPL(gpiochip_set_nested_irqchip); - #ifdef CONFIG_IRQ_DOMAIN_HIERARCHY /** @@ -1635,98 +1574,6 @@ static void gpiochip_irqchip_remove(struct gpio_chip *gc) gpiochip_irqchip_free_valid_mask(gc); } -/** - * gpiochip_irqchip_add_key() - adds an irqchip to a gpiochip - * @gc: the gpiochip to add the irqchip to - * @irqchip: the irqchip to add to the gpiochip - * @first_irq: if not dynamically assigned, the base (first) IRQ to - * allocate gpiochip irqs from - * @handler: the irq handler to use (often a predefined irq core function) - * @type: the default type for IRQs on this irqchip, pass IRQ_TYPE_NONE - * to have the core avoid setting up any default type in the hardware. - * @threaded: whether this irqchip uses a nested thread handler - * @lock_key: lockdep class for IRQ lock - * @request_key: lockdep class for IRQ request - * - * This function closely associates a certain irqchip with a certain - * gpiochip, providing an irq domain to translate the local IRQs to - * global irqs in the gpiolib core, and making sure that the gpiochip - * is passed as chip data to all related functions. Driver callbacks - * need to use gpiochip_get_data() to get their local state containers back - * from the gpiochip passed as chip data. An irqdomain will be stored - * in the gpiochip that shall be used by the driver to handle IRQ number - * translation. The gpiochip will need to be initialized and registered - * before calling this function. - * - * This function will handle two cell:ed simple IRQs and assumes all - * the pins on the gpiochip can generate a unique IRQ. Everything else - * need to be open coded. - */ -int gpiochip_irqchip_add_key(struct gpio_chip *gc, - struct irq_chip *irqchip, - unsigned int first_irq, - irq_flow_handler_t handler, - unsigned int type, - bool threaded, - struct lock_class_key *lock_key, - struct lock_class_key *request_key) -{ - struct device_node *of_node; - - if (!gc || !irqchip) - return -EINVAL; - - if (!gc->parent) { - chip_err(gc, "missing gpiochip .dev parent pointer\n"); - return -EINVAL; - } - gc->irq.threaded = threaded; - of_node = gc->parent->of_node; -#ifdef CONFIG_OF_GPIO - /* - * If the gpiochip has an assigned OF node this takes precedence - * FIXME: get rid of this and use gc->parent->of_node - * everywhere - */ - if (gc->of_node) - of_node = gc->of_node; -#endif - /* - * Specifying a default trigger is a terrible idea if DT or ACPI is - * used to configure the interrupts, as you may end-up with - * conflicting triggers. Tell the user, and reset to NONE. - */ - if (WARN(of_node && type != IRQ_TYPE_NONE, - "%pOF: Ignoring %d default trigger\n", of_node, type)) - type = IRQ_TYPE_NONE; - if (has_acpi_companion(gc->parent) && type != IRQ_TYPE_NONE) { - acpi_handle_warn(ACPI_HANDLE(gc->parent), - "Ignoring %d default trigger\n", type); - type = IRQ_TYPE_NONE; - } - - gc->irq.chip = irqchip; - gc->irq.handler = handler; - gc->irq.default_type = type; - gc->to_irq = gpiochip_to_irq; - gc->irq.lock_key = lock_key; - gc->irq.request_key = request_key; - gc->irq.domain = irq_domain_add_simple(of_node, - gc->ngpio, first_irq, - &gpiochip_domain_ops, gc); - if (!gc->irq.domain) { - gc->irq.chip = NULL; - return -EINVAL; - } - - gpiochip_set_irq_hooks(gc); - - acpi_gpiochip_request_interrupts(gc); - - return 0; -} -EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_key); - /** * gpiochip_irqchip_add_domain() - adds an irqdomain to a gpiochip * @gc: the gpiochip to add the irqchip to diff --git a/include/linux/gpio/driver.h b/include/linux/gpio/driver.h index 4a7e295c3640..286de0520574 100644 --- a/include/linux/gpio/driver.h +++ b/include/linux/gpio/driver.h @@ -621,83 +621,12 @@ int gpiochip_irq_domain_activate(struct irq_domain *domain, void gpiochip_irq_domain_deactivate(struct irq_domain *domain, struct irq_data *data); -void gpiochip_set_nested_irqchip(struct gpio_chip *gc, - struct irq_chip *irqchip, - unsigned int parent_irq); - -int gpiochip_irqchip_add_key(struct gpio_chip *gc, - struct irq_chip *irqchip, - unsigned int first_irq, - irq_flow_handler_t handler, - unsigned int type, - bool threaded, - struct lock_class_key *lock_key, - struct lock_class_key *request_key); - bool gpiochip_irqchip_irq_valid(const struct gpio_chip *gc, unsigned int offset); int gpiochip_irqchip_add_domain(struct gpio_chip *gc, struct irq_domain *domain); -#ifdef CONFIG_LOCKDEP - -/* - * Lockdep requires that each irqchip instance be created with a - * unique key so as to avoid unnecessary warnings. This upfront - * boilerplate static inlines provides such a key for each - * unique instance. - */ -static inline int gpiochip_irqchip_add(struct gpio_chip *gc, - struct irq_chip *irqchip, - unsigned int first_irq, - irq_flow_handler_t handler, - unsigned int type) -{ - static struct lock_class_key lock_key; - static struct lock_class_key request_key; - - return gpiochip_irqchip_add_key(gc, irqchip, first_irq, - handler, type, false, - &lock_key, &request_key); -} - -static inline int gpiochip_irqchip_add_nested(struct gpio_chip *gc, - struct irq_chip *irqchip, - unsigned int first_irq, - irq_flow_handler_t handler, - unsigned int type) -{ - - static struct lock_class_key lock_key; - static struct lock_class_key request_key; - - return gpiochip_irqchip_add_key(gc, irqchip, first_irq, - handler, type, true, - &lock_key, &request_key); -} -#else /* ! CONFIG_LOCKDEP */ -static inline int gpiochip_irqchip_add(struct gpio_chip *gc, - struct irq_chip *irqchip, - unsigned int first_irq, - irq_flow_handler_t handler, - unsigned int type) -{ - return gpiochip_irqchip_add_key(gc, irqchip, first_irq, - handler, type, false, NULL, NULL); -} - -static inline int gpiochip_irqchip_add_nested(struct gpio_chip *gc, - struct irq_chip *irqchip, - unsigned int first_irq, - irq_flow_handler_t handler, - unsigned int type) -{ - return gpiochip_irqchip_add_key(gc, irqchip, first_irq, - handler, type, true, NULL, NULL); -} -#endif /* CONFIG_LOCKDEP */ - int gpiochip_generic_request(struct gpio_chip *gc, unsigned int offset); void gpiochip_generic_free(struct gpio_chip *gc, unsigned int offset); int gpiochip_generic_config(struct gpio_chip *gc, unsigned int offset, -- cgit v1.2.3 From 8aa16335050663357281fb1f1b0483ab91b4d8de Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 19 Oct 2020 15:44:29 +0200 Subject: gpio: stmpe: Fix forgotten refactoring We actually handle the gpio_irq_chip set-up properly now despite what the comment says. Also assign this pointer along with the rest of the gpio_irq_chip setup code. Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20201019134429.65563-1-linus.walleij@linaro.org --- Documentation/driver-api/gpio/driver.rst | 4 ++-- drivers/gpio/gpio-stmpe.c | 10 +--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/Documentation/driver-api/gpio/driver.rst b/Documentation/driver-api/gpio/driver.rst index 65d708093b71..0fb57e298b41 100644 --- a/Documentation/driver-api/gpio/driver.rst +++ b/Documentation/driver-api/gpio/driver.rst @@ -538,8 +538,8 @@ these helpers, we can set .irq.need_valid_mask of the gpiochip before devm_gpiochip_add_data() or gpiochip_add_data() is called. This allocates an .irq.valid_mask with as many bits set as there are GPIO lines in the chip, each bit representing line 0..n-1. Drivers can exclude GPIO lines by clearing bits -from this mask. The mask must be filled in before gpiochip_irqchip_add() or -gpiochip_irqchip_add_nested() is called. +from this mask. The mask can be filled in the init_valid_mask() callback +that is part of the struct gpio_irq_chip. To use the helpers please keep the following in mind: diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index b0155d6007c8..b94ef8181428 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -474,15 +474,6 @@ static int stmpe_gpio_probe(struct platform_device *pdev) stmpe_gpio->chip.parent = &pdev->dev; stmpe_gpio->chip.of_node = np; stmpe_gpio->chip.base = -1; - /* - * REVISIT: this makes sure the valid mask gets allocated and - * filled in when adding the gpio_chip, but the rest of the - * gpio_irqchip is still filled in using the old method - * in gpiochip_irqchip_add_nested() so clean this up once we - * get the gpio_irqchip to initialize while adding the - * gpio_chip also for threaded irqchips. - */ - stmpe_gpio->chip.irq.init_valid_mask = stmpe_init_irq_valid_mask; if (IS_ENABLED(CONFIG_DEBUG_FS)) stmpe_gpio->chip.dbg_show = stmpe_dbg_show; @@ -520,6 +511,7 @@ static int stmpe_gpio_probe(struct platform_device *pdev) girq->default_type = IRQ_TYPE_NONE; girq->handler = handle_simple_irq; girq->threaded = true; + girq->init_valid_mask = stmpe_init_irq_valid_mask; } ret = gpiochip_add_data(&stmpe_gpio->chip, stmpe_gpio); -- cgit v1.2.3 From 43ddebdd096638ef8cd2ec41d6acca3400069171 Mon Sep 17 00:00:00 2001 From: Vincent Whitchurch Date: Thu, 29 Oct 2020 09:17:20 +0100 Subject: gpio: mockup: Allow probing from device tree Allow the mockup driver to be probed via the device tree without any module parameters, allowing it to be used to configure and test higher level drivers like the leds-gpio driver and corresponding userspace before actual hardware is available. Signed-off-by: Vincent Whitchurch Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mockup.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c index 67ed4f238d43..28b757d34046 100644 --- a/drivers/gpio/gpio-mockup.c +++ b/drivers/gpio/gpio-mockup.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -460,9 +461,16 @@ static int gpio_mockup_probe(struct platform_device *pdev) return 0; } +static const struct of_device_id gpio_mockup_of_match[] = { + { .compatible = "gpio-mockup", }, + {}, +}; +MODULE_DEVICE_TABLE(of, gpio_mockup_of_match); + static struct platform_driver gpio_mockup_driver = { .driver = { .name = "gpio-mockup", + .of_match_table = gpio_mockup_of_match, }, .probe = gpio_mockup_probe, }; @@ -556,8 +564,7 @@ static int __init gpio_mockup_init(void) { int i, num_chips, err; - if ((gpio_mockup_num_ranges < 2) || - (gpio_mockup_num_ranges % 2) || + if ((gpio_mockup_num_ranges % 2) || (gpio_mockup_num_ranges > GPIO_MOCKUP_MAX_RANGES)) return -EINVAL; -- cgit v1.2.3 From b9bf97105f4b9adc32604d24072147b242564fb3 Mon Sep 17 00:00:00 2001 From: Deepak R Varma Date: Wed, 14 Oct 2020 00:32:12 +0530 Subject: gpio: 104-idi-48: improve code indentation Address code indentation warning messages by checkpatch script. Combine split function parameters on one line. This also resolves the "use tabs instead of space" warning by checkpatch script. Signed-off-by: Deepak R Varma Link: https://lore.kernel.org/r/20201013190212.GA85788@ubuntu204 Signed-off-by: Linus Walleij --- drivers/gpio/gpio-104-idi-48.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index 94c3a9bc4e75..b132afaf7d99 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -132,8 +132,7 @@ static void idi_48_irq_mask(struct irq_data *data) outb(idi48gpio->cos_enb, idi48gpio->base + 7); - raw_spin_unlock_irqrestore(&idi48gpio->lock, - flags); + raw_spin_unlock_irqrestore(&idi48gpio->lock, flags); } return; @@ -166,8 +165,7 @@ static void idi_48_irq_unmask(struct irq_data *data) outb(idi48gpio->cos_enb, idi48gpio->base + 7); - raw_spin_unlock_irqrestore(&idi48gpio->lock, - flags); + raw_spin_unlock_irqrestore(&idi48gpio->lock, flags); } return; -- cgit v1.2.3 From 5e2ca893d772560a0926a31b88f8c83efbc6b058 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Thu, 29 Oct 2020 16:48:32 +0800 Subject: gpiolib: cdev: add GPIO_V2_LINE_FLAG_EDGE_BOTH and use it in edge_irq_thread() Add GPIO_V2_LINE_FLAG_EDGE_BOTH macro and use it in edge_irq_thread() to improve readability of edge handling cases. Suggested-by: Andy Shevchenko Signed-off-by: Kent Gibson Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-cdev.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 519ecfa40a3b..ab9a49693cae 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -510,6 +510,8 @@ struct linereq { (GPIO_V2_LINE_FLAG_EDGE_RISING | \ GPIO_V2_LINE_FLAG_EDGE_FALLING) +#define GPIO_V2_LINE_FLAG_EDGE_BOTH GPIO_V2_LINE_EDGE_FLAGS + #define GPIO_V2_LINE_VALID_FLAGS \ (GPIO_V2_LINE_FLAG_ACTIVE_LOW | \ GPIO_V2_LINE_DIRECTION_FLAGS | \ @@ -560,8 +562,7 @@ static irqreturn_t edge_irq_thread(int irq, void *p) line->timestamp_ns = 0; eflags = READ_ONCE(line->eflags); - if (eflags == (GPIO_V2_LINE_FLAG_EDGE_RISING | - GPIO_V2_LINE_FLAG_EDGE_FALLING)) { + if (eflags == GPIO_V2_LINE_FLAG_EDGE_BOTH) { int level = gpiod_get_value_cansleep(line->desc); if (level) -- cgit v1.2.3 From 714d3a295854c14295fc633be1abbb947d2059a1 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 28 Oct 2020 15:15:01 +0100 Subject: gpio: rcar: Cache gpiochip_get_data() return value Since commit 43c54ecade400cf6 ("gpio: move the subdriver data pointer into gpio_device") changed gpiochip_get_data() to an out-of-line function, it is now worthwhile to avoid multiple calls in a row by caching its return value in a local variable. Signed-off-by: Geert Uytterhoeven Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rcar.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index 3ef19cef8da9..a75bbc9af1f1 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -295,14 +295,15 @@ static int gpio_rcar_direction_input(struct gpio_chip *chip, unsigned offset) static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset) { + struct gpio_rcar_priv *p = gpiochip_get_data(chip); u32 bit = BIT(offset); /* testing on r8a7790 shows that INDT does not show correct pin state * when configured as output, so use OUTDT in case of output pins */ - if (gpio_rcar_read(gpiochip_get_data(chip), INOUTSEL) & bit) - return !!(gpio_rcar_read(gpiochip_get_data(chip), OUTDT) & bit); + if (gpio_rcar_read(p, INOUTSEL) & bit) + return !!(gpio_rcar_read(p, OUTDT) & bit); else - return !!(gpio_rcar_read(gpiochip_get_data(chip), INDT) & bit); + return !!(gpio_rcar_read(p, INDT) & bit); } static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) -- cgit v1.2.3 From 677d7d613a61de948056cc8b3e4b881df5cd795c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 28 Oct 2020 15:15:02 +0100 Subject: gpio: rcar: Align register offsets Improve readability by aligning the offsets in the register definitions. Signed-off-by: Geert Uytterhoeven Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rcar.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index a75bbc9af1f1..a7fb0ec78e44 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -45,19 +45,19 @@ struct gpio_rcar_priv { struct gpio_rcar_bank_info bank_info; }; -#define IOINTSEL 0x00 /* General IO/Interrupt Switching Register */ -#define INOUTSEL 0x04 /* General Input/Output Switching Register */ -#define OUTDT 0x08 /* General Output Register */ -#define INDT 0x0c /* General Input Register */ -#define INTDT 0x10 /* Interrupt Display Register */ -#define INTCLR 0x14 /* Interrupt Clear Register */ -#define INTMSK 0x18 /* Interrupt Mask Register */ -#define MSKCLR 0x1c /* Interrupt Mask Clear Register */ -#define POSNEG 0x20 /* Positive/Negative Logic Select Register */ -#define EDGLEVEL 0x24 /* Edge/level Select Register */ -#define FILONOFF 0x28 /* Chattering Prevention On/Off Register */ -#define OUTDTSEL 0x40 /* Output Data Select Register */ -#define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */ +#define IOINTSEL 0x00 /* General IO/Interrupt Switching Register */ +#define INOUTSEL 0x04 /* General Input/Output Switching Register */ +#define OUTDT 0x08 /* General Output Register */ +#define INDT 0x0c /* General Input Register */ +#define INTDT 0x10 /* Interrupt Display Register */ +#define INTCLR 0x14 /* Interrupt Clear Register */ +#define INTMSK 0x18 /* Interrupt Mask Register */ +#define MSKCLR 0x1c /* Interrupt Mask Clear Register */ +#define POSNEG 0x20 /* Positive/Negative Logic Select Register */ +#define EDGLEVEL 0x24 /* Edge/level Select Register */ +#define FILONOFF 0x28 /* Chattering Prevention On/Off Register */ +#define OUTDTSEL 0x40 /* Output Data Select Register */ +#define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */ #define RCAR_MAX_GPIO_PER_BANK 32 -- cgit v1.2.3 From 208c80f14b5935edb9f3881c489dcc83742d8916 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 28 Oct 2020 15:15:03 +0100 Subject: gpio: rcar: Rework hardware features handling Reuse gpio_rcar_info inside gpio_rcar_priv instead of duplicating the individual members, so gpio_rcar_parse_dt() can copy them in one go. Signed-off-by: Geert Uytterhoeven Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rcar.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index a7fb0ec78e44..b33d1a2076ea 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -32,6 +32,11 @@ struct gpio_rcar_bank_info { u32 intmsk; }; +struct gpio_rcar_info { + bool has_outdtsel; + bool has_both_edge_trigger; +}; + struct gpio_rcar_priv { void __iomem *base; spinlock_t lock; @@ -40,8 +45,7 @@ struct gpio_rcar_priv { struct irq_chip irq_chip; unsigned int irq_parent; atomic_t wakeup_path; - bool has_outdtsel; - bool has_both_edge_trigger; + struct gpio_rcar_info info; struct gpio_rcar_bank_info bank_info; }; @@ -123,7 +127,7 @@ static void gpio_rcar_config_interrupt_input_mode(struct gpio_rcar_priv *p, gpio_rcar_modify_bit(p, EDGLEVEL, hwirq, !level_trigger); /* Select one edge or both edges in BOTHEDGE */ - if (p->has_both_edge_trigger) + if (p->info.has_both_edge_trigger) gpio_rcar_modify_bit(p, BOTHEDGE, hwirq, both); /* Select "Interrupt Input Mode" in IOINTSEL */ @@ -162,7 +166,7 @@ static int gpio_rcar_irq_set_type(struct irq_data *d, unsigned int type) false); break; case IRQ_TYPE_EDGE_BOTH: - if (!p->has_both_edge_trigger) + if (!p->info.has_both_edge_trigger) return -EINVAL; gpio_rcar_config_interrupt_input_mode(p, hwirq, true, false, true); @@ -238,7 +242,7 @@ static void gpio_rcar_config_general_input_output_mode(struct gpio_chip *chip, gpio_rcar_modify_bit(p, INOUTSEL, gpio, output); /* Select General Output Register to output data in OUTDTSEL */ - if (p->has_outdtsel && output) + if (p->info.has_outdtsel && output) gpio_rcar_modify_bit(p, OUTDTSEL, gpio, false); spin_unlock_irqrestore(&p->lock, flags); @@ -347,11 +351,6 @@ static int gpio_rcar_direction_output(struct gpio_chip *chip, unsigned offset, return 0; } -struct gpio_rcar_info { - bool has_outdtsel; - bool has_both_edge_trigger; -}; - static const struct gpio_rcar_info gpio_rcar_info_gen1 = { .has_outdtsel = false, .has_both_edge_trigger = false, @@ -418,8 +417,7 @@ static int gpio_rcar_parse_dt(struct gpio_rcar_priv *p, unsigned int *npins) int ret; info = of_device_get_match_data(p->dev); - p->has_outdtsel = info->has_outdtsel; - p->has_both_edge_trigger = info->has_both_edge_trigger; + p->info = *info; ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args); *npins = ret == 0 ? args.args[2] : RCAR_MAX_GPIO_PER_BANK; @@ -553,7 +551,7 @@ static int gpio_rcar_suspend(struct device *dev) p->bank_info.intmsk = gpio_rcar_read(p, INTMSK); p->bank_info.posneg = gpio_rcar_read(p, POSNEG); p->bank_info.edglevel = gpio_rcar_read(p, EDGLEVEL); - if (p->has_both_edge_trigger) + if (p->info.has_both_edge_trigger) p->bank_info.bothedge = gpio_rcar_read(p, BOTHEDGE); if (atomic_read(&p->wakeup_path)) -- cgit v1.2.3 From 183245c4f204bc1458163388c8de8ddfc032a607 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 28 Oct 2020 15:15:04 +0100 Subject: gpio: rcar: Implement gpio_chip.get_multiple() Add support for getting the state of multiple pins using a minimum of register reads. Signed-off-by: Geert Uytterhoeven Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-rcar.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c index b33d1a2076ea..0b572dbc4a36 100644 --- a/drivers/gpio/gpio-rcar.c +++ b/drivers/gpio/gpio-rcar.c @@ -310,6 +310,35 @@ static int gpio_rcar_get(struct gpio_chip *chip, unsigned offset) return !!(gpio_rcar_read(p, INDT) & bit); } +static int gpio_rcar_get_multiple(struct gpio_chip *chip, unsigned long *mask, + unsigned long *bits) +{ + struct gpio_rcar_priv *p = gpiochip_get_data(chip); + u32 bankmask, outputs, m, val = 0; + unsigned long flags; + + bankmask = mask[0] & GENMASK(chip->ngpio - 1, 0); + if (chip->valid_mask) + bankmask &= chip->valid_mask[0]; + + if (!bankmask) + return 0; + + spin_lock_irqsave(&p->lock, flags); + outputs = gpio_rcar_read(p, INOUTSEL); + m = outputs & bankmask; + if (m) + val |= gpio_rcar_read(p, OUTDT) & m; + + m = ~outputs & bankmask; + if (m) + val |= gpio_rcar_read(p, INDT) & m; + spin_unlock_irqrestore(&p->lock, flags); + + bits[0] = val; + return 0; +} + static void gpio_rcar_set(struct gpio_chip *chip, unsigned offset, int value) { struct gpio_rcar_priv *p = gpiochip_get_data(chip); @@ -478,6 +507,7 @@ static int gpio_rcar_probe(struct platform_device *pdev) gpio_chip->get_direction = gpio_rcar_get_direction; gpio_chip->direction_input = gpio_rcar_direction_input; gpio_chip->get = gpio_rcar_get; + gpio_chip->get_multiple = gpio_rcar_get_multiple; gpio_chip->direction_output = gpio_rcar_direction_output; gpio_chip->set = gpio_rcar_set; gpio_chip->set_multiple = gpio_rcar_set_multiple; -- cgit v1.2.3 From 6ea68fc0a6049b59ff87f58faac5836e293d2801 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 4 Nov 2020 20:04:22 +0300 Subject: gpio: tegra: Add lockdep class Add lockdep class in order to fix debug warnings that are coming from a legit nested use of irq_set_irq_wake() by the Tegra GPIO driver. WARNING: possible recursive locking detected ... (irq_set_irq_wake) from (tegra_gpio_irq_set_wake) (tegra_gpio_irq_set_wake) from (irq_set_irq_wake) (irq_set_irq_wake) from (brcmf_sdiod_intr_register [brcmfmac]) ... Tested-by: Peter Geis Reported-by: Peter Geis Signed-off-by: Dmitry Osipenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 86568154cdb3..98fc78739ebf 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -560,6 +560,9 @@ static const struct dev_pm_ops tegra_gpio_pm_ops = { SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(tegra_gpio_suspend, tegra_gpio_resume) }; +static struct lock_class_key gpio_lock_class; +static struct lock_class_key gpio_request_class; + static int tegra_gpio_probe(struct platform_device *pdev) { struct tegra_gpio_info *tgi; @@ -661,6 +664,7 @@ static int tegra_gpio_probe(struct platform_device *pdev) bank = &tgi->bank_info[GPIO_BANK(gpio)]; irq_set_chip_data(irq, bank); + irq_set_lockdep_class(irq, &gpio_lock_class, &gpio_request_class); irq_set_chip_and_handler(irq, &tgi->ic, handle_simple_irq); } -- cgit v1.2.3 From 37174f3341306eaea7b7f4f5cc624e0040305a98 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Wed, 4 Nov 2020 20:04:23 +0300 Subject: gpio: tegra: Use raw_spinlock Use raw_spinlock in order to fix spurious messages about invalid context when spinlock debugging is enabled. This happens because there is a legit nested raw_spinlock->spinlock locking usage within IRQ-related code. IRQ core uses raw spinlock and then Tegra GPIO driver uses a nested spinlock. The debug code can't recognize and handle this case, hence we need to use raw spinlock in the GPIO driver. [ BUG: Invalid wait context ] ... (dump_stack) from (__lock_acquire) (__lock_acquire) from (lock_acquire) (lock_acquire) from (_raw_spin_lock_irqsave) (_raw_spin_lock_irqsave) from (tegra_gpio_irq_set_type) (tegra_gpio_irq_set_type) from (__irq_set_trigger) (__irq_set_trigger) from (__setup_irq) (__setup_irq) from (request_threaded_irq) (request_threaded_irq) from (devm_request_threaded_irq) (devm_request_threaded_irq) from (elants_i2c_probe) (elants_i2c_probe) from (i2c_device_probe) ... Tested-by: Peter Geis Signed-off-by: Dmitry Osipenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-tegra.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-tegra.c b/drivers/gpio/gpio-tegra.c index 98fc78739ebf..e19ebff6018c 100644 --- a/drivers/gpio/gpio-tegra.c +++ b/drivers/gpio/gpio-tegra.c @@ -61,8 +61,16 @@ struct tegra_gpio_info; struct tegra_gpio_bank { unsigned int bank; unsigned int irq; - spinlock_t lvl_lock[4]; - spinlock_t dbc_lock[4]; /* Lock for updating debounce count register */ + + /* + * IRQ-core code uses raw locking, and thus, nested locking also + * should be raw in order not to trip spinlock debug warnings. + */ + raw_spinlock_t lvl_lock[4]; + + /* Lock for updating debounce count register */ + spinlock_t dbc_lock[4]; + #ifdef CONFIG_PM_SLEEP u32 cnf[4]; u32 out[4]; @@ -334,14 +342,14 @@ static int tegra_gpio_irq_set_type(struct irq_data *d, unsigned int type) return -EINVAL; } - spin_lock_irqsave(&bank->lvl_lock[port], flags); + raw_spin_lock_irqsave(&bank->lvl_lock[port], flags); val = tegra_gpio_readl(tgi, GPIO_INT_LVL(tgi, gpio)); val &= ~(GPIO_INT_LVL_MASK << GPIO_BIT(gpio)); val |= lvl_type << GPIO_BIT(gpio); tegra_gpio_writel(tgi, val, GPIO_INT_LVL(tgi, gpio)); - spin_unlock_irqrestore(&bank->lvl_lock[port], flags); + raw_spin_unlock_irqrestore(&bank->lvl_lock[port], flags); tegra_gpio_mask_write(tgi, GPIO_MSK_OE(tgi, gpio), gpio, 0); tegra_gpio_enable(tgi, gpio); @@ -675,7 +683,7 @@ static int tegra_gpio_probe(struct platform_device *pdev) tegra_gpio_irq_handler, bank); for (j = 0; j < 4; j++) { - spin_lock_init(&bank->lvl_lock[j]); + raw_spin_lock_init(&bank->lvl_lock[j]); spin_lock_init(&bank->dbc_lock[j]); } } -- cgit v1.2.3 From 3c6e73e47afc874c231b48157be669efaf768471 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 10 Nov 2020 10:39:21 +0100 Subject: gpiolib: devres: shrink devm_gpiochip_add_data_with_key() If all we want to manage is a single pointer, there's no need to manually allocate and add a new devres. We can simply use devm_add_action_or_reset() and shrink the code by a good bit. Signed-off-by: Bartosz Golaszewski Reviewed-by: Linus Walleij --- drivers/gpio/gpiolib-devres.c | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 174f88d5ec17..4a517e5dedf0 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -477,9 +477,9 @@ void devm_gpio_free(struct device *dev, unsigned int gpio) } EXPORT_SYMBOL_GPL(devm_gpio_free); -static void devm_gpio_chip_release(struct device *dev, void *res) +static void devm_gpio_chip_release(void *data) { - struct gpio_chip *gc = *(struct gpio_chip **)res; + struct gpio_chip *gc = data; gpiochip_remove(gc); } @@ -505,23 +505,12 @@ int devm_gpiochip_add_data_with_key(struct device *dev, struct gpio_chip *gc, vo struct lock_class_key *lock_key, struct lock_class_key *request_key) { - struct gpio_chip **ptr; int ret; - ptr = devres_alloc(devm_gpio_chip_release, sizeof(*ptr), - GFP_KERNEL); - if (!ptr) - return -ENOMEM; - ret = gpiochip_add_data_with_key(gc, data, lock_key, request_key); - if (ret < 0) { - devres_free(ptr); + if (ret < 0) return ret; - } - *ptr = gc; - devres_add(dev, ptr); - - return 0; + return devm_add_action_or_reset(dev, devm_gpio_chip_release, gc); } EXPORT_SYMBOL_GPL(devm_gpiochip_add_data_with_key); -- cgit v1.2.3 From 13daf48978280ea8bce38f1e0598b913b09f5395 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:16 +0200 Subject: gpiolib: Replace unsigned by unsigned int Replace unsigned by unsigned int in GPIO library code. Note, legacy API left untouched. Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib.c | 16 ++++++++-------- include/linux/gpio/consumer.h | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c980ddcda833..fe31e7f1fb6e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -211,7 +211,7 @@ static int gpiochip_find_base(int ngpio) int gpiod_get_direction(struct gpio_desc *desc) { struct gpio_chip *gc; - unsigned offset; + unsigned int offset; int ret; gc = gpiod_to_chip(desc); @@ -1333,7 +1333,7 @@ void gpiochip_irq_domain_deactivate(struct irq_domain *domain, } EXPORT_SYMBOL_GPL(gpiochip_irq_domain_deactivate); -static int gpiochip_to_irq(struct gpio_chip *gc, unsigned offset) +static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset) { struct irq_domain *domain = gc->irq.domain; @@ -1635,7 +1635,7 @@ static inline void gpiochip_irqchip_free_valid_mask(struct gpio_chip *gc) * @gc: the gpiochip owning the GPIO * @offset: the offset of the GPIO to request for GPIO function */ -int gpiochip_generic_request(struct gpio_chip *gc, unsigned offset) +int gpiochip_generic_request(struct gpio_chip *gc, unsigned int offset) { #ifdef CONFIG_PINCTRL if (list_empty(&gc->gpiodev->pin_ranges)) @@ -1651,7 +1651,7 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_request); * @gc: the gpiochip to request the gpio function for * @offset: the offset of the GPIO to free from GPIO function */ -void gpiochip_generic_free(struct gpio_chip *gc, unsigned offset) +void gpiochip_generic_free(struct gpio_chip *gc, unsigned int offset) { pinctrl_gpio_free(gc->gpiodev->base + offset); } @@ -1663,7 +1663,7 @@ EXPORT_SYMBOL_GPL(gpiochip_generic_free); * @offset: the offset of the GPIO to apply the configuration * @config: the configuration to be applied */ -int gpiochip_generic_config(struct gpio_chip *gc, unsigned offset, +int gpiochip_generic_config(struct gpio_chip *gc, unsigned int offset, unsigned long config) { return pinctrl_gpio_set_config(gc->gpiodev->base + offset, config); @@ -1993,7 +1993,7 @@ void gpiod_free(struct gpio_desc *desc) * help with diagnostics, and knowing that the signal is used as a GPIO * can help avoid accidentally multiplexing it to another controller. */ -const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned offset) +const char *gpiochip_is_requested(struct gpio_chip *gc, unsigned int offset) { struct gpio_desc *desc; @@ -2097,7 +2097,7 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) { struct gpio_chip *gc = desc->gdev->chip; unsigned long config; - unsigned arg; + unsigned int arg; switch (mode) { case PIN_CONFIG_BIAS_PULL_DOWN: @@ -2353,7 +2353,7 @@ EXPORT_SYMBOL_GPL(gpiod_set_config); * 0 on success, %-ENOTSUPP if the controller doesn't support setting the * debounce time. */ -int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) +int gpiod_set_debounce(struct gpio_desc *desc, unsigned int debounce) { unsigned long config; diff --git a/include/linux/gpio/consumer.h b/include/linux/gpio/consumer.h index 901aab89d025..ef49307611d2 100644 --- a/include/linux/gpio/consumer.h +++ b/include/linux/gpio/consumer.h @@ -158,7 +158,7 @@ int gpiod_set_raw_array_value_cansleep(unsigned int array_size, unsigned long *value_bitmap); int gpiod_set_config(struct gpio_desc *desc, unsigned long config); -int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce); +int gpiod_set_debounce(struct gpio_desc *desc, unsigned int debounce); int gpiod_set_transitory(struct gpio_desc *desc, bool transitory); void gpiod_toggle_active_low(struct gpio_desc *desc); @@ -481,7 +481,7 @@ static inline int gpiod_set_config(struct gpio_desc *desc, unsigned long config) return -ENOSYS; } -static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned debounce) +static inline int gpiod_set_debounce(struct gpio_desc *desc, unsigned int debounce) { /* GPIO can never have been requested */ WARN_ON(desc); -- cgit v1.2.3 From 6900fad60ac6987b7c1e4dee2e99e28701a2b8fb Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:17 +0200 Subject: gpiolib: add missed break statement It's no difference in the functionality, but after the change the code is less error prone to various checkers. Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index fe31e7f1fb6e..23fc5bfd2045 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2107,6 +2107,7 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) default: arg = 0; + break; } config = PIN_CONF_PACKED(mode, arg); -- cgit v1.2.3 From 8b69461c2b7c801e37259dc6e71b126c23c3f20d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:18 +0200 Subject: gpiolib: use proper API to pack pin configuration parameters Instead of open coded macro use, call pinconf_to_config_packed(). Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 23fc5bfd2045..13653bbd5010 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2110,7 +2110,7 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) break; } - config = PIN_CONF_PACKED(mode, arg); + config = pinconf_to_config_packed(mode, arg); return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config); } -- cgit v1.2.3 From 0c4d86663ba134cfe216eec5dd2c1ed3d52767e6 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:20 +0200 Subject: gpiolib: Extract gpio_set_config_with_argument() for future use In the future we will need to have a separate function that takes an arbitrary argument value. Extract gpio_set_config_with_argument() for that purpose. Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 13653bbd5010..df9f6ed96b64 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2093,10 +2093,19 @@ static int gpio_do_set_config(struct gpio_chip *gc, unsigned int offset, return gc->set_config(gc, offset, config); } -static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) +static int gpio_set_config_with_argument(struct gpio_desc *desc, + enum pin_config_param mode, + u32 argument) { struct gpio_chip *gc = desc->gdev->chip; unsigned long config; + + config = pinconf_to_config_packed(mode, argument); + return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config); +} + +static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) +{ unsigned int arg; switch (mode) { @@ -2110,8 +2119,7 @@ static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) break; } - config = pinconf_to_config_packed(mode, arg); - return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config); + return gpio_set_config_with_argument(desc, mode, arg); } static int gpio_set_bias(struct gpio_desc *desc) -- cgit v1.2.3 From 6aa32ad70759a9e4f6ceee137b06ac55d36a71e3 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:21 +0200 Subject: gpiolib: move bias related code from gpio_set_config() to gpio_set_bias() Move bias related code from gpio_set_config() to gpio_set_bias(). Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index df9f6ed96b64..7e40c827bd48 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2106,25 +2106,13 @@ static int gpio_set_config_with_argument(struct gpio_desc *desc, static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) { - unsigned int arg; - - switch (mode) { - case PIN_CONFIG_BIAS_PULL_DOWN: - case PIN_CONFIG_BIAS_PULL_UP: - arg = 1; - break; - - default: - arg = 0; - break; - } - - return gpio_set_config_with_argument(desc, mode, arg); + return gpio_set_config_with_argument(desc, mode, 0); } static int gpio_set_bias(struct gpio_desc *desc) { enum pin_config_param bias; + unsigned int arg; int ret; if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) @@ -2136,7 +2124,18 @@ static int gpio_set_bias(struct gpio_desc *desc) else return 0; - ret = gpio_set_config(desc, bias); + switch (bias) { + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_UP: + arg = 1; + break; + + default: + arg = 0; + break; + } + + ret = gpio_set_config_with_argument(desc, bias, arg); if (ret != -ENOTSUPP) return ret; -- cgit v1.2.3 From baca3b15cd2a171fa967223e2d7aea6e5f98ba9e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 11 Nov 2020 20:49:30 +0200 Subject: gpiolib: Extract gpio_set_config_with_argument_optional() helper This function is useful for internal use in the GPIO library. There will be new user coming, prepare a helper for the new comer and the existing ones. Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib.c | 53 +++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 7e40c827bd48..e5338f6d78d7 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2104,6 +2104,29 @@ static int gpio_set_config_with_argument(struct gpio_desc *desc, return gpio_do_set_config(gc, gpio_chip_hwgpio(desc), config); } +static int gpio_set_config_with_argument_optional(struct gpio_desc *desc, + enum pin_config_param mode, + u32 argument) +{ + struct device *dev = &desc->gdev->dev; + int gpio = gpio_chip_hwgpio(desc); + int ret; + + ret = gpio_set_config_with_argument(desc, mode, argument); + if (ret != -ENOTSUPP) + return ret; + + switch (mode) { + case PIN_CONFIG_PERSIST_STATE: + dev_dbg(dev, "Persistence not supported for GPIO %d\n", gpio); + break; + default: + break; + } + + return 0; +} + static int gpio_set_config(struct gpio_desc *desc, enum pin_config_param mode) { return gpio_set_config_with_argument(desc, mode, 0); @@ -2113,7 +2136,6 @@ static int gpio_set_bias(struct gpio_desc *desc) { enum pin_config_param bias; unsigned int arg; - int ret; if (test_bit(FLAG_BIAS_DISABLE, &desc->flags)) bias = PIN_CONFIG_BIAS_DISABLE; @@ -2135,11 +2157,7 @@ static int gpio_set_bias(struct gpio_desc *desc) break; } - ret = gpio_set_config_with_argument(desc, bias, arg); - if (ret != -ENOTSUPP) - return ret; - - return 0; + return gpio_set_config_with_argument_optional(desc, bias, arg); } /** @@ -2380,11 +2398,6 @@ EXPORT_SYMBOL_GPL(gpiod_set_debounce); */ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) { - struct gpio_chip *gc; - unsigned long packed; - int gpio; - int rc; - VALIDATE_DESC(desc); /* * Handle FLAG_TRANSITORY first, enabling queries to gpiolib for @@ -2393,21 +2406,9 @@ int gpiod_set_transitory(struct gpio_desc *desc, bool transitory) assign_bit(FLAG_TRANSITORY, &desc->flags, transitory); /* If the driver supports it, set the persistence state now */ - gc = desc->gdev->chip; - if (!gc->set_config) - return 0; - - packed = pinconf_to_config_packed(PIN_CONFIG_PERSIST_STATE, - !transitory); - gpio = gpio_chip_hwgpio(desc); - rc = gpio_do_set_config(gc, gpio, packed); - if (rc == -ENOTSUPP) { - dev_dbg(&desc->gdev->dev, "Persistence not supported for GPIO %d\n", - gpio); - return 0; - } - - return rc; + return gpio_set_config_with_argument_optional(desc, + PIN_CONFIG_PERSIST_STATE, + !transitory); } EXPORT_SYMBOL_GPL(gpiod_set_transitory); -- cgit v1.2.3 From f725edd86b6b2415db9ae9bb6293f8300b9dbce9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:23 +0200 Subject: gpiolib: Introduce gpio_set_debounce_timeout() for internal use In some cases we would like to have debounce setter which doesn't fail when a feature is not supported by a controller. Cc: Mika Westerberg Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede --- drivers/gpio/gpiolib.c | 7 +++++++ drivers/gpio/gpiolib.h | 1 + 2 files changed, 8 insertions(+) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index e5338f6d78d7..c6db72d5420e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -2160,6 +2160,13 @@ static int gpio_set_bias(struct gpio_desc *desc) return gpio_set_config_with_argument_optional(desc, bias, arg); } +int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce) +{ + return gpio_set_config_with_argument_optional(desc, + PIN_CONFIG_INPUT_DEBOUNCE, + debounce); +} + /** * gpiod_direction_input - set the GPIO direction to input * @desc: GPIO to set to input diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index b674b5bb980e..42d81454da21 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -134,6 +134,7 @@ int gpiod_request(struct gpio_desc *desc, const char *label); void gpiod_free(struct gpio_desc *desc); int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, unsigned long lflags, enum gpiod_flags dflags); +int gpio_set_debounce_timeout(struct gpio_desc *desc, unsigned int debounce); int gpiod_hog(struct gpio_desc *desc, const char *name, unsigned long lflags, enum gpiod_flags dflags); -- cgit v1.2.3 From e7b731327aeac9c5b3c5c8677102813a34cc380a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:24 +0200 Subject: gpiolib: acpi: Respect bias settings for GpioInt() resource In some cases the GpioInt() resource is coming with bias settings which may affect system functioning. Respect bias settings for GpioInt() resource by calling acpi_gpio_update_gpiod_*flags() API in acpi_dev_gpio_irq_get(). Reported-by: Jamie McClymont Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib-acpi.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 834a12f3219e..3a39e8a93226 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -942,6 +942,7 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) if (info.gpioint && idx++ == index) { unsigned long lflags = GPIO_LOOKUP_FLAGS_DEFAULT; + enum gpiod_flags dflags = GPIOD_ASIS; char label[32]; int irq; @@ -952,8 +953,11 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) if (irq < 0) return irq; + acpi_gpio_update_gpiod_flags(&dflags, &info); + acpi_gpio_update_gpiod_lookup_flags(&lflags, &info); + snprintf(label, sizeof(label), "GpioInt() %d", index); - ret = gpiod_configure_flags(desc, label, lflags, info.flags); + ret = gpiod_configure_flags(desc, label, lflags, dflags); if (ret < 0) return ret; -- cgit v1.2.3 From 32fa65527ce13607de0fbf2e7aeddb978ea2220a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:25 +0200 Subject: gpiolib: acpi: Use named item for enum gpiod_flags variable Use named item instead of plain integer for enum gpiod_flags to make it clear that even 0 has its own meaning. Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib-acpi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 3a39e8a93226..c127b410a7a2 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -1136,7 +1136,7 @@ acpi_gpiochip_parse_own_gpio(struct acpi_gpio_chip *achip, int ret; *lflags = GPIO_LOOKUP_FLAGS_DEFAULT; - *dflags = 0; + *dflags = GPIOD_ASIS; *name = NULL; ret = fwnode_property_read_u32_array(fwnode, "gpios", gpios, -- cgit v1.2.3 From 8dcb7a15a585b6d0fee15751ce11d7a68cfedd56 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:26 +0200 Subject: gpiolib: acpi: Take into account debounce settings We didn't take into account the debounce settings supplied by ACPI. This change is targeting the mentioned gap. Reported-by: Coiby Xu Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib-acpi.c | 18 ++++++++++++++++++ drivers/gpio/gpiolib-acpi.h | 2 ++ 2 files changed, 20 insertions(+) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index c127b410a7a2..a9254de964cc 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -299,6 +299,10 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, return AE_OK; } + ret = gpio_set_debounce_timeout(desc, agpio->debounce_timeout); + if (ret) + goto fail_free_desc; + ret = gpiochip_lock_as_irq(chip, pin); if (ret) { dev_err(chip->parent, @@ -664,6 +668,7 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) lookup->desc = acpi_get_gpiod(agpio->resource_source.string_ptr, agpio->pin_table[pin_index]); lookup->info.pin_config = agpio->pin_config; + lookup->info.debounce = agpio->debounce_timeout; lookup->info.gpioint = gpioint; /* @@ -961,6 +966,10 @@ int acpi_dev_gpio_irq_get(struct acpi_device *adev, int index) if (ret < 0) return ret; + ret = gpio_set_debounce_timeout(desc, info.debounce); + if (ret) + return ret; + irq_flags = acpi_dev_get_irq_type(info.triggering, info.polarity); @@ -1048,6 +1057,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, if (!found) { enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio); const char *label = "ACPI:OpRegion"; + int ret; desc = gpiochip_request_own_desc(chip, pin, label, GPIO_ACTIVE_HIGH, @@ -1058,6 +1068,14 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, goto out; } + ret = gpio_set_debounce_timeout(desc, agpio->debounce_timeout); + if (ret) { + gpiochip_free_own_desc(desc); + mutex_unlock(&achip->conn_lock); + status = AE_ERROR; + goto out; + } + conn = kzalloc(sizeof(*conn), GFP_KERNEL); if (!conn) { status = AE_NO_MEMORY; diff --git a/drivers/gpio/gpiolib-acpi.h b/drivers/gpio/gpiolib-acpi.h index 1c6d65cf0629..e2edb632b2cc 100644 --- a/drivers/gpio/gpiolib-acpi.h +++ b/drivers/gpio/gpiolib-acpi.h @@ -18,6 +18,7 @@ struct acpi_device; * @pin_config: pin bias as provided by ACPI * @polarity: interrupt polarity as provided by ACPI * @triggering: triggering type as provided by ACPI + * @debounce: debounce timeout as provided by ACPI * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping */ struct acpi_gpio_info { @@ -27,6 +28,7 @@ struct acpi_gpio_info { int pin_config; int polarity; int triggering; + unsigned int debounce; unsigned int quirks; }; -- cgit v1.2.3 From ce698f4ec18c56ca1f5f725fcf6f7e2c04d90be1 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 11 Nov 2020 20:01:52 +0200 Subject: gpiolib: acpi: Move non-critical code outside of critical section Mika noticed that some code is run under mutex when it doesn't require the lock, like an error code assignment. Move non-critical code outside of critical section. Suggested-by: Mika Westerberg Cc: Hans de Goede Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib-acpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index a9254de964cc..b00171d2aaf5 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -1063,8 +1063,8 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, GPIO_ACTIVE_HIGH, flags); if (IS_ERR(desc)) { - status = AE_ERROR; mutex_unlock(&achip->conn_lock); + status = AE_ERROR; goto out; } @@ -1078,9 +1078,9 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, conn = kzalloc(sizeof(*conn), GFP_KERNEL); if (!conn) { - status = AE_NO_MEMORY; gpiochip_free_own_desc(desc); mutex_unlock(&achip->conn_lock); + status = AE_NO_MEMORY; goto out; } -- cgit v1.2.3 From 1a81f19154b4afcd4216a7253938adf1c0e65ea9 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:27 +0200 Subject: gpiolib: acpi: Move acpi_gpio_to_gpiod_flags() upper in the code Move acpi_gpio_to_gpiod_flags() upper in the code to allow further refactoring. Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib-acpi.c | 66 ++++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index b00171d2aaf5..ac1bde0720f2 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -205,6 +205,39 @@ static void acpi_gpiochip_request_irqs(struct acpi_gpio_chip *acpi_gpio) acpi_gpiochip_request_irq(acpi_gpio, event); } +static enum gpiod_flags +acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) +{ + switch (agpio->io_restriction) { + case ACPI_IO_RESTRICT_INPUT: + return GPIOD_IN; + case ACPI_IO_RESTRICT_OUTPUT: + /* + * ACPI GPIO resources don't contain an initial value for the + * GPIO. Therefore we deduce that value from the pull field + * instead. If the pin is pulled up we assume default to be + * high, if it is pulled down we assume default to be low, + * otherwise we leave pin untouched. + */ + switch (agpio->pin_config) { + case ACPI_PIN_CONFIG_PULLUP: + return GPIOD_OUT_HIGH; + case ACPI_PIN_CONFIG_PULLDOWN: + return GPIOD_OUT_LOW; + default: + break; + } + default: + break; + } + + /* + * Assume that the BIOS has configured the direction and pull + * accordingly. + */ + return GPIOD_ASIS; +} + static bool acpi_gpio_in_ignore_list(const char *controller_in, int pin_in) { const char *controller, *pin_str; @@ -530,39 +563,6 @@ static bool acpi_get_driver_gpio_data(struct acpi_device *adev, return false; } -static enum gpiod_flags -acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) -{ - switch (agpio->io_restriction) { - case ACPI_IO_RESTRICT_INPUT: - return GPIOD_IN; - case ACPI_IO_RESTRICT_OUTPUT: - /* - * ACPI GPIO resources don't contain an initial value for the - * GPIO. Therefore we deduce that value from the pull field - * instead. If the pin is pulled up we assume default to be - * high, if it is pulled down we assume default to be low, - * otherwise we leave pin untouched. - */ - switch (agpio->pin_config) { - case ACPI_PIN_CONFIG_PULLUP: - return GPIOD_OUT_HIGH; - case ACPI_PIN_CONFIG_PULLDOWN: - return GPIOD_OUT_LOW; - default: - break; - } - default: - break; - } - - /* - * Assume that the BIOS has configured the direction and pull - * accordingly. - */ - return GPIOD_ASIS; -} - static int __acpi_gpio_update_gpiod_flags(enum gpiod_flags *flags, enum gpiod_flags update) { -- cgit v1.2.3 From 56f7058af0dc0fb07b03cb49b945d8793dc3264a Mon Sep 17 00:00:00 2001 From: Vasile-Laurentiu Stanimir Date: Thu, 1 Oct 2020 20:12:12 +0300 Subject: gpiolib: acpi: Set initial value for output pin based on bias and polarity GpioIo() resources don't contain an initial value for the output pin. Therefore instead of deducting its value solely based on bias field we should deduce that value from the polarity and the bias fields. Typical scenario is, when pin is defined in the table and its polarity, specified in _DSD or via platform code, is defined as active low, in the following call chain: -> acpi_populate_gpio_lookup() -> acpi_gpio_to_gpiod_flags() it will return GPIOD_OUT_HIGH if bias is set no matter if polarity is GPIO_ACTIVE_LOW, so it will return the current level instead of the logical level. Cc: Hans de Goede Signed-off-by: Vasile-Laurentiu Stanimir Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib-acpi.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index ac1bde0720f2..b47d5e8edaeb 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -206,7 +206,7 @@ static void acpi_gpiochip_request_irqs(struct acpi_gpio_chip *acpi_gpio) } static enum gpiod_flags -acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) +acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio, int polarity) { switch (agpio->io_restriction) { case ACPI_IO_RESTRICT_INPUT: @@ -215,15 +215,17 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) /* * ACPI GPIO resources don't contain an initial value for the * GPIO. Therefore we deduce that value from the pull field - * instead. If the pin is pulled up we assume default to be - * high, if it is pulled down we assume default to be low, - * otherwise we leave pin untouched. + * and the polarity instead. If the pin is pulled up we assume + * default to be high, if it is pulled down we assume default + * to be low, otherwise we leave pin untouched. For active low + * polarity values will be switched. See also + * Documentation/firmware-guide/acpi/gpio-properties.rst. */ switch (agpio->pin_config) { case ACPI_PIN_CONFIG_PULLUP: - return GPIOD_OUT_HIGH; + return polarity == GPIO_ACTIVE_LOW ? GPIOD_OUT_LOW : GPIOD_OUT_HIGH; case ACPI_PIN_CONFIG_PULLDOWN: - return GPIOD_OUT_LOW; + return polarity == GPIO_ACTIVE_LOW ? GPIOD_OUT_HIGH : GPIOD_OUT_LOW; default: break; } @@ -683,8 +685,8 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) lookup->info.polarity = agpio->polarity; lookup->info.triggering = agpio->triggering; } else { - lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio); lookup->info.polarity = lookup->active_low; + lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio, lookup->info.polarity); } } @@ -1055,12 +1057,13 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, } if (!found) { - enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio); + int polarity = GPIO_ACTIVE_HIGH; + enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio, polarity); const char *label = "ACPI:OpRegion"; int ret; desc = gpiochip_request_own_desc(chip, pin, label, - GPIO_ACTIVE_HIGH, + polarity, flags); if (IS_ERR(desc)) { mutex_unlock(&achip->conn_lock); -- cgit v1.2.3 From bca404802ceade19d7649a840178c415316814cc Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:28 +0200 Subject: gpiolib: acpi: Make acpi_gpio_to_gpiod_flags() usable for GpioInt() GpioInt() implies input configuration of the pin. Add this to the acpi_gpio_to_gpiod_flags() and make usable for GpioInt(). Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib-acpi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index b47d5e8edaeb..644067cc0f81 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -208,6 +208,10 @@ static void acpi_gpiochip_request_irqs(struct acpi_gpio_chip *acpi_gpio) static enum gpiod_flags acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio, int polarity) { + /* GpioInt() implies input configuration */ + if (agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT) + return GPIOD_IN; + switch (agpio->io_restriction) { case ACPI_IO_RESTRICT_INPUT: return GPIOD_IN; @@ -681,13 +685,13 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH */ if (lookup->info.gpioint) { - lookup->info.flags = GPIOD_IN; lookup->info.polarity = agpio->polarity; lookup->info.triggering = agpio->triggering; } else { lookup->info.polarity = lookup->active_low; - lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio, lookup->info.polarity); } + + lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio, lookup->info.polarity); } return 1; -- cgit v1.2.3 From 2e2b496cebefb9514fc04adcb4658df4f82ceb0d Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 11 Nov 2020 23:35:33 +0200 Subject: gpiolib: acpi: Extract acpi_request_own_gpiod() helper It appears that we are using similar code excerpts for ACPI OpRegion and event handling. Deduplicate those excerpts by extracting a new acpi_request_own_gpiod() helper. Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib-acpi.c | 46 +++++++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 644067cc0f81..c46fd51007d0 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -244,6 +244,28 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio, int polarity) return GPIOD_ASIS; } +static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip, + struct acpi_resource_gpio *agpio, + unsigned int index, + const char *label) +{ + int polarity = GPIO_ACTIVE_HIGH; + enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio, polarity); + unsigned int pin = agpio->pin_table[index]; + struct gpio_desc *desc; + int ret; + + desc = gpiochip_request_own_desc(chip, pin, label, polarity, flags); + if (IS_ERR(desc)) + return desc; + + ret = gpio_set_debounce_timeout(desc, agpio->debounce_timeout); + if (ret) + gpiochip_free_own_desc(desc); + + return ret ? ERR_PTR(ret) : desc; +} + static bool acpi_gpio_in_ignore_list(const char *controller_in, int pin_in) { const char *controller, *pin_str; @@ -329,8 +351,7 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, if (!handler) return AE_OK; - desc = gpiochip_request_own_desc(chip, pin, "ACPI:Event", - GPIO_ACTIVE_HIGH, GPIOD_IN); + desc = acpi_request_own_gpiod(chip, agpio, 0, "ACPI:Event"); if (IS_ERR(desc)) { dev_err(chip->parent, "Failed to request GPIO for pin 0x%04X, err %ld\n", @@ -338,10 +359,6 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, return AE_OK; } - ret = gpio_set_debounce_timeout(desc, agpio->debounce_timeout); - if (ret) - goto fail_free_desc; - ret = gpiochip_lock_as_irq(chip, pin); if (ret) { dev_err(chip->parent, @@ -1061,28 +1078,13 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, } if (!found) { - int polarity = GPIO_ACTIVE_HIGH; - enum gpiod_flags flags = acpi_gpio_to_gpiod_flags(agpio, polarity); - const char *label = "ACPI:OpRegion"; - int ret; - - desc = gpiochip_request_own_desc(chip, pin, label, - polarity, - flags); + desc = acpi_request_own_gpiod(chip, agpio, i, "ACPI:OpRegion"); if (IS_ERR(desc)) { mutex_unlock(&achip->conn_lock); status = AE_ERROR; goto out; } - ret = gpio_set_debounce_timeout(desc, agpio->debounce_timeout); - if (ret) { - gpiochip_free_own_desc(desc); - mutex_unlock(&achip->conn_lock); - status = AE_ERROR; - goto out; - } - conn = kzalloc(sizeof(*conn), GFP_KERNEL); if (!conn) { gpiochip_free_own_desc(desc); -- cgit v1.2.3 From 74301f2781586d0e6669466b2b4d59d94c63fa5a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:30 +0200 Subject: gpiolib: acpi: Convert pin_index to be u16 As specified by ACPI the pin index is 16-bit unsigned integer. Define the variable, which holds it, accordingly. Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib-acpi.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index c46fd51007d0..a556e2ec0a39 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -660,7 +660,7 @@ int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags, struct acpi_gpio_lookup { struct acpi_gpio_info info; int index; - int pin_index; + u16 pin_index; bool active_low; struct gpio_desc *desc; int n; @@ -676,7 +676,7 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) if (!lookup->desc) { const struct acpi_resource_gpio *agpio = &ares->data.gpio; bool gpioint = agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT; - int pin_index; + u16 pin_index; if (lookup->info.quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint) lookup->index++; @@ -822,7 +822,7 @@ static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, if (ret) return ERR_PTR(ret); - dev_dbg(&adev->dev, "GPIO: _DSD returned %s %d %d %u\n", + dev_dbg(&adev->dev, "GPIO: _DSD returned %s %d %u %u\n", dev_name(&lookup.info.adev->dev), lookup.index, lookup.pin_index, lookup.active_low); } else { @@ -1018,7 +1018,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, struct gpio_chip *chip = achip->chip; struct acpi_resource_gpio *agpio; struct acpi_resource *ares; - int pin_index = (int)address; + u16 pin_index = address; acpi_status status; int length; int i; @@ -1041,7 +1041,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, return AE_BAD_PARAMETER; } - length = min(agpio->pin_table_length, (u16)(pin_index + bits)); + length = min_t(u16, agpio->pin_table_length, pin_index + bits); for (i = pin_index; i < length; ++i) { int pin = agpio->pin_table[i]; struct acpi_gpio_connection *conn; -- cgit v1.2.3 From 2c4d00cb8fc5e01004eb2e84d13c09a2d9ecab0f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:31 +0200 Subject: gpiolib: acpi: Use BIT() macro to increase readability We may use BIT() macro to increase readability in acpi_gpio_adr_space_handler(). Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Hans de Goede Reviewed-by: Mika Westerberg --- drivers/gpio/gpiolib-acpi.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index a556e2ec0a39..6cc5f91bfe2e 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -1101,8 +1101,7 @@ acpi_gpio_adr_space_handler(u32 function, acpi_physical_address address, mutex_unlock(&achip->conn_lock); if (function == ACPI_WRITE) - gpiod_set_raw_value_cansleep(desc, - !!((1 << i) & *value)); + gpiod_set_raw_value_cansleep(desc, !!(*value & BIT(i))); else *value |= (u64)gpiod_get_raw_value_cansleep(desc) << i; } -- cgit v1.2.3 From e709a7b5a066362b697d65dda90edc71f913df70 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 9 Nov 2020 22:53:32 +0200 Subject: gpiolib: acpi: Make Intel GPIO tree official for GPIO ACPI work Make Intel GPIO tree official for GPIO ACPI work. Signed-off-by: Andy Shevchenko Acked-by: Linus Walleij Reviewed-by: Mika Westerberg --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index e73636b75f29..53236b2ea0af 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7483,6 +7483,7 @@ M: Andy Shevchenko L: linux-gpio@vger.kernel.org L: linux-acpi@vger.kernel.org S: Maintained +T: git git://git.kernel.org/pub/scm/linux/kernel/git/andy/linux-gpio-intel.git F: Documentation/firmware-guide/acpi/gpio-properties.rst F: drivers/gpio/gpiolib-acpi.c F: drivers/gpio/gpiolib-acpi.h -- cgit v1.2.3 From 8c669fe69a7d931d29345ab6f2ff28891a8b6a25 Mon Sep 17 00:00:00 2001 From: Srinivas Neeli Date: Thu, 12 Nov 2020 22:42:20 +0530 Subject: gpio: gpio-xilinx: Arrange headers in sorting order Arrange header files in sorted order. Signed-off-by: Srinivas Neeli Acked-by: Michal Simek Link: https://lore.kernel.org/r/1605201148-4508-2-git-send-email-srinivas.neeli@xilinx.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-xilinx.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 67f9f82e0db0..a28aa5b2bc0f 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -6,13 +6,13 @@ */ #include -#include #include +#include +#include +#include #include #include #include -#include -#include #include /* Register Offset Definitions */ -- cgit v1.2.3 From 700a2b53bdc9c3b3f7241626eaf9a81b04c7593d Mon Sep 17 00:00:00 2001 From: Srinivas Neeli Date: Thu, 12 Nov 2020 22:42:21 +0530 Subject: dt-bindings: gpio: gpio-xilinx: Add clk support to xilinx soft gpio IP Specify clock property in binding. Signed-off-by: Srinivas Neeli Acked-by: Michal Simek Link: https://lore.kernel.org/r/1605201148-4508-3-git-send-email-srinivas.neeli@xilinx.com Signed-off-by: Linus Walleij --- Documentation/devicetree/bindings/gpio/gpio-xilinx.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/gpio/gpio-xilinx.txt b/Documentation/devicetree/bindings/gpio/gpio-xilinx.txt index 08eed2335db0..e506f30e1a95 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-xilinx.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-xilinx.txt @@ -13,6 +13,7 @@ Required properties: - gpio-controller : Marks the device node as a GPIO controller. Optional properties: +- clocks : Input clock specifier. Refer to common clock bindings. - interrupts : Interrupt mapping for GPIO IRQ. - xlnx,all-inputs : if n-th bit is setup, GPIO-n is input - xlnx,dout-default : if n-th bit is 1, GPIO-n default value is 1 @@ -29,6 +30,7 @@ Example: gpio: gpio@40000000 { #gpio-cells = <2>; compatible = "xlnx,xps-gpio-1.00.a"; + clocks = <&clkc25>; gpio-controller ; interrupt-parent = <µblaze_0_intc>; interrupts = < 6 2 >; -- cgit v1.2.3 From 65bbe531b54668099783cd687e674b9587c7e56e Mon Sep 17 00:00:00 2001 From: Srinivas Neeli Date: Thu, 12 Nov 2020 22:42:22 +0530 Subject: gpio: gpio-xilinx: Add clock support Adds clock support to the Xilinx GPIO driver. Signed-off-by: Srinivas Neeli Acked-by: Michal Simek Link: https://lore.kernel.org/r/1605201148-4508-4-git-send-email-srinivas.neeli@xilinx.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-xilinx.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index a28aa5b2bc0f..5327457586d7 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -6,6 +6,7 @@ */ #include +#include #include #include #include @@ -38,6 +39,7 @@ * @gpio_state: GPIO state shadow register * @gpio_dir: GPIO direction shadow register * @gpio_lock: Lock used for synchronization + * @clk: clock resource for this driver */ struct xgpio_instance { struct gpio_chip gc; @@ -46,6 +48,7 @@ struct xgpio_instance { u32 gpio_state[2]; u32 gpio_dir[2]; spinlock_t gpio_lock[2]; + struct clk *clk; }; static inline int xgpio_index(struct xgpio_instance *chip, int gpio) @@ -334,11 +337,25 @@ static int xgpio_probe(struct platform_device *pdev) return PTR_ERR(chip->regs); } + chip->clk = devm_clk_get_optional(&pdev->dev, NULL); + if (IS_ERR(chip->clk)) { + if (PTR_ERR(chip->clk) != -EPROBE_DEFER) + dev_dbg(&pdev->dev, "Input clock not found\n"); + return PTR_ERR(chip->clk); + } + + status = clk_prepare_enable(chip->clk); + if (status < 0) { + dev_err(&pdev->dev, "Failed to prepare clk\n"); + return status; + } + xgpio_save_regs(chip); status = devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip); if (status) { dev_err(&pdev->dev, "failed to add GPIO chip\n"); + clk_disable_unprepare(chip->clk); return status; } -- cgit v1.2.3 From 0230a41ed6a818675c0166d506c3c9386af20986 Mon Sep 17 00:00:00 2001 From: Srinivas Neeli Date: Thu, 12 Nov 2020 22:42:25 +0530 Subject: gpio: gpio-xilinx: Add remove function Added remove function support. Signed-off-by: Srinivas Neeli Acked-by: Michal Simek Link: https://lore.kernel.org/r/1605201148-4508-7-git-send-email-srinivas.neeli@xilinx.com [dropped pm disable call] Signed-off-by: Linus Walleij --- drivers/gpio/gpio-xilinx.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 5327457586d7..9f2dfb734832 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -259,6 +259,23 @@ static void xgpio_save_regs(struct xgpio_instance *chip) chip->gpio_dir[1]); } +/** + * xgpio_remove - Remove method for the GPIO device. + * @pdev: pointer to the platform device + * + * This function remove gpiochips and frees all the allocated resources. + * + * Return: 0 always + */ +static int xgpio_remove(struct platform_device *pdev) +{ + struct xgpio_instance *gpio = platform_get_drvdata(pdev); + + clk_disable_unprepare(gpio->clk); + + return 0; +} + /** * xgpio_of_probe - Probe method for the GPIO device. * @pdev: pointer to the platform device @@ -371,6 +388,7 @@ MODULE_DEVICE_TABLE(of, xgpio_of_match); static struct platform_driver xgpio_plat_driver = { .probe = xgpio_probe, + .remove = xgpio_remove, .driver = { .name = "gpio-xilinx", .of_match_table = xgpio_of_match, -- cgit v1.2.3 From bea67aeab02208cbb2f3d60e358f331e2e6f1ab1 Mon Sep 17 00:00:00 2001 From: Srinivas Neeli Date: Thu, 12 Nov 2020 22:42:27 +0530 Subject: gpio: gpio-xilinx: Check return value of of_property_read_u32 In two different instances the return value of "of_property_read_u32" API was neither captured nor checked. Fixed it by capturing the return value and then checking for any error. Signed-off-by: Srinivas Neeli Acked-by: Michal Simek Addresses-Coverity: "check_return" Link: https://lore.kernel.org/r/1605201148-4508-9-git-send-email-srinivas.neeli@xilinx.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-xilinx.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 9f2dfb734832..be539381fd82 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -298,7 +298,8 @@ static int xgpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, chip); /* Update GPIO state shadow register with default value */ - of_property_read_u32(np, "xlnx,dout-default", &chip->gpio_state[0]); + if (of_property_read_u32(np, "xlnx,dout-default", &chip->gpio_state[0])) + chip->gpio_state[0] = 0x0; /* Update GPIO direction shadow register with default value */ if (of_property_read_u32(np, "xlnx,tri-default", &chip->gpio_dir[0])) @@ -318,8 +319,9 @@ static int xgpio_probe(struct platform_device *pdev) if (is_dual) { /* Update GPIO state shadow register with default value */ - of_property_read_u32(np, "xlnx,dout-default-2", - &chip->gpio_state[1]); + if (of_property_read_u32(np, "xlnx,dout-default-2", + &chip->gpio_state[1])) + chip->gpio_state[1] = 0x0; /* Update GPIO direction shadow register with default value */ if (of_property_read_u32(np, "xlnx,tri-default-2", -- cgit v1.2.3 From 8b51658347affcebfa30b82cd814201a329725fc Mon Sep 17 00:00:00 2001 From: Srinivas Neeli Date: Thu, 12 Nov 2020 22:42:28 +0530 Subject: MAINTAINERS: add fragment for xilinx GPIO drivers Added entry for xilinx GPIO drivers. Signed-off-by: Srinivas Neeli Acked-by: Shubhrajyoti Datta Acked-by: Michal Simek Link: https://lore.kernel.org/r/1605201148-4508-10-git-send-email-srinivas.neeli@xilinx.com Signed-off-by: Linus Walleij --- MAINTAINERS | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5263505de0bf..eadfbac4fcd0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19254,6 +19254,16 @@ S: Maintained F: Documentation/devicetree/bindings/net/can/xilinx_can.txt F: drivers/net/can/xilinx_can.c +XILINX GPIO DRIVER +M: Shubhrajyoti Datta +R: Srinivas Neeli +R: Michal Simek +S: Maintained +F: Documentation/devicetree/bindings/gpio/gpio-xilinx.txt +F: Documentation/devicetree/bindings/gpio/gpio-zynq.txt +F: drivers/gpio/gpio-xilinx.c +F: drivers/gpio/gpio-zynq.c + XILINX SD-FEC IP CORES M: Derek Kiernan M: Dragan Cvetic -- cgit v1.2.3 From 0f2c7af45d7eef8455d7ad39c5326229bf19a2ed Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 17 Nov 2020 07:59:17 -0300 Subject: gpio: mxc: Convert the driver to DT-only Since 5.10-rc1 i.MX is a devicetree-only platform, so simplify the code by removing the unused non-DT support. Signed-off-by: Fabio Estevam Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20201117105917.27591-1-festevam@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mxc.c | 102 +++++++++++------------------------------------- 1 file changed, 22 insertions(+), 80 deletions(-) diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 643f4c557ac2..157106e1e438 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -24,13 +24,6 @@ #include #include -enum mxc_gpio_hwtype { - IMX1_GPIO, /* runs on i.mx1 */ - IMX21_GPIO, /* runs on i.mx21 and i.mx27 */ - IMX31_GPIO, /* runs on i.mx31 */ - IMX35_GPIO, /* runs on all other i.mx */ -}; - /* device type dependent stuff */ struct mxc_gpio_hwdata { unsigned dr_reg; @@ -68,6 +61,7 @@ struct mxc_gpio_port { u32 both_edges; struct mxc_gpio_reg_saved gpio_saved_reg; bool power_off; + const struct mxc_gpio_hwdata *hwdata; }; static struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = { @@ -115,48 +109,27 @@ static struct mxc_gpio_hwdata imx35_gpio_hwdata = { .fall_edge = 0x03, }; -static enum mxc_gpio_hwtype mxc_gpio_hwtype; -static struct mxc_gpio_hwdata *mxc_gpio_hwdata; - -#define GPIO_DR (mxc_gpio_hwdata->dr_reg) -#define GPIO_GDIR (mxc_gpio_hwdata->gdir_reg) -#define GPIO_PSR (mxc_gpio_hwdata->psr_reg) -#define GPIO_ICR1 (mxc_gpio_hwdata->icr1_reg) -#define GPIO_ICR2 (mxc_gpio_hwdata->icr2_reg) -#define GPIO_IMR (mxc_gpio_hwdata->imr_reg) -#define GPIO_ISR (mxc_gpio_hwdata->isr_reg) -#define GPIO_EDGE_SEL (mxc_gpio_hwdata->edge_sel_reg) - -#define GPIO_INT_LOW_LEV (mxc_gpio_hwdata->low_level) -#define GPIO_INT_HIGH_LEV (mxc_gpio_hwdata->high_level) -#define GPIO_INT_RISE_EDGE (mxc_gpio_hwdata->rise_edge) -#define GPIO_INT_FALL_EDGE (mxc_gpio_hwdata->fall_edge) +#define GPIO_DR (port->hwdata->dr_reg) +#define GPIO_GDIR (port->hwdata->gdir_reg) +#define GPIO_PSR (port->hwdata->psr_reg) +#define GPIO_ICR1 (port->hwdata->icr1_reg) +#define GPIO_ICR2 (port->hwdata->icr2_reg) +#define GPIO_IMR (port->hwdata->imr_reg) +#define GPIO_ISR (port->hwdata->isr_reg) +#define GPIO_EDGE_SEL (port->hwdata->edge_sel_reg) + +#define GPIO_INT_LOW_LEV (port->hwdata->low_level) +#define GPIO_INT_HIGH_LEV (port->hwdata->high_level) +#define GPIO_INT_RISE_EDGE (port->hwdata->rise_edge) +#define GPIO_INT_FALL_EDGE (port->hwdata->fall_edge) #define GPIO_INT_BOTH_EDGES 0x4 -static const struct platform_device_id mxc_gpio_devtype[] = { - { - .name = "imx1-gpio", - .driver_data = IMX1_GPIO, - }, { - .name = "imx21-gpio", - .driver_data = IMX21_GPIO, - }, { - .name = "imx31-gpio", - .driver_data = IMX31_GPIO, - }, { - .name = "imx35-gpio", - .driver_data = IMX35_GPIO, - }, { - /* sentinel */ - } -}; - static const struct of_device_id mxc_gpio_dt_ids[] = { - { .compatible = "fsl,imx1-gpio", .data = &mxc_gpio_devtype[IMX1_GPIO], }, - { .compatible = "fsl,imx21-gpio", .data = &mxc_gpio_devtype[IMX21_GPIO], }, - { .compatible = "fsl,imx31-gpio", .data = &mxc_gpio_devtype[IMX31_GPIO], }, - { .compatible = "fsl,imx35-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], }, - { .compatible = "fsl,imx7d-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], }, + { .compatible = "fsl,imx1-gpio", .data = &imx1_imx21_gpio_hwdata }, + { .compatible = "fsl,imx21-gpio", .data = &imx1_imx21_gpio_hwdata }, + { .compatible = "fsl,imx31-gpio", .data = &imx31_gpio_hwdata }, + { .compatible = "fsl,imx35-gpio", .data = &imx35_gpio_hwdata }, + { .compatible = "fsl,imx7d-gpio", .data = &imx35_gpio_hwdata }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, mxc_gpio_dt_ids); @@ -372,36 +345,6 @@ static int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base) return rv; } -static void mxc_gpio_get_hw(struct platform_device *pdev) -{ - const struct of_device_id *of_id = - of_match_device(mxc_gpio_dt_ids, &pdev->dev); - enum mxc_gpio_hwtype hwtype; - - if (of_id) - pdev->id_entry = of_id->data; - hwtype = pdev->id_entry->driver_data; - - if (mxc_gpio_hwtype) { - /* - * The driver works with a reasonable presupposition, - * that is all gpio ports must be the same type when - * running on one soc. - */ - BUG_ON(mxc_gpio_hwtype != hwtype); - return; - } - - if (hwtype == IMX35_GPIO) - mxc_gpio_hwdata = &imx35_gpio_hwdata; - else if (hwtype == IMX31_GPIO) - mxc_gpio_hwdata = &imx31_gpio_hwdata; - else - mxc_gpio_hwdata = &imx1_imx21_gpio_hwdata; - - mxc_gpio_hwtype = hwtype; -} - static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset) { struct mxc_gpio_port *port = gpiochip_get_data(gc); @@ -417,14 +360,14 @@ static int mxc_gpio_probe(struct platform_device *pdev) int irq_base; int err; - mxc_gpio_get_hw(pdev); - port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); if (!port) return -ENOMEM; port->dev = &pdev->dev; + port->hwdata = device_get_match_data(&pdev->dev); + port->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(port->base)) return PTR_ERR(port->base); @@ -461,7 +404,7 @@ static int mxc_gpio_probe(struct platform_device *pdev) writel(0, port->base + GPIO_IMR); writel(~0, port->base + GPIO_ISR); - if (mxc_gpio_hwtype == IMX21_GPIO) { + if (of_device_is_compatible(np, "fsl,imx21-gpio")) { /* * Setup one handler for all GPIO interrupts. Actually setting * the handler is needed only once, but doing it for every port @@ -596,7 +539,6 @@ static struct platform_driver mxc_gpio_driver = { .suppress_bind_attrs = true, }, .probe = mxc_gpio_probe, - .id_table = mxc_gpio_devtype, }; static int __init gpio_mxc_init(void) -- cgit v1.2.3 From f52d6d8b43e51cb2d0dbd60caf7d37150391182f Mon Sep 17 00:00:00 2001 From: Greentime Hu Date: Fri, 13 Nov 2020 10:33:55 +0800 Subject: gpio: sifive: To get gpio irq offset from device tree data We can get hwirq number of the gpio by its irq_data->hwirq so that we don't need to add more macros for different platforms. This patch is tested in SiFive Unleashed board and SiFive Unmatched board. Signed-off-by: Greentime Hu Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-sifive.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c index c54dd08f2cbf..630bddec48e5 100644 --- a/drivers/gpio/gpio-sifive.c +++ b/drivers/gpio/gpio-sifive.c @@ -29,7 +29,6 @@ #define SIFIVE_GPIO_OUTPUT_XOR 0x40 #define SIFIVE_GPIO_MAX 32 -#define SIFIVE_GPIO_IRQ_OFFSET 7 struct sifive_gpio { void __iomem *base; @@ -37,7 +36,7 @@ struct sifive_gpio { struct regmap *regs; unsigned long irq_state; unsigned int trigger[SIFIVE_GPIO_MAX]; - unsigned int irq_parent[SIFIVE_GPIO_MAX]; + unsigned int irq_number[SIFIVE_GPIO_MAX]; }; static void sifive_gpio_set_ie(struct sifive_gpio *chip, unsigned int offset) @@ -144,8 +143,12 @@ static int sifive_gpio_child_to_parent_hwirq(struct gpio_chip *gc, unsigned int *parent, unsigned int *parent_type) { + struct sifive_gpio *chip = gpiochip_get_data(gc); + struct irq_data *d = irq_get_irq_data(chip->irq_number[child]); + *parent_type = IRQ_TYPE_NONE; - *parent = child + SIFIVE_GPIO_IRQ_OFFSET; + *parent = irqd_to_hwirq(d); + return 0; } @@ -165,7 +168,7 @@ static int sifive_gpio_probe(struct platform_device *pdev) struct irq_domain *parent; struct gpio_irq_chip *girq; struct sifive_gpio *chip; - int ret, ngpio; + int ret, ngpio, i; chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); if (!chip) @@ -200,6 +203,9 @@ static int sifive_gpio_probe(struct platform_device *pdev) return -ENODEV; } + for (i = 0; i < ngpio; i++) + chip->irq_number[i] = platform_get_irq(pdev, i); + ret = bgpio_init(&chip->gc, dev, 4, chip->base + SIFIVE_GPIO_INPUT_VAL, chip->base + SIFIVE_GPIO_OUTPUT_VAL, -- cgit v1.2.3 From 1bfaf1299c38c1215c1ad1c196a8f39e658befec Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Sat, 19 Sep 2020 15:32:15 +0200 Subject: gpio: exar: add a newline after the copyright notice It's customary to have a newline between the copyright header and the includes. Add one to gpio-exar. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpio-exar.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index b1accfba017d..4202dd363a11 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -4,6 +4,7 @@ * * Copyright (C) 2015 Sudip Mukherjee */ + #include #include #include -- cgit v1.2.3 From 26ced453a519629278bfd0ac789a8a1786f71099 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Sat, 19 Sep 2020 15:36:56 +0200 Subject: gpio: exar: include idr.h This driver uses IDA APIs but doesn't include the relevant header. This fixes it. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpio-exar.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 4202dd363a11..1941ae533418 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 8e27c2aef8c321ce94c69f397f26d7647a1914d0 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 28 Sep 2020 14:35:51 +0200 Subject: gpio: exar: switch to a simpler IDA interface We don't need to specify any ranges when allocating IDs so we can switch to ida_alloc() and ida_free() instead of the ida_simple_ counterparts. ida_simple_get(ida, 0, 0, gfp) is equivalent to ida_alloc_range(ida, 0, UINT_MAX, gfp) which is equivalent to ida_alloc(ida, gfp). Note: IDR will never actually allocate an ID larger than INT_MAX. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpio-exar.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 1941ae533418..752e8437ff80 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -149,7 +149,7 @@ static int gpio_exar_probe(struct platform_device *pdev) mutex_init(&exar_gpio->lock); - index = ida_simple_get(&ida_index, 0, 0, GFP_KERNEL); + index = ida_alloc(&ida_index, GFP_KERNEL); if (index < 0) { ret = index; goto err_mutex_destroy; @@ -179,7 +179,7 @@ static int gpio_exar_probe(struct platform_device *pdev) return 0; err_destroy: - ida_simple_remove(&ida_index, index); + ida_free(&ida_index, index); err_mutex_destroy: mutex_destroy(&exar_gpio->lock); return ret; @@ -189,7 +189,7 @@ static int gpio_exar_remove(struct platform_device *pdev) { struct exar_gpio_chip *exar_gpio = platform_get_drvdata(pdev); - ida_simple_remove(&ida_index, exar_gpio->index); + ida_free(&ida_index, exar_gpio->index); mutex_destroy(&exar_gpio->lock); return 0; -- cgit v1.2.3 From 0c2c7e1323b44548c10f899df565e4c4154600f1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 28 Sep 2020 16:34:37 +0200 Subject: gpio: exar: use a helper variable for &pdev->dev It's more elegant to use a helper local variable to store the address of the underlying struct device than to dereference pdev everywhere. It also has the benefit of avoiding unnecessary line breaks. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpio-exar.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 752e8437ff80..db366d85b6b4 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -120,7 +120,8 @@ static int exar_direction_input(struct gpio_chip *chip, unsigned int offset) static int gpio_exar_probe(struct platform_device *pdev) { - struct pci_dev *pcidev = to_pci_dev(pdev->dev.parent); + struct device *dev = &pdev->dev; + struct pci_dev *pcidev = to_pci_dev(dev->parent); struct exar_gpio_chip *exar_gpio; u32 first_pin, ngpios; void __iomem *p; @@ -134,16 +135,15 @@ static int gpio_exar_probe(struct platform_device *pdev) if (!p) return -ENOMEM; - ret = device_property_read_u32(&pdev->dev, "exar,first-pin", - &first_pin); + ret = device_property_read_u32(dev, "exar,first-pin", &first_pin); if (ret) return ret; - ret = device_property_read_u32(&pdev->dev, "ngpios", &ngpios); + ret = device_property_read_u32(dev, "ngpios", &ngpios); if (ret) return ret; - exar_gpio = devm_kzalloc(&pdev->dev, sizeof(*exar_gpio), GFP_KERNEL); + exar_gpio = devm_kzalloc(dev, sizeof(*exar_gpio), GFP_KERNEL); if (!exar_gpio) return -ENOMEM; @@ -157,7 +157,7 @@ static int gpio_exar_probe(struct platform_device *pdev) sprintf(exar_gpio->name, "exar_gpio%d", index); exar_gpio->gpio_chip.label = exar_gpio->name; - exar_gpio->gpio_chip.parent = &pdev->dev; + exar_gpio->gpio_chip.parent = dev; exar_gpio->gpio_chip.direction_output = exar_direction_output; exar_gpio->gpio_chip.direction_input = exar_direction_input; exar_gpio->gpio_chip.get_direction = exar_get_direction; @@ -169,8 +169,7 @@ static int gpio_exar_probe(struct platform_device *pdev) exar_gpio->index = index; exar_gpio->first_pin = first_pin; - ret = devm_gpiochip_add_data(&pdev->dev, - &exar_gpio->gpio_chip, exar_gpio); + ret = devm_gpiochip_add_data(dev, &exar_gpio->gpio_chip, exar_gpio); if (ret) goto err_destroy; -- cgit v1.2.3 From 696868d0a79c211b51d0d0f7a1e6805e12d7fb42 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Wed, 30 Sep 2020 10:20:10 +0200 Subject: gpio: exar: unduplicate address and offset computation Provide and use helpers for calculating the register address and bit offset instead of hand coding it in every function. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpio-exar.c | 40 ++++++++++++++++++++++++++++------------ 1 file changed, 28 insertions(+), 12 deletions(-) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index db366d85b6b4..28b0b4b5fa35 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -33,6 +33,26 @@ struct exar_gpio_chip { unsigned int first_pin; }; +static unsigned int +exar_offset_to_sel_addr(struct exar_gpio_chip *exar_gpio, unsigned int offset) +{ + return (offset + exar_gpio->first_pin) / 8 ? EXAR_OFFSET_MPIOSEL_HI + : EXAR_OFFSET_MPIOSEL_LO; +} + +static unsigned int +exar_offset_to_lvl_addr(struct exar_gpio_chip *exar_gpio, unsigned int offset) +{ + return (offset + exar_gpio->first_pin) / 8 ? EXAR_OFFSET_MPIOLVL_HI + : EXAR_OFFSET_MPIOLVL_LO; +} + +static unsigned int +exar_offset_to_bit(struct exar_gpio_chip *exar_gpio, unsigned int offset) +{ + return (offset + exar_gpio->first_pin) % 8; +} + static void exar_update(struct gpio_chip *chip, unsigned int reg, int val, unsigned int offset) { @@ -52,9 +72,8 @@ static int exar_set_direction(struct gpio_chip *chip, int direction, unsigned int offset) { struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); - unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? - EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; - unsigned int bit = (offset + exar_gpio->first_pin) % 8; + unsigned int addr = exar_offset_to_sel_addr(exar_gpio, offset); + unsigned int bit = exar_offset_to_bit(exar_gpio, offset); exar_update(chip, addr, direction, bit); return 0; @@ -75,9 +94,8 @@ static int exar_get(struct gpio_chip *chip, unsigned int reg) static int exar_get_direction(struct gpio_chip *chip, unsigned int offset) { struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); - unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? - EXAR_OFFSET_MPIOSEL_HI : EXAR_OFFSET_MPIOSEL_LO; - unsigned int bit = (offset + exar_gpio->first_pin) % 8; + unsigned int addr = exar_offset_to_sel_addr(exar_gpio, offset); + unsigned int bit = exar_offset_to_bit(exar_gpio, offset); if (exar_get(chip, addr) & BIT(bit)) return GPIO_LINE_DIRECTION_IN; @@ -88,9 +106,8 @@ static int exar_get_direction(struct gpio_chip *chip, unsigned int offset) static int exar_get_value(struct gpio_chip *chip, unsigned int offset) { struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); - unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? - EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; - unsigned int bit = (offset + exar_gpio->first_pin) % 8; + unsigned int addr = exar_offset_to_lvl_addr(exar_gpio, offset); + unsigned int bit = exar_offset_to_bit(exar_gpio, offset); return !!(exar_get(chip, addr) & BIT(bit)); } @@ -99,9 +116,8 @@ static void exar_set_value(struct gpio_chip *chip, unsigned int offset, int value) { struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); - unsigned int addr = (offset + exar_gpio->first_pin) / 8 ? - EXAR_OFFSET_MPIOLVL_HI : EXAR_OFFSET_MPIOLVL_LO; - unsigned int bit = (offset + exar_gpio->first_pin) % 8; + unsigned int addr = exar_offset_to_lvl_addr(exar_gpio, offset); + unsigned int bit = exar_offset_to_bit(exar_gpio, offset); exar_update(chip, addr, value, bit); } -- cgit v1.2.3 From 36fb7218e87833b17e3042e77f3b102c75129e8f Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 28 Sep 2020 17:00:26 +0200 Subject: gpio: exar: switch to using regmap We can simplify the code in gpio-exar by using regmap. This allows us to drop the mutex (regmap provides its own locking) and we can also reuse regmap's bit operations instead of implementing our own update function. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/Kconfig | 1 + drivers/gpio/gpio-exar.c | 91 ++++++++++++++++++++---------------------------- 2 files changed, 38 insertions(+), 54 deletions(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5d4de5cd6759..253a61ec9645 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -255,6 +255,7 @@ config GPIO_EP93XX config GPIO_EXAR tristate "Support for GPIO pins on XR17V352/354/358" depends on SERIAL_8250_EXAR + select REGMAP_MMIO help Selecting this option will enable handling of GPIO pins present on Exar XR17V352/354/358 chips. diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 28b0b4b5fa35..2fdca872c7c0 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -14,6 +14,7 @@ #include #include #include +#include #define EXAR_OFFSET_MPIOLVL_LO 0x90 #define EXAR_OFFSET_MPIOSEL_LO 0x93 @@ -26,9 +27,8 @@ static DEFINE_IDA(ida_index); struct exar_gpio_chip { struct gpio_chip gpio_chip; - struct mutex lock; + struct regmap *regmap; int index; - void __iomem *regs; char name[20]; unsigned int first_pin; }; @@ -53,51 +53,13 @@ exar_offset_to_bit(struct exar_gpio_chip *exar_gpio, unsigned int offset) return (offset + exar_gpio->first_pin) % 8; } -static void exar_update(struct gpio_chip *chip, unsigned int reg, int val, - unsigned int offset) -{ - struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); - int temp; - - mutex_lock(&exar_gpio->lock); - temp = readb(exar_gpio->regs + reg); - temp &= ~BIT(offset); - if (val) - temp |= BIT(offset); - writeb(temp, exar_gpio->regs + reg); - mutex_unlock(&exar_gpio->lock); -} - -static int exar_set_direction(struct gpio_chip *chip, int direction, - unsigned int offset) -{ - struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); - unsigned int addr = exar_offset_to_sel_addr(exar_gpio, offset); - unsigned int bit = exar_offset_to_bit(exar_gpio, offset); - - exar_update(chip, addr, direction, bit); - return 0; -} - -static int exar_get(struct gpio_chip *chip, unsigned int reg) -{ - struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); - int value; - - mutex_lock(&exar_gpio->lock); - value = readb(exar_gpio->regs + reg); - mutex_unlock(&exar_gpio->lock); - - return value; -} - static int exar_get_direction(struct gpio_chip *chip, unsigned int offset) { struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); unsigned int addr = exar_offset_to_sel_addr(exar_gpio, offset); unsigned int bit = exar_offset_to_bit(exar_gpio, offset); - if (exar_get(chip, addr) & BIT(bit)) + if (regmap_test_bits(exar_gpio->regmap, addr, BIT(bit))) return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_OUT; @@ -109,7 +71,7 @@ static int exar_get_value(struct gpio_chip *chip, unsigned int offset) unsigned int addr = exar_offset_to_lvl_addr(exar_gpio, offset); unsigned int bit = exar_offset_to_bit(exar_gpio, offset); - return !!(exar_get(chip, addr) & BIT(bit)); + return !!(regmap_test_bits(exar_gpio->regmap, addr, BIT(bit))); } static void exar_set_value(struct gpio_chip *chip, unsigned int offset, @@ -119,21 +81,42 @@ static void exar_set_value(struct gpio_chip *chip, unsigned int offset, unsigned int addr = exar_offset_to_lvl_addr(exar_gpio, offset); unsigned int bit = exar_offset_to_bit(exar_gpio, offset); - exar_update(chip, addr, value, bit); + if (value) + regmap_set_bits(exar_gpio->regmap, addr, BIT(bit)); + else + regmap_clear_bits(exar_gpio->regmap, addr, BIT(bit)); } static int exar_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = exar_offset_to_sel_addr(exar_gpio, offset); + unsigned int bit = exar_offset_to_bit(exar_gpio, offset); + exar_set_value(chip, offset, value); - return exar_set_direction(chip, 0, offset); + regmap_clear_bits(exar_gpio->regmap, addr, BIT(bit)); + + return 0; } static int exar_direction_input(struct gpio_chip *chip, unsigned int offset) { - return exar_set_direction(chip, 1, offset); + struct exar_gpio_chip *exar_gpio = gpiochip_get_data(chip); + unsigned int addr = exar_offset_to_sel_addr(exar_gpio, offset); + unsigned int bit = exar_offset_to_bit(exar_gpio, offset); + + regmap_set_bits(exar_gpio->regmap, addr, BIT(bit)); + + return 0; } +static const struct regmap_config exar_regmap_config = { + .name = "exar-gpio", + .reg_bits = 16, + .val_bits = 8, +}; + static int gpio_exar_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -163,13 +146,17 @@ static int gpio_exar_probe(struct platform_device *pdev) if (!exar_gpio) return -ENOMEM; - mutex_init(&exar_gpio->lock); + /* + * We don't need to check the return values of mmio regmap operations (unless + * the regmap has a clock attached which is not the case here). + */ + exar_gpio->regmap = devm_regmap_init_mmio(dev, p, &exar_regmap_config); + if (IS_ERR(exar_gpio->regmap)) + return PTR_ERR(exar_gpio->regmap); index = ida_alloc(&ida_index, GFP_KERNEL); - if (index < 0) { - ret = index; - goto err_mutex_destroy; - } + if (index < 0) + return index; sprintf(exar_gpio->name, "exar_gpio%d", index); exar_gpio->gpio_chip.label = exar_gpio->name; @@ -181,7 +168,6 @@ static int gpio_exar_probe(struct platform_device *pdev) exar_gpio->gpio_chip.set = exar_set_value; exar_gpio->gpio_chip.base = -1; exar_gpio->gpio_chip.ngpio = ngpios; - exar_gpio->regs = p; exar_gpio->index = index; exar_gpio->first_pin = first_pin; @@ -195,8 +181,6 @@ static int gpio_exar_probe(struct platform_device *pdev) err_destroy: ida_free(&ida_index, index); -err_mutex_destroy: - mutex_destroy(&exar_gpio->lock); return ret; } @@ -205,7 +189,6 @@ static int gpio_exar_remove(struct platform_device *pdev) struct exar_gpio_chip *exar_gpio = platform_get_drvdata(pdev); ida_free(&ida_index, exar_gpio->index); - mutex_destroy(&exar_gpio->lock); return 0; } -- cgit v1.2.3 From 5300ebb695fa0672589ab191062392f686fca75d Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Tue, 20 Oct 2020 09:24:04 +0200 Subject: gpio: exar: use devm action for freeing the IDA and drop remove() We can simplify the error path in probe() and drop remove() entirely if we provide a devm action for freeing the device ID. Signed-off-by: Bartosz Golaszewski Reviewed-by: Andy Shevchenko --- drivers/gpio/gpio-exar.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index 2fdca872c7c0..d37de78247a6 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -111,6 +111,13 @@ static int exar_direction_input(struct gpio_chip *chip, unsigned int offset) return 0; } +static void exar_devm_ida_free(void *data) +{ + struct exar_gpio_chip *exar_gpio = data; + + ida_free(&ida_index, exar_gpio->index); +} + static const struct regmap_config exar_regmap_config = { .name = "exar-gpio", .reg_bits = 16, @@ -158,6 +165,10 @@ static int gpio_exar_probe(struct platform_device *pdev) if (index < 0) return index; + ret = devm_add_action_or_reset(dev, exar_devm_ida_free, exar_gpio); + if (ret) + return ret; + sprintf(exar_gpio->name, "exar_gpio%d", index); exar_gpio->gpio_chip.label = exar_gpio->name; exar_gpio->gpio_chip.parent = dev; @@ -173,29 +184,15 @@ static int gpio_exar_probe(struct platform_device *pdev) ret = devm_gpiochip_add_data(dev, &exar_gpio->gpio_chip, exar_gpio); if (ret) - goto err_destroy; + return ret; platform_set_drvdata(pdev, exar_gpio); - return 0; - -err_destroy: - ida_free(&ida_index, index); - return ret; -} - -static int gpio_exar_remove(struct platform_device *pdev) -{ - struct exar_gpio_chip *exar_gpio = platform_get_drvdata(pdev); - - ida_free(&ida_index, exar_gpio->index); - return 0; } static struct platform_driver gpio_exar_driver = { .probe = gpio_exar_probe, - .remove = gpio_exar_remove, .driver = { .name = DRIVER_NAME, }, -- cgit v1.2.3 From 7d3615ae401146ab40115546667e8ebc0d5c7d73 Mon Sep 17 00:00:00 2001 From: Damien Le Moal Date: Mon, 30 Nov 2020 19:57:49 +0900 Subject: gpio: dwapb: Remove unnecessary error message In dwapb_get_reset(), if devm_reset_control_get_optional_shared() fails, an error message is printed even if the failure is the benign EPROBE_DEFER error due to the reset controller not yet being initialized. Use dev_err_probe() to handle devm_reset_control_get_optional_shared() errors to avoid unnecessarilly printing an error message for the deferred probe error. Signed-off-by: Damien Le Moal Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-dwapb.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-dwapb.c b/drivers/gpio/gpio-dwapb.c index a5b326754124..7dbcc702fa34 100644 --- a/drivers/gpio/gpio-dwapb.c +++ b/drivers/gpio/gpio-dwapb.c @@ -616,10 +616,9 @@ static int dwapb_get_reset(struct dwapb_gpio *gpio) int err; gpio->rst = devm_reset_control_get_optional_shared(gpio->dev, NULL); - if (IS_ERR(gpio->rst)) { - dev_err(gpio->dev, "Cannot get reset descriptor\n"); - return PTR_ERR(gpio->rst); - } + if (IS_ERR(gpio->rst)) + return dev_err_probe(gpio->dev, PTR_ERR(gpio->rst), + "Cannot get reset descriptor\n"); err = reset_control_deassert(gpio->rst); if (err) { -- cgit v1.2.3 From 0aa42370084cb8c87f5485e04bee50612d4db644 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Thu, 19 Nov 2020 16:21:04 +0200 Subject: gpio: xra1403: remove unneeded spi_set_drvdata() There is no matching spi_get_drvdata() call in the driver, so there is no need to do spi_set_drvdata(). This looks like it probably was copied from a driver that used both spi_set_drvdata() & spi_get_drvdata(). Signed-off-by: Alexandru Ardelean Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-xra1403.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/drivers/gpio/gpio-xra1403.c b/drivers/gpio/gpio-xra1403.c index e2cac12092af..49c878cfd5c6 100644 --- a/drivers/gpio/gpio-xra1403.c +++ b/drivers/gpio/gpio-xra1403.c @@ -186,15 +186,7 @@ static int xra1403_probe(struct spi_device *spi) return ret; } - ret = devm_gpiochip_add_data(&spi->dev, &xra->chip, xra); - if (ret < 0) { - dev_err(&spi->dev, "Unable to register gpiochip\n"); - return ret; - } - - spi_set_drvdata(spi, xra); - - return 0; + return devm_gpiochip_add_data(&spi->dev, &xra->chip, xra); } static const struct spi_device_id xra1403_ids[] = { -- cgit v1.2.3 From 2ae136a34fce9bbeb7c582449b03bd4e05aac565 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Wed, 18 Nov 2020 16:31:49 +0200 Subject: gpio: omap: handle deferred probe with dev_err_probe() for gpiochip_add_data() The gpiochip_add_data() may return -EPROBE_DEFER which is not handled properly by TI GPIO driver and causes unnecessary boot log messages. Hence, add proper deferred probe handling with new dev_err_probe() API. Signed-off-by: Grygorii Strashko Acked-by: Tony Lindgren Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-omap.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c index 6d59e3a43761..e0a60aca3d27 100644 --- a/drivers/gpio/gpio-omap.c +++ b/drivers/gpio/gpio-omap.c @@ -1049,11 +1049,8 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct irq_chip *irqc) irq->first = irq_base; ret = gpiochip_add_data(&bank->chip, bank); - if (ret) { - dev_err(bank->chip.parent, - "Could not register gpio chip %d\n", ret); - return ret; - } + if (ret) + return dev_err_probe(bank->chip.parent, ret, "Could not register gpio chip\n"); ret = devm_request_irq(bank->chip.parent, bank->irq, omap_gpio_irq_handler, -- cgit v1.2.3 From bc5d098432225e381328be0301948a6cb34f11e3 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 19 Nov 2020 11:07:39 -0600 Subject: gpiolib: acpi: Fix fall-through warnings for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a warning by explicitly adding a break statement instead of letting the code fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Reviewed-by: Andy Shevchenko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib-acpi.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index 834a12f3219e..23fa9df8241d 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -548,6 +548,7 @@ acpi_gpio_to_gpiod_flags(const struct acpi_resource_gpio *agpio) default: break; } + break; default: break; } -- cgit v1.2.3 From d49ee56292d7d11586efd0b4feaafafc36d54101 Mon Sep 17 00:00:00 2001 From: "Gustavo A. R. Silva" Date: Thu, 19 Nov 2020 11:09:01 -0600 Subject: gpio: ath79: Fix fall-through warning for Clang In preparation to enable -Wimplicit-fallthrough for Clang, fix a warning by explicitly adding a fallthrough pseudo-keyword to indicate that the code is intended to fall through to the next case. Link: https://github.com/KSPP/linux/issues/115 Signed-off-by: Gustavo A. R. Silva Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-ath79.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpio-ath79.c b/drivers/gpio/gpio-ath79.c index d5359341cc6b..678ddd375891 100644 --- a/drivers/gpio/gpio-ath79.c +++ b/drivers/gpio/gpio-ath79.c @@ -123,6 +123,7 @@ static int ath79_gpio_irq_set_type(struct irq_data *data, switch (flow_type) { case IRQ_TYPE_EDGE_RISING: polarity |= mask; + fallthrough; case IRQ_TYPE_EDGE_FALLING: case IRQ_TYPE_EDGE_BOTH: break; -- cgit v1.2.3 From 3cc1fb73993905b598da3802f87ac59411c52516 Mon Sep 17 00:00:00 2001 From: Grygorii Strashko Date: Wed, 18 Nov 2020 16:29:17 +0200 Subject: gpiolib: do not print err message for EPROBE_DEFER The gpiochip may have dependencies from pinmux and so got deferred. Now it will print error message every time -EPROBE_DEFER is returned which is unnecessary: "gpiochip_add_data_with_key: GPIOs 0..31 (gpio-0-31) failed to register, -517" Hence, do suppress error message for -EPROBE_DEFER case. Signed-off-by: Grygorii Strashko Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpiolib.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index a72d6293435f..53aa1a2feed3 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -759,9 +759,11 @@ err_free_ida: ida_free(&gpio_ida, gdev->id); err_free_gdev: /* failures here can mean systems won't boot... */ - pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__, - gdev->base, gdev->base + gdev->ngpio - 1, - gc->label ? : "generic", ret); + if (ret != -EPROBE_DEFER) { + pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__, + gdev->base, gdev->base + gdev->ngpio - 1, + gc->label ? : "generic", ret); + } kfree(gdev); return ret; } -- cgit v1.2.3 From 64b19f6abedc0b7c8087b64e49f293bc4603ac23 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Wed, 2 Dec 2020 09:15:33 +0200 Subject: gpio: mvebu: update Armada XP per-CPU comment Commit 2233bf7a92e ("gpio: mvebu: switch to regmap for register access") introduced percpu_regs to replace percpu_membase. Update the comment to match. Cc: Thomas Petazzoni Fixes: 2233bf7a92e7 ("gpio: mvebu: switch to regmap for register access") Reviewed-by: Andrew Lunn Signed-off-by: Baruch Siach Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mvebu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 433e2c3f3fd5..bdc4d813a42e 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -78,8 +78,7 @@ /* * The Armada XP has per-CPU registers for interrupt cause, interrupt - * mask and interrupt level mask. Those are relative to the - * percpu_membase. + * mask and interrupt level mask. Those are in percpu_regs range. */ #define GPIO_EDGE_CAUSE_ARMADAXP_OFF(cpu) ((cpu) * 0x4) #define GPIO_EDGE_MASK_ARMADAXP_OFF(cpu) (0x10 + (cpu) * 0x4) -- cgit v1.2.3 From 48f32a835373779c6357accd36cc34a4080b5065 Mon Sep 17 00:00:00 2001 From: Baruch Siach Date: Wed, 2 Dec 2020 09:15:34 +0200 Subject: gpio: mvebu: switch pwm duration registers to regmap Commit 2233bf7a92e ("gpio: mvebu: switch to regmap for register access") changed most readl/writel registers access calls to the regmap API in preparation for Armada 7K/8K support. PWM duration registers were left using readl/writel, as the driver does not support PWM for Armada 7K/8K. Switch PWM duration registers to regmap as first step in adding Armada 7K/8K PWM functionality support. Reviewed-by: Andrew Lunn Signed-off-by: Baruch Siach Signed-off-by: Bartosz Golaszewski --- drivers/gpio/gpio-mvebu.c | 68 +++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 32 deletions(-) diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index bdc4d813a42e..946571e70928 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -92,7 +92,7 @@ #define MVEBU_MAX_GPIO_PER_BANK 32 struct mvebu_pwm { - void __iomem *membase; + struct regmap *regs; unsigned long clk_rate; struct gpio_desc *gpiod; struct pwm_chip chip; @@ -278,17 +278,17 @@ mvebu_gpio_write_level_mask(struct mvebu_gpio_chip *mvchip, u32 val) } /* - * Functions returning addresses of individual registers for a given + * Functions returning offsets of individual registers for a given * PWM controller. */ -static void __iomem *mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm) +static unsigned int mvebu_pwmreg_blink_on_duration(struct mvebu_pwm *mvpwm) { - return mvpwm->membase + PWM_BLINK_ON_DURATION_OFF; + return PWM_BLINK_ON_DURATION_OFF; } -static void __iomem *mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm) +static unsigned int mvebu_pwmreg_blink_off_duration(struct mvebu_pwm *mvpwm) { - return mvpwm->membase + PWM_BLINK_OFF_DURATION_OFF; + return PWM_BLINK_OFF_DURATION_OFF; } /* @@ -599,6 +599,13 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc) chained_irq_exit(chip, desc); } +static const struct regmap_config mvebu_gpio_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .fast_io = true, +}; + /* * Functions implementing the pwm_chip methods */ @@ -659,9 +666,8 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, spin_lock_irqsave(&mvpwm->lock, flags); - val = (unsigned long long) - readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm)); - val *= NSEC_PER_SEC; + regmap_read(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), &u); + val = (unsigned long long) u * NSEC_PER_SEC; do_div(val, mvpwm->clk_rate); if (val > UINT_MAX) state->duty_cycle = UINT_MAX; @@ -670,9 +676,8 @@ static void mvebu_pwm_get_state(struct pwm_chip *chip, else state->duty_cycle = 1; - val = (unsigned long long) - readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm)); - val *= NSEC_PER_SEC; + regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), &u); + val = (unsigned long long) u * NSEC_PER_SEC; do_div(val, mvpwm->clk_rate); if (val < state->duty_cycle) { state->period = 1; @@ -725,8 +730,8 @@ static int mvebu_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, spin_lock_irqsave(&mvpwm->lock, flags); - writel_relaxed(on, mvebu_pwmreg_blink_on_duration(mvpwm)); - writel_relaxed(off, mvebu_pwmreg_blink_off_duration(mvpwm)); + regmap_write(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), on); + regmap_write(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), off); if (state->enabled) mvebu_gpio_blink(&mvchip->chip, pwm->hwpwm, 1); else @@ -751,10 +756,10 @@ static void __maybe_unused mvebu_pwm_suspend(struct mvebu_gpio_chip *mvchip) regmap_read(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, &mvpwm->blink_select); - mvpwm->blink_on_duration = - readl_relaxed(mvebu_pwmreg_blink_on_duration(mvpwm)); - mvpwm->blink_off_duration = - readl_relaxed(mvebu_pwmreg_blink_off_duration(mvpwm)); + regmap_read(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), + &mvpwm->blink_on_duration); + regmap_read(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), + &mvpwm->blink_off_duration); } static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) @@ -763,10 +768,10 @@ static void __maybe_unused mvebu_pwm_resume(struct mvebu_gpio_chip *mvchip) regmap_write(mvchip->regs, GPIO_BLINK_CNT_SELECT_OFF + mvchip->offset, mvpwm->blink_select); - writel_relaxed(mvpwm->blink_on_duration, - mvebu_pwmreg_blink_on_duration(mvpwm)); - writel_relaxed(mvpwm->blink_off_duration, - mvebu_pwmreg_blink_off_duration(mvpwm)); + regmap_write(mvpwm->regs, mvebu_pwmreg_blink_on_duration(mvpwm), + mvpwm->blink_on_duration); + regmap_write(mvpwm->regs, mvebu_pwmreg_blink_off_duration(mvpwm), + mvpwm->blink_off_duration); } static int mvebu_pwm_probe(struct platform_device *pdev, @@ -775,6 +780,7 @@ static int mvebu_pwm_probe(struct platform_device *pdev, { struct device *dev = &pdev->dev; struct mvebu_pwm *mvpwm; + void __iomem *base; u32 set; if (!of_device_is_compatible(mvchip->chip.of_node, @@ -812,9 +818,14 @@ static int mvebu_pwm_probe(struct platform_device *pdev, mvchip->mvpwm = mvpwm; mvpwm->mvchip = mvchip; - mvpwm->membase = devm_platform_ioremap_resource_byname(pdev, "pwm"); - if (IS_ERR(mvpwm->membase)) - return PTR_ERR(mvpwm->membase); + base = devm_platform_ioremap_resource_byname(pdev, "pwm"); + if (IS_ERR(base)) + return PTR_ERR(base); + + mvpwm->regs = devm_regmap_init_mmio(&pdev->dev, base, + &mvebu_gpio_regmap_config); + if (IS_ERR(mvpwm->regs)) + return PTR_ERR(mvpwm->regs); mvpwm->clk_rate = clk_get_rate(mvchip->clk); if (!mvpwm->clk_rate) { @@ -1021,13 +1032,6 @@ static int mvebu_gpio_resume(struct platform_device *pdev) return 0; } -static const struct regmap_config mvebu_gpio_regmap_config = { - .reg_bits = 32, - .reg_stride = 4, - .val_bits = 32, - .fast_io = true, -}; - static int mvebu_gpio_probe_raw(struct platform_device *pdev, struct mvebu_gpio_chip *mvchip) { -- cgit v1.2.3 From 3b4feb21158f873269ff3fbe2fe8d23a88d64b24 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 10 Nov 2020 15:27:24 +0100 Subject: gpio: sysfs: Enforce character device If users select sysfs support they get the character device as well so that end-users cannot complain that they "only have sysfs on my system". They should have the character device at all times. If someone is in so dire need of stripping out the character device while still enabling the sysfs ABI they can very well patch the kernel. Also only show this obsolete option to expert users. Signed-off-by: Linus Walleij Link: https://lore.kernel.org/r/20201110142724.14760-1-linus.walleij@linaro.org --- drivers/gpio/Kconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5d4de5cd6759..4dd566f7ea39 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -59,8 +59,9 @@ config DEBUG_GPIO that are most common when setting up new platforms or boards. config GPIO_SYSFS - bool "/sys/class/gpio/... (sysfs interface)" + bool "/sys/class/gpio/... (sysfs interface)" if EXPERT depends on SYSFS + select GPIO_CDEV # We need to encourage the new ABI help Say Y here to add the legacy sysfs interface for GPIOs. -- cgit v1.2.3 From 011a78c1942ed6441880786d96cb90229e3ab0c9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 17 Nov 2020 22:33:50 +0100 Subject: gpio: sifive: Set affinity callback to parent This assigns the .irq_set_affinity to the parent callback. I assume the sifive GPIO can be used in systems with SMP. I used the pattern making the hirerarchy tolerant for missing parent as in Marc's earlier patches. Suggested-by: Marc Zyngier Signed-off-by: Linus Walleij Cc: Yash Shah Cc: Wesley W. Terpstra Link: https://lore.kernel.org/r/20201117213351.249668-1-linus.walleij@linaro.org --- drivers/gpio/gpio-sifive.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpio/gpio-sifive.c b/drivers/gpio/gpio-sifive.c index d5eb9ca11901..c7d47e42fa2b 100644 --- a/drivers/gpio/gpio-sifive.c +++ b/drivers/gpio/gpio-sifive.c @@ -128,6 +128,16 @@ static void sifive_gpio_irq_eoi(struct irq_data *d) irq_chip_eoi_parent(d); } +static int sifive_gpio_irq_set_affinity(struct irq_data *data, + const struct cpumask *dest, + bool force) +{ + if (data->parent_data) + return irq_chip_set_affinity_parent(data, dest, force); + + return -EINVAL; +} + static struct irq_chip sifive_gpio_irqchip = { .name = "sifive-gpio", .irq_set_type = sifive_gpio_irq_set_type, @@ -136,6 +146,7 @@ static struct irq_chip sifive_gpio_irqchip = { .irq_enable = sifive_gpio_irq_enable, .irq_disable = sifive_gpio_irq_disable, .irq_eoi = sifive_gpio_irq_eoi, + .irq_set_affinity = sifive_gpio_irq_set_affinity, }; static int sifive_gpio_child_to_parent_hwirq(struct gpio_chip *gc, -- cgit v1.2.3 From c4e1f7d92cd609e4929b2b3d8abe5eb21b9823ef Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Tue, 17 Nov 2020 22:33:51 +0100 Subject: gpio: tegra186: Set affinity callback to parent This assigns the .irq_set_affinity to the parent callback. I assume the Tegra186 is an SMP system so this would be beneficial. I used the pattern making the hirerarchy tolerant for missing parent as in Marc's earlier patch. Suggested-by: Marc Zyngier Signed-off-by: Linus Walleij Cc: Thierry Reding Cc: Vidya Sagar Link: https://lore.kernel.org/r/20201117213351.249668-2-linus.walleij@linaro.org --- drivers/gpio/gpio-tegra186.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c index 9500074b1f1b..286e0b1f46e4 100644 --- a/drivers/gpio/gpio-tegra186.c +++ b/drivers/gpio/gpio-tegra186.c @@ -444,6 +444,16 @@ static int tegra186_irq_set_wake(struct irq_data *data, unsigned int on) return 0; } +static int tegra186_irq_set_affinity(struct irq_data *data, + const struct cpumask *dest, + bool force) +{ + if (data->parent_data) + return irq_chip_set_affinity_parent(data, dest, force); + + return -EINVAL; +} + static void tegra186_gpio_irq(struct irq_desc *desc) { struct tegra_gpio *gpio = irq_desc_get_handler_data(desc); @@ -690,6 +700,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev) gpio->intc.irq_unmask = tegra186_irq_unmask; gpio->intc.irq_set_type = tegra186_irq_set_type; gpio->intc.irq_set_wake = tegra186_irq_set_wake; + gpio->intc.irq_set_affinity = tegra186_irq_set_affinity; irq = &gpio->gpio.irq; irq->chip = &gpio->intc; -- cgit v1.2.3 From 105e051f1ae4cf1e94110a834987fdc78673f0c8 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Wed, 18 Nov 2020 16:19:38 -0300 Subject: gpio: mxs: Remove unused .id_table support mxs is a devicetree-only platform and hence it does not make use of the id_table mechanism. Get rid of the .id_table as it is unused. Signed-off-by: Fabio Estevam Link: https://lore.kernel.org/r/20201118191938.32693-1-festevam@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-mxs.c | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index c4a314c68555..dfc0c1eb1b33 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -254,19 +254,6 @@ static int mxs_gpio_get_direction(struct gpio_chip *gc, unsigned offset) return GPIO_LINE_DIRECTION_IN; } -static const struct platform_device_id mxs_gpio_ids[] = { - { - .name = "imx23-gpio", - .driver_data = IMX23_GPIO, - }, { - .name = "imx28-gpio", - .driver_data = IMX28_GPIO, - }, { - /* sentinel */ - } -}; -MODULE_DEVICE_TABLE(platform, mxs_gpio_ids); - static const struct of_device_id mxs_gpio_dt_ids[] = { { .compatible = "fsl,imx23-gpio", .data = (void *) IMX23_GPIO, }, { .compatible = "fsl,imx28-gpio", .data = (void *) IMX28_GPIO, }, @@ -370,7 +357,6 @@ static struct platform_driver mxs_gpio_driver = { .suppress_bind_attrs = true, }, .probe = mxs_gpio_probe, - .id_table = mxs_gpio_ids, }; static int __init mxs_gpio_init(void) -- cgit v1.2.3 From a0de695819f63b02645e0c1c8d493324c02b0eb0 Mon Sep 17 00:00:00 2001 From: Alexandre Courbot Date: Sun, 22 Nov 2020 18:25:48 +0900 Subject: Documentation: gpio: fix typo and unclear legacy API section The "Interacting With the Legacy GPIO Subsystem" of the documentation was unclear at best, and even included a sentence that seems to say the opposite of what it should say about the lifetime of the return value of the conversion functions. Try to clarify things a bit and hopefully make that section more readable. Reported-by: Andy Shevchenko BugLink: https://stackoverflow.com/q/64455505/2511795 Signed-off-by: Alexandre Courbot Link: https://lore.kernel.org/r/20201122092548.61979-1-gnurou@gmail.com Signed-off-by: Linus Walleij --- Documentation/driver-api/gpio/consumer.rst | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst index 423492d125b9..173e4c7b037d 100644 --- a/Documentation/driver-api/gpio/consumer.rst +++ b/Documentation/driver-api/gpio/consumer.rst @@ -440,18 +440,20 @@ For details refer to Documentation/firmware-guide/acpi/gpio-properties.rst Interacting With the Legacy GPIO Subsystem ========================================== -Many kernel subsystems still handle GPIOs using the legacy integer-based -interface. Although it is strongly encouraged to upgrade them to the safer -descriptor-based API, the following two functions allow you to convert a GPIO -descriptor into the GPIO integer namespace and vice-versa:: +Many kernel subsystems and drivers still handle GPIOs using the legacy +integer-based interface. It is strongly recommended to update these to the new +gpiod interface. For cases where both interfaces need to be used, the following +two functions allow to convert a GPIO descriptor into the GPIO integer namespace +and vice-versa:: int desc_to_gpio(const struct gpio_desc *desc) struct gpio_desc *gpio_to_desc(unsigned gpio) -The GPIO number returned by desc_to_gpio() can be safely used as long as the -GPIO descriptor has not been freed. All the same, a GPIO number passed to -gpio_to_desc() must have been properly acquired, and usage of the returned GPIO -descriptor is only possible after the GPIO number has been released. +The GPIO number returned by desc_to_gpio() can safely be used as a parameter of +the gpio\_*() functions for as long as the GPIO descriptor `desc` is not freed. +All the same, a GPIO number passed to gpio_to_desc() must first be properly +acquired using e.g. gpio_request_one(), and the returned GPIO descriptor is only +considered valid until that GPIO number is released using gpio_free(). Freeing a GPIO obtained by one API with the other API is forbidden and an unchecked error. -- cgit v1.2.3 From 64a38367b45015de42521c4835541f43838caf39 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Fri, 27 Nov 2020 15:08:51 +0100 Subject: dt-bindings: gpio: Use Tegra186-specific include guard Use a unique include guard for the Tegra186 GPIO DT bindings header to avoid clashes with the DT bindings header for earlier chips. Signed-off-by: Thierry Reding Link: https://lore.kernel.org/r/20201127140852.123192-2-thierry.reding@gmail.com Signed-off-by: Linus Walleij --- include/dt-bindings/gpio/tegra186-gpio.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/dt-bindings/gpio/tegra186-gpio.h b/include/dt-bindings/gpio/tegra186-gpio.h index 0782b05e2775..af0d9583be70 100644 --- a/include/dt-bindings/gpio/tegra186-gpio.h +++ b/include/dt-bindings/gpio/tegra186-gpio.h @@ -8,8 +8,8 @@ * The second cell contains standard flag values specified in gpio.h. */ -#ifndef _DT_BINDINGS_GPIO_TEGRA_GPIO_H -#define _DT_BINDINGS_GPIO_TEGRA_GPIO_H +#ifndef _DT_BINDINGS_GPIO_TEGRA186_GPIO_H +#define _DT_BINDINGS_GPIO_TEGRA186_GPIO_H #include -- cgit v1.2.3 From 588cc1a02633dcc9ee0923d052cd20087e1a6b0a Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Sun, 29 Nov 2020 20:07:58 +0900 Subject: dt-bindings: gpio: Add a binding header for the MSC313 GPIO driver Header adds defines for the gpio number of each pad from the driver view. The gpio block seems to have enough registers for 128 lines but what line is mapped to a physical pin depends on the chip. The gpio block also seems to contain some registers that are not related to gpio but needed somewhere to go. Because of the above the driver itself uses the index of a pin's offset in an array of the possible offsets for a chip as the gpio number. Signed-off-by: Daniel Palmer Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20201129110803.2461700-2-daniel@0x0f.com Signed-off-by: Linus Walleij --- MAINTAINERS | 1 + include/dt-bindings/gpio/msc313-gpio.h | 53 ++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 include/dt-bindings/gpio/msc313-gpio.h diff --git a/MAINTAINERS b/MAINTAINERS index eadfbac4fcd0..9caa53b595d4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2133,6 +2133,7 @@ W: http://linux-chenxing.org/ F: Documentation/devicetree/bindings/arm/mstar/* F: arch/arm/boot/dts/mstar-* F: arch/arm/mach-mstar/ +F: include/dt-bindings/gpio/msc313-gpio.h ARM/NEC MOBILEPRO 900/c MACHINE SUPPORT M: Michael Petchkovsky diff --git a/include/dt-bindings/gpio/msc313-gpio.h b/include/dt-bindings/gpio/msc313-gpio.h new file mode 100644 index 000000000000..2dd56683d3c1 --- /dev/null +++ b/include/dt-bindings/gpio/msc313-gpio.h @@ -0,0 +1,53 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause */ +/* + * GPIO definitions for MStar/SigmaStar MSC313 and later SoCs + * + * Copyright (C) 2020 Daniel Palmer + */ + +#ifndef _DT_BINDINGS_MSC313_GPIO_H +#define _DT_BINDINGS_MSC313_GPIO_H + +#define MSC313_GPIO_FUART 0 +#define MSC313_GPIO_FUART_RX (MSC313_GPIO_FUART + 0) +#define MSC313_GPIO_FUART_TX (MSC313_GPIO_FUART + 1) +#define MSC313_GPIO_FUART_CTS (MSC313_GPIO_FUART + 2) +#define MSC313_GPIO_FUART_RTS (MSC313_GPIO_FUART + 3) + +#define MSC313_GPIO_SR (MSC313_GPIO_FUART_RTS + 1) +#define MSC313_GPIO_SR_IO2 (MSC313_GPIO_SR + 0) +#define MSC313_GPIO_SR_IO3 (MSC313_GPIO_SR + 1) +#define MSC313_GPIO_SR_IO4 (MSC313_GPIO_SR + 2) +#define MSC313_GPIO_SR_IO5 (MSC313_GPIO_SR + 3) +#define MSC313_GPIO_SR_IO6 (MSC313_GPIO_SR + 4) +#define MSC313_GPIO_SR_IO7 (MSC313_GPIO_SR + 5) +#define MSC313_GPIO_SR_IO8 (MSC313_GPIO_SR + 6) +#define MSC313_GPIO_SR_IO9 (MSC313_GPIO_SR + 7) +#define MSC313_GPIO_SR_IO10 (MSC313_GPIO_SR + 8) +#define MSC313_GPIO_SR_IO11 (MSC313_GPIO_SR + 9) +#define MSC313_GPIO_SR_IO12 (MSC313_GPIO_SR + 10) +#define MSC313_GPIO_SR_IO13 (MSC313_GPIO_SR + 11) +#define MSC313_GPIO_SR_IO14 (MSC313_GPIO_SR + 12) +#define MSC313_GPIO_SR_IO15 (MSC313_GPIO_SR + 13) +#define MSC313_GPIO_SR_IO16 (MSC313_GPIO_SR + 14) +#define MSC313_GPIO_SR_IO17 (MSC313_GPIO_SR + 15) + +#define MSC313_GPIO_SD (MSC313_GPIO_SR_IO17 + 1) +#define MSC313_GPIO_SD_CLK (MSC313_GPIO_SD + 0) +#define MSC313_GPIO_SD_CMD (MSC313_GPIO_SD + 1) +#define MSC313_GPIO_SD_D0 (MSC313_GPIO_SD + 2) +#define MSC313_GPIO_SD_D1 (MSC313_GPIO_SD + 3) +#define MSC313_GPIO_SD_D2 (MSC313_GPIO_SD + 4) +#define MSC313_GPIO_SD_D3 (MSC313_GPIO_SD + 5) + +#define MSC313_GPIO_I2C1 (MSC313_GPIO_SD_D3 + 1) +#define MSC313_GPIO_I2C1_SCL (MSC313_GPIO_I2C1 + 0) +#define MSC313_GPIO_I2C1_SDA (MSC313_GPIO_I2C1 + 1) + +#define MSC313_GPIO_SPI0 (MSC313_GPIO_I2C1_SDA + 1) +#define MSC313_GPIO_SPI0_CZ (MSC313_GPIO_SPI0 + 0) +#define MSC313_GPIO_SPI0_CK (MSC313_GPIO_SPI0 + 1) +#define MSC313_GPIO_SPI0_DI (MSC313_GPIO_SPI0 + 2) +#define MSC313_GPIO_SPI0_DO (MSC313_GPIO_SPI0 + 3) + +#endif /* _DT_BINDINGS_MSC313_GPIO_H */ -- cgit v1.2.3 From 493c7e03f837b46c64ebf941d0084e3e25909b7e Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Sun, 29 Nov 2020 20:07:59 +0900 Subject: dt-bindings: gpio: Binding for MStar MSC313 GPIO controller Add a binding description for the MStar/SigmaStar GPIO controller found in the MSC313 and later ARMv7 SoCs. Signed-off-by: Daniel Palmer Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20201129110803.2461700-3-daniel@0x0f.com Signed-off-by: Linus Walleij --- .../bindings/gpio/mstar,msc313-gpio.yaml | 59 ++++++++++++++++++++++ MAINTAINERS | 1 + 2 files changed, 60 insertions(+) create mode 100644 Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml b/Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml new file mode 100644 index 000000000000..1f2ef408bb43 --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/mstar,msc313-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MStar/SigmaStar GPIO controller + +maintainers: + - Daniel Palmer + +properties: + $nodename: + pattern: "^gpio@[0-9a-f]+$" + + compatible: + const: mstar,msc313-gpio + + reg: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + + gpio-ranges: true + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + +required: + - compatible + - reg + - gpio-controller + - "#gpio-cells" + - interrupt-controller + - "#interrupt-cells" + +additionalProperties: false + +examples: + - | + #include + + gpio: gpio@207800 { + compatible = "mstar,msc313e-gpio"; + #gpio-cells = <2>; + reg = <0x207800 0x200>; + gpio-controller; + gpio-ranges = <&pinctrl 0 36 22>, + <&pinctrl 22 63 4>, + <&pinctrl 26 68 6>; + #interrupt-cells = <2>; + interrupt-controller; + interrupt-parent = <&intc_fiq>; + }; diff --git a/MAINTAINERS b/MAINTAINERS index 9caa53b595d4..6c8f2181e91a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2131,6 +2131,7 @@ L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Maintained W: http://linux-chenxing.org/ F: Documentation/devicetree/bindings/arm/mstar/* +F: Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml F: arch/arm/boot/dts/mstar-* F: arch/arm/mach-mstar/ F: include/dt-bindings/gpio/msc313-gpio.h -- cgit v1.2.3 From 93224edf0b9fd7f643e7ead5b683bdac87f20aa2 Mon Sep 17 00:00:00 2001 From: Daniel Palmer Date: Sun, 29 Nov 2020 20:08:00 +0900 Subject: gpio: msc313: MStar MSC313 GPIO driver This adds a driver that supports the GPIO block found in MStar/SigmaStar ARMv7 SoCs. The controller seems to have enough register for 128 lines but where they are wired up differs between chips and no currently known chip uses anywhere near 128 lines so there needs to be some per-chip data to collect together what lines actually have physical pins attached and map the right names to them. The core peripherals seem to use the same lines on the currently known chips but the lines used for the sensor interface, lcd controller etc pins seem to be totally different between the infinity and mercury chips The code tries to collect all of the re-usable names, offsets etc together so that it's easy to build the extra per-chip data for other chips in the future. So far this only supports the MSC313 and MSC313E chips. Support for the SSC8336N (mercury5) is trivial to add once all of the lines have been mapped out. Signed-off-by: Daniel Palmer Link: https://lore.kernel.org/r/20201129110803.2461700-4-daniel@0x0f.com Signed-off-by: Linus Walleij --- MAINTAINERS | 1 + drivers/gpio/Kconfig | 11 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-msc313.c | 460 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 473 insertions(+) create mode 100644 drivers/gpio/gpio-msc313.c diff --git a/MAINTAINERS b/MAINTAINERS index 6c8f2181e91a..8f18a5b33a60 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2134,6 +2134,7 @@ F: Documentation/devicetree/bindings/arm/mstar/* F: Documentation/devicetree/bindings/gpio/mstar,msc313-gpio.yaml F: arch/arm/boot/dts/mstar-* F: arch/arm/mach-mstar/ +F: drivers/gpio/gpio-msc313.c F: include/dt-bindings/gpio/msc313-gpio.h ARM/NEC MOBILEPRO 900/c MACHINE SUPPORT diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4dd566f7ea39..2985e7475d3a 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -738,6 +738,17 @@ config GPIO_AMD_FCH Note: This driver doesn't registers itself automatically, as it needs to be provided with platform specific configuration. (See eg. CONFIG_PCENGINES_APU2.) + +config GPIO_MSC313 + bool "MStar MSC313 GPIO support" + depends on ARCH_MSTARV7 + default ARCH_MSTARV7 + select GPIOLIB_IRQCHIP + select IRQ_DOMAIN_HIERARCHY + help + Say Y here to support the main GPIO block on MStar/SigmaStar + ARMv7 based SoCs. + endmenu menu "Port-mapped I/O GPIO drivers" diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 09dada80ac34..b6c116a7c785 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -101,6 +101,7 @@ obj-$(CONFIG_GPIO_MOCKUP) += gpio-mockup.o obj-$(CONFIG_GPIO_MOXTET) += gpio-moxtet.o obj-$(CONFIG_GPIO_MPC5200) += gpio-mpc5200.o obj-$(CONFIG_GPIO_MPC8XXX) += gpio-mpc8xxx.o +obj-$(CONFIG_GPIO_MSC313) += gpio-msc313.o obj-$(CONFIG_GPIO_MSIC) += gpio-msic.o obj-$(CONFIG_GPIO_MT7621) += gpio-mt7621.o obj-$(CONFIG_GPIO_MVEBU) += gpio-mvebu.o diff --git a/drivers/gpio/gpio-msc313.c b/drivers/gpio/gpio-msc313.c new file mode 100644 index 000000000000..da31a5ff7a2b --- /dev/null +++ b/drivers/gpio/gpio-msc313.c @@ -0,0 +1,460 @@ +// SPDX-License-Identifier: GPL-2.0 +/* Copyright (C) 2020 Daniel Palmer */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DRIVER_NAME "gpio-msc313" + +#define MSC313_GPIO_IN BIT(0) +#define MSC313_GPIO_OUT BIT(4) +#define MSC313_GPIO_OEN BIT(5) + +/* + * These bits need to be saved to correctly restore the + * gpio state when resuming from suspend to memory. + */ +#define MSC313_GPIO_BITSTOSAVE (MSC313_GPIO_OUT | MSC313_GPIO_OEN) + +/* pad names for fuart, same for all SoCs so far */ +#define MSC313_PINNAME_FUART_RX "fuart_rx" +#define MSC313_PINNAME_FUART_TX "fuart_tx" +#define MSC313_PINNAME_FUART_CTS "fuart_cts" +#define MSC313_PINNAME_FUART_RTS "fuart_rts" + +/* pad names for sr, mercury5 is different */ +#define MSC313_PINNAME_SR_IO2 "sr_io2" +#define MSC313_PINNAME_SR_IO3 "sr_io3" +#define MSC313_PINNAME_SR_IO4 "sr_io4" +#define MSC313_PINNAME_SR_IO5 "sr_io5" +#define MSC313_PINNAME_SR_IO6 "sr_io6" +#define MSC313_PINNAME_SR_IO7 "sr_io7" +#define MSC313_PINNAME_SR_IO8 "sr_io8" +#define MSC313_PINNAME_SR_IO9 "sr_io9" +#define MSC313_PINNAME_SR_IO10 "sr_io10" +#define MSC313_PINNAME_SR_IO11 "sr_io11" +#define MSC313_PINNAME_SR_IO12 "sr_io12" +#define MSC313_PINNAME_SR_IO13 "sr_io13" +#define MSC313_PINNAME_SR_IO14 "sr_io14" +#define MSC313_PINNAME_SR_IO15 "sr_io15" +#define MSC313_PINNAME_SR_IO16 "sr_io16" +#define MSC313_PINNAME_SR_IO17 "sr_io17" + +/* pad names for sd, same for all SoCs so far */ +#define MSC313_PINNAME_SD_CLK "sd_clk" +#define MSC313_PINNAME_SD_CMD "sd_cmd" +#define MSC313_PINNAME_SD_D0 "sd_d0" +#define MSC313_PINNAME_SD_D1 "sd_d1" +#define MSC313_PINNAME_SD_D2 "sd_d2" +#define MSC313_PINNAME_SD_D3 "sd_d3" + +/* pad names for i2c1, same for all SoCs so for */ +#define MSC313_PINNAME_I2C1_SCL "i2c1_scl" +#define MSC313_PINNAME_I2C1_SCA "i2c1_sda" + +/* pad names for spi0, same for all SoCs so far */ +#define MSC313_PINNAME_SPI0_CZ "spi0_cz" +#define MSC313_PINNAME_SPI0_CK "spi0_ck" +#define MSC313_PINNAME_SPI0_DI "spi0_di" +#define MSC313_PINNAME_SPI0_DO "spi0_do" + +#define FUART_NAMES \ + MSC313_PINNAME_FUART_RX, \ + MSC313_PINNAME_FUART_TX, \ + MSC313_PINNAME_FUART_CTS, \ + MSC313_PINNAME_FUART_RTS + +#define OFF_FUART_RX 0x50 +#define OFF_FUART_TX 0x54 +#define OFF_FUART_CTS 0x58 +#define OFF_FUART_RTS 0x5c + +#define FUART_OFFSETS \ + OFF_FUART_RX, \ + OFF_FUART_TX, \ + OFF_FUART_CTS, \ + OFF_FUART_RTS + +#define SR_NAMES \ + MSC313_PINNAME_SR_IO2, \ + MSC313_PINNAME_SR_IO3, \ + MSC313_PINNAME_SR_IO4, \ + MSC313_PINNAME_SR_IO5, \ + MSC313_PINNAME_SR_IO6, \ + MSC313_PINNAME_SR_IO7, \ + MSC313_PINNAME_SR_IO8, \ + MSC313_PINNAME_SR_IO9, \ + MSC313_PINNAME_SR_IO10, \ + MSC313_PINNAME_SR_IO11, \ + MSC313_PINNAME_SR_IO12, \ + MSC313_PINNAME_SR_IO13, \ + MSC313_PINNAME_SR_IO14, \ + MSC313_PINNAME_SR_IO15, \ + MSC313_PINNAME_SR_IO16, \ + MSC313_PINNAME_SR_IO17 + +#define OFF_SR_IO2 0x88 +#define OFF_SR_IO3 0x8c +#define OFF_SR_IO4 0x90 +#define OFF_SR_IO5 0x94 +#define OFF_SR_IO6 0x98 +#define OFF_SR_IO7 0x9c +#define OFF_SR_IO8 0xa0 +#define OFF_SR_IO9 0xa4 +#define OFF_SR_IO10 0xa8 +#define OFF_SR_IO11 0xac +#define OFF_SR_IO12 0xb0 +#define OFF_SR_IO13 0xb4 +#define OFF_SR_IO14 0xb8 +#define OFF_SR_IO15 0xbc +#define OFF_SR_IO16 0xc0 +#define OFF_SR_IO17 0xc4 + +#define SR_OFFSETS \ + OFF_SR_IO2, \ + OFF_SR_IO3, \ + OFF_SR_IO4, \ + OFF_SR_IO5, \ + OFF_SR_IO6, \ + OFF_SR_IO7, \ + OFF_SR_IO8, \ + OFF_SR_IO9, \ + OFF_SR_IO10, \ + OFF_SR_IO11, \ + OFF_SR_IO12, \ + OFF_SR_IO13, \ + OFF_SR_IO14, \ + OFF_SR_IO15, \ + OFF_SR_IO16, \ + OFF_SR_IO17 + +#define SD_NAMES \ + MSC313_PINNAME_SD_CLK, \ + MSC313_PINNAME_SD_CMD, \ + MSC313_PINNAME_SD_D0, \ + MSC313_PINNAME_SD_D1, \ + MSC313_PINNAME_SD_D2, \ + MSC313_PINNAME_SD_D3 + +#define OFF_SD_CLK 0x140 +#define OFF_SD_CMD 0x144 +#define OFF_SD_D0 0x148 +#define OFF_SD_D1 0x14c +#define OFF_SD_D2 0x150 +#define OFF_SD_D3 0x154 + +#define SD_OFFSETS \ + OFF_SD_CLK, \ + OFF_SD_CMD, \ + OFF_SD_D0, \ + OFF_SD_D1, \ + OFF_SD_D2, \ + OFF_SD_D3 + +#define I2C1_NAMES \ + MSC313_PINNAME_I2C1_SCL, \ + MSC313_PINNAME_I2C1_SCA + +#define OFF_I2C1_SCL 0x188 +#define OFF_I2C1_SCA 0x18c + +#define I2C1_OFFSETS \ + OFF_I2C1_SCL, \ + OFF_I2C1_SCA + +#define SPI0_NAMES \ + MSC313_PINNAME_SPI0_CZ, \ + MSC313_PINNAME_SPI0_CK, \ + MSC313_PINNAME_SPI0_DI, \ + MSC313_PINNAME_SPI0_DO + +#define OFF_SPI0_CZ 0x1c0 +#define OFF_SPI0_CK 0x1c4 +#define OFF_SPI0_DI 0x1c8 +#define OFF_SPI0_DO 0x1cc + +#define SPI0_OFFSETS \ + OFF_SPI0_CZ, \ + OFF_SPI0_CK, \ + OFF_SPI0_DI, \ + OFF_SPI0_DO + +struct msc313_gpio_data { + const char * const *names; + const unsigned int *offsets; + const unsigned int num; +}; + +#define MSC313_GPIO_CHIPDATA(_chip) \ +static const struct msc313_gpio_data _chip##_data = { \ + .names = _chip##_names, \ + .offsets = _chip##_offsets, \ + .num = ARRAY_SIZE(_chip##_offsets), \ +} + +#ifdef CONFIG_MACH_INFINITY +static const char * const msc313_names[] = { + FUART_NAMES, + SR_NAMES, + SD_NAMES, + I2C1_NAMES, + SPI0_NAMES, +}; + +static const unsigned int msc313_offsets[] = { + FUART_OFFSETS, + SR_OFFSETS, + SD_OFFSETS, + I2C1_OFFSETS, + SPI0_OFFSETS, +}; + +MSC313_GPIO_CHIPDATA(msc313); +#endif + +struct msc313_gpio { + void __iomem *base; + const struct msc313_gpio_data *gpio_data; + u8 *saved; +}; + +static void msc313_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct msc313_gpio *gpio = gpiochip_get_data(chip); + u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]); + + if (value) + gpioreg |= MSC313_GPIO_OUT; + else + gpioreg &= ~MSC313_GPIO_OUT; + + writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]); +} + +static int msc313_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + struct msc313_gpio *gpio = gpiochip_get_data(chip); + + return readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]) & MSC313_GPIO_IN; +} + +static int msc313_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) +{ + struct msc313_gpio *gpio = gpiochip_get_data(chip); + u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]); + + gpioreg |= MSC313_GPIO_OEN; + writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]); + + return 0; +} + +static int msc313_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) +{ + struct msc313_gpio *gpio = gpiochip_get_data(chip); + u8 gpioreg = readb_relaxed(gpio->base + gpio->gpio_data->offsets[offset]); + + gpioreg &= ~MSC313_GPIO_OEN; + if (value) + gpioreg |= MSC313_GPIO_OUT; + else + gpioreg &= ~MSC313_GPIO_OUT; + writeb_relaxed(gpioreg, gpio->base + gpio->gpio_data->offsets[offset]); + + return 0; +} + +/* + * The interrupt handling happens in the parent interrupt controller, + * we don't do anything here. + */ +static struct irq_chip msc313_gpio_irqchip = { + .name = "GPIO", + .irq_eoi = irq_chip_eoi_parent, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_affinity = irq_chip_set_affinity_parent, +}; + +/* + * The parent interrupt controller needs the GIC interrupt type set to GIC_SPI + * so we need to provide the fwspec. Essentially gpiochip_populate_parent_fwspec_twocell + * that puts GIC_SPI into the first cell. + */ +static void *msc313_gpio_populate_parent_fwspec(struct gpio_chip *gc, + unsigned int parent_hwirq, + unsigned int parent_type) +{ + struct irq_fwspec *fwspec; + + fwspec = kmalloc(sizeof(*fwspec), GFP_KERNEL); + if (!fwspec) + return NULL; + + fwspec->fwnode = gc->irq.parent_domain->fwnode; + fwspec->param_count = 3; + fwspec->param[0] = GIC_SPI; + fwspec->param[1] = parent_hwirq; + fwspec->param[2] = parent_type; + + return fwspec; +} + +static int msc313e_gpio_child_to_parent_hwirq(struct gpio_chip *chip, + unsigned int child, + unsigned int child_type, + unsigned int *parent, + unsigned int *parent_type) +{ + struct msc313_gpio *priv = gpiochip_get_data(chip); + unsigned int offset = priv->gpio_data->offsets[child]; + + /* + * only the spi0 pins have interrupts on the parent + * on all of the known chips and so far they are all + * mapped to the same place + */ + if (offset >= OFF_SPI0_CZ && offset <= OFF_SPI0_DO) { + *parent_type = child_type; + *parent = ((offset - OFF_SPI0_CZ) >> 2) + 28; + return 0; + } + + return -EINVAL; +} + +static int msc313_gpio_probe(struct platform_device *pdev) +{ + const struct msc313_gpio_data *match_data; + struct msc313_gpio *gpio; + struct gpio_chip *gpiochip; + struct gpio_irq_chip *gpioirqchip; + struct irq_domain *parent_domain; + struct device_node *parent_node; + struct device *dev = &pdev->dev; + int ret; + + match_data = of_device_get_match_data(dev); + if (!match_data) + return -EINVAL; + + parent_node = of_irq_find_parent(dev->of_node); + if (!parent_node) + return -ENODEV; + + parent_domain = irq_find_host(parent_node); + if (!parent_domain) + return -ENODEV; + + gpio = devm_kzalloc(dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) + return -ENOMEM; + + gpio->gpio_data = match_data; + + gpio->saved = devm_kcalloc(dev, gpio->gpio_data->num, sizeof(*gpio->saved), GFP_KERNEL); + if (!gpio->saved) + return -ENOMEM; + + gpio->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(gpio->base)) + return PTR_ERR(gpio->base); + + platform_set_drvdata(pdev, gpio); + + gpiochip = devm_kzalloc(dev, sizeof(*gpiochip), GFP_KERNEL); + if (!gpiochip) + return -ENOMEM; + + gpiochip->label = DRIVER_NAME; + gpiochip->parent = dev; + gpiochip->request = gpiochip_generic_request; + gpiochip->free = gpiochip_generic_free; + gpiochip->direction_input = msc313_gpio_direction_input; + gpiochip->direction_output = msc313_gpio_direction_output; + gpiochip->get = msc313_gpio_get; + gpiochip->set = msc313_gpio_set; + gpiochip->base = -1; + gpiochip->ngpio = gpio->gpio_data->num; + gpiochip->names = gpio->gpio_data->names; + + gpioirqchip = &gpiochip->irq; + gpioirqchip->chip = &msc313_gpio_irqchip; + gpioirqchip->fwnode = of_node_to_fwnode(dev->of_node); + gpioirqchip->parent_domain = parent_domain; + gpioirqchip->child_to_parent_hwirq = msc313e_gpio_child_to_parent_hwirq; + gpioirqchip->populate_parent_alloc_arg = msc313_gpio_populate_parent_fwspec; + gpioirqchip->handler = handle_bad_irq; + gpioirqchip->default_type = IRQ_TYPE_NONE; + + ret = devm_gpiochip_add_data(dev, gpiochip, gpio); + return ret; +} + +static int msc313_gpio_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id msc313_gpio_of_match[] = { +#ifdef CONFIG_MACH_INFINITY + { + .compatible = "mstar,msc313-gpio", + .data = &msc313_data, + }, +#endif + { } +}; + +/* + * The GPIO controller loses the state of the registers when the + * SoC goes into suspend to memory mode so we need to save some + * of the register bits before suspending and put it back when resuming + */ +static int __maybe_unused msc313_gpio_suspend(struct device *dev) +{ + struct msc313_gpio *gpio = dev_get_drvdata(dev); + int i; + + for (i = 0; i < gpio->gpio_data->num; i++) + gpio->saved[i] = readb_relaxed(gpio->base + gpio->gpio_data->offsets[i]) & MSC313_GPIO_BITSTOSAVE; + + return 0; +} + +static int __maybe_unused msc313_gpio_resume(struct device *dev) +{ + struct msc313_gpio *gpio = dev_get_drvdata(dev); + int i; + + for (i = 0; i < gpio->gpio_data->num; i++) + writeb_relaxed(gpio->saved[i], gpio->base + gpio->gpio_data->offsets[i]); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(msc313_gpio_ops, msc313_gpio_suspend, msc313_gpio_resume); + +static struct platform_driver msc313_gpio_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = msc313_gpio_of_match, + .pm = &msc313_gpio_ops, + }, + .probe = msc313_gpio_probe, + .remove = msc313_gpio_remove, +}; + +builtin_platform_driver(msc313_gpio_driver); -- cgit v1.2.3 From 26d060e47e25f2c715a1b2c48fea391f67907a30 Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Thu, 15 Oct 2020 07:11:56 +0800 Subject: gpiolib: cdev: allow edge event timestamps to be configured as REALTIME Using CLOCK_REALTIME as the source for event timestamps is crucial for some specific applications, particularly those requiring timetamps relative to a PTP clock, so provide an option to switch the event timestamp source from the default CLOCK_MONOTONIC to CLOCK_REALTIME. Note that CLOCK_REALTIME was the default source clock for GPIO until Linux 5.7 when it was changed to CLOCK_MONOTONIC due to issues with the shifting of the realtime clock. Providing this option maintains the CLOCK_MONOTONIC as the default, while also providing a path forward for those dependent on the pre-5.7 behaviour. Suggested-by: Jack Winch Signed-off-by: Kent Gibson Link: https://lore.kernel.org/r/20201014231158.34117-2-warthog618@gmail.com Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-cdev.c | 21 ++++++++++++++++++--- drivers/gpio/gpiolib.h | 1 + include/uapi/linux/gpio.h | 12 +++++++++--- 3 files changed, 28 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 192721f829a3..f64a35767434 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -509,6 +509,7 @@ struct linereq { GPIO_V2_LINE_DIRECTION_FLAGS | \ GPIO_V2_LINE_DRIVE_FLAGS | \ GPIO_V2_LINE_EDGE_FLAGS | \ + GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME | \ GPIO_V2_LINE_BIAS_FLAGS) static void linereq_put_event(struct linereq *lr, @@ -529,6 +530,14 @@ static void linereq_put_event(struct linereq *lr, pr_debug_ratelimited("event FIFO is full - event dropped\n"); } +static u64 line_event_timestamp(struct line *line) +{ + if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &line->desc->flags)) + return ktime_get_real_ns(); + + return ktime_get_ns(); +} + static irqreturn_t edge_irq_thread(int irq, void *p) { struct line *line = p; @@ -546,7 +555,7 @@ static irqreturn_t edge_irq_thread(int irq, void *p) * which case we didn't get the timestamp from * edge_irq_handler(). */ - le.timestamp_ns = ktime_get_ns(); + le.timestamp_ns = line_event_timestamp(line); if (lr->num_lines != 1) line->req_seqno = atomic_inc_return(&lr->seqno); } @@ -590,7 +599,7 @@ static irqreturn_t edge_irq_handler(int irq, void *p) * Just store the timestamp in hardirq context so we get it as * close in time as possible to the actual event. */ - line->timestamp_ns = ktime_get_ns(); + line->timestamp_ns = line_event_timestamp(line); if (lr->num_lines != 1) line->req_seqno = atomic_inc_return(&lr->seqno); @@ -663,7 +672,7 @@ static void debounce_work_func(struct work_struct *work) memset(&le, 0, sizeof(le)); lr = line->req; - le.timestamp_ns = ktime_get_ns(); + le.timestamp_ns = line_event_timestamp(line); le.offset = gpio_chip_hwgpio(line->desc); line->line_seqno++; le.line_seqno = line->line_seqno; @@ -967,6 +976,9 @@ static void gpio_v2_line_config_flags_to_desc_flags(u64 flags, flags & GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN); assign_bit(FLAG_BIAS_DISABLE, flagsp, flags & GPIO_V2_LINE_FLAG_BIAS_DISABLED); + + assign_bit(FLAG_EVENT_CLOCK_REALTIME, flagsp, + flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME); } static long linereq_get_values(struct linereq *lr, void __user *ip) @@ -1930,6 +1942,9 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, if (test_bit(FLAG_EDGE_FALLING, &desc->flags)) info->flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING; + if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &desc->flags)) + info->flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME; + debounce_period_us = READ_ONCE(desc->debounce_period_us); if (debounce_period_us) { info->attrs[num_attrs].id = GPIO_V2_LINE_ATTR_ID_DEBOUNCE; diff --git a/drivers/gpio/gpiolib.h b/drivers/gpio/gpiolib.h index 42d81454da21..9c32d4ace572 100644 --- a/drivers/gpio/gpiolib.h +++ b/drivers/gpio/gpiolib.h @@ -116,6 +116,7 @@ struct gpio_desc { #define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */ #define FLAG_EDGE_RISING 16 /* GPIO CDEV detects rising edge events */ #define FLAG_EDGE_FALLING 17 /* GPIO CDEV detects falling edge events */ +#define FLAG_EVENT_CLOCK_REALTIME 18 /* GPIO CDEV reports REALTIME timestamps in events */ /* Connection label */ const char *label; diff --git a/include/uapi/linux/gpio.h b/include/uapi/linux/gpio.h index 2072c260f5d0..e4eb0b8c5cf9 100644 --- a/include/uapi/linux/gpio.h +++ b/include/uapi/linux/gpio.h @@ -65,6 +65,7 @@ struct gpiochip_info { * @GPIO_V2_LINE_FLAG_BIAS_PULL_UP: line has pull-up bias enabled * @GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN: line has pull-down bias enabled * @GPIO_V2_LINE_FLAG_BIAS_DISABLED: line has bias disabled + * @GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME: line events contain REALTIME timestamps */ enum gpio_v2_line_flag { GPIO_V2_LINE_FLAG_USED = _BITULL(0), @@ -78,6 +79,7 @@ enum gpio_v2_line_flag { GPIO_V2_LINE_FLAG_BIAS_PULL_UP = _BITULL(8), GPIO_V2_LINE_FLAG_BIAS_PULL_DOWN = _BITULL(9), GPIO_V2_LINE_FLAG_BIAS_DISABLED = _BITULL(10), + GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME = _BITULL(11), }; /** @@ -270,9 +272,6 @@ enum gpio_v2_line_event_id { /** * struct gpio_v2_line_event - The actual event being pushed to userspace * @timestamp_ns: best estimate of time of event occurrence, in nanoseconds. - * The @timestamp_ns is read from %CLOCK_MONOTONIC and is intended to allow - * the accurate measurement of the time between events. It does not provide - * the wall-clock time. * @id: event identifier with value from &enum gpio_v2_line_event_id * @offset: the offset of the line that triggered the event * @seqno: the sequence number for this event in the sequence of events for @@ -280,6 +279,13 @@ enum gpio_v2_line_event_id { * @line_seqno: the sequence number for this event in the sequence of * events on this particular line * @padding: reserved for future use + * + * By default the @timestamp_ns is read from %CLOCK_MONOTONIC and is + * intended to allow the accurate measurement of the time between events. + * It does not provide the wall-clock time. + * + * If the %GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME flag is set then the + * @timestamp_ns is read from %CLOCK_REALTIME. */ struct gpio_v2_line_event { __aligned_u64 timestamp_ns; -- cgit v1.2.3 From da777be6de014be6b302644685797ed3860a0d0d Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Thu, 15 Oct 2020 07:11:57 +0800 Subject: tools: gpio: add support for reporting realtime event clock to lsgpio Add support for reporting if a line is configured to report realtime timestamps in events. Signed-off-by: Kent Gibson Link: https://lore.kernel.org/r/20201014231158.34117-3-warthog618@gmail.com Signed-off-by: Linus Walleij --- tools/gpio/lsgpio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/gpio/lsgpio.c b/tools/gpio/lsgpio.c index 5a05a454d0c9..c61d061247e1 100644 --- a/tools/gpio/lsgpio.c +++ b/tools/gpio/lsgpio.c @@ -65,6 +65,10 @@ struct gpio_flag flagnames[] = { .name = "bias-disabled", .mask = GPIO_V2_LINE_FLAG_BIAS_DISABLED, }, + { + .name = "clock-realtime", + .mask = GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME, + }, }; static void print_attributes(struct gpio_v2_line_info *info) -- cgit v1.2.3 From e0822cf9b892ed051830daaf57896aca48c8567b Mon Sep 17 00:00:00 2001 From: Kent Gibson Date: Thu, 15 Oct 2020 07:11:58 +0800 Subject: tools: gpio: add option to report wall-clock time to gpio-event-mon Add support for selecting the realtime clock for events. Signed-off-by: Kent Gibson Link: https://lore.kernel.org/r/20201014231158.34117-4-warthog618@gmail.com Signed-off-by: Linus Walleij --- tools/gpio/gpio-event-mon.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/gpio/gpio-event-mon.c b/tools/gpio/gpio-event-mon.c index 90c3155f05b1..cacd66ad7926 100644 --- a/tools/gpio/gpio-event-mon.c +++ b/tools/gpio/gpio-event-mon.c @@ -148,6 +148,7 @@ void print_usage(void) " -s Set line as open source\n" " -r Listen for rising edges\n" " -f Listen for falling edges\n" + " -w Report the wall-clock time for events\n" " -b Debounce the line with period n microseconds\n" " [-c ] Do loops (optional, infinite loop if not stated)\n" " -? This helptext\n" @@ -173,7 +174,7 @@ int main(int argc, char **argv) memset(&config, 0, sizeof(config)); config.flags = GPIO_V2_LINE_FLAG_INPUT; - while ((c = getopt(argc, argv, "c:n:o:b:dsrf?")) != -1) { + while ((c = getopt(argc, argv, "c:n:o:b:dsrfw?")) != -1) { switch (c) { case 'c': loops = strtoul(optarg, NULL, 10); @@ -204,6 +205,9 @@ int main(int argc, char **argv) case 'f': config.flags |= GPIO_V2_LINE_FLAG_EDGE_FALLING; break; + case 'w': + config.flags |= GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME; + break; case '?': print_usage(); return -1; -- cgit v1.2.3 From c47d9e1b734361a5d809fae02a268b85ab0f95ee Mon Sep 17 00:00:00 2001 From: Enrico Weigelt Date: Wed, 2 Dec 2020 14:37:54 +0100 Subject: gpio: just plain warning when nonexisting gpio requested When trying to export an nonexisting gpio ID, the kernel prints out a big warning w/ stacktrace, sounding like a huge problem. In fact it's a pretty normal situation, like file or device not found. So, just print a more relaxed warning instead. changes v2: drop defining pr_fmt() Signed-off-by: Enrico Weigelt Link: https://lore.kernel.org/r/20201202133754.32045-1-info@metux.net Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index c6db72d5420e..fd622c05cbd5 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1,4 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 + #include #include #include @@ -119,7 +120,7 @@ struct gpio_desc *gpio_to_desc(unsigned gpio) spin_unlock_irqrestore(&gpio_lock, flags); if (!gpio_is_valid(gpio)) - WARN(1, "invalid GPIO %d\n", gpio); + pr_warn("invalid GPIO %d\n", gpio); return NULL; } -- cgit v1.2.3 From dd0fa81143f60cbc90cd6ce1c9a2c51a7b40046e Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Fri, 4 Dec 2020 09:35:33 +0100 Subject: gpio: Add TODO item for debugfs interface The idea to create a debugfs to replace the aging and dangerous sysfs ABI for hacking and tinkering came up on the list. Signed-off-by: Linus Walleij Reviewed-by: Geert Uytterhoeven Cc: Geert Uytterhoeven Link: https://lore.kernel.org/r/20201204083533.65830-1-linus.walleij@linaro.org --- drivers/gpio/TODO | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index cd04e0b60159..0229fa79499e 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -142,3 +142,39 @@ use of the global GPIO numbers. Once the above is complete, it may make sense to simply join the subsystems into one and make pin multiplexing, pin configuration, GPIO, etc selectable options in one and the same pin control and GPIO subsystem. + + +Debugfs in place of sysfs + +The old sysfs code that enables simple uses of GPIOs from the +command line is still popular despite the existance of the proper +character device. The reason is that it is simple to use on +root filesystems where you only have a minimal set of tools such +as "cat", "echo" etc. + +The old sysfs still need to be strongly deprecated and removed +as it relies on the global GPIO numberspace that assume a strict +order of global GPIO numbers that do not change between boots +and is independent of probe order. + +To solve this and provide an ABI that people can use for hacks +and development, implement a debugfs interface to manipulate +GPIO lines that can do everything that sysfs can do today: one +directory per gpiochip and one file entry per line: + +/sys/kernel/debug/gpiochip/gpiochip0 +/sys/kernel/debug/gpiochip/gpiochip0/gpio0 +/sys/kernel/debug/gpiochip/gpiochip0/gpio1 +/sys/kernel/debug/gpiochip/gpiochip0/gpio2 +/sys/kernel/debug/gpiochip/gpiochip0/gpio3 +... +/sys/kernel/debug/gpiochip/gpiochip1 +/sys/kernel/debug/gpiochip/gpiochip1/gpio0 +/sys/kernel/debug/gpiochip/gpiochip1/gpio1 +... + +The exact files and design of the debugfs interface can be +discussed but the idea is to provide a low-level access point +for debugging and hacking and to expose all lines without the +need of any exporting. Also provide ample ammunition to shoot +oneself in the foot, because this is debugfs after all. -- cgit v1.2.3 From 3bf1d26c8a165db5bbb65c21327ac2055d70e76f Mon Sep 17 00:00:00 2001 From: "Enrico Weigelt, metux IT consult" Date: Thu, 3 Dec 2020 19:24:21 +0100 Subject: drivers: gpio: bt8xx: prefer dev_err()/dev_warn() over of raw printk For logging in device contexts, dev_*() functions are preferred over raw printk(), which also print out device name. Signed-off-by: Enrico Weigelt, metux IT consult Link: https://lore.kernel.org/r/20201203182423.5499-1-info@metux.net Signed-off-by: Linus Walleij --- drivers/gpio/gpio-bt8xx.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-bt8xx.c b/drivers/gpio/gpio-bt8xx.c index a6f30ad6750f..7920cf256798 100644 --- a/drivers/gpio/gpio-bt8xx.c +++ b/drivers/gpio/gpio-bt8xx.c @@ -175,13 +175,13 @@ static int bt8xxgpio_probe(struct pci_dev *dev, err = pci_enable_device(dev); if (err) { - printk(KERN_ERR "bt8xxgpio: Can't enable device.\n"); + dev_err(&dev->dev, "can't enable device.\n"); return err; } if (!devm_request_mem_region(&dev->dev, pci_resource_start(dev, 0), pci_resource_len(dev, 0), "bt8xxgpio")) { - printk(KERN_WARNING "bt8xxgpio: Can't request iomem (0x%llx).\n", + dev_warn(&dev->dev, "can't request iomem (0x%llx).\n", (unsigned long long)pci_resource_start(dev, 0)); err = -EBUSY; goto err_disable; @@ -191,7 +191,7 @@ static int bt8xxgpio_probe(struct pci_dev *dev, bg->mmio = devm_ioremap(&dev->dev, pci_resource_start(dev, 0), 0x1000); if (!bg->mmio) { - printk(KERN_ERR "bt8xxgpio: ioremap() failed\n"); + dev_err(&dev->dev, "ioremap() failed\n"); err = -EIO; goto err_disable; } @@ -207,7 +207,7 @@ static int bt8xxgpio_probe(struct pci_dev *dev, bt8xxgpio_gpio_setup(bg); err = gpiochip_add_data(&bg->gpio, bg); if (err) { - printk(KERN_ERR "bt8xxgpio: Failed to register GPIOs\n"); + dev_err(&dev->dev, "failed to register GPIOs\n"); goto err_disable; } -- cgit v1.2.3 From a922a24454088c62688472c16c42ea944496cf24 Mon Sep 17 00:00:00 2001 From: "Enrico Weigelt, metux IT consult" Date: Thu, 3 Dec 2020 19:24:22 +0100 Subject: drivers: gpio: amd8111: prefer dev_err()/dev_info() over raw printk For logging in device contexts, dev_*() functions are preferred over raw printk(), which also print out device name. Signed-off-by: Enrico Weigelt, metux IT consult Link: https://lore.kernel.org/r/20201203182423.5499-2-info@metux.net Signed-off-by: Linus Walleij --- drivers/gpio/gpio-amd8111.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c index fdcebe59510d..0e260950992d 100644 --- a/drivers/gpio/gpio-amd8111.c +++ b/drivers/gpio/gpio-amd8111.c @@ -179,7 +179,6 @@ static int __init amd_gpio_init(void) struct pci_dev *pdev = NULL; const struct pci_device_id *ent; - /* We look for our device - AMD South Bridge * I don't know about a system with two such bridges, * so we can assume that there is max. one device. @@ -223,11 +222,10 @@ found: spin_lock_init(&gp.lock); - printk(KERN_INFO "AMD-8111 GPIO detected\n"); + dev_info(&pdev->dev, "AMD-8111 GPIO detected\n"); err = gpiochip_add_data(&gp.chip, &gp); if (err) { - printk(KERN_ERR "GPIO registering failed (%d)\n", - err); + dev_err(&pdev->dev, "GPIO registering failed (%d)\n", err); ioport_unmap(gp.pm); goto out; } -- cgit v1.2.3 From 37ddba0245b4547621862c0b24bf36deb199bf5c Mon Sep 17 00:00:00 2001 From: "Enrico Weigelt, metux IT consult" Date: Thu, 3 Dec 2020 19:24:23 +0100 Subject: drivers: gpio: amd8111: use SPDX-License-Identifier Prefer SPDX-License-Identifier over hand-written texts. Signed-off-by: Enrico Weigelt, metux IT consult Link: https://lore.kernel.org/r/20201203182423.5499-3-info@metux.net Signed-off-by: Linus Walleij --- drivers/gpio/gpio-amd8111.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/gpio/gpio-amd8111.c b/drivers/gpio/gpio-amd8111.c index 0e260950992d..14e6b3e64add 100644 --- a/drivers/gpio/gpio-amd8111.c +++ b/drivers/gpio/gpio-amd8111.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * GPIO driver for AMD 8111 south bridges * @@ -20,10 +21,6 @@ * Hardware driver for Intel i810 Random Number Generator (RNG) * Copyright 2000,2001 Jeff Garzik * Copyright 2000,2001 Philipp Rumpf - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include -- cgit v1.2.3 From b5252196d08abd82f3b21532354f71a40dd2801d Mon Sep 17 00:00:00 2001 From: "Enrico Weigelt, metux IT consult" Date: Mon, 7 Dec 2020 21:38:16 +0100 Subject: gpio: put virtual gpio device into their own submenu Since we already have a few virtual GPIO drivers, and more to come, this category deserves its own submenu. Signed-off-by: Enrico Weigelt, metux IT consult Signed-off-by: Bartosz Golaszewski --- drivers/gpio/Kconfig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 253a61ec9645..8629860a9035 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -1591,6 +1591,8 @@ config GPIO_VIPERBOARD endmenu +menu "Virtual GPIO drivers" + config GPIO_AGGREGATOR tristate "GPIO Aggregator" help @@ -1614,4 +1616,6 @@ config GPIO_MOCKUP tools/testing/selftests/gpio/gpio-mockup.sh. Reference the usage in it. +endmenu + endif -- cgit v1.2.3 From a0db197f534fb24d64cc8c716b5f128f2de1c898 Mon Sep 17 00:00:00 2001 From: Marc Zyngier Date: Fri, 4 Dec 2020 16:47:36 +0000 Subject: gpiolib: cdev: Flag invalid GPIOs as used When reporting the state of a GPIO to userspace, we never check for the actual validity of the line, meaning we report invalid lines as being usable. A subsequent request will fail though, which is an inconsistent behaviour from a userspace perspective. Instead, let's check for the validity of the line and report it as used if it is invalid. This allows a tool such as gpioinfo to report something sensible: gpiochip3 - 4 lines: line 0: unnamed unused input active-high line 1: unnamed kernel input active-high [used] line 2: unnamed kernel input active-high [used] line 3: unnamed unused input active-high In this example, lines 1 and 2 are invalid, and cannot be used by userspace. Signed-off-by: Marc Zyngier Reviewed-by: Andy Shevchenko Link: https://lore.kernel.org/r/20201204164739.781812-2-maz@kernel.org Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib-cdev.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index f64a35767434..bd40d717765c 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -1914,6 +1914,7 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc, test_bit(FLAG_USED_AS_IRQ, &desc->flags) || test_bit(FLAG_EXPORT, &desc->flags) || test_bit(FLAG_SYSFS, &desc->flags) || + !gpiochip_line_is_valid(gc, info->offset) || !ok_for_pinctrl) info->flags |= GPIO_V2_LINE_FLAG_USED; -- cgit v1.2.3 From e6071cada1694bf13c63e31381993df494d78c42 Mon Sep 17 00:00:00 2001 From: Sergio Paracuellos Date: Mon, 7 Dec 2020 09:11:51 +0100 Subject: dt-bindings: mt7621-gpio: convert bindings to YAML format Convert the mt7621-gpio device tree bindings to the new YAML format. Signed-off-by: Sergio Paracuellos Reviewed-by: Rob Herring Link: https://lore.kernel.org/r/20201207081151.7489-1-sergio.paracuellos@gmail.com Signed-off-by: Linus Walleij --- .../bindings/gpio/mediatek,mt7621-gpio.txt | 35 ----------- .../bindings/gpio/mediatek,mt7621-gpio.yaml | 72 ++++++++++++++++++++++ 2 files changed, 72 insertions(+), 35 deletions(-) delete mode 100644 Documentation/devicetree/bindings/gpio/mediatek,mt7621-gpio.txt create mode 100644 Documentation/devicetree/bindings/gpio/mediatek,mt7621-gpio.yaml diff --git a/Documentation/devicetree/bindings/gpio/mediatek,mt7621-gpio.txt b/Documentation/devicetree/bindings/gpio/mediatek,mt7621-gpio.txt deleted file mode 100644 index e1c49b660d3a..000000000000 --- a/Documentation/devicetree/bindings/gpio/mediatek,mt7621-gpio.txt +++ /dev/null @@ -1,35 +0,0 @@ -Mediatek MT7621 SoC GPIO controller bindings - -The IP core used inside these SoCs has 3 banks of 32 GPIOs each. -The registers of all the banks are interwoven inside one single IO range. -We load one GPIO controller instance per bank. Also the GPIO controller can receive -interrupts on any of the GPIOs, either edge or level. It then interrupts the CPU -using GIC INT12. - -Required properties for the top level node: -- #gpio-cells : Should be two. The first cell is the GPIO pin number and the - second cell specifies GPIO flags, as defined in . - Only the GPIO_ACTIVE_HIGH and GPIO_ACTIVE_LOW flags are supported. -- #interrupt-cells : Specifies the number of cells needed to encode an - interrupt. Should be 2. The first cell defines the interrupt number, - the second encodes the trigger flags encoded as described in - Documentation/devicetree/bindings/interrupt-controller/interrupts.txt -- compatible: - - "mediatek,mt7621-gpio" for Mediatek controllers -- reg : Physical base address and length of the controller's registers -- interrupt-parent : phandle of the parent interrupt controller. -- interrupts : Interrupt specifier for the controllers interrupt. -- interrupt-controller : Mark the device node as an interrupt controller. -- gpio-controller : Marks the device node as a GPIO controller. - -Example: - gpio@600 { - #gpio-cells = <2>; - #interrupt-cells = <2>; - compatible = "mediatek,mt7621-gpio"; - gpio-controller; - interrupt-controller; - reg = <0x600 0x100>; - interrupt-parent = <&gic>; - interrupts = ; - }; diff --git a/Documentation/devicetree/bindings/gpio/mediatek,mt7621-gpio.yaml b/Documentation/devicetree/bindings/gpio/mediatek,mt7621-gpio.yaml new file mode 100644 index 000000000000..5bbb2a31266e --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/mediatek,mt7621-gpio.yaml @@ -0,0 +1,72 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/mediatek,mt7621-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Mediatek MT7621 SoC GPIO controller + +maintainers: + - Sergio Paracuellos + +description: | + The IP core used inside these SoCs has 3 banks of 32 GPIOs each. + The registers of all the banks are interwoven inside one single IO range. + We load one GPIO controller instance per bank. Also the GPIO controller can receive + interrupts on any of the GPIOs, either edge or level. It then interrupts the CPU + using GIC INT12. + +properties: + $nodename: + pattern: "^gpio@[0-9a-f]+$" + + compatible: + const: mediatek,mt7621-gpio + + reg: + maxItems: 1 + + "#gpio-cells": + const: 2 + + gpio-controller: true + gpio-ranges: true + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - "#gpio-cells" + - gpio-controller + - gpio-ranges + - interrupt-controller + - "#interrupt-cells" + - interrupts + +additionalProperties: false + +examples: + - | + #include + #include + + gpio@600 { + compatible = "mediatek,mt7621-gpio"; + reg = <0x600 0x100>; + #gpio-cells = <2>; + gpio-controller; + gpio-ranges = <&pinctrl 0 0 95>; + interrupt-controller; + #interrupt-cells = <2>; + interrupt-parent = <&gic>; + interrupts = ; + }; + +... -- cgit v1.2.3 From 9d5522199505c761575c8ea31dcfd9a2a8d73614 Mon Sep 17 00:00:00 2001 From: Nikita Shubin Date: Thu, 10 Dec 2020 10:05:14 +0300 Subject: gpiolib: irq hooks: fix recursion in gpiochip_irq_unmask irqchip shared with multiple gpiochips, leads to recursive call of gpiochip_irq_mask/gpiochip_irq_unmask which was assigned to rqchip->irq_mask/irqchip->irq_unmask, these happens becouse of only irqchip->irq_enable == gpiochip_irq_enable is checked. Let's add an additional check to make sure shared irqchip is detected even if irqchip->irq_enable wasn't defined. Fixes: a8173820f441 ("gpio: gpiolib: Allow GPIO IRQs to lazy disable") Signed-off-by: Nikita Shubin Link: https://lore.kernel.org/r/20201210070514.13238-1-nikita.shubin@maquefel.me Signed-off-by: Linus Walleij --- drivers/gpio/gpiolib.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 589eceecf374..5ce0c14c637b 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -1419,7 +1419,8 @@ static void gpiochip_set_irq_hooks(struct gpio_chip *gc) if (WARN_ON(gc->irq.irq_enable)) return; /* Check if the irqchip already has this hook... */ - if (irqchip->irq_enable == gpiochip_irq_enable) { + if (irqchip->irq_enable == gpiochip_irq_enable || + irqchip->irq_mask == gpiochip_irq_mask) { /* * ...and if so, give a gentle warning that this is bad * practice. -- cgit v1.2.3 From 9777d0bfdae796de3f8d73879a43bc00145dc8ee Mon Sep 17 00:00:00 2001 From: Zheng Yongjun Date: Thu, 10 Dec 2020 21:56:09 +0800 Subject: gpio: cs5535: Simplify the return expression of cs5535_gpio_probe() Simplify the return expression. Signed-off-by: Zheng Yongjun Link: https://lore.kernel.org/r/20201210135609.1372-1-zhengyongjun3@huawei.com Signed-off-by: Linus Walleij --- drivers/gpio/gpio-cs5535.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/gpio/gpio-cs5535.c b/drivers/gpio/gpio-cs5535.c index 53b24e3ae7de..6da3a247614a 100644 --- a/drivers/gpio/gpio-cs5535.c +++ b/drivers/gpio/gpio-cs5535.c @@ -345,12 +345,8 @@ static int cs5535_gpio_probe(struct platform_device *pdev) mask_orig, mask); /* finally, register with the generic GPIO API */ - err = devm_gpiochip_add_data(&pdev->dev, &cs5535_gpio_chip.chip, - &cs5535_gpio_chip); - if (err) - return err; - - return 0; + return devm_gpiochip_add_data(&pdev->dev, &cs5535_gpio_chip.chip, + &cs5535_gpio_chip); } static struct platform_driver cs5535_gpio_driver = { -- cgit v1.2.3 From 356b01a986a5550ee16dd0b85306c6741f2d02d5 Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Mon, 14 Dec 2020 16:24:13 +0800 Subject: gpio: gpio-hisi: Add HiSilicon GPIO support This GPIO driver is for HiSilicon's ARM SoC. HiSilicon's GPIO controller support double-edge interrupt and multi-core concurrent access. ACPI table example for this GPIO controller: Device (GPO0) { Name (_HID, "HISI0184") Device (PRTA) { Name (_ADR, Zero) Name (_UID, Zero) Name (_DSD, Package (0x01) { Package (0x02) { "ngpios", 0x20 } }) } } Signed-off-by: Luo Jiaxing Link: https://lore.kernel.org/r/1607934255-52544-2-git-send-email-luojiaxing@huawei.com Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 11 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-hisi.c | 328 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 340 insertions(+) create mode 100644 drivers/gpio/gpio-hisi.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 4e942318cb05..5358e9720e8c 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -298,6 +298,17 @@ config GPIO_GRGPIO Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB VHDL IP core library. +config GPIO_HISI + tristate "HiSilicon GPIO controller driver" + depends on (ARM64 || COMPILE_TEST) && ACPI + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Say Y or M here to build support for the HiSilicon GPIO controller + driver GPIO block. + This GPIO controller support double-edge interrupt and multi-core + concurrent access. + config GPIO_HLWD tristate "Nintendo Wii (Hollywood) GPIO" depends on OF_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index b6c116a7c785..35e3b6026665 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o +obj-$(CONFIG_GPIO_HISI) += gpio-hisi.o obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o diff --git a/drivers/gpio/gpio-hisi.c b/drivers/gpio/gpio-hisi.c new file mode 100644 index 000000000000..a3897800f811 --- /dev/null +++ b/drivers/gpio/gpio-hisi.c @@ -0,0 +1,328 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Copyright (c) 2020 HiSilicon Limited. */ +#include +#include +#include +#include +#include + +#define HISI_GPIO_SWPORT_DR_SET_WX 0x000 +#define HISI_GPIO_SWPORT_DR_CLR_WX 0x004 +#define HISI_GPIO_SWPORT_DDR_SET_WX 0x010 +#define HISI_GPIO_SWPORT_DDR_CLR_WX 0x014 +#define HISI_GPIO_SWPORT_DDR_ST_WX 0x018 +#define HISI_GPIO_INTEN_SET_WX 0x020 +#define HISI_GPIO_INTEN_CLR_WX 0x024 +#define HISI_GPIO_INTMASK_SET_WX 0x030 +#define HISI_GPIO_INTMASK_CLR_WX 0x034 +#define HISI_GPIO_INTTYPE_EDGE_SET_WX 0x040 +#define HISI_GPIO_INTTYPE_EDGE_CLR_WX 0x044 +#define HISI_GPIO_INT_POLARITY_SET_WX 0x050 +#define HISI_GPIO_INT_POLARITY_CLR_WX 0x054 +#define HISI_GPIO_DEBOUNCE_SET_WX 0x060 +#define HISI_GPIO_DEBOUNCE_CLR_WX 0x064 +#define HISI_GPIO_INTSTATUS_WX 0x070 +#define HISI_GPIO_PORTA_EOI_WX 0x078 +#define HISI_GPIO_EXT_PORT_WX 0x080 +#define HISI_GPIO_INTCOMB_MASK_WX 0x0a0 +#define HISI_GPIO_INT_DEDGE_SET 0x0b0 +#define HISI_GPIO_INT_DEDGE_CLR 0x0b4 +#define HISI_GPIO_INT_DEDGE_ST 0x0b8 + +#define HISI_GPIO_LINE_NUM_MAX 32 +#define HISI_GPIO_DRIVER_NAME "gpio-hisi" + +struct hisi_gpio { + struct gpio_chip chip; + struct device *dev; + void __iomem *reg_base; + unsigned int line_num; + struct irq_chip irq_chip; + int irq; +}; + +static inline u32 hisi_gpio_read_reg(struct gpio_chip *chip, + unsigned int off) +{ + struct hisi_gpio *hisi_gpio = + container_of(chip, struct hisi_gpio, chip); + void __iomem *reg = hisi_gpio->reg_base + off; + + return readl(reg); +} + +static inline void hisi_gpio_write_reg(struct gpio_chip *chip, + unsigned int off, u32 val) +{ + struct hisi_gpio *hisi_gpio = + container_of(chip, struct hisi_gpio, chip); + void __iomem *reg = hisi_gpio->reg_base + off; + + writel(val, reg); +} + +static void hisi_gpio_set_debounce(struct gpio_chip *chip, unsigned int off, + u32 debounce) +{ + if (debounce) + hisi_gpio_write_reg(chip, HISI_GPIO_DEBOUNCE_SET_WX, BIT(off)); + else + hisi_gpio_write_reg(chip, HISI_GPIO_DEBOUNCE_CLR_WX, BIT(off)); +} + +static int hisi_gpio_set_config(struct gpio_chip *chip, unsigned int offset, + unsigned long config) +{ + u32 config_para = pinconf_to_config_param(config); + u32 config_arg; + + switch (config_para) { + case PIN_CONFIG_INPUT_DEBOUNCE: + config_arg = pinconf_to_config_argument(config); + hisi_gpio_set_debounce(chip, offset, config_arg); + break; + default: + return -ENOTSUPP; + } + + return 0; +} + +static void hisi_gpio_set_ack(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + + hisi_gpio_write_reg(chip, HISI_GPIO_PORTA_EOI_WX, BIT(irqd_to_hwirq(d))); +} + +static void hisi_gpio_irq_set_mask(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + + hisi_gpio_write_reg(chip, HISI_GPIO_INTMASK_SET_WX, BIT(irqd_to_hwirq(d))); +} + +static void hisi_gpio_irq_clr_mask(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + + hisi_gpio_write_reg(chip, HISI_GPIO_INTMASK_CLR_WX, BIT(irqd_to_hwirq(d))); +} + +static int hisi_gpio_irq_set_type(struct irq_data *d, u32 type) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + unsigned int mask = BIT(irqd_to_hwirq(d)); + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + hisi_gpio_write_reg(chip, HISI_GPIO_INT_DEDGE_SET, mask); + break; + case IRQ_TYPE_EDGE_RISING: + hisi_gpio_write_reg(chip, HISI_GPIO_INTTYPE_EDGE_SET_WX, mask); + hisi_gpio_write_reg(chip, HISI_GPIO_INT_POLARITY_SET_WX, mask); + break; + case IRQ_TYPE_EDGE_FALLING: + hisi_gpio_write_reg(chip, HISI_GPIO_INTTYPE_EDGE_SET_WX, mask); + hisi_gpio_write_reg(chip, HISI_GPIO_INT_POLARITY_CLR_WX, mask); + break; + case IRQ_TYPE_LEVEL_HIGH: + hisi_gpio_write_reg(chip, HISI_GPIO_INTTYPE_EDGE_CLR_WX, mask); + hisi_gpio_write_reg(chip, HISI_GPIO_INT_POLARITY_SET_WX, mask); + break; + case IRQ_TYPE_LEVEL_LOW: + hisi_gpio_write_reg(chip, HISI_GPIO_INTTYPE_EDGE_CLR_WX, mask); + hisi_gpio_write_reg(chip, HISI_GPIO_INT_POLARITY_CLR_WX, mask); + break; + default: + return -EINVAL; + } + + /* + * The dual-edge interrupt and other interrupt's registers do not + * take effect at the same time. The registers of the two-edge + * interrupts have higher priorities, the configuration of + * the dual-edge interrupts must be disabled before the configuration + * of other kind of interrupts. + */ + if (type != IRQ_TYPE_EDGE_BOTH) { + unsigned int both = hisi_gpio_read_reg(chip, HISI_GPIO_INT_DEDGE_ST); + + if (both & mask) + hisi_gpio_write_reg(chip, HISI_GPIO_INT_DEDGE_CLR, mask); + } + + if (type & IRQ_TYPE_LEVEL_MASK) + irq_set_handler_locked(d, handle_level_irq); + else if (type & IRQ_TYPE_EDGE_BOTH) + irq_set_handler_locked(d, handle_edge_irq); + + return 0; +} + +static void hisi_gpio_irq_enable(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + + hisi_gpio_irq_clr_mask(d); + hisi_gpio_write_reg(chip, HISI_GPIO_INTEN_SET_WX, BIT(irqd_to_hwirq(d))); +} + +static void hisi_gpio_irq_disable(struct irq_data *d) +{ + struct gpio_chip *chip = irq_data_get_irq_chip_data(d); + + hisi_gpio_irq_set_mask(d); + hisi_gpio_write_reg(chip, HISI_GPIO_INTEN_CLR_WX, BIT(irqd_to_hwirq(d))); +} + +static void hisi_gpio_irq_handler(struct irq_desc *desc) +{ + struct hisi_gpio *hisi_gpio = irq_desc_get_handler_data(desc); + unsigned long irq_msk = hisi_gpio_read_reg(&hisi_gpio->chip, + HISI_GPIO_INTSTATUS_WX); + struct irq_chip *irq_c = irq_desc_get_chip(desc); + int hwirq; + + chained_irq_enter(irq_c, desc); + for_each_set_bit(hwirq, &irq_msk, HISI_GPIO_LINE_NUM_MAX) + generic_handle_irq(irq_find_mapping(hisi_gpio->chip.irq.domain, + hwirq)); + chained_irq_exit(irq_c, desc); +} + +static void hisi_gpio_init_irq(struct hisi_gpio *hisi_gpio) +{ + struct gpio_chip *chip = &hisi_gpio->chip; + struct gpio_irq_chip *girq_chip = &chip->irq; + + /* Set hooks for irq_chip */ + hisi_gpio->irq_chip.irq_ack = hisi_gpio_set_ack; + hisi_gpio->irq_chip.irq_mask = hisi_gpio_irq_set_mask; + hisi_gpio->irq_chip.irq_unmask = hisi_gpio_irq_clr_mask; + hisi_gpio->irq_chip.irq_set_type = hisi_gpio_irq_set_type; + hisi_gpio->irq_chip.irq_enable = hisi_gpio_irq_enable; + hisi_gpio->irq_chip.irq_disable = hisi_gpio_irq_disable; + + girq_chip->chip = &hisi_gpio->irq_chip; + girq_chip->default_type = IRQ_TYPE_NONE; + girq_chip->num_parents = 1; + girq_chip->parents = &hisi_gpio->irq; + girq_chip->parent_handler = hisi_gpio_irq_handler; + girq_chip->parent_handler_data = hisi_gpio; + + /* Clear Mask of GPIO controller combine IRQ */ + hisi_gpio_write_reg(chip, HISI_GPIO_INTCOMB_MASK_WX, 1); +} + +static const struct acpi_device_id hisi_gpio_acpi_match[] = { + {"HISI0184", 0}, + {} +}; +MODULE_DEVICE_TABLE(acpi, hisi_gpio_acpi_match); + +static void hisi_gpio_get_pdata(struct device *dev, + struct hisi_gpio *hisi_gpio) +{ + struct platform_device *pdev = to_platform_device(dev); + struct fwnode_handle *fwnode; + int idx = 0; + + device_for_each_child_node(dev, fwnode) { + /* Cycle for once, no need for an array to save line_num */ + if (fwnode_property_read_u32(fwnode, "ngpios", + &hisi_gpio->line_num)) { + dev_err(dev, + "failed to get number of lines for port%d and use default value instead\n", + idx); + hisi_gpio->line_num = HISI_GPIO_LINE_NUM_MAX; + } + + if (WARN_ON(hisi_gpio->line_num > HISI_GPIO_LINE_NUM_MAX)) + hisi_gpio->line_num = HISI_GPIO_LINE_NUM_MAX; + + hisi_gpio->irq = platform_get_irq(pdev, idx); + + dev_info(dev, + "get hisi_gpio[%d] with %d lines\n", idx, + hisi_gpio->line_num); + + idx++; + } +} + +static int hisi_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + void __iomem *dat, *set, *clr; + struct hisi_gpio *hisi_gpio; + int port_num; + int ret; + + /* + * One GPIO controller own one port currently, + * if we get more from ACPI table, return error. + */ + port_num = device_get_child_node_count(dev); + if (WARN_ON(port_num != 1)) + return -ENODEV; + + hisi_gpio = devm_kzalloc(dev, sizeof(*hisi_gpio), GFP_KERNEL); + if (!hisi_gpio) + return -ENOMEM; + + hisi_gpio->reg_base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(hisi_gpio->reg_base)) + return PTR_ERR(hisi_gpio->reg_base); + + hisi_gpio_get_pdata(dev, hisi_gpio); + + hisi_gpio->dev = dev; + + dat = hisi_gpio->reg_base + HISI_GPIO_EXT_PORT_WX; + set = hisi_gpio->reg_base + HISI_GPIO_SWPORT_DR_SET_WX; + clr = hisi_gpio->reg_base + HISI_GPIO_SWPORT_DR_CLR_WX; + + ret = bgpio_init(&hisi_gpio->chip, hisi_gpio->dev, 0x4, + hisi_gpio->reg_base + HISI_GPIO_EXT_PORT_WX, + hisi_gpio->reg_base + HISI_GPIO_SWPORT_DR_SET_WX, + hisi_gpio->reg_base + HISI_GPIO_SWPORT_DR_CLR_WX, + hisi_gpio->reg_base + HISI_GPIO_SWPORT_DDR_SET_WX, + hisi_gpio->reg_base + HISI_GPIO_SWPORT_DDR_CLR_WX, + BGPIOF_NO_SET_ON_INPUT); + if (ret) { + dev_err(dev, "failed to init, ret = %d\n", ret); + return ret; + } + + hisi_gpio->chip.set_config = hisi_gpio_set_config; + hisi_gpio->chip.ngpio = hisi_gpio->line_num; + hisi_gpio->chip.bgpio_dir_unreadable = 1; + hisi_gpio->chip.base = -1; + + if (hisi_gpio->irq > 0) + hisi_gpio_init_irq(hisi_gpio); + + ret = devm_gpiochip_add_data(dev, &hisi_gpio->chip, hisi_gpio); + if (ret) { + dev_err(dev, "failed to register gpiochip, ret = %d\n", ret); + return ret; + } + + return 0; +} + +static struct platform_driver hisi_gpio_driver = { + .driver = { + .name = HISI_GPIO_DRIVER_NAME, + .acpi_match_table = hisi_gpio_acpi_match, + }, + .probe = hisi_gpio_probe, +}; + +module_platform_driver(hisi_gpio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Luo Jiaxing "); +MODULE_DESCRIPTION("HiSilicon GPIO controller driver"); +MODULE_ALIAS("platform:" HISI_GPIO_DRIVER_NAME); -- cgit v1.2.3 From 80e493d2b74af7442eac968c060b26adbfaa96f9 Mon Sep 17 00:00:00 2001 From: Luo Jiaxing Date: Mon, 14 Dec 2020 16:24:14 +0800 Subject: MAINTAINERS: Add maintainer for HiSilicon GPIO driver Here add maintainer information for HiSilicon GPIO driver. Signed-off-by: Luo Jiaxing Link: https://lore.kernel.org/r/1607934255-52544-3-git-send-email-luojiaxing@huawei.com [Dropped some dead code when applying] Signed-off-by: Linus Walleij --- MAINTAINERS | 7 +++++++ drivers/gpio/gpio-hisi.c | 5 ----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 8f18a5b33a60..42ef0200c962 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7908,6 +7908,13 @@ L: dmaengine@vger.kernel.org S: Maintained F: drivers/dma/hisi_dma.c +HISILICON GPIO DRIVER +M: Luo Jiaxing +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-hisi.c +F: include/linux/platform_data/gpio-hisi.h + HISILICON HIGH PERFORMANCE RSA ENGINE DRIVER (HPRE) M: Zaibo Xu L: linux-crypto@vger.kernel.org diff --git a/drivers/gpio/gpio-hisi.c b/drivers/gpio/gpio-hisi.c index a3897800f811..ad3d4da25160 100644 --- a/drivers/gpio/gpio-hisi.c +++ b/drivers/gpio/gpio-hisi.c @@ -254,7 +254,6 @@ static void hisi_gpio_get_pdata(struct device *dev, static int hisi_gpio_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - void __iomem *dat, *set, *clr; struct hisi_gpio *hisi_gpio; int port_num; int ret; @@ -279,10 +278,6 @@ static int hisi_gpio_probe(struct platform_device *pdev) hisi_gpio->dev = dev; - dat = hisi_gpio->reg_base + HISI_GPIO_EXT_PORT_WX; - set = hisi_gpio->reg_base + HISI_GPIO_SWPORT_DR_SET_WX; - clr = hisi_gpio->reg_base + HISI_GPIO_SWPORT_DR_CLR_WX; - ret = bgpio_init(&hisi_gpio->chip, hisi_gpio->dev, 0x4, hisi_gpio->reg_base + HISI_GPIO_EXT_PORT_WX, hisi_gpio->reg_base + HISI_GPIO_SWPORT_DR_SET_WX, -- cgit v1.2.3 From 72db5d54d76300fb4153874b8466c0ebbb10208f Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 14 Dec 2020 18:55:23 +0200 Subject: gpio: hisi: Do not require ACPI for COMPILE_TEST Make it clear that ACPI needs to be present only to get driver functional. It is not required for compilation. Fixes: 356b01a986a5 ("gpio: gpio-hisi: Add HiSilicon GPIO support") Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20201214165524.43843-1-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- drivers/gpio/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 5358e9720e8c..c70f46e80a3b 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -300,7 +300,7 @@ config GPIO_GRGPIO config GPIO_HISI tristate "HiSilicon GPIO controller driver" - depends on (ARM64 || COMPILE_TEST) && ACPI + depends on (ARM64 && ACPI) || COMPILE_TEST select GPIO_GENERIC select GPIOLIB_IRQCHIP help -- cgit v1.2.3 From 7ac554888233468a9fd7c4f28721396952dd9959 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 14 Dec 2020 18:55:24 +0200 Subject: MAINTAINERS: Remove reference to non-existing file GPIO HiSilicon driver doesn't provide any platform data header. Fixes: a8f25236e6e3 ("MAINTAINERS: Add maintainer for HiSilicon GPIO driver") Signed-off-by: Andy Shevchenko Link: https://lore.kernel.org/r/20201214165524.43843-2-andriy.shevchenko@linux.intel.com Signed-off-by: Linus Walleij --- MAINTAINERS | 1 - 1 file changed, 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 42ef0200c962..40cd101eea7d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7913,7 +7913,6 @@ M: Luo Jiaxing L: linux-gpio@vger.kernel.org S: Maintained F: drivers/gpio/gpio-hisi.c -F: include/linux/platform_data/gpio-hisi.h HISILICON HIGH PERFORMANCE RSA ENGINE DRIVER (HPRE) M: Zaibo Xu -- cgit v1.2.3