gpio fixes for v7.4-rc5

- fix a regression introduced by moving GPIO hog handling into GPIOLIB
   core where of_node_name was used if line name property was missing on
   DT systems
 - fix kernel stack leak to user-space in error path in GPIO character
   device code
 - fix runtime PM leaks in gpio-xilinx and gpio-arizona
 - fix several register programming bugs in gpio-tps65219
 - fix interrupt storm on resume in gpio-mvebu
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEkeUTLeW1Rh17omX8BZ0uy/82hMMFAmq2KYwACgkQBZ0uy/82
 hMOkFhAAjIaH6nUhhUBykRyIbBVAbxYFWrps/qLw9Wlg1wDteUwwMWpdHWivsi2M
 Ch0zCR1/op812cu47dTsnNteHuK9oGoL0IHXGweylNJVU7IXTiUx6sUu+kLlT2wK
 /yIH6Zk78rULodV/80S9aNbFNNHOu7Pu/AEAPPJjGIKK6UVxT7o2o04jG1xORVbr
 X11hLjzKe1RpVKo3fhBFglzW1T4YVpulorsgkMNabTG4hbf5pArtkaQGPHVvH3Hk
 LNpTFJ3Jmoczo4UgsKAaUso2LoC19hoIUMRBVoOwscTLlPoIE6MTH0bLj+U9npId
 TUhGpVYIvfy4B6V2+FU+9YnWTkDE7efmnmm0l+GnNp7FmDTxTO0aVJoouPNEeUgu
 Zu1CJxaKYTWuTm5mdk2Tgw5G+3YtzcGS6iW3dCFvshyYSdHHkvPQ6ys2blJJyeEd
 2MRoZQZdSzMDmCfw5Y+RxF1jp1BRjIID2NH1p/cxUokFvMDgFJU+MFxdOo6ySnC/
 +1EEeA7Gak4StJUDa2Y4+uI0PMokWAEXs3ixlQUmgIpgGVtBIzI17psj4RDumdGD
 6HIG/31Pedg3LtUkkzC8P6vfdREzzquWUPBr9F7dApur4/MMJZyeu3pUwFtx0AKs
 +T5qgUwwsoKxLmg2EAdg5pThzLVgqgtrSYuw8esqfG8tNRzoRgc=
 =Mp0f
 -----END PGP SIGNATURE-----

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

Pull gpio fixes from Bartosz Golaszewski:

 - fix a regression introduced by moving GPIO hog handling into GPIOLIB
   core where of_node_name was used if line name property was missing on
   DT systems

 - fix kernel stack leak to user-space in error path in GPIO character
   device code

 - fix runtime PM leaks in gpio-xilinx and gpio-arizona

 - fix several register programming bugs in gpio-tps65219

 - fix interrupt storm on resume in gpio-mvebu

* tag 'gpio-fixes-for-v7.3-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux:
  gpio: tps65219: Fix TPS65214 GPIO direction programming
  gpio: tps65219: Use the variant-specific direction callback
  gpio: tps65219: Fix GPIO input value reads
  gpio: zynq: fix runtime PM leak on request error path
  gpio: cdev: fix kernel stack leak to user-space in error path
  gpiolib: use of_node_name if line-name is missing
  gpio: mvebu: keep resume masks within the irqchip cache
  gpio: arizona: Fix runtime PM leak in arizona_gpio_direction_out()
This commit is contained in:
Linus Torvalds 2026-09-25 09:35:07 -07:00
commit 80e466f0c8
6 changed files with 73 additions and 29 deletions

View File

@ -115,8 +115,12 @@ static int arizona_gpio_direction_out(struct gpio_chip *chip,
if (value)
value = ARIZONA_GPN_LVL;
return regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
ARIZONA_GPN_DIR | ARIZONA_GPN_LVL, value);
ret = regmap_update_bits(arizona->regmap, ARIZONA_GPIO1_CTRL + offset,
ARIZONA_GPN_DIR | ARIZONA_GPN_LVL, value);
if (ret < 0 && (val & ARIZONA_GPN_DIR) && persistent)
pm_runtime_put_autosuspend(chip->parent);
return ret;
}
static int arizona_gpio_set(struct gpio_chip *chip, unsigned int offset,

View File

@ -1034,6 +1034,8 @@ static int mvebu_gpio_suspend(struct platform_device *pdev, pm_message_t state)
static int mvebu_gpio_resume(struct platform_device *pdev)
{
struct mvebu_gpio_chip *mvchip = platform_get_drvdata(pdev);
u32 edge_cache = ~0U, level_cache = ~0U;
unsigned long flags;
int i;
regmap_write(mvchip->regs, GPIO_OUT_OFF + mvchip->offset,
@ -1045,32 +1047,51 @@ static int mvebu_gpio_resume(struct platform_device *pdev)
regmap_write(mvchip->regs, GPIO_IN_POL_OFF + mvchip->offset,
mvchip->in_pol_reg);
/*
* genirq skips mask_irq() for a line it already considers masked, so
* unmasking one behind its back leaves an asserted level line that
* nobody masks. Restore only bits the irqchip cache still has set.
*
* Snapshot the caches under the raw spinlock, but release it before
* the regmap writes below: regmap_write() takes a sleepable lock on
* PREEMPT_RT.
*/
if (mvchip->domain) {
struct irq_chip_generic *gc;
gc = irq_get_domain_generic_chip(mvchip->domain, 0);
raw_spin_lock_irqsave(&gc->lock, flags);
level_cache = gc->chip_types[0].mask_cache_priv;
edge_cache = gc->chip_types[1].mask_cache_priv;
raw_spin_unlock_irqrestore(&gc->lock, flags);
}
switch (mvchip->soc_variant) {
case MVEBU_GPIO_SOC_VARIANT_ORION:
case MVEBU_GPIO_SOC_VARIANT_A8K:
regmap_write(mvchip->regs, GPIO_EDGE_MASK_OFF + mvchip->offset,
mvchip->edge_mask_regs[0]);
mvchip->edge_mask_regs[0] & edge_cache);
regmap_write(mvchip->regs, GPIO_LEVEL_MASK_OFF + mvchip->offset,
mvchip->level_mask_regs[0]);
mvchip->level_mask_regs[0] & level_cache);
break;
case MVEBU_GPIO_SOC_VARIANT_MV78200:
for (i = 0; i < 2; i++) {
regmap_write(mvchip->regs,
GPIO_EDGE_MASK_MV78200_OFF(i),
mvchip->edge_mask_regs[i]);
mvchip->edge_mask_regs[i] & edge_cache);
regmap_write(mvchip->regs,
GPIO_LEVEL_MASK_MV78200_OFF(i),
mvchip->level_mask_regs[i]);
mvchip->level_mask_regs[i] & level_cache);
}
break;
case MVEBU_GPIO_SOC_VARIANT_ARMADAXP:
for (i = 0; i < 4; i++) {
regmap_write(mvchip->regs,
GPIO_EDGE_MASK_ARMADAXP_OFF(i),
mvchip->edge_mask_regs[i]);
mvchip->edge_mask_regs[i] & edge_cache);
regmap_write(mvchip->regs,
GPIO_LEVEL_MASK_ARMADAXP_OFF(i),
mvchip->level_mask_regs[i]);
mvchip->level_mask_regs[i] & level_cache);
}
break;
default:

View File

@ -79,7 +79,7 @@ static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset)
if (ret)
return ret;
ret = !!(val & BIT(TPS65219_MFP_GPIO_STATUS_MASK));
ret = !!(val & TPS65219_MFP_GPIO_STATUS_MASK);
dev_warn(dev, "GPIO%d = %d, MULTI_DEVICE_ENABLE, not a standard GPIO\n", offset, ret);
/*
@ -87,7 +87,7 @@ static int tps65219_gpio_get(struct gpio_chip *gc, unsigned int offset)
* status bit.
*/
if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
if (gc->get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
return -ENOTSUPP;
return ret;
@ -158,8 +158,10 @@ static int tps65214_gpio_change_direction(struct gpio_chip *gc, unsigned int off
if (ret)
dev_err(dev, "GPIO%d configured as VSEL, not GPIO\n", offset);
val = direction == GPIO_LINE_DIRECTION_OUT ?
TPS65214_GPIO0_DIR_MASK : 0;
ret = regmap_update_bits(gpio->tps->regmap, TPS65219_REG_GENERAL_CONFIG,
TPS65214_GPIO0_DIR_MASK, direction);
TPS65214_GPIO0_DIR_MASK, val);
if (ret)
dev_err(dev, "Fail to change direction to %u for GPIO%d.\n", direction, offset);
@ -176,7 +178,7 @@ static int tps65219_gpio_direction_input(struct gpio_chip *gc, unsigned int offs
return -ENOTSUPP;
}
if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN)
if (gc->get_direction(gc, offset) == GPIO_LINE_DIRECTION_IN)
return 0;
return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_IN);
@ -190,7 +192,7 @@ static int tps65219_gpio_direction_output(struct gpio_chip *gc, unsigned int off
if (offset != TPS6521X_GPIO0_IDX)
return 0;
if (tps65219_gpio_get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
if (gc->get_direction(gc, offset) == GPIO_LINE_DIRECTION_OUT)
return 0;
return gpio->change_dir(gc, offset, GPIO_LINE_DIRECTION_OUT);

View File

@ -798,15 +798,7 @@ static int zynq_gpio_runtime_resume(struct device *dev)
static int zynq_gpio_request(struct gpio_chip *chip, unsigned int offset)
{
int ret;
ret = pm_runtime_get_sync(chip->parent);
/*
* If the device is already active pm_runtime_get() will return 1 on
* success, but gpio_request still needs to return 0.
*/
return ret < 0 ? ret : 0;
return pm_runtime_resume_and_get(chip->parent);
}
static void zynq_gpio_free(struct gpio_chip *chip, unsigned int offset)

View File

@ -2172,18 +2172,19 @@ static void gpio_v2_line_info_changed_to_v1(
#endif /* CONFIG_GPIO_CDEV_V1 */
static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
struct gpio_v2_line_info *info, bool atomic)
static int gpio_desc_to_lineinfo(struct gpio_desc *desc,
struct gpio_v2_line_info *info, bool atomic)
{
u32 debounce_period_us;
unsigned long dflags;
const char *label;
memset(info, 0, sizeof(*info));
CLASS(gpio_chip_guard, guard)(desc);
if (!guard.gc)
return;
return -ENODEV;
memset(info, 0, sizeof(*info));
info->offset = gpiod_hwgpio(desc);
if (desc->name)
@ -2258,6 +2259,8 @@ static void gpio_desc_to_lineinfo(struct gpio_desc *desc,
debounce_period_us;
info->num_attrs++;
}
return 0;
}
struct gpio_chardev_data {
@ -2309,6 +2312,7 @@ static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip,
struct gpio_desc *desc;
struct gpioline_info lineinfo;
struct gpio_v2_line_info lineinfo_v2;
int ret;
if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
return -EFAULT;
@ -2326,7 +2330,10 @@ static int lineinfo_get_v1(struct gpio_chardev_data *cdev, void __user *ip,
return -EBUSY;
}
gpio_desc_to_lineinfo(desc, &lineinfo_v2, false);
ret = gpio_desc_to_lineinfo(desc, &lineinfo_v2, false);
if (ret)
return ret;
gpio_v2_line_info_to_v1(&lineinfo_v2, &lineinfo);
if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
@ -2344,6 +2351,7 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
{
struct gpio_desc *desc;
struct gpio_v2_line_info lineinfo;
int ret;
if (copy_from_user(&lineinfo, ip, sizeof(lineinfo)))
return -EFAULT;
@ -2363,7 +2371,10 @@ static int lineinfo_get(struct gpio_chardev_data *cdev, void __user *ip,
if (test_and_set_bit(lineinfo.offset, cdev->watched_lines))
return -EBUSY;
}
gpio_desc_to_lineinfo(desc, &lineinfo, false);
ret = gpio_desc_to_lineinfo(desc, &lineinfo, false);
if (ret)
return ret;
if (copy_to_user(ip, &lineinfo, sizeof(lineinfo))) {
if (watch)
@ -2489,6 +2500,7 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
struct lineinfo_changed_ctx *ctx;
struct gpio_desc *desc = data;
struct file *fp;
int ret;
if (!test_bit(gpiod_hwgpio(desc), cdev->watched_lines))
return NOTIFY_DONE;
@ -2519,7 +2531,13 @@ static int lineinfo_changed_notify(struct notifier_block *nb,
ctx->chg.event_type = action;
ctx->chg.timestamp_ns = ktime_get_ns();
gpio_desc_to_lineinfo(desc, &ctx->chg.info, true);
ret = gpio_desc_to_lineinfo(desc, &ctx->chg.info, true);
if (ret) {
fput(fp);
return NOTIFY_DONE;
}
/* Keep the GPIO device alive until we emit the event. */
ctx->gdev = gpio_device_get(desc->gdev);
ctx->cdev = cdev;

View File

@ -1029,6 +1029,13 @@ int gpiochip_add_hog(struct gpio_chip *gc, struct fwnode_handle *fwnode)
ret = of_gpiochip_get_lflags(gc, &gpiospec, &lflags);
if (ret)
return ret;
/*
* If no line-name property is present, fall back to the OF
* node name as in the previous implementation.
*/
if (!name)
name = to_of_node(fwnode)->name;
} else {
/*
* GPIO_ACTIVE_LOW is currently the only lookup flag