Merge tag 'gpio-updates-for-v6.6' of git://git.kernel.org/pub/scm/linux/kernel/git...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 29 Aug 2023 17:21:56 +0000 (10:21 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 29 Aug 2023 17:21:56 +0000 (10:21 -0700)
Pull gpio updates from Bartosz Golaszewski:
 "We have a lot of code refactoring using common helpers and ended up
  removing more lines then we're adding this release cycle.

  Nothing really stands out, just small updates all over the place.

  Core GPIOLIB updates:
   - wake-up poll() in user-space on device unbind
   - improve fwnode usage
   - interrupt domain handling improvements
   - correctly handle the ngpios property in gpio-mmio

  Driver cleanups:
   - remove unneeded calls to platform_set_drvdata() all around the
     place
   - remove unneeded of_match_ptr() expansions whenever a driver depends
     on CONFIG_OF
   - remove redundant calls to dev_err_probe() from gpio-omap and
     gpio-davinci

  Driver improvements:
   - use autopointers and guards from cleanup.h in gpio-sim
   - shrink code in gpio-sim using some common helpers
   - convert the idio family of drivers to using gpio-regmap
   - convert gpio-ws16c48 to using gpio-regmap
   - use devres to simplify code in gpio-pisosr and gpio-mxc
   - update gpio-sifive: support IRQ wake, improve interrupt handling,
     allow building as module
   - make gpio-ge and gpio-bcm-kona OF-independent (plus some minor
     tweaks)
   - add support for new models in gpio-pca953x and gpio-ds4520
   - add runtime PM support to gpio-mxc
   - fix a build warning in gpio-mxs
   - add support for adding pin ranges to gpio-mlxbf3
   - add counter/timer support to gpio-104-dio-48e
   - switch to dynamic GPIO base allocation in gpio-vf610
   - minor oneliners here and there

  Device-tree bindings updates:
   - enable the gpio-line-names property in snps,dw-apb and STMPE GPIO
   - document new models in fsl-imx-gpio, ds4520 and pca95xx
   - convert the bindings for brcm,kona-gpio to YAML"

* tag 'gpio-updates-for-v6.6' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (94 commits)
  gpio: pca953x: add support for TCA9538
  dt-bindings: gpio: pca95xx: document new tca9538 chip
  gpio: pca953x: Use i2c_get_match_data()
  gpio: mlxbf3: use capital "OR" for multiple licenses in SPDX
  gpio: pcf857x: Extend match data support for OF tables
  gpio: vf610: switch to dynamic allocat GPIO base
  gpiolib: provide and use gpiod_line_state_notify()
  gpio: cdev: wake up lineevent poll() on device unbind
  gpio: cdev: wake up linereq poll() on device unbind
  gpio: cdev: wake up chardev poll() on device unbind
  gpiolib: add a second blocking notifier to struct gpio_device
  gpio: cdev: open-code to_gpio_chardev_data()
  gpiolib: rename the gpio_device notifier
  gpio: mlxbf3: Support add_pin_ranges()
  gpio: mxc: Use helper function devm_clk_get_optional_enabled()
  gpio: pca9570: fix kerneldoc
  gpio: sim: simplify code with cleanup helpers
  gpio: sim: replace memmove() + strstrip() with skip_spaces() + strim()
  gpio: sim: simplify gpio_sim_device_config_live_store()
  gpio: mxc: release the parent IRQ in runtime suspend
  ...

1  2 
MAINTAINERS
drivers/gpio/gpio-sim.c
drivers/gpio/gpio-ws16c48.c
drivers/gpio/gpiolib-of.c
drivers/gpio/gpiolib.c

diff --combined MAINTAINERS
@@@ -1865,11 -1865,9 +1865,11 @@@ M:    Martin Povišer <povik+lin@cutebit.o
  L:    asahi@lists.linux.dev
  L:    alsa-devel@alsa-project.org (moderated for non-subscribers)
  S:    Maintained
 +F:    Documentation/devicetree/bindings/sound/adi,ssm3515.yaml
  F:    Documentation/devicetree/bindings/sound/apple,*
  F:    sound/soc/apple/*
  F:    sound/soc/codecs/cs42l83-i2c.c
 +F:    sound/soc/codecs/ssm3515.c
  
  ARM/APPLE MACHINE SUPPORT
  M:    Hector Martin <marcan@marcan.st>
@@@ -2339,7 -2337,7 +2339,7 @@@ F:      drivers/phy/mediatek
  ARM/MICROCHIP (ARM64) SoC support
  M:    Conor Dooley <conor@kernel.org>
  M:    Nicolas Ferre <nicolas.ferre@microchip.com>
 -M:    Claudiu Beznea <claudiu.beznea@microchip.com>
 +M:    Claudiu Beznea <claudiu.beznea@tuxon.dev>
  L:    linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
  S:    Supported
  T:    git https://git.kernel.org/pub/scm/linux/kernel/git/at91/linux.git
@@@ -2348,7 -2346,7 +2348,7 @@@ F:      arch/arm64/boot/dts/microchip
  ARM/Microchip (AT91) SoC support
  M:    Nicolas Ferre <nicolas.ferre@microchip.com>
  M:    Alexandre Belloni <alexandre.belloni@bootlin.com>
 -M:    Claudiu Beznea <claudiu.beznea@microchip.com>
 +M:    Claudiu Beznea <claudiu.beznea@tuxon.dev>
  L:    linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
  S:    Supported
  W:    http://www.linux4sam.org
@@@ -3250,7 -3248,7 +3250,7 @@@ F:      include/uapi/linux/atm
  
  ATMEL MACB ETHERNET DRIVER
  M:    Nicolas Ferre <nicolas.ferre@microchip.com>
 -M:    Claudiu Beznea <claudiu.beznea@microchip.com>
 +M:    Claudiu Beznea <claudiu.beznea@tuxon.dev>
  S:    Supported
  F:    drivers/net/ethernet/cadence/
  
@@@ -3262,8 -3260,9 +3262,8 @@@ F:      Documentation/devicetree/bindings/in
  F:    drivers/input/touchscreen/atmel_mxt_ts.c
  
  ATMEL WIRELESS DRIVER
 -M:    Simon Kelley <simon@thekelleys.org.uk>
  L:    linux-wireless@vger.kernel.org
 -S:    Maintained
 +S:    Orphan
  W:    http://www.thekelleys.org.uk/atmel
  W:    http://atmelwlandriver.sourceforge.net/
  F:    drivers/net/wireless/atmel/atmel*
@@@ -3393,7 -3392,7 +3393,7 @@@ F:      drivers/media/radio/radio-aztech
  B43 WIRELESS DRIVER
  L:    linux-wireless@vger.kernel.org
  L:    b43-dev@lists.infradead.org
 -S:    Odd Fixes
 +S:    Orphan
  W:    https://wireless.wiki.kernel.org/en/users/Drivers/b43
  F:    drivers/net/wireless/broadcom/b43/
  
@@@ -4122,13 -4121,6 +4122,13 @@@ F:    Documentation/devicetree/bindings/sp
  F:    drivers/spi/spi-bcm63xx-hsspi.c
  F:    drivers/spi/spi-bcmbca-hsspi.c
  
 +BROADCOM BCM6348/BCM6358 SPI controller DRIVER
 +M:    Jonas Gorski <jonas.gorski@gmail.com>
 +L:    linux-spi@vger.kernel.org
 +S:    Odd Fixes
 +F:    Documentation/devicetree/bindings/spi/brcm,bcm63xx-spi.yaml
 +F:    drivers/spi/spi-bcm63xx.c
 +
  BROADCOM ETHERNET PHY DRIVERS
  M:    Florian Fainelli <florian.fainelli@broadcom.com>
  R:    Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com>
@@@ -4195,7 -4187,7 +4195,7 @@@ BROADCOM KONA GPIO DRIVE
  M:    Ray Jui <rjui@broadcom.com>
  R:    Broadcom internal kernel review list <bcm-kernel-feedback-list@broadcom.com>
  S:    Supported
- F:    Documentation/devicetree/bindings/gpio/brcm,kona-gpio.txt
+ F:    Documentation/devicetree/bindings/gpio/brcm,kona-gpio.yaml
  F:    drivers/gpio/gpio-bcm-kona.c
  
  BROADCOM MPI3 STORAGE CONTROLLER DRIVER
@@@ -4462,6 -4454,7 +4462,6 @@@ CADENCE USB3 DRD IP DRIVE
  M:    Peter Chen <peter.chen@kernel.org>
  M:    Pawel Laszczak <pawell@cadence.com>
  R:    Roger Quadros <rogerq@kernel.org>
 -R:    Aswath Govindraju <a-govindraju@ti.com>
  L:    linux-usb@vger.kernel.org
  S:    Maintained
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/peter.chen/usb.git
@@@ -4819,7 -4812,6 +4819,7 @@@ F:      drivers/input/touchscreen/chipone_ic
  
  CHROME HARDWARE PLATFORM SUPPORT
  M:    Benson Leung <bleung@chromium.org>
 +M:    Tzung-Bi Shih <tzungbi@kernel.org>
  L:    chrome-platform@lists.linux.dev
  S:    Maintained
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux.git
@@@ -4887,11 -4879,7 +4887,11 @@@ L:    alsa-devel@alsa-project.org (moderat
  L:    patches@opensource.cirrus.com
  S:    Maintained
  F:    Documentation/devicetree/bindings/sound/cirrus,cs*
 +F:    drivers/mfd/cs42l43*
 +F:    drivers/pinctrl/cirrus/pinctrl-cs42l43*
 +F:    drivers/spi/spi-cs42l43*
  F:    include/dt-bindings/sound/cs*
 +F:    include/linux/mfd/cs42l43*
  F:    include/sound/cs*
  F:    sound/pci/hda/cs*
  F:    sound/pci/hda/hda_cs_dsp_ctl.*
@@@ -5152,12 -5140,10 +5152,12 @@@ S:   Maintaine
  F:    include/linux/compiler_attributes.h
  
  COMPUTE EXPRESS LINK (CXL)
 +M:    Davidlohr Bueso <dave@stgolabs.net>
 +M:    Jonathan Cameron <jonathan.cameron@huawei.com>
 +M:    Dave Jiang <dave.jiang@intel.com>
  M:    Alison Schofield <alison.schofield@intel.com>
  M:    Vishal Verma <vishal.l.verma@intel.com>
  M:    Ira Weiny <ira.weiny@intel.com>
 -M:    Ben Widawsky <bwidawsk@kernel.org>
  M:    Dan Williams <dan.j.williams@intel.com>
  L:    linux-cxl@vger.kernel.org
  S:    Maintained
@@@ -5466,7 -5452,8 +5466,7 @@@ F:      Documentation/devicetree/bindings/ne
  F:    drivers/net/can/ctucanfd/
  
  CW1200 WLAN driver
 -M:    Solomon Peachy <pizza@shaftnet.org>
 -S:    Maintained
 +S:    Orphan
  F:    drivers/net/wireless/st/cw1200/
  
  CX18 VIDEO4LINUX DRIVER
@@@ -6014,7 -6001,7 +6014,7 @@@ F:      Documentation/devicetree/bindings/mf
  F:    Documentation/devicetree/bindings/mfd/dlg,da90*.yaml
  F:    Documentation/devicetree/bindings/regulator/da92*.txt
  F:    Documentation/devicetree/bindings/regulator/dlg,da9*.yaml
 -F:    Documentation/devicetree/bindings/regulator/slg51000.txt
 +F:    Documentation/devicetree/bindings/regulator/dlg,slg51000.yaml
  F:    Documentation/devicetree/bindings/sound/da[79]*.txt
  F:    Documentation/devicetree/bindings/thermal/da90??-thermal.txt
  F:    Documentation/devicetree/bindings/watchdog/da90??-wdt.txt
@@@ -8685,11 -8672,8 +8685,11 @@@ S:    Maintaine
  F:    drivers/input/touchscreen/resistive-adc-touch.c
  
  GENERIC STRING LIBRARY
 +M:    Kees Cook <keescook@chromium.org>
  R:    Andy Shevchenko <andy@kernel.org>
 -S:    Maintained
 +L:    linux-hardening@vger.kernel.org
 +S:    Supported
 +T:    git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening
  F:    include/linux/string.h
  F:    include/linux/string_choices.h
  F:    include/linux/string_helpers.h
@@@ -8774,15 -8758,6 +8774,15 @@@ S:    Supporte
  F:    Documentation/networking/device_drivers/ethernet/google/gve.rst
  F:    drivers/net/ethernet/google
  
 +GOOGLE FIRMWARE DRIVERS
 +M:    Tzung-Bi Shih <tzungbi@kernel.org>
 +R:    Brian Norris <briannorris@chromium.org>
 +R:    Julius Werner <jwerner@chromium.org>
 +L:    chrome-platform@lists.linux.dev
 +S:    Maintained
 +T:    git git://git.kernel.org/pub/scm/linux/kernel/git/chrome-platform/linux.git
 +F:    drivers/firmware/google/
 +
  GPD POCKET FAN DRIVER
  M:    Hans de Goede <hdegoede@redhat.com>
  L:    platform-driver-x86@vger.kernel.org
@@@ -8826,7 -8801,6 +8826,7 @@@ R:      Michael Walle <michael@walle.cc
  S:    Maintained
  F:    drivers/gpio/gpio-regmap.c
  F:    include/linux/gpio/regmap.h
 +K:    (devm_)?gpio_regmap_(un)?register
  
  GPIO SUBSYSTEM
  M:    Linus Walleij <linus.walleij@linaro.org>
@@@ -9320,7 -9294,7 +9320,7 @@@ F:      drivers/crypto/hisilicon/hpre/hpre_c
  F:    drivers/crypto/hisilicon/hpre/hpre_main.c
  
  HISILICON HNS3 PMU DRIVER
 -M:    Guangbin Huang <huangguangbin2@huawei.com>
 +M:    Jijie Shao <shaojijie@huawei.com>
  S:    Supported
  F:    Documentation/admin-guide/perf/hns3-pmu.rst
  F:    drivers/perf/hisilicon/hns3_pmu.c
@@@ -9358,7 -9332,7 +9358,7 @@@ F:      Documentation/devicetree/bindings/ne
  F:    drivers/net/ethernet/hisilicon/
  
  HISILICON PMU DRIVER
 -M:    Shaokun Zhang <zhangshaokun@hisilicon.com>
 +M:    Yicong Yang <yangyicong@hisilicon.com>
  M:    Jonathan Cameron <jonathan.cameron@huawei.com>
  S:    Supported
  W:    http://www.hisilicon.com
@@@ -9390,6 -9364,7 +9390,6 @@@ F:      drivers/crypto/hisilicon/sgl.
  F:    include/linux/hisi_acc_qm.h
  
  HISILICON ROCE DRIVER
 -M:    Haoyue Xu <xuhaoyue1@hisilicon.com>
  M:    Junxian Huang <huangjunxian6@hisilicon.com>
  L:    linux-rdma@vger.kernel.org
  S:    Maintained
@@@ -9509,12 -9484,6 +9509,12 @@@ S:    Maintaine
  W:    http://artax.karlin.mff.cuni.cz/~mikulas/vyplody/hpfs/index-e.cgi
  F:    fs/hpfs/
  
 +HS3001 Hardware Temperature and Humidity Sensor
 +M:    Andre Werner <andre.werner@systec-electronic.com>
 +L:    linux-hwmon@vger.kernel.org
 +S:    Maintained
 +F:    drivers/hwmon/hs3001.c
 +
  HSI SUBSYSTEM
  M:    Sebastian Reichel <sre@kernel.org>
  S:    Maintained
@@@ -9680,7 -9649,6 +9680,7 @@@ F:      tools/hv
  
  HYPERBUS SUPPORT
  M:    Vignesh Raghavendra <vigneshr@ti.com>
 +R:    Tudor Ambarus <tudor.ambarus@linaro.org>
  L:    linux-mtd@lists.infradead.org
  S:    Supported
  Q:    http://patchwork.ozlabs.org/project/linux-mtd/list/
@@@ -11404,8 -11372,6 +11404,8 @@@ T:   git git://git.kernel.org/pub/scm/lin
  F:    Documentation/dev-tools/kunit/
  F:    include/kunit/
  F:    lib/kunit/
 +F:    rust/kernel/kunit.rs
 +F:    scripts/rustdoc_test_*
  F:    tools/testing/kunit/
  
  KERNEL USERMODE HELPER
@@@ -12292,16 -12258,6 +12292,16 @@@ F: Documentation/devicetree/bindings/cl
  F:    drivers/clk/clk-loongson2.c
  F:    include/dt-bindings/clock/loongson,ls2k-clk.h
  
 +LOONGSON SPI DRIVER
 +M:    Yinbo Zhu <zhuyinbo@loongson.cn>
 +L:    linux-spi@vger.kernel.org
 +S:    Maintained
 +F:    Documentation/devicetree/bindings/spi/loongson,ls2k-spi.yaml
 +F:    drivers/spi/spi-loongson-core.c
 +F:    drivers/spi/spi-loongson-pci.c
 +F:    drivers/spi/spi-loongson-plat.c
 +F:    drivers/spi/spi-loongson.h
 +
  LOONGSON-2 SOC SERIES GUTS DRIVER
  M:    Yinbo Zhu <zhuyinbo@loongson.cn>
  L:    loongarch@lists.linux.dev
@@@ -12512,7 -12468,6 +12512,7 @@@ F:   net/mctp
  
  MAPLE TREE
  M:    Liam R. Howlett <Liam.Howlett@oracle.com>
 +L:    maple-tree@lists.infradead.org
  L:    linux-mm@kvack.org
  S:    Supported
  F:    Documentation/core-api/maple_tree.rst
@@@ -12624,14 -12579,18 +12624,14 @@@ F:        Documentation/devicetree/bindings/ne
  F:    drivers/net/ethernet/marvell/mvpp2/
  
  MARVELL MWIFIEX WIRELESS DRIVER
 -M:    Amitkumar Karwar <amitkarwar@gmail.com>
 -M:    Ganapathi Bhat <ganapathi017@gmail.com>
 -M:    Sharvari Harisangam <sharvari.harisangam@nxp.com>
 -M:    Xinming Hu <huxinming820@gmail.com>
 +M:    Brian Norris <briannorris@chromium.org>
  L:    linux-wireless@vger.kernel.org
 -S:    Maintained
 +S:    Odd Fixes
  F:    drivers/net/wireless/marvell/mwifiex/
  
  MARVELL MWL8K WIRELESS DRIVER
 -M:    Lennert Buytenhek <buytenh@wantstofly.org>
  L:    linux-wireless@vger.kernel.org
 -S:    Odd Fixes
 +S:    Orphan
  F:    drivers/net/wireless/marvell/mwl8k.c
  
  MARVELL NAND CONTROLLER DRIVER
@@@ -13819,7 -13778,7 +13819,7 @@@ F:   Documentation/devicetree/bindings/se
  F:    drivers/spi/spi-at91-usart.c
  
  MICROCHIP AUDIO ASOC DRIVERS
 -M:    Claudiu Beznea <claudiu.beznea@microchip.com>
 +M:    Claudiu Beznea <claudiu.beznea@tuxon.dev>
  L:    alsa-devel@alsa-project.org (moderated for non-subscribers)
  S:    Supported
  F:    Documentation/devicetree/bindings/sound/atmel*
@@@ -13842,7 -13801,7 +13842,7 @@@ S:   Maintaine
  F:    drivers/crypto/atmel-ecc.*
  
  MICROCHIP EIC DRIVER
 -M:    Claudiu Beznea <claudiu.beznea@microchip.com>
 +M:    Claudiu Beznea <claudiu.beznea@tuxon.dev>
  L:    linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
  S:    Supported
  F:    Documentation/devicetree/bindings/interrupt-controller/microchip,sama7g5-eic.yaml
@@@ -13915,7 -13874,7 +13915,7 @@@ F:   drivers/video/fbdev/atmel_lcdfb.
  F:    include/video/atmel_lcdc.h
  
  MICROCHIP MCP16502 PMIC DRIVER
 -M:    Claudiu Beznea <claudiu.beznea@microchip.com>
 +M:    Claudiu Beznea <claudiu.beznea@tuxon.dev>
  L:    linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
  S:    Supported
  F:    Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt
@@@ -13942,7 -13901,7 +13942,7 @@@ F:   Documentation/devicetree/bindings/mt
  F:    drivers/mtd/nand/raw/atmel/*
  
  MICROCHIP OTPC DRIVER
 -M:    Claudiu Beznea <claudiu.beznea@microchip.com>
 +M:    Claudiu Beznea <claudiu.beznea@tuxon.dev>
  L:    linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
  S:    Supported
  F:    Documentation/devicetree/bindings/nvmem/microchip,sama7g5-otpc.yaml
@@@ -13981,7 -13940,7 +13981,7 @@@ F:   Documentation/devicetree/bindings/fp
  F:    drivers/fpga/microchip-spi.c
  
  MICROCHIP PWM DRIVER
 -M:    Claudiu Beznea <claudiu.beznea@microchip.com>
 +M:    Claudiu Beznea <claudiu.beznea@tuxon.dev>
  L:    linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
  L:    linux-pwm@vger.kernel.org
  S:    Supported
@@@ -13997,7 -13956,7 +13997,7 @@@ F:   drivers/iio/adc/at91-sama5d2_adc.
  F:    include/dt-bindings/iio/adc/at91-sama5d2_adc.h
  
  MICROCHIP SAMA5D2-COMPATIBLE SHUTDOWN CONTROLLER
 -M:    Claudiu Beznea <claudiu.beznea@microchip.com>
 +M:    Claudiu Beznea <claudiu.beznea@tuxon.dev>
  S:    Supported
  F:    Documentation/devicetree/bindings/power/reset/atmel,sama5d2-shdwc.yaml
  F:    drivers/power/reset/at91-sama5d2_shdwc.c
@@@ -14009,12 -13968,12 +14009,12 @@@ T:        git https://git.kernel.org/pub/scm/l
  F:    drivers/soc/microchip/
  
  MICROCHIP SPI DRIVER
 -M:    Tudor Ambarus <tudor.ambarus@linaro.org>
 +M:    Ryan Wanner <ryan.wanner@microchip.com>
  S:    Supported
  F:    drivers/spi/spi-atmel.*
  
  MICROCHIP SSC DRIVER
 -M:    Claudiu Beznea <claudiu.beznea@microchip.com>
 +M:    Claudiu Beznea <claudiu.beznea@tuxon.dev>
  L:    linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
  S:    Supported
  F:    Documentation/devicetree/bindings/misc/atmel-ssc.txt
@@@ -14043,7 -14002,7 +14043,7 @@@ F:   drivers/usb/gadget/udc/atmel_usba_ud
  
  MICROCHIP WILC1000 WIFI DRIVER
  M:    Ajay Singh <ajay.kathat@microchip.com>
 -M:    Claudiu Beznea <claudiu.beznea@microchip.com>
 +M:    Claudiu Beznea <claudiu.beznea@tuxon.dev>
  L:    linux-wireless@vger.kernel.org
  S:    Supported
  F:    drivers/net/wireless/microchip/wilc1000/
@@@ -14835,16 -14794,6 +14835,16 @@@ F: net/netfilter/xt_CONNSECMARK.
  F:    net/netfilter/xt_SECMARK.c
  F:    net/netlabel/
  
 +NETWORKING [MACSEC]
 +M:    Sabrina Dubroca <sd@queasysnail.net>
 +L:    netdev@vger.kernel.org
 +S:    Maintained
 +F:    drivers/net/macsec.c
 +F:    include/net/macsec.h
 +F:    include/uapi/linux/if_macsec.h
 +K:    macsec
 +K:    \bmdo_
 +
  NETWORKING [MPTCP]
  M:    Matthieu Baerts <matthieu.baerts@tessares.net>
  M:    Mat Martineau <martineau@kernel.org>
@@@ -15030,7 -14979,6 +15030,7 @@@ F:   include/linux/power/bq27xxx_battery.
  
  NOLIBC HEADER FILE
  M:    Willy Tarreau <w@1wt.eu>
 +M:    Thomas Weißschuh <linux@weissschuh.net>
  S:    Maintained
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/wtarreau/nolibc.git
  F:    tools/include/nolibc/
@@@ -16337,7 -16285,6 +16337,7 @@@ F:   drivers/pci/controller/dwc/pci-exyno
  PCI DRIVER FOR SYNOPSYS DESIGNWARE
  M:    Jingoo Han <jingoohan1@gmail.com>
  M:    Gustavo Pimentel <gustavo.pimentel@synopsys.com>
 +M:    Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
  L:    linux-pci@vger.kernel.org
  S:    Maintained
  F:    Documentation/devicetree/bindings/pci/snps,dw-pcie-ep.yaml
@@@ -17090,7 -17037,6 +17090,7 @@@ F:   drivers/net/ppp/pptp.
  PRESSURE STALL INFORMATION (PSI)
  M:    Johannes Weiner <hannes@cmpxchg.org>
  M:    Suren Baghdasaryan <surenb@google.com>
 +R:    Peter Ziljstra <peterz@infradead.org>
  S:    Maintained
  F:    include/linux/psi*
  F:    kernel/sched/psi.c
@@@ -17490,7 -17436,6 +17490,7 @@@ F:   drivers/media/tuners/qt1010
  
  QUALCOMM ATH12K WIRELESS DRIVER
  M:    Kalle Valo <kvalo@kernel.org>
 +M:    Jeff Johnson <quic_jjohnson@quicinc.com>
  L:    ath12k@lists.infradead.org
  S:    Supported
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git
@@@ -17498,7 -17443,6 +17498,7 @@@ F:   drivers/net/wireless/ath/ath12k
  
  QUALCOMM ATHEROS ATH10K WIRELESS DRIVER
  M:    Kalle Valo <kvalo@kernel.org>
 +M:    Jeff Johnson <quic_jjohnson@quicinc.com>
  L:    ath10k@lists.infradead.org
  S:    Supported
  W:    https://wireless.wiki.kernel.org/en/users/Drivers/ath10k
@@@ -17508,7 -17452,6 +17508,7 @@@ F:   drivers/net/wireless/ath/ath10k
  
  QUALCOMM ATHEROS ATH11K WIRELESS DRIVER
  M:    Kalle Valo <kvalo@kernel.org>
 +M:    Jeff Johnson <quic_jjohnson@quicinc.com>
  L:    ath11k@lists.infradead.org
  S:    Supported
  W:    https://wireless.wiki.kernel.org/en/users/Drivers/ath11k
@@@ -17600,7 -17543,6 +17600,7 @@@ QUALCOMM ETHQOS ETHERNET DRIVE
  M:    Vinod Koul <vkoul@kernel.org>
  R:    Bhupesh Sharma <bhupesh.sharma@linaro.org>
  L:    netdev@vger.kernel.org
 +L:    linux-arm-msm@vger.kernel.org
  S:    Maintained
  F:    Documentation/devicetree/bindings/net/qcom,ethqos.yaml
  F:    drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
@@@ -18029,7 -17971,7 +18029,7 @@@ T:   git git://git.kernel.org/pub/scm/lin
  F:    drivers/net/wireless/realtek/rtlwifi/
  
  REALTEK WIRELESS DRIVER (rtw88)
 -M:    Yan-Hsuan Chuang <tony0620emma@gmail.com>
 +M:    Ping-Ke Shih <pkshih@realtek.com>
  L:    linux-wireless@vger.kernel.org
  S:    Maintained
  F:    drivers/net/wireless/realtek/rtw88/
@@@ -18554,14 -18496,17 +18554,14 @@@ RTL8180 WIRELESS DRIVE
  L:    linux-wireless@vger.kernel.org
  S:    Orphan
  W:    https://wireless.wiki.kernel.org/
 -T:    git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
  F:    drivers/net/wireless/realtek/rtl818x/rtl8180/
  
  RTL8187 WIRELESS DRIVER
 -M:    Herton Ronaldo Krzesinski <herton@canonical.com>
 -M:    Hin-Tak Leung <htl10@users.sourceforge.net>
 +M:    Hin-Tak Leung <hintak.leung@gmail.com>
  M:    Larry Finger <Larry.Finger@lwfinger.net>
  L:    linux-wireless@vger.kernel.org
  S:    Maintained
  W:    https://wireless.wiki.kernel.org/
 -T:    git git://git.kernel.org/pub/scm/linux/kernel/git/linville/wireless-testing.git
  F:    drivers/net/wireless/realtek/rtl818x/rtl8187/
  
  RTL8XXXU WIRELESS DRIVER (rtl8xxxu)
@@@ -18597,8 -18542,6 +18597,8 @@@ R:   Boqun Feng <boqun.feng@gmail.com
  R:    Gary Guo <gary@garyguo.net>
  R:    Björn Roy Baron <bjorn3_gh@protonmail.com>
  R:    Benno Lossin <benno.lossin@proton.me>
 +R:    Andreas Hindborg <a.hindborg@samsung.com>
 +R:    Alice Ryhl <aliceryhl@google.com>
  L:    rust-for-linux@vger.kernel.org
  S:    Supported
  W:    https://github.com/Rust-for-Linux/linux
@@@ -18640,7 -18583,7 +18640,7 @@@ L:   linux-s390@vger.kernel.or
  S:    Supported
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux.git
  F:    Documentation/driver-api/s390-drivers.rst
 -F:    Documentation/s390/
 +F:    Documentation/arch/s390/
  F:    arch/s390/
  F:    drivers/s390/
  F:    drivers/watchdog/diag288_wdt.c
@@@ -18701,7 -18644,7 +18701,7 @@@ M:   Niklas Schnelle <schnelle@linux.ibm.
  M:    Gerald Schaefer <gerald.schaefer@linux.ibm.com>
  L:    linux-s390@vger.kernel.org
  S:    Supported
 -F:    Documentation/s390/pci.rst
 +F:    Documentation/arch/s390/pci.rst
  F:    arch/s390/pci/
  F:    drivers/pci/hotplug/s390_pci_hpc.c
  
@@@ -18718,7 -18661,7 +18718,7 @@@ M:   Halil Pasic <pasic@linux.ibm.com
  M:    Jason Herne <jjherne@linux.ibm.com>
  L:    linux-s390@vger.kernel.org
  S:    Supported
 -F:    Documentation/s390/vfio-ap*
 +F:    Documentation/arch/s390/vfio-ap*
  F:    drivers/s390/crypto/vfio_ap*
  
  S390 VFIO-CCW DRIVER
@@@ -18728,7 -18671,7 +18728,7 @@@ R:   Halil Pasic <pasic@linux.ibm.com
  L:    linux-s390@vger.kernel.org
  L:    kvm@vger.kernel.org
  S:    Supported
 -F:    Documentation/s390/vfio-ccw.rst
 +F:    Documentation/arch/s390/vfio-ccw.rst
  F:    drivers/s390/cio/vfio_ccw*
  F:    include/uapi/linux/vfio_ccw.h
  
@@@ -19270,6 -19213,13 +19270,6 @@@ F:  Documentation/devicetree/bindings/se
  F:    drivers/tty/serdev/
  F:    include/linux/serdev.h
  
 -SERIAL DRIVERS
 -M:    Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -L:    linux-serial@vger.kernel.org
 -S:    Maintained
 -F:    Documentation/devicetree/bindings/serial/
 -F:    drivers/tty/serial/
 -
  SERIAL IR RECEIVER
  M:    Sean Young <sean@mess.org>
  L:    linux-media@vger.kernel.org
@@@ -19321,6 -19271,7 +19321,6 @@@ F:   drivers/misc/sgi-gru
  SGI XP/XPC/XPNET DRIVER
  M:    Robin Holt <robinmholt@gmail.com>
  M:    Steve Wahl <steve.wahl@hpe.com>
 -R:    Mike Travis <mike.travis@hpe.com>
  S:    Maintained
  F:    drivers/misc/sgi-xp/
  
@@@ -19631,6 -19582,13 +19631,6 @@@ M:  Nicolas Pitre <nico@fluxnic.net
  S:    Odd Fixes
  F:    drivers/net/ethernet/smsc/smc91x.*
  
 -SMM665 HARDWARE MONITOR DRIVER
 -M:    Guenter Roeck <linux@roeck-us.net>
 -L:    linux-hwmon@vger.kernel.org
 -S:    Maintained
 -F:    Documentation/hwmon/smm665.rst
 -F:    drivers/hwmon/smm665.c
 -
  SMSC EMC2103 HARDWARE MONITOR DRIVER
  M:    Steve Glendinning <steve.glendinning@shawell.net>
  L:    linux-hwmon@vger.kernel.org
@@@ -20432,6 -20390,7 +20432,6 @@@ F:   drivers/pwm/pwm-stm32
  F:    include/linux/*/stm32-*tim*
  
  STMMAC ETHERNET DRIVER
 -M:    Giuseppe Cavallaro <peppe.cavallaro@st.com>
  M:    Alexandre Torgue <alexandre.torgue@foss.st.com>
  M:    Jose Abreu <joabreu@synopsys.com>
  L:    netdev@vger.kernel.org
@@@ -21090,39 -21049,6 +21090,39 @@@ S: Maintaine
  F:    Documentation/devicetree/bindings/sound/davinci-mcasp-audio.yaml
  F:    sound/soc/ti/
  
 +TEXAS INSTRUMENTS AUDIO (ASoC/HDA) DRIVERS
 +M:    Shenghao Ding <shenghao-ding@ti.com>
 +M:    Kevin Lu <kevin-lu@ti.com>
 +M:    Baojun Xu <x1077012@ti.com>
 +L:    alsa-devel@alsa-project.org (moderated for non-subscribers)
 +S:    Maintained
 +F:    Documentation/devicetree/bindings/sound/tas2552.txt
 +F:    Documentation/devicetree/bindings/sound/tas2562.yaml
 +F:    Documentation/devicetree/bindings/sound/tas2770.yaml
 +F:    Documentation/devicetree/bindings/sound/tas27xx.yaml
 +F:    Documentation/devicetree/bindings/sound/ti,pcm1681.txt
 +F:    Documentation/devicetree/bindings/sound/ti,pcm3168a.yaml
 +F:    Documentation/devicetree/bindings/sound/ti,tlv320*.yaml
 +F:    Documentation/devicetree/bindings/sound/tlv320adcx140.yaml
 +F:    Documentation/devicetree/bindings/sound/tlv320aic31xx.txt
 +F:    Documentation/devicetree/bindings/sound/tpa6130a2.txt
 +F:    include/sound/tas2*.h
 +F:    include/sound/tlv320*.h
 +F:    include/sound/tpa6130a2-plat.h
 +F:    sound/pci/hda/tas2781_hda_i2c.c
 +F:    sound/soc/codecs/pcm1681.c
 +F:    sound/soc/codecs/pcm1789*.*
 +F:    sound/soc/codecs/pcm179x*.*
 +F:    sound/soc/codecs/pcm186x*.*
 +F:    sound/soc/codecs/pcm3008.*
 +F:    sound/soc/codecs/pcm3060*.*
 +F:    sound/soc/codecs/pcm3168a*.*
 +F:    sound/soc/codecs/pcm5102a.c
 +F:    sound/soc/codecs/pcm512x*.*
 +F:    sound/soc/codecs/tas2*.*
 +F:    sound/soc/codecs/tlv320*.*
 +F:    sound/soc/codecs/tpa6130a2.*
 +
  TEXAS INSTRUMENTS DMA DRIVERS
  M:    Peter Ujfalusi <peter.ujfalusi@gmail.com>
  L:    dmaengine@vger.kernel.org
@@@ -21699,16 -21625,14 +21699,16 @@@ W:        https://github.com/srcres258/linux-d
  T:    git git://github.com/srcres258/linux-doc.git doc-zh-tw
  F:    Documentation/translations/zh_TW/
  
 -TTY LAYER
 +TTY LAYER AND SERIAL DRIVERS
  M:    Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  M:    Jiri Slaby <jirislaby@kernel.org>
 +L:    linux-kernel@vger.kernel.org
 +L:    linux-serial@vger.kernel.org
  S:    Supported
  T:    git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
 +F:    Documentation/devicetree/bindings/serial/
  F:    Documentation/driver-api/serial/
  F:    drivers/tty/
 -F:    drivers/tty/serial/serial_core.c
  F:    include/linux/selection.h
  F:    include/linux/serial.h
  F:    include/linux/serial_core.h
@@@ -21737,14 -21661,11 +21737,14 @@@ S:        Orpha
  F:    drivers/net/ethernet/dec/tulip/
  
  TUN/TAP driver
 -M:    Maxim Krasnyansky <maxk@qti.qualcomm.com>
 +M:    Willem de Bruijn <willemdebruijn.kernel@gmail.com>
 +M:    Jason Wang <jasowang@redhat.com>
  S:    Maintained
  W:    http://vtun.sourceforge.net/tun
  F:    Documentation/networking/tuntap.rst
  F:    arch/um/os-Linux/drivers/
 +F:    drivers/net/tap.c
 +F:    drivers/net/tun.c
  
  TURBOCHANNEL SUBSYSTEM
  M:    "Maciej W. Rozycki" <macro@orcam.me.uk>
@@@ -21967,8 -21888,9 +21967,8 @@@ S:   Maintaine
  F:    drivers/usb/misc/apple-mfi-fastcharge.c
  
  USB AR5523 WIRELESS DRIVER
 -M:    Pontus Fuchs <pontus.fuchs@gmail.com>
  L:    linux-wireless@vger.kernel.org
 -S:    Maintained
 +S:    Orphan
  F:    drivers/net/wireless/ath/ar5523/
  
  USB ATTACHED SCSI
@@@ -22245,8 -22167,9 +22245,8 @@@ F:   drivers/usb/gadget/legacy/webcam.
  F:    include/uapi/linux/usb/g_uvc.h
  
  USB WIRELESS RNDIS DRIVER (rndis_wlan)
 -M:    Jussi Kivilinna <jussi.kivilinna@iki.fi>
  L:    linux-wireless@vger.kernel.org
 -S:    Maintained
 +S:    Orphan
  F:    drivers/net/wireless/legacy/rndis_wlan.c
  
  USB XHCI DRIVER
@@@ -22535,6 -22458,7 +22535,6 @@@ L:   virtualization@lists.linux-foundatio
  S:    Maintained
  F:    drivers/block/virtio_blk.c
  F:    drivers/scsi/virtio_scsi.c
 -F:    drivers/vhost/scsi.c
  F:    include/uapi/linux/virtio_blk.h
  F:    include/uapi/linux/virtio_scsi.h
  
@@@ -22633,16 -22557,6 +22633,16 @@@ F: include/linux/vhost_iotlb.
  F:    include/uapi/linux/vhost.h
  F:    kernel/vhost_task.c
  
 +VIRTIO HOST (VHOST-SCSI)
 +M:    "Michael S. Tsirkin" <mst@redhat.com>
 +M:    Jason Wang <jasowang@redhat.com>
 +M:    Mike Christie <michael.christie@oracle.com>
 +R:    Paolo Bonzini <pbonzini@redhat.com>
 +R:    Stefan Hajnoczi <stefanha@redhat.com>
 +L:    virtualization@lists.linux-foundation.org
 +S:    Maintained
 +F:    drivers/vhost/scsi.c
 +
  VIRTIO I2C DRIVER
  M:    Conghui Chen <conghui.chen@intel.com>
  M:    Viresh Kumar <viresh.kumar@linaro.org>
@@@ -23030,7 -22944,7 +23030,7 @@@ F:   drivers/input/misc/wistron_btns.
  
  WL3501 WIRELESS PCMCIA CARD DRIVER
  L:    linux-wireless@vger.kernel.org
 -S:    Odd fixes
 +S:    Orphan
  F:    drivers/net/wireless/legacy/wl3501*
  
  WMI BINARY MOF DRIVER
@@@ -23210,8 -23124,7 +23210,8 @@@ F:   arch/x86/platfor
  
  X86 PLATFORM UV HPE SUPERDOME FLEX
  M:    Steve Wahl <steve.wahl@hpe.com>
 -R:    Mike Travis <mike.travis@hpe.com>
 +R:    Justin Ernst <justin.ernst@hpe.com>
 +R:    Kyle Meyer <kyle.meyer@hpe.com>
  R:    Dimitri Sivanich <dimitri.sivanich@hpe.com>
  R:    Russ Anderson <russ.anderson@hpe.com>
  S:    Supported
@@@ -23602,8 -23515,11 +23602,8 @@@ S:  Maintaine
  F:    mm/zbud.c
  
  ZD1211RW WIRELESS DRIVER
 -M:    Ulrich Kunitz <kune@deine-taler.de>
  L:    linux-wireless@vger.kernel.org
 -L:    zd1211-devs@lists.sourceforge.net (subscribers-only)
 -S:    Maintained
 -W:    http://zd1211.ath.cx/wiki/DriverRewrite
 +S:    Orphan
  F:    drivers/net/wireless/zydas/zd1211rw/
  
  ZD1301 MEDIA DRIVER
diff --combined drivers/gpio/gpio-sim.c
@@@ -8,6 -8,7 +8,7 @@@
  #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  
  #include <linux/bitmap.h>
+ #include <linux/cleanup.h>
  #include <linux/completion.h>
  #include <linux/configfs.h>
  #include <linux/device.h>
@@@ -68,7 -69,7 +69,7 @@@ static int gpio_sim_apply_pull(struct g
        gc = &chip->gc;
        desc = &gc->gpiodev->descs[offset];
  
-       mutex_lock(&chip->lock);
+       guard(mutex)(&chip->lock);
  
        if (test_bit(FLAG_REQUESTED, &desc->flags) &&
            !test_bit(FLAG_IS_OUT, &desc->flags)) {
@@@ -104,29 -105,24 +105,24 @@@ set_value
  
  set_pull:
        __assign_bit(offset, chip->pull_map, value);
-       mutex_unlock(&chip->lock);
        return 0;
  }
  
  static int gpio_sim_get(struct gpio_chip *gc, unsigned int offset)
  {
        struct gpio_sim_chip *chip = gpiochip_get_data(gc);
-       int ret;
  
-       mutex_lock(&chip->lock);
-       ret = !!test_bit(offset, chip->value_map);
-       mutex_unlock(&chip->lock);
+       guard(mutex)(&chip->lock);
  
-       return ret;
+       return !!test_bit(offset, chip->value_map);
  }
  
  static void gpio_sim_set(struct gpio_chip *gc, unsigned int offset, int value)
  {
        struct gpio_sim_chip *chip = gpiochip_get_data(gc);
  
-       mutex_lock(&chip->lock);
-       __assign_bit(offset, chip->value_map, value);
-       mutex_unlock(&chip->lock);
+       scoped_guard(mutex, &chip->lock)
+               __assign_bit(offset, chip->value_map, value);
  }
  
  static int gpio_sim_get_multiple(struct gpio_chip *gc,
  {
        struct gpio_sim_chip *chip = gpiochip_get_data(gc);
  
-       mutex_lock(&chip->lock);
-       bitmap_replace(bits, bits, chip->value_map, mask, gc->ngpio);
-       mutex_unlock(&chip->lock);
+       scoped_guard(mutex, &chip->lock)
+               bitmap_replace(bits, bits, chip->value_map, mask, gc->ngpio);
  
        return 0;
  }
@@@ -146,9 -141,9 +141,9 @@@ static void gpio_sim_set_multiple(struc
  {
        struct gpio_sim_chip *chip = gpiochip_get_data(gc);
  
-       mutex_lock(&chip->lock);
-       bitmap_replace(chip->value_map, chip->value_map, bits, mask, gc->ngpio);
-       mutex_unlock(&chip->lock);
+       scoped_guard(mutex, &chip->lock)
+               bitmap_replace(chip->value_map, chip->value_map, bits, mask,
+                              gc->ngpio);
  }
  
  static int gpio_sim_direction_output(struct gpio_chip *gc,
  {
        struct gpio_sim_chip *chip = gpiochip_get_data(gc);
  
-       mutex_lock(&chip->lock);
-       __clear_bit(offset, chip->direction_map);
-       __assign_bit(offset, chip->value_map, value);
-       mutex_unlock(&chip->lock);
+       scoped_guard(mutex, &chip->lock) {
+               __clear_bit(offset, chip->direction_map);
+               __assign_bit(offset, chip->value_map, value);
+       }
  
        return 0;
  }
@@@ -168,9 -163,8 +163,8 @@@ static int gpio_sim_direction_input(str
  {
        struct gpio_sim_chip *chip = gpiochip_get_data(gc);
  
-       mutex_lock(&chip->lock);
-       __set_bit(offset, chip->direction_map);
-       mutex_unlock(&chip->lock);
+       scoped_guard(mutex, &chip->lock)
+               __set_bit(offset, chip->direction_map);
  
        return 0;
  }
@@@ -180,9 -174,8 +174,8 @@@ static int gpio_sim_get_direction(struc
        struct gpio_sim_chip *chip = gpiochip_get_data(gc);
        int direction;
  
-       mutex_lock(&chip->lock);
-       direction = !!test_bit(offset, chip->direction_map);
-       mutex_unlock(&chip->lock);
+       scoped_guard(mutex, &chip->lock)
+               direction = !!test_bit(offset, chip->direction_map);
  
        return direction ? GPIO_LINE_DIRECTION_IN : GPIO_LINE_DIRECTION_OUT;
  }
@@@ -215,9 -208,9 +208,9 @@@ static void gpio_sim_free(struct gpio_c
  {
        struct gpio_sim_chip *chip = gpiochip_get_data(gc);
  
-       mutex_lock(&chip->lock);
-       __assign_bit(offset, chip->value_map, !!test_bit(offset, chip->pull_map));
-       mutex_unlock(&chip->lock);
+       scoped_guard(mutex, &chip->lock)
+               __assign_bit(offset, chip->value_map,
+                            !!test_bit(offset, chip->pull_map));
  }
  
  static ssize_t gpio_sim_sysfs_val_show(struct device *dev,
        struct gpio_sim_chip *chip = dev_get_drvdata(dev);
        int val;
  
-       mutex_lock(&chip->lock);
-       val = !!test_bit(line_attr->offset, chip->value_map);
-       mutex_unlock(&chip->lock);
+       scoped_guard(mutex, &chip->lock)
+               val = !!test_bit(line_attr->offset, chip->value_map);
  
        return sysfs_emit(buf, "%d\n", val);
  }
@@@ -258,9 -250,8 +250,8 @@@ static ssize_t gpio_sim_sysfs_pull_show
        struct gpio_sim_chip *chip = dev_get_drvdata(dev);
        int pull;
  
-       mutex_lock(&chip->lock);
-       pull = !!test_bit(line_attr->offset, chip->pull_map);
-       mutex_unlock(&chip->lock);
+       scoped_guard(mutex, &chip->lock)
+               pull = !!test_bit(line_attr->offset, chip->pull_map);
  
        return sysfs_emit(buf, "%s\n", gpio_sim_sysfs_pull_strings[pull]);
  }
@@@ -291,15 -282,6 +282,15 @@@ static void gpio_sim_mutex_destroy(voi
        mutex_destroy(lock);
  }
  
 +static void gpio_sim_dispose_mappings(void *data)
 +{
 +      struct gpio_sim_chip *chip = data;
 +      unsigned int i;
 +
 +      for (i = 0; i < chip->gc.ngpio; i++)
 +              irq_dispose_mapping(irq_find_mapping(chip->irq_sim, i));
 +}
 +
  static void gpio_sim_sysfs_remove(void *data)
  {
        struct gpio_sim_chip *chip = data;
@@@ -411,14 -393,10 +402,14 @@@ static int gpio_sim_add_bank(struct fwn
        if (!chip->pull_map)
                return -ENOMEM;
  
 -      chip->irq_sim = devm_irq_domain_create_sim(dev, NULL, num_lines);
 +      chip->irq_sim = devm_irq_domain_create_sim(dev, swnode, num_lines);
        if (IS_ERR(chip->irq_sim))
                return PTR_ERR(chip->irq_sim);
  
 +      ret = devm_add_action_or_reset(dev, gpio_sim_dispose_mappings, chip);
 +      if (ret)
 +              return ret;
 +
        mutex_init(&chip->lock);
        ret = devm_add_action_or_reset(dev, gpio_sim_mutex_destroy,
                                       &chip->lock);
        gc->set_config = gpio_sim_set_config;
        gc->to_irq = gpio_sim_to_irq;
        gc->free = gpio_sim_free;
 +      gc->can_sleep = true;
  
        ret = devm_gpiochip_add_data(dev, gc, chip);
        if (ret)
@@@ -502,7 -479,7 +493,7 @@@ struct gpio_sim_device 
         * This structure however can be modified by callbacks of different
         * attributes so we need another lock.
         *
-        * We use this lock fo protecting all data structures owned by this
+        * We use this lock for protecting all data structures owned by this
         * object too.
         */
        struct mutex lock;
@@@ -656,16 -633,13 +647,13 @@@ static bool gpio_sim_device_is_live_unl
  
  static char *gpio_sim_strdup_trimmed(const char *str, size_t count)
  {
-       char *dup, *trimmed;
+       char *trimmed;
  
-       dup = kstrndup(str, count, GFP_KERNEL);
-       if (!dup)
+       trimmed = kstrndup(skip_spaces(str), count, GFP_KERNEL);
+       if (!trimmed)
                return NULL;
  
-       trimmed = strstrip(dup);
-       memmove(dup, trimmed, strlen(trimmed) + 1);
-       return dup;
+       return strim(trimmed);
  }
  
  static ssize_t gpio_sim_device_config_dev_name_show(struct config_item *item,
  {
        struct gpio_sim_device *dev = to_gpio_sim_device(item);
        struct platform_device *pdev;
-       int ret;
  
-       mutex_lock(&dev->lock);
+       guard(mutex)(&dev->lock);
        pdev = dev->pdev;
        if (pdev)
-               ret = sprintf(page, "%s\n", dev_name(&pdev->dev));
-       else
-               ret = sprintf(page, "gpio-sim.%d\n", dev->id);
-       mutex_unlock(&dev->lock);
+               return sprintf(page, "%s\n", dev_name(&pdev->dev));
  
-       return ret;
+       return sprintf(page, "gpio-sim.%d\n", dev->id);
  }
  
  CONFIGFS_ATTR_RO(gpio_sim_device_config_, dev_name);
@@@ -694,9 -665,8 +679,8 @@@ gpio_sim_device_config_live_show(struc
        struct gpio_sim_device *dev = to_gpio_sim_device(item);
        bool live;
  
-       mutex_lock(&dev->lock);
-       live = gpio_sim_device_is_live_unlocked(dev);
-       mutex_unlock(&dev->lock);
+       scoped_guard(mutex, &dev->lock)
+               live = gpio_sim_device_is_live_unlocked(dev);
  
        return sprintf(page, "%c\n", live ? '1' : '0');
  }
@@@ -851,8 -821,7 +835,7 @@@ gpio_sim_make_bank_swnode(struct gpio_s
  {
        struct property_entry properties[GPIO_SIM_PROP_MAX];
        unsigned int prop_idx = 0, line_names_size = 0;
-       struct fwnode_handle *swnode;
-       char **line_names;
+       char **line_names __free(kfree) = NULL;
  
        memset(properties, 0, sizeof(properties));
  
                                                "gpio-line-names",
                                                line_names, line_names_size);
  
-       swnode = fwnode_create_software_node(properties, parent);
-       kfree(line_names);
-       return swnode;
+       return fwnode_create_software_node(properties, parent);
  }
  
  static void gpio_sim_remove_swnode_recursive(struct fwnode_handle *swnode)
@@@ -998,18 -965,15 +979,15 @@@ gpio_sim_device_config_live_store(struc
        if (ret)
                return ret;
  
-       mutex_lock(&dev->lock);
+       guard(mutex)(&dev->lock);
  
-       if ((!live && !gpio_sim_device_is_live_unlocked(dev)) ||
-           (live && gpio_sim_device_is_live_unlocked(dev)))
+       if (live == gpio_sim_device_is_live_unlocked(dev))
                ret = -EPERM;
        else if (live)
                ret = gpio_sim_device_activate_unlocked(dev);
        else
                gpio_sim_device_deactivate_unlocked(dev);
  
-       mutex_unlock(&dev->lock);
        return ret ?: count;
  }
  
@@@ -1046,17 -1010,14 +1024,14 @@@ static ssize_t gpio_sim_bank_config_chi
        struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
        struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
        struct gpio_sim_chip_name_ctx ctx = { bank->swnode, page };
-       int ret;
  
-       mutex_lock(&dev->lock);
+       guard(mutex)(&dev->lock);
        if (gpio_sim_device_is_live_unlocked(dev))
-               ret = device_for_each_child(&dev->pdev->dev, &ctx,
-                                           gpio_sim_emit_chip_name);
-       else
-               ret = sprintf(page, "none\n");
-       mutex_unlock(&dev->lock);
+               return device_for_each_child(&dev->pdev->dev, &ctx,
+                                            gpio_sim_emit_chip_name);
  
-       return ret;
+       return sprintf(page, "none\n");
  }
  
  CONFIGFS_ATTR_RO(gpio_sim_bank_config_, chip_name);
@@@ -1066,13 -1027,10 +1041,10 @@@ gpio_sim_bank_config_label_show(struct 
  {
        struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
        struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
-       int ret;
  
-       mutex_lock(&dev->lock);
-       ret = sprintf(page, "%s\n", bank->label ?: "");
-       mutex_unlock(&dev->lock);
+       guard(mutex)(&dev->lock);
  
-       return ret;
+       return sprintf(page, "%s\n", bank->label ?: "");
  }
  
  static ssize_t gpio_sim_bank_config_label_store(struct config_item *item,
        struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
        char *trimmed;
  
-       mutex_lock(&dev->lock);
+       guard(mutex)(&dev->lock);
  
-       if (gpio_sim_device_is_live_unlocked(dev)) {
-               mutex_unlock(&dev->lock);
+       if (gpio_sim_device_is_live_unlocked(dev))
                return -EBUSY;
-       }
  
        trimmed = gpio_sim_strdup_trimmed(page, count);
-       if (!trimmed) {
-               mutex_unlock(&dev->lock);
+       if (!trimmed)
                return -ENOMEM;
-       }
  
        kfree(bank->label);
        bank->label = trimmed;
  
-       mutex_unlock(&dev->lock);
        return count;
  }
  
@@@ -1109,13 -1062,10 +1076,10 @@@ gpio_sim_bank_config_num_lines_show(str
  {
        struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
        struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
-       int ret;
  
-       mutex_lock(&dev->lock);
-       ret = sprintf(page, "%u\n", bank->num_lines);
-       mutex_unlock(&dev->lock);
+       guard(mutex)(&dev->lock);
  
-       return ret;
+       return sprintf(page, "%u\n", bank->num_lines);
  }
  
  static ssize_t
@@@ -1134,16 -1084,13 +1098,13 @@@ gpio_sim_bank_config_num_lines_store(st
        if (num_lines == 0)
                return -EINVAL;
  
-       mutex_lock(&dev->lock);
+       guard(mutex)(&dev->lock);
  
-       if (gpio_sim_device_is_live_unlocked(dev)) {
-               mutex_unlock(&dev->lock);
+       if (gpio_sim_device_is_live_unlocked(dev))
                return -EBUSY;
-       }
  
        bank->num_lines = num_lines;
  
-       mutex_unlock(&dev->lock);
        return count;
  }
  
@@@ -1161,13 -1108,10 +1122,10 @@@ gpio_sim_line_config_name_show(struct c
  {
        struct gpio_sim_line *line = to_gpio_sim_line(item);
        struct gpio_sim_device *dev = gpio_sim_line_get_device(line);
-       int ret;
  
-       mutex_lock(&dev->lock);
-       ret = sprintf(page, "%s\n", line->name ?: "");
-       mutex_unlock(&dev->lock);
+       guard(mutex)(&dev->lock);
  
-       return ret;
+       return sprintf(page, "%s\n", line->name ?: "");
  }
  
  static ssize_t gpio_sim_line_config_name_store(struct config_item *item,
        struct gpio_sim_device *dev = gpio_sim_line_get_device(line);
        char *trimmed;
  
-       mutex_lock(&dev->lock);
+       guard(mutex)(&dev->lock);
  
-       if (gpio_sim_device_is_live_unlocked(dev)) {
-               mutex_unlock(&dev->lock);
+       if (gpio_sim_device_is_live_unlocked(dev))
                return -EBUSY;
-       }
  
        trimmed = gpio_sim_strdup_trimmed(page, count);
-       if (!trimmed) {
-               mutex_unlock(&dev->lock);
+       if (!trimmed)
                return -ENOMEM;
-       }
  
        kfree(line->name);
        line->name = trimmed;
  
-       mutex_unlock(&dev->lock);
        return count;
  }
  
@@@ -1210,13 -1148,10 +1162,10 @@@ static ssize_t gpio_sim_hog_config_name
  {
        struct gpio_sim_hog *hog = to_gpio_sim_hog(item);
        struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
-       int ret;
  
-       mutex_lock(&dev->lock);
-       ret = sprintf(page, "%s\n", hog->name ?: "");
-       mutex_unlock(&dev->lock);
+       guard(mutex)(&dev->lock);
  
-       return ret;
+       return sprintf(page, "%s\n", hog->name ?: "");
  }
  
  static ssize_t gpio_sim_hog_config_name_store(struct config_item *item,
        struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
        char *trimmed;
  
-       mutex_lock(&dev->lock);
+       guard(mutex)(&dev->lock);
  
-       if (gpio_sim_device_is_live_unlocked(dev)) {
-               mutex_unlock(&dev->lock);
+       if (gpio_sim_device_is_live_unlocked(dev))
                return -EBUSY;
-       }
  
        trimmed = gpio_sim_strdup_trimmed(page, count);
-       if (!trimmed) {
-               mutex_unlock(&dev->lock);
+       if (!trimmed)
                return -ENOMEM;
-       }
  
        kfree(hog->name);
        hog->name = trimmed;
  
-       mutex_unlock(&dev->lock);
        return count;
  }
  
@@@ -1257,9 -1186,8 +1200,8 @@@ static ssize_t gpio_sim_hog_config_dire
        char *repr;
        int dir;
  
-       mutex_lock(&dev->lock);
-       dir = hog->dir;
-       mutex_unlock(&dev->lock);
+       scoped_guard(mutex, &dev->lock)
+               dir = hog->dir;
  
        switch (dir) {
        case GPIOD_IN:
@@@ -1286,42 -1214,24 +1228,24 @@@ gpio_sim_hog_config_direction_store(str
  {
        struct gpio_sim_hog *hog = to_gpio_sim_hog(item);
        struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
-       char *trimmed;
        int dir;
  
-       mutex_lock(&dev->lock);
+       guard(mutex)(&dev->lock);
  
-       if (gpio_sim_device_is_live_unlocked(dev)) {
-               mutex_unlock(&dev->lock);
+       if (gpio_sim_device_is_live_unlocked(dev))
                return -EBUSY;
-       }
  
-       trimmed = gpio_sim_strdup_trimmed(page, count);
-       if (!trimmed) {
-               mutex_unlock(&dev->lock);
-               return -ENOMEM;
-       }
-       if (strcmp(trimmed, "input") == 0)
+       if (sysfs_streq(page, "input"))
                dir = GPIOD_IN;
-       else if (strcmp(trimmed, "output-high") == 0)
+       else if (sysfs_streq(page, "output-high"))
                dir = GPIOD_OUT_HIGH;
-       else if (strcmp(trimmed, "output-low") == 0)
+       else if (sysfs_streq(page, "output-low"))
                dir = GPIOD_OUT_LOW;
        else
-               dir = -EINVAL;
-       kfree(trimmed);
-       if (dir < 0) {
-               mutex_unlock(&dev->lock);
-               return dir;
-       }
+               return -EINVAL;
  
        hog->dir = dir;
  
-       mutex_unlock(&dev->lock);
        return count;
  }
  
@@@ -1339,9 -1249,8 +1263,8 @@@ static void gpio_sim_hog_config_item_re
        struct gpio_sim_line *line = hog->parent;
        struct gpio_sim_device *dev = gpio_sim_hog_get_device(hog);
  
-       mutex_lock(&dev->lock);
-       line->hog = NULL;
-       mutex_unlock(&dev->lock);
+       scoped_guard(mutex, &dev->lock)
+               line->hog = NULL;
  
        kfree(hog->name);
        kfree(hog);
@@@ -1367,13 -1276,11 +1290,11 @@@ gpio_sim_line_config_make_hog_item(stru
        if (strcmp(name, "hog") != 0)
                return ERR_PTR(-EINVAL);
  
-       mutex_lock(&dev->lock);
+       guard(mutex)(&dev->lock);
  
        hog = kzalloc(sizeof(*hog), GFP_KERNEL);
-       if (!hog) {
-               mutex_unlock(&dev->lock);
+       if (!hog)
                return ERR_PTR(-ENOMEM);
-       }
  
        config_item_init_type_name(&hog->item, name,
                                   &gpio_sim_hog_config_type);
        hog->parent = line;
        line->hog = hog;
  
-       mutex_unlock(&dev->lock);
        return &hog->item;
  }
  
@@@ -1393,9 -1298,8 +1312,8 @@@ static void gpio_sim_line_config_group_
        struct gpio_sim_line *line = to_gpio_sim_line(item);
        struct gpio_sim_device *dev = gpio_sim_line_get_device(line);
  
-       mutex_lock(&dev->lock);
-       list_del(&line->siblings);
-       mutex_unlock(&dev->lock);
+       scoped_guard(mutex, &dev->lock)
+               list_del(&line->siblings);
  
        kfree(line->name);
        kfree(line);
@@@ -1430,18 -1334,14 +1348,14 @@@ gpio_sim_bank_config_make_line_group(st
        if (ret != 1 || nchar != strlen(name))
                return ERR_PTR(-EINVAL);
  
-       mutex_lock(&dev->lock);
+       guard(mutex)(&dev->lock);
  
-       if (gpio_sim_device_is_live_unlocked(dev)) {
-               mutex_unlock(&dev->lock);
+       if (gpio_sim_device_is_live_unlocked(dev))
                return ERR_PTR(-EBUSY);
-       }
  
        line = kzalloc(sizeof(*line), GFP_KERNEL);
-       if (!line) {
-               mutex_unlock(&dev->lock);
+       if (!line)
                return ERR_PTR(-ENOMEM);
-       }
  
        config_group_init_type_name(&line->group, name,
                                    &gpio_sim_line_config_type);
        line->offset = offset;
        list_add_tail(&line->siblings, &bank->line_list);
  
-       mutex_unlock(&dev->lock);
        return &line->group;
  }
  
@@@ -1460,9 -1358,8 +1372,8 @@@ static void gpio_sim_bank_config_group_
        struct gpio_sim_bank *bank = to_gpio_sim_bank(item);
        struct gpio_sim_device *dev = gpio_sim_bank_get_device(bank);
  
-       mutex_lock(&dev->lock);
-       list_del(&bank->siblings);
-       mutex_unlock(&dev->lock);
+       scoped_guard(mutex, &dev->lock)
+               list_del(&bank->siblings);
  
        kfree(bank->label);
        kfree(bank);
@@@ -1490,18 -1387,14 +1401,14 @@@ gpio_sim_device_config_make_bank_group(
        struct gpio_sim_device *dev = to_gpio_sim_device(&group->cg_item);
        struct gpio_sim_bank *bank;
  
-       mutex_lock(&dev->lock);
+       guard(mutex)(&dev->lock);
  
-       if (gpio_sim_device_is_live_unlocked(dev)) {
-               mutex_unlock(&dev->lock);
+       if (gpio_sim_device_is_live_unlocked(dev))
                return ERR_PTR(-EBUSY);
-       }
  
        bank = kzalloc(sizeof(*bank), GFP_KERNEL);
-       if (!bank) {
-               mutex_unlock(&dev->lock);
+       if (!bank)
                return ERR_PTR(-ENOMEM);
-       }
  
        config_group_init_type_name(&bank->group, name,
                                    &gpio_sim_bank_config_group_type);
        INIT_LIST_HEAD(&bank->line_list);
        list_add_tail(&bank->siblings, &dev->bank_list);
  
-       mutex_unlock(&dev->lock);
        return &bank->group;
  }
  
@@@ -1519,10 -1410,10 +1424,10 @@@ static void gpio_sim_device_config_grou
  {
        struct gpio_sim_device *dev = to_gpio_sim_device(item);
  
-       mutex_lock(&dev->lock);
-       if (gpio_sim_device_is_live_unlocked(dev))
-               gpio_sim_device_deactivate_unlocked(dev);
-       mutex_unlock(&dev->lock);
+       scoped_guard(mutex, &dev->lock) {
+               if (gpio_sim_device_is_live_unlocked(dev))
+                       gpio_sim_device_deactivate_unlocked(dev);
+       }
  
        mutex_destroy(&dev->lock);
        ida_free(&gpio_sim_ida, dev->id);
@@@ -1547,7 -1438,7 +1452,7 @@@ static const struct config_item_type gp
  static struct config_group *
  gpio_sim_config_make_device_group(struct config_group *group, const char *name)
  {
-       struct gpio_sim_device *dev;
+       struct gpio_sim_device *dev __free(kfree) = NULL;
        int id;
  
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
                return ERR_PTR(-ENOMEM);
  
        id = ida_alloc(&gpio_sim_ida, GFP_KERNEL);
-       if (id < 0) {
-               kfree(dev);
+       if (id < 0)
                return ERR_PTR(id);
-       }
  
        config_group_init_type_name(&dev->group, name,
                                    &gpio_sim_device_config_group_type);
        dev->bus_notifier.notifier_call = gpio_sim_bus_notifier_call;
        init_completion(&dev->probe_completion);
  
-       return &dev->group;
+       return &no_free_ptr(dev)->group;
  }
  
  static struct configfs_group_operations gpio_sim_config_group_ops = {
@@@ -3,22 -3,21 +3,21 @@@
   * GPIO driver for the WinSystems WS16C48
   * Copyright (C) 2016 William Breathitt Gray
   */
- #include <linux/bitmap.h>
+ #include <linux/bitfield.h>
+ #include <linux/bits.h>
  #include <linux/device.h>
- #include <linux/errno.h>
- #include <linux/gpio/driver.h>
- #include <linux/io.h>
- #include <linux/ioport.h>
- #include <linux/interrupt.h>
- #include <linux/irqdesc.h>
+ #include <linux/err.h>
+ #include <linux/gpio/regmap.h>
+ #include <linux/irq.h>
  #include <linux/isa.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/moduleparam.h>
  #include <linux/spinlock.h>
+ #include <linux/regmap.h>
  #include <linux/types.h>
  
 -#define WS16C48_EXTENT 10
 +#define WS16C48_EXTENT 11
  #define MAX_NUM_WS16C48 max_num_isa_dev(WS16C48_EXTENT)
  
  static unsigned int base[MAX_NUM_WS16C48];
@@@ -31,371 -30,178 +30,178 @@@ static unsigned int num_irq
  module_param_hw_array(irq, uint, irq, &num_irq, 0);
  MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers");
  
- /**
-  * struct ws16c48_reg - device register structure
-  * @port:             Port 0 through 5 I/O
-  * @int_pending:      Interrupt Pending
-  * @page_lock:                Register page (Bits 7-6) and I/O port lock (Bits 5-0)
-  * @pol_enab_int_id:  Interrupt polarity, enable, and ID
-  */
- struct ws16c48_reg {
-       u8 port[6];
-       u8 int_pending;
-       u8 page_lock;
-       u8 pol_enab_int_id[3];
+ #define WS16C48_DAT_BASE 0x0
+ #define WS16C48_PAGE_LOCK 0x7
+ #define WS16C48_PAGE_BASE 0x8
+ #define WS16C48_POL WS16C48_PAGE_BASE
+ #define WS16C48_ENAB WS16C48_PAGE_BASE
+ #define WS16C48_INT_ID WS16C48_PAGE_BASE
+ #define PAGE_LOCK_PAGE_FIELD GENMASK(7, 6)
+ #define POL_PAGE u8_encode_bits(1, PAGE_LOCK_PAGE_FIELD)
+ #define ENAB_PAGE u8_encode_bits(2, PAGE_LOCK_PAGE_FIELD)
+ #define INT_ID_PAGE u8_encode_bits(3, PAGE_LOCK_PAGE_FIELD)
+ static const struct regmap_range ws16c48_wr_ranges[] = {
+       regmap_reg_range(0x0, 0x5), regmap_reg_range(0x7, 0xA),
+ };
+ static const struct regmap_range ws16c48_rd_ranges[] = {
+       regmap_reg_range(0x0, 0xA),
+ };
+ static const struct regmap_range ws16c48_volatile_ranges[] = {
+       regmap_reg_range(0x0, 0x6), regmap_reg_range(0x8, 0xA),
+ };
+ static const struct regmap_access_table ws16c48_wr_table = {
+       .yes_ranges = ws16c48_wr_ranges,
+       .n_yes_ranges = ARRAY_SIZE(ws16c48_wr_ranges),
+ };
+ static const struct regmap_access_table ws16c48_rd_table = {
+       .yes_ranges = ws16c48_rd_ranges,
+       .n_yes_ranges = ARRAY_SIZE(ws16c48_rd_ranges),
+ };
+ static const struct regmap_access_table ws16c48_volatile_table = {
+       .yes_ranges = ws16c48_volatile_ranges,
+       .n_yes_ranges = ARRAY_SIZE(ws16c48_volatile_ranges),
+ };
+ static const struct regmap_config ws16c48_regmap_config = {
+       .reg_bits = 8,
+       .reg_stride = 1,
+       .val_bits = 8,
+       .io_port = true,
+       .wr_table = &ws16c48_wr_table,
+       .rd_table = &ws16c48_rd_table,
+       .volatile_table = &ws16c48_volatile_table,
+       .cache_type = REGCACHE_FLAT,
+       .use_raw_spinlock = true,
+ };
+ #define WS16C48_NGPIO_PER_REG 8
+ #define WS16C48_REGMAP_IRQ(_id)                                                       \
+       [_id] = {                                                               \
+               .reg_offset = (_id) / WS16C48_NGPIO_PER_REG,                    \
+               .mask = BIT((_id) % WS16C48_NGPIO_PER_REG),                     \
+               .type = {                                                       \
+                       .type_reg_offset = (_id) / WS16C48_NGPIO_PER_REG,       \
+                       .types_supported = IRQ_TYPE_EDGE_BOTH,                  \
+               },                                                              \
+       }
+ /* Only the first 24 lines (Port 0-2) support interrupts */
+ #define WS16C48_NUM_IRQS 24
+ static const struct regmap_irq ws16c48_regmap_irqs[WS16C48_NUM_IRQS] = {
+       WS16C48_REGMAP_IRQ(0), WS16C48_REGMAP_IRQ(1), WS16C48_REGMAP_IRQ(2), /* 0-2 */
+       WS16C48_REGMAP_IRQ(3), WS16C48_REGMAP_IRQ(4), WS16C48_REGMAP_IRQ(5), /* 3-5 */
+       WS16C48_REGMAP_IRQ(6), WS16C48_REGMAP_IRQ(7), WS16C48_REGMAP_IRQ(8), /* 6-8 */
+       WS16C48_REGMAP_IRQ(9), WS16C48_REGMAP_IRQ(10), WS16C48_REGMAP_IRQ(11), /* 9-11 */
+       WS16C48_REGMAP_IRQ(12), WS16C48_REGMAP_IRQ(13), WS16C48_REGMAP_IRQ(14), /* 12-14 */
+       WS16C48_REGMAP_IRQ(15), WS16C48_REGMAP_IRQ(16), WS16C48_REGMAP_IRQ(17), /* 15-17 */
+       WS16C48_REGMAP_IRQ(18), WS16C48_REGMAP_IRQ(19), WS16C48_REGMAP_IRQ(20), /* 18-20 */
+       WS16C48_REGMAP_IRQ(21), WS16C48_REGMAP_IRQ(22), WS16C48_REGMAP_IRQ(23), /* 21-23 */
  };
  
  /**
   * struct ws16c48_gpio - GPIO device private data structure
-  * @chip:     instance of the gpio_chip
-  * @io_state: bit I/O state (whether bit is set to input or output)
-  * @out_state:        output bits state
+  * @map:      regmap for the device
   * @lock:     synchronization lock to prevent I/O race conditions
   * @irq_mask: I/O bits affected by interrupts
-  * @flow_mask:        IRQ flow type mask for the respective I/O bits
-  * @reg:      I/O address offset for the device registers
   */
  struct ws16c48_gpio {
-       struct gpio_chip chip;
-       unsigned char io_state[6];
-       unsigned char out_state[6];
+       struct regmap *map;
        raw_spinlock_t lock;
-       unsigned long irq_mask;
-       unsigned long flow_mask;
-       struct ws16c48_reg __iomem *reg;
+       u8 irq_mask[WS16C48_NUM_IRQS / WS16C48_NGPIO_PER_REG];
  };
  
- static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset)
+ static int ws16c48_handle_pre_irq(void *const irq_drv_data) __acquires(&ws16c48gpio->lock)
  {
-       struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
-       const unsigned port = offset / 8;
-       const unsigned mask = BIT(offset % 8);
+       struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
  
-       if (ws16c48gpio->io_state[port] & mask)
-               return GPIO_LINE_DIRECTION_IN;
-       return GPIO_LINE_DIRECTION_OUT;
- }
- static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
- {
-       struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
-       const unsigned port = offset / 8;
-       const unsigned mask = BIT(offset % 8);
-       unsigned long flags;
-       raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
-       ws16c48gpio->io_state[port] |= mask;
-       ws16c48gpio->out_state[port] &= ~mask;
-       iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port);
-       raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+       /* Lock to prevent Page/Lock register change while we handle IRQ */
+       raw_spin_lock(&ws16c48gpio->lock);
  
        return 0;
  }
  
- static int ws16c48_gpio_direction_output(struct gpio_chip *chip,
-       unsigned offset, int value)
+ static int ws16c48_handle_post_irq(void *const irq_drv_data) __releases(&ws16c48gpio->lock)
  {
-       struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
-       const unsigned port = offset / 8;
-       const unsigned mask = BIT(offset % 8);
-       unsigned long flags;
-       raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
+       struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
  
-       ws16c48gpio->io_state[port] &= ~mask;
-       if (value)
-               ws16c48gpio->out_state[port] |= mask;
-       else
-               ws16c48gpio->out_state[port] &= ~mask;
-       iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port);
-       raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+       raw_spin_unlock(&ws16c48gpio->lock);
  
        return 0;
  }
  
- static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset)
- {
-       struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
-       const unsigned port = offset / 8;
-       const unsigned mask = BIT(offset % 8);
-       unsigned long flags;
-       unsigned port_state;
-       raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
-       /* ensure that GPIO is set for input */
-       if (!(ws16c48gpio->io_state[port] & mask)) {
-               raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
-               return -EINVAL;
-       }
-       port_state = ioread8(ws16c48gpio->reg->port + port);
-       raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
-       return !!(port_state & mask);
- }
- static int ws16c48_gpio_get_multiple(struct gpio_chip *chip,
-       unsigned long *mask, unsigned long *bits)
- {
-       struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
-       unsigned long offset;
-       unsigned long gpio_mask;
-       size_t index;
-       u8 __iomem *port_addr;
-       unsigned long port_state;
-       /* clear bits array to a clean slate */
-       bitmap_zero(bits, chip->ngpio);
-       for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) {
-               index = offset / 8;
-               port_addr = ws16c48gpio->reg->port + index;
-               port_state = ioread8(port_addr) & gpio_mask;
-               bitmap_set_value8(bits, port_state, offset);
-       }
-       return 0;
- }
- static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
- {
-       struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
-       const unsigned port = offset / 8;
-       const unsigned mask = BIT(offset % 8);
-       unsigned long flags;
-       raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
-       /* ensure that GPIO is set for output */
-       if (ws16c48gpio->io_state[port] & mask) {
-               raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
-               return;
-       }
-       if (value)
-               ws16c48gpio->out_state[port] |= mask;
-       else
-               ws16c48gpio->out_state[port] &= ~mask;
-       iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port);
-       raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
- }
- static void ws16c48_gpio_set_multiple(struct gpio_chip *chip,
-       unsigned long *mask, unsigned long *bits)
- {
-       struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
-       unsigned long offset;
-       unsigned long gpio_mask;
-       size_t index;
-       u8 __iomem *port_addr;
-       unsigned long bitmask;
-       unsigned long flags;
-       for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) {
-               index = offset / 8;
-               port_addr = ws16c48gpio->reg->port + index;
-               /* mask out GPIO configured for input */
-               gpio_mask &= ~ws16c48gpio->io_state[index];
-               bitmask = bitmap_get_value8(bits, offset) & gpio_mask;
-               raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
-               /* update output state data and set device gpio register */
-               ws16c48gpio->out_state[index] &= ~gpio_mask;
-               ws16c48gpio->out_state[index] |= bitmask;
-               iowrite8(ws16c48gpio->out_state[index], port_addr);
-               raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
-       }
- }
- static void ws16c48_irq_ack(struct irq_data *data)
+ static int ws16c48_handle_mask_sync(const int index, const unsigned int mask_buf_def,
+                                   const unsigned int mask_buf, void *const irq_drv_data)
  {
-       struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
-       struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
-       const unsigned long offset = irqd_to_hwirq(data);
-       const unsigned port = offset / 8;
-       const unsigned mask = BIT(offset % 8);
+       struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
        unsigned long flags;
-       unsigned port_state;
-       /* only the first 3 ports support interrupts */
-       if (port > 2)
-               return;
+       int ret = 0;
  
        raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
  
-       port_state = ws16c48gpio->irq_mask >> (8*port);
+       /* exit early if no change since the last mask sync */
+       if (mask_buf == ws16c48gpio->irq_mask[index])
+               goto exit_unlock;
+       ws16c48gpio->irq_mask[index] = mask_buf;
  
-       /* Select Register Page 2; Unlock all I/O ports */
-       iowrite8(0x80, &ws16c48gpio->reg->page_lock);
+       ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, ENAB_PAGE);
+       if (ret)
+               goto exit_unlock;
  
-       /* Clear pending interrupt */
-       iowrite8(port_state & ~mask, ws16c48gpio->reg->pol_enab_int_id + port);
-       iowrite8(port_state | mask, ws16c48gpio->reg->pol_enab_int_id + port);
+       /* Update ENAB register (inverted mask) */
+       ret = regmap_write(ws16c48gpio->map, WS16C48_ENAB + index, ~mask_buf);
+       if (ret)
+               goto exit_unlock;
  
-       /* Select Register Page 3; Unlock all I/O ports */
-       iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
+       ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, INT_ID_PAGE);
+       if (ret)
+               goto exit_unlock;
  
+ exit_unlock:
        raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
- }
- static void ws16c48_irq_mask(struct irq_data *data)
- {
-       struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
-       struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
-       const unsigned long offset = irqd_to_hwirq(data);
-       const unsigned long mask = BIT(offset);
-       const unsigned port = offset / 8;
-       unsigned long flags;
-       unsigned long port_state;
-       /* only the first 3 ports support interrupts */
-       if (port > 2)
-               return;
-       raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
  
-       ws16c48gpio->irq_mask &= ~mask;
-       gpiochip_disable_irq(chip, offset);
-       port_state = ws16c48gpio->irq_mask >> (8 * port);
-       /* Select Register Page 2; Unlock all I/O ports */
-       iowrite8(0x80, &ws16c48gpio->reg->page_lock);
-       /* Disable interrupt */
-       iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);
-       /* Select Register Page 3; Unlock all I/O ports */
-       iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
-       raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
+       return ret;
  }
  
- static void ws16c48_irq_unmask(struct irq_data *data)
+ static int ws16c48_set_type_config(unsigned int **const buf, const unsigned int type,
+                                  const struct regmap_irq *const irq_data, const int idx,
+                                  void *const irq_drv_data)
  {
-       struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
-       struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
-       const unsigned long offset = irqd_to_hwirq(data);
-       const unsigned long mask = BIT(offset);
-       const unsigned port = offset / 8;
+       struct ws16c48_gpio *const ws16c48gpio = irq_drv_data;
+       unsigned int polarity;
        unsigned long flags;
-       unsigned long port_state;
-       /* only the first 3 ports support interrupts */
-       if (port > 2)
-               return;
-       raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
-       gpiochip_enable_irq(chip, offset);
-       ws16c48gpio->irq_mask |= mask;
-       port_state = ws16c48gpio->irq_mask >> (8 * port);
-       /* Select Register Page 2; Unlock all I/O ports */
-       iowrite8(0x80, &ws16c48gpio->reg->page_lock);
-       /* Enable interrupt */
-       iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);
-       /* Select Register Page 3; Unlock all I/O ports */
-       iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
-       raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
- }
+       int ret;
  
- static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type)
- {
-       struct gpio_chip *chip = irq_data_get_irq_chip_data(data);
-       struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip);
-       const unsigned long offset = irqd_to_hwirq(data);
-       const unsigned long mask = BIT(offset);
-       const unsigned port = offset / 8;
-       unsigned long flags;
-       unsigned long port_state;
-       /* only the first 3 ports support interrupts */
-       if (port > 2)
-               return -EINVAL;
-       raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
-       switch (flow_type) {
-       case IRQ_TYPE_NONE:
-               break;
+       switch (type) {
        case IRQ_TYPE_EDGE_RISING:
-               ws16c48gpio->flow_mask |= mask;
+               polarity = irq_data->mask;
                break;
        case IRQ_TYPE_EDGE_FALLING:
-               ws16c48gpio->flow_mask &= ~mask;
+               polarity = 0;
                break;
        default:
-               raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
                return -EINVAL;
        }
  
-       port_state = ws16c48gpio->flow_mask >> (8 * port);
+       raw_spin_lock_irqsave(&ws16c48gpio->lock, flags);
  
-       /* Select Register Page 1; Unlock all I/O ports */
-       iowrite8(0x40, &ws16c48gpio->reg->page_lock);
+       ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, POL_PAGE);
+       if (ret)
+               goto exit_unlock;
  
        /* Set interrupt polarity */
-       iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port);
+       ret = regmap_update_bits(ws16c48gpio->map, WS16C48_POL + idx, irq_data->mask, polarity);
+       if (ret)
+               goto exit_unlock;
  
-       /* Select Register Page 3; Unlock all I/O ports */
-       iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
+       ret = regmap_write(ws16c48gpio->map, WS16C48_PAGE_LOCK, INT_ID_PAGE);
+       if (ret)
+               goto exit_unlock;
  
+ exit_unlock:
        raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags);
  
-       return 0;
- }
- static const struct irq_chip ws16c48_irqchip = {
-       .name = "ws16c48",
-       .irq_ack = ws16c48_irq_ack,
-       .irq_mask = ws16c48_irq_mask,
-       .irq_unmask = ws16c48_irq_unmask,
-       .irq_set_type = ws16c48_irq_set_type,
-       .flags = IRQCHIP_IMMUTABLE,
-       GPIOCHIP_IRQ_RESOURCE_HELPERS,
- };
- static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id)
- {
-       struct ws16c48_gpio *const ws16c48gpio = dev_id;
-       struct gpio_chip *const chip = &ws16c48gpio->chip;
-       struct ws16c48_reg __iomem *const reg = ws16c48gpio->reg;
-       unsigned long int_pending;
-       unsigned long port;
-       unsigned long int_id;
-       unsigned long gpio;
-       int_pending = ioread8(&reg->int_pending) & 0x7;
-       if (!int_pending)
-               return IRQ_NONE;
-       /* loop until all pending interrupts are handled */
-       do {
-               for_each_set_bit(port, &int_pending, 3) {
-                       int_id = ioread8(reg->pol_enab_int_id + port);
-                       for_each_set_bit(gpio, &int_id, 8)
-                               generic_handle_domain_irq(chip->irq.domain,
-                                                         gpio + 8*port);
-               }
-               int_pending = ioread8(&reg->int_pending) & 0x7;
-       } while (int_pending);
-       return IRQ_HANDLED;
+       return ret;
  }
  
  #define WS16C48_NGPIO 48
@@@ -414,30 -220,37 +220,37 @@@ static const char *ws16c48_names[WS16C4
        "Port 5 Bit 4", "Port 5 Bit 5", "Port 5 Bit 6", "Port 5 Bit 7"
  };
  
- static int ws16c48_irq_init_hw(struct gpio_chip *gc)
+ static int ws16c48_irq_init_hw(struct regmap *const map)
  {
-       struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc);
+       int err;
  
-       /* Select Register Page 2; Unlock all I/O ports */
-       iowrite8(0x80, &ws16c48gpio->reg->page_lock);
+       err = regmap_write(map, WS16C48_PAGE_LOCK, ENAB_PAGE);
+       if (err)
+               return err;
  
        /* Disable interrupts for all lines */
-       iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[0]);
-       iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[1]);
-       iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[2]);
-       /* Select Register Page 3; Unlock all I/O ports */
-       iowrite8(0xC0, &ws16c48gpio->reg->page_lock);
+       err = regmap_write(map, WS16C48_ENAB + 0, 0x00);
+       if (err)
+               return err;
+       err = regmap_write(map, WS16C48_ENAB + 1, 0x00);
+       if (err)
+               return err;
+       err = regmap_write(map, WS16C48_ENAB + 2, 0x00);
+       if (err)
+               return err;
  
-       return 0;
+       return regmap_write(map, WS16C48_PAGE_LOCK, INT_ID_PAGE);
  }
  
  static int ws16c48_probe(struct device *dev, unsigned int id)
  {
        struct ws16c48_gpio *ws16c48gpio;
        const char *const name = dev_name(dev);
-       struct gpio_irq_chip *girq;
        int err;
+       struct gpio_regmap_config gpio_config = {};
+       void __iomem *regs;
+       struct regmap_irq_chip *chip;
+       struct regmap_irq_chip_data *chip_data;
  
        ws16c48gpio = devm_kzalloc(dev, sizeof(*ws16c48gpio), GFP_KERNEL);
        if (!ws16c48gpio)
                return -EBUSY;
        }
  
-       ws16c48gpio->reg = devm_ioport_map(dev, base[id], WS16C48_EXTENT);
-       if (!ws16c48gpio->reg)
+       regs = devm_ioport_map(dev, base[id], WS16C48_EXTENT);
+       if (!regs)
                return -ENOMEM;
  
-       ws16c48gpio->chip.label = name;
-       ws16c48gpio->chip.parent = dev;
-       ws16c48gpio->chip.owner = THIS_MODULE;
-       ws16c48gpio->chip.base = -1;
-       ws16c48gpio->chip.ngpio = WS16C48_NGPIO;
-       ws16c48gpio->chip.names = ws16c48_names;
-       ws16c48gpio->chip.get_direction = ws16c48_gpio_get_direction;
-       ws16c48gpio->chip.direction_input = ws16c48_gpio_direction_input;
-       ws16c48gpio->chip.direction_output = ws16c48_gpio_direction_output;
-       ws16c48gpio->chip.get = ws16c48_gpio_get;
-       ws16c48gpio->chip.get_multiple = ws16c48_gpio_get_multiple;
-       ws16c48gpio->chip.set = ws16c48_gpio_set;
-       ws16c48gpio->chip.set_multiple = ws16c48_gpio_set_multiple;
-       girq = &ws16c48gpio->chip.irq;
-       gpio_irq_chip_set_chip(girq, &ws16c48_irqchip);
-       /* 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_edge_irq;
-       girq->init_hw = ws16c48_irq_init_hw;
+       ws16c48gpio->map = devm_regmap_init_mmio(dev, regs, &ws16c48_regmap_config);
+       if (IS_ERR(ws16c48gpio->map))
+               return dev_err_probe(dev, PTR_ERR(ws16c48gpio->map),
+                                    "Unable to initialize register map\n");
  
-       raw_spin_lock_init(&ws16c48gpio->lock);
+       chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return -ENOMEM;
  
-       err = devm_gpiochip_add_data(dev, &ws16c48gpio->chip, ws16c48gpio);
-       if (err) {
-               dev_err(dev, "GPIO registering failed (%d)\n", err);
-               return err;
-       }
+       chip->name = name;
+       chip->status_base = WS16C48_INT_ID;
+       chip->mask_base = WS16C48_ENAB;
+       chip->ack_base = WS16C48_INT_ID;
+       chip->num_regs = 3;
+       chip->irqs = ws16c48_regmap_irqs;
+       chip->num_irqs = ARRAY_SIZE(ws16c48_regmap_irqs);
+       chip->handle_pre_irq = ws16c48_handle_pre_irq;
+       chip->handle_post_irq = ws16c48_handle_post_irq;
+       chip->handle_mask_sync = ws16c48_handle_mask_sync;
+       chip->set_type_config = ws16c48_set_type_config;
+       chip->irq_drv_data = ws16c48gpio;
  
-       err = devm_request_irq(dev, irq[id], ws16c48_irq_handler, IRQF_SHARED,
-               name, ws16c48gpio);
-       if (err) {
-               dev_err(dev, "IRQ handler registering failed (%d)\n", err);
+       raw_spin_lock_init(&ws16c48gpio->lock);
+       /* Initialize to prevent spurious interrupts before we're ready */
+       err = ws16c48_irq_init_hw(ws16c48gpio->map);
+       if (err)
                return err;
-       }
  
-       return 0;
+       err = devm_regmap_add_irq_chip(dev, ws16c48gpio->map, irq[id], 0, 0, chip, &chip_data);
+       if (err)
+               return dev_err_probe(dev, err, "IRQ registration failed\n");
+       gpio_config.parent = dev;
+       gpio_config.regmap = ws16c48gpio->map;
+       gpio_config.ngpio = WS16C48_NGPIO;
+       gpio_config.names = ws16c48_names;
+       gpio_config.reg_dat_base = GPIO_REGMAP_ADDR(WS16C48_DAT_BASE);
+       gpio_config.reg_set_base = GPIO_REGMAP_ADDR(WS16C48_DAT_BASE);
+       /* Setting a GPIO to 0 allows it to be used as an input */
+       gpio_config.reg_dir_out_base = GPIO_REGMAP_ADDR(WS16C48_DAT_BASE);
+       gpio_config.ngpio_per_reg = WS16C48_NGPIO_PER_REG;
+       gpio_config.irq_domain = regmap_irq_get_domain(chip_data);
+       return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &gpio_config));
  }
  
  static struct isa_driver ws16c48_driver = {
@@@ -209,8 -209,6 +209,8 @@@ static void of_gpio_set_polarity_by_pro
                                             const char *propname,
                                             enum of_gpio_flags *flags)
  {
 +      const struct device_node *np_compat = np;
 +      const struct device_node *np_propname = np;
        static const struct {
                const char *compatible;
                const char *gpio_propname;
                { "regulator-gpio",    "enable-gpio",  "enable-active-high" },
                { "regulator-gpio",    "enable-gpios", "enable-active-high" },
  #endif
 +#if IS_ENABLED(CONFIG_MMC_ATMELMCI)
 +              { "atmel,hsmci",       "cd-gpios",     "cd-inverted" },
 +#endif
        };
        unsigned int i;
        bool active_high;
  
 +#if IS_ENABLED(CONFIG_MMC_ATMELMCI)
 +      /*
 +       * The Atmel HSMCI has compatible property in the parent node and
 +       * gpio property in a child node
 +       */
 +      if (of_device_is_compatible(np->parent, "atmel,hsmci")) {
 +              np_compat = np->parent;
 +              np_propname = np;
 +      }
 +#endif
 +
        for (i = 0; i < ARRAY_SIZE(gpios); i++) {
 -              if (of_device_is_compatible(np, gpios[i].compatible) &&
 +              if (of_device_is_compatible(np_compat, gpios[i].compatible) &&
                    !strcmp(propname, gpios[i].gpio_propname)) {
 -                      active_high = of_property_read_bool(np,
 +                      active_high = of_property_read_bool(np_propname,
                                                gpios[i].polarity_propname);
                        of_gpio_quirk_polarity(np, active_high, flags);
                        break;
@@@ -1094,16 -1078,16 +1094,16 @@@ int of_gpiochip_add(struct gpio_chip *c
        if (ret)
                return ret;
  
-       fwnode_handle_get(chip->fwnode);
+       of_node_get(np);
  
        ret = of_gpiochip_scan_gpios(chip);
        if (ret)
-               fwnode_handle_put(chip->fwnode);
+               of_node_put(np);
  
        return ret;
  }
  
  void of_gpiochip_remove(struct gpio_chip *chip)
  {
-       fwnode_handle_put(chip->fwnode);
+       of_node_put(dev_of_node(&chip->gpiodev->dev));
  }
diff --combined drivers/gpio/gpiolib.c
@@@ -700,6 -700,40 +700,40 @@@ void *gpiochip_get_data(struct gpio_chi
  }
  EXPORT_SYMBOL_GPL(gpiochip_get_data);
  
+ int gpiochip_get_ngpios(struct gpio_chip *gc, struct device *dev)
+ {
+       u32 ngpios = gc->ngpio;
+       int ret;
+       if (ngpios == 0) {
+               ret = device_property_read_u32(dev, "ngpios", &ngpios);
+               if (ret == -ENODATA)
+                       /*
+                        * -ENODATA means that there is no property found and
+                        * we want to issue the error message to the user.
+                        * Besides that, we want to return different error code
+                        * to state that supplied value is not valid.
+                        */
+                       ngpios = 0;
+               else if (ret)
+                       return ret;
+               gc->ngpio = ngpios;
+       }
+       if (gc->ngpio == 0) {
+               chip_err(gc, "tried to insert a GPIO chip with zero lines\n");
+               return -EINVAL;
+       }
+       if (gc->ngpio > FASTPATH_NGPIO)
+               chip_warn(gc, "line cnt %u is greater than fast path cnt %u\n",
+                       gc->ngpio, FASTPATH_NGPIO);
+       return 0;
+ }
+ EXPORT_SYMBOL_GPL(gpiochip_get_ngpios);
  int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
                               struct lock_class_key *lock_key,
                               struct lock_class_key *request_key)
        struct gpio_device *gdev;
        unsigned long flags;
        unsigned int i;
-       u32 ngpios = 0;
        int base = 0;
        int ret = 0;
  
        /*
-        * If the calling driver did not initialize firmware node, do it here
-        * using the parent device, if any.
-        */
-       if (!gc->fwnode && gc->parent)
-               gc->fwnode = dev_fwnode(gc->parent);
-       /*
         * First: allocate and populate the internal stat container, and
         * set up the struct device.
         */
        gc->gpiodev = gdev;
        gpiochip_set_data(gc, data);
  
-       device_set_node(&gdev->dev, gc->fwnode);
+       /*
+        * If the calling driver did not initialize firmware node,
+        * do it here using the parent device, if any.
+        */
+       if (gc->fwnode)
+               device_set_node(&gdev->dev, gc->fwnode);
+       else if (gc->parent)
+               device_set_node(&gdev->dev, dev_fwnode(gc->parent));
  
        gdev->id = ida_alloc(&gpio_ida, GFP_KERNEL);
        if (gdev->id < 0) {
        else
                gdev->owner = THIS_MODULE;
  
-       /*
-        * Try the device properties if the driver didn't supply the number
-        * of GPIO lines.
-        */
-       ngpios = gc->ngpio;
-       if (ngpios == 0) {
-               ret = device_property_read_u32(&gdev->dev, "ngpios", &ngpios);
-               if (ret == -ENODATA)
-                       /*
-                        * -ENODATA means that there is no property found and
-                        * we want to issue the error message to the user.
-                        * Besides that, we want to return different error code
-                        * to state that supplied value is not valid.
-                        */
-                       ngpios = 0;
-               else if (ret)
-                       goto err_free_dev_name;
-               gc->ngpio = ngpios;
-       }
-       if (gc->ngpio == 0) {
-               chip_err(gc, "tried to insert a GPIO chip with zero lines\n");
-               ret = -EINVAL;
+       ret = gpiochip_get_ngpios(gc, &gdev->dev);
+       if (ret)
                goto err_free_dev_name;
-       }
-       if (gc->ngpio > FASTPATH_NGPIO)
-               chip_warn(gc, "line cnt %u is greater than fast path cnt %u\n",
-                         gc->ngpio, FASTPATH_NGPIO);
  
        gdev->descs = kcalloc(gc->ngpio, sizeof(*gdev->descs), GFP_KERNEL);
        if (!gdev->descs) {
  
        spin_unlock_irqrestore(&gpio_lock, flags);
  
-       BLOCKING_INIT_NOTIFIER_HEAD(&gdev->notifier);
+       BLOCKING_INIT_NOTIFIER_HEAD(&gdev->line_state_notifier);
+       BLOCKING_INIT_NOTIFIER_HEAD(&gdev->device_notifier);
        init_rwsem(&gdev->sem);
  
  #ifdef CONFIG_PINCTRL
@@@ -947,7 -954,7 +954,7 @@@ err_print_message
        /* failures here can mean systems won't boot... */
        if (ret != -EPROBE_DEFER) {
                pr_err("%s: GPIOs %d..%d (%s) failed to register, %d\n", __func__,
-                      base, base + (int)ngpios - 1,
+                      base, base + (int)gc->ngpio - 1,
                       gc->label ? : "generic", ret);
        }
        return ret;
@@@ -1292,12 -1299,14 +1299,14 @@@ static void gpiochip_hierarchy_setup_do
                ops->free = irq_domain_free_irqs_common;
  }
  
- static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
+ static struct irq_domain *gpiochip_hierarchy_create_domain(struct gpio_chip *gc)
  {
+       struct irq_domain *domain;
        if (!gc->irq.child_to_parent_hwirq ||
            !gc->irq.fwnode) {
                chip_err(gc, "missing irqdomain vital data\n");
-               return -EINVAL;
+               return ERR_PTR(-EINVAL);
        }
  
        if (!gc->irq.child_offset_to_irq)
  
        gpiochip_hierarchy_setup_domain_ops(&gc->irq.child_irq_domain_ops);
  
-       gc->irq.domain = irq_domain_create_hierarchy(
+       domain = irq_domain_create_hierarchy(
                gc->irq.parent_domain,
                0,
                gc->ngpio,
                &gc->irq.child_irq_domain_ops,
                gc);
  
-       if (!gc->irq.domain)
-               return -ENOMEM;
+       if (!domain)
+               return ERR_PTR(-ENOMEM);
  
        gpiochip_set_hierarchical_irqchip(gc, gc->irq.chip);
  
-       return 0;
+       return domain;
  }
  
  static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
@@@ -1366,9 -1375,9 +1375,9 @@@ EXPORT_SYMBOL_GPL(gpiochip_populate_par
  
  #else
  
- static int gpiochip_hierarchy_add_domain(struct gpio_chip *gc)
+ static struct irq_domain *gpiochip_hierarchy_create_domain(struct gpio_chip *gc)
  {
-       return -EINVAL;
+       return ERR_PTR(-EINVAL);
  }
  
  static bool gpiochip_hierarchy_is_hierarchical(struct gpio_chip *gc)
@@@ -1445,6 -1454,19 +1454,19 @@@ static const struct irq_domain_ops gpio
        .xlate  = irq_domain_xlate_twocell,
  };
  
+ static struct irq_domain *gpiochip_simple_create_domain(struct gpio_chip *gc)
+ {
+       struct fwnode_handle *fwnode = dev_fwnode(&gc->gpiodev->dev);
+       struct irq_domain *domain;
+       domain = irq_domain_create_simple(fwnode, gc->ngpio, gc->irq.first,
+                                         &gpiochip_domain_ops, gc);
+       if (!domain)
+               return ERR_PTR(-EINVAL);
+       return domain;
+ }
  /*
   * TODO: move these activate/deactivate in under the hierarchicial
   * irqchip implementation as static once SPMI and SSBI (all external
@@@ -1623,6 -1645,31 +1645,31 @@@ static void gpiochip_set_irq_hooks(stru
        }
  }
  
+ static int gpiochip_irqchip_add_allocated_domain(struct gpio_chip *gc,
+                                                struct irq_domain *domain,
+                                                bool allocated_externally)
+ {
+       if (!domain)
+               return -EINVAL;
+       if (gc->to_irq)
+               chip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", __func__);
+       gc->to_irq = gpiochip_to_irq;
+       gc->irq.domain = domain;
+       gc->irq.domain_is_allocated_externally = allocated_externally;
+       /*
+        * Using barrier() here to prevent compiler from reordering
+        * gc->irq.initialized before adding irqdomain.
+        */
+       barrier();
+       gc->irq.initialized = true;
+       return 0;
+ }
  /**
   * gpiochip_add_irqchip() - adds an IRQ chip to a GPIO chip
   * @gc: the GPIO chip to add the IRQ chip to
@@@ -1635,8 -1682,10 +1682,10 @@@ static int gpiochip_add_irqchip(struct 
  {
        struct fwnode_handle *fwnode = dev_fwnode(&gc->gpiodev->dev);
        struct irq_chip *irqchip = gc->irq.chip;
+       struct irq_domain *domain;
        unsigned int type;
        unsigned int i;
+       int ret;
  
        if (!irqchip)
                return 0;
                 "%pfw: Ignoring %u default trigger\n", fwnode, type))
                type = IRQ_TYPE_NONE;
  
-       if (gc->to_irq)
-               chip_warn(gc, "to_irq is redefined in %s and you shouldn't rely on it\n", __func__);
-       gc->to_irq = gpiochip_to_irq;
        gc->irq.default_type = type;
        gc->irq.lock_key = lock_key;
        gc->irq.request_key = request_key;
  
        /* If a parent irqdomain is provided, let's build a hierarchy */
        if (gpiochip_hierarchy_is_hierarchical(gc)) {
-               int ret = gpiochip_hierarchy_add_domain(gc);
-               if (ret)
-                       return ret;
+               domain = gpiochip_hierarchy_create_domain(gc);
        } else {
-               gc->irq.domain = irq_domain_create_simple(fwnode,
-                       gc->ngpio,
-                       gc->irq.first,
-                       &gpiochip_domain_ops,
-                       gc);
-               if (!gc->irq.domain)
-                       return -EINVAL;
+               domain = gpiochip_simple_create_domain(gc);
        }
+       if (IS_ERR(domain))
+               return PTR_ERR(domain);
  
        if (gc->irq.parent_handler) {
                for (i = 0; i < gc->irq.num_parents; i++) {
  
        gpiochip_set_irq_hooks(gc);
  
-       /*
-        * Using barrier() here to prevent compiler from reordering
-        * gc->irq.initialized before initialization of above
-        * GPIO chip irq members.
-        */
-       barrier();
-       gc->irq.initialized = true;
+       ret = gpiochip_irqchip_add_allocated_domain(gc, domain, false);
+       if (ret)
+               return ret;
  
        acpi_gpiochip_request_interrupts(gc);
  
@@@ -1780,22 -1814,7 +1814,7 @@@ static void gpiochip_irqchip_remove(str
  int gpiochip_irqchip_add_domain(struct gpio_chip *gc,
                                struct irq_domain *domain)
  {
-       if (!domain)
-               return -EINVAL;
-       gc->to_irq = gpiochip_to_irq;
-       gc->irq.domain = domain;
-       gc->irq.domain_is_allocated_externally = true;
-       /*
-        * Using barrier() here to prevent compiler from reordering
-        * gc->irq.initialized before adding irqdomain.
-        */
-       barrier();
-       gc->irq.initialized = true;
-       return 0;
+       return gpiochip_irqchip_add_allocated_domain(gc, domain, true);
  }
  EXPORT_SYMBOL_GPL(gpiochip_irqchip_add_domain);
  
@@@ -2159,26 -2178,19 +2178,25 @@@ static bool gpiod_free_commit(struct gp
        }
  
        spin_unlock_irqrestore(&gpio_lock, flags);
-       blocking_notifier_call_chain(&desc->gdev->notifier,
-                                    GPIOLINE_CHANGED_RELEASED, desc);
+       gpiod_line_state_notify(desc, GPIOLINE_CHANGED_RELEASED);
  
        return ret;
  }
  
  void gpiod_free(struct gpio_desc *desc)
  {
 -      if (desc && desc->gdev && gpiod_free_commit(desc)) {
 -              module_put(desc->gdev->owner);
 -              gpio_device_put(desc->gdev);
 -      } else {
 +      /*
 +       * We must not use VALIDATE_DESC_VOID() as the underlying gdev->chip
 +       * may already be NULL but we still want to put the references.
 +       */
 +      if (!desc)
 +              return;
 +
 +      if (!gpiod_free_commit(desc))
                WARN_ON(extra_checks);
 -      }
 +
 +      module_put(desc->gdev->owner);
 +      gpio_device_put(desc->gdev);
  }
  
  /**
@@@ -3728,6 -3740,12 +3746,12 @@@ int gpiod_set_array_value_cansleep(unsi
  }
  EXPORT_SYMBOL_GPL(gpiod_set_array_value_cansleep);
  
+ void gpiod_line_state_notify(struct gpio_desc *desc, unsigned long action)
+ {
+       blocking_notifier_call_chain(&desc->gdev->line_state_notifier,
+                                    action, desc);
+ }
  /**
   * gpiod_add_lookup_table() - register GPIO device consumers
   * @table: table of consumers to register
@@@ -3995,8 -4013,7 +4019,7 @@@ static struct gpio_desc *gpiod_find_and
                return ERR_PTR(ret);
        }
  
-       blocking_notifier_call_chain(&desc->gdev->notifier,
-                                    GPIOLINE_CHANGED_REQUESTED, desc);
+       gpiod_line_state_notify(desc, GPIOLINE_CHANGED_REQUESTED);
  
        return desc;
  }