diff options
Diffstat (limited to 'drivers/gpio')
67 files changed, 3101 insertions, 985 deletions
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index f2c39bbff83a..44f922e10db2 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -201,6 +201,7 @@ config GPIO_RASPBERRYPI_EXP config GPIO_BCM_KONA bool "Broadcom Kona GPIO" depends on ARCH_BCM_MOBILE || COMPILE_TEST + select GPIOLIB_IRQCHIP help Turn on GPIO support for Broadcom "Kona" chips. @@ -213,6 +214,18 @@ config GPIO_BCM_XGS_IPROC help Say yes here to enable GPIO support for Broadcom XGS iProc SoCs. +config GPIO_BLZP1600 + tristate "Blaize BLZP1600 GPIO support" + default y if ARCH_BLAIZE + depends on ARCH_BLAIZE || COMPILE_TEST + depends on OF_GPIO + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Say Y or M here to add support for the Blaize BLZP1600 GPIO device. + The controller is based on the Verisilicon Microelectronics GPIO APB v0.2 + IP block. + config GPIO_BRCMSTB tristate "BRCMSTB GPIO support" default y if (ARCH_BRCMSTB || BMIPS_GENERIC) @@ -241,6 +254,7 @@ config GPIO_DAVINCI tristate "TI Davinci/Keystone GPIO support" default y if ARCH_DAVINCI depends on ((ARM || ARM64) && (ARCH_DAVINCI || ARCH_KEYSTONE || ARCH_K3)) || COMPILE_TEST + select GPIOLIB_IRQCHIP help Say yes here to enable GPIO support for TI Davinci/Keystone SoCs. @@ -340,7 +354,7 @@ config GPIO_GRGPIO tristate "Aeroflex Gaisler GRGPIO support" depends on OF || COMPILE_TEST select GPIO_GENERIC - select IRQ_DOMAIN + select GPIOLIB_IRQCHIP help Select this to support Aeroflex Gaisler GRGPIO cores from the GRLIB VHDL IP core library. @@ -368,8 +382,7 @@ config GPIO_HLWD config GPIO_ICH tristate "Intel ICH GPIO" - depends on X86 - depends on LPC_ICH + depends on (X86 && LPC_ICH) || (COMPILE_TEST && HAS_IOPORT) help Say yes here to support the GPIO functionality of a number of Intel ICH-based chipsets. Currently supported devices: ICH6, ICH7, ICH8 @@ -425,6 +438,7 @@ config GPIO_LPC18XX default y if ARCH_LPC18XX depends on OF_GPIO && (ARCH_LPC18XX || COMPILE_TEST) select IRQ_DOMAIN_HIERARCHY + select GPIOLIB_IRQCHIP help Select this option to enable GPIO driver for NXP LPC18XX/43XX devices. @@ -468,7 +482,7 @@ config GPIO_MPC8XXX FSL_SOC_BOOKE || PPC_86xx || ARCH_LAYERSCAPE || ARM || \ COMPILE_TEST select GPIO_GENERIC - select IRQ_DOMAIN + select GPIOLIB_IRQCHIP help Say Y here if you're going to use hardware that connects to the MPC512x/831x/834x/837x/8572/8610/QorIQ GPIOs. @@ -540,7 +554,7 @@ config GPIO_OMAP config GPIO_PL061 tristate "PrimeCell PL061 GPIO support" - depends on ARM_AMBA + depends on ARM_AMBA || COMPILE_TEST select IRQ_DOMAIN select GPIOLIB_IRQCHIP help @@ -555,6 +569,7 @@ config GPIO_POLARFIRE_SOC config GPIO_PXA bool "PXA GPIO support" depends on ARCH_PXA || ARCH_MMP || COMPILE_TEST + select GPIOLIB_IRQCHIP help Say yes here to support the PXA GPIO device. @@ -604,7 +619,7 @@ config GPIO_ROCKCHIP config GPIO_RTD tristate "Realtek DHC GPIO support" - depends on ARCH_REALTEK + depends on ARCH_REALTEK || COMPILE_TEST default y select GPIOLIB_IRQCHIP help @@ -656,6 +671,15 @@ config GPIO_SNPS_CREG where only several fields in register belong to GPIO lines and each GPIO line owns a field with different length and on/off value. +config GPIO_SPACEMIT_K1 + tristate "SPACEMIT K1 GPIO support" + depends on ARCH_SPACEMIT || COMPILE_TEST + depends on OF_GPIO + select GPIO_GENERIC + select GPIOLIB_IRQCHIP + help + Say yes here to support the SpacemiT's K1 GPIO device. + config GPIO_SPEAR_SPICS bool "ST SPEAr13xx SPI Chip Select as GPIO support" depends on PLAT_SPEAR @@ -753,7 +777,7 @@ config GPIO_UNIPHIER Say yes here to support UniPhier GPIOs. config GPIO_VF610 - bool "VF610 GPIO support" + tristate "VF610 GPIO support" default y if SOC_VF610 depends on ARCH_MXC || COMPILE_TEST select GPIOLIB_IRQCHIP @@ -830,14 +854,14 @@ config GPIO_ZEVIO config GPIO_ZYNQ tristate "Xilinx Zynq GPIO support" - depends on ARCH_ZYNQ || ARCH_ZYNQMP + depends on ARCH_ZYNQ || ARCH_ZYNQMP || COMPILE_TEST select GPIOLIB_IRQCHIP help Say yes here to support Xilinx Zynq GPIO controller. config GPIO_ZYNQMP_MODEPIN tristate "ZynqMP ps-mode pin GPIO configuration driver" - depends on ZYNQMP_FIRMWARE + depends on ZYNQMP_FIRMWARE || COMPILE_TEST default ZYNQMP_FIRMWARE help Say yes here to support the ZynqMP ps-mode pin GPIO configuration @@ -866,7 +890,7 @@ config GPIO_AMD_FCH config GPIO_MSC313 bool "MStar MSC313 GPIO support" - depends on ARCH_MSTARV7 + depends on ARCH_MSTARV7 || COMPILE_TEST default ARCH_MSTARV7 select GPIOLIB_IRQCHIP select IRQ_DOMAIN_HIERARCHY @@ -1365,7 +1389,7 @@ config GPIO_DLN2 config HTC_EGPIO bool "HTC EGPIO support" - depends on ARM + depends on ARM || COMPILE_TEST help This driver supports the CPLD egpio chip present on several HTC phones. It provides basic support for input @@ -1463,6 +1487,19 @@ config GPIO_MAX77650 GPIO driver for MAX77650/77651 PMIC from Maxim Semiconductor. These chips have a single pin that can be configured as GPIO. +config GPIO_MAX77759 + tristate "Maxim Integrated MAX77759 GPIO support" + depends on MFD_MAX77759 + default MFD_MAX77759 + select GPIOLIB_IRQCHIP + help + GPIO driver for MAX77759 PMIC from Maxim Integrated. + There are two GPIOs available on these chips in total, both of + which can also generate interrupts. + + This driver can also be built as a module. If so, the module will be + called gpio-max77759. + config GPIO_PALMAS bool "TI PALMAS series PMICs GPIO" depends on MFD_PALMAS @@ -1520,12 +1557,13 @@ config GPIO_TC3589X config GPIO_TIMBERDALE bool "Support for timberdale GPIO IP" depends on MFD_TIMBERDALE + select GPIOLIB_IRQCHIP help Add support for the GPIO IP in the timberdale FPGA. config GPIO_TN48M_CPLD tristate "Delta Networks TN48M switch CPLD GPIO driver" - depends on MFD_TN48M_CPLD + depends on MFD_TN48M_CPLD || COMPILE_TEST select GPIO_REGMAP help This enables support for the GPIOs found on the Delta @@ -1869,6 +1907,8 @@ menu "Virtual GPIO drivers" config GPIO_AGGREGATOR tristate "GPIO Aggregator" + select CONFIGFS_FS + select DEV_SYNC_PROBE help Say yes here to enable the GPIO Aggregator, which provides a way to aggregate existing GPIO lines into a new virtual GPIO chip. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index af130882ffee..88dedd298256 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_OF_GPIO) += gpiolib-of.o obj-$(CONFIG_GPIO_CDEV) += gpiolib-cdev.o obj-$(CONFIG_GPIO_SYSFS) += gpiolib-sysfs.o obj-$(CONFIG_GPIO_ACPI) += gpiolib-acpi.o +gpiolib-acpi-y := gpiolib-acpi-core.o gpiolib-acpi-quirks.o obj-$(CONFIG_GPIOLIB) += gpiolib-swnode.o # Device drivers. Generally keep list sorted alphabetically @@ -45,6 +46,7 @@ obj-$(CONFIG_GPIO_BCM_XGS_IPROC) += gpio-xgs-iproc.o obj-$(CONFIG_GPIO_BD71815) += gpio-bd71815.o obj-$(CONFIG_GPIO_BD71828) += gpio-bd71828.o obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o +obj-$(CONFIG_GPIO_BLZP1600) += gpio-blzp1600.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o @@ -105,6 +107,7 @@ obj-$(CONFIG_GPIO_MAX730X) += gpio-max730x.o obj-$(CONFIG_GPIO_MAX732X) += gpio-max732x.o obj-$(CONFIG_GPIO_MAX77620) += gpio-max77620.o obj-$(CONFIG_GPIO_MAX77650) += gpio-max77650.o +obj-$(CONFIG_GPIO_MAX77759) += gpio-max77759.o obj-$(CONFIG_GPIO_MB86S7X) += gpio-mb86s7x.o obj-$(CONFIG_GPIO_MC33880) += gpio-mc33880.o obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o @@ -159,6 +162,7 @@ obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o obj-$(CONFIG_GPIO_SLOPPY_LOGIC_ANALYZER) += gpio-sloppy-logic-analyzer.o obj-$(CONFIG_GPIO_SODAVILLE) += gpio-sodaville.o +obj-$(CONFIG_GPIO_SPACEMIT_K1) += gpio-spacemit-k1.o obj-$(CONFIG_GPIO_SPEAR_SPICS) += gpio-spear-spics.o obj-$(CONFIG_GPIO_SPRD) += gpio-sprd.o obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o diff --git a/drivers/gpio/TODO b/drivers/gpio/TODO index 4b70cbaa1caa..4a8b349f2483 100644 --- a/drivers/gpio/TODO +++ b/drivers/gpio/TODO @@ -44,6 +44,13 @@ Work items: to a machine description such as device tree, ACPI or fwnode that implicitly does not use global GPIO numbers. +- Fix drivers to not read back struct gpio_chip::base. Some drivers do + that and would be broken by attempts to poison it or make it dynamic. + Example in AT91 pinctrl driver: + https://lore.kernel.org/all/1d00c056-3d61-4c22-bedd-3bae0bf1ddc4@pengutronix.de/ + This particular driver is also DT-only, so with the above fixed, the + base can be made dynamic (set to -1) if CONFIG_GPIO_SYSFS is disabled. + - When this work is complete (will require some of the items in the following ongoing work as well) we can delete the old global numberspace accessors from <linux/gpio.h> and eventually delete diff --git a/drivers/gpio/gpio-aggregator.c b/drivers/gpio/gpio-aggregator.c index d232ea865356..6f941db02c04 100644 --- a/drivers/gpio/gpio-aggregator.c +++ b/drivers/gpio/gpio-aggregator.c @@ -9,10 +9,13 @@ #include <linux/bitmap.h> #include <linux/bitops.h> +#include <linux/configfs.h> #include <linux/ctype.h> #include <linux/delay.h> #include <linux/idr.h> #include <linux/kernel.h> +#include <linux/list.h> +#include <linux/lockdep.h> #include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> @@ -27,226 +30,200 @@ #include <linux/gpio/driver.h> #include <linux/gpio/machine.h> +#include "dev-sync-probe.h" + #define AGGREGATOR_MAX_GPIOS 512 +#define AGGREGATOR_LEGACY_PREFIX "_sysfs" /* * GPIO Aggregator sysfs interface */ struct gpio_aggregator { + struct dev_sync_probe_data probe_data; + struct config_group group; struct gpiod_lookup_table *lookups; - struct platform_device *pdev; + struct mutex lock; + int id; + + /* List of gpio_aggregator_line. Always added in order */ + struct list_head list_head; + + /* used by legacy sysfs interface only */ + bool init_via_sysfs; char args[]; }; +struct gpio_aggregator_line { + struct config_group group; + struct gpio_aggregator *parent; + struct list_head entry; + + /* Line index within the aggregator device */ + unsigned int idx; + + /* Custom name for the virtual line */ + const char *name; + /* GPIO chip label or line name */ + const char *key; + /* Can be negative to indicate lookup by line name */ + int offset; + + enum gpio_lookup_flags flags; +}; + +struct gpio_aggregator_pdev_meta { + bool init_via_sysfs; +}; + static DEFINE_MUTEX(gpio_aggregator_lock); /* protects idr */ static DEFINE_IDR(gpio_aggregator_idr); -static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key, - int hwnum, unsigned int *n) +static int gpio_aggregator_alloc(struct gpio_aggregator **aggr, size_t arg_size) { - struct gpiod_lookup_table *lookups; + int ret; - lookups = krealloc(aggr->lookups, struct_size(lookups, table, *n + 2), - GFP_KERNEL); - if (!lookups) + struct gpio_aggregator *new __free(kfree) = kzalloc( + sizeof(*new) + arg_size, GFP_KERNEL); + if (!new) return -ENOMEM; - lookups->table[*n] = GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0); + scoped_guard(mutex, &gpio_aggregator_lock) + ret = idr_alloc(&gpio_aggregator_idr, new, 0, 0, GFP_KERNEL); - (*n)++; - memset(&lookups->table[*n], 0, sizeof(lookups->table[*n])); + if (ret < 0) + return ret; - aggr->lookups = lookups; + new->id = ret; + INIT_LIST_HEAD(&new->list_head); + mutex_init(&new->lock); + *aggr = no_free_ptr(new); return 0; } -static int aggr_parse(struct gpio_aggregator *aggr) +static void gpio_aggregator_free(struct gpio_aggregator *aggr) { - char *args = skip_spaces(aggr->args); - char *name, *offsets, *p; - unsigned int i, n = 0; - int error = 0; - - unsigned long *bitmap __free(bitmap) = - bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL); - if (!bitmap) - return -ENOMEM; - - args = next_arg(args, &name, &p); - while (*args) { - args = next_arg(args, &offsets, &p); - - p = get_options(offsets, 0, &error); - if (error == 0 || *p) { - /* Named GPIO line */ - error = aggr_add_gpio(aggr, name, U16_MAX, &n); - if (error) - return error; + scoped_guard(mutex, &gpio_aggregator_lock) + idr_remove(&gpio_aggregator_idr, aggr->id); - name = offsets; - continue; - } + mutex_destroy(&aggr->lock); + kfree(aggr); +} - /* GPIO chip + offset(s) */ - error = bitmap_parselist(offsets, bitmap, AGGREGATOR_MAX_GPIOS); - if (error) { - pr_err("Cannot parse %s: %d\n", offsets, error); - return error; - } +static int gpio_aggregator_add_gpio(struct gpio_aggregator *aggr, + const char *key, int hwnum, unsigned int *n) +{ + struct gpiod_lookup_table *lookups; - for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { - error = aggr_add_gpio(aggr, name, i, &n); - if (error) - return error; - } + lookups = krealloc(aggr->lookups, struct_size(lookups, table, *n + 2), + GFP_KERNEL); + if (!lookups) + return -ENOMEM; - args = next_arg(args, &name, &p); - } + lookups->table[*n] = GPIO_LOOKUP_IDX(key, hwnum, NULL, *n, 0); - if (!n) { - pr_err("No GPIOs specified\n"); - return -EINVAL; - } + (*n)++; + memset(&lookups->table[*n], 0, sizeof(lookups->table[*n])); + aggr->lookups = lookups; return 0; } -static ssize_t new_device_store(struct device_driver *driver, const char *buf, - size_t count) +static bool gpio_aggregator_is_active(struct gpio_aggregator *aggr) { - struct gpio_aggregator *aggr; - struct platform_device *pdev; - int res, id; + lockdep_assert_held(&aggr->lock); - if (!try_module_get(THIS_MODULE)) - return -ENOENT; - - /* kernfs guarantees string termination, so count + 1 is safe */ - aggr = kzalloc(sizeof(*aggr) + count + 1, GFP_KERNEL); - if (!aggr) { - res = -ENOMEM; - goto put_module; - } - - memcpy(aggr->args, buf, count + 1); + return aggr->probe_data.pdev && platform_get_drvdata(aggr->probe_data.pdev); +} - aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1), - GFP_KERNEL); - if (!aggr->lookups) { - res = -ENOMEM; - goto free_ga; - } +/* Only aggregators created via legacy sysfs can be "activating". */ +static bool gpio_aggregator_is_activating(struct gpio_aggregator *aggr) +{ + lockdep_assert_held(&aggr->lock); - mutex_lock(&gpio_aggregator_lock); - id = idr_alloc(&gpio_aggregator_idr, aggr, 0, 0, GFP_KERNEL); - mutex_unlock(&gpio_aggregator_lock); + return aggr->probe_data.pdev && !platform_get_drvdata(aggr->probe_data.pdev); +} - if (id < 0) { - res = id; - goto free_table; - } +static size_t gpio_aggregator_count_lines(struct gpio_aggregator *aggr) +{ + lockdep_assert_held(&aggr->lock); - aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, id); - if (!aggr->lookups->dev_id) { - res = -ENOMEM; - goto remove_idr; - } + return list_count_nodes(&aggr->list_head); +} - res = aggr_parse(aggr); - if (res) - goto free_dev_id; +static struct gpio_aggregator_line * +gpio_aggregator_line_alloc(struct gpio_aggregator *parent, unsigned int idx, + char *key, int offset) +{ + struct gpio_aggregator_line *line; - gpiod_add_lookup_table(aggr->lookups); + line = kzalloc(sizeof(*line), GFP_KERNEL); + if (!line) + return ERR_PTR(-ENOMEM); - pdev = platform_device_register_simple(DRV_NAME, id, NULL, 0); - if (IS_ERR(pdev)) { - res = PTR_ERR(pdev); - goto remove_table; + if (key) { + line->key = kstrdup(key, GFP_KERNEL); + if (!line->key) { + kfree(line); + return ERR_PTR(-ENOMEM); + } } - aggr->pdev = pdev; - module_put(THIS_MODULE); - return count; + line->flags = GPIO_LOOKUP_FLAGS_DEFAULT; + line->parent = parent; + line->idx = idx; + line->offset = offset; + INIT_LIST_HEAD(&line->entry); -remove_table: - gpiod_remove_lookup_table(aggr->lookups); -free_dev_id: - kfree(aggr->lookups->dev_id); -remove_idr: - mutex_lock(&gpio_aggregator_lock); - idr_remove(&gpio_aggregator_idr, id); - mutex_unlock(&gpio_aggregator_lock); -free_table: - kfree(aggr->lookups); -free_ga: - kfree(aggr); -put_module: - module_put(THIS_MODULE); - return res; -} - -static DRIVER_ATTR_WO(new_device); - -static void gpio_aggregator_free(struct gpio_aggregator *aggr) -{ - platform_device_unregister(aggr->pdev); - gpiod_remove_lookup_table(aggr->lookups); - kfree(aggr->lookups->dev_id); - kfree(aggr->lookups); - kfree(aggr); + return line; } -static ssize_t delete_device_store(struct device_driver *driver, - const char *buf, size_t count) +static void gpio_aggregator_line_add(struct gpio_aggregator *aggr, + struct gpio_aggregator_line *line) { - struct gpio_aggregator *aggr; - unsigned int id; - int error; + struct gpio_aggregator_line *tmp; - if (!str_has_prefix(buf, DRV_NAME ".")) - return -EINVAL; + lockdep_assert_held(&aggr->lock); - error = kstrtouint(buf + strlen(DRV_NAME "."), 10, &id); - if (error) - return error; - - if (!try_module_get(THIS_MODULE)) - return -ENOENT; - - mutex_lock(&gpio_aggregator_lock); - aggr = idr_remove(&gpio_aggregator_idr, id); - mutex_unlock(&gpio_aggregator_lock); - if (!aggr) { - module_put(THIS_MODULE); - return -ENOENT; + list_for_each_entry(tmp, &aggr->list_head, entry) { + if (tmp->idx > line->idx) { + list_add_tail(&line->entry, &tmp->entry); + return; + } } - - gpio_aggregator_free(aggr); - module_put(THIS_MODULE); - return count; + list_add_tail(&line->entry, &aggr->list_head); } -static DRIVER_ATTR_WO(delete_device); - -static struct attribute *gpio_aggregator_attrs[] = { - &driver_attr_new_device.attr, - &driver_attr_delete_device.attr, - NULL -}; -ATTRIBUTE_GROUPS(gpio_aggregator); -static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data) +static void gpio_aggregator_line_del(struct gpio_aggregator *aggr, + struct gpio_aggregator_line *line) { - gpio_aggregator_free(p); - return 0; + lockdep_assert_held(&aggr->lock); + + list_del(&line->entry); } -static void __exit gpio_aggregator_remove_all(void) +static void gpio_aggregator_free_lines(struct gpio_aggregator *aggr) { - mutex_lock(&gpio_aggregator_lock); - idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL); - idr_destroy(&gpio_aggregator_idr); - mutex_unlock(&gpio_aggregator_lock); + struct gpio_aggregator_line *line, *tmp; + + list_for_each_entry_safe(line, tmp, &aggr->list_head, entry) { + configfs_unregister_group(&line->group); + /* + * Normally, we acquire aggr->lock within the configfs + * callback. However, in the legacy sysfs interface case, + * calling configfs_(un)register_group while holding + * aggr->lock could cause a deadlock. Fortunately, this is + * unnecessary because the new_device/delete_device path + * and the module unload path are mutually exclusive, + * thanks to an explicit try_module_get. That's why this + * minimal scoped_guard suffices. + */ + scoped_guard(mutex, &aggr->lock) + gpio_aggregator_line_del(aggr, line); + kfree(line->key); + kfree(line->name); + kfree(line); + } } @@ -582,6 +559,728 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev, return fwd; } +/* + * Configfs interface + */ + +static struct gpio_aggregator * +to_gpio_aggregator(struct config_item *item) +{ + struct config_group *group = to_config_group(item); + + return container_of(group, struct gpio_aggregator, group); +} + +static struct gpio_aggregator_line * +to_gpio_aggregator_line(struct config_item *item) +{ + struct config_group *group = to_config_group(item); + + return container_of(group, struct gpio_aggregator_line, group); +} + +static struct fwnode_handle * +gpio_aggregator_make_device_sw_node(struct gpio_aggregator *aggr) +{ + struct property_entry properties[2]; + struct gpio_aggregator_line *line; + size_t num_lines; + int n = 0; + + memset(properties, 0, sizeof(properties)); + + num_lines = gpio_aggregator_count_lines(aggr); + if (num_lines == 0) + return NULL; + + const char **line_names __free(kfree) = kcalloc( + num_lines, sizeof(*line_names), GFP_KERNEL); + if (!line_names) + return ERR_PTR(-ENOMEM); + + /* The list is always sorted as new elements are inserted in order. */ + list_for_each_entry(line, &aggr->list_head, entry) + line_names[n++] = line->name ?: ""; + + properties[0] = PROPERTY_ENTRY_STRING_ARRAY_LEN( + "gpio-line-names", + line_names, num_lines); + + return fwnode_create_software_node(properties, NULL); +} + +static int gpio_aggregator_activate(struct gpio_aggregator *aggr) +{ + struct platform_device_info pdevinfo; + struct gpio_aggregator_line *line; + struct fwnode_handle *swnode; + unsigned int n = 0; + int ret = 0; + + if (gpio_aggregator_count_lines(aggr) == 0) + return -EINVAL; + + aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1), + GFP_KERNEL); + if (!aggr->lookups) + return -ENOMEM; + + swnode = gpio_aggregator_make_device_sw_node(aggr); + if (IS_ERR(swnode)) { + ret = PTR_ERR(swnode); + goto err_remove_lookups; + } + + memset(&pdevinfo, 0, sizeof(pdevinfo)); + pdevinfo.name = DRV_NAME; + pdevinfo.id = aggr->id; + pdevinfo.fwnode = swnode; + + /* The list is always sorted as new elements are inserted in order. */ + list_for_each_entry(line, &aggr->list_head, entry) { + /* + * - Either GPIO chip label or line name must be configured + * (i.e. line->key must be non-NULL) + * - Line directories must be named with sequential numeric + * suffixes starting from 0. (i.e. ./line0, ./line1, ...) + */ + if (!line->key || line->idx != n) { + ret = -EINVAL; + goto err_remove_swnode; + } + + if (line->offset < 0) + ret = gpio_aggregator_add_gpio(aggr, line->key, + U16_MAX, &n); + else + ret = gpio_aggregator_add_gpio(aggr, line->key, + line->offset, &n); + if (ret) + goto err_remove_swnode; + } + + aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, aggr->id); + if (!aggr->lookups->dev_id) { + ret = -ENOMEM; + goto err_remove_swnode; + } + + gpiod_add_lookup_table(aggr->lookups); + + ret = dev_sync_probe_register(&aggr->probe_data, &pdevinfo); + if (ret) + goto err_remove_lookup_table; + + return 0; + +err_remove_lookup_table: + kfree(aggr->lookups->dev_id); + gpiod_remove_lookup_table(aggr->lookups); +err_remove_swnode: + fwnode_remove_software_node(swnode); +err_remove_lookups: + kfree(aggr->lookups); + + return ret; +} + +static void gpio_aggregator_deactivate(struct gpio_aggregator *aggr) +{ + dev_sync_probe_unregister(&aggr->probe_data); + gpiod_remove_lookup_table(aggr->lookups); + kfree(aggr->lookups->dev_id); + kfree(aggr->lookups); +} + +static void gpio_aggregator_lockup_configfs(struct gpio_aggregator *aggr, + bool lock) +{ + struct configfs_subsystem *subsys = aggr->group.cg_subsys; + struct gpio_aggregator_line *line; + + /* + * The device only needs to depend on leaf lines. This is + * sufficient to lock up all the configfs entries that the + * instantiated, alive device depends on. + */ + list_for_each_entry(line, &aggr->list_head, entry) { + if (lock) + configfs_depend_item_unlocked( + subsys, &line->group.cg_item); + else + configfs_undepend_item_unlocked( + &line->group.cg_item); + } +} + +static ssize_t +gpio_aggregator_line_key_show(struct config_item *item, char *page) +{ + struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = line->parent; + + guard(mutex)(&aggr->lock); + + return sysfs_emit(page, "%s\n", line->key ?: ""); +} + +static ssize_t +gpio_aggregator_line_key_store(struct config_item *item, const char *page, + size_t count) +{ + struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = line->parent; + + char *key __free(kfree) = kstrndup(skip_spaces(page), count, + GFP_KERNEL); + if (!key) + return -ENOMEM; + + strim(key); + + guard(mutex)(&aggr->lock); + + if (gpio_aggregator_is_activating(aggr) || + gpio_aggregator_is_active(aggr)) + return -EBUSY; + + kfree(line->key); + line->key = no_free_ptr(key); + + return count; +} +CONFIGFS_ATTR(gpio_aggregator_line_, key); + +static ssize_t +gpio_aggregator_line_name_show(struct config_item *item, char *page) +{ + struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = line->parent; + + guard(mutex)(&aggr->lock); + + return sysfs_emit(page, "%s\n", line->name ?: ""); +} + +static ssize_t +gpio_aggregator_line_name_store(struct config_item *item, const char *page, + size_t count) +{ + struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = line->parent; + + char *name __free(kfree) = kstrndup(skip_spaces(page), count, + GFP_KERNEL); + if (!name) + return -ENOMEM; + + strim(name); + + guard(mutex)(&aggr->lock); + + if (gpio_aggregator_is_activating(aggr) || + gpio_aggregator_is_active(aggr)) + return -EBUSY; + + kfree(line->name); + line->name = no_free_ptr(name); + + return count; +} +CONFIGFS_ATTR(gpio_aggregator_line_, name); + +static ssize_t +gpio_aggregator_line_offset_show(struct config_item *item, char *page) +{ + struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = line->parent; + + guard(mutex)(&aggr->lock); + + return sysfs_emit(page, "%d\n", line->offset); +} + +static ssize_t +gpio_aggregator_line_offset_store(struct config_item *item, const char *page, + size_t count) +{ + struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = line->parent; + int offset, ret; + + ret = kstrtoint(page, 0, &offset); + if (ret) + return ret; + + /* + * When offset == -1, 'key' represents a line name to lookup. + * When 0 <= offset < 65535, 'key' represents the label of the chip with + * the 'offset' value representing the line within that chip. + * + * GPIOLIB uses the U16_MAX value to indicate lookup by line name so + * the greatest offset we can accept is (U16_MAX - 1). + */ + if (offset > (U16_MAX - 1) || offset < -1) + return -EINVAL; + + guard(mutex)(&aggr->lock); + + if (gpio_aggregator_is_activating(aggr) || + gpio_aggregator_is_active(aggr)) + return -EBUSY; + + line->offset = offset; + + return count; +} +CONFIGFS_ATTR(gpio_aggregator_line_, offset); + +static struct configfs_attribute *gpio_aggregator_line_attrs[] = { + &gpio_aggregator_line_attr_key, + &gpio_aggregator_line_attr_name, + &gpio_aggregator_line_attr_offset, + NULL +}; + +static ssize_t +gpio_aggregator_device_dev_name_show(struct config_item *item, char *page) +{ + struct gpio_aggregator *aggr = to_gpio_aggregator(item); + struct platform_device *pdev; + + guard(mutex)(&aggr->lock); + + pdev = aggr->probe_data.pdev; + if (pdev) + return sysfs_emit(page, "%s\n", dev_name(&pdev->dev)); + + return sysfs_emit(page, "%s.%d\n", DRV_NAME, aggr->id); +} +CONFIGFS_ATTR_RO(gpio_aggregator_device_, dev_name); + +static ssize_t +gpio_aggregator_device_live_show(struct config_item *item, char *page) +{ + struct gpio_aggregator *aggr = to_gpio_aggregator(item); + + guard(mutex)(&aggr->lock); + + return sysfs_emit(page, "%c\n", + gpio_aggregator_is_active(aggr) ? '1' : '0'); +} + +static ssize_t +gpio_aggregator_device_live_store(struct config_item *item, const char *page, + size_t count) +{ + struct gpio_aggregator *aggr = to_gpio_aggregator(item); + int ret = 0; + bool live; + + ret = kstrtobool(page, &live); + if (ret) + return ret; + + if (!try_module_get(THIS_MODULE)) + return -ENOENT; + + if (live && !aggr->init_via_sysfs) + gpio_aggregator_lockup_configfs(aggr, true); + + scoped_guard(mutex, &aggr->lock) { + if (gpio_aggregator_is_activating(aggr) || + (live == gpio_aggregator_is_active(aggr))) + ret = -EPERM; + else if (live) + ret = gpio_aggregator_activate(aggr); + else + gpio_aggregator_deactivate(aggr); + } + + /* + * Undepend is required only if device disablement (live == 0) + * succeeds or if device enablement (live == 1) fails. + */ + if (live == !!ret && !aggr->init_via_sysfs) + gpio_aggregator_lockup_configfs(aggr, false); + + module_put(THIS_MODULE); + + return ret ?: count; +} +CONFIGFS_ATTR(gpio_aggregator_device_, live); + +static struct configfs_attribute *gpio_aggregator_device_attrs[] = { + &gpio_aggregator_device_attr_dev_name, + &gpio_aggregator_device_attr_live, + NULL +}; + +static void +gpio_aggregator_line_release(struct config_item *item) +{ + struct gpio_aggregator_line *line = to_gpio_aggregator_line(item); + struct gpio_aggregator *aggr = line->parent; + + guard(mutex)(&aggr->lock); + + gpio_aggregator_line_del(aggr, line); + kfree(line->key); + kfree(line->name); + kfree(line); +} + +static struct configfs_item_operations gpio_aggregator_line_item_ops = { + .release = gpio_aggregator_line_release, +}; + +static const struct config_item_type gpio_aggregator_line_type = { + .ct_item_ops = &gpio_aggregator_line_item_ops, + .ct_attrs = gpio_aggregator_line_attrs, + .ct_owner = THIS_MODULE, +}; + +static void gpio_aggregator_device_release(struct config_item *item) +{ + struct gpio_aggregator *aggr = to_gpio_aggregator(item); + + /* + * At this point, aggr is neither active nor activating, + * so calling gpio_aggregator_deactivate() is always unnecessary. + */ + gpio_aggregator_free(aggr); +} + +static struct configfs_item_operations gpio_aggregator_device_item_ops = { + .release = gpio_aggregator_device_release, +}; + +static struct config_group * +gpio_aggregator_device_make_group(struct config_group *group, const char *name) +{ + struct gpio_aggregator *aggr = to_gpio_aggregator(&group->cg_item); + struct gpio_aggregator_line *line; + unsigned int idx; + int ret, nchar; + + ret = sscanf(name, "line%u%n", &idx, &nchar); + if (ret != 1 || nchar != strlen(name)) + return ERR_PTR(-EINVAL); + + if (aggr->init_via_sysfs) + /* + * Aggregators created via legacy sysfs interface are exposed as + * default groups, which means rmdir(2) is prohibited for them. + * For simplicity, and to avoid confusion, we also prohibit + * mkdir(2). + */ + return ERR_PTR(-EPERM); + + guard(mutex)(&aggr->lock); + + if (gpio_aggregator_is_active(aggr)) + return ERR_PTR(-EBUSY); + + list_for_each_entry(line, &aggr->list_head, entry) + if (line->idx == idx) + return ERR_PTR(-EINVAL); + + line = gpio_aggregator_line_alloc(aggr, idx, NULL, -1); + if (IS_ERR(line)) + return ERR_CAST(line); + + config_group_init_type_name(&line->group, name, &gpio_aggregator_line_type); + + gpio_aggregator_line_add(aggr, line); + + return &line->group; +} + +static struct configfs_group_operations gpio_aggregator_device_group_ops = { + .make_group = gpio_aggregator_device_make_group, +}; + +static const struct config_item_type gpio_aggregator_device_type = { + .ct_group_ops = &gpio_aggregator_device_group_ops, + .ct_item_ops = &gpio_aggregator_device_item_ops, + .ct_attrs = gpio_aggregator_device_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_group * +gpio_aggregator_make_group(struct config_group *group, const char *name) +{ + struct gpio_aggregator *aggr; + int ret; + + /* + * "_sysfs" prefix is reserved for auto-generated config group + * for devices create via legacy sysfs interface. + */ + if (strncmp(name, AGGREGATOR_LEGACY_PREFIX, + sizeof(AGGREGATOR_LEGACY_PREFIX) - 1) == 0) + return ERR_PTR(-EINVAL); + + /* arg space is unneeded */ + ret = gpio_aggregator_alloc(&aggr, 0); + if (ret) + return ERR_PTR(ret); + + config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type); + dev_sync_probe_init(&aggr->probe_data); + + return &aggr->group; +} + +static struct configfs_group_operations gpio_aggregator_group_ops = { + .make_group = gpio_aggregator_make_group, +}; + +static const struct config_item_type gpio_aggregator_type = { + .ct_group_ops = &gpio_aggregator_group_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem gpio_aggregator_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = DRV_NAME, + .ci_type = &gpio_aggregator_type, + }, + }, +}; + +/* + * Sysfs interface + */ +static int gpio_aggregator_parse(struct gpio_aggregator *aggr) +{ + char *args = skip_spaces(aggr->args); + struct gpio_aggregator_line *line; + char name[CONFIGFS_ITEM_NAME_LEN]; + char *key, *offsets, *p; + unsigned int i, n = 0; + int error = 0; + + unsigned long *bitmap __free(bitmap) = + bitmap_alloc(AGGREGATOR_MAX_GPIOS, GFP_KERNEL); + if (!bitmap) + return -ENOMEM; + + args = next_arg(args, &key, &p); + while (*args) { + args = next_arg(args, &offsets, &p); + + p = get_options(offsets, 0, &error); + if (error == 0 || *p) { + /* Named GPIO line */ + scnprintf(name, sizeof(name), "line%u", n); + line = gpio_aggregator_line_alloc(aggr, n, key, -1); + if (IS_ERR(line)) { + error = PTR_ERR(line); + goto err; + } + config_group_init_type_name(&line->group, name, + &gpio_aggregator_line_type); + error = configfs_register_group(&aggr->group, + &line->group); + if (error) + goto err; + scoped_guard(mutex, &aggr->lock) + gpio_aggregator_line_add(aggr, line); + + error = gpio_aggregator_add_gpio(aggr, key, U16_MAX, &n); + if (error) + goto err; + + key = offsets; + continue; + } + + /* GPIO chip + offset(s) */ + error = bitmap_parselist(offsets, bitmap, AGGREGATOR_MAX_GPIOS); + if (error) { + pr_err("Cannot parse %s: %d\n", offsets, error); + goto err; + } + + for_each_set_bit(i, bitmap, AGGREGATOR_MAX_GPIOS) { + scnprintf(name, sizeof(name), "line%u", n); + line = gpio_aggregator_line_alloc(aggr, n, key, i); + if (IS_ERR(line)) { + error = PTR_ERR(line); + goto err; + } + config_group_init_type_name(&line->group, name, + &gpio_aggregator_line_type); + error = configfs_register_group(&aggr->group, + &line->group); + if (error) + goto err; + scoped_guard(mutex, &aggr->lock) + gpio_aggregator_line_add(aggr, line); + + error = gpio_aggregator_add_gpio(aggr, key, i, &n); + if (error) + goto err; + } + + args = next_arg(args, &key, &p); + } + + if (!n) { + pr_err("No GPIOs specified\n"); + error = -EINVAL; + goto err; + } + + return 0; + +err: + gpio_aggregator_free_lines(aggr); + return error; +} + +static ssize_t gpio_aggregator_new_device_store(struct device_driver *driver, + const char *buf, size_t count) +{ + struct gpio_aggregator_pdev_meta meta = { .init_via_sysfs = true }; + char name[CONFIGFS_ITEM_NAME_LEN]; + struct gpio_aggregator *aggr; + struct platform_device *pdev; + int res; + + if (!try_module_get(THIS_MODULE)) + return -ENOENT; + + /* kernfs guarantees string termination, so count + 1 is safe */ + res = gpio_aggregator_alloc(&aggr, count + 1); + if (res) + goto put_module; + + memcpy(aggr->args, buf, count + 1); + + aggr->init_via_sysfs = true; + aggr->lookups = kzalloc(struct_size(aggr->lookups, table, 1), + GFP_KERNEL); + if (!aggr->lookups) { + res = -ENOMEM; + goto free_ga; + } + + aggr->lookups->dev_id = kasprintf(GFP_KERNEL, "%s.%d", DRV_NAME, aggr->id); + if (!aggr->lookups->dev_id) { + res = -ENOMEM; + goto free_table; + } + + scnprintf(name, sizeof(name), "%s.%d", AGGREGATOR_LEGACY_PREFIX, aggr->id); + config_group_init_type_name(&aggr->group, name, &gpio_aggregator_device_type); + + /* + * Since the device created by sysfs might be toggled via configfs + * 'live' attribute later, this initialization is needed. + */ + dev_sync_probe_init(&aggr->probe_data); + + /* Expose to configfs */ + res = configfs_register_group(&gpio_aggregator_subsys.su_group, + &aggr->group); + if (res) + goto free_dev_id; + + res = gpio_aggregator_parse(aggr); + if (res) + goto unregister_group; + + gpiod_add_lookup_table(aggr->lookups); + + pdev = platform_device_register_data(NULL, DRV_NAME, aggr->id, &meta, sizeof(meta)); + if (IS_ERR(pdev)) { + res = PTR_ERR(pdev); + goto remove_table; + } + + aggr->probe_data.pdev = pdev; + module_put(THIS_MODULE); + return count; + +remove_table: + gpiod_remove_lookup_table(aggr->lookups); +unregister_group: + configfs_unregister_group(&aggr->group); +free_dev_id: + kfree(aggr->lookups->dev_id); +free_table: + kfree(aggr->lookups); +free_ga: + gpio_aggregator_free(aggr); +put_module: + module_put(THIS_MODULE); + return res; +} + +static struct driver_attribute driver_attr_gpio_aggregator_new_device = + __ATTR(new_device, 0200, NULL, gpio_aggregator_new_device_store); + +static void gpio_aggregator_destroy(struct gpio_aggregator *aggr) +{ + scoped_guard(mutex, &aggr->lock) { + if (gpio_aggregator_is_activating(aggr) || + gpio_aggregator_is_active(aggr)) + gpio_aggregator_deactivate(aggr); + } + gpio_aggregator_free_lines(aggr); + configfs_unregister_group(&aggr->group); + kfree(aggr); +} + +static ssize_t gpio_aggregator_delete_device_store(struct device_driver *driver, + const char *buf, size_t count) +{ + struct gpio_aggregator *aggr; + unsigned int id; + int error; + + if (!str_has_prefix(buf, DRV_NAME ".")) + return -EINVAL; + + error = kstrtouint(buf + strlen(DRV_NAME "."), 10, &id); + if (error) + return error; + + if (!try_module_get(THIS_MODULE)) + return -ENOENT; + + mutex_lock(&gpio_aggregator_lock); + aggr = idr_find(&gpio_aggregator_idr, id); + /* + * For simplicity, devices created via configfs cannot be deleted + * via sysfs. + */ + if (aggr && aggr->init_via_sysfs) + idr_remove(&gpio_aggregator_idr, id); + else { + mutex_unlock(&gpio_aggregator_lock); + module_put(THIS_MODULE); + return -ENOENT; + } + mutex_unlock(&gpio_aggregator_lock); + + gpio_aggregator_destroy(aggr); + module_put(THIS_MODULE); + return count; +} + +static struct driver_attribute driver_attr_gpio_aggregator_delete_device = + __ATTR(delete_device, 0200, NULL, gpio_aggregator_delete_device_store); + +static struct attribute *gpio_aggregator_attrs[] = { + &driver_attr_gpio_aggregator_new_device.attr, + &driver_attr_gpio_aggregator_delete_device.attr, + NULL +}; +ATTRIBUTE_GROUPS(gpio_aggregator); /* * GPIO Aggregator platform device @@ -589,7 +1288,9 @@ static struct gpiochip_fwd *gpiochip_fwd_create(struct device *dev, static int gpio_aggregator_probe(struct platform_device *pdev) { + struct gpio_aggregator_pdev_meta *meta; struct device *dev = &pdev->dev; + bool init_via_sysfs = false; struct gpio_desc **descs; struct gpiochip_fwd *fwd; unsigned long features; @@ -603,10 +1304,28 @@ static int gpio_aggregator_probe(struct platform_device *pdev) if (!descs) return -ENOMEM; + meta = dev_get_platdata(&pdev->dev); + if (meta && meta->init_via_sysfs) + init_via_sysfs = true; + for (i = 0; i < n; i++) { descs[i] = devm_gpiod_get_index(dev, NULL, i, GPIOD_ASIS); - if (IS_ERR(descs[i])) + if (IS_ERR(descs[i])) { + /* + * Deferred probing is not suitable when the aggregator + * is created via configfs. They should just retry later + * whenever they like. For device creation via sysfs, + * error is propagated without overriding for backward + * compatibility. .prevent_deferred_probe is kept unset + * for other cases. + */ + if (!init_via_sysfs && !dev_of_node(dev) && + descs[i] == ERR_PTR(-EPROBE_DEFER)) { + pr_warn("Deferred probe canceled for creation via configfs.\n"); + return -ENODEV; + } return PTR_ERR(descs[i]); + } } features = (uintptr_t)device_get_match_data(dev); @@ -640,9 +1359,63 @@ static struct platform_driver gpio_aggregator_driver = { }, }; +static int __exit gpio_aggregator_idr_remove(int id, void *p, void *data) +{ + /* + * There should be no aggregator created via configfs, as their + * presence would prevent module unloading. + */ + gpio_aggregator_destroy(p); + return 0; +} + +static void __exit gpio_aggregator_remove_all(void) +{ + /* + * Configfs callbacks acquire gpio_aggregator_lock when accessing + * gpio_aggregator_idr, so to prevent lock inversion deadlock, we + * cannot protect idr_for_each invocation here with + * gpio_aggregator_lock, as gpio_aggregator_idr_remove() accesses + * configfs groups. Fortunately, the new_device/delete_device path + * and the module unload path are mutually exclusive, thanks to an + * explicit try_module_get inside of those driver attr handlers. + * Also, when we reach here, no configfs entries present or being + * created. Therefore, no need to protect with gpio_aggregator_lock + * below. + */ + idr_for_each(&gpio_aggregator_idr, gpio_aggregator_idr_remove, NULL); + idr_destroy(&gpio_aggregator_idr); +} + static int __init gpio_aggregator_init(void) { - return platform_driver_register(&gpio_aggregator_driver); + int ret = 0; + + config_group_init(&gpio_aggregator_subsys.su_group); + mutex_init(&gpio_aggregator_subsys.su_mutex); + ret = configfs_register_subsystem(&gpio_aggregator_subsys); + if (ret) { + pr_err("Failed to register the '%s' configfs subsystem: %d\n", + gpio_aggregator_subsys.su_group.cg_item.ci_namebuf, ret); + mutex_destroy(&gpio_aggregator_subsys.su_mutex); + return ret; + } + + /* + * CAVEAT: This must occur after configfs registration. Otherwise, + * a race condition could arise: driver attribute groups might be + * exposed and accessed by users before configfs registration + * completes. new_device_store() does not expect a partially + * initialized configfs state. + */ + ret = platform_driver_register(&gpio_aggregator_driver); + if (ret) { + pr_err("Failed to register the platform driver: %d\n", ret); + mutex_destroy(&gpio_aggregator_subsys.su_mutex); + configfs_unregister_subsystem(&gpio_aggregator_subsys); + } + + return ret; } module_init(gpio_aggregator_init); @@ -650,6 +1423,7 @@ static void __exit gpio_aggregator_exit(void) { gpio_aggregator_remove_all(); platform_driver_unregister(&gpio_aggregator_driver); + configfs_unregister_subsystem(&gpio_aggregator_subsys); } module_exit(gpio_aggregator_exit); diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index 17c287dc7471..8f22cb36004d 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -516,6 +516,7 @@ static struct irq_chip bcm_gpio_irq_chip = { .irq_set_type = bcm_kona_gpio_irq_set_type, .irq_request_resources = bcm_kona_gpio_irq_reqres, .irq_release_resources = bcm_kona_gpio_irq_relres, + .flags = IRQCHIP_IMMUTABLE, }; static struct of_device_id const bcm_kona_gpio_of_match[] = { diff --git a/drivers/gpio/gpio-blzp1600.c b/drivers/gpio/gpio-blzp1600.c new file mode 100644 index 000000000000..055cb296ae54 --- /dev/null +++ b/drivers/gpio/gpio-blzp1600.c @@ -0,0 +1,281 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2019 VeriSilicon Limited. + * Copyright (C) 2025 Blaize, Inc. + */ + +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/slab.h> +#include <linux/spinlock.h> + +#define GPIO_DIR_REG 0x00 +#define GPIO_CTRL_REG 0x04 +#define GPIO_SET_REG 0x08 +#define GPIO_CLR_REG 0x0C +#define GPIO_ODATA_REG 0x10 +#define GPIO_IDATA_REG 0x14 +#define GPIO_IEN_REG 0x18 +#define GPIO_IS_REG 0x1C +#define GPIO_IBE_REG 0x20 +#define GPIO_IEV_REG 0x24 +#define GPIO_RIS_REG 0x28 +#define GPIO_IM_REG 0x2C +#define GPIO_MIS_REG 0x30 +#define GPIO_IC_REG 0x34 +#define GPIO_DB_REG 0x38 +#define GPIO_DFG_REG 0x3C + +#define DRIVER_NAME "blzp1600-gpio" + +struct blzp1600_gpio { + void __iomem *base; + struct gpio_chip gc; + int irq; +}; + +static inline struct blzp1600_gpio *get_blzp1600_gpio_from_irq_data(struct irq_data *d) +{ + return gpiochip_get_data(irq_data_get_irq_chip_data(d)); +} + +static inline struct blzp1600_gpio *get_blzp1600_gpio_from_irq_desc(struct irq_desc *d) +{ + return gpiochip_get_data(irq_desc_get_handler_data(d)); +} + +static inline u32 blzp1600_gpio_read(struct blzp1600_gpio *chip, unsigned int offset) +{ + return readl_relaxed(chip->base + offset); +} + +static inline void blzp1600_gpio_write(struct blzp1600_gpio *chip, unsigned int offset, u32 val) +{ + writel_relaxed(val, chip->base + offset); +} + +static inline void blzp1600_gpio_rmw(void __iomem *reg, u32 mask, bool set) +{ + u32 val = readl_relaxed(reg); + + if (set) + val |= mask; + else + val &= ~mask; + + writel_relaxed(val, reg); +} + +static void blzp1600_gpio_irq_mask(struct irq_data *d) +{ + struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d); + + guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock); + blzp1600_gpio_rmw(chip->base + GPIO_IM_REG, BIT(d->hwirq), 1); +} + +static void blzp1600_gpio_irq_unmask(struct irq_data *d) +{ + struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d); + + guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock); + blzp1600_gpio_rmw(chip->base + GPIO_IM_REG, BIT(d->hwirq), 0); +} + +static void blzp1600_gpio_irq_ack(struct irq_data *d) +{ + struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d); + + blzp1600_gpio_write(chip, GPIO_IC_REG, BIT(d->hwirq)); +} + +static void blzp1600_gpio_irq_enable(struct irq_data *d) +{ + struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d); + + gpiochip_enable_irq(&chip->gc, irqd_to_hwirq(d)); + + guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock); + blzp1600_gpio_rmw(chip->base + GPIO_DIR_REG, BIT(d->hwirq), 0); + blzp1600_gpio_rmw(chip->base + GPIO_IEN_REG, BIT(d->hwirq), 1); +} + +static void blzp1600_gpio_irq_disable(struct irq_data *d) +{ + struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d); + + guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock); + blzp1600_gpio_rmw(chip->base + GPIO_IEN_REG, BIT(d->hwirq), 0); + gpiochip_disable_irq(&chip->gc, irqd_to_hwirq(d)); +} + +static int blzp1600_gpio_irq_set_type(struct irq_data *d, u32 type) +{ + struct blzp1600_gpio *chip = get_blzp1600_gpio_from_irq_data(d); + u32 edge_level, single_both, fall_rise; + int mask = BIT(d->hwirq); + + guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock); + edge_level = blzp1600_gpio_read(chip, GPIO_IS_REG); + single_both = blzp1600_gpio_read(chip, GPIO_IBE_REG); + fall_rise = blzp1600_gpio_read(chip, GPIO_IEV_REG); + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + edge_level &= ~mask; + single_both |= mask; + break; + case IRQ_TYPE_EDGE_RISING: + edge_level &= ~mask; + single_both &= ~mask; + fall_rise |= mask; + break; + case IRQ_TYPE_EDGE_FALLING: + edge_level &= ~mask; + single_both &= ~mask; + fall_rise &= ~mask; + break; + case IRQ_TYPE_LEVEL_HIGH: + edge_level |= mask; + fall_rise |= mask; + break; + case IRQ_TYPE_LEVEL_LOW: + edge_level |= mask; + fall_rise &= ~mask; + break; + default: + return -EINVAL; + } + + blzp1600_gpio_write(chip, GPIO_IS_REG, edge_level); + blzp1600_gpio_write(chip, GPIO_IBE_REG, single_both); + blzp1600_gpio_write(chip, GPIO_IEV_REG, fall_rise); + + if (type & IRQ_TYPE_LEVEL_MASK) + irq_set_handler_locked(d, handle_level_irq); + else + irq_set_handler_locked(d, handle_edge_irq); + + return 0; +} + +static const struct irq_chip blzp1600_gpio_irqchip = { + .name = DRIVER_NAME, + .irq_ack = blzp1600_gpio_irq_ack, + .irq_mask = blzp1600_gpio_irq_mask, + .irq_unmask = blzp1600_gpio_irq_unmask, + .irq_set_type = blzp1600_gpio_irq_set_type, + .irq_enable = blzp1600_gpio_irq_enable, + .irq_disable = blzp1600_gpio_irq_disable, + .flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static void blzp1600_gpio_irqhandler(struct irq_desc *desc) +{ + struct blzp1600_gpio *gpio = get_blzp1600_gpio_from_irq_desc(desc); + struct irq_chip *irqchip = irq_desc_get_chip(desc); + unsigned long irq_status; + int hwirq = 0; + + chained_irq_enter(irqchip, desc); + irq_status = blzp1600_gpio_read(gpio, GPIO_RIS_REG); + for_each_set_bit(hwirq, &irq_status, gpio->gc.ngpio) + generic_handle_domain_irq(gpio->gc.irq.domain, hwirq); + + chained_irq_exit(irqchip, desc); +} + +static int blzp1600_gpio_set_debounce(struct gpio_chip *gc, unsigned int offset, + unsigned int debounce) +{ + struct blzp1600_gpio *chip = gpiochip_get_data(gc); + + guard(raw_spinlock_irqsave)(&chip->gc.bgpio_lock); + blzp1600_gpio_rmw(chip->base + GPIO_DB_REG, BIT(offset), debounce); + + return 0; +} + +static int blzp1600_gpio_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config) +{ + u32 debounce; + + if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE) + return -ENOTSUPP; + + debounce = pinconf_to_config_argument(config); + return blzp1600_gpio_set_debounce(gc, offset, debounce); +} + +static int blzp1600_gpio_probe(struct platform_device *pdev) +{ + struct blzp1600_gpio *chip; + struct gpio_chip *gc; + int ret; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(chip->base)) + return PTR_ERR(chip->base); + + ret = bgpio_init(&chip->gc, &pdev->dev, 4, chip->base + GPIO_IDATA_REG, + chip->base + GPIO_SET_REG, chip->base + GPIO_CLR_REG, + chip->base + GPIO_DIR_REG, NULL, 0); + if (ret) + return dev_err_probe(&pdev->dev, ret, "Failed to register generic gpio\n"); + + /* configure the gpio chip */ + gc = &chip->gc; + gc->set_config = blzp1600_gpio_set_config; + + if (device_property_present(&pdev->dev, "interrupt-controller")) { + struct gpio_irq_chip *girq; + + chip->irq = platform_get_irq(pdev, 0); + if (chip->irq < 0) + return chip->irq; + + girq = &gc->irq; + gpio_irq_chip_set_chip(girq, &blzp1600_gpio_irqchip); + girq->parent_handler = blzp1600_gpio_irqhandler; + girq->num_parents = 1; + girq->parents = devm_kcalloc(&pdev->dev, 1, sizeof(*girq->parents), GFP_KERNEL); + if (!girq->parents) + return -ENOMEM; + + girq->parents[0] = chip->irq; + girq->default_type = IRQ_TYPE_NONE; + } + + return devm_gpiochip_add_data(&pdev->dev, gc, chip); +} + +static const struct of_device_id blzp1600_gpio_of_match[] = { + { .compatible = "blaize,blzp1600-gpio", }, + { /* Sentinel */ }, +}; +MODULE_DEVICE_TABLE(of, blzp1600_gpio_of_match); + +static struct platform_driver blzp1600_gpio_driver = { + .driver = { + .name = DRIVER_NAME, + .of_match_table = blzp1600_gpio_of_match, + }, + .probe = blzp1600_gpio_probe, +}; + +module_platform_driver(blzp1600_gpio_driver); + +MODULE_AUTHOR("Nikolaos Pasaloukos <nikolaos.pasaloukos@blaize.com>"); +MODULE_DESCRIPTION("Blaize BLZP1600 GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index ca3472977431..e7671bcd5c07 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -437,7 +437,7 @@ static int brcmstb_gpio_irq_setup(struct platform_device *pdev, int err; priv->irq_domain = - irq_domain_add_linear(np, priv->num_gpios, + irq_domain_create_linear(of_fwnode_handle(np), priv->num_gpios, &brcmstb_gpio_irq_domain_ops, priv); if (!priv->irq_domain) { diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index 63fc7888c1d4..80a82492171e 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -68,15 +68,6 @@ static inline u32 __gpio_mask(unsigned gpio) return 1 << (gpio % 32); } -static inline struct davinci_gpio_regs __iomem *irq2regs(struct irq_data *d) -{ - struct davinci_gpio_regs __iomem *g; - - g = (__force struct davinci_gpio_regs __iomem *)irq_data_get_irq_chip_data(d); - - return g; -} - static int davinci_gpio_irq_setup(struct platform_device *pdev); /*--------------------------------------------------------------------------*/ @@ -255,19 +246,27 @@ static int davinci_gpio_probe(struct platform_device *pdev) static void gpio_irq_mask(struct irq_data *d) { - struct davinci_gpio_regs __iomem *g = irq2regs(d); + struct davinci_gpio_controller *chips = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + struct davinci_gpio_regs __iomem *g = chips->regs[hwirq / 32]; uintptr_t mask = (uintptr_t)irq_data_get_irq_handler_data(d); writel_relaxed(mask, &g->clr_falling); writel_relaxed(mask, &g->clr_rising); + + gpiochip_disable_irq(&chips->chip, hwirq); } static void gpio_irq_unmask(struct irq_data *d) { - struct davinci_gpio_regs __iomem *g = irq2regs(d); + struct davinci_gpio_controller *chips = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + struct davinci_gpio_regs __iomem *g = chips->regs[hwirq / 32]; uintptr_t mask = (uintptr_t)irq_data_get_irq_handler_data(d); unsigned status = irqd_get_trigger_type(d); + gpiochip_enable_irq(&chips->chip, hwirq); + status &= IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING; if (!status) status = IRQ_TYPE_EDGE_FALLING | IRQ_TYPE_EDGE_RISING; @@ -286,12 +285,13 @@ static int gpio_irq_type(struct irq_data *d, unsigned trigger) return 0; } -static struct irq_chip gpio_irqchip = { +static const struct irq_chip gpio_irqchip = { .name = "GPIO", .irq_unmask = gpio_irq_unmask, .irq_mask = gpio_irq_mask, .irq_set_type = gpio_irq_type, - .flags = IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE, + .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SET_TYPE_MASKED | IRQCHIP_SKIP_SET_WAKE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static void gpio_irq_handler(struct irq_desc *desc) @@ -399,12 +399,11 @@ davinci_gpio_irq_map(struct irq_domain *d, unsigned int irq, { struct davinci_gpio_controller *chips = (struct davinci_gpio_controller *)d->host_data; - struct davinci_gpio_regs __iomem *g = chips->regs[hw / 32]; irq_set_chip_and_handler_name(irq, &gpio_irqchip, handle_simple_irq, "davinci_gpio"); irq_set_irq_type(irq, IRQ_TYPE_NONE); - irq_set_chip_data(irq, (__force void *)g); + irq_set_chip_data(irq, (__force void *)chips); irq_set_handler_data(irq, (void *)(uintptr_t)__gpio_mask(hw)); return 0; @@ -479,9 +478,8 @@ static int davinci_gpio_irq_setup(struct platform_device *pdev) return irq; } - irq_domain = irq_domain_add_legacy(dev->of_node, ngpio, irq, 0, - &davinci_gpio_irq_ops, - chips); + irq_domain = irq_domain_create_legacy(of_fwnode_handle(dev->of_node), ngpio, irq, 0, + &davinci_gpio_irq_ops, chips); if (!irq_domain) { dev_err(dev, "Couldn't register an IRQ domain\n"); return -ENODEV; diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c index 596da59d4b13..4bd3c47eaf93 100644 --- a/drivers/gpio/gpio-dln2.c +++ b/drivers/gpio/gpio-dln2.c @@ -220,11 +220,12 @@ static int dln2_gpio_get(struct gpio_chip *chip, unsigned int offset) return dln2_gpio_pin_get_out_val(dln2, offset); } -static void dln2_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int dln2_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct dln2_gpio *dln2 = gpiochip_get_data(chip); - dln2_gpio_pin_set_out_val(dln2, offset, value); + return dln2_gpio_pin_set_out_val(dln2, offset, value); } static int dln2_gpio_set_direction(struct gpio_chip *chip, unsigned offset, @@ -468,7 +469,7 @@ static int dln2_gpio_probe(struct platform_device *pdev) dln2->gpio.base = -1; dln2->gpio.ngpio = pins; dln2->gpio.can_sleep = true; - dln2->gpio.set = dln2_gpio_set; + dln2->gpio.set_rv = dln2_gpio_set; dln2->gpio.get = dln2_gpio_get; dln2->gpio.request = dln2_gpio_request; dln2->gpio.free = dln2_gpio_free; diff --git a/drivers/gpio/gpio-ds4520.c b/drivers/gpio/gpio-ds4520.c index 1903deaef3e9..f52ecae382a4 100644 --- a/drivers/gpio/gpio-ds4520.c +++ b/drivers/gpio/gpio-ds4520.c @@ -25,7 +25,6 @@ static int ds4520_gpio_probe(struct i2c_client *client) struct gpio_regmap_config config = { }; struct device *dev = &client->dev; struct regmap *regmap; - u32 ngpio; u32 base; int ret; @@ -33,10 +32,6 @@ static int ds4520_gpio_probe(struct i2c_client *client) if (ret) return dev_err_probe(dev, ret, "Missing 'reg' property.\n"); - ret = device_property_read_u32(dev, "ngpios", &ngpio); - if (ret) - return dev_err_probe(dev, ret, "Missing 'ngpios' property.\n"); - regmap = devm_regmap_init_i2c(client, &ds4520_regmap_config); if (IS_ERR(regmap)) return dev_err_probe(dev, PTR_ERR(regmap), @@ -44,7 +39,6 @@ static int ds4520_gpio_probe(struct i2c_client *client) config.regmap = regmap; config.parent = dev; - config.ngpio = ngpio; config.reg_dat_base = base + DS4520_IO_STATUS0; config.reg_set_base = base + DS4520_PULLUP0; diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c index d4bf8d187e16..f2973d0b7138 100644 --- a/drivers/gpio/gpio-eic-sprd.c +++ b/drivers/gpio/gpio-eic-sprd.c @@ -203,9 +203,10 @@ static int sprd_eic_direction_input(struct gpio_chip *chip, unsigned int offset) return 0; } -static void sprd_eic_set(struct gpio_chip *chip, unsigned int offset, int value) +static int sprd_eic_set(struct gpio_chip *chip, unsigned int offset, int value) { /* EICs are always input, nothing need to do here. */ + return 0; } static int sprd_eic_set_debounce(struct gpio_chip *chip, unsigned int offset, @@ -662,7 +663,7 @@ static int sprd_eic_probe(struct platform_device *pdev) sprd_eic->chip.request = sprd_eic_request; sprd_eic->chip.free = sprd_eic_free; sprd_eic->chip.set_config = sprd_eic_set_config; - sprd_eic->chip.set = sprd_eic_set; + sprd_eic->chip.set_rv = sprd_eic_set; fallthrough; case SPRD_EIC_ASYNC: case SPRD_EIC_SYNC: diff --git a/drivers/gpio/gpio-em.c b/drivers/gpio/gpio-em.c index 6c862c572322..a5e6e446f39c 100644 --- a/drivers/gpio/gpio-em.c +++ b/drivers/gpio/gpio-em.c @@ -204,13 +204,15 @@ static void __em_gio_set(struct gpio_chip *chip, unsigned int reg, (BIT(shift + 16)) | (value << shift)); } -static void em_gio_set(struct gpio_chip *chip, unsigned offset, int value) +static int em_gio_set(struct gpio_chip *chip, unsigned int offset, int value) { /* output is split into two registers */ if (offset < 16) __em_gio_set(chip, GIO_OL, offset, value); else __em_gio_set(chip, GIO_OH, offset - 16, value); + + return 0; } static int em_gio_direction_output(struct gpio_chip *chip, unsigned offset, @@ -304,7 +306,7 @@ static int em_gio_probe(struct platform_device *pdev) gpio_chip->direction_input = em_gio_direction_input; gpio_chip->get = em_gio_get; gpio_chip->direction_output = em_gio_direction_output; - gpio_chip->set = em_gio_set; + gpio_chip->set_rv = em_gio_set; gpio_chip->to_irq = em_gio_to_irq; gpio_chip->request = pinctrl_gpio_request; gpio_chip->free = em_gio_free; @@ -323,8 +325,9 @@ static int em_gio_probe(struct platform_device *pdev) irq_chip->irq_release_resources = em_gio_irq_relres; irq_chip->flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND; - p->irq_domain = irq_domain_add_simple(dev->of_node, ngpios, 0, - &em_gio_irq_domain_ops, p); + p->irq_domain = irq_domain_create_simple(of_fwnode_handle(dev->of_node), + ngpios, 0, + &em_gio_irq_domain_ops, p); if (!p->irq_domain) { dev_err(dev, "cannot initialize irq domain\n"); return -ENXIO; diff --git a/drivers/gpio/gpio-exar.c b/drivers/gpio/gpio-exar.c index d5909a4f0433..beb98286d13e 100644 --- a/drivers/gpio/gpio-exar.c +++ b/drivers/gpio/gpio-exar.c @@ -93,8 +93,8 @@ static int exar_get_value(struct gpio_chip *chip, unsigned int offset) return !!(regmap_test_bits(exar_gpio->regmap, addr, BIT(bit))); } -static void exar_set_value(struct gpio_chip *chip, unsigned int offset, - int value) +static int 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 = exar_offset_to_lvl_addr(exar_gpio, offset); @@ -105,7 +105,7 @@ static void exar_set_value(struct gpio_chip *chip, unsigned int offset, * regmap_write_bits() forces value to be written when an external * pull up/down might otherwise indicate value was already set. */ - regmap_write_bits(exar_gpio->regmap, addr, BIT(bit), bit_value); + return regmap_write_bits(exar_gpio->regmap, addr, BIT(bit), bit_value); } static int exar_direction_output(struct gpio_chip *chip, unsigned int offset, @@ -114,11 +114,13 @@ static int exar_direction_output(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); + int ret; - exar_set_value(chip, offset, value); - regmap_clear_bits(exar_gpio->regmap, addr, BIT(bit)); + ret = exar_set_value(chip, offset, value); + if (ret) + return ret; - return 0; + return regmap_clear_bits(exar_gpio->regmap, addr, BIT(bit)); } static int exar_direction_input(struct gpio_chip *chip, unsigned int offset) @@ -209,7 +211,7 @@ static int gpio_exar_probe(struct platform_device *pdev) exar_gpio->gpio_chip.direction_input = exar_direction_input; exar_gpio->gpio_chip.get_direction = exar_get_direction; exar_gpio->gpio_chip.get = exar_get_value; - exar_gpio->gpio_chip.set = exar_set_value; + exar_gpio->gpio_chip.set_rv = exar_set_value; exar_gpio->gpio_chip.base = -1; exar_gpio->gpio_chip.ngpio = ngpios; exar_gpio->index = index; diff --git a/drivers/gpio/gpio-f7188x.c b/drivers/gpio/gpio-f7188x.c index 3875fd940ccb..dfcd3634f279 100644 --- a/drivers/gpio/gpio-f7188x.c +++ b/drivers/gpio/gpio-f7188x.c @@ -159,7 +159,8 @@ static int f7188x_gpio_direction_in(struct gpio_chip *chip, unsigned offset); static int f7188x_gpio_get(struct gpio_chip *chip, unsigned offset); static int f7188x_gpio_direction_out(struct gpio_chip *chip, unsigned offset, int value); -static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value); +static int f7188x_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value); static int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset, unsigned long config); @@ -172,7 +173,7 @@ static int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset, .direction_input = f7188x_gpio_direction_in, \ .get = f7188x_gpio_get, \ .direction_output = f7188x_gpio_direction_out, \ - .set = f7188x_gpio_set, \ + .set_rv = f7188x_gpio_set, \ .set_config = f7188x_gpio_set_config, \ .base = -1, \ .ngpio = _ngpio, \ @@ -391,7 +392,8 @@ static int f7188x_gpio_direction_out(struct gpio_chip *chip, return 0; } -static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int f7188x_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { int err; struct f7188x_gpio_bank *bank = gpiochip_get_data(chip); @@ -400,7 +402,8 @@ static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) err = superio_enter(sio->addr); if (err) - return; + return err; + superio_select(sio->addr, sio->device); data_out = superio_inb(sio->addr, f7188x_gpio_data_out(bank->regbase)); @@ -411,6 +414,8 @@ static void f7188x_gpio_set(struct gpio_chip *chip, unsigned offset, int value) superio_outb(sio->addr, f7188x_gpio_data_out(bank->regbase), data_out); superio_exit(sio->addr); + + return 0; } static int f7188x_gpio_set_config(struct gpio_chip *chip, unsigned offset, diff --git a/drivers/gpio/gpio-graniterapids.c b/drivers/gpio/gpio-graniterapids.c index ad6a045fd3d2..f25283e5239d 100644 --- a/drivers/gpio/gpio-graniterapids.c +++ b/drivers/gpio/gpio-graniterapids.c @@ -116,7 +116,7 @@ static int gnr_gpio_get(struct gpio_chip *gc, unsigned int gpio) return !!(dw & GNR_CFG_DW_RXSTATE); } -static void gnr_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) +static int gnr_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) { u32 clear = 0; u32 set = 0; @@ -126,7 +126,7 @@ static void gnr_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) else clear = GNR_CFG_DW_TXSTATE; - gnr_gpio_configure_line(gc, gpio, clear, set); + return gnr_gpio_configure_line(gc, gpio, clear, set); } static int gnr_gpio_get_direction(struct gpio_chip *gc, unsigned int gpio) @@ -159,7 +159,7 @@ static const struct gpio_chip gnr_gpio_chip = { .owner = THIS_MODULE, .request = gnr_gpio_request, .get = gnr_gpio_get, - .set = gnr_gpio_set, + .set_rv = gnr_gpio_set, .get_direction = gnr_gpio_get_direction, .direction_input = gnr_gpio_direction_input, .direction_output = gnr_gpio_direction_output, diff --git a/drivers/gpio/gpio-grgpio.c b/drivers/gpio/gpio-grgpio.c index 30a0522ae735..d38a2d9854ca 100644 --- a/drivers/gpio/gpio-grgpio.c +++ b/drivers/gpio/gpio-grgpio.c @@ -170,6 +170,8 @@ static void grgpio_irq_mask(struct irq_data *d) grgpio_set_imask(priv, offset, 0); raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); + + gpiochip_disable_irq(&priv->gc, d->hwirq); } static void grgpio_irq_unmask(struct irq_data *d) @@ -178,6 +180,7 @@ static void grgpio_irq_unmask(struct irq_data *d) int offset = d->hwirq; unsigned long flags; + gpiochip_enable_irq(&priv->gc, d->hwirq); raw_spin_lock_irqsave(&priv->gc.bgpio_lock, flags); grgpio_set_imask(priv, offset, 1); @@ -185,11 +188,13 @@ static void grgpio_irq_unmask(struct irq_data *d) raw_spin_unlock_irqrestore(&priv->gc.bgpio_lock, flags); } -static struct irq_chip grgpio_irq_chip = { +static const struct irq_chip grgpio_irq_chip = { .name = "grgpio", .irq_mask = grgpio_irq_mask, .irq_unmask = grgpio_irq_unmask, .irq_set_type = grgpio_irq_set_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static irqreturn_t grgpio_irq_handler(int irq, void *dev) @@ -397,7 +402,7 @@ static int grgpio_probe(struct platform_device *ofdev) return -EINVAL; } - priv->domain = irq_domain_add_linear(np, gc->ngpio, + priv->domain = irq_domain_create_linear(of_fwnode_handle(np), gc->ngpio, &grgpio_irq_domain_ops, priv); if (!priv->domain) { diff --git a/drivers/gpio/gpio-gw-pld.c b/drivers/gpio/gpio-gw-pld.c index 7e29a2d8de1a..a40ba99a3aea 100644 --- a/drivers/gpio/gpio-gw-pld.c +++ b/drivers/gpio/gpio-gw-pld.c @@ -62,9 +62,9 @@ static int gw_pld_output8(struct gpio_chip *gc, unsigned offset, int value) return i2c_smbus_write_byte(gw->client, gw->out); } -static void gw_pld_set8(struct gpio_chip *gc, unsigned offset, int value) +static int gw_pld_set8(struct gpio_chip *gc, unsigned int offset, int value) { - gw_pld_output8(gc, offset, value); + return gw_pld_output8(gc, offset, value); } static int gw_pld_probe(struct i2c_client *client) @@ -86,7 +86,7 @@ static int gw_pld_probe(struct i2c_client *client) gw->chip.direction_input = gw_pld_input8; gw->chip.get = gw_pld_get8; gw->chip.direction_output = gw_pld_output8; - gw->chip.set = gw_pld_set8; + gw->chip.set_rv = gw_pld_set8; gw->client = client; /* diff --git a/drivers/gpio/gpio-htc-egpio.c b/drivers/gpio/gpio-htc-egpio.c index a40bd56673fe..b1844a676c7c 100644 --- a/drivers/gpio/gpio-htc-egpio.c +++ b/drivers/gpio/gpio-htc-egpio.c @@ -170,7 +170,7 @@ static int egpio_direction_input(struct gpio_chip *chip, unsigned offset) * Output pins */ -static void egpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int egpio_set(struct gpio_chip *chip, unsigned int offset, int value) { unsigned long flag; struct egpio_chip *egpio; @@ -198,6 +198,8 @@ static void egpio_set(struct gpio_chip *chip, unsigned offset, int value) egpio->cached_values &= ~(1 << offset); egpio_writew((egpio->cached_values >> shift) & ei->reg_mask, ei, reg); spin_unlock_irqrestore(&ei->lock, flag); + + return 0; } static int egpio_direction_output(struct gpio_chip *chip, @@ -206,12 +208,10 @@ static int egpio_direction_output(struct gpio_chip *chip, struct egpio_chip *egpio; egpio = gpiochip_get_data(chip); - if (test_bit(offset, &egpio->is_out)) { - egpio_set(chip, offset, value); - return 0; - } else { - return -EINVAL; - } + if (test_bit(offset, &egpio->is_out)) + return egpio_set(chip, offset, value); + + return -EINVAL; } static int egpio_get_direction(struct gpio_chip *chip, unsigned offset) @@ -324,7 +324,7 @@ static int __init egpio_probe(struct platform_device *pdev) chip->parent = &pdev->dev; chip->owner = THIS_MODULE; chip->get = egpio_get; - chip->set = egpio_set; + chip->set_rv = egpio_set; chip->direction_input = egpio_direction_input; chip->direction_output = egpio_direction_output; chip->get_direction = egpio_get_direction; diff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.c index 0be9285efebc..67089b2423d8 100644 --- a/drivers/gpio/gpio-ich.c +++ b/drivers/gpio/gpio-ich.c @@ -175,12 +175,16 @@ static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned int nr) static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned int nr, int val) { + int ret; + /* Disable blink hardware which is available for GPIOs from 0 to 31. */ if (nr < 32 && ichx_priv.desc->have_blink) ichx_write_bit(GPO_BLINK, nr, 0, 0); /* Set GPIO output value. */ - ichx_write_bit(GPIO_LVL, nr, val, 0); + ret = ichx_write_bit(GPIO_LVL, nr, val, 0); + if (ret) + return ret; /* * Try setting pin as an output and verify it worked since many pins @@ -252,9 +256,9 @@ static int ich6_gpio_request(struct gpio_chip *chip, unsigned int nr) return ichx_gpio_request(chip, nr); } -static void ichx_gpio_set(struct gpio_chip *chip, unsigned int nr, int val) +static int ichx_gpio_set(struct gpio_chip *chip, unsigned int nr, int val) { - ichx_write_bit(GPIO_LVL, nr, val, 0); + return ichx_write_bit(GPIO_LVL, nr, val, 0); } static void ichx_gpiolib_setup(struct gpio_chip *chip) @@ -269,7 +273,7 @@ static void ichx_gpiolib_setup(struct gpio_chip *chip) chip->get = ichx_priv.desc->get ? ichx_priv.desc->get : ichx_gpio_get; - chip->set = ichx_gpio_set; + chip->set_rv = ichx_gpio_set; chip->get_direction = ichx_gpio_get_direction; chip->direction_input = ichx_gpio_direction_input; chip->direction_output = ichx_gpio_direction_output; diff --git a/drivers/gpio/gpio-idt3243x.c b/drivers/gpio/gpio-idt3243x.c index 00f547d26254..535f25514455 100644 --- a/drivers/gpio/gpio-idt3243x.c +++ b/drivers/gpio/gpio-idt3243x.c @@ -37,7 +37,7 @@ static void idt_gpio_dispatch(struct irq_desc *desc) pending = readl(ctrl->pic + IDT_PIC_IRQ_PEND); pending &= ~ctrl->mask_cache; for_each_set_bit(bit, &pending, gc->ngpio) { - virq = irq_linear_revmap(gc->irq.domain, bit); + virq = irq_find_mapping(gc->irq.domain, bit); if (virq) generic_handle_irq(virq); } diff --git a/drivers/gpio/gpio-imx-scu.c b/drivers/gpio/gpio-imx-scu.c index 13baf465aedf..1693dbf1b777 100644 --- a/drivers/gpio/gpio-imx-scu.c +++ b/drivers/gpio/gpio-imx-scu.c @@ -6,8 +6,10 @@ * to control the PIN resources on SCU domain. */ +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/gpio/driver.h> #include <linux/platform_device.h> #include <linux/firmware/imx/svc/rm.h> @@ -37,16 +39,11 @@ static int imx_scu_gpio_get(struct gpio_chip *chip, unsigned int offset) int level; int err; - if (offset >= chip->ngpio) - return -EINVAL; - - mutex_lock(&priv->lock); - - /* to read PIN state via scu api */ - err = imx_sc_misc_get_control(priv->handle, - scu_rsrc_arr[offset], 0, &level); - mutex_unlock(&priv->lock); - + scoped_guard(mutex, &priv->lock) { + /* to read PIN state via scu api */ + err = imx_sc_misc_get_control(priv->handle, + scu_rsrc_arr[offset], 0, &level); + } if (err) { dev_err(priv->dev, "SCU get failed: %d\n", err); return err; @@ -55,31 +52,26 @@ static int imx_scu_gpio_get(struct gpio_chip *chip, unsigned int offset) return level; } -static void imx_scu_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int imx_scu_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct scu_gpio_priv *priv = gpiochip_get_data(chip); int err; - if (offset >= chip->ngpio) - return; - - mutex_lock(&priv->lock); - - /* to set PIN output level via scu api */ - err = imx_sc_misc_set_control(priv->handle, - scu_rsrc_arr[offset], 0, value); - mutex_unlock(&priv->lock); - + scoped_guard(mutex, &priv->lock) { + /* to set PIN output level via scu api */ + err = imx_sc_misc_set_control(priv->handle, + scu_rsrc_arr[offset], 0, value); + } if (err) dev_err(priv->dev, "SCU set (%d) failed: %d\n", scu_rsrc_arr[offset], err); + + return err; } static int imx_scu_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { - if (offset >= chip->ngpio) - return -EINVAL; - return GPIO_LINE_DIRECTION_OUT; } @@ -99,7 +91,10 @@ static int imx_scu_gpio_probe(struct platform_device *pdev) return ret; priv->dev = dev; - mutex_init(&priv->lock); + + ret = devm_mutex_init(&pdev->dev, &priv->lock); + if (ret) + return ret; gc = &priv->chip; gc->base = -1; @@ -107,7 +102,7 @@ static int imx_scu_gpio_probe(struct platform_device *pdev) gc->ngpio = ARRAY_SIZE(scu_rsrc_arr); gc->label = dev_name(dev); gc->get = imx_scu_gpio_get; - gc->set = imx_scu_gpio_set; + gc->set_rv = imx_scu_gpio_set; gc->get_direction = imx_scu_gpio_get_direction; platform_set_drvdata(pdev, priv); diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c index f332341fd4c8..d8184b527bac 100644 --- a/drivers/gpio/gpio-it87.c +++ b/drivers/gpio/gpio-it87.c @@ -213,8 +213,7 @@ exit: return rc; } -static void it87_gpio_set(struct gpio_chip *chip, - unsigned gpio_num, int val) +static int it87_gpio_set(struct gpio_chip *chip, unsigned int gpio_num, int val) { u8 mask, curr_vals; u16 reg; @@ -228,6 +227,8 @@ static void it87_gpio_set(struct gpio_chip *chip, outb(curr_vals | mask, reg); else outb(curr_vals & ~mask, reg); + + return 0; } static int it87_gpio_direction_out(struct gpio_chip *chip, @@ -249,7 +250,9 @@ static int it87_gpio_direction_out(struct gpio_chip *chip, /* set the output enable bit */ superio_set_mask(mask, group + it87_gpio->output_base); - it87_gpio_set(chip, gpio_num, val); + rc = it87_gpio_set(chip, gpio_num, val); + if (rc) + goto exit; superio_exit(); @@ -264,7 +267,7 @@ static const struct gpio_chip it87_template_chip = { .request = it87_gpio_request, .get = it87_gpio_get, .direction_input = it87_gpio_direction_in, - .set = it87_gpio_set, + .set_rv = it87_gpio_set, .direction_output = it87_gpio_direction_out, .base = -1 }; diff --git a/drivers/gpio/gpio-janz-ttl.c b/drivers/gpio/gpio-janz-ttl.c index cdf50e4ea165..9f548eda3888 100644 --- a/drivers/gpio/gpio-janz-ttl.c +++ b/drivers/gpio/gpio-janz-ttl.c @@ -76,7 +76,7 @@ static int ttl_get_value(struct gpio_chip *gpio, unsigned offset) return !!ret; } -static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value) +static int ttl_set_value(struct gpio_chip *gpio, unsigned int offset, int value) { struct ttl_module *mod = dev_get_drvdata(gpio->parent); void __iomem *port; @@ -103,6 +103,8 @@ static void ttl_set_value(struct gpio_chip *gpio, unsigned offset, int value) iowrite16be(*shadow, port); spin_unlock(&mod->lock); + + return 0; } static void ttl_write_reg(struct ttl_module *mod, u8 reg, u16 val) @@ -169,7 +171,7 @@ static int ttl_probe(struct platform_device *pdev) gpio->parent = &pdev->dev; gpio->label = pdev->name; gpio->get = ttl_get_value; - gpio->set = ttl_set_value; + gpio->set_rv = ttl_set_value; gpio->owner = THIS_MODULE; /* request dynamic allocation */ diff --git a/drivers/gpio/gpio-kempld.c b/drivers/gpio/gpio-kempld.c index 4ea15f08e0f4..e38e604baa22 100644 --- a/drivers/gpio/gpio-kempld.c +++ b/drivers/gpio/gpio-kempld.c @@ -63,7 +63,8 @@ static int kempld_gpio_get(struct gpio_chip *chip, unsigned offset) return !!kempld_gpio_get_bit(pld, KEMPLD_GPIO_LVL_NUM(offset), offset); } -static void kempld_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int kempld_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct kempld_gpio_data *gpio = gpiochip_get_data(chip); struct kempld_device_data *pld = gpio->pld; @@ -71,6 +72,8 @@ static void kempld_gpio_set(struct gpio_chip *chip, unsigned offset, int value) kempld_get_mutex(pld); kempld_gpio_bitop(pld, KEMPLD_GPIO_LVL_NUM(offset), offset, value); kempld_release_mutex(pld); + + return 0; } static int kempld_gpio_direction_input(struct gpio_chip *chip, unsigned offset) @@ -166,7 +169,7 @@ static int kempld_gpio_probe(struct platform_device *pdev) chip->direction_output = kempld_gpio_direction_output; chip->get_direction = kempld_gpio_get_direction; chip->get = kempld_gpio_get; - chip->set = kempld_gpio_set; + chip->set_rv = kempld_gpio_set; chip->ngpio = kempld_gpio_pincount(pld); if (chip->ngpio == 0) { dev_err(dev, "No GPIO pins detected\n"); diff --git a/drivers/gpio/gpio-ljca.c b/drivers/gpio/gpio-ljca.c index 817ecb12d550..61524a9ba765 100644 --- a/drivers/gpio/gpio-ljca.c +++ b/drivers/gpio/gpio-ljca.c @@ -144,8 +144,8 @@ static int ljca_gpio_get_value(struct gpio_chip *chip, unsigned int offset) return ljca_gpio_read(ljca_gpio, offset); } -static void ljca_gpio_set_value(struct gpio_chip *chip, unsigned int offset, - int val) +static int ljca_gpio_set_value(struct gpio_chip *chip, unsigned int offset, + int val) { struct ljca_gpio_dev *ljca_gpio = gpiochip_get_data(chip); int ret; @@ -155,6 +155,8 @@ static void ljca_gpio_set_value(struct gpio_chip *chip, unsigned int offset, dev_err(chip->parent, "set value failed offset: %u val: %d ret: %d\n", offset, val, ret); + + return ret; } static int ljca_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) @@ -183,7 +185,10 @@ static int ljca_gpio_direction_output(struct gpio_chip *chip, if (ret) return ret; - ljca_gpio_set_value(chip, offset, val); + ret = ljca_gpio_set_value(chip, offset, val); + if (ret) + return ret; + set_bit(offset, ljca_gpio->output_enabled); return 0; @@ -432,7 +437,7 @@ static int ljca_gpio_probe(struct auxiliary_device *auxdev, ljca_gpio->gc.direction_output = ljca_gpio_direction_output; ljca_gpio->gc.get_direction = ljca_gpio_get_direction; ljca_gpio->gc.get = ljca_gpio_get_value; - ljca_gpio->gc.set = ljca_gpio_set_value; + ljca_gpio->gc.set_rv = ljca_gpio_set_value; ljca_gpio->gc.set_config = ljca_gpio_set_config; ljca_gpio->gc.init_valid_mask = ljca_gpio_init_valid_mask; ljca_gpio->gc.can_sleep = true; diff --git a/drivers/gpio/gpio-logicvc.c b/drivers/gpio/gpio-logicvc.c index 05d62011f335..19cd2847467c 100644 --- a/drivers/gpio/gpio-logicvc.c +++ b/drivers/gpio/gpio-logicvc.c @@ -61,23 +61,22 @@ static int logicvc_gpio_get(struct gpio_chip *chip, unsigned offset) return !!(value & bit); } -static void logicvc_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int logicvc_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct logicvc_gpio *logicvc = gpiochip_get_data(chip); unsigned int reg, bit; logicvc_gpio_offset(logicvc, offset, ®, &bit); - regmap_update_bits(logicvc->regmap, reg, bit, value ? bit : 0); + return regmap_update_bits(logicvc->regmap, reg, bit, value ? bit : 0); } static int logicvc_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { /* Pins are always configured as output, so just set the value. */ - logicvc_gpio_set(chip, offset, value); - - return 0; + return logicvc_gpio_set(chip, offset, value); } static struct regmap_config logicvc_gpio_regmap_config = { @@ -135,7 +134,7 @@ static int logicvc_gpio_probe(struct platform_device *pdev) logicvc->chip.ngpio = LOGICVC_CTRL_GPIO_BITS + LOGICVC_POWER_CTRL_GPIO_BITS; logicvc->chip.get = logicvc_gpio_get; - logicvc->chip.set = logicvc_gpio_set; + logicvc->chip.set_rv = logicvc_gpio_set; logicvc->chip.direction_output = logicvc_gpio_direction_output; return devm_gpiochip_add_data(dev, &logicvc->chip, logicvc); diff --git a/drivers/gpio/gpio-loongson-64bit.c b/drivers/gpio/gpio-loongson-64bit.c index a9a93036f08f..26227669f026 100644 --- a/drivers/gpio/gpio-loongson-64bit.c +++ b/drivers/gpio/gpio-loongson-64bit.c @@ -105,7 +105,7 @@ static int loongson_gpio_get_direction(struct gpio_chip *chip, unsigned int pin) return GPIO_LINE_DIRECTION_OUT; } -static void loongson_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) +static int loongson_gpio_set(struct gpio_chip *chip, unsigned int pin, int value) { unsigned long flags; struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip); @@ -113,6 +113,8 @@ static void loongson_gpio_set(struct gpio_chip *chip, unsigned int pin, int valu spin_lock_irqsave(&lgpio->lock, flags); loongson_commit_level(lgpio, pin, value); spin_unlock_irqrestore(&lgpio->lock, flags); + + return 0; } static int loongson_gpio_to_irq(struct gpio_chip *chip, unsigned int offset) @@ -155,7 +157,7 @@ static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgp lgpio->chip.get = loongson_gpio_get; lgpio->chip.get_direction = loongson_gpio_get_direction; lgpio->chip.direction_output = loongson_gpio_direction_output; - lgpio->chip.set = loongson_gpio_set; + lgpio->chip.set_rv = loongson_gpio_set; lgpio->chip.parent = dev; spin_lock_init(&lgpio->lock); } diff --git a/drivers/gpio/gpio-loongson.c b/drivers/gpio/gpio-loongson.c index a42145873cc9..8f3668169ebf 100644 --- a/drivers/gpio/gpio-loongson.c +++ b/drivers/gpio/gpio-loongson.c @@ -48,8 +48,8 @@ static int loongson_gpio_get_value(struct gpio_chip *chip, unsigned gpio) return !!(val & BIT(gpio + LOONGSON_GPIO_IN_OFFSET)); } -static void loongson_gpio_set_value(struct gpio_chip *chip, - unsigned gpio, int value) +static int loongson_gpio_set_value(struct gpio_chip *chip, unsigned int gpio, + int value) { u32 val; @@ -61,6 +61,8 @@ static void loongson_gpio_set_value(struct gpio_chip *chip, val &= ~BIT(gpio); LOONGSON_GPIODATA = val; spin_unlock(&gpio_lock); + + return 0; } static int loongson_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) @@ -104,7 +106,7 @@ static int loongson_gpio_probe(struct platform_device *pdev) gc->base = 0; gc->ngpio = LOONGSON_N_GPIO; gc->get = loongson_gpio_get_value; - gc->set = loongson_gpio_set_value; + gc->set_rv = loongson_gpio_set_value; gc->direction_input = loongson_gpio_direction_input; gc->direction_output = loongson_gpio_direction_output; diff --git a/drivers/gpio/gpio-lp3943.c b/drivers/gpio/gpio-lp3943.c index 8e58242f5123..52ab3ac4844c 100644 --- a/drivers/gpio/gpio-lp3943.c +++ b/drivers/gpio/gpio-lp3943.c @@ -147,7 +147,8 @@ static int lp3943_gpio_get(struct gpio_chip *chip, unsigned int offset) return lp3943_get_gpio_out_status(lp3943_gpio, chip, offset); } -static void lp3943_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) +static int lp3943_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); u8 data; @@ -157,15 +158,19 @@ static void lp3943_gpio_set(struct gpio_chip *chip, unsigned int offset, int val else data = LP3943_GPIO_OUT_LOW; - lp3943_gpio_set_mode(lp3943_gpio, offset, data); + return lp3943_gpio_set_mode(lp3943_gpio, offset, data); } static int lp3943_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); + int ret; + + ret = lp3943_gpio_set(chip, offset, value); + if (ret) + return ret; - lp3943_gpio_set(chip, offset, value); lp3943_gpio->input_mask &= ~BIT(offset); return 0; @@ -179,7 +184,7 @@ static const struct gpio_chip lp3943_gpio_chip = { .direction_input = lp3943_gpio_direction_input, .get = lp3943_gpio_get, .direction_output = lp3943_gpio_direction_output, - .set = lp3943_gpio_set, + .set_rv = lp3943_gpio_set, .base = -1, .ngpio = LP3943_MAX_GPIO, .can_sleep = 1, diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c index 5c79ba1f229c..1908ed302e92 100644 --- a/drivers/gpio/gpio-lp873x.c +++ b/drivers/gpio/gpio-lp873x.c @@ -58,14 +58,14 @@ static int lp873x_gpio_get(struct gpio_chip *chip, unsigned int offset) return val & BIT(offset * BITS_PER_GPO); } -static void lp873x_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int lp873x_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct lp873x_gpio *gpio = gpiochip_get_data(chip); - regmap_update_bits(gpio->lp873->regmap, LP873X_REG_GPO_CTRL, - BIT(offset * BITS_PER_GPO), - value ? BIT(offset * BITS_PER_GPO) : 0); + return regmap_update_bits(gpio->lp873->regmap, LP873X_REG_GPO_CTRL, + BIT(offset * BITS_PER_GPO), + value ? BIT(offset * BITS_PER_GPO) : 0); } static int lp873x_gpio_request(struct gpio_chip *gc, unsigned int offset) @@ -124,7 +124,7 @@ static const struct gpio_chip template_chip = { .direction_input = lp873x_gpio_direction_input, .direction_output = lp873x_gpio_direction_output, .get = lp873x_gpio_get, - .set = lp873x_gpio_set, + .set_rv = lp873x_gpio_set, .set_config = lp873x_gpio_set_config, .base = -1, .ngpio = 2, diff --git a/drivers/gpio/gpio-lp87565.c b/drivers/gpio/gpio-lp87565.c index d3ce027de081..8ea687d5d028 100644 --- a/drivers/gpio/gpio-lp87565.c +++ b/drivers/gpio/gpio-lp87565.c @@ -30,13 +30,13 @@ static int lp87565_gpio_get(struct gpio_chip *chip, unsigned int offset) return !!(val & BIT(offset)); } -static void lp87565_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int lp87565_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct lp87565_gpio *gpio = gpiochip_get_data(chip); - regmap_update_bits(gpio->map, LP87565_REG_GPIO_OUT, - BIT(offset), value ? BIT(offset) : 0); + return regmap_update_bits(gpio->map, LP87565_REG_GPIO_OUT, + BIT(offset), value ? BIT(offset) : 0); } static int lp87565_gpio_get_direction(struct gpio_chip *chip, @@ -69,8 +69,11 @@ static int lp87565_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { struct lp87565_gpio *gpio = gpiochip_get_data(chip); + int ret; - lp87565_gpio_set(chip, offset, value); + ret = lp87565_gpio_set(chip, offset, value); + if (ret) + return ret; return regmap_update_bits(gpio->map, LP87565_REG_GPIO_CONFIG, @@ -136,7 +139,7 @@ static const struct gpio_chip template_chip = { .direction_input = lp87565_gpio_direction_input, .direction_output = lp87565_gpio_direction_output, .get = lp87565_gpio_get, - .set = lp87565_gpio_set, + .set_rv = lp87565_gpio_set, .set_config = lp87565_gpio_set_config, .base = -1, .ngpio = 3, diff --git a/drivers/gpio/gpio-lpc18xx.c b/drivers/gpio/gpio-lpc18xx.c index 2cf9fb4637a2..b0a8da5c058d 100644 --- a/drivers/gpio/gpio-lpc18xx.c +++ b/drivers/gpio/gpio-lpc18xx.c @@ -42,6 +42,7 @@ struct lpc18xx_gpio_pin_ic { void __iomem *base; struct irq_domain *domain; struct raw_spinlock lock; + struct gpio_chip *gpio; }; struct lpc18xx_gpio_chip { @@ -74,6 +75,7 @@ static void lpc18xx_gpio_pin_ic_mask(struct irq_data *d) { struct lpc18xx_gpio_pin_ic *ic = d->chip_data; u32 type = irqd_get_trigger_type(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); raw_spin_lock(&ic->lock); @@ -88,12 +90,17 @@ static void lpc18xx_gpio_pin_ic_mask(struct irq_data *d) raw_spin_unlock(&ic->lock); irq_chip_mask_parent(d); + + gpiochip_disable_irq(ic->gpio, hwirq); } static void lpc18xx_gpio_pin_ic_unmask(struct irq_data *d) { struct lpc18xx_gpio_pin_ic *ic = d->chip_data; u32 type = irqd_get_trigger_type(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + gpiochip_enable_irq(ic->gpio, hwirq); raw_spin_lock(&ic->lock); @@ -149,13 +156,14 @@ static int lpc18xx_gpio_pin_ic_set_type(struct irq_data *d, unsigned int type) return 0; } -static struct irq_chip lpc18xx_gpio_pin_ic = { +static const struct irq_chip lpc18xx_gpio_pin_ic = { .name = "LPC18xx GPIO pin", .irq_mask = lpc18xx_gpio_pin_ic_mask, .irq_unmask = lpc18xx_gpio_pin_ic_unmask, .irq_eoi = lpc18xx_gpio_pin_ic_eoi, .irq_set_type = lpc18xx_gpio_pin_ic_set_type, - .flags = IRQCHIP_SET_TYPE_MASKED, + .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SET_TYPE_MASKED, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int lpc18xx_gpio_pin_ic_domain_alloc(struct irq_domain *domain, @@ -240,17 +248,16 @@ static int lpc18xx_gpio_pin_ic_probe(struct lpc18xx_gpio_chip *gc) raw_spin_lock_init(&ic->lock); - ic->domain = irq_domain_add_hierarchy(parent_domain, 0, - NR_LPC18XX_GPIO_PIN_IC_IRQS, - dev->of_node, - &lpc18xx_gpio_pin_ic_domain_ops, - ic); + ic->domain = irq_domain_create_hierarchy(parent_domain, 0, NR_LPC18XX_GPIO_PIN_IC_IRQS, + of_fwnode_handle(dev->of_node), + &lpc18xx_gpio_pin_ic_domain_ops, ic); if (!ic->domain) { pr_err("unable to add irq domain\n"); ret = -ENODEV; goto free_iomap; } + ic->gpio = &gc->gpio; gc->pin_ic = ic; return 0; @@ -263,10 +270,14 @@ free_ic: return ret; } -static void lpc18xx_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static int lpc18xx_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct lpc18xx_gpio_chip *gc = gpiochip_get_data(chip); + writeb(value ? 1 : 0, gc->base + offset); + + return 0; } static int lpc18xx_gpio_get(struct gpio_chip *chip, unsigned offset) @@ -316,7 +327,7 @@ static const struct gpio_chip lpc18xx_chip = { .free = gpiochip_generic_free, .direction_input = lpc18xx_gpio_direction_input, .direction_output = lpc18xx_gpio_direction_output, - .set = lpc18xx_gpio_set, + .set_rv = lpc18xx_gpio_set, .get = lpc18xx_gpio_get, .ngpio = LPC18XX_MAX_PORTS * LPC18XX_PINS_PER_PORT, .owner = THIS_MODULE, diff --git a/drivers/gpio/gpio-lpc32xx.c b/drivers/gpio/gpio-lpc32xx.c index c097e310c9e8..6668b8bd9f1e 100644 --- a/drivers/gpio/gpio-lpc32xx.c +++ b/drivers/gpio/gpio-lpc32xx.c @@ -340,28 +340,34 @@ static int lpc32xx_gpio_dir_out_always(struct gpio_chip *chip, unsigned pin, return 0; } -static void lpc32xx_gpio_set_value_p012(struct gpio_chip *chip, unsigned pin, - int value) +static int lpc32xx_gpio_set_value_p012(struct gpio_chip *chip, + unsigned int pin, int value) { struct lpc32xx_gpio_chip *group = gpiochip_get_data(chip); __set_gpio_level_p012(group, pin, value); + + return 0; } -static void lpc32xx_gpio_set_value_p3(struct gpio_chip *chip, unsigned pin, - int value) +static int lpc32xx_gpio_set_value_p3(struct gpio_chip *chip, + unsigned int pin, int value) { struct lpc32xx_gpio_chip *group = gpiochip_get_data(chip); __set_gpio_level_p3(group, pin, value); + + return 0; } -static void lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned pin, - int value) +static int lpc32xx_gpo_set_value(struct gpio_chip *chip, unsigned int pin, + int value) { struct lpc32xx_gpio_chip *group = gpiochip_get_data(chip); __set_gpo_level_p3(group, pin, value); + + return 0; } static int lpc32xx_gpo_get_value(struct gpio_chip *chip, unsigned pin) @@ -401,7 +407,7 @@ static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = { .direction_input = lpc32xx_gpio_dir_input_p012, .get = lpc32xx_gpio_get_value_p012, .direction_output = lpc32xx_gpio_dir_output_p012, - .set = lpc32xx_gpio_set_value_p012, + .set_rv = lpc32xx_gpio_set_value_p012, .request = lpc32xx_gpio_request, .to_irq = lpc32xx_gpio_to_irq_p01, .base = LPC32XX_GPIO_P0_GRP, @@ -417,7 +423,7 @@ static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = { .direction_input = lpc32xx_gpio_dir_input_p012, .get = lpc32xx_gpio_get_value_p012, .direction_output = lpc32xx_gpio_dir_output_p012, - .set = lpc32xx_gpio_set_value_p012, + .set_rv = lpc32xx_gpio_set_value_p012, .request = lpc32xx_gpio_request, .to_irq = lpc32xx_gpio_to_irq_p01, .base = LPC32XX_GPIO_P1_GRP, @@ -433,7 +439,7 @@ static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = { .direction_input = lpc32xx_gpio_dir_input_p012, .get = lpc32xx_gpio_get_value_p012, .direction_output = lpc32xx_gpio_dir_output_p012, - .set = lpc32xx_gpio_set_value_p012, + .set_rv = lpc32xx_gpio_set_value_p012, .request = lpc32xx_gpio_request, .base = LPC32XX_GPIO_P2_GRP, .ngpio = LPC32XX_GPIO_P2_MAX, @@ -448,7 +454,7 @@ static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = { .direction_input = lpc32xx_gpio_dir_input_p3, .get = lpc32xx_gpio_get_value_p3, .direction_output = lpc32xx_gpio_dir_output_p3, - .set = lpc32xx_gpio_set_value_p3, + .set_rv = lpc32xx_gpio_set_value_p3, .request = lpc32xx_gpio_request, .to_irq = lpc32xx_gpio_to_irq_gpio_p3, .base = LPC32XX_GPIO_P3_GRP, @@ -476,7 +482,7 @@ static struct lpc32xx_gpio_chip lpc32xx_gpiochip[] = { .chip = { .label = "gpo_p3", .direction_output = lpc32xx_gpio_dir_out_always, - .set = lpc32xx_gpo_set_value, + .set_rv = lpc32xx_gpo_set_value, .get = lpc32xx_gpo_get_value, .request = lpc32xx_gpio_request, .base = LPC32XX_GPO_P3_GRP, diff --git a/drivers/gpio/gpio-madera.c b/drivers/gpio/gpio-madera.c index 8f38303fcbc4..e73e72d62bc8 100644 --- a/drivers/gpio/gpio-madera.c +++ b/drivers/gpio/gpio-madera.c @@ -87,23 +87,17 @@ static int madera_gpio_direction_out(struct gpio_chip *chip, MADERA_GP1_LVL_MASK, reg_val); } -static void madera_gpio_set(struct gpio_chip *chip, unsigned int offset, - int value) +static int madera_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct madera_gpio *madera_gpio = gpiochip_get_data(chip); struct madera *madera = madera_gpio->madera; unsigned int reg_offset = 2 * offset; unsigned int reg_val = value ? MADERA_GP1_LVL : 0; - int ret; - - ret = regmap_update_bits(madera->regmap, - MADERA_GPIO1_CTRL_1 + reg_offset, - MADERA_GP1_LVL_MASK, reg_val); - /* set() doesn't return an error so log a warning */ - if (ret) - dev_warn(madera->dev, "Failed to write to 0x%x (%d)\n", - MADERA_GPIO1_CTRL_1 + reg_offset, ret); + return regmap_update_bits(madera->regmap, + MADERA_GPIO1_CTRL_1 + reg_offset, + MADERA_GP1_LVL_MASK, reg_val); } static const struct gpio_chip madera_gpio_chip = { @@ -115,7 +109,7 @@ static const struct gpio_chip madera_gpio_chip = { .direction_input = madera_gpio_direction_in, .get = madera_gpio_get, .direction_output = madera_gpio_direction_out, - .set = madera_gpio_set, + .set_rv = madera_gpio_set, .set_config = gpiochip_generic_config, .can_sleep = true, }; diff --git a/drivers/gpio/gpio-max3191x.c b/drivers/gpio/gpio-max3191x.c index fc0708ab5192..6e6504ab740a 100644 --- a/drivers/gpio/gpio-max3191x.c +++ b/drivers/gpio/gpio-max3191x.c @@ -103,19 +103,6 @@ static int max3191x_direction_input(struct gpio_chip *gpio, unsigned int offset) return 0; } -static int max3191x_direction_output(struct gpio_chip *gpio, - unsigned int offset, int value) -{ - return -EINVAL; -} - -static void max3191x_set(struct gpio_chip *gpio, unsigned int offset, int value) -{ } - -static void max3191x_set_multiple(struct gpio_chip *gpio, unsigned long *mask, - unsigned long *bits) -{ } - static unsigned int max3191x_wordlen(struct max3191x_chip *max3191x) { return max3191x->mode == STATUS_BYTE_ENABLED ? 2 : 1; @@ -421,9 +408,6 @@ static int max3191x_probe(struct spi_device *spi) max3191x->gpio.get_direction = max3191x_get_direction; max3191x->gpio.direction_input = max3191x_direction_input; - max3191x->gpio.direction_output = max3191x_direction_output; - max3191x->gpio.set = max3191x_set; - max3191x->gpio.set_multiple = max3191x_set_multiple; max3191x->gpio.get = max3191x_get; max3191x->gpio.get_multiple = max3191x_get_multiple; max3191x->gpio.set_config = max3191x_set_config; diff --git a/drivers/gpio/gpio-max730x.c b/drivers/gpio/gpio-max730x.c index e688c13c8cc3..75d414d8c992 100644 --- a/drivers/gpio/gpio-max730x.c +++ b/drivers/gpio/gpio-max730x.c @@ -143,18 +143,21 @@ static int max7301_get(struct gpio_chip *chip, unsigned offset) return level; } -static void max7301_set(struct gpio_chip *chip, unsigned offset, int value) +static int max7301_set(struct gpio_chip *chip, unsigned int offset, int value) { struct max7301 *ts = gpiochip_get_data(chip); + int ret; /* First 4 pins are unused in the controller */ offset += 4; mutex_lock(&ts->lock); - __max7301_set(ts, offset, value); + ret = __max7301_set(ts, offset, value); mutex_unlock(&ts->lock); + + return ret; } int __max730x_probe(struct max7301 *ts) @@ -185,7 +188,7 @@ int __max730x_probe(struct max7301 *ts) ts->chip.direction_input = max7301_direction_input; ts->chip.get = max7301_get; ts->chip.direction_output = max7301_direction_output; - ts->chip.set = max7301_set; + ts->chip.set_rv = max7301_set; ts->chip.ngpio = PIN_NUMBER; ts->chip.can_sleep = true; diff --git a/drivers/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c index 49d362907bc7..d5ffedb086af 100644 --- a/drivers/gpio/gpio-max732x.c +++ b/drivers/gpio/gpio-max732x.c @@ -225,16 +225,19 @@ out: mutex_unlock(&chip->lock); } -static void max732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val) +static int max732x_gpio_set_value(struct gpio_chip *gc, unsigned int off, + int val) { unsigned base = off & ~0x7; uint8_t mask = 1u << (off & 0x7); max732x_gpio_set_mask(gc, base, mask, val << (off & 0x7)); + + return 0; } -static void max732x_gpio_set_multiple(struct gpio_chip *gc, - unsigned long *mask, unsigned long *bits) +static int max732x_gpio_set_multiple(struct gpio_chip *gc, + unsigned long *mask, unsigned long *bits) { unsigned mask_lo = mask[0] & 0xff; unsigned mask_hi = (mask[0] >> 8) & 0xff; @@ -243,6 +246,8 @@ static void max732x_gpio_set_multiple(struct gpio_chip *gc, max732x_gpio_set_mask(gc, 0, mask_lo, bits[0] & 0xff); if (mask_hi) max732x_gpio_set_mask(gc, 8, mask_hi, (bits[0] >> 8) & 0xff); + + return 0; } static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off) @@ -580,8 +585,8 @@ static int max732x_setup_gpio(struct max732x_chip *chip, gc->direction_input = max732x_gpio_direction_input; if (chip->dir_output) { gc->direction_output = max732x_gpio_direction_output; - gc->set = max732x_gpio_set_value; - gc->set_multiple = max732x_gpio_set_multiple; + gc->set_rv = max732x_gpio_set_value; + gc->set_multiple_rv = max732x_gpio_set_multiple; } gc->get = max732x_gpio_get_value; gc->can_sleep = true; diff --git a/drivers/gpio/gpio-max77620.c b/drivers/gpio/gpio-max77620.c index 8c2a5609161f..af7af8e40afe 100644 --- a/drivers/gpio/gpio-max77620.c +++ b/drivers/gpio/gpio-max77620.c @@ -223,20 +223,17 @@ static int max77620_gpio_set_debounce(struct max77620_gpio *mgpio, return ret; } -static void max77620_gpio_set(struct gpio_chip *gc, unsigned int offset, - int value) +static int max77620_gpio_set(struct gpio_chip *gc, unsigned int offset, + int value) { struct max77620_gpio *mgpio = gpiochip_get_data(gc); u8 val; - int ret; val = (value) ? MAX77620_CNFG_GPIO_OUTPUT_VAL_HIGH : MAX77620_CNFG_GPIO_OUTPUT_VAL_LOW; - ret = regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), - MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val); - if (ret < 0) - dev_err(mgpio->dev, "CNFG_GPIO_OUT update failed: %d\n", ret); + return regmap_update_bits(mgpio->rmap, GPIO_REG_ADDR(offset), + MAX77620_CNFG_GPIO_OUTPUT_VAL_MASK, val); } static int max77620_gpio_set_config(struct gpio_chip *gc, unsigned int offset, @@ -314,7 +311,7 @@ static int max77620_gpio_probe(struct platform_device *pdev) mgpio->gpio_chip.direction_input = max77620_gpio_dir_input; mgpio->gpio_chip.get = max77620_gpio_get; mgpio->gpio_chip.direction_output = max77620_gpio_dir_output; - mgpio->gpio_chip.set = max77620_gpio_set; + mgpio->gpio_chip.set_rv = max77620_gpio_set; mgpio->gpio_chip.set_config = max77620_gpio_set_config; mgpio->gpio_chip.ngpio = MAX77620_GPIO_NR; mgpio->gpio_chip.can_sleep = 1; diff --git a/drivers/gpio/gpio-max77759.c b/drivers/gpio/gpio-max77759.c new file mode 100644 index 000000000000..7fe8e6f697d0 --- /dev/null +++ b/drivers/gpio/gpio-max77759.c @@ -0,0 +1,530 @@ +// SPDX-License-Identifier: GPL-2.0-only +// +// Copyright 2020 Google Inc +// Copyright 2025 Linaro Ltd. +// +// GPIO driver for Maxim MAX77759 + +#include <linux/dev_printk.h> +#include <linux/device.h> +#include <linux/device/driver.h> +#include <linux/gpio/driver.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/irqreturn.h> +#include <linux/lockdep.h> +#include <linux/mfd/max77759.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/overflow.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/seq_file.h> + +#define MAX77759_N_GPIOS ARRAY_SIZE(max77759_gpio_line_names) +static const char * const max77759_gpio_line_names[] = { "GPIO5", "GPIO6" }; + +struct max77759_gpio_chip { + struct regmap *map; + struct max77759 *max77759; + struct gpio_chip gc; + struct mutex maxq_lock; /* protect MaxQ r/m/w operations */ + + struct mutex irq_lock; /* protect irq bus */ + int irq_mask; + int irq_mask_changed; + int irq_trig; + int irq_trig_changed; +}; + +#define MAX77759_GPIOx_TRIGGER(offs, val) (((val) & 1) << (offs)) +#define MAX77759_GPIOx_TRIGGER_MASK(offs) MAX77759_GPIOx_TRIGGER(offs, ~0) +enum max77759_trigger_gpio_type { + MAX77759_GPIO_TRIGGER_RISING = 0, + MAX77759_GPIO_TRIGGER_FALLING = 1 +}; + +#define MAX77759_GPIOx_DIR(offs, dir) (((dir) & 1) << (2 + (3 * (offs)))) +#define MAX77759_GPIOx_DIR_MASK(offs) MAX77759_GPIOx_DIR(offs, ~0) +enum max77759_control_gpio_dir { + MAX77759_GPIO_DIR_IN = 0, + MAX77759_GPIO_DIR_OUT = 1 +}; + +#define MAX77759_GPIOx_OUTVAL(offs, val) (((val) & 1) << (3 + (3 * (offs)))) +#define MAX77759_GPIOx_OUTVAL_MASK(offs) MAX77759_GPIOx_OUTVAL(offs, ~0) + +#define MAX77759_GPIOx_INVAL_MASK(offs) (BIT(4) << (3 * (offs))) + +static int max77759_gpio_maxq_gpio_trigger_read(struct max77759_gpio_chip *chip) +{ + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 1); + DEFINE_FLEX(struct max77759_maxq_response, rsp, rsp, length, 2); + int ret; + + cmd->cmd[0] = MAX77759_MAXQ_OPCODE_GPIO_TRIGGER_READ; + + ret = max77759_maxq_command(chip->max77759, cmd, rsp); + if (ret < 0) + return ret; + + return rsp->rsp[1]; +} + +static int max77759_gpio_maxq_gpio_trigger_write(struct max77759_gpio_chip *chip, + u8 trigger) +{ + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 2); + + cmd->cmd[0] = MAX77759_MAXQ_OPCODE_GPIO_TRIGGER_WRITE; + cmd->cmd[1] = trigger; + + return max77759_maxq_command(chip->max77759, cmd, NULL); +} + +static int max77759_gpio_maxq_gpio_control_read(struct max77759_gpio_chip *chip) +{ + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 1); + DEFINE_FLEX(struct max77759_maxq_response, rsp, rsp, length, 2); + int ret; + + cmd->cmd[0] = MAX77759_MAXQ_OPCODE_GPIO_CONTROL_READ; + + ret = max77759_maxq_command(chip->max77759, cmd, rsp); + if (ret < 0) + return ret; + + return rsp->rsp[1]; +} + +static int max77759_gpio_maxq_gpio_control_write(struct max77759_gpio_chip *chip, + u8 ctrl) +{ + DEFINE_FLEX(struct max77759_maxq_command, cmd, cmd, length, 2); + + cmd->cmd[0] = MAX77759_MAXQ_OPCODE_GPIO_CONTROL_WRITE; + cmd->cmd[1] = ctrl; + + return max77759_maxq_command(chip->max77759, cmd, NULL); +} + +static int +max77759_gpio_direction_from_control(int ctrl, unsigned int offset) +{ + enum max77759_control_gpio_dir dir; + + dir = !!(ctrl & MAX77759_GPIOx_DIR_MASK(offset)); + return ((dir == MAX77759_GPIO_DIR_OUT) + ? GPIO_LINE_DIRECTION_OUT + : GPIO_LINE_DIRECTION_IN); +} + +static int max77759_gpio_get_direction(struct gpio_chip *gc, + unsigned int offset) +{ + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + int ctrl; + + ctrl = max77759_gpio_maxq_gpio_control_read(chip); + if (ctrl < 0) + return ctrl; + + return max77759_gpio_direction_from_control(ctrl, offset); +} + +static int max77759_gpio_direction_helper(struct gpio_chip *gc, + unsigned int offset, + enum max77759_control_gpio_dir dir, + int value) +{ + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + int ctrl, new_ctrl; + + guard(mutex)(&chip->maxq_lock); + + ctrl = max77759_gpio_maxq_gpio_control_read(chip); + if (ctrl < 0) + return ctrl; + + new_ctrl = ctrl & ~MAX77759_GPIOx_DIR_MASK(offset); + new_ctrl |= MAX77759_GPIOx_DIR(offset, dir); + + if (dir == MAX77759_GPIO_DIR_OUT) { + new_ctrl &= ~MAX77759_GPIOx_OUTVAL_MASK(offset); + new_ctrl |= MAX77759_GPIOx_OUTVAL(offset, value); + } + + if (new_ctrl == ctrl) + return 0; + + return max77759_gpio_maxq_gpio_control_write(chip, new_ctrl); +} + +static int max77759_gpio_direction_input(struct gpio_chip *gc, + unsigned int offset) +{ + return max77759_gpio_direction_helper(gc, offset, + MAX77759_GPIO_DIR_IN, -1); +} + +static int max77759_gpio_direction_output(struct gpio_chip *gc, + unsigned int offset, int value) +{ + return max77759_gpio_direction_helper(gc, offset, + MAX77759_GPIO_DIR_OUT, value); +} + +static int max77759_gpio_get_value(struct gpio_chip *gc, unsigned int offset) +{ + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + int ctrl, mask; + + ctrl = max77759_gpio_maxq_gpio_control_read(chip); + if (ctrl < 0) + return ctrl; + + /* + * The input status bit doesn't reflect the pin state when the GPIO is + * configured as an output. Check the direction, and inspect the input + * or output bit accordingly. + */ + mask = ((max77759_gpio_direction_from_control(ctrl, offset) + == GPIO_LINE_DIRECTION_IN) + ? MAX77759_GPIOx_INVAL_MASK(offset) + : MAX77759_GPIOx_OUTVAL_MASK(offset)); + + return !!(ctrl & mask); +} + +static int max77759_gpio_set_value(struct gpio_chip *gc, + unsigned int offset, int value) +{ + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + int ctrl, new_ctrl; + + guard(mutex)(&chip->maxq_lock); + + ctrl = max77759_gpio_maxq_gpio_control_read(chip); + if (ctrl < 0) + return ctrl; + + new_ctrl = ctrl & ~MAX77759_GPIOx_OUTVAL_MASK(offset); + new_ctrl |= MAX77759_GPIOx_OUTVAL(offset, value); + + if (new_ctrl == ctrl) + return 0; + + return max77759_gpio_maxq_gpio_control_write(chip, new_ctrl); +} + +static void max77759_gpio_irq_mask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + chip->irq_mask &= ~MAX77759_MAXQ_REG_UIC_INT1_GPIOxI_MASK(hwirq); + chip->irq_mask |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1); + chip->irq_mask_changed |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1); + + gpiochip_disable_irq(gc, hwirq); +} + +static void max77759_gpio_irq_unmask(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + gpiochip_enable_irq(gc, hwirq); + + chip->irq_mask &= ~MAX77759_MAXQ_REG_UIC_INT1_GPIOxI_MASK(hwirq); + chip->irq_mask |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 0); + chip->irq_mask_changed |= MAX77759_MAXQ_REG_UIC_INT1_GPIOxI(hwirq, 1); +} + +static int max77759_gpio_set_irq_type(struct irq_data *d, unsigned int type) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + + chip->irq_trig &= ~MAX77759_GPIOx_TRIGGER_MASK(hwirq); + switch (type) { + case IRQ_TYPE_EDGE_RISING: + chip->irq_trig |= MAX77759_GPIOx_TRIGGER(hwirq, + MAX77759_GPIO_TRIGGER_RISING); + break; + + case IRQ_TYPE_EDGE_FALLING: + chip->irq_trig |= MAX77759_GPIOx_TRIGGER(hwirq, + MAX77759_GPIO_TRIGGER_FALLING); + break; + + default: + return -EINVAL; + } + + chip->irq_trig_changed |= MAX77759_GPIOx_TRIGGER(hwirq, 1); + + return 0; +} + +static void max77759_gpio_bus_lock(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + + mutex_lock(&chip->irq_lock); +} + +static int max77759_gpio_bus_sync_unlock_helper(struct gpio_chip *gc, + struct max77759_gpio_chip *chip) + __must_hold(&chip->maxq_lock) +{ + int ctrl, trigger, new_trigger, new_ctrl; + unsigned long irq_trig_changed; + int offset; + int ret; + + lockdep_assert_held(&chip->maxq_lock); + + ctrl = max77759_gpio_maxq_gpio_control_read(chip); + trigger = max77759_gpio_maxq_gpio_trigger_read(chip); + if (ctrl < 0 || trigger < 0) { + dev_err(gc->parent, "failed to read current state: %d / %d\n", + ctrl, trigger); + return (ctrl < 0) ? ctrl : trigger; + } + + new_trigger = trigger & ~chip->irq_trig_changed; + new_trigger |= (chip->irq_trig & chip->irq_trig_changed); + + /* change GPIO direction if required */ + new_ctrl = ctrl; + irq_trig_changed = chip->irq_trig_changed; + for_each_set_bit(offset, &irq_trig_changed, MAX77759_N_GPIOS) { + new_ctrl &= ~MAX77759_GPIOx_DIR_MASK(offset); + new_ctrl |= MAX77759_GPIOx_DIR(offset, MAX77759_GPIO_DIR_IN); + } + + if (new_trigger != trigger) { + ret = max77759_gpio_maxq_gpio_trigger_write(chip, new_trigger); + if (ret) { + dev_err(gc->parent, + "failed to write new trigger: %d\n", ret); + return ret; + } + } + + if (new_ctrl != ctrl) { + ret = max77759_gpio_maxq_gpio_control_write(chip, new_ctrl); + if (ret) { + dev_err(gc->parent, + "failed to write new control: %d\n", ret); + return ret; + } + } + + chip->irq_trig_changed = 0; + + return 0; +} + +static void max77759_gpio_bus_sync_unlock(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct max77759_gpio_chip *chip = gpiochip_get_data(gc); + int ret; + + scoped_guard(mutex, &chip->maxq_lock) { + ret = max77759_gpio_bus_sync_unlock_helper(gc, chip); + if (ret) + goto out_unlock; + } + + ret = regmap_update_bits(chip->map, + MAX77759_MAXQ_REG_UIC_INT1_M, + chip->irq_mask_changed, chip->irq_mask); + if (ret) { + dev_err(gc->parent, + "failed to update UIC_INT1 irq mask: %d\n", ret); + goto out_unlock; + } + + chip->irq_mask_changed = 0; + +out_unlock: + mutex_unlock(&chip->irq_lock); +} + +static void max77759_gpio_irq_print_chip(struct irq_data *d, struct seq_file *p) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + + seq_puts(p, dev_name(gc->parent)); +} + +static const struct irq_chip max77759_gpio_irq_chip = { + .irq_mask = max77759_gpio_irq_mask, + .irq_unmask = max77759_gpio_irq_unmask, + .irq_set_type = max77759_gpio_set_irq_type, + .irq_bus_lock = max77759_gpio_bus_lock, + .irq_bus_sync_unlock = max77759_gpio_bus_sync_unlock, + .irq_print_chip = max77759_gpio_irq_print_chip, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static irqreturn_t max77759_gpio_irqhandler(int irq, void *data) +{ + struct max77759_gpio_chip *chip = data; + struct gpio_chip *gc = &chip->gc; + bool handled = false; + + /* iterate until no interrupt is pending */ + while (true) { + unsigned int uic_int1; + int ret; + unsigned long pending; + int offset; + + ret = regmap_read(chip->map, MAX77759_MAXQ_REG_UIC_INT1, + &uic_int1); + if (ret < 0) { + dev_err_ratelimited(gc->parent, + "failed to read IRQ status: %d\n", + ret); + /* + * If !handled, we have looped not even once, which + * means we should return IRQ_NONE in that case (and + * of course IRQ_HANDLED otherwise). + */ + return IRQ_RETVAL(handled); + } + + pending = uic_int1; + pending &= (MAX77759_MAXQ_REG_UIC_INT1_GPIO6I + | MAX77759_MAXQ_REG_UIC_INT1_GPIO5I); + if (!pending) + break; + + for_each_set_bit(offset, &pending, MAX77759_N_GPIOS) { + /* + * ACK interrupt by writing 1 to bit 'offset', all + * others need to be written as 0. This needs to be + * done unconditionally hence regmap_set_bits() is + * inappropriate here. + */ + regmap_write(chip->map, MAX77759_MAXQ_REG_UIC_INT1, + BIT(offset)); + + handle_nested_irq(irq_find_mapping(gc->irq.domain, + offset)); + + handled = true; + } + } + + return IRQ_RETVAL(handled); +} + +static int max77759_gpio_probe(struct platform_device *pdev) +{ + struct max77759_gpio_chip *chip; + int irq; + struct gpio_irq_chip *girq; + int ret; + unsigned long irq_flags; + struct irq_data *irqd; + + chip = devm_kzalloc(&pdev->dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->map = dev_get_regmap(pdev->dev.parent, "maxq"); + if (!chip->map) + return dev_err_probe(&pdev->dev, -ENODEV, "Missing regmap\n"); + + irq = platform_get_irq_byname(pdev, "GPI"); + if (irq < 0) + return dev_err_probe(&pdev->dev, irq, "Failed to get IRQ\n"); + + chip->max77759 = dev_get_drvdata(pdev->dev.parent); + ret = devm_mutex_init(&pdev->dev, &chip->maxq_lock); + if (ret) + return ret; + ret = devm_mutex_init(&pdev->dev, &chip->irq_lock); + if (ret) + return ret; + + chip->gc.base = -1; + chip->gc.label = dev_name(&pdev->dev); + chip->gc.parent = &pdev->dev; + chip->gc.can_sleep = true; + + chip->gc.names = max77759_gpio_line_names; + chip->gc.ngpio = MAX77759_N_GPIOS; + chip->gc.get_direction = max77759_gpio_get_direction; + chip->gc.direction_input = max77759_gpio_direction_input; + chip->gc.direction_output = max77759_gpio_direction_output; + chip->gc.get = max77759_gpio_get_value; + chip->gc.set_rv = max77759_gpio_set_value; + + girq = &chip->gc.irq; + gpio_irq_chip_set_chip(girq, &max77759_gpio_irq_chip); + /* This will let us handle the parent IRQ in the driver */ + girq->parent_handler = NULL; + girq->num_parents = 0; + girq->parents = NULL; + girq->default_type = IRQ_TYPE_NONE; + girq->handler = handle_simple_irq; + girq->threaded = true; + + ret = devm_gpiochip_add_data(&pdev->dev, &chip->gc, chip); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to add GPIO chip\n"); + + irq_flags = IRQF_ONESHOT | IRQF_SHARED; + irqd = irq_get_irq_data(irq); + if (irqd) + irq_flags |= irqd_get_trigger_type(irqd); + + ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, + max77759_gpio_irqhandler, irq_flags, + dev_name(&pdev->dev), chip); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, + "Failed to request IRQ\n"); + + return ret; +} + +static const struct of_device_id max77759_gpio_of_id[] = { + { .compatible = "maxim,max77759-gpio", }, + { } +}; +MODULE_DEVICE_TABLE(of, max77759_gpio_of_id); + +static const struct platform_device_id max77759_gpio_platform_id[] = { + { "max77759-gpio", }, + { } +}; +MODULE_DEVICE_TABLE(platform, max77759_gpio_platform_id); + +static struct platform_driver max77759_gpio_driver = { + .driver = { + .name = "max77759-gpio", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = max77759_gpio_of_id, + }, + .probe = max77759_gpio_probe, + .id_table = max77759_gpio_platform_id, +}; + +module_platform_driver(max77759_gpio_driver); + +MODULE_AUTHOR("André Draszik <andre.draszik@linaro.org>"); +MODULE_DESCRIPTION("GPIO driver for Maxim MAX77759"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-mb86s7x.c b/drivers/gpio/gpio-mb86s7x.c index 7ee891ef6905..5ee2991ecdfd 100644 --- a/drivers/gpio/gpio-mb86s7x.c +++ b/drivers/gpio/gpio-mb86s7x.c @@ -119,7 +119,7 @@ static int mb86s70_gpio_get(struct gpio_chip *gc, unsigned gpio) return !!(readl(gchip->base + PDR(gpio)) & OFFSET(gpio)); } -static void mb86s70_gpio_set(struct gpio_chip *gc, unsigned gpio, int value) +static int mb86s70_gpio_set(struct gpio_chip *gc, unsigned int gpio, int value) { struct mb86s70_gpio_chip *gchip = gpiochip_get_data(gc); unsigned long flags; @@ -135,6 +135,8 @@ static void mb86s70_gpio_set(struct gpio_chip *gc, unsigned gpio, int value) writel(val, gchip->base + PDR(gpio)); spin_unlock_irqrestore(&gchip->lock, flags); + + return 0; } static int mb86s70_gpio_to_irq(struct gpio_chip *gc, unsigned int offset) @@ -178,7 +180,7 @@ static int mb86s70_gpio_probe(struct platform_device *pdev) gchip->gc.request = mb86s70_gpio_request; gchip->gc.free = mb86s70_gpio_free; gchip->gc.get = mb86s70_gpio_get; - gchip->gc.set = mb86s70_gpio_set; + gchip->gc.set_rv = mb86s70_gpio_set; gchip->gc.to_irq = mb86s70_gpio_to_irq; gchip->gc.label = dev_name(&pdev->dev); gchip->gc.ngpio = 32; diff --git a/drivers/gpio/gpio-mc33880.c b/drivers/gpio/gpio-mc33880.c index 5fb357d7b78a..e68956104161 100644 --- a/drivers/gpio/gpio-mc33880.c +++ b/drivers/gpio/gpio-mc33880.c @@ -57,15 +57,18 @@ static int __mc33880_set(struct mc33880 *mc, unsigned offset, int value) } -static void mc33880_set(struct gpio_chip *chip, unsigned offset, int value) +static int mc33880_set(struct gpio_chip *chip, unsigned int offset, int value) { struct mc33880 *mc = gpiochip_get_data(chip); + int ret; mutex_lock(&mc->lock); - __mc33880_set(mc, offset, value); + ret = __mc33880_set(mc, offset, value); mutex_unlock(&mc->lock); + + return ret; } static int mc33880_probe(struct spi_device *spi) @@ -100,7 +103,7 @@ static int mc33880_probe(struct spi_device *spi) mc->spi = spi; mc->chip.label = DRIVER_NAME; - mc->chip.set = mc33880_set; + mc->chip.set_rv = mc33880_set; mc->chip.base = pdata->base; mc->chip.ngpio = PIN_NUMBER; mc->chip.can_sleep = true; diff --git a/drivers/gpio/gpio-ml-ioh.c b/drivers/gpio/gpio-ml-ioh.c index 48e3768a830e..12cf36f9ca63 100644 --- a/drivers/gpio/gpio-ml-ioh.c +++ b/drivers/gpio/gpio-ml-ioh.c @@ -89,7 +89,7 @@ struct ioh_gpio { static const int num_ports[] = {6, 12, 16, 16, 15, 16, 16, 12}; -static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) +static int ioh_gpio_set(struct gpio_chip *gpio, unsigned int nr, int val) { u32 reg_val; struct ioh_gpio *chip = gpiochip_get_data(gpio); @@ -104,6 +104,8 @@ static void ioh_gpio_set(struct gpio_chip *gpio, unsigned nr, int val) iowrite32(reg_val, &chip->reg->regs[chip->ch].po); spin_unlock_irqrestore(&chip->spinlock, flags); + + return 0; } static int ioh_gpio_get(struct gpio_chip *gpio, unsigned nr) @@ -222,7 +224,7 @@ static void ioh_gpio_setup(struct ioh_gpio *chip, int num_port) gpio->direction_input = ioh_gpio_direction_input; gpio->get = ioh_gpio_get; gpio->direction_output = ioh_gpio_direction_output; - gpio->set = ioh_gpio_set; + gpio->set_rv = ioh_gpio_set; gpio->dbg_show = NULL; gpio->base = -1; gpio->ngpio = num_port; diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index 541517536489..121efdd71e45 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -123,9 +123,12 @@ static irqreturn_t mpc8xxx_gpio_irq_cascade(int irq, void *data) static void mpc8xxx_irq_unmask(struct irq_data *d) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); struct gpio_chip *gc = &mpc8xxx_gc->gc; unsigned long flags; + gpiochip_enable_irq(gc, hwirq); + raw_spin_lock_irqsave(&mpc8xxx_gc->lock, flags); gc->write_reg(mpc8xxx_gc->regs + GPIO_IMR, @@ -138,6 +141,7 @@ static void mpc8xxx_irq_unmask(struct irq_data *d) static void mpc8xxx_irq_mask(struct irq_data *d) { struct mpc8xxx_gpio_chip *mpc8xxx_gc = irq_data_get_irq_chip_data(d); + irq_hw_number_t hwirq = irqd_to_hwirq(d); struct gpio_chip *gc = &mpc8xxx_gc->gc; unsigned long flags; @@ -148,6 +152,8 @@ static void mpc8xxx_irq_mask(struct irq_data *d) & ~mpc_pin2mask(irqd_to_hwirq(d))); raw_spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags); + + gpiochip_disable_irq(gc, hwirq); } static void mpc8xxx_irq_ack(struct irq_data *d) @@ -244,6 +250,8 @@ static struct irq_chip mpc8xxx_irq_chip = { .irq_ack = mpc8xxx_irq_ack, /* this might get overwritten in mpc8xxx_probe() */ .irq_set_type = mpc8xxx_irq_set_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int mpc8xxx_gpio_irq_map(struct irq_domain *h, unsigned int irq, diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 3604abcb6fec..57633a7b4270 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -408,9 +408,8 @@ static void mvebu_gpio_irq_ack(struct irq_data *d) struct mvebu_gpio_chip *mvchip = gc->private; u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); mvebu_gpio_write_edge_cause(mvchip, ~mask); - irq_gc_unlock(gc); } static void mvebu_gpio_edge_irq_mask(struct irq_data *d) @@ -420,10 +419,9 @@ static void mvebu_gpio_edge_irq_mask(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); ct->mask_cache_priv &= ~mask; mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv); - irq_gc_unlock(gc); } static void mvebu_gpio_edge_irq_unmask(struct irq_data *d) @@ -433,11 +431,10 @@ static void mvebu_gpio_edge_irq_unmask(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); mvebu_gpio_write_edge_cause(mvchip, ~mask); ct->mask_cache_priv |= mask; mvebu_gpio_write_edge_mask(mvchip, ct->mask_cache_priv); - irq_gc_unlock(gc); } static void mvebu_gpio_level_irq_mask(struct irq_data *d) @@ -447,10 +444,9 @@ static void mvebu_gpio_level_irq_mask(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); ct->mask_cache_priv &= ~mask; mvebu_gpio_write_level_mask(mvchip, ct->mask_cache_priv); - irq_gc_unlock(gc); } static void mvebu_gpio_level_irq_unmask(struct irq_data *d) @@ -460,10 +456,9 @@ static void mvebu_gpio_level_irq_unmask(struct irq_data *d) struct irq_chip_type *ct = irq_data_get_chip_type(d); u32 mask = d->mask; - irq_gc_lock(gc); + guard(raw_spinlock)(&gc->lock); ct->mask_cache_priv |= mask; mvebu_gpio_write_level_mask(mvchip, ct->mask_cache_priv); - irq_gc_unlock(gc); } /***************************************************************************** @@ -1242,7 +1237,7 @@ static int mvebu_gpio_probe(struct platform_device *pdev) return 0; mvchip->domain = - irq_domain_add_linear(np, ngpios, &irq_generic_chip_ops, NULL); + irq_domain_create_linear(of_fwnode_handle(np), ngpios, &irq_generic_chip_ops, NULL); if (!mvchip->domain) { dev_err(&pdev->dev, "couldn't allocate irq domain %s (DT).\n", mvchip->chip.label); diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c index 619b6fb9d833..fae1a30f8ae6 100644 --- a/drivers/gpio/gpio-mxc.c +++ b/drivers/gpio/gpio-mxc.c @@ -490,7 +490,14 @@ static int mxc_gpio_probe(struct platform_device *pdev) port->gc.request = mxc_gpio_request; port->gc.free = mxc_gpio_free; port->gc.to_irq = mxc_gpio_to_irq; - port->gc.base = of_alias_get_id(np, "gpio") * 32; + /* + * Driver is DT-only, so a fixed base needs only be maintained for legacy + * userspace with sysfs interface. + */ + if (IS_ENABLED(CONFIG_GPIO_SYSFS)) + port->gc.base = of_alias_get_id(np, "gpio") * 32; + else /* silence boot time warning */ + port->gc.base = -1; err = devm_gpiochip_add_data(&pdev->dev, &port->gc, port); if (err) @@ -502,7 +509,7 @@ static int mxc_gpio_probe(struct platform_device *pdev) goto out_bgio; } - port->domain = irq_domain_add_legacy(np, 32, irq_base, 0, + port->domain = irq_domain_create_legacy(of_fwnode_handle(np), 32, irq_base, 0, &irq_domain_simple_ops, NULL); if (!port->domain) { err = -ENODEV; diff --git a/drivers/gpio/gpio-mxs.c b/drivers/gpio/gpio-mxs.c index 024ad077e98d..b418fbccb26c 100644 --- a/drivers/gpio/gpio-mxs.c +++ b/drivers/gpio/gpio-mxs.c @@ -303,8 +303,8 @@ static int mxs_gpio_probe(struct platform_device *pdev) goto out_iounmap; } - port->domain = irq_domain_add_legacy(np, 32, irq_base, 0, - &irq_domain_simple_ops, NULL); + port->domain = irq_domain_create_legacy(of_fwnode_handle(np), 32, irq_base, 0, + &irq_domain_simple_ops, NULL); if (!port->domain) { err = -ENODEV; goto out_iounmap; diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c index 442435ded020..b852e4997629 100644 --- a/drivers/gpio/gpio-pca953x.c +++ b/drivers/gpio/gpio-pca953x.c @@ -215,6 +215,8 @@ struct pca953x_chip { DECLARE_BITMAP(irq_stat, MAX_LINE); DECLARE_BITMAP(irq_trig_raise, MAX_LINE); DECLARE_BITMAP(irq_trig_fall, MAX_LINE); + DECLARE_BITMAP(irq_trig_level_high, MAX_LINE); + DECLARE_BITMAP(irq_trig_level_low, MAX_LINE); #endif atomic_t wakeup_path; @@ -774,6 +776,8 @@ static void pca953x_irq_bus_sync_unlock(struct irq_data *d) pca953x_read_regs(chip, chip->regs->direction, reg_direction); bitmap_or(irq_mask, chip->irq_trig_fall, chip->irq_trig_raise, gc->ngpio); + bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_high, gc->ngpio); + bitmap_or(irq_mask, irq_mask, chip->irq_trig_level_low, gc->ngpio); bitmap_complement(reg_direction, reg_direction, gc->ngpio); bitmap_and(irq_mask, irq_mask, reg_direction, gc->ngpio); @@ -791,13 +795,15 @@ static int pca953x_irq_set_type(struct irq_data *d, unsigned int type) struct device *dev = &chip->client->dev; irq_hw_number_t hwirq = irqd_to_hwirq(d); - if (!(type & IRQ_TYPE_EDGE_BOTH)) { + if (!(type & IRQ_TYPE_SENSE_MASK)) { dev_err(dev, "irq %d: unsupported type %d\n", d->irq, type); return -EINVAL; } assign_bit(hwirq, chip->irq_trig_fall, type & IRQ_TYPE_EDGE_FALLING); assign_bit(hwirq, chip->irq_trig_raise, type & IRQ_TYPE_EDGE_RISING); + assign_bit(hwirq, chip->irq_trig_level_low, type & IRQ_TYPE_LEVEL_LOW); + assign_bit(hwirq, chip->irq_trig_level_high, type & IRQ_TYPE_LEVEL_HIGH); return 0; } @@ -810,6 +816,8 @@ static void pca953x_irq_shutdown(struct irq_data *d) clear_bit(hwirq, chip->irq_trig_raise); clear_bit(hwirq, chip->irq_trig_fall); + clear_bit(hwirq, chip->irq_trig_level_low); + clear_bit(hwirq, chip->irq_trig_level_high); } static void pca953x_irq_print_chip(struct irq_data *data, struct seq_file *p) @@ -840,6 +848,7 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin DECLARE_BITMAP(cur_stat, MAX_LINE); DECLARE_BITMAP(new_stat, MAX_LINE); DECLARE_BITMAP(trigger, MAX_LINE); + DECLARE_BITMAP(edges, MAX_LINE); int ret; ret = pca953x_read_regs(chip, chip->regs->input, cur_stat); @@ -857,13 +866,26 @@ static bool pca953x_irq_pending(struct pca953x_chip *chip, unsigned long *pendin bitmap_copy(chip->irq_stat, new_stat, gc->ngpio); - if (bitmap_empty(trigger, gc->ngpio)) - return false; + if (bitmap_empty(chip->irq_trig_level_high, gc->ngpio) && + bitmap_empty(chip->irq_trig_level_low, gc->ngpio)) { + if (bitmap_empty(trigger, gc->ngpio)) + return false; + } bitmap_and(cur_stat, chip->irq_trig_fall, old_stat, gc->ngpio); bitmap_and(old_stat, chip->irq_trig_raise, new_stat, gc->ngpio); - bitmap_or(new_stat, old_stat, cur_stat, gc->ngpio); - bitmap_and(pending, new_stat, trigger, gc->ngpio); + bitmap_or(edges, old_stat, cur_stat, gc->ngpio); + bitmap_and(pending, edges, trigger, gc->ngpio); + + bitmap_and(cur_stat, new_stat, chip->irq_trig_level_high, gc->ngpio); + bitmap_and(cur_stat, cur_stat, chip->irq_mask, gc->ngpio); + bitmap_or(pending, pending, cur_stat, gc->ngpio); + + bitmap_complement(cur_stat, new_stat, gc->ngpio); + bitmap_and(cur_stat, cur_stat, reg_direction, gc->ngpio); + bitmap_and(old_stat, cur_stat, chip->irq_trig_level_low, gc->ngpio); + bitmap_and(old_stat, old_stat, chip->irq_mask, gc->ngpio); + bitmap_or(pending, pending, old_stat, gc->ngpio); return !bitmap_empty(pending, gc->ngpio); } @@ -1204,6 +1226,8 @@ static int pca953x_restore_context(struct pca953x_chip *chip) guard(mutex)(&chip->i2c_lock); + if (chip->client->irq > 0) + enable_irq(chip->client->irq); regcache_cache_only(chip->regmap, false); regcache_mark_dirty(chip->regmap); ret = pca953x_regcache_sync(chip); @@ -1216,6 +1240,10 @@ static int pca953x_restore_context(struct pca953x_chip *chip) static void pca953x_save_context(struct pca953x_chip *chip) { guard(mutex)(&chip->i2c_lock); + + /* Disable IRQ to prevent early triggering while regmap "cache only" is on */ + if (chip->client->irq > 0) + disable_irq(chip->client->irq); regcache_cache_only(chip->regmap, true); } diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c index 91cea97255fa..aead35ea090e 100644 --- a/drivers/gpio/gpio-pxa.c +++ b/drivers/gpio/gpio-pxa.c @@ -497,6 +497,8 @@ static void pxa_mask_muxed_gpio(struct irq_data *d) gfer = readl_relaxed(base + GFER_OFFSET) & ~GPIO_bit(gpio); writel_relaxed(grer, base + GRER_OFFSET); writel_relaxed(gfer, base + GFER_OFFSET); + + gpiochip_disable_irq(&pchip->chip, gpio); } static int pxa_gpio_set_wake(struct irq_data *d, unsigned int on) @@ -516,17 +518,21 @@ static void pxa_unmask_muxed_gpio(struct irq_data *d) unsigned int gpio = irqd_to_hwirq(d); struct pxa_gpio_bank *c = gpio_to_pxabank(&pchip->chip, gpio); + gpiochip_enable_irq(&pchip->chip, gpio); + c->irq_mask |= GPIO_bit(gpio); update_edge_detect(c); } -static struct irq_chip pxa_muxed_gpio_chip = { +static const struct irq_chip pxa_muxed_gpio_chip = { .name = "GPIO", .irq_ack = pxa_ack_muxed_gpio, .irq_mask = pxa_mask_muxed_gpio, .irq_unmask = pxa_unmask_muxed_gpio, .irq_set_type = pxa_gpio_irq_type, .irq_set_wake = pxa_gpio_set_wake, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int pxa_gpio_nums(struct platform_device *pdev) @@ -636,9 +642,9 @@ static int pxa_gpio_probe(struct platform_device *pdev) if (!pxa_last_gpio) return -EINVAL; - pchip->irqdomain = irq_domain_add_legacy(pdev->dev.of_node, - pxa_last_gpio + 1, irq_base, - 0, &pxa_irq_domain_ops, pchip); + pchip->irqdomain = irq_domain_create_legacy(of_fwnode_handle(pdev->dev.of_node), + pxa_last_gpio + 1, irq_base, 0, + &pxa_irq_domain_ops, pchip); if (!pchip->irqdomain) return -ENOMEM; diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index 01a3b3dac58b..c63352f2f1ec 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -521,7 +521,7 @@ static int rockchip_interrupts_register(struct rockchip_pin_bank *bank) struct irq_chip_generic *gc; int ret; - bank->domain = irq_domain_add_linear(bank->of_node, 32, + bank->domain = irq_domain_create_linear(of_fwnode_handle(bank->of_node), 32, &irq_generic_chip_ops, NULL); if (!bank->domain) { dev_warn(bank->dev, "could not init irq domain for bank %s\n", diff --git a/drivers/gpio/gpio-sa1100.c b/drivers/gpio/gpio-sa1100.c index 242dad763ac4..3f3ee36bc3cb 100644 --- a/drivers/gpio/gpio-sa1100.c +++ b/drivers/gpio/gpio-sa1100.c @@ -319,7 +319,7 @@ void __init sa1100_init_gpio(void) gpiochip_add_data(&sa1100_gpio_chip.chip, NULL); - sa1100_gpio_irqdomain = irq_domain_add_simple(NULL, + sa1100_gpio_irqdomain = irq_domain_create_simple(NULL, 28, IRQ_GPIO0, &sa1100_gpio_irqdomain_ops, sgc); diff --git a/drivers/gpio/gpio-sodaville.c b/drivers/gpio/gpio-sodaville.c index c2a2c76c1652..6a3c4c625138 100644 --- a/drivers/gpio/gpio-sodaville.c +++ b/drivers/gpio/gpio-sodaville.c @@ -169,7 +169,7 @@ static int sdv_register_irqsupport(struct sdv_gpio_chip_data *sd, IRQ_GC_INIT_MASK_CACHE, IRQ_NOREQUEST, IRQ_LEVEL | IRQ_NOPROBE); - sd->id = irq_domain_add_legacy(pdev->dev.of_node, SDV_NUM_PUB_GPIOS, + sd->id = irq_domain_create_legacy(of_fwnode_handle(pdev->dev.of_node), SDV_NUM_PUB_GPIOS, sd->irq_base, 0, &irq_domain_sdv_ops, sd); if (!sd->id) return -ENODEV; diff --git a/drivers/gpio/gpio-spacemit-k1.c b/drivers/gpio/gpio-spacemit-k1.c new file mode 100644 index 000000000000..f027066365ff --- /dev/null +++ b/drivers/gpio/gpio-spacemit-k1.c @@ -0,0 +1,293 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Copyright (C) 2023-2025 SpacemiT (Hangzhou) Technology Co. Ltd + * Copyright (C) 2025 Yixun Lan <dlan@gentoo.org> + */ + +#include <linux/clk.h> +#include <linux/gpio/driver.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/seq_file.h> + +/* register offset */ +#define SPACEMIT_GPLR 0x00 /* port level - R */ +#define SPACEMIT_GPDR 0x0c /* port direction - R/W */ +#define SPACEMIT_GPSR 0x18 /* port set - W */ +#define SPACEMIT_GPCR 0x24 /* port clear - W */ +#define SPACEMIT_GRER 0x30 /* port rising edge R/W */ +#define SPACEMIT_GFER 0x3c /* port falling edge R/W */ +#define SPACEMIT_GEDR 0x48 /* edge detect status - R/W1C */ +#define SPACEMIT_GSDR 0x54 /* (set) direction - W */ +#define SPACEMIT_GCDR 0x60 /* (clear) direction - W */ +#define SPACEMIT_GSRER 0x6c /* (set) rising edge detect enable - W */ +#define SPACEMIT_GCRER 0x78 /* (clear) rising edge detect enable - W */ +#define SPACEMIT_GSFER 0x84 /* (set) falling edge detect enable - W */ +#define SPACEMIT_GCFER 0x90 /* (clear) falling edge detect enable - W */ +#define SPACEMIT_GAPMASK 0x9c /* interrupt mask , 0 disable, 1 enable - R/W */ + +#define SPACEMIT_NR_BANKS 4 +#define SPACEMIT_NR_GPIOS_PER_BANK 32 + +#define to_spacemit_gpio_bank(x) container_of((x), struct spacemit_gpio_bank, gc) + +struct spacemit_gpio; + +struct spacemit_gpio_bank { + struct gpio_chip gc; + struct spacemit_gpio *sg; + void __iomem *base; + u32 irq_mask; + u32 irq_rising_edge; + u32 irq_falling_edge; +}; + +struct spacemit_gpio { + struct device *dev; + struct spacemit_gpio_bank sgb[SPACEMIT_NR_BANKS]; +}; + +static u32 spacemit_gpio_bank_index(struct spacemit_gpio_bank *gb) +{ + return (u32)(gb - gb->sg->sgb); +} + +static irqreturn_t spacemit_gpio_irq_handler(int irq, void *dev_id) +{ + struct spacemit_gpio_bank *gb = dev_id; + unsigned long pending; + u32 n, gedr; + + gedr = readl(gb->base + SPACEMIT_GEDR); + if (!gedr) + return IRQ_NONE; + writel(gedr, gb->base + SPACEMIT_GEDR); + + pending = gedr & gb->irq_mask; + if (!pending) + return IRQ_NONE; + + for_each_set_bit(n, &pending, BITS_PER_LONG) + handle_nested_irq(irq_find_mapping(gb->gc.irq.domain, n)); + + return IRQ_HANDLED; +} + +static void spacemit_gpio_irq_ack(struct irq_data *d) +{ + struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); + + writel(BIT(irqd_to_hwirq(d)), gb->base + SPACEMIT_GEDR); +} + +static void spacemit_gpio_irq_mask(struct irq_data *d) +{ + struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); + u32 bit = BIT(irqd_to_hwirq(d)); + + gb->irq_mask &= ~bit; + writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK); + + if (bit & gb->irq_rising_edge) + writel(bit, gb->base + SPACEMIT_GCRER); + + if (bit & gb->irq_falling_edge) + writel(bit, gb->base + SPACEMIT_GCFER); +} + +static void spacemit_gpio_irq_unmask(struct irq_data *d) +{ + struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); + u32 bit = BIT(irqd_to_hwirq(d)); + + gb->irq_mask |= bit; + + if (bit & gb->irq_rising_edge) + writel(bit, gb->base + SPACEMIT_GSRER); + + if (bit & gb->irq_falling_edge) + writel(bit, gb->base + SPACEMIT_GSFER); + + writel(gb->irq_mask, gb->base + SPACEMIT_GAPMASK); +} + +static int spacemit_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(d); + u32 bit = BIT(irqd_to_hwirq(d)); + + if (type & IRQ_TYPE_EDGE_RISING) { + gb->irq_rising_edge |= bit; + writel(bit, gb->base + SPACEMIT_GSRER); + } else { + gb->irq_rising_edge &= ~bit; + writel(bit, gb->base + SPACEMIT_GCRER); + } + + if (type & IRQ_TYPE_EDGE_FALLING) { + gb->irq_falling_edge |= bit; + writel(bit, gb->base + SPACEMIT_GSFER); + } else { + gb->irq_falling_edge &= ~bit; + writel(bit, gb->base + SPACEMIT_GCFER); + } + + return 0; +} + +static void spacemit_gpio_irq_print_chip(struct irq_data *data, struct seq_file *p) +{ + struct spacemit_gpio_bank *gb = irq_data_get_irq_chip_data(data); + + seq_printf(p, "%s-%d", dev_name(gb->gc.parent), spacemit_gpio_bank_index(gb)); +} + +static struct irq_chip spacemit_gpio_chip = { + .name = "k1-gpio-irqchip", + .irq_ack = spacemit_gpio_irq_ack, + .irq_mask = spacemit_gpio_irq_mask, + .irq_unmask = spacemit_gpio_irq_unmask, + .irq_set_type = spacemit_gpio_irq_set_type, + .irq_print_chip = spacemit_gpio_irq_print_chip, + .flags = IRQCHIP_IMMUTABLE | IRQCHIP_SKIP_SET_WAKE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, +}; + +static bool spacemit_of_node_instance_match(struct gpio_chip *gc, unsigned int i) +{ + struct spacemit_gpio_bank *gb = gpiochip_get_data(gc); + struct spacemit_gpio *sg = gb->sg; + + if (i >= SPACEMIT_NR_BANKS) + return false; + + return (gc == &sg->sgb[i].gc); +} + +static int spacemit_gpio_add_bank(struct spacemit_gpio *sg, + void __iomem *regs, + int index, int irq) +{ + struct spacemit_gpio_bank *gb = &sg->sgb[index]; + struct gpio_chip *gc = &gb->gc; + struct device *dev = sg->dev; + struct gpio_irq_chip *girq; + void __iomem *dat, *set, *clr, *dirin, *dirout; + int ret, bank_base[] = { 0x0, 0x4, 0x8, 0x100 }; + + gb->base = regs + bank_base[index]; + + dat = gb->base + SPACEMIT_GPLR; + set = gb->base + SPACEMIT_GPSR; + clr = gb->base + SPACEMIT_GPCR; + dirin = gb->base + SPACEMIT_GCDR; + dirout = gb->base + SPACEMIT_GSDR; + + /* This registers 32 GPIO lines per bank */ + ret = bgpio_init(gc, dev, 4, dat, set, clr, dirout, dirin, + BGPIOF_UNREADABLE_REG_SET | BGPIOF_UNREADABLE_REG_DIR); + if (ret) + return dev_err_probe(dev, ret, "failed to init gpio chip\n"); + + gb->sg = sg; + + gc->label = dev_name(dev); + gc->request = gpiochip_generic_request; + gc->free = gpiochip_generic_free; + gc->ngpio = SPACEMIT_NR_GPIOS_PER_BANK; + gc->base = -1; + gc->of_gpio_n_cells = 3; + gc->of_node_instance_match = spacemit_of_node_instance_match; + + girq = &gc->irq; + girq->threaded = true; + girq->handler = handle_simple_irq; + + gpio_irq_chip_set_chip(girq, &spacemit_gpio_chip); + + /* Disable Interrupt */ + writel(0, gb->base + SPACEMIT_GAPMASK); + /* Disable Edge Detection Settings */ + writel(0x0, gb->base + SPACEMIT_GRER); + writel(0x0, gb->base + SPACEMIT_GFER); + /* Clear Interrupt */ + writel(0xffffffff, gb->base + SPACEMIT_GCRER); + writel(0xffffffff, gb->base + SPACEMIT_GCFER); + + ret = devm_request_threaded_irq(dev, irq, NULL, + spacemit_gpio_irq_handler, + IRQF_ONESHOT | IRQF_SHARED, + gb->gc.label, gb); + if (ret < 0) + return dev_err_probe(dev, ret, "failed to register IRQ\n"); + + ret = devm_gpiochip_add_data(dev, gc, gb); + if (ret) + return ret; + + /* Distuingish IRQ domain, for selecting threecells mode */ + irq_domain_update_bus_token(girq->domain, DOMAIN_BUS_WIRED); + + return 0; +} + +static int spacemit_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct spacemit_gpio *sg; + struct clk *core_clk, *bus_clk; + void __iomem *regs; + int i, irq, ret; + + sg = devm_kzalloc(dev, sizeof(*sg), GFP_KERNEL); + if (!sg) + return -ENOMEM; + + regs = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(regs)) + return PTR_ERR(regs); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + sg->dev = dev; + + core_clk = devm_clk_get_enabled(dev, "core"); + if (IS_ERR(core_clk)) + return dev_err_probe(dev, PTR_ERR(core_clk), "failed to get clock\n"); + + bus_clk = devm_clk_get_enabled(dev, "bus"); + if (IS_ERR(bus_clk)) + return dev_err_probe(dev, PTR_ERR(bus_clk), "failed to get bus clock\n"); + + for (i = 0; i < SPACEMIT_NR_BANKS; i++) { + ret = spacemit_gpio_add_bank(sg, regs, i, irq); + if (ret) + return ret; + } + + return 0; +} + +static const struct of_device_id spacemit_gpio_dt_ids[] = { + { .compatible = "spacemit,k1-gpio" }, + { /* sentinel */ } +}; + +static struct platform_driver spacemit_gpio_driver = { + .probe = spacemit_gpio_probe, + .driver = { + .name = "k1-gpio", + .of_match_table = spacemit_gpio_dt_ids, + }, +}; +module_platform_driver(spacemit_gpio_driver); + +MODULE_AUTHOR("Yixun Lan <dlan@gentoo.org>"); +MODULE_DESCRIPTION("GPIO driver for SpacemiT K1 SoC"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-tb10x.c b/drivers/gpio/gpio-tb10x.c index b6335cde455f..8cf676fd0a0b 100644 --- a/drivers/gpio/gpio-tb10x.c +++ b/drivers/gpio/gpio-tb10x.c @@ -183,7 +183,7 @@ static int tb10x_gpio_probe(struct platform_device *pdev) if (ret != 0) return ret; - tb10x_gpio->domain = irq_domain_add_linear(np, + tb10x_gpio->domain = irq_domain_create_linear(of_fwnode_handle(np), tb10x_gpio->gc.ngpio, &irq_generic_chip_ops, NULL); if (!tb10x_gpio->domain) { diff --git a/drivers/gpio/gpio-timberdale.c b/drivers/gpio/gpio-timberdale.c index fad979797486..cb303a26f4d3 100644 --- a/drivers/gpio/gpio-timberdale.c +++ b/drivers/gpio/gpio-timberdale.c @@ -103,20 +103,26 @@ static void timbgpio_irq_disable(struct irq_data *d) { struct timbgpio *tgpio = irq_data_get_irq_chip_data(d); int offset = d->irq - tgpio->irq_base; + irq_hw_number_t hwirq = irqd_to_hwirq(d); unsigned long flags; spin_lock_irqsave(&tgpio->lock, flags); tgpio->last_ier &= ~(1UL << offset); iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); spin_unlock_irqrestore(&tgpio->lock, flags); + + gpiochip_disable_irq(&tgpio->gpio, hwirq); } static void timbgpio_irq_enable(struct irq_data *d) { struct timbgpio *tgpio = irq_data_get_irq_chip_data(d); int offset = d->irq - tgpio->irq_base; + irq_hw_number_t hwirq = irqd_to_hwirq(d); unsigned long flags; + gpiochip_enable_irq(&tgpio->gpio, hwirq); + spin_lock_irqsave(&tgpio->lock, flags); tgpio->last_ier |= 1UL << offset; iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); @@ -205,11 +211,13 @@ static void timbgpio_irq(struct irq_desc *desc) iowrite32(tgpio->last_ier, tgpio->membase + TGPIO_IER); } -static struct irq_chip timbgpio_irqchip = { +static const struct irq_chip timbgpio_irqchip = { .name = "GPIO", .irq_enable = timbgpio_irq_enable, .irq_disable = timbgpio_irq_disable, .irq_set_type = timbgpio_irq_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int timbgpio_probe(struct platform_device *pdev) diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index bcd692229c7c..0d17985a5fdc 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -502,7 +502,6 @@ static void gpio_twl4030_power_off_action(void *data) static int gpio_twl4030_probe(struct platform_device *pdev) { struct twl4030_gpio_platform_data *pdata; - struct device_node *node = pdev->dev.of_node; struct gpio_twl4030_priv *priv; int ret, irq_base; @@ -524,8 +523,8 @@ static int gpio_twl4030_probe(struct platform_device *pdev) return irq_base; } - irq_domain_add_legacy(node, TWL4030_GPIO_MAX, irq_base, 0, - &irq_domain_simple_ops, NULL); + irq_domain_create_legacy(of_fwnode_handle(pdev->dev.of_node), TWL4030_GPIO_MAX, irq_base, 0, + &irq_domain_simple_ops, NULL); ret = twl4030_sih_setup(&pdev->dev, TWL4030_MODULE_GPIO, irq_base); if (ret < 0) diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c index 4dad7ce0c4dc..7de0d5b53d56 100644 --- a/drivers/gpio/gpio-vf610.c +++ b/drivers/gpio/gpio-vf610.c @@ -345,4 +345,6 @@ static struct platform_driver vf610_gpio_driver = { .probe = vf610_gpio_probe, }; -builtin_platform_driver(vf610_gpio_driver); +module_platform_driver(vf610_gpio_driver); +MODULE_DESCRIPTION("VF610 GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-virtuser.c b/drivers/gpio/gpio-virtuser.c index 13407fd4f0eb..eab6726953b4 100644 --- a/drivers/gpio/gpio-virtuser.c +++ b/drivers/gpio/gpio-virtuser.c @@ -401,10 +401,15 @@ static ssize_t gpio_virtuser_direction_do_write(struct file *file, char buf[32], *trimmed; int ret, dir, val = 0; - ret = simple_write_to_buffer(buf, sizeof(buf), ppos, user_buf, count); + if (count >= sizeof(buf)) + return -EINVAL; + + ret = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); if (ret < 0) return ret; + buf[ret] = '\0'; + trimmed = strim(buf); if (strcmp(trimmed, "input") == 0) { @@ -623,12 +628,15 @@ static ssize_t gpio_virtuser_consumer_write(struct file *file, char buf[GPIO_VIRTUSER_NAME_BUF_LEN + 2]; int ret; + if (count >= sizeof(buf)) + return -EINVAL; + ret = simple_write_to_buffer(buf, GPIO_VIRTUSER_NAME_BUF_LEN, ppos, user_buf, count); if (ret < 0) return ret; - buf[strlen(buf) - 1] = '\0'; + buf[ret] = '\0'; ret = gpiod_set_consumer_name(data->ad.desc, buf); if (ret) diff --git a/drivers/gpio/gpio-xgene-sb.c b/drivers/gpio/gpio-xgene-sb.c index 48b829733b15..b51b1fa726bb 100644 --- a/drivers/gpio/gpio-xgene-sb.c +++ b/drivers/gpio/gpio-xgene-sb.c @@ -103,12 +103,32 @@ static int xgene_gpio_sb_irq_set_type(struct irq_data *d, unsigned int type) return irq_chip_set_type_parent(d, IRQ_TYPE_LEVEL_HIGH); } -static struct irq_chip xgene_gpio_sb_irq_chip = { +static void xgene_gpio_sb_irq_mask(struct irq_data *d) +{ + struct xgene_gpio_sb *priv = irq_data_get_irq_chip_data(d); + + irq_chip_mask_parent(d); + + gpiochip_disable_irq(&priv->gc, d->hwirq); +} + +static void xgene_gpio_sb_irq_unmask(struct irq_data *d) +{ + struct xgene_gpio_sb *priv = irq_data_get_irq_chip_data(d); + + gpiochip_enable_irq(&priv->gc, d->hwirq); + + irq_chip_unmask_parent(d); +} + +static const struct irq_chip xgene_gpio_sb_irq_chip = { .name = "sbgpio", .irq_eoi = irq_chip_eoi_parent, - .irq_mask = irq_chip_mask_parent, - .irq_unmask = irq_chip_unmask_parent, + .irq_mask = xgene_gpio_sb_irq_mask, + .irq_unmask = xgene_gpio_sb_irq_unmask, .irq_set_type = xgene_gpio_sb_irq_set_type, + .flags = IRQCHIP_IMMUTABLE, + GPIOCHIP_IRQ_RESOURCE_HELPERS, }; static int xgene_gpio_sb_to_irq(struct gpio_chip *gc, u32 gpio) diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi-core.c index 69caa35c58df..12b24a717e43 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi-core.c @@ -23,29 +23,6 @@ #include "gpiolib.h" #include "gpiolib-acpi.h" -static int run_edge_events_on_boot = -1; -module_param(run_edge_events_on_boot, int, 0444); -MODULE_PARM_DESC(run_edge_events_on_boot, - "Run edge _AEI event-handlers at boot: 0=no, 1=yes, -1=auto"); - -static char *ignore_wake; -module_param(ignore_wake, charp, 0444); -MODULE_PARM_DESC(ignore_wake, - "controller@pin combos on which to ignore the ACPI wake flag " - "ignore_wake=controller@pin[,controller@pin[,...]]"); - -static char *ignore_interrupt; -module_param(ignore_interrupt, charp, 0444); -MODULE_PARM_DESC(ignore_interrupt, - "controller@pin combos on which to ignore interrupt " - "ignore_interrupt=controller@pin[,controller@pin[,...]]"); - -struct acpi_gpiolib_dmi_quirk { - bool no_edge_events_on_boot; - char *ignore_wake; - char *ignore_interrupt; -}; - /** * struct acpi_gpio_event - ACPI GPIO event handler data * @@ -96,10 +73,10 @@ struct acpi_gpio_chip { * @adev: reference to ACPI device which consumes GPIO resource * @flags: GPIO initialization flags * @gpioint: if %true this GPIO is of type GpioInt otherwise type is GpioIo + * @wake_capable: wake capability as provided by ACPI * @pin_config: pin bias as provided by ACPI * @polarity: interrupt polarity as provided by ACPI * @triggering: triggering type as provided by ACPI - * @wake_capable: wake capability as provided by ACPI * @debounce: debounce timeout as provided by ACPI * @quirks: Linux specific quirks as provided by struct acpi_gpio_mapping */ @@ -107,25 +84,14 @@ struct acpi_gpio_info { struct acpi_device *adev; enum gpiod_flags flags; bool gpioint; + bool wake_capable; int pin_config; int polarity; int triggering; - bool wake_capable; unsigned int debounce; unsigned int quirks; }; -/* - * For GPIO chips which call acpi_gpiochip_request_interrupts() before late_init - * (so builtin drivers) we register the ACPI GpioInt IRQ handlers from a - * late_initcall_sync() handler, so that other builtin drivers can register their - * OpRegions before the event handlers can run. This list contains GPIO chips - * for which the acpi_gpiochip_request_irqs() call has been deferred. - */ -static DEFINE_MUTEX(acpi_gpio_deferred_req_irqs_lock); -static LIST_HEAD(acpi_gpio_deferred_req_irqs_list); -static bool acpi_gpio_deferred_req_irqs_done; - static int acpi_gpiochip_find(struct gpio_chip *gc, const void *data) { /* First check the actual GPIO device */ @@ -268,7 +234,7 @@ static void acpi_gpiochip_request_irq(struct acpi_gpio_chip *acpi_gpio, event->irq_requested = true; /* Make sure we trigger the initial state of edge-triggered IRQs */ - if (run_edge_events_on_boot && + if (acpi_gpio_need_run_edge_events_on_boot() && (event->irqflags & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING))) { value = gpiod_get_raw_value_cansleep(event->desc); if (((event->irqflags & IRQF_TRIGGER_RISING) && value == 1) || @@ -350,42 +316,6 @@ static struct gpio_desc *acpi_request_own_gpiod(struct gpio_chip *chip, return desc; } -static bool acpi_gpio_in_ignore_list(const char *ignore_list, const char *controller_in, - unsigned int pin_in) -{ - const char *controller, *pin_str; - unsigned int pin; - char *endp; - int len; - - controller = ignore_list; - while (controller) { - pin_str = strchr(controller, '@'); - if (!pin_str) - goto err; - - len = pin_str - controller; - if (len == strlen(controller_in) && - strncmp(controller, controller_in, len) == 0) { - pin = simple_strtoul(pin_str + 1, &endp, 10); - if (*endp != 0 && *endp != ',') - goto err; - - if (pin == pin_in) - return true; - } - - controller = strchr(controller, ','); - if (controller) - controller++; - } - - return false; -err: - pr_err_once("Error: Invalid value for gpiolib_acpi.ignore_...: %s\n", ignore_list); - return false; -} - static bool acpi_gpio_irq_is_wake(struct device *parent, const struct acpi_resource_gpio *agpio) { @@ -394,7 +324,7 @@ static bool acpi_gpio_irq_is_wake(struct device *parent, if (agpio->wake_capable != ACPI_WAKE_CAPABLE) return false; - if (acpi_gpio_in_ignore_list(ignore_wake, dev_name(parent), pin)) { + if (acpi_gpio_in_ignore_list(ACPI_GPIO_IGNORE_WAKE, dev_name(parent), pin)) { dev_info(parent, "Ignoring wakeup on pin %u\n", pin); return false; } @@ -437,7 +367,7 @@ static acpi_status acpi_gpiochip_alloc_event(struct acpi_resource *ares, if (!handler) return AE_OK; - if (acpi_gpio_in_ignore_list(ignore_interrupt, dev_name(chip->parent), pin)) { + if (acpi_gpio_in_ignore_list(ACPI_GPIO_IGNORE_INTERRUPT, dev_name(chip->parent), pin)) { dev_info(chip->parent, "Ignoring interrupt on pin %u\n", pin); return AE_OK; } @@ -525,7 +455,6 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip) struct acpi_gpio_chip *acpi_gpio; acpi_handle handle; acpi_status status; - bool defer; if (!chip->parent || !chip->to_irq) return; @@ -544,14 +473,7 @@ void acpi_gpiochip_request_interrupts(struct gpio_chip *chip) acpi_walk_resources(handle, METHOD_NAME__AEI, acpi_gpiochip_alloc_event, acpi_gpio); - mutex_lock(&acpi_gpio_deferred_req_irqs_lock); - defer = !acpi_gpio_deferred_req_irqs_done; - if (defer) - list_add(&acpi_gpio->deferred_req_irqs_list_entry, - &acpi_gpio_deferred_req_irqs_list); - mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); - - if (defer) + if (acpi_gpio_add_to_deferred_list(&acpi_gpio->deferred_req_irqs_list_entry)) return; acpi_gpiochip_request_irqs(acpi_gpio); @@ -583,10 +505,7 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) if (ACPI_FAILURE(status)) return; - mutex_lock(&acpi_gpio_deferred_req_irqs_lock); - if (!list_empty(&acpi_gpio->deferred_req_irqs_list_entry)) - list_del_init(&acpi_gpio->deferred_req_irqs_list_entry); - mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); + acpi_gpio_remove_from_deferred_list(&acpi_gpio->deferred_req_irqs_list_entry); list_for_each_entry_safe_reverse(event, ep, &acpi_gpio->events, node) { if (event->irq_requested) { @@ -604,6 +523,14 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip) } EXPORT_SYMBOL_GPL(acpi_gpiochip_free_interrupts); +void __init acpi_gpio_process_deferred_list(struct list_head *list) +{ + struct acpi_gpio_chip *acpi_gpio, *tmp; + + list_for_each_entry_safe(acpi_gpio, tmp, list, deferred_req_irqs_list_entry) + acpi_gpiochip_request_irqs(acpi_gpio); +} + int acpi_dev_add_driver_gpios(struct acpi_device *adev, const struct acpi_gpio_mapping *gpios) { @@ -653,12 +580,12 @@ static bool acpi_get_driver_gpio_data(struct acpi_device *adev, for (gm = adev->driver_gpios; gm->name; gm++) if (!strcmp(name, gm->name) && gm->data && index < gm->size) { - const struct acpi_gpio_params *par = gm->data + index; + const struct acpi_gpio_params *params = gm->data + index; args->fwnode = acpi_fwnode_handle(adev); - args->args[0] = par->crs_entry_index; - args->args[1] = par->line_index; - args->args[2] = par->active_low; + args->args[0] = params->crs_entry_index; + args->args[1] = params->line_index; + args->args[2] = params->active_low; args->nargs = 3; *quirks = gm->quirks; @@ -743,10 +670,8 @@ static int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags, } struct acpi_gpio_lookup { - struct acpi_gpio_info info; - int index; - u16 pin_index; - bool active_low; + struct acpi_gpio_params params; + struct acpi_gpio_info *info; struct gpio_desc *desc; int n; }; @@ -754,6 +679,8 @@ struct acpi_gpio_lookup { static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) { struct acpi_gpio_lookup *lookup = data; + struct acpi_gpio_params *params = &lookup->params; + struct acpi_gpio_info *info = lookup->info; if (ares->type != ACPI_RESOURCE_TYPE_GPIO) return 1; @@ -764,26 +691,26 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) struct gpio_desc *desc; u16 pin_index; - if (lookup->info.quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint) - lookup->index++; + if (info->quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint) + params->crs_entry_index++; - if (lookup->n++ != lookup->index) + if (lookup->n++ != params->crs_entry_index) return 1; - pin_index = lookup->pin_index; + pin_index = params->line_index; if (pin_index >= agpio->pin_table_length) return 1; - if (lookup->info.quirks & ACPI_GPIO_QUIRK_ABSOLUTE_NUMBER) + if (info->quirks & ACPI_GPIO_QUIRK_ABSOLUTE_NUMBER) desc = gpio_to_desc(agpio->pin_table[pin_index]); else desc = acpi_get_gpiod(agpio->resource_source.string_ptr, agpio->pin_table[pin_index]); lookup->desc = desc; - lookup->info.pin_config = agpio->pin_config; - lookup->info.debounce = agpio->debounce_timeout; - lookup->info.gpioint = gpioint; - lookup->info.wake_capable = acpi_gpio_irq_is_wake(&lookup->info.adev->dev, agpio); + info->pin_config = agpio->pin_config; + info->debounce = agpio->debounce_timeout; + info->gpioint = gpioint; + info->wake_capable = acpi_gpio_irq_is_wake(&info->adev->dev, agpio); /* * Polarity and triggering are only specified for GpioInt @@ -792,23 +719,23 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data) * - ACPI_ACTIVE_LOW == GPIO_ACTIVE_LOW * - ACPI_ACTIVE_HIGH == GPIO_ACTIVE_HIGH */ - if (lookup->info.gpioint) { - lookup->info.polarity = agpio->polarity; - lookup->info.triggering = agpio->triggering; + if (info->gpioint) { + info->polarity = agpio->polarity; + info->triggering = agpio->triggering; } else { - lookup->info.polarity = lookup->active_low; + info->polarity = params->active_low; } - lookup->info.flags = acpi_gpio_to_gpiod_flags(agpio, lookup->info.polarity); + info->flags = acpi_gpio_to_gpiod_flags(agpio, info->polarity); } return 1; } -static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, - struct acpi_gpio_info *info) +static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup) { - struct acpi_device *adev = lookup->info.adev; + struct acpi_gpio_info *info = lookup->info; + struct acpi_device *adev = info->adev; struct list_head res_list; int ret; @@ -825,22 +752,22 @@ static int acpi_gpio_resource_lookup(struct acpi_gpio_lookup *lookup, if (!lookup->desc) return -ENOENT; - if (info) - *info = lookup->info; return 0; } -static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, - const char *propname, int index, +static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, const char *propname, struct acpi_gpio_lookup *lookup) { struct fwnode_reference_args args; + struct acpi_gpio_params *params = &lookup->params; + struct acpi_gpio_info *info = lookup->info; + unsigned int index = params->crs_entry_index; unsigned int quirks = 0; int ret; memset(&args, 0, sizeof(args)); - ret = __acpi_node_get_property_reference(fwnode, propname, index, 3, - &args); + + ret = __acpi_node_get_property_reference(fwnode, propname, index, 3, &args); if (ret) { struct acpi_device *adev; @@ -857,12 +784,12 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, if (args.nargs != 3) return -EPROTO; - lookup->index = args.args[0]; - lookup->pin_index = args.args[1]; - lookup->active_low = !!args.args[2]; + params->crs_entry_index = args.args[0]; + params->line_index = args.args[1]; + params->active_low = !!args.args[2]; - lookup->info.adev = to_acpi_device_node(args.fwnode); - lookup->info.quirks = quirks; + info->adev = to_acpi_device_node(args.fwnode); + info->quirks = quirks; return 0; } @@ -871,96 +798,83 @@ static int acpi_gpio_property_lookup(struct fwnode_handle *fwnode, * acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources * @adev: pointer to a ACPI device to get GPIO from * @propname: Property name of the GPIO (optional) - * @index: index of GpioIo/GpioInt resource (starting from %0) - * @info: info pointer to fill in (optional) + * @lookup: pointer to struct acpi_gpio_lookup to fill in * - * Function goes through ACPI resources for @adev and based on @index looks + * Function goes through ACPI resources for @adev and based on @lookup.index looks * up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor, - * and returns it. @index matches GpioIo/GpioInt resources only so if there - * are total %3 GPIO resources, the index goes from %0 to %2. + * and returns it. @lookup.index matches GpioIo/GpioInt resources only so if there + * are total 3 GPIO resources, the index goes from 0 to 2. * * If @propname is specified the GPIO is looked using device property. In * that case @index is used to select the GPIO entry in the property value * (in case of multiple). * * Returns: - * GPIO descriptor to use with Linux generic GPIO API. - * If the GPIO cannot be translated or there is an error an ERR_PTR is - * returned. + * 0 on success, negative errno on failure. + * + * The @lookup is filled with GPIO descriptor to use with Linux generic GPIO API. + * If the GPIO cannot be translated an error will be returned. * * Note: if the GPIO resource has multiple entries in the pin list, this * function only returns the first. */ -static struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev, - const char *propname, - int index, - struct acpi_gpio_info *info) +static int acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname, + struct acpi_gpio_lookup *lookup) { - struct acpi_gpio_lookup lookup; + struct acpi_gpio_params *params = &lookup->params; + struct acpi_gpio_info *info = lookup->info; int ret; - memset(&lookup, 0, sizeof(lookup)); - lookup.index = index; - if (propname) { dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname); - ret = acpi_gpio_property_lookup(acpi_fwnode_handle(adev), - propname, index, &lookup); + ret = acpi_gpio_property_lookup(acpi_fwnode_handle(adev), propname, lookup); if (ret) - return ERR_PTR(ret); + return ret; - 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); + dev_dbg(&adev->dev, "GPIO: _DSD returned %s %u %u %u\n", + dev_name(&info->adev->dev), + params->crs_entry_index, params->line_index, params->active_low); } else { - dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index); - lookup.info.adev = adev; + dev_dbg(&adev->dev, "GPIO: looking up %u in _CRS\n", params->crs_entry_index); + info->adev = adev; } - ret = acpi_gpio_resource_lookup(&lookup, info); - return ret ? ERR_PTR(ret) : lookup.desc; + return acpi_gpio_resource_lookup(lookup); } /** * acpi_get_gpiod_from_data() - get a GPIO descriptor from ACPI data node * @fwnode: pointer to an ACPI firmware node to get the GPIO information from * @propname: Property name of the GPIO - * @index: index of GpioIo/GpioInt resource (starting from %0) - * @info: info pointer to fill in (optional) + * @lookup: pointer to struct acpi_gpio_lookup to fill in * * This function uses the property-based GPIO lookup to get to the GPIO * resource with the relevant information from a data-only ACPI firmware node * and uses that to obtain the GPIO descriptor to return. * * Returns: - * GPIO descriptor to use with Linux generic GPIO API. - * If the GPIO cannot be translated or there is an error an ERR_PTR is - * returned. + * 0 on success, negative errno on failure. + * + * The @lookup is filled with GPIO descriptor to use with Linux generic GPIO API. + * If the GPIO cannot be translated an error will be returned. */ -static struct gpio_desc *acpi_get_gpiod_from_data(struct fwnode_handle *fwnode, - const char *propname, - int index, - struct acpi_gpio_info *info) +static int acpi_get_gpiod_from_data(struct fwnode_handle *fwnode, const char *propname, + struct acpi_gpio_lookup *lookup) { - struct acpi_gpio_lookup lookup; int ret; if (!is_acpi_data_node(fwnode)) - return ERR_PTR(-ENODEV); + return -ENODEV; if (!propname) - return ERR_PTR(-EINVAL); - - memset(&lookup, 0, sizeof(lookup)); - lookup.index = index; + return -EINVAL; - ret = acpi_gpio_property_lookup(fwnode, propname, index, &lookup); + ret = acpi_gpio_property_lookup(fwnode, propname, lookup); if (ret) - return ERR_PTR(ret); + return ret; - ret = acpi_gpio_resource_lookup(&lookup, info); - return ret ? ERR_PTR(ret) : lookup.desc; + return acpi_gpio_resource_lookup(lookup); } static bool acpi_can_fallback_to_crs(struct acpi_device *adev, @@ -982,17 +896,25 @@ __acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, unsigned int bool can_fallback, struct acpi_gpio_info *info) { struct acpi_device *adev = to_acpi_device_node(fwnode); + struct acpi_gpio_lookup lookup; struct gpio_desc *desc; char propname[32]; + int ret; + + memset(&lookup, 0, sizeof(lookup)); + lookup.params.crs_entry_index = idx; + lookup.info = info; /* Try first from _DSD */ for_each_gpio_property_name(propname, con_id) { if (adev) - desc = acpi_get_gpiod_by_index(adev, - propname, idx, info); + ret = acpi_get_gpiod_by_index(adev, propname, &lookup); else - desc = acpi_get_gpiod_from_data(fwnode, - propname, idx, info); + ret = acpi_get_gpiod_from_data(fwnode, propname, &lookup); + if (ret) + continue; + + desc = lookup.desc; if (PTR_ERR(desc) == -EPROBE_DEFER) return desc; @@ -1001,8 +923,13 @@ __acpi_find_gpio(struct fwnode_handle *fwnode, const char *con_id, unsigned int } /* Then from plain _CRS GPIOs */ - if (can_fallback) - return acpi_get_gpiod_by_index(adev, NULL, idx, info); + if (can_fallback) { + ret = acpi_get_gpiod_by_index(adev, NULL, &lookup); + if (ret) + return ERR_PTR(ret); + + return lookup.desc; + } return ERR_PTR(-ENOENT); } @@ -1488,248 +1415,3 @@ int acpi_gpio_count(const struct fwnode_handle *fwnode, const char *con_id) } return count ? count : -ENOENT; } - -/* Run deferred acpi_gpiochip_request_irqs() */ -static int __init acpi_gpio_handle_deferred_request_irqs(void) -{ - struct acpi_gpio_chip *acpi_gpio, *tmp; - - mutex_lock(&acpi_gpio_deferred_req_irqs_lock); - list_for_each_entry_safe(acpi_gpio, tmp, - &acpi_gpio_deferred_req_irqs_list, - deferred_req_irqs_list_entry) - acpi_gpiochip_request_irqs(acpi_gpio); - - acpi_gpio_deferred_req_irqs_done = true; - mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); - - return 0; -} -/* We must use _sync so that this runs after the first deferred_probe run */ -late_initcall_sync(acpi_gpio_handle_deferred_request_irqs); - -static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { - { - /* - * The Minix Neo Z83-4 has a micro-USB-B id-pin handler for - * a non existing micro-USB-B connector which puts the HDMI - * DDC pins in GPIO mode, breaking HDMI support. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "MINIX"), - DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .no_edge_events_on_boot = true, - }, - }, - { - /* - * The Terra Pad 1061 has a micro-USB-B id-pin handler, which - * instead of controlling the actual micro-USB-B turns the 5V - * boost for its USB-A connector off. The actual micro-USB-B - * connector is wired for charging only. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"), - DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .no_edge_events_on_boot = true, - }, - }, - { - /* - * The Dell Venue 10 Pro 5055, with Bay Trail SoC + TI PMIC uses an - * external embedded-controller connected via I2C + an ACPI GPIO - * event handler on INT33FFC:02 pin 12, causing spurious wakeups. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), - DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "INT33FC:02@12", - }, - }, - { - /* - * HP X2 10 models with Cherry Trail SoC + TI PMIC use an - * external embedded-controller connected via I2C + an ACPI GPIO - * event handler on INT33FF:01 pin 0, causing spurious wakeups. - * When suspending by closing the LID, the power to the USB - * keyboard is turned off, causing INT0002 ACPI events to - * trigger once the XHCI controller notices the keyboard is - * gone. So INT0002 events cause spurious wakeups too. Ignoring - * EC wakes breaks wakeup when opening the lid, the user needs - * to press the power-button to wakeup the system. The - * alternative is suspend simply not working, which is worse. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "HP"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "INT33FF:01@0,INT0002:00@2", - }, - }, - { - /* - * HP X2 10 models with Bay Trail SoC + AXP288 PMIC use an - * external embedded-controller connected via I2C + an ACPI GPIO - * event handler on INT33FC:02 pin 28, causing spurious wakeups. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), - DMI_MATCH(DMI_BOARD_NAME, "815D"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "INT33FC:02@28", - }, - }, - { - /* - * HP X2 10 models with Cherry Trail SoC + AXP288 PMIC use an - * external embedded-controller connected via I2C + an ACPI GPIO - * event handler on INT33FF:01 pin 0, causing spurious wakeups. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "HP"), - DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), - DMI_MATCH(DMI_BOARD_NAME, "813E"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "INT33FF:01@0", - }, - }, - { - /* - * Interrupt storm caused from edge triggered floating pin - * Found in BIOS UX325UAZ.300 - * https://bugzilla.kernel.org/show_bug.cgi?id=216208 - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), - DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UAZ_UM325UAZ"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_interrupt = "AMDI0030:00@18", - }, - }, - { - /* - * Spurious wakeups from TP_ATTN# pin - * Found in BIOS 1.7.8 - * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 - */ - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "ELAN0415:00@9", - }, - }, - { - /* - * Spurious wakeups from TP_ATTN# pin - * Found in BIOS 1.7.8 - * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 - */ - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "ELAN0415:00@9", - }, - }, - { - /* - * Spurious wakeups from TP_ATTN# pin - * Found in BIOS 1.7.7 - */ - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "SYNA1202:00@16", - }, - }, - { - /* - * On the Peaq C1010 2-in-1 INT33FC:00 pin 3 is connected to - * a "dolby" button. At the ACPI level an _AEI event-handler - * is connected which sets an ACPI variable to 1 on both - * edges. This variable can be polled + cleared to 0 using - * WMI. But since the variable is set on both edges the WMI - * interface is pretty useless even when polling. - * So instead the x86-android-tablets code instantiates - * a gpio-keys platform device for it. - * Ignore the _AEI handler for the pin, so that it is not busy. - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), - DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_interrupt = "INT33FC:00@3", - }, - }, - { - /* - * Spurious wakeups from TP_ATTN# pin - * Found in BIOS 0.35 - * https://gitlab.freedesktop.org/drm/amd/-/issues/3073 - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "GPD"), - DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_wake = "PNP0C50:00@8", - }, - }, - { - /* - * Spurious wakeups from GPIO 11 - * Found in BIOS 1.04 - * https://gitlab.freedesktop.org/drm/amd/-/issues/3954 - */ - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Acer"), - DMI_MATCH(DMI_PRODUCT_FAMILY, "Acer Nitro V 14"), - }, - .driver_data = &(struct acpi_gpiolib_dmi_quirk) { - .ignore_interrupt = "AMDI0030:00@11", - }, - }, - {} /* Terminating entry */ -}; - -static int __init acpi_gpio_setup_params(void) -{ - const struct acpi_gpiolib_dmi_quirk *quirk = NULL; - const struct dmi_system_id *id; - - id = dmi_first_match(gpiolib_acpi_quirks); - if (id) - quirk = id->driver_data; - - if (run_edge_events_on_boot < 0) { - if (quirk && quirk->no_edge_events_on_boot) - run_edge_events_on_boot = 0; - else - run_edge_events_on_boot = 1; - } - - if (ignore_wake == NULL && quirk && quirk->ignore_wake) - ignore_wake = quirk->ignore_wake; - - if (ignore_interrupt == NULL && quirk && quirk->ignore_interrupt) - ignore_interrupt = quirk->ignore_interrupt; - - return 0; -} - -/* Directly after dmi_setup() which runs as core_initcall() */ -postcore_initcall(acpi_gpio_setup_params); diff --git a/drivers/gpio/gpiolib-acpi-quirks.c b/drivers/gpio/gpiolib-acpi-quirks.c new file mode 100644 index 000000000000..219667315b2c --- /dev/null +++ b/drivers/gpio/gpiolib-acpi-quirks.c @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ACPI quirks for GPIO ACPI helpers + * + * Author: Hans de Goede <hdegoede@redhat.com> + */ + +#include <linux/dmi.h> +#include <linux/kstrtox.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/printk.h> +#include <linux/string.h> +#include <linux/types.h> + +#include "gpiolib-acpi.h" + +static int run_edge_events_on_boot = -1; +module_param(run_edge_events_on_boot, int, 0444); +MODULE_PARM_DESC(run_edge_events_on_boot, + "Run edge _AEI event-handlers at boot: 0=no, 1=yes, -1=auto"); + +static char *ignore_wake; +module_param(ignore_wake, charp, 0444); +MODULE_PARM_DESC(ignore_wake, + "controller@pin combos on which to ignore the ACPI wake flag " + "ignore_wake=controller@pin[,controller@pin[,...]]"); + +static char *ignore_interrupt; +module_param(ignore_interrupt, charp, 0444); +MODULE_PARM_DESC(ignore_interrupt, + "controller@pin combos on which to ignore interrupt " + "ignore_interrupt=controller@pin[,controller@pin[,...]]"); + +/* + * For GPIO chips which call acpi_gpiochip_request_interrupts() before late_init + * (so builtin drivers) we register the ACPI GpioInt IRQ handlers from a + * late_initcall_sync() handler, so that other builtin drivers can register their + * OpRegions before the event handlers can run. This list contains GPIO chips + * for which the acpi_gpiochip_request_irqs() call has been deferred. + */ +static DEFINE_MUTEX(acpi_gpio_deferred_req_irqs_lock); +static LIST_HEAD(acpi_gpio_deferred_req_irqs_list); +static bool acpi_gpio_deferred_req_irqs_done; + +bool acpi_gpio_add_to_deferred_list(struct list_head *list) +{ + bool defer; + + mutex_lock(&acpi_gpio_deferred_req_irqs_lock); + defer = !acpi_gpio_deferred_req_irqs_done; + if (defer) + list_add(list, &acpi_gpio_deferred_req_irqs_list); + mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); + + return defer; +} + +void acpi_gpio_remove_from_deferred_list(struct list_head *list) +{ + mutex_lock(&acpi_gpio_deferred_req_irqs_lock); + if (!list_empty(list)) + list_del_init(list); + mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); +} + +int acpi_gpio_need_run_edge_events_on_boot(void) +{ + return run_edge_events_on_boot; +} + +bool acpi_gpio_in_ignore_list(enum acpi_gpio_ignore_list list, + const char *controller_in, unsigned int pin_in) +{ + const char *ignore_list, *controller, *pin_str; + unsigned int pin; + char *endp; + int len; + + switch (list) { + case ACPI_GPIO_IGNORE_WAKE: + ignore_list = ignore_wake; + break; + case ACPI_GPIO_IGNORE_INTERRUPT: + ignore_list = ignore_interrupt; + break; + default: + return false; + } + + controller = ignore_list; + while (controller) { + pin_str = strchr(controller, '@'); + if (!pin_str) + goto err; + + len = pin_str - controller; + if (len == strlen(controller_in) && + strncmp(controller, controller_in, len) == 0) { + pin = simple_strtoul(pin_str + 1, &endp, 10); + if (*endp != 0 && *endp != ',') + goto err; + + if (pin == pin_in) + return true; + } + + controller = strchr(controller, ','); + if (controller) + controller++; + } + + return false; +err: + pr_err_once("Error: Invalid value for gpiolib_acpi.ignore_...: %s\n", ignore_list); + return false; +} + +/* Run deferred acpi_gpiochip_request_irqs() */ +static int __init acpi_gpio_handle_deferred_request_irqs(void) +{ + mutex_lock(&acpi_gpio_deferred_req_irqs_lock); + acpi_gpio_process_deferred_list(&acpi_gpio_deferred_req_irqs_list); + acpi_gpio_deferred_req_irqs_done = true; + mutex_unlock(&acpi_gpio_deferred_req_irqs_lock); + + return 0; +} +/* We must use _sync so that this runs after the first deferred_probe run */ +late_initcall_sync(acpi_gpio_handle_deferred_request_irqs); + +struct acpi_gpiolib_dmi_quirk { + bool no_edge_events_on_boot; + char *ignore_wake; + char *ignore_interrupt; +}; + +static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = { + { + /* + * The Minix Neo Z83-4 has a micro-USB-B id-pin handler for + * a non existing micro-USB-B connector which puts the HDMI + * DDC pins in GPIO mode, breaking HDMI support. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "MINIX"), + DMI_MATCH(DMI_PRODUCT_NAME, "Z83-4"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .no_edge_events_on_boot = true, + }, + }, + { + /* + * The Terra Pad 1061 has a micro-USB-B id-pin handler, which + * instead of controlling the actual micro-USB-B turns the 5V + * boost for its USB-A connector off. The actual micro-USB-B + * connector is wired for charging only. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Wortmann_AG"), + DMI_MATCH(DMI_PRODUCT_NAME, "TERRA_PAD_1061"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .no_edge_events_on_boot = true, + }, + }, + { + /* + * The Dell Venue 10 Pro 5055, with Bay Trail SoC + TI PMIC uses an + * external embedded-controller connected via I2C + an ACPI GPIO + * event handler on INT33FFC:02 pin 12, causing spurious wakeups. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."), + DMI_MATCH(DMI_PRODUCT_NAME, "Venue 10 Pro 5055"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "INT33FC:02@12", + }, + }, + { + /* + * HP X2 10 models with Cherry Trail SoC + TI PMIC use an + * external embedded-controller connected via I2C + an ACPI GPIO + * event handler on INT33FF:01 pin 0, causing spurious wakeups. + * When suspending by closing the LID, the power to the USB + * keyboard is turned off, causing INT0002 ACPI events to + * trigger once the XHCI controller notices the keyboard is + * gone. So INT0002 events cause spurious wakeups too. Ignoring + * EC wakes breaks wakeup when opening the lid, the user needs + * to press the power-button to wakeup the system. The + * alternative is suspend simply not working, which is worse. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP x2 Detachable 10-p0XX"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "INT33FF:01@0,INT0002:00@2", + }, + }, + { + /* + * HP X2 10 models with Bay Trail SoC + AXP288 PMIC use an + * external embedded-controller connected via I2C + an ACPI GPIO + * event handler on INT33FC:02 pin 28, causing spurious wakeups. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), + DMI_MATCH(DMI_BOARD_NAME, "815D"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "INT33FC:02@28", + }, + }, + { + /* + * HP X2 10 models with Cherry Trail SoC + AXP288 PMIC use an + * external embedded-controller connected via I2C + an ACPI GPIO + * event handler on INT33FF:01 pin 0, causing spurious wakeups. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Pavilion x2 Detachable"), + DMI_MATCH(DMI_BOARD_NAME, "813E"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "INT33FF:01@0", + }, + }, + { + /* + * Interrupt storm caused from edge triggered floating pin + * Found in BIOS UX325UAZ.300 + * https://bugzilla.kernel.org/show_bug.cgi?id=216208 + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), + DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UAZ_UM325UAZ"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_interrupt = "AMDI0030:00@18", + }, + }, + { + /* + * Spurious wakeups from TP_ATTN# pin + * Found in BIOS 1.7.8 + * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 + */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "NL5xNU"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "ELAN0415:00@9", + }, + }, + { + /* + * Spurious wakeups from TP_ATTN# pin + * Found in BIOS 1.7.8 + * https://gitlab.freedesktop.org/drm/amd/-/issues/1722#note_1720627 + */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "NL5xRU"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "ELAN0415:00@9", + }, + }, + { + /* + * Spurious wakeups from TP_ATTN# pin + * Found in BIOS 1.7.7 + */ + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "NH5xAx"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "SYNA1202:00@16", + }, + }, + { + /* + * On the Peaq C1010 2-in-1 INT33FC:00 pin 3 is connected to + * a "dolby" button. At the ACPI level an _AEI event-handler + * is connected which sets an ACPI variable to 1 on both + * edges. This variable can be polled + cleared to 0 using + * WMI. But since the variable is set on both edges the WMI + * interface is pretty useless even when polling. + * So instead the x86-android-tablets code instantiates + * a gpio-keys platform device for it. + * Ignore the _AEI handler for the pin, so that it is not busy. + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "PEAQ"), + DMI_MATCH(DMI_PRODUCT_NAME, "PEAQ PMM C1010 MD99187"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_interrupt = "INT33FC:00@3", + }, + }, + { + /* + * Spurious wakeups from TP_ATTN# pin + * Found in BIOS 0.35 + * https://gitlab.freedesktop.org/drm/amd/-/issues/3073 + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "GPD"), + DMI_MATCH(DMI_PRODUCT_NAME, "G1619-04"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_wake = "PNP0C50:00@8", + }, + }, + { + /* + * Spurious wakeups from GPIO 11 + * Found in BIOS 1.04 + * https://gitlab.freedesktop.org/drm/amd/-/issues/3954 + */ + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Acer"), + DMI_MATCH(DMI_PRODUCT_FAMILY, "Acer Nitro V 14"), + }, + .driver_data = &(struct acpi_gpiolib_dmi_quirk) { + .ignore_interrupt = "AMDI0030:00@11", + }, + }, + {} /* Terminating entry */ +}; + +static int __init acpi_gpio_setup_params(void) +{ + const struct acpi_gpiolib_dmi_quirk *quirk = NULL; + const struct dmi_system_id *id; + + id = dmi_first_match(gpiolib_acpi_quirks); + if (id) + quirk = id->driver_data; + + if (run_edge_events_on_boot < 0) { + if (quirk && quirk->no_edge_events_on_boot) + run_edge_events_on_boot = 0; + else + run_edge_events_on_boot = 1; + } + + if (ignore_wake == NULL && quirk && quirk->ignore_wake) + ignore_wake = quirk->ignore_wake; + + if (ignore_interrupt == NULL && quirk && quirk->ignore_interrupt) + ignore_interrupt = quirk->ignore_interrupt; + + return 0; +} + +/* Directly after dmi_setup() which runs as core_initcall() */ +postcore_initcall(acpi_gpio_setup_params); diff --git a/drivers/gpio/gpiolib-acpi.h b/drivers/gpio/gpiolib-acpi.h index 7e1c51d04040..a90267470a4e 100644 --- a/drivers/gpio/gpiolib-acpi.h +++ b/drivers/gpio/gpiolib-acpi.h @@ -58,4 +58,19 @@ static inline int acpi_gpio_count(const struct fwnode_handle *fwnode, } #endif +void acpi_gpio_process_deferred_list(struct list_head *list); + +bool acpi_gpio_add_to_deferred_list(struct list_head *list); +void acpi_gpio_remove_from_deferred_list(struct list_head *list); + +int acpi_gpio_need_run_edge_events_on_boot(void); + +enum acpi_gpio_ignore_list { + ACPI_GPIO_IGNORE_WAKE, + ACPI_GPIO_IGNORE_INTERRUPT, +}; + +bool acpi_gpio_in_ignore_list(enum acpi_gpio_ignore_list list, + const char *controller_in, unsigned int pin_in); + #endif /* GPIOLIB_ACPI_H */ diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index 107d75558b5a..e6a289fa0f8f 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -1366,9 +1366,6 @@ static long linereq_set_values(struct linereq *lr, void __user *ip) /* scan requested lines to determine the subset to be set */ for (num_set = 0, i = 0; i < lr->num_lines; i++) { if (lv.mask & BIT_ULL(i)) { - /* setting inputs is not allowed */ - if (!test_bit(FLAG_IS_OUT, &lr->lines[i].desc->flags)) - return -EPERM; /* add to compacted values */ if (lv.bits & BIT_ULL(i)) __set_bit(num_set, vals); diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 120d1ec5af3b..4d5f83b17624 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -6,7 +6,7 @@ * Copyright (c) 2011 John Crispin <john@phrozen.org> */ -#include <linux/device.h> +#include <linux/device/devres.h> #include <linux/err.h> #include <linux/export.h> #include <linux/gfp.h> @@ -19,32 +19,14 @@ struct fwnode_handle; struct lock_class_key; -static void devm_gpiod_release(struct device *dev, void *res) +static void devm_gpiod_release(void *desc) { - struct gpio_desc **desc = res; - - gpiod_put(*desc); -} - -static int devm_gpiod_match(struct device *dev, void *res, void *data) -{ - struct gpio_desc **this = res, **gpio = data; - - return *this == *gpio; + gpiod_put(desc); } -static void devm_gpiod_release_array(struct device *dev, void *res) +static void devm_gpiod_release_array(void *descs) { - struct gpio_descs **descs = res; - - gpiod_put_array(*descs); -} - -static int devm_gpiod_match_array(struct device *dev, void *res, void *data) -{ - struct gpio_descs **this = res, **gpios = data; - - return *this == *gpios; + gpiod_put_array(descs); } /** @@ -114,8 +96,8 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, unsigned int idx, enum gpiod_flags flags) { - struct gpio_desc **dr; struct gpio_desc *desc; + int ret; desc = gpiod_get_index(dev, con_id, idx, flags); if (IS_ERR(desc)) @@ -126,23 +108,16 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev, * already under resource management by this device. */ if (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) { - struct devres *dres; + bool dres; - dres = devres_find(dev, devm_gpiod_release, - devm_gpiod_match, &desc); + dres = devm_is_action_added(dev, devm_gpiod_release, desc); if (dres) return desc; } - dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), - GFP_KERNEL); - if (!dr) { - gpiod_put(desc); - return ERR_PTR(-ENOMEM); - } - - *dr = desc; - devres_add(dev, dr); + ret = devm_add_action_or_reset(dev, devm_gpiod_release, desc); + if (ret) + return ERR_PTR(ret); return desc; } @@ -171,22 +146,16 @@ struct gpio_desc *devm_fwnode_gpiod_get_index(struct device *dev, enum gpiod_flags flags, const char *label) { - struct gpio_desc **dr; struct gpio_desc *desc; - - dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *), - GFP_KERNEL); - if (!dr) - return ERR_PTR(-ENOMEM); + int ret; desc = gpiod_find_and_request(dev, fwnode, con_id, index, flags, label, false); - if (IS_ERR(desc)) { - devres_free(dr); + if (IS_ERR(desc)) return desc; - } - *dr = desc; - devres_add(dev, dr); + ret = devm_add_action_or_reset(dev, devm_gpiod_release, desc); + if (ret) + return ERR_PTR(ret); return desc; } @@ -244,22 +213,16 @@ struct gpio_descs *__must_check devm_gpiod_get_array(struct device *dev, const char *con_id, enum gpiod_flags flags) { - struct gpio_descs **dr; struct gpio_descs *descs; - - dr = devres_alloc(devm_gpiod_release_array, - sizeof(struct gpio_descs *), GFP_KERNEL); - if (!dr) - return ERR_PTR(-ENOMEM); + int ret; descs = gpiod_get_array(dev, con_id, flags); - if (IS_ERR(descs)) { - devres_free(dr); + if (IS_ERR(descs)) return descs; - } - *dr = descs; - devres_add(dev, dr); + ret = devm_add_action_or_reset(dev, devm_gpiod_release_array, descs); + if (ret) + return ERR_PTR(ret); return descs; } @@ -307,8 +270,7 @@ EXPORT_SYMBOL_GPL(devm_gpiod_get_array_optional); */ void devm_gpiod_put(struct device *dev, struct gpio_desc *desc) { - WARN_ON(devres_release(dev, devm_gpiod_release, devm_gpiod_match, - &desc)); + devm_release_action(dev, devm_gpiod_release, desc); } EXPORT_SYMBOL_GPL(devm_gpiod_put); @@ -332,13 +294,13 @@ void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc) if (IS_ERR_OR_NULL(desc)) return; - ret = devres_destroy(dev, devm_gpiod_release, - devm_gpiod_match, &desc); + /* * If the GPIO descriptor is requested as nonexclusive, we * may call this function several times on the same descriptor * so it is OK if devres_destroy() returns -ENOENT. */ + ret = devm_remove_action_nowarn(dev, devm_gpiod_release, desc); if (ret == -ENOENT) return; /* Anything else we should warn about */ @@ -357,8 +319,7 @@ EXPORT_SYMBOL_GPL(devm_gpiod_unhinge); */ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs) { - WARN_ON(devres_release(dev, devm_gpiod_release_array, - devm_gpiod_match_array, &descs)); + devm_remove_action(dev, devm_gpiod_release_array, descs); } EXPORT_SYMBOL_GPL(devm_gpiod_put_array); diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 65f6a7177b78..73ba73b31cb1 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -224,6 +224,15 @@ static void of_gpio_try_fixup_polarity(const struct device_node *np, */ { "lantiq,pci-xway", "gpio-reset", false }, #endif +#if IS_ENABLED(CONFIG_REGULATOR_S5M8767) + /* + * According to S5M8767, the DVS and DS pin are + * active-high signals. However, exynos5250-spring.dts use + * active-low setting. + */ + { "samsung,s5m8767-pmic", "s5m8767,pmic-buck-dvs-gpios", true }, + { "samsung,s5m8767-pmic", "s5m8767,pmic-buck-ds-gpios", true }, +#endif #if IS_ENABLED(CONFIG_TOUCHSCREEN_TSC2005) /* * DTS for Nokia N900 incorrectly specified "active high" @@ -1278,3 +1287,11 @@ void of_gpiochip_remove(struct gpio_chip *chip) { of_node_put(dev_of_node(&chip->gpiodev->dev)); } + +bool of_gpiochip_instance_match(struct gpio_chip *gc, unsigned int index) +{ + if (gc->of_node_instance_match) + return gc->of_node_instance_match(gc, index); + + return false; +} diff --git a/drivers/gpio/gpiolib-of.h b/drivers/gpio/gpiolib-of.h index 16d6ac8cb156..3eebfac290c5 100644 --- a/drivers/gpio/gpiolib-of.h +++ b/drivers/gpio/gpiolib-of.h @@ -22,6 +22,7 @@ struct gpio_desc *of_find_gpio(struct device_node *np, unsigned long *lookupflags); int of_gpiochip_add(struct gpio_chip *gc); void of_gpiochip_remove(struct gpio_chip *gc); +bool of_gpiochip_instance_match(struct gpio_chip *gc, unsigned int index); int of_gpio_count(const struct fwnode_handle *fwnode, const char *con_id); #else static inline struct gpio_desc *of_find_gpio(struct device_node *np, @@ -33,6 +34,11 @@ static inline struct gpio_desc *of_find_gpio(struct device_node *np, } static inline int of_gpiochip_add(struct gpio_chip *gc) { return 0; } static inline void of_gpiochip_remove(struct gpio_chip *gc) { } +static inline bool of_gpiochip_instance_match(struct gpio_chip *gc, + unsigned int index) +{ + return false; +} static inline int of_gpio_count(const struct fwnode_handle *fwnode, const char *con_id) { diff --git a/drivers/gpio/gpiolib-sysfs.c b/drivers/gpio/gpiolib-sysfs.c index 1acfa43bf1ab..4a3aa09dad9d 100644 --- a/drivers/gpio/gpiolib-sysfs.c +++ b/drivers/gpio/gpiolib-sysfs.c @@ -134,17 +134,15 @@ static ssize_t value_store(struct device *dev, long value; status = kstrtol(buf, 0, &value); + if (status) + return status; guard(mutex)(&data->mutex); - if (!test_bit(FLAG_IS_OUT, &desc->flags)) - return -EPERM; - + status = gpiod_set_value_cansleep(desc, value); if (status) return status; - gpiod_set_value_cansleep(desc, value); - return size; } static DEVICE_ATTR_PREALLOC(value, S_IWUSR | S_IRUGO, value_show, value_store); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index cd4fecbb41f2..fdafa0df1b43 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -266,6 +266,20 @@ struct gpio_device *gpiod_to_gpio_device(struct gpio_desc *desc) EXPORT_SYMBOL_GPL(gpiod_to_gpio_device); /** + * gpiod_is_equal() - Check if two GPIO descriptors refer to the same pin. + * @desc: Descriptor to compare. + * @other: The second descriptor to compare against. + * + * Returns: + * True if the descriptors refer to the same physical pin. False otherwise. + */ +bool gpiod_is_equal(struct gpio_desc *desc, struct gpio_desc *other) +{ + return desc == other; +} +EXPORT_SYMBOL_GPL(gpiod_is_equal); + +/** * gpio_device_get_base() - Get the base GPIO number allocated by this device * @gdev: GPIO device * @@ -342,6 +356,37 @@ static int gpiochip_find_base_unlocked(u16 ngpio) } } +/* + * This descriptor validation needs to be inserted verbatim into each + * function taking a descriptor, so we need to use a preprocessor + * macro to avoid endless duplication. If the desc is NULL it is an + * optional GPIO and calls should just bail out. + */ +static int validate_desc(const struct gpio_desc *desc, const char *func) +{ + if (!desc) + return 0; + + if (IS_ERR(desc)) { + pr_warn("%s: invalid GPIO (errorpointer: %pe)\n", func, desc); + return PTR_ERR(desc); + } + + return 1; +} + +#define VALIDATE_DESC(desc) do { \ + int __valid = validate_desc(desc, __func__); \ + if (__valid <= 0) \ + return __valid; \ + } while (0) + +#define VALIDATE_DESC_VOID(desc) do { \ + int __valid = validate_desc(desc, __func__); \ + if (__valid <= 0) \ + return; \ + } while (0) + static int gpiochip_get_direction(struct gpio_chip *gc, unsigned int offset) { int ret; @@ -376,11 +421,8 @@ int gpiod_get_direction(struct gpio_desc *desc) unsigned int offset; int ret; - /* - * We cannot use VALIDATE_DESC() as we must not return 0 for a NULL - * descriptor like we usually do. - */ - if (IS_ERR_OR_NULL(desc)) + ret = validate_desc(desc, __func__); + if (ret <= 0) return -EINVAL; CLASS(gpio_chip_guard, guard)(desc); @@ -742,6 +784,12 @@ EXPORT_SYMBOL_GPL(gpiochip_query_valid_mask); bool gpiochip_line_is_valid(const struct gpio_chip *gc, unsigned int offset) { + /* + * hog pins are requested before registering GPIO chip + */ + if (!gc->gpiodev) + return true; + /* No mask means all valid */ if (likely(!gc->gpiodev->valid_mask)) return true; @@ -874,14 +922,12 @@ static void machine_gpiochip_add(struct gpio_chip *gc) { struct gpiod_hog *hog; - mutex_lock(&gpio_machine_hogs_mutex); + guard(mutex)(&gpio_machine_hogs_mutex); list_for_each_entry(hog, &gpio_machine_hogs, list) { if (!strcmp(gc->label, hog->chip_label)) gpiochip_machine_hog(gc, hog); } - - mutex_unlock(&gpio_machine_hogs_mutex); } static void gpiochip_setup_devs(void) @@ -975,7 +1021,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, struct gpio_device *gdev; unsigned int desc_index; int base = 0; - int ret = 0; + int ret; /* Only allow one set() and one set_multiple(). */ if ((gc->set && gc->set_rv) || @@ -1000,11 +1046,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data, device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc)); - gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL); - if (gdev->id < 0) { - ret = gdev->id; + ret = ida_alloc(&gpio_ida, GFP_KERNEL); + if (ret < 0) goto err_free_gdev; - } + gdev->id = ret; ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id); if (ret) @@ -1507,9 +1552,8 @@ static int gpiochip_hierarchy_irq_domain_translate(struct irq_domain *d, unsigned int *type) { /* We support standard DT translation */ - if (is_of_node(fwspec->fwnode) && fwspec->param_count == 2) { - return irq_domain_translate_twocell(d, fwspec, hwirq, type); - } + if (is_of_node(fwspec->fwnode)) + return irq_domain_translate_twothreecell(d, fwspec, hwirq, type); /* This is for board files and others not using DT */ if (is_fwnode_irqchip(fwspec->fwnode)) { @@ -1811,11 +1855,26 @@ static void gpiochip_irq_unmap(struct irq_domain *d, unsigned int irq) irq_set_chip_data(irq, NULL); } +static int gpiochip_irq_select(struct irq_domain *d, struct irq_fwspec *fwspec, + enum irq_domain_bus_token bus_token) +{ + struct fwnode_handle *fwnode = fwspec->fwnode; + struct gpio_chip *gc = d->host_data; + unsigned int index = fwspec->param[0]; + + if (fwspec->param_count == 3 && is_of_node(fwnode)) + return of_gpiochip_instance_match(gc, index); + + /* Fallback for twocells */ + return (fwnode && (d->fwnode == fwnode) && (d->bus_token == bus_token)); +} + static const struct irq_domain_ops gpiochip_domain_ops = { .map = gpiochip_irq_map, .unmap = gpiochip_irq_unmap, + .select = gpiochip_irq_select, /* Virtually all GPIO irqchips are twocell:ed */ - .xlate = irq_domain_xlate_twocell, + .xlate = irq_domain_xlate_twothreecell, }; static struct irq_domain *gpiochip_simple_create_domain(struct gpio_chip *gc) @@ -1835,7 +1894,6 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset) { struct irq_domain *domain = gc->irq.domain; -#ifdef CONFIG_GPIOLIB_IRQCHIP /* * Avoid race condition with other code, which tries to lookup * an IRQ before the irqchip has been properly registered, @@ -1843,7 +1901,6 @@ static int gpiochip_to_irq(struct gpio_chip *gc, unsigned int offset) */ if (!gc->irq.initialized) return -EPROBE_DEFER; -#endif if (!gpiochip_irqchip_irq_valid(gc, offset)) return -ENXIO; @@ -2405,37 +2462,6 @@ out_clear_bit: return ret; } -/* - * This descriptor validation needs to be inserted verbatim into each - * function taking a descriptor, so we need to use a preprocessor - * macro to avoid endless duplication. If the desc is NULL it is an - * optional GPIO and calls should just bail out. - */ -static int validate_desc(const struct gpio_desc *desc, const char *func) -{ - if (!desc) - return 0; - - if (IS_ERR(desc)) { - pr_warn("%s: invalid GPIO (errorpointer)\n", func); - return PTR_ERR(desc); - } - - return 1; -} - -#define VALIDATE_DESC(desc) do { \ - int __valid = validate_desc(desc, __func__); \ - if (__valid <= 0) \ - return __valid; \ - } while (0) - -#define VALIDATE_DESC_VOID(desc) do { \ - int __valid = validate_desc(desc, __func__); \ - if (__valid <= 0) \ - return; \ - } while (0) - int gpiod_request(struct gpio_desc *desc, const char *label) { int ret = -EPROBE_DEFER; @@ -3045,7 +3071,7 @@ set_output_flag: */ int gpiod_enable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags) { - int ret = 0; + int ret; VALIDATE_DESC(desc); @@ -3078,7 +3104,7 @@ EXPORT_SYMBOL_GPL(gpiod_enable_hw_timestamp_ns); */ int gpiod_disable_hw_timestamp_ns(struct gpio_desc *desc, unsigned long flags) { - int ret = 0; + int ret; VALIDATE_DESC(desc); @@ -3593,6 +3619,9 @@ static int gpio_set_open_source_value_commit(struct gpio_desc *desc, bool value) static int gpiod_set_raw_value_commit(struct gpio_desc *desc, bool value) { + if (unlikely(!test_bit(FLAG_IS_OUT, &desc->flags))) + return -EPERM; + CLASS(gpio_chip_guard, guard)(desc); if (!guard.gc) return -ENODEV; @@ -3664,6 +3693,12 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, if (!can_sleep) WARN_ON(array_info->gdev->can_sleep); + for (i = 0; i < array_size; i++) { + if (unlikely(!test_bit(FLAG_IS_OUT, + &desc_array[i]->flags))) + return -EPERM; + } + guard(srcu)(&array_info->gdev->srcu); gc = srcu_dereference(array_info->gdev->chip, &array_info->gdev->srcu); @@ -3723,6 +3758,9 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep, int hwgpio = gpio_chip_hwgpio(desc); int value = test_bit(i, value_bitmap); + if (unlikely(!test_bit(FLAG_IS_OUT, &desc->flags))) + return -EPERM; + /* * Pins applicable for fast input but not for * fast output processing may have been already @@ -3944,13 +3982,10 @@ int gpiod_to_irq(const struct gpio_desc *desc) struct gpio_device *gdev; struct gpio_chip *gc; int offset; + int ret; - /* - * Cannot VALIDATE_DESC() here as gpiod_to_irq() consumer semantics - * requires this function to not return zero on an invalid descriptor - * but rather a negative error number. - */ - if (IS_ERR_OR_NULL(desc)) + ret = validate_desc(desc, __func__); + if (ret <= 0) return -EINVAL; gdev = desc->gdev; @@ -3962,13 +3997,12 @@ int gpiod_to_irq(const struct gpio_desc *desc) offset = gpio_chip_hwgpio(desc); if (gc->to_irq) { - int retirq = gc->to_irq(gc, offset); + ret = gc->to_irq(gc, offset); + if (ret) + return ret; /* Zero means NO_IRQ */ - if (!retirq) - return -ENXIO; - - return retirq; + return -ENXIO; } #ifdef CONFIG_GPIOLIB_IRQCHIP if (gc->irq.chip) { @@ -4323,12 +4357,10 @@ void gpiod_add_lookup_tables(struct gpiod_lookup_table **tables, size_t n) { unsigned int i; - mutex_lock(&gpio_lookup_lock); + guard(mutex)(&gpio_lookup_lock); for (i = 0; i < n; i++) list_add_tail(&tables[i]->list, &gpio_lookup_list); - - mutex_unlock(&gpio_lookup_lock); } /** @@ -4387,11 +4419,9 @@ void gpiod_remove_lookup_table(struct gpiod_lookup_table *table) if (!table) return; - mutex_lock(&gpio_lookup_lock); + guard(mutex)(&gpio_lookup_lock); list_del(&table->list); - - mutex_unlock(&gpio_lookup_lock); } EXPORT_SYMBOL_GPL(gpiod_remove_lookup_table); @@ -4403,7 +4433,7 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) { struct gpiod_hog *hog; - mutex_lock(&gpio_machine_hogs_mutex); + guard(mutex)(&gpio_machine_hogs_mutex); for (hog = &hogs[0]; hog->chip_label; hog++) { list_add_tail(&hog->list, &gpio_machine_hogs); @@ -4417,8 +4447,6 @@ void gpiod_add_hogs(struct gpiod_hog *hogs) if (gdev) gpiochip_machine_hog(gpio_device_get_chip(gdev), hog); } - - mutex_unlock(&gpio_machine_hogs_mutex); } EXPORT_SYMBOL_GPL(gpiod_add_hogs); @@ -4426,10 +4454,10 @@ void gpiod_remove_hogs(struct gpiod_hog *hogs) { struct gpiod_hog *hog; - mutex_lock(&gpio_machine_hogs_mutex); + guard(mutex)(&gpio_machine_hogs_mutex); + for (hog = &hogs[0]; hog->chip_label; hog++) list_del(&hog->list); - mutex_unlock(&gpio_machine_hogs_mutex); } EXPORT_SYMBOL_GPL(gpiod_remove_hogs); @@ -5108,8 +5136,7 @@ EXPORT_SYMBOL_GPL(gpiod_get_array_optional); */ void gpiod_put(struct gpio_desc *desc) { - if (desc) - gpiod_free(desc); + gpiod_free(desc); } EXPORT_SYMBOL_GPL(gpiod_put); |