gpio fixes for v7.0-rc7

- fix kerneldocs for gpio-timberdale and gpio-nomadik
 - clear the "requested" flag in error path in gpiod_request_commit()
 - call of_xlate() if provided when setting up shared GPIOs
 - handle pins shared by child firmware nodes of consumer devices
 - fix return value check in gpio-qixis-fpga
 - fix suspend on gpio-mxc
 - fix gpio-microchip DT bindings
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEkeUTLeW1Rh17omX8BZ0uy/82hMMFAmnPrRAACgkQBZ0uy/82
 hMMuug//dHmJlcs/5gHbFgno6CzB8v3Ngkd5UF1cmdI8ebiiRwqx4NqwlnQwBP3o
 Dr+iTG1z5M7o47rSOeYn50a79Xns3ROQkJoeeY8Uf0ZCpaSUs7htvFXCqLyCie5o
 o9or5nu87V6TcV/Ue7nnRvXCRk7GTZ5z3UjpMvafuRwCz53KTLXQXae/a9KqiRHi
 XbCmByrw9XpdGrr5kbrdkc3SwXILREKvvN8YxGIh37XeeQYcXIKb6FjlPN86Y/xC
 v+krLnFjZ+duguikvGi7HlP11/JpB2RtPDPs5QOkY/GTtkZXE6zyxDWagbYIKwrr
 3+QlGs+kbxzlrbVu95kw381PNcNCVZIuDLUm2aguPGRuazPhaeI+hG+XbYCeCmQI
 DVIzDwhcGzsj7wrrICQTkT2kWQg6FsMgzwLOc/8BeJnrohnNFQlcrI1T6EX7EIuM
 JLw6CkDUKN4v4ULg0B0+59D/yPSx8HgUrI4KbNgQyALDUiKW1K+bxGsS9PpWCCFT
 PFfHamkXO6zT+NRZV6n6GQNdpOi9EewNFXbFBlcTtBCJyZttJI7IMi0M0JndCshO
 V3yeOMU/LElucEMGuRMZJUJwrKPnkWe+afq0bekKrzag67cYpy0qxie4UDqVo9qB
 hREwBJXwtdZan4gZk4eerg+6qzVgFTF1el1fZaU8JsdByb0zkoQ=
 =pQpQ
 -----END PGP SIGNATURE-----

Merge tag 'gpio-fixes-for-v7.0-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux

Pull gpio fixes from Bartosz Golaszewski:

 - fix kerneldocs for gpio-timberdale and gpio-nomadik

 - clear the "requested" flag in error path in gpiod_request_commit()

 - call of_xlate() if provided when setting up shared GPIOs

 - handle pins shared by child firmware nodes of consumer devices

 - fix return value check in gpio-qixis-fpga

 - fix suspend on gpio-mxc

 - fix gpio-microchip DT bindings

* tag 'gpio-fixes-for-v7.0-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux:
  dt-bindings: gpio: fix microchip #interrupt-cells
  gpio: shared: shorten the critical section in gpiochip_setup_shared()
  gpio: mxc: map Both Edge pad wakeup to Rising Edge
  gpio: qixis-fpga: Fix error handling for devm_regmap_init_mmio()
  gpio: shared: handle pins shared by child nodes of devices
  gpio: shared: call gpio_chip::of_xlate() if set
  gpiolib: clear requested flag if line is invalid
  gpio: nomadik: repair some kernel-doc comments
  gpio: timberdale: repair kernel-doc comments
  gpio: Fix resource leaks on errors in gpiochip_add_data_with_key()
This commit is contained in:
Linus Torvalds 2026-04-03 09:33:38 -07:00
commit 116a3308e1
8 changed files with 135 additions and 104 deletions

View File

@ -37,7 +37,7 @@ properties:
const: 2
"#interrupt-cells":
const: 1
const: 2
ngpios:
description:
@ -86,7 +86,7 @@ examples:
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <1>;
#interrupt-cells = <2>;
interrupts = <53>, <53>, <53>, <53>,
<53>, <53>, <53>, <53>,
<53>, <53>, <53>, <53>,

View File

@ -584,12 +584,13 @@ static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable)
unsigned long config;
bool ret = false;
int i, type;
bool is_imx8qm = of_device_is_compatible(port->dev->of_node, "fsl,imx8qm-gpio");
static const u32 pad_type_map[] = {
IMX_SCU_WAKEUP_OFF, /* 0 */
IMX_SCU_WAKEUP_RISE_EDGE, /* IRQ_TYPE_EDGE_RISING */
IMX_SCU_WAKEUP_FALL_EDGE, /* IRQ_TYPE_EDGE_FALLING */
IMX_SCU_WAKEUP_FALL_EDGE, /* IRQ_TYPE_EDGE_BOTH */
IMX_SCU_WAKEUP_RISE_EDGE, /* IRQ_TYPE_EDGE_BOTH */
IMX_SCU_WAKEUP_HIGH_LVL, /* IRQ_TYPE_LEVEL_HIGH */
IMX_SCU_WAKEUP_OFF, /* 5 */
IMX_SCU_WAKEUP_OFF, /* 6 */
@ -604,6 +605,13 @@ static bool mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable)
config = pad_type_map[type];
else
config = IMX_SCU_WAKEUP_OFF;
if (is_imx8qm && config == IMX_SCU_WAKEUP_FALL_EDGE) {
dev_warn_once(port->dev,
"No falling-edge support for wakeup on i.MX8QM\n");
config = IMX_SCU_WAKEUP_OFF;
}
ret |= mxc_gpio_generic_config(port, i, config);
}
}

View File

@ -60,8 +60,8 @@ static int qixis_cpld_gpio_probe(struct platform_device *pdev)
return PTR_ERR(reg);
regmap = devm_regmap_init_mmio(&pdev->dev, reg, &regmap_config_8r_8v);
if (!regmap)
return -ENODEV;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
/* In this case, the offset of our register is 0 inside the
* regmap area that we just created.

View File

@ -443,8 +443,8 @@ static bool gpio_shared_dev_is_reset_gpio(struct device *consumer,
}
#endif /* CONFIG_RESET_GPIO */
int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id,
unsigned long lflags)
int gpio_shared_add_proxy_lookup(struct device *consumer, struct fwnode_handle *fwnode,
const char *con_id, unsigned long lflags)
{
const char *dev_id = dev_name(consumer);
struct gpiod_lookup_table *lookup;
@ -458,7 +458,7 @@ int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id,
if (!ref->fwnode && device_is_compatible(consumer, "reset-gpio")) {
if (!gpio_shared_dev_is_reset_gpio(consumer, entry, ref))
continue;
} else if (!device_match_fwnode(consumer, ref->fwnode)) {
} else if (fwnode != ref->fwnode) {
continue;
}
@ -506,8 +506,9 @@ static void gpio_shared_remove_adev(struct auxiliary_device *adev)
auxiliary_device_uninit(adev);
}
int gpio_device_setup_shared(struct gpio_device *gdev)
int gpiochip_setup_shared(struct gpio_chip *gc)
{
struct gpio_device *gdev = gc->gpiodev;
struct gpio_shared_entry *entry;
struct gpio_shared_ref *ref;
struct gpio_desc *desc;
@ -538,20 +539,42 @@ int gpio_device_setup_shared(struct gpio_device *gdev)
if (list_count_nodes(&entry->refs) <= 1)
continue;
desc = &gdev->descs[entry->offset];
scoped_guard(mutex, &entry->lock) {
#if IS_ENABLED(CONFIG_OF)
if (is_of_node(entry->fwnode) && gc->of_xlate) {
/*
* This is the earliest that we can tranlate the
* devicetree offset to the chip offset.
*/
struct of_phandle_args gpiospec = { };
__set_bit(GPIOD_FLAG_SHARED, &desc->flags);
/*
* Shared GPIOs are not requested via the normal path. Make
* them inaccessible to anyone even before we register the
* chip.
*/
ret = gpiod_request_commit(desc, "shared");
if (ret)
return ret;
gpiospec.np = to_of_node(entry->fwnode);
gpiospec.args_count = 2;
gpiospec.args[0] = entry->offset;
pr_debug("GPIO %u owned by %s is shared by multiple consumers\n",
entry->offset, gpio_device_get_label(gdev));
ret = gc->of_xlate(gc, &gpiospec, NULL);
if (ret < 0)
return ret;
entry->offset = ret;
}
#endif /* CONFIG_OF */
desc = &gdev->descs[entry->offset];
__set_bit(GPIOD_FLAG_SHARED, &desc->flags);
/*
* Shared GPIOs are not requested via the normal path. Make
* them inaccessible to anyone even before we register the
* chip.
*/
ret = gpiod_request_commit(desc, "shared");
if (ret)
return ret;
pr_debug("GPIO %u owned by %s is shared by multiple consumers\n",
entry->offset, gpio_device_get_label(gdev));
}
list_for_each_entry(ref, &entry->refs, list) {
pr_debug("Setting up a shared GPIO entry for %s (con_id: '%s')\n",
@ -575,6 +598,8 @@ void gpio_device_teardown_shared(struct gpio_device *gdev)
struct gpio_shared_ref *ref;
list_for_each_entry(entry, &gpio_shared_list, list) {
guard(mutex)(&entry->lock);
if (!device_match_fwnode(&gdev->dev, entry->fwnode))
continue;

View File

@ -11,17 +11,19 @@
struct gpio_device;
struct gpio_desc;
struct device;
struct fwnode_handle;
#if IS_ENABLED(CONFIG_GPIO_SHARED)
int gpio_device_setup_shared(struct gpio_device *gdev);
int gpiochip_setup_shared(struct gpio_chip *gc);
void gpio_device_teardown_shared(struct gpio_device *gdev);
int gpio_shared_add_proxy_lookup(struct device *consumer, const char *con_id,
unsigned long lflags);
int gpio_shared_add_proxy_lookup(struct device *consumer,
struct fwnode_handle *fwnode,
const char *con_id, unsigned long lflags);
#else
static inline int gpio_device_setup_shared(struct gpio_device *gdev)
static inline int gpiochip_setup_shared(struct gpio_chip *gc)
{
return 0;
}
@ -29,6 +31,7 @@ static inline int gpio_device_setup_shared(struct gpio_device *gdev)
static inline void gpio_device_teardown_shared(struct gpio_device *gdev) { }
static inline int gpio_shared_add_proxy_lookup(struct device *consumer,
struct fwnode_handle *fwnode,
const char *con_id,
unsigned long lflags)
{

View File

@ -892,13 +892,15 @@ static const struct device_type gpio_dev_type = {
#define gcdev_unregister(gdev) device_del(&(gdev)->dev)
#endif
/*
* An initial reference count has been held in gpiochip_add_data_with_key().
* The caller should drop the reference via gpio_device_put() on errors.
*/
static int gpiochip_setup_dev(struct gpio_device *gdev)
{
struct fwnode_handle *fwnode = dev_fwnode(&gdev->dev);
int ret;
device_initialize(&gdev->dev);
/*
* If fwnode doesn't belong to another device, it's safe to clear its
* initialized flag.
@ -964,9 +966,11 @@ static void gpiochip_setup_devs(void)
list_for_each_entry_srcu(gdev, &gpio_devices, list,
srcu_read_lock_held(&gpio_devices_srcu)) {
ret = gpiochip_setup_dev(gdev);
if (ret)
if (ret) {
gpio_device_put(gdev);
dev_err(&gdev->dev,
"Failed to initialize gpio device (%d)\n", ret);
}
}
}
@ -1047,33 +1051,65 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
int base = 0;
int ret;
/*
* First: allocate and populate the internal stat container, and
* set up the struct device.
*/
gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
if (!gdev)
return -ENOMEM;
gdev->dev.type = &gpio_dev_type;
gdev->dev.bus = &gpio_bus_type;
gdev->dev.parent = gc->parent;
rcu_assign_pointer(gdev->chip, gc);
gc->gpiodev = gdev;
gpiochip_set_data(gc, data);
device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc));
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);
ret = init_srcu_struct(&gdev->srcu);
if (ret)
goto err_free_ida;
rcu_assign_pointer(gdev->chip, gc);
ret = init_srcu_struct(&gdev->desc_srcu);
if (ret)
goto err_cleanup_gdev_srcu;
ret = dev_set_name(&gdev->dev, GPIOCHIP_NAME "%d", gdev->id);
if (ret)
goto err_cleanup_desc_srcu;
device_initialize(&gdev->dev);
/*
* After this point any allocated resources to `gdev` will be
* free():ed by gpiodev_release(). If you add new resources
* then make sure they get free():ed there.
*/
gdev->dev.type = &gpio_dev_type;
gdev->dev.bus = &gpio_bus_type;
gdev->dev.parent = gc->parent;
device_set_node(&gdev->dev, gpiochip_choose_fwnode(gc));
ret = gpiochip_get_ngpios(gc, &gdev->dev);
if (ret)
goto err_put_device;
gdev->ngpio = gc->ngpio;
gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL);
if (!gdev->descs) {
ret = -ENOMEM;
goto err_put_device;
}
gdev->label = kstrdup_const(gc->label ?: "unknown", GFP_KERNEL);
if (!gdev->label) {
ret = -ENOMEM;
goto err_put_device;
}
gdev->can_sleep = gc->can_sleep;
rwlock_init(&gdev->line_state_lock);
RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);
#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&gdev->pin_ranges);
#endif
if (gc->parent && gc->parent->driver)
gdev->owner = gc->parent->driver->owner;
else if (gc->owner)
@ -1082,37 +1118,6 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
else
gdev->owner = THIS_MODULE;
ret = gpiochip_get_ngpios(gc, &gdev->dev);
if (ret)
goto err_free_dev_name;
gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL);
if (!gdev->descs) {
ret = -ENOMEM;
goto err_free_dev_name;
}
gdev->label = kstrdup_const(gc->label ?: "unknown", GFP_KERNEL);
if (!gdev->label) {
ret = -ENOMEM;
goto err_free_descs;
}
gdev->ngpio = gc->ngpio;
gdev->can_sleep = gc->can_sleep;
rwlock_init(&gdev->line_state_lock);
RAW_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);
ret = init_srcu_struct(&gdev->srcu);
if (ret)
goto err_free_label;
ret = init_srcu_struct(&gdev->desc_srcu);
if (ret)
goto err_cleanup_gdev_srcu;
scoped_guard(mutex, &gpio_devices_lock) {
/*
* TODO: this allocates a Linux GPIO number base in the global
@ -1127,7 +1132,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (base < 0) {
ret = base;
base = 0;
goto err_cleanup_desc_srcu;
goto err_put_device;
}
/*
@ -1147,14 +1152,10 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
ret = gpiodev_add_to_list_unlocked(gdev);
if (ret) {
gpiochip_err(gc, "GPIO integer space overlap, cannot add chip\n");
goto err_cleanup_desc_srcu;
goto err_put_device;
}
}
#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&gdev->pin_ranges);
#endif
if (gc->names)
gpiochip_set_desc_names(gc);
@ -1210,7 +1211,7 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
if (ret)
goto err_remove_irqchip_mask;
ret = gpio_device_setup_shared(gdev);
ret = gpiochip_setup_shared(gc);
if (ret)
goto err_remove_irqchip;
@ -1248,25 +1249,19 @@ int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
scoped_guard(mutex, &gpio_devices_lock)
list_del_rcu(&gdev->list);
synchronize_srcu(&gpio_devices_srcu);
if (gdev->dev.release) {
/* release() has been registered by gpiochip_setup_dev() */
gpio_device_put(gdev);
goto err_print_message;
}
err_put_device:
gpio_device_put(gdev);
goto err_print_message;
err_cleanup_desc_srcu:
cleanup_srcu_struct(&gdev->desc_srcu);
err_cleanup_gdev_srcu:
cleanup_srcu_struct(&gdev->srcu);
err_free_label:
kfree_const(gdev->label);
err_free_descs:
kfree(gdev->descs);
err_free_dev_name:
kfree(dev_name(&gdev->dev));
err_free_ida:
ida_free(&gpio_ida, gdev->id);
err_free_gdev:
kfree(gdev);
err_print_message:
/* failures here can mean systems won't boot... */
if (ret != -EPROBE_DEFER) {
@ -2465,8 +2460,10 @@ int gpiod_request_commit(struct gpio_desc *desc, const char *label)
return -EBUSY;
offset = gpiod_hwgpio(desc);
if (!gpiochip_line_is_valid(guard.gc, offset))
return -EINVAL;
if (!gpiochip_line_is_valid(guard.gc, offset)) {
ret = -EINVAL;
goto out_clear_bit;
}
/* NOTE: gpio_request() can be called in early boot,
* before IRQs are enabled, for non-sleeping (SOC) GPIOs.
@ -4717,8 +4714,8 @@ struct gpio_desc *gpiod_find_and_request(struct device *consumer,
* lookup table for the proxy device as previously
* we only knew the consumer's fwnode.
*/
ret = gpio_shared_add_proxy_lookup(consumer, con_id,
lookupflags);
ret = gpio_shared_add_proxy_lookup(consumer, fwnode,
con_id, lookupflags);
if (ret)
return ERR_PTR(ret);

View File

@ -114,8 +114,7 @@ struct nmk_gpio_chip {
}
/**
* enum prcm_gpiocr_reg_index
* Used to reference an PRCM GPIOCR register address.
* enum prcm_gpiocr_reg_index - Used to reference a PRCM GPIOCR register address.
*/
enum prcm_gpiocr_reg_index {
PRCM_IDX_GPIOCR1,
@ -123,8 +122,7 @@ enum prcm_gpiocr_reg_index {
PRCM_IDX_GPIOCR3
};
/**
* enum prcm_gpiocr_altcx_index
* Used to reference an Other alternate-C function.
* enum prcm_gpiocr_altcx_index - Used to reference an Other alternate-C function.
*/
enum prcm_gpiocr_altcx_index {
PRCM_IDX_GPIOCR_ALTC1,
@ -135,7 +133,7 @@ enum prcm_gpiocr_altcx_index {
};
/**
* struct prcm_gpio_altcx - Other alternate-C function
* struct prcm_gpiocr_altcx - Other alternate-C function
* @used: other alternate-C function availability
* @reg_index: PRCM GPIOCR register index used to control the function
* @control_bit: PRCM GPIOCR bit used to control the function
@ -147,7 +145,7 @@ struct prcm_gpiocr_altcx {
} __packed;
/**
* struct prcm_gpio_altcx_pin_desc - Other alternate-C pin
* struct prcm_gpiocr_altcx_pin_desc - Other alternate-C pin
* @pin: The pin number
* @altcx: array of other alternate-C[1-4] functions
*/
@ -193,7 +191,7 @@ struct nmk_pingroup {
* numbering.
* @npins: The number of entries in @pins.
* @functions: The functions supported on this SoC.
* @nfunction: The number of entries in @functions.
* @nfunctions: The number of entries in @functions.
* @groups: An array describing all pin groups the pin SoC supports.
* @ngroups: The number of entries in @groups.
* @altcx_pins: The pins that support Other alternate-C function on this SoC

View File

@ -9,10 +9,10 @@
/**
* struct timbgpio_platform_data - Platform data of the Timberdale GPIO driver
* @gpio_base The number of the first GPIO pin, set to -1 for
* @gpio_base: The number of the first GPIO pin, set to -1 for
* dynamic number allocation.
* @nr_pins Number of pins that is supported by the hardware (1-32)
* @irq_base If IRQ is supported by the hardware, this is the base
* @nr_pins: Number of pins that is supported by the hardware (1-32)
* @irq_base: If IRQ is supported by the hardware, this is the base
* number of IRQ:s. One IRQ per pin will be used. Set to
* -1 if IRQ:s is not supported.
*/