summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-22 06:59:20 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-22 06:59:20 -1000
commitbfa231bb4c7eae56b9967da19d1678bf0847259d (patch)
tree857a1be5784d9e9600ea1dac5fcd6e4bd67a7eec /drivers
parent74b1dbf6f236f4fe88bd1077ae4657ca4b2c339e (diff)
parent47f16cf7a6017212230985f6bc7f1b55b6d234d9 (diff)
Merge tag 'gpio-updates-for-v7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux
Pull gpio updates from Bartosz Golaszewski: "Nothing stands out as particularly interesting, it's been a calm cycle. We have three new drivers, support for more models and lots of updates across many modules (details are in the signed tag as usual). There are also some minor changes and additions to the in-kernel GPIO API. GPIO core: - provide a new controller API: gpiochip_generic_get_config() - return -EOPNOTSUPP instead of -ENODEV to user-space if it requests hardware timestamping on systems where it's unavailable (no user-space regressions reported) New drivers: - add a driver for ROHM BD73800 PMIC GPIO controller - add a driver for Axiado AX3005 SGPIO controller - add a driver for Analog Devices AD7768 GPIO controller New hardware support: - extend gpio-rcar with support for R-Car X5H (R8A78000) - extend gpio-tegra186 with support for Tegra 268 Driver changes: - refactor and improve gpio-rtd1625: switch to modern or better fitting interfaces and improve coding style - improve error handling of return values of platform_get_irq_optional() in several drivers - normalize the return value of the gpio_chip::get() callback in gpio-siox in order to not propagate invalid values up to the GPIO core - implement gpio_chip::get_direction() for gpio-pcf857x - use managed variants of notifier registration functions in several drivers - remove legacy gpio_set_value_cansleep() as it has no more users - add pinctrl support to gpio-realtek-otto - add runtime PM support to the gpio-regmap helper module - add more ACPI GPIO quirks for various hardware - stop needlessly reading gpio_chip::base in gpio-pxa - add support for tracking of direction of generic GPIO chips with no direction registers via pinctrl backend to gpio-mmio (use the new core API: gpiochip_generic_get_config()) - use the "safe" variants of debugfs_create_file() in gpio-sloppy-analyzer - use device name as GPIO chip label in gpio-omap - refactor bit ops in gpio-dln2 - prepare gpio-it87 for upcoming kmalloc_obj() conversion Devicetree bindings: - document new model in gpio-pca95xx - allow the gpio-hogs property in gpio-zynq and fsl,qoriq-gpio - allow the gpio-ranges property in gpio-realtek-otto Misc fixes: - add missing MODULE_ALIAS() to gpio-crystalcove - fix a comment typo in example GPIO tools - fix unbalanced brackets in documentation - add names to regmaps in gpio-mvebu to avoid collisions in debugfs output - fix GPIO lookup using relative paths in GPIO ACPI" * tag 'gpio-updates-for-v7.4-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux: (63 commits) gpiolib: acpi: Fix GPIO lookup using relative path gpio: legacy: Kill gpio_set_value_cansleep() dt-bindings: gpio: pca95xx: add onnn,pca9655 with nxp,pca9555 fallback gpiolib: acpi: Ignore GPIO 11 interrupt on Acer Nitro V 16S AI gpio: pxa: stop reading gpio_chip::base in direction callbacks gpio: axiado: add AX3005 SGPIO controller support dt-bindings: gpio: add Axiado AX3005 SGPIO controller gpiolib: acpi: Add quirk for Lenovo IdeaPad Slim 3 15ABR8 MAINTAINERS: Add ROHM BD73800 PMIC files gpio: bd73800: Support ROHM BD73800 PMIC GPIOs clk: bd718x7: Support ROHM BD73800 regulator: bd71828: Support ROHM BD73800 rtc: bd70528: Support RTC on ROHM BD73800 mfd: Support for ROHM BD73800 PMIC core dt-bindings: mfd: ROHM BD73800 PMIC dt-bindings: regulator: ROHM BD73800 regulators dt-bindings: rohm,bd*: Ref common ROHM bindings dt-bindings: mfd: Common ROHM PMIC properties gpio: siox: normalize return value of gpio_get() gpio: dln2: use assign_bit() where applicable ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/clk/clk-bd718x7.c8
-rw-r--r--drivers/gpio/Kconfig45
-rw-r--r--drivers/gpio/Makefile3
-rw-r--r--drivers/gpio/gpio-ad7768.c121
-rw-r--r--drivers/gpio/gpio-adp5585.c20
-rw-r--r--drivers/gpio/gpio-altera.c5
-rw-r--r--drivers/gpio/gpio-axiado-sgpio.c736
-rw-r--r--drivers/gpio/gpio-bd73800.c209
-rw-r--r--drivers/gpio/gpio-crystalcove.c1
-rw-r--r--drivers/gpio/gpio-dln2.c6
-rw-r--r--drivers/gpio/gpio-eic-sprd.c17
-rw-r--r--drivers/gpio/gpio-it87.c2
-rw-r--r--drivers/gpio/gpio-mlxbf2.c4
-rw-r--r--drivers/gpio/gpio-mlxbf3.c4
-rw-r--r--drivers/gpio/gpio-mmio.c65
-rw-r--r--drivers/gpio/gpio-mvebu.c19
-rw-r--r--drivers/gpio/gpio-omap.c11
-rw-r--r--drivers/gpio/gpio-pcf857x.c16
-rw-r--r--drivers/gpio/gpio-pxa.c4
-rw-r--r--drivers/gpio/gpio-rcar.c86
-rw-r--r--drivers/gpio/gpio-realtek-otto.c21
-rw-r--r--drivers/gpio/gpio-regmap.c98
-rw-r--r--drivers/gpio/gpio-rtd1625.c26
-rw-r--r--drivers/gpio/gpio-siox.c4
-rw-r--r--drivers/gpio/gpio-sloppy-logic-analyzer.c4
-rw-r--r--drivers/gpio/gpio-tegra186.c74
-rw-r--r--drivers/gpio/gpiolib-acpi-core.c10
-rw-r--r--drivers/gpio/gpiolib-acpi-quirks.c42
-rw-r--r--drivers/gpio/gpiolib-cdev.c2
-rw-r--r--drivers/gpio/gpiolib-kunit.c14
-rw-r--r--drivers/gpio/gpiolib.c21
-rw-r--r--drivers/mfd/Kconfig15
-rw-r--r--drivers/mfd/rohm-bd71828.c147
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx.c54
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx.h4
-rw-r--r--drivers/pinctrl/freescale/pinctrl-vf610.c2
-rw-r--r--drivers/regulator/Kconfig4
-rw-r--r--drivers/regulator/bd71828-regulator.c558
-rw-r--r--drivers/rtc/rtc-bd70528.c8
39 files changed, 2347 insertions, 143 deletions
diff --git a/drivers/clk/clk-bd718x7.c b/drivers/clk/clk-bd718x7.c
index 8ddb50c67757..1698b763bcb7 100644
--- a/drivers/clk/clk-bd718x7.c
+++ b/drivers/clk/clk-bd718x7.c
@@ -21,6 +21,9 @@
#define BD718XX_REG_OUT32K 0x2E
/* BD72720 */
#define BD72720_REG_OUT32K 0x9a
+/* BD73800 */
+#define BD73800_REG_OUT32K 0x50
+
/*
* BD71837, BD71847, and BD71828 all use bit [0] to clk output control
*/
@@ -123,6 +126,10 @@ static int bd71837_clk_probe(struct platform_device *pdev)
c->reg = BD72720_REG_OUT32K;
c->mask = CLK_OUT_EN_MASK;
break;
+ case ROHM_CHIP_TYPE_BD73800:
+ c->reg = BD73800_REG_OUT32K;
+ c->mask = CLK_OUT_EN_MASK;
+ break;
default:
dev_err(&pdev->dev, "Unknown clk chip\n");
return -EINVAL;
@@ -152,6 +159,7 @@ static const struct platform_device_id bd718x7_clk_id[] = {
{ .name = "bd71828-clk", .driver_data = ROHM_CHIP_TYPE_BD71828 },
{ .name = "bd71815-clk", .driver_data = ROHM_CHIP_TYPE_BD71815 },
{ .name = "bd72720-clk", .driver_data = ROHM_CHIP_TYPE_BD72720 },
+ { .name = "bd73800-clk", .driver_data = ROHM_CHIP_TYPE_BD73800 },
{ }
};
MODULE_DEVICE_TABLE(platform, bd718x7_clk_id);
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index a48586bb8edb..0e3865725d74 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -204,6 +204,27 @@ config GPIO_ATH79
Select this option to enable GPIO driver for
Atheros AR71XX/AR724X/AR913X SoC devices.
+config GPIO_AXIADO_SGPIO
+ tristate "Axiado AX3005 SGPIO support"
+ depends on ARCH_AXIADO || COMPILE_TEST
+ select GPIOLIB_IRQCHIP
+ select GPIO_REGMAP
+ select REGMAP_MMIO
+ help
+ Enable support for the Axiado AX3005 Serial GPIO (SGPIO)
+ controller.
+
+ The SGPIO controller provides a serialized interface for
+ controlling multiple GPIO signals over a limited number of
+ physical lines. Each signal is exposed as one input and one
+ output line, and input lines can raise interrupts.
+
+ This driver integrates with the Linux GPIO subsystem and
+ exposes the controller as a standard GPIO provider.
+
+ To compile this driver as a module, choose M here: the module
+ will be called gpio-axiado-sgpio.
+
config GPIO_RASPBERRYPI_EXP
tristate "Raspberry Pi 3 GPIO Expander"
default RASPBERRYPI_FIRMWARE
@@ -1385,6 +1406,18 @@ config GPIO_BD72720
be configured to GPO on the ROHM PMIC. The pin configuration is done
on OTP at manufacturing.
+config GPIO_BD73800
+ tristate "ROHM BD73800 GPIO support"
+ depends on MFD_ROHM_BD71828
+ select GPIO_REGMAP
+ help
+ Support for GPIOs on ROHM BD73800 PMIC. There can be up to 4
+ GPI or GPO pins available on the PMIC in total. The purpose of
+ the pins is decided at the device manufacturing by OTP
+ configuration and can't be reconfigured later. Enable this
+ if your PMIC has pins set as GPIs or GPOs and if you wish to
+ control the pins via the GPIO framework.
+
config GPIO_BD9571MWV
tristate "ROHM BD9571 GPIO support"
depends on MFD_BD9571MWV
@@ -1818,6 +1851,18 @@ endmenu
menu "Auxiliary Bus GPIO drivers"
depends on AUXILIARY_BUS
+config GPIO_AD7768
+ tristate "Analog Devices AD7768 GPIO support"
+ depends on AD7768
+ depends on GPIOLIB
+ select GPIO_REGMAP
+ help
+ Say yes here to expose the AD7768 utility pins as GPIOs when the
+ device tree node is marked as a GPIO controller.
+
+ To compile this driver as a module, choose M here: the module will be
+ called gpio-ad7768.
+
config GPIO_LTC4283
tristate "Analog Devices LTC4283 GPIO support"
depends on SENSORS_LTC4283
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index dc9e6d643b5b..f0ab389763d4 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_GPIO_104_IDI_48) += gpio-104-idi-48.o
obj-$(CONFIG_GPIO_104_IDIO_16) += gpio-104-idio-16.o
obj-$(CONFIG_GPIO_74X164) += gpio-74x164.o
obj-$(CONFIG_GPIO_74XX_MMIO) += gpio-74xx-mmio.o
+obj-$(CONFIG_GPIO_AD7768) += gpio-ad7768.o
obj-$(CONFIG_GPIO_ADNP) += gpio-adnp.o
obj-$(CONFIG_GPIO_ADP5520) += gpio-adp5520.o
obj-$(CONFIG_GPIO_ADP5585) += gpio-adp5585.o
@@ -40,11 +41,13 @@ obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o
obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o
obj-$(CONFIG_GPIO_ASPEED_SGPIO) += gpio-aspeed-sgpio.o
obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o
+obj-$(CONFIG_GPIO_AXIADO_SGPIO) += gpio-axiado-sgpio.o
obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o
obj-$(CONFIG_GPIO_BCM_XGS_IPROC) += gpio-xgs-iproc.o
obj-$(CONFIG_GPIO_BD71815) += gpio-bd71815.o
obj-$(CONFIG_GPIO_BD71828) += gpio-bd71828.o
obj-$(CONFIG_GPIO_BD72720) += gpio-bd72720.o
+obj-$(CONFIG_GPIO_BD73800) += gpio-bd73800.o
obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o
obj-$(CONFIG_GPIO_BLZP1600) += gpio-blzp1600.o
obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o
diff --git a/drivers/gpio/gpio-ad7768.c b/drivers/gpio/gpio-ad7768.c
new file mode 100644
index 000000000000..c37e1ac30612
--- /dev/null
+++ b/drivers/gpio/gpio-ad7768.c
@@ -0,0 +1,121 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices AD7768 GPIO auxiliary driver
+ *
+ * Copyright 2026 Analog Devices Inc.
+ */
+
+#include <linux/auxiliary_bus.h>
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+
+#define AD7768_REG_GPIO_CONTROL 0x0E
+#define AD7768_GPIO_UGPIO_ENABLE BIT(7)
+
+#define AD7768_REG_GPIO_WRITE 0x0F
+#define AD7768_REG_GPIO_READ 0x10
+
+#define AD7768_NUM_GPIOS 5
+#define AD7768_FILTER_GPIO 4
+
+static int ad7768_gpio_init_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ if (ngpios > AD7768_FILTER_GPIO &&
+ fwnode_property_match_string(gc->fwnode, "clock-names", "mclk") < 0)
+ __clear_bit(AD7768_FILTER_GPIO, valid_mask);
+
+ return 0;
+}
+
+static int ad7768_gpio_reg_mask_xlate(struct gpio_regmap *gpio,
+ enum gpio_regmap_operation op,
+ unsigned int base, unsigned int offset,
+ unsigned int *reg, unsigned int *mask)
+{
+ struct regmap *regmap = gpio_regmap_get_drvdata(gpio);
+ int ret;
+
+ *reg = base;
+ *mask = BIT(offset);
+
+ if (op != GPIO_REGMAP_GET_OP)
+ return 0;
+
+ /*
+ * AD7768 has separate input-state and output-latch registers. For an
+ * output line, report the programmed value from the output latch;
+ * input lines continue to use the input-state register.
+ */
+ ret = regmap_test_bits(regmap, AD7768_REG_GPIO_CONTROL, *mask);
+ if (ret < 0)
+ return ret;
+ if (ret)
+ *reg = AD7768_REG_GPIO_WRITE;
+
+ return 0;
+}
+
+static int ad7768_gpio_probe(struct auxiliary_device *adev,
+ const struct auxiliary_device_id *id)
+{
+ struct gpio_regmap_config config;
+ struct device *dev = &adev->dev;
+ struct device *parent = dev->parent;
+ struct regmap *map;
+ int ret;
+
+ map = dev_get_regmap(parent, NULL);
+ if (!map)
+ return -ENODEV;
+
+ PM_RUNTIME_ACQUIRE_AUTOSUSPEND(parent, pm);
+ ret = PM_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = regmap_set_bits(map, AD7768_REG_GPIO_CONTROL, AD7768_GPIO_UGPIO_ENABLE);
+ if (ret)
+ return ret;
+
+ config = (struct gpio_regmap_config) {
+ .parent = dev,
+ .regmap = map,
+ .label = dev_name(parent),
+ .ngpio = AD7768_NUM_GPIOS,
+ .reg_dat_base = AD7768_REG_GPIO_READ,
+ .reg_set_base = AD7768_REG_GPIO_WRITE,
+ .reg_dir_out_base = AD7768_REG_GPIO_CONTROL,
+ .pm_dev = parent,
+ .fwnode = dev_fwnode(parent),
+ .reg_mask_xlate = ad7768_gpio_reg_mask_xlate,
+ .init_valid_mask = ad7768_gpio_init_valid_mask,
+ .drvdata = map,
+ };
+
+ return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &config));
+}
+
+static const struct auxiliary_device_id ad7768_gpio_ids[] = {
+ { .name = "ad7768.gpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(auxiliary, ad7768_gpio_ids);
+
+static struct auxiliary_driver ad7768_gpio_driver = {
+ .probe = ad7768_gpio_probe,
+ .id_table = ad7768_gpio_ids,
+};
+module_auxiliary_driver(ad7768_gpio_driver);
+
+MODULE_AUTHOR("Janani Sunil <janani.sunil@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7768 GPIO auxiliary driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-adp5585.c b/drivers/gpio/gpio-adp5585.c
index 6f10fc646008..7c04a7e86c8f 100644
--- a/drivers/gpio/gpio-adp5585.c
+++ b/drivers/gpio/gpio-adp5585.c
@@ -390,16 +390,6 @@ static const struct irq_chip adp5585_irq_chip = {
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
-static void adp5585_gpio_unreg_notifier(void *data)
-{
- struct adp5585_gpio_dev *adp5585_gpio = data;
- struct device *dev = adp5585_gpio->gpio_chip.parent;
- struct adp5585_dev *adp5585 = dev_get_drvdata(dev->parent);
-
- blocking_notifier_chain_unregister(&adp5585->event_notifier,
- &adp5585_gpio->nb);
-}
-
static int adp5585_gpio_probe(struct platform_device *pdev)
{
struct adp5585_dev *adp5585 = dev_get_drvdata(pdev->dev.parent);
@@ -450,13 +440,9 @@ static int adp5585_gpio_probe(struct platform_device *pdev)
girq->threaded = true;
adp5585_gpio->nb.notifier_call = adp5585_gpio_key_event;
- ret = blocking_notifier_chain_register(&adp5585->event_notifier,
- &adp5585_gpio->nb);
- if (ret)
- return ret;
-
- ret = devm_add_action_or_reset(dev, adp5585_gpio_unreg_notifier,
- adp5585_gpio);
+ ret = devm_blocking_notifier_chain_register(dev,
+ &adp5585->event_notifier,
+ &adp5585_gpio->nb);
if (ret)
return ret;
}
diff --git a/drivers/gpio/gpio-altera.c b/drivers/gpio/gpio-altera.c
index 532e3360b70e..a6ac9356229c 100644
--- a/drivers/gpio/gpio-altera.c
+++ b/drivers/gpio/gpio-altera.c
@@ -220,8 +220,11 @@ static int altera_gpio_probe(struct platform_device *pdev)
return -ENOMEM;
mapped_irq = platform_get_irq_optional(pdev, 0);
- if (mapped_irq < 0)
+ if (mapped_irq < 0) {
+ if (mapped_irq != -ENXIO)
+ return mapped_irq;
goto skip_irq;
+ }
if (device_property_read_u32(dev, "altr,interrupt-type", &reg)) {
dev_err(&pdev->dev,
diff --git a/drivers/gpio/gpio-axiado-sgpio.c b/drivers/gpio/gpio-axiado-sgpio.c
new file mode 100644
index 000000000000..b57a5074ccef
--- /dev/null
+++ b/drivers/gpio/gpio-axiado-sgpio.c
@@ -0,0 +1,736 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2022-2026 Axiado Corporation
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqdesc.h>
+#include <linux/irqdomain.h>
+#include <linux/math.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+struct axiado_sgpio_reg_offsets {
+ u32 mux_0;
+ u32 preset_0;
+ u32 count_0;
+ u32 pos_0;
+
+ u32 mux_1;
+ u32 ld;
+ u32 ld_ss;
+
+ u32 preset_1;
+ u32 count_1;
+ u32 pos_1;
+
+ u32 mux_2;
+ u32 dout;
+ u32 dout_ss;
+
+ u32 preset_2;
+ u32 count_2;
+ u32 pos_2;
+
+ u32 mux_3;
+ u32 preset_3;
+ u32 count_3;
+ u32 pos_3;
+
+ u32 mux_4;
+ u32 oe;
+ u32 oe_ss;
+
+ u32 preset_4;
+ u32 count_4;
+ u32 pos_4;
+
+ u32 mask;
+ u32 ctrl_en;
+ u32 ctrl_en_pos;
+
+ u32 din_ss;
+ u32 status;
+};
+
+static const struct axiado_sgpio_reg_offsets axiado_sgpio_offsets = {
+ .mux_0 = 0x000,
+ .preset_0 = 0x1dc,
+ .count_0 = 0x1f0,
+ .pos_0 = 0x204,
+
+ .mux_1 = 0x004,
+ .ld = 0x014,
+ .ld_ss = 0x0d8,
+
+ .preset_1 = 0x1e0,
+ .count_1 = 0x1f4,
+ .pos_1 = 0x208,
+
+ .mux_2 = 0x008,
+ .dout = 0x054,
+ .dout_ss = 0x158,
+
+ .preset_2 = 0x1e4,
+ .count_2 = 0x1f8,
+ .pos_2 = 0x20c,
+
+ .mux_3 = 0x00c,
+ .preset_3 = 0x1e8,
+ .count_3 = 0x1fc,
+ .pos_3 = 0x210,
+
+ .mux_4 = 0x010,
+ .oe = 0x0d4,
+ .oe_ss = 0x1d8,
+
+ .preset_4 = 0x1ec,
+ .count_4 = 0x200,
+ .pos_4 = 0x214,
+
+ .mask = 0x224,
+ .ctrl_en = 0x218,
+ .ctrl_en_pos = 0x21c,
+
+ .din_ss = 0x198,
+ .status = 0x228,
+};
+
+/* Number of SGPIO positions held by one bank register. */
+#define SGPIO_BANK_SIZE 32
+#define SGPIO_MAX_SIGNALS 512
+#define SGPIO_NUM_BANKS 16
+
+/*
+ * Slice mux encodings taken from the AX3005 SGPIO programming sequence.
+ */
+#define SGPIO_MUX_CLK 0x306
+#define SGPIO_MUX_DATA 0x30c
+#define SGPIO_MUX_DIN 0x14c
+#define SGPIO_MUX_OE 0x10c
+
+/* A POS register holds the first and the last position driven by the slice. */
+#define SGPIO_POS_FIRST GENMASK(31, 16)
+#define SGPIO_POS_LAST GENMASK(15, 0)
+#define SGPIO_POS(pos) (FIELD_PREP(SGPIO_POS_FIRST, (pos)) | \
+ FIELD_PREP(SGPIO_POS_LAST, (pos)))
+
+/* The clock and output enable slices always cover a full bank. */
+#define SGPIO_POS_FULL_BANK SGPIO_POS(SGPIO_BANK_SIZE - 1)
+
+/* The enable and interrupt registers hold one bit per slice. */
+#define SGPIO_SLICE_MASK GENMASK(15, 0)
+
+/*
+ * Interrupt sources enabled by the programming sequence. Bit 13 is left
+ * masked, the remaining slice events are all reported.
+ */
+#define SGPIO_IRQ_ENABLE 0xdfff
+
+/* The upper half of the status register reports one bit per data bank. */
+#define SGPIO_STATUS_EXCHANGE GENMASK(31, 16)
+
+struct axiado_sgpio {
+ u32 preset_value;
+ u32 count_value;
+ u32 pos_reg;
+ /* Last sampled input word of each bank, for edge detection. */
+ u32 *din_shadow;
+ unsigned long *irq_unmasked;
+ unsigned long *irq_rising;
+ unsigned long *irq_falling;
+ u32 nsignals;
+ u32 din_bank_shift;
+ unsigned long *dir_out;
+ struct gpio_regmap *gpio;
+ struct irq_domain *domain;
+ struct regmap *regmap;
+ const struct axiado_sgpio_reg_offsets *regs;
+};
+
+/*
+ * Tell lockdep that these interrupts are in a different category than the
+ * parent they are handled from, so that the nested handling is not reported
+ * as recursion.
+ */
+static struct lock_class_key axiado_sgpio_irq_lock_class;
+static struct lock_class_key axiado_sgpio_irq_request_class;
+
+/*
+ * Each SGPIO signal is exposed as two GPIO lines: an even line for the serial
+ * input and the following odd line for the serial output. Signal n therefore
+ * lives in bit (n % 32) of bank (n / 32) of either the input or the output
+ * register.
+ */
+static int axiado_sgpio_reg_mask_xlate(struct gpio_regmap *gpio,
+ enum gpio_regmap_operation op,
+ unsigned int base, unsigned int offset,
+ unsigned int *reg, unsigned int *mask)
+{
+ struct axiado_sgpio *sgpio = gpio_regmap_get_drvdata(gpio);
+ unsigned int signal = offset / 2;
+ unsigned int bank = signal / SGPIO_BANK_SIZE;
+
+ /*
+ * An output line lives in the output register for both reads and
+ * writes, so the register follows from the line rather than from the
+ * operation. The AX3005 input banks are right aligned, hence the
+ * shift.
+ */
+ if (offset % 2)
+ base = sgpio->regs->dout_ss;
+ else
+ bank += sgpio->din_bank_shift;
+
+ *reg = base + bank * sizeof(u32);
+ *mask = BIT(signal % SGPIO_BANK_SIZE);
+
+ return 0;
+}
+
+static irqreturn_t axiado_sgpio_irq_handler(int irq, void *arg)
+{
+ struct axiado_sgpio *sgpio = arg;
+ unsigned int nbanks = DIV_ROUND_UP(sgpio->nsignals, SGPIO_BANK_SIZE);
+ u32 status, new_value, changed_value;
+ unsigned int bit, reg_ptr, i;
+ int ret;
+
+ /* Read-on-clear (ACK) parent cause */
+ ret = regmap_read(sgpio->regmap, sgpio->regs->status, &status);
+ if (ret)
+ return IRQ_NONE;
+
+ /* Nothing pending: the interrupt came from somebody else. */
+ if (!status)
+ return IRQ_NONE;
+
+ status = FIELD_GET(SGPIO_STATUS_EXCHANGE, status);
+
+ reg_ptr = sgpio->din_bank_shift;
+
+ for (i = 0; i < nbanks; i++, reg_ptr++) {
+ if (status & BIT(reg_ptr)) {
+ ret = regmap_read(sgpio->regmap,
+ sgpio->regs->din_ss + reg_ptr * sizeof(u32),
+ &new_value);
+ if (ret)
+ continue;
+
+ /*
+ * Track every signal so that the shadow always holds
+ * the last sampled level, then report only the ones
+ * whose interrupt is unmasked. Transitions of a masked
+ * signal are dropped rather than delivered later, the
+ * hardware has no per signal latch to replay them
+ * from. Only this handler touches the shadow and it is
+ * not reentrant.
+ */
+ changed_value = sgpio->din_shadow[i] ^ new_value;
+ sgpio->din_shadow[i] = new_value;
+ changed_value &= bitmap_read(sgpio->irq_unmasked,
+ i * SGPIO_BANK_SIZE,
+ SGPIO_BANK_SIZE);
+
+ while (changed_value) {
+ unsigned int child_irq;
+ irq_hw_number_t hwirq;
+ unsigned int position;
+ bool rising;
+
+ bit = __ffs(changed_value);
+ changed_value &= ~BIT(bit);
+
+ position = i * SGPIO_BANK_SIZE + bit;
+ hwirq = position * 2;
+
+ rising = !!(new_value & BIT(bit));
+
+ if (!test_bit(position, rising ? sgpio->irq_rising
+ : sgpio->irq_falling))
+ continue;
+
+ child_irq = irq_find_mapping(sgpio->domain, hwirq);
+ if (child_irq)
+ handle_nested_irq(child_irq);
+ }
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int axiado_sgpio_hw_init(struct axiado_sgpio *sgpio)
+{
+ const struct axiado_sgpio_reg_offsets *regs = sgpio->regs;
+ unsigned int nbanks = DIV_ROUND_UP(sgpio->nsignals, SGPIO_BANK_SIZE);
+ unsigned int last_bank = (sgpio->nsignals - 1) / SGPIO_BANK_SIZE;
+ unsigned int last_bit = (sgpio->nsignals - 1) % SGPIO_BANK_SIZE;
+ unsigned int i;
+ int ret;
+
+ /* Slice A0 is the shift clock, slice B1 the data load. */
+ const struct reg_sequence pre_load[] = {
+ { regs->mask, 0 },
+ { regs->mux_0, SGPIO_MUX_CLK },
+ { regs->preset_0, sgpio->preset_value },
+ { regs->count_0, sgpio->count_value },
+ { regs->pos_0, SGPIO_POS_FULL_BANK },
+ { regs->mux_1, SGPIO_MUX_DATA },
+ };
+ /* Slice C2 is the data output. */
+ const struct reg_sequence pre_dout[] = {
+ { regs->preset_1, sgpio->preset_value },
+ { regs->count_1, sgpio->count_value },
+ { regs->pos_1, sgpio->pos_reg },
+ { regs->mux_2, SGPIO_MUX_DATA },
+ };
+ /* Slice D3 is the data input, slice E4 the output enable. */
+ const struct reg_sequence post_dout[] = {
+ { regs->preset_2, sgpio->preset_value },
+ { regs->count_2, sgpio->count_value },
+ { regs->pos_2, sgpio->pos_reg },
+ { regs->mux_3, SGPIO_MUX_DIN },
+ { regs->preset_3, sgpio->preset_value },
+ { regs->count_3, sgpio->count_value },
+ { regs->pos_3, sgpio->pos_reg },
+ { regs->mux_4, SGPIO_MUX_OE },
+ { regs->oe, GENMASK(31, 0) },
+ { regs->oe_ss, GENMASK(31, 0) },
+ { regs->preset_4, sgpio->preset_value },
+ { regs->count_4, sgpio->count_value },
+ { regs->pos_4, SGPIO_POS_FULL_BANK },
+ { regs->ctrl_en, SGPIO_SLICE_MASK },
+ { regs->ctrl_en_pos, SGPIO_SLICE_MASK },
+ };
+
+ ret = regmap_multi_reg_write(sgpio->regmap, pre_load,
+ ARRAY_SIZE(pre_load));
+ if (ret)
+ return ret;
+
+ /*
+ * Latch the signals that are present. Only the last bank may be
+ * partially populated; a last bit of zero still describes one signal.
+ */
+ for (i = 0; i < nbanks; i++) {
+ u32 val = i < last_bank ? GENMASK(SGPIO_BANK_SIZE - 1, 0) :
+ GENMASK(last_bit, 0);
+
+ ret = regmap_write(sgpio->regmap, regs->ld + i * 4, val);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(sgpio->regmap, regs->ld_ss + i * 4, val);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_multi_reg_write(sgpio->regmap, pre_dout,
+ ARRAY_SIZE(pre_dout));
+ if (ret)
+ return ret;
+
+ /* All outputs start out driving zero. */
+ for (i = 0; i < nbanks; i++) {
+ ret = regmap_write(sgpio->regmap, regs->dout + i * 4, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(sgpio->regmap, regs->dout_ss + i * 4, 0);
+ if (ret)
+ return ret;
+ }
+
+ return regmap_multi_reg_write(sgpio->regmap, post_dout,
+ ARRAY_SIZE(post_dout));
+}
+
+static int axiado_sgpio_irq_enable(struct axiado_sgpio *sgpio)
+{
+ return regmap_write(sgpio->regmap, sgpio->regs->mask,
+ SGPIO_IRQ_ENABLE);
+}
+
+static void axiado_sgpio_disable(void *data)
+{
+ struct axiado_sgpio *sgpio = data;
+
+ /* Mask the interrupts and stop the shift engine. */
+ regmap_write(sgpio->regmap, sgpio->regs->mask, 0);
+ regmap_write(sgpio->regmap, sgpio->regs->ctrl_en, 0);
+}
+
+static int axiado_sgpio_set_irq_type(struct irq_data *d, unsigned int type)
+{
+ struct axiado_sgpio *sgpio = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ unsigned int position;
+
+ /* Only even GPIO offsets represent SGPIO inputs. */
+ if (hwirq & 1)
+ return -EINVAL;
+
+ position = hwirq / 2;
+ if (position >= sgpio->nsignals)
+ return -EINVAL;
+
+ type &= IRQ_TYPE_SENSE_MASK;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_BOTH:
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_EDGE_FALLING:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ assign_bit(position, sgpio->irq_rising, type & IRQ_TYPE_EDGE_RISING);
+ assign_bit(position, sgpio->irq_falling, type & IRQ_TYPE_EDGE_FALLING);
+
+ return 0;
+}
+
+static void axiado_sgpio_mask_irq(struct irq_data *d)
+{
+ struct axiado_sgpio *sgpio = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ clear_bit(hwirq / 2, sgpio->irq_unmasked);
+ gpio_regmap_disable_irq(sgpio->gpio, hwirq);
+}
+
+static void axiado_sgpio_unmask_irq(struct irq_data *d)
+{
+ struct axiado_sgpio *sgpio = irq_data_get_irq_chip_data(d);
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ gpio_regmap_enable_irq(sgpio->gpio, hwirq);
+ set_bit(hwirq / 2, sgpio->irq_unmasked);
+}
+
+static int axiado_sgpio_irq_request_resources(struct irq_data *d)
+{
+ struct axiado_sgpio *sgpio = irq_data_get_irq_chip_data(d);
+
+ return gpio_regmap_reqres_irq(sgpio->gpio, d->hwirq);
+}
+
+static void axiado_sgpio_irq_release_resources(struct irq_data *d)
+{
+ struct axiado_sgpio *sgpio = irq_data_get_irq_chip_data(d);
+
+ gpio_regmap_relres_irq(sgpio->gpio, d->hwirq);
+}
+
+static const struct irq_chip axiado_sgpio_irqchip = {
+ .name = "axiado-sgpio",
+ .irq_mask = axiado_sgpio_mask_irq,
+ .irq_unmask = axiado_sgpio_unmask_irq,
+ .irq_set_type = axiado_sgpio_set_irq_type,
+ .flags = IRQCHIP_IMMUTABLE | IRQCHIP_MASK_ON_SUSPEND,
+ .irq_request_resources = axiado_sgpio_irq_request_resources,
+ .irq_release_resources = axiado_sgpio_irq_release_resources,
+};
+
+static int axiado_sgpio_irq_map(struct irq_domain *domain, unsigned int virq,
+ irq_hw_number_t hwirq)
+{
+ struct axiado_sgpio *sgpio = domain->host_data;
+
+ /* Only even offsets represent input GPIOs. */
+ if (hwirq % 2)
+ return -EINVAL;
+
+ irq_set_chip_data(virq, sgpio);
+ irq_set_lockdep_class(virq, &axiado_sgpio_irq_lock_class,
+ &axiado_sgpio_irq_request_class);
+ irq_set_chip_and_handler(virq, &axiado_sgpio_irqchip, handle_simple_irq);
+ irq_set_nested_thread(virq, true);
+ irq_set_noprobe(virq);
+
+ return 0;
+}
+
+static const struct irq_domain_ops axiado_sgpio_domain_ops = {
+ .map = axiado_sgpio_irq_map,
+ .xlate = irq_domain_xlate_twocell,
+};
+
+static int axiado_sgpio_init_input_cache(struct axiado_sgpio *sgpio)
+{
+ unsigned int num_banks = DIV_ROUND_UP(sgpio->nsignals,
+ SGPIO_BANK_SIZE);
+ unsigned int reg_ptr = sgpio->din_bank_shift;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < num_banks; i++, reg_ptr++) {
+ ret = regmap_read(sgpio->regmap,
+ sgpio->regs->din_ss + reg_ptr * sizeof(u32),
+ &sgpio->din_shadow[i]);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static bool axiado_sgpio_dout_reg(const struct axiado_sgpio *sgpio,
+ unsigned int reg)
+{
+ unsigned int span = SGPIO_NUM_BANKS * sizeof(u32);
+
+ return reg >= sgpio->regs->dout_ss &&
+ reg < sgpio->regs->dout_ss + span;
+}
+
+static bool axiado_sgpio_readable_reg(struct device *dev, unsigned int reg)
+{
+ const struct axiado_sgpio *sgpio = dev_get_drvdata(dev);
+
+ /* The status register is the last one of the block. */
+ if (reg > sgpio->regs->status)
+ return false;
+
+ /*
+ * The output shadow feeds the shift register and cannot be read back,
+ * the driven value is kept in the register cache instead.
+ */
+ return !axiado_sgpio_dout_reg(sgpio, reg);
+}
+
+static bool axiado_sgpio_volatile_reg(struct device *dev, unsigned int reg)
+{
+ const struct axiado_sgpio *sgpio = dev_get_drvdata(dev);
+
+ /*
+ * Everything but the output shadow is live hardware state: shifted in
+ * data, running counters and the interrupt status. Caching any of it
+ * would hand out stale values, so the cache holds the output banks
+ * alone.
+ */
+ return !axiado_sgpio_dout_reg(sgpio, reg);
+}
+
+static bool axiado_sgpio_precious_reg(struct device *dev, unsigned int reg)
+{
+ const struct axiado_sgpio *sgpio = dev_get_drvdata(dev);
+
+ /* Reading the status register acknowledges the interrupts. */
+ return reg == sgpio->regs->status;
+}
+
+static const struct regmap_config axiado_sgpio_regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .readable_reg = axiado_sgpio_readable_reg,
+ .volatile_reg = axiado_sgpio_volatile_reg,
+ .precious_reg = axiado_sgpio_precious_reg,
+ .cache_type = REGCACHE_MAPLE,
+};
+
+static int axiado_sgpio_probe(struct platform_device *pdev)
+{
+ struct gpio_regmap_config config = { };
+ struct irq_domain_info d_info = { };
+ struct axiado_sgpio *sgpio;
+ unsigned int ngpio, i;
+ unsigned long apb_freq;
+ struct clk *apb_clk;
+ void __iomem *base;
+ u32 sgpio_freq;
+ int irq, rc;
+
+ sgpio = devm_kzalloc(&pdev->dev, sizeof(*sgpio), GFP_KERNEL);
+ if (!sgpio)
+ return -ENOMEM;
+
+ /* The regmap callbacks below need both of these. */
+ platform_set_drvdata(pdev, sgpio);
+
+ sgpio->regs = &axiado_sgpio_offsets;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ sgpio->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &axiado_sgpio_regmap_config);
+ if (IS_ERR(sgpio->regmap))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sgpio->regmap),
+ "Failed to init regmap\n");
+
+ rc = device_property_read_u32(&pdev->dev, "ngpios", &ngpio);
+ if (rc)
+ return dev_err_probe(&pdev->dev, rc,
+ "Failed to read ngpios property\n");
+
+ /*
+ * Each SGPIO signal is exposed as one input and one output line, so
+ * the number of lines is twice the number of signals.
+ */
+ if (!ngpio || ngpio % 2 || ngpio > SGPIO_MAX_SIGNALS * 2)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "Invalid ngpios value: %u (even, max %u)\n",
+ ngpio, SGPIO_MAX_SIGNALS * 2);
+
+ sgpio->nsignals = ngpio / 2;
+
+ sgpio->din_shadow = devm_kcalloc(&pdev->dev,
+ DIV_ROUND_UP(sgpio->nsignals,
+ SGPIO_BANK_SIZE),
+ sizeof(*sgpio->din_shadow), GFP_KERNEL);
+ sgpio->irq_unmasked = devm_bitmap_zalloc(&pdev->dev, sgpio->nsignals,
+ GFP_KERNEL);
+ sgpio->irq_rising = devm_bitmap_zalloc(&pdev->dev, sgpio->nsignals,
+ GFP_KERNEL);
+ sgpio->irq_falling = devm_bitmap_zalloc(&pdev->dev, sgpio->nsignals,
+ GFP_KERNEL);
+ if (!sgpio->din_shadow || !sgpio->irq_unmasked ||
+ !sgpio->irq_rising || !sgpio->irq_falling)
+ return -ENOMEM;
+
+ apb_clk = devm_clk_get_enabled(&pdev->dev, NULL);
+ if (IS_ERR(apb_clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(apb_clk),
+ "Failed to get and enable APB clock\n");
+
+ rc = device_property_read_u32(&pdev->dev, "bus-frequency",
+ &sgpio_freq);
+ if (rc)
+ return dev_err_probe(&pdev->dev, rc,
+ "Failed to read bus-frequency\n");
+
+ apb_freq = clk_get_rate(apb_clk);
+
+ if (!apb_freq || !sgpio_freq || sgpio_freq > apb_freq)
+ return dev_err_probe(&pdev->dev, -EINVAL,
+ "Invalid SGPIO bus frequency\n");
+
+ sgpio->preset_value = (apb_freq / sgpio_freq) - 1;
+ sgpio->count_value = sgpio->preset_value;
+
+ sgpio->pos_reg = SGPIO_POS(sgpio->nsignals - 1);
+
+ sgpio->din_bank_shift = SGPIO_NUM_BANKS -
+ DIV_ROUND_UP(sgpio->nsignals, SGPIO_BANK_SIZE);
+
+ /*
+ * From here on the hardware may be running, so the shutdown action has
+ * to be in place before the first register is programmed. It only ever
+ * writes zeroes, which is harmless if initialisation never got that
+ * far, and it runs before the APB clock is disabled because that was
+ * requested earlier.
+ */
+ rc = devm_add_action_or_reset(&pdev->dev, axiado_sgpio_disable, sgpio);
+ if (rc)
+ return rc;
+
+ rc = axiado_sgpio_hw_init(sgpio);
+ if (rc)
+ return dev_err_probe(&pdev->dev, rc,
+ "Failed to initialize hardware\n");
+
+ rc = axiado_sgpio_init_input_cache(sgpio);
+ if (rc)
+ return dev_err_probe(&pdev->dev, rc,
+ "Failed to initialize input cache\n");
+
+ sgpio->dir_out = devm_bitmap_zalloc(&pdev->dev, ngpio, GFP_KERNEL);
+ if (!sgpio->dir_out)
+ return -ENOMEM;
+
+ /* Even lines are the serial inputs, odd lines the serial outputs. */
+ for (i = 1; i < ngpio; i += 2)
+ __set_bit(i, sgpio->dir_out);
+
+ d_info.fwnode = dev_fwnode(&pdev->dev);
+ d_info.size = ngpio;
+ d_info.hwirq_max = ngpio;
+ d_info.ops = &axiado_sgpio_domain_ops;
+ d_info.host_data = sgpio;
+
+ sgpio->domain = devm_irq_domain_instantiate(&pdev->dev, &d_info);
+ if (IS_ERR(sgpio->domain))
+ return PTR_ERR(sgpio->domain);
+
+ config.parent = &pdev->dev;
+ config.regmap = sgpio->regmap;
+ config.ngpio = ngpio;
+ config.reg_dat_base = GPIO_REGMAP_ADDR(sgpio->regs->din_ss);
+ config.reg_set_base = GPIO_REGMAP_ADDR(sgpio->regs->dout_ss);
+ config.reg_mask_xlate = axiado_sgpio_reg_mask_xlate;
+ config.fixed_direction_output = sgpio->dir_out;
+ config.irq_domain = sgpio->domain;
+ config.drvdata = sgpio;
+
+ sgpio->gpio = devm_gpio_regmap_register(&pdev->dev, &config);
+ if (IS_ERR(sgpio->gpio))
+ return dev_err_probe(&pdev->dev, PTR_ERR(sgpio->gpio),
+ "Could not register gpiochip\n");
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ rc = devm_request_threaded_irq(&pdev->dev, irq, NULL, axiado_sgpio_irq_handler,
+ IRQF_ONESHOT, dev_name(&pdev->dev), sgpio);
+ if (rc)
+ return dev_err_probe(&pdev->dev, rc, "Failed to request IRQ\n");
+
+ rc = axiado_sgpio_irq_enable(sgpio);
+ if (rc)
+ return dev_err_probe(&pdev->dev, rc,
+ "Failed to enable interrupts\n");
+
+ return 0;
+}
+
+static void axiado_sgpio_remove(struct platform_device *pdev)
+{
+ struct axiado_sgpio *sgpio = platform_get_drvdata(pdev);
+
+ /*
+ * Runs before the devres unwind, so the interrupts are masked while the
+ * parent interrupt is still requested. The shift engine is stopped by
+ * axiado_sgpio_disable(), registered before the hardware was programmed
+ * so that it also covers failures during probe.
+ */
+ regmap_write(sgpio->regmap, sgpio->regs->mask, 0);
+}
+
+static const struct of_device_id axiado_sgpio_of_match[] = {
+ { .compatible = "axiado,ax3005-sgpio" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, axiado_sgpio_of_match);
+
+static struct platform_driver axiado_sgpio_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = axiado_sgpio_of_match,
+ },
+ .probe = axiado_sgpio_probe,
+ .remove = axiado_sgpio_remove,
+};
+module_platform_driver(axiado_sgpio_driver);
+
+MODULE_DESCRIPTION("Axiado AX3005 Serial GPIO Driver");
+MODULE_AUTHOR("Axiado Corporation");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-bd73800.c b/drivers/gpio/gpio-bd73800.c
new file mode 100644
index 000000000000..2be2a885820e
--- /dev/null
+++ b/drivers/gpio/gpio-bd73800.c
@@ -0,0 +1,209 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support to GPIOs on ROHM BD73800
+ * Copyright 2024 ROHM Semiconductors.
+ * Author: Matti Vaittinen <mazziesaccount@gmail.com>
+ */
+
+#include <linux/bitmap.h>
+#include <linux/bitops.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/rohm-bd73800.h>
+
+#define BD73800_GPIO_MAX_PINS 4
+/*
+ * The BD73800 has several "one time programmable" (OTP) configurations which
+ * can be set at manufacturing phase. Some of these options allow using
+ * individual pins as GPI or GPO (not both at the same time). The OTP
+ * configuration can't be read at run-time, so drivers rely on device-tree to
+ * advertise the OTP programmed in manufacturing.
+ *
+ * The pins which can be used as GPIO are:
+ * GPIO1, CLKOUT (GPIO2), FAULT_B, EXTEN_OUT.
+ *
+ * The OTP options 2 and 3 state for all the pins:
+ * - OTP2: GPI (also IRQ source)
+ * - OTP3: GPO (NOTE: This is actually 2 different OTP options. Either a
+ * register controllable output or a power-sequence controlled output.
+ * The "gpo" referred here means only the register controllable output.)
+ * The datasheet refers to this as: "<pin> output is controlled by
+ * GPIO<N>_OUT or power on/off sequencer to control external VRs. ON/OFF
+ * sequence timing is configurable."
+ *
+ * The data-sheet further says that the GPI/GPO is not a default OTP
+ * configuration for any of the pins. Hence the GPIO driver defaults to a pin
+ * not being a GPI or GPO, but requires the pin to be explicitly marked as a
+ * GPI or GPO in the device-tree.
+ *
+ * DT properties:
+ * "rohm,pin-gpio1", "rohm,pin-clkout", "rohm,pin-fault_b", "rohm,pin-exten"
+ * can be set to one of the values "gpi" or "gpo" to enable them to be used as
+ * GPIO.
+ *
+ * The amount of GPIO lines the chip exposes to the user-space (chip.ngpio) is
+ * always the same, regardless of the OTP variant in use. The lines which are
+ * not usable as GPIO on a given OTP variant are marked invalid via the GPIO
+ * valid_mask. This way the user-space always sees a constant amount of GPIO
+ * lines, where numbering (relative to the chip's base) stays also the same.
+ * Users can then use the valid_mask to find out which of the lines are usable.
+ */
+
+struct bd73800_gpio {
+ /* dev points to the platform device for devm and prints */
+ struct device *dev;
+ struct regmap *regmap;
+ /* Pins which have been OTP configured as GPI or GPO */
+ DECLARE_BITMAP(valid_mask, BD73800_GPIO_MAX_PINS);
+ /* Subset of valid_mask - pins which have been OTP configured as GPO */
+ DECLARE_BITMAP(output_mask, BD73800_GPIO_MAX_PINS);
+};
+
+static const char * const bd73800_gpio_properties[BD73800_GPIO_MAX_PINS] = {
+ "rohm,pin-gpio1",
+ "rohm,pin-clkout",
+ "rohm,pin-fault_b",
+ "rohm,pin-exten"
+};
+
+static int bd73800_gpio_get_pins(struct bd73800_gpio *data)
+{
+ struct device *parent = data->dev->parent;
+ const char *val;
+ int i, ret;
+
+ for (i = 0; i < BD73800_GPIO_MAX_PINS; i++) {
+ ret = device_property_read_string(parent,
+ bd73800_gpio_properties[i],
+ &val);
+ if (ret) {
+ if (ret == -EINVAL)
+ continue;
+
+ return dev_err_probe(data->dev, ret,
+ "pin %d (%s), bad configuration\n", i,
+ bd73800_gpio_properties[i]);
+ }
+
+ if (!strcmp(val, "gpi")) {
+ __set_bit(i, data->valid_mask);
+ } else if (!strcmp(val, "gpo")) {
+ __set_bit(i, data->valid_mask);
+ __set_bit(i, data->output_mask);
+ } else {
+ dev_warn(data->dev,
+ "pin %d (%s), unknown value '%s' ignored\n", i,
+ bd73800_gpio_properties[i], val);
+ }
+ }
+
+ return 0;
+}
+
+static int bd73800_gpio_init_valid_mask(struct gpio_chip *gc,
+ unsigned long *valid_mask,
+ unsigned int ngpios)
+{
+ struct gpio_regmap *gpio = gpiochip_get_data(gc);
+ struct bd73800_gpio *data = gpio_regmap_get_drvdata(gpio);
+
+ bitmap_copy(valid_mask, data->valid_mask, ngpios);
+
+ return 0;
+}
+
+/*
+ * The used register depends on OTP:
+ * - If OTP has set pin as GPO, only the GPO_OUT register is valid.
+ * - If OTP has set pin as GPI, only the INT_5_SRC register is valid.
+ */
+static int bd73800_gpio_reg_mask_xlate(struct gpio_regmap *gpio,
+ enum gpio_regmap_operation op,
+ unsigned int base, unsigned int offset,
+ unsigned int *reg, unsigned int *mask)
+{
+ struct bd73800_gpio *data = gpio_regmap_get_drvdata(gpio);
+ bool is_output = test_bit(offset, data->output_mask);
+
+ if (is_output)
+ *reg = BD73800_REG_GPO_OUT;
+ else
+ *reg = base;
+
+ *mask = BIT(offset);
+
+ return 0;
+}
+
+static int gpo_bd73800_probe(struct platform_device *pdev)
+{
+ struct gpio_regmap_config config = { };
+ struct bd73800_gpio *data;
+ struct device *parent, *dev;
+ int ret;
+
+ dev = &pdev->dev;
+ /* The device-tree and regmap come from MFD => use parent for that */
+ parent = dev->parent;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->dev = dev;
+ data->regmap = dev_get_regmap(parent, NULL);
+ if (!data->regmap)
+ return dev_err_probe(dev, -ENODEV, "no parent regmap\n");
+
+ ret = bd73800_gpio_get_pins(data);
+ if (ret)
+ return ret;
+
+ if (bitmap_empty(data->valid_mask, BD73800_GPIO_MAX_PINS)) {
+ /*
+ * The BD73800 may or may not have pins allocated for GPIO
+ * depending on the OTP used at manufacturing.
+ * If there are no pins, then we have nothing to do.
+ */
+ dev_dbg(dev, "no GPIO pins\n");
+ return -ENODEV;
+ }
+
+ config.parent = parent;
+ config.regmap = data->regmap;
+ config.label = "bd73800";
+ config.ngpio = BD73800_GPIO_MAX_PINS;
+ config.reg_dat_base = BD73800_REG_INT_5_SRC;
+ config.reg_set_base = BD73800_REG_GPO_OUT;
+ config.reg_mask_xlate = bd73800_gpio_reg_mask_xlate;
+ config.init_valid_mask = bd73800_gpio_init_valid_mask;
+ /* All pins that are valid GPIO lines also have a fixed direction */
+ config.fixed_direction_mask = data->valid_mask;
+ config.fixed_direction_output = data->output_mask;
+ config.drvdata = data;
+
+ return PTR_ERR_OR_ZERO(devm_gpio_regmap_register(dev, &config));
+}
+
+static const struct platform_device_id bd73800_gpio_id[] = {
+ { "bd73800-gpio" },
+ { },
+};
+MODULE_DEVICE_TABLE(platform, bd73800_gpio_id);
+
+static struct platform_driver gpo_bd73800_driver = {
+ .driver = {
+ .name = "bd73800-gpio",
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = gpo_bd73800_probe,
+ .id_table = bd73800_gpio_id,
+};
+module_platform_driver(gpo_bd73800_driver);
+
+MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>");
+MODULE_DESCRIPTION("GPIO interface for BD73800");
+MODULE_LICENSE("GPL");
diff --git a/drivers/gpio/gpio-crystalcove.c b/drivers/gpio/gpio-crystalcove.c
index 0fb5c06d0886..ab7fab5b9754 100644
--- a/drivers/gpio/gpio-crystalcove.c
+++ b/drivers/gpio/gpio-crystalcove.c
@@ -396,3 +396,4 @@ module_platform_driver(crystalcove_gpio_driver);
MODULE_AUTHOR("Yang, Bin <bin.yang@intel.com>");
MODULE_DESCRIPTION("Intel Crystal Cove GPIO Driver");
MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:crystal_cove_gpio");
diff --git a/drivers/gpio/gpio-dln2.c b/drivers/gpio/gpio-dln2.c
index 4670ffd7ea7f..aef4345d265e 100644
--- a/drivers/gpio/gpio-dln2.c
+++ b/drivers/gpio/gpio-dln2.c
@@ -243,10 +243,8 @@ static int dln2_gpio_set_direction(struct gpio_chip *chip, unsigned offset,
if (ret < 0)
return ret;
- if (dir == DLN2_GPIO_DIRECTION_OUT)
- set_bit(offset, dln2->output_enabled);
- else
- clear_bit(offset, dln2->output_enabled);
+ assign_bit(offset, dln2->output_enabled,
+ dir == DLN2_GPIO_DIRECTION_OUT);
return ret;
}
diff --git a/drivers/gpio/gpio-eic-sprd.c b/drivers/gpio/gpio-eic-sprd.c
index 3b7ebcf12fe7..86eb9624d38a 100644
--- a/drivers/gpio/gpio-eic-sprd.c
+++ b/drivers/gpio/gpio-eic-sprd.c
@@ -601,13 +601,6 @@ static const struct irq_chip sprd_eic_irq = {
GPIOCHIP_IRQ_RESOURCE_HELPERS,
};
-static void sprd_eic_unregister_notifier(void *data)
-{
- struct notifier_block *nb = data;
-
- atomic_notifier_chain_unregister(&sprd_eic_irq_notifier, nb);
-}
-
static int sprd_eic_probe(struct platform_device *pdev)
{
const struct sprd_eic_variant_data *pdata;
@@ -690,14 +683,8 @@ static int sprd_eic_probe(struct platform_device *pdev)
}
sprd_eic->irq_nb.notifier_call = sprd_eic_irq_notify;
- ret = atomic_notifier_chain_register(&sprd_eic_irq_notifier,
- &sprd_eic->irq_nb);
- if (ret)
- return dev_err_probe(dev, ret,
- "Failed to register with the interrupt notifier");
-
- return devm_add_action_or_reset(dev, sprd_eic_unregister_notifier,
- &sprd_eic->irq_nb);
+ return devm_atomic_notifier_chain_register(dev, &sprd_eic_irq_notifier,
+ &sprd_eic->irq_nb);
}
static const struct of_device_id sprd_eic_of_match[] = {
diff --git a/drivers/gpio/gpio-it87.c b/drivers/gpio/gpio-it87.c
index 2ad3c239367b..699325129a7d 100644
--- a/drivers/gpio/gpio-it87.c
+++ b/drivers/gpio/gpio-it87.c
@@ -362,7 +362,7 @@ static int __init it87_gpio_init(void)
*/
labels = kcalloc(it87_gpio->chip.ngpio, sizeof("it87_gpXY"),
GFP_KERNEL);
- labels_table = kcalloc(it87_gpio->chip.ngpio, sizeof(const char *),
+ labels_table = kcalloc(it87_gpio->chip.ngpio, sizeof(*labels_table),
GFP_KERNEL);
if (!labels || !labels_table) {
diff --git a/drivers/gpio/gpio-mlxbf2.c b/drivers/gpio/gpio-mlxbf2.c
index 4e2f3381d82b..4e5c5d196a50 100644
--- a/drivers/gpio/gpio-mlxbf2.c
+++ b/drivers/gpio/gpio-mlxbf2.c
@@ -394,7 +394,9 @@ mlxbf2_gpio_probe(struct platform_device *pdev)
gc->owner = THIS_MODULE;
irq = platform_get_irq_optional(pdev, 0);
- if (irq >= 0) {
+ if (irq < 0 && irq != -ENXIO)
+ return irq;
+ if (irq > 0) {
girq = &gs->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &mlxbf2_gpio_irq_chip);
girq->handler = handle_simple_irq;
diff --git a/drivers/gpio/gpio-mlxbf3.c b/drivers/gpio/gpio-mlxbf3.c
index 566326644a2c..929f64c5140a 100644
--- a/drivers/gpio/gpio-mlxbf3.c
+++ b/drivers/gpio/gpio-mlxbf3.c
@@ -231,7 +231,9 @@ static int mlxbf3_gpio_probe(struct platform_device *pdev)
gc->add_pin_ranges = mlxbf3_gpio_add_pin_ranges;
irq = platform_get_irq_optional(pdev, 0);
- if (irq >= 0) {
+ if (irq < 0 && irq != -ENXIO)
+ return irq;
+ if (irq > 0) {
girq = &gs->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &gpio_mlxbf3_irqchip);
girq->default_type = IRQ_TYPE_NONE;
diff --git a/drivers/gpio/gpio-mmio.c b/drivers/gpio/gpio-mmio.c
index e9c531eef452..0709ffaab4de 100644
--- a/drivers/gpio/gpio-mmio.c
+++ b/drivers/gpio/gpio-mmio.c
@@ -49,6 +49,7 @@ o ` ~~~~\___/~~~~ ` controller in FPGA is ,.`
#include <linux/log2.h>
#include <linux/module.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/pinconf-generic.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/spinlock.h>
@@ -354,6 +355,11 @@ static int gpio_mmio_dir_return(struct gpio_chip *gc, unsigned int gpio,
if (!chip->pinctrl)
return 0;
+#ifdef CONFIG_PINCTRL
+ if (list_empty(&gc->gpiodev->pin_ranges))
+ return 0;
+#endif
+
if (dir_out)
return pinctrl_gpio_direction_output(gc, gpio);
else
@@ -367,7 +373,17 @@ static int gpio_mmio_dir_in_err(struct gpio_chip *gc, unsigned int gpio)
static int gpio_mmio_simple_dir_in(struct gpio_chip *gc, unsigned int gpio)
{
- return gpio_mmio_dir_return(gc, gpio, false);
+ struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
+ int ret;
+
+ ret = gpio_mmio_dir_return(gc, gpio, false);
+ if (ret)
+ return ret;
+
+ guard(raw_spinlock_irqsave)(&chip->lock);
+ chip->sdir &= ~gpio_mmio_line2mask(gc, gpio);
+
+ return 0;
}
static int gpio_mmio_dir_out_err(struct gpio_chip *gc, unsigned int gpio,
@@ -379,9 +395,19 @@ static int gpio_mmio_dir_out_err(struct gpio_chip *gc, unsigned int gpio,
static int gpio_mmio_simple_dir_out(struct gpio_chip *gc, unsigned int gpio,
int val)
{
+ struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
+ int ret;
+
gc->set(gc, gpio, val);
- return gpio_mmio_dir_return(gc, gpio, true);
+ ret = gpio_mmio_dir_return(gc, gpio, true);
+ if (ret)
+ return ret;
+
+ guard(raw_spinlock_irqsave)(&chip->lock);
+ chip->sdir |= gpio_mmio_line2mask(gc, gpio);
+
+ return 0;
}
static int gpio_mmio_dir_in(struct gpio_chip *gc, unsigned int gpio)
@@ -596,20 +622,51 @@ static int gpio_mmio_setup_direction(struct gpio_generic_chip *chip,
gc->direction_input = gpio_mmio_dir_in_err;
else
gc->direction_input = gpio_mmio_simple_dir_in;
+
+ if (cfg->flags & GPIO_GENERIC_PINCTRL_BACKEND) {
+ chip->dir_unreadable = true;
+ gc->get_direction = gpio_mmio_get_dir;
+ gc->get_config = gpiochip_generic_get_config;
+ }
}
return 0;
}
+static void gpio_mmio_seed_dir_from_pinctrl(struct gpio_chip *gc,
+ unsigned int gpio)
+{
+ struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
+ unsigned long config;
+
+ if (!gc->get_config || chip->reg_dir_out || chip->reg_dir_in)
+ return;
+
+ config = pinconf_to_config_packed(PIN_CONFIG_OUTPUT_ENABLE, 0);
+ if (gc->get_config(gc, gpio, &config))
+ return;
+
+ guard(raw_spinlock_irqsave)(&chip->lock);
+ if (config)
+ chip->sdir |= gpio_mmio_line2mask(gc, gpio);
+ else
+ chip->sdir &= ~gpio_mmio_line2mask(gc, gpio);
+}
+
static int gpio_mmio_request(struct gpio_chip *gc, unsigned int gpio_pin)
{
struct gpio_generic_chip *chip = to_gpio_generic_chip(gc);
+ int ret;
if (gpio_pin >= gc->ngpio)
return -EINVAL;
- if (chip->pinctrl)
- return gpiochip_generic_request(gc, gpio_pin);
+ if (chip->pinctrl) {
+ ret = gpiochip_generic_request(gc, gpio_pin);
+ if (ret)
+ return ret;
+ gpio_mmio_seed_dir_from_pinctrl(gc, gpio_pin);
+ }
return 0;
}
diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c
index a2796240fe14..267e6d5bacd0 100644
--- a/drivers/gpio/gpio-mvebu.c
+++ b/drivers/gpio/gpio-mvebu.c
@@ -599,6 +599,21 @@ static void mvebu_gpio_irq_handler(struct irq_desc *desc)
}
static const struct regmap_config mvebu_gpio_regmap_config = {
+ .name = "gpio",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+};
+
+static const struct regmap_config mvebu_gpio_percpu_regmap_config = {
+ .name = "percpu",
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+};
+
+static const struct regmap_config mvebu_pwm_regmap_config = {
+ .name = "pwm",
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
@@ -868,7 +883,7 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
return PTR_ERR(base);
mvpwm->regs = devm_regmap_init_mmio(&pdev->dev, base,
- &mvebu_gpio_regmap_config);
+ &mvebu_pwm_regmap_config);
if (IS_ERR(mvpwm->regs))
return PTR_ERR(mvpwm->regs);
@@ -1135,7 +1150,7 @@ static int mvebu_gpio_probe_raw(struct platform_device *pdev,
mvchip->percpu_regs =
devm_regmap_init_mmio(&pdev->dev, base,
- &mvebu_gpio_regmap_config);
+ &mvebu_gpio_percpu_regmap_config);
if (IS_ERR(mvchip->percpu_regs))
return PTR_ERR(mvchip->percpu_regs);
}
diff --git a/drivers/gpio/gpio-omap.c b/drivers/gpio/gpio-omap.c
index 005420baecf4..a9b9a3d9631e 100644
--- a/drivers/gpio/gpio-omap.c
+++ b/drivers/gpio/gpio-omap.c
@@ -1032,8 +1032,6 @@ static void omap_gpio_mod_init(struct gpio_bank *bank)
static int omap_gpio_chip_init(struct gpio_bank *bank, struct device *pm_dev)
{
struct gpio_irq_chip *irq;
- static int gpio;
- const char *label;
int ret;
/*
@@ -1055,11 +1053,7 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct device *pm_dev)
if (bank->regs->wkup_en)
bank->chip.parent = &omap_mpuio_device.dev;
} else {
- label = devm_kasprintf(bank->chip.parent, GFP_KERNEL, "gpio-%d-%d",
- gpio, gpio + bank->width - 1);
- if (!label)
- return -ENOMEM;
- bank->chip.label = label;
+ bank->chip.label = dev_name(bank->chip.parent);
}
bank->chip.base = -1;
bank->chip.ngpio = bank->width;
@@ -1086,9 +1080,6 @@ static int omap_gpio_chip_init(struct gpio_bank *bank, struct device *pm_dev)
if (ret)
gpiochip_remove(&bank->chip);
- if (!bank->is_mpuio)
- gpio += bank->width;
-
return ret;
}
diff --git a/drivers/gpio/gpio-pcf857x.c b/drivers/gpio/gpio-pcf857x.c
index c1f5e10a3c20..1ba6a09542f3 100644
--- a/drivers/gpio/gpio-pcf857x.c
+++ b/drivers/gpio/gpio-pcf857x.c
@@ -71,8 +71,9 @@ MODULE_DEVICE_TABLE(of, pcf857x_of_table);
struct pcf857x {
struct gpio_chip chip;
struct i2c_client *client;
- struct mutex lock; /* protect 'out' */
+ struct mutex lock; /* protect 'out' and 'dir' */
unsigned int out; /* software latch */
+ unsigned int dir; /* direction latch (1 = input) */
unsigned int status; /* current status */
unsigned int irq_enabled; /* enabled irqs */
@@ -126,12 +127,21 @@ static int pcf857x_input(struct gpio_chip *chip, unsigned int offset)
mutex_lock(&gpio->lock);
gpio->out |= (1 << offset);
+ gpio->dir |= (1 << offset);
status = gpio->write(gpio->client, gpio->out);
mutex_unlock(&gpio->lock);
return status;
}
+static int pcf857x_get_direction(struct gpio_chip *chip, unsigned int offset)
+{
+ struct pcf857x *gpio = gpiochip_get_data(chip);
+
+ return (gpio->dir & (1 << offset)) ? GPIO_LINE_DIRECTION_IN
+ : GPIO_LINE_DIRECTION_OUT;
+}
+
static int pcf857x_get(struct gpio_chip *chip, unsigned int offset)
{
struct pcf857x *gpio = gpiochip_get_data(chip);
@@ -167,6 +177,7 @@ static int pcf857x_output(struct gpio_chip *chip, unsigned int offset, int value
gpio->out |= bit;
else
gpio->out &= ~bit;
+ gpio->dir &= ~bit;
status = gpio->write(gpio->client, gpio->out);
mutex_unlock(&gpio->lock);
@@ -187,6 +198,7 @@ static int pcf857x_set_multiple(struct gpio_chip *chip, unsigned long *mask,
mutex_lock(&gpio->lock);
gpio->out &= ~*mask;
gpio->out |= *bits & *mask;
+ gpio->dir &= ~*mask;
status = gpio->write(gpio->client, gpio->out);
mutex_unlock(&gpio->lock);
@@ -301,6 +313,7 @@ static int pcf857x_probe(struct i2c_client *client)
gpio->chip.set_multiple = pcf857x_set_multiple;
gpio->chip.direction_input = pcf857x_input;
gpio->chip.direction_output = pcf857x_output;
+ gpio->chip.get_direction = pcf857x_get_direction;
gpio->chip.ngpio = (uintptr_t)i2c_get_match_data(client);
reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH);
@@ -396,6 +409,7 @@ static int pcf857x_probe(struct i2c_client *client)
* reset state. Otherwise it flags pins to be driven low.
*/
gpio->out = ~n_latch;
+ gpio->dir = ~n_latch;
gpio->status = gpio->read(gpio->client);
/* Enable irqchip if we have an interrupt */
diff --git a/drivers/gpio/gpio-pxa.c b/drivers/gpio/gpio-pxa.c
index 2b5da973f51f..3ff21e899278 100644
--- a/drivers/gpio/gpio-pxa.c
+++ b/drivers/gpio/gpio-pxa.c
@@ -268,7 +268,7 @@ static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
spin_lock_irqsave(&gpio_lock, flags);
value = readl_relaxed(base + GPDR_OFFSET);
- if (__gpio_is_inverted(chip->base + offset))
+ if (__gpio_is_inverted(offset))
value |= mask;
else
value &= ~mask;
@@ -297,7 +297,7 @@ static int pxa_gpio_direction_output(struct gpio_chip *chip,
spin_lock_irqsave(&gpio_lock, flags);
tmp = readl_relaxed(base + GPDR_OFFSET);
- if (__gpio_is_inverted(chip->base + offset))
+ if (__gpio_is_inverted(offset))
tmp &= ~mask;
else
tmp |= mask;
diff --git a/drivers/gpio/gpio-rcar.c b/drivers/gpio/gpio-rcar.c
index 09bebde5c426..54dc47d45de7 100644
--- a/drivers/gpio/gpio-rcar.c
+++ b/drivers/gpio/gpio-rcar.c
@@ -36,6 +36,7 @@ struct gpio_rcar_info {
bool has_both_edge_trigger;
bool has_always_in;
bool has_inen;
+ const u8 *offsetmap;
};
struct gpio_rcar_priv {
@@ -49,34 +50,68 @@ struct gpio_rcar_priv {
struct gpio_rcar_bank_info bank_info;
};
-#define IOINTSEL 0x00 /* General IO/Interrupt Switching Register */
-#define INOUTSEL 0x04 /* General Input/Output Switching Register */
-#define OUTDT 0x08 /* General Output Register */
-#define INDT 0x0c /* General Input Register */
-#define INTDT 0x10 /* Interrupt Display Register */
-#define INTCLR 0x14 /* Interrupt Clear Register */
-#define INTMSK 0x18 /* Interrupt Mask Register */
-#define MSKCLR 0x1c /* Interrupt Mask Clear Register */
-#define POSNEG 0x20 /* Positive/Negative Logic Select Register */
-#define EDGLEVEL 0x24 /* Edge/level Select Register */
-#define OUTDTSEL 0x40 /* Output Data Select Register */
-#define BOTHEDGE 0x4c /* One Edge/Both Edge Select Register */
-#define INEN 0x50 /* General Input Enable Register */
+enum gpio_rcar_reg {
+ IOINTSEL, /* General IO/Interrupt Switching Register */
+ INOUTSEL, /* General Input/Output Switching Register */
+ OUTDT, /* General Output Register */
+ INDT, /* General Input Register */
+ INTDT, /* Interrupt Display Register */
+ INTCLR, /* Interrupt Clear Register */
+ INTMSK, /* Interrupt Mask Register */
+ MSKCLR, /* Interrupt Mask Clear Register */
+ POSNEG, /* Positive/Negative Logic Select Register */
+ EDGLEVEL, /* Edge/level Select Register */
+ OUTDTSEL, /* Output Data Select Register */
+ BOTHEDGE, /* One Edge/Both Edge Select Register */
+ INEN, /* General Input Enable Register */
+};
+
+static const u8 gpio_rcar_offsetmap_gen1[] = {
+ [IOINTSEL] = 0x00,
+ [INOUTSEL] = 0x04,
+ [OUTDT] = 0x08,
+ [INDT] = 0x0c,
+ [INTDT] = 0x10,
+ [INTCLR] = 0x14,
+ [INTMSK] = 0x18,
+ [MSKCLR] = 0x1c,
+ [POSNEG] = 0x20,
+ [EDGLEVEL] = 0x24,
+ [OUTDTSEL] = 0x40,
+ [BOTHEDGE] = 0x4c,
+ [INEN] = 0x50,
+};
+
+static const u8 gpio_rcar_offsetmap_gen5[] = {
+ [IOINTSEL] = 0x00,
+ [INOUTSEL] = 0x04,
+ [OUTDT] = 0x08,
+ [INDT] = 0x1c,
+ [INTDT] = 0x80,
+ [INTCLR] = 0x84,
+ [INTMSK] = 0x88,
+ [MSKCLR] = 0x8c,
+ [POSNEG] = 0x90,
+ [EDGLEVEL] = 0x94,
+ [OUTDTSEL] = 0x0c,
+ [BOTHEDGE] = 0xbc,
+ [INEN] = 0x18,
+};
#define RCAR_MAX_GPIO_PER_BANK 32
-static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, int offs)
+static inline u32 gpio_rcar_read(struct gpio_rcar_priv *p, enum gpio_rcar_reg offs)
{
- return ioread32(p->base + offs);
+ return ioread32(p->base + p->info.offsetmap[offs]);
}
-static inline void gpio_rcar_write(struct gpio_rcar_priv *p, int offs,
+static inline void gpio_rcar_write(struct gpio_rcar_priv *p, enum gpio_rcar_reg offs,
u32 value)
{
- iowrite32(value, p->base + offs);
+ iowrite32(value, p->base + p->info.offsetmap[offs]);
}
-static void gpio_rcar_modify_bit(struct gpio_rcar_priv *p, int offs,
+static void gpio_rcar_modify_bit(struct gpio_rcar_priv *p, enum gpio_rcar_reg offs,
int bit, bool value)
{
u32 tmp = gpio_rcar_read(p, offs);
@@ -399,6 +434,7 @@ static const struct gpio_rcar_info gpio_rcar_info_gen1 = {
.has_both_edge_trigger = false,
.has_always_in = false,
.has_inen = false,
+ .offsetmap = gpio_rcar_offsetmap_gen1,
};
static const struct gpio_rcar_info gpio_rcar_info_gen2 = {
@@ -406,6 +442,7 @@ static const struct gpio_rcar_info gpio_rcar_info_gen2 = {
.has_both_edge_trigger = true,
.has_always_in = false,
.has_inen = false,
+ .offsetmap = gpio_rcar_offsetmap_gen1,
};
static const struct gpio_rcar_info gpio_rcar_info_gen3 = {
@@ -413,6 +450,7 @@ static const struct gpio_rcar_info gpio_rcar_info_gen3 = {
.has_both_edge_trigger = true,
.has_always_in = true,
.has_inen = false,
+ .offsetmap = gpio_rcar_offsetmap_gen1,
};
static const struct gpio_rcar_info gpio_rcar_info_gen4 = {
@@ -420,6 +458,15 @@ static const struct gpio_rcar_info gpio_rcar_info_gen4 = {
.has_both_edge_trigger = true,
.has_always_in = true,
.has_inen = true,
+ .offsetmap = gpio_rcar_offsetmap_gen1,
+};
+
+static const struct gpio_rcar_info gpio_rcar_info_gen5 = {
+ .has_outdtsel = true,
+ .has_both_edge_trigger = true,
+ .has_always_in = true,
+ .has_inen = true,
+ .offsetmap = gpio_rcar_offsetmap_gen5,
};
static const struct of_device_id gpio_rcar_of_table[] = {
@@ -439,6 +486,9 @@ static const struct of_device_id gpio_rcar_of_table[] = {
.compatible = "renesas,rcar-gen4-gpio",
.data = &gpio_rcar_info_gen4,
}, {
+ .compatible = "renesas,rcar-gen5-gpio",
+ .data = &gpio_rcar_info_gen5,
+ }, {
.compatible = "renesas,gpio-rcar",
.data = &gpio_rcar_info_gen1,
}, {
diff --git a/drivers/gpio/gpio-realtek-otto.c b/drivers/gpio/gpio-realtek-otto.c
index 4a606bad5848..5d98ad597d9e 100644
--- a/drivers/gpio/gpio-realtek-otto.c
+++ b/drivers/gpio/gpio-realtek-otto.c
@@ -176,10 +176,10 @@ static void realtek_gpio_update_line_imr(struct realtek_gpio_ctrl *ctrl, unsigne
u32 reg_val;
reg += 4 * (line_shift / 32);
- reg_val = ioread32(reg);
+ reg_val = __raw_readl(reg);
reg_val &= ~(REALTEK_GPIO_IMR_LINE_MASK << shift);
reg_val |= (irq_type & irq_mask & REALTEK_GPIO_IMR_LINE_MASK) << shift;
- iowrite32(reg_val, reg);
+ __raw_writel(reg_val, reg);
}
static void realtek_gpio_irq_ack(struct irq_data *data)
@@ -393,18 +393,29 @@ static int realtek_gpio_probe(struct platform_device *pdev)
raw_spin_lock_init(&ctrl->lock);
+ gen_gc_flags = GPIO_GENERIC_PINCTRL_BACKEND;
+
if (dev_flags & GPIO_PORTS_REVERSED) {
- gen_gc_flags = 0;
ctrl->bank_read = realtek_gpio_bank_read;
ctrl->bank_write = realtek_gpio_bank_write;
ctrl->line_imr_pos = realtek_gpio_line_imr_pos;
} else {
- gen_gc_flags = GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER;
+ gen_gc_flags |= GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER;
ctrl->bank_read = realtek_gpio_bank_read_swapped;
ctrl->bank_write = realtek_gpio_bank_write_swapped;
ctrl->line_imr_pos = realtek_gpio_line_imr_pos_swapped;
}
+ if (device_property_read_bool(dev, "little-endian")) {
+ gen_gc_flags = 0;
+ ctrl->bank_read = realtek_gpio_bank_read;
+ ctrl->bank_write = realtek_gpio_bank_write;
+ } else if (device_is_big_endian(dev)) {
+ gen_gc_flags = GPIO_GENERIC_BIG_ENDIAN_BYTE_ORDER;
+ ctrl->bank_read = realtek_gpio_bank_read_swapped;
+ ctrl->bank_write = realtek_gpio_bank_write_swapped;
+ }
+
config = (struct gpio_generic_chip_config) {
.dev = dev,
.sz = 4,
@@ -423,6 +434,8 @@ static int realtek_gpio_probe(struct platform_device *pdev)
ctrl->chip.gc.owner = THIS_MODULE;
irq = platform_get_irq_optional(pdev, 0);
+ if (irq < 0 && irq != -ENXIO)
+ return irq;
if (!(dev_flags & GPIO_INTERRUPTS_DISABLED) && irq > 0) {
girq = &ctrl->chip.gc.irq;
gpio_irq_chip_set_chip(girq, &realtek_gpio_irq_chip);
diff --git a/drivers/gpio/gpio-regmap.c b/drivers/gpio/gpio-regmap.c
index fb21396e1d02..2f5f45b84cc9 100644
--- a/drivers/gpio/gpio-regmap.c
+++ b/drivers/gpio/gpio-regmap.c
@@ -6,11 +6,13 @@
*/
#include <linux/bits.h>
+#include <linux/cleanup.h>
#include <linux/compiler_attributes.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/types.h>
@@ -23,6 +25,8 @@
struct gpio_regmap {
struct device *parent;
struct regmap *regmap;
+ struct device *pm_dev;
+
struct gpio_chip gpio_chip;
int reg_stride;
@@ -79,6 +83,33 @@ static int gpio_regmap_simple_xlate(struct gpio_regmap *gpio,
return 0;
}
+static int gpio_regmap_runtime_get(struct gpio_regmap *gpio)
+{
+ if (!gpio->pm_dev)
+ return 0;
+
+ return pm_runtime_get_active(gpio->pm_dev, RPM_TRANSPARENT);
+}
+
+static void gpio_regmap_runtime_put(struct gpio_regmap *gpio)
+{
+ if (!gpio->pm_dev)
+ return;
+
+ pm_runtime_put_autosuspend(gpio->pm_dev);
+}
+
+DEFINE_GUARD(gpio_regmap_runtime, struct gpio_regmap *,
+ gpio_regmap_runtime_get(_T), gpio_regmap_runtime_put(_T))
+DEFINE_GUARD_COND(gpio_regmap_runtime, _try,
+ gpio_regmap_runtime_get(_T), _RET == 0)
+
+#define GPIO_REGMAP_RUNTIME_ACQUIRE(_gpio, _var) \
+ ACQUIRE(gpio_regmap_runtime_try, _var)(_gpio)
+
+#define GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(_var_ptr) \
+ ACQUIRE_ERR(gpio_regmap_runtime, _var_ptr)
+
static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
{
struct gpio_regmap *gpio = gpiochip_get_data(chip);
@@ -91,15 +122,21 @@ static int gpio_regmap_get(struct gpio_chip *chip, unsigned int offset)
else
base = gpio_regmap_addr(gpio->reg_set_base);
- ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_OP, base, offset, &reg, &mask);
+ GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm);
+ ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_OP, base, offset,
+ &reg, &mask);
if (ret)
return ret;
+ if (gpio->reg_dat_base != gpio->reg_set_base)
+ return regmap_test_bits(gpio->regmap, reg, mask);
+
/* ensure we don't spoil any register cache with pin input values */
- if (gpio->reg_dat_base == gpio->reg_set_base)
- ret = regmap_read_bypassed(gpio->regmap, reg, &val);
- else
- ret = regmap_read(gpio->regmap, reg, &val);
+ ret = regmap_read_bypassed(gpio->regmap, reg, &val);
if (ret)
return ret;
@@ -114,7 +151,13 @@ static int gpio_regmap_set(struct gpio_chip *chip, unsigned int offset,
unsigned int reg, mask, mask_val;
int ret;
- ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset, &reg, &mask);
+ GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm);
+ ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_OP, base, offset,
+ &reg, &mask);
if (ret)
return ret;
@@ -146,6 +189,11 @@ static int gpio_regmap_set_with_clear(struct gpio_chip *chip,
unsigned int base, reg, mask, value = 0;
int ret;
+ GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm);
+ ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
if (val)
base = gpio_regmap_addr(gpio->reg_set_base);
else
@@ -183,7 +231,7 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip,
unsigned int offset)
{
struct gpio_regmap *gpio = gpiochip_get_data(chip);
- unsigned int base, val, reg, mask;
+ unsigned int base, reg, mask;
int invert, ret;
if (gpio_regmap_fixed_direction(gpio, offset)) {
@@ -208,18 +256,24 @@ static int gpio_regmap_get_direction(struct gpio_chip *chip,
return -ENOTSUPP;
}
- ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_DIR_OP, base, offset, &reg, &mask);
+ GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm);
+ ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm);
if (ret)
return ret;
- ret = regmap_read(gpio->regmap, reg, &val);
+ ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_GET_DIR_OP, base, offset,
+ &reg, &mask);
if (ret)
return ret;
- if (!!(val & mask) ^ invert)
+ ret = regmap_test_bits(gpio->regmap, reg, mask);
+ if (ret < 0)
+ return ret;
+
+ if (ret ^ invert)
return GPIO_LINE_DIRECTION_OUT;
- else
- return GPIO_LINE_DIRECTION_IN;
+
+ return GPIO_LINE_DIRECTION_IN;
}
static int gpio_regmap_try_direction_fixed(struct gpio_regmap *gpio,
@@ -262,7 +316,13 @@ static int gpio_regmap_set_direction(struct gpio_chip *chip,
return -ENOTSUPP;
}
- ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_DIR_OP, base, offset, &reg, &mask);
+ GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm);
+ ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
+ ret = gpio->reg_mask_xlate(gpio, GPIO_REGMAP_SET_DIR_OP, base, offset,
+ &reg, &mask);
if (ret)
return ret;
@@ -293,6 +353,11 @@ static int gpio_regmap_direction_output(struct gpio_chip *chip,
struct gpio_regmap *gpio = gpiochip_get_data(chip);
int ret;
+ GPIO_REGMAP_RUNTIME_ACQUIRE(gpio, pm);
+ ret = GPIO_REGMAP_RUNTIME_ACQUIRE_ERR(&pm);
+ if (ret)
+ return ret;
+
/*
* First check if this is gonna work on a fixed direction line,
* if it doesn't (i.e. this is a fixed input line), then do not
@@ -304,7 +369,9 @@ static int gpio_regmap_direction_output(struct gpio_chip *chip,
return ret;
}
- gpio_regmap_set(chip, offset, value);
+ ret = gpio_regmap_set(chip, offset, value);
+ if (ret)
+ return ret;
return gpio_regmap_set_direction(chip, offset, true);
}
@@ -399,6 +466,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
gpio->reg_clr_base = config->reg_clr_base;
gpio->reg_dir_in_base = config->reg_dir_in_base;
gpio->reg_dir_out_base = config->reg_dir_out_base;
+ gpio->pm_dev = config->pm_dev;
chip = &gpio->gpio_chip;
chip->parent = config->parent;
@@ -406,7 +474,7 @@ struct gpio_regmap *gpio_regmap_register(const struct gpio_regmap_config *config
chip->base = -1;
chip->names = config->names;
chip->label = config->label ?: dev_name(config->parent);
- chip->can_sleep = regmap_might_sleep(config->regmap);
+ chip->can_sleep = config->pm_dev || regmap_might_sleep(config->regmap);
chip->init_valid_mask = config->init_valid_mask;
chip->request = gpiochip_generic_request;
diff --git a/drivers/gpio/gpio-rtd1625.c b/drivers/gpio/gpio-rtd1625.c
index 483e44cf5abc..bebd1b8ede48 100644
--- a/drivers/gpio/gpio-rtd1625.c
+++ b/drivers/gpio/gpio-rtd1625.c
@@ -107,10 +107,11 @@ static int rtd1625_reg_mask_xlate(struct gpio_regmap *gpio, enum gpio_regmap_ope
unsigned int base, unsigned int offset, unsigned int *reg,
unsigned int *mask)
{
- /* Each GPIO has its own dedicated 32-bit register */
struct rtd1625_gpio *data = gpio_regmap_get_drvdata(gpio);
- int val = 0, ret = 0;
+ /* Each GPIO has its own dedicated 32-bit register */
*reg = base + offset * 4;
+ unsigned int val;
+ int ret;
switch (op) {
case GPIO_REGMAP_SET_OP:
@@ -220,11 +221,11 @@ static void rtd1625_gpio_irq_handle(struct irq_desc *desc)
{
unsigned int (*get_reg_offset)(struct rtd1625_gpio *gpio, unsigned int offset);
struct rtd1625_gpio *data = irq_desc_get_handler_data(desc);
+ struct device *dev = regmap_get_device(data->regmap);
struct irq_chip *chip = irq_desc_get_chip(desc);
unsigned int irq = irq_desc_get_irq(desc);
struct irq_domain *domain = data->domain;
- unsigned int reg_offset, i, j, val;
- irq_hw_number_t hwirq;
+ unsigned int reg_offset, j, val;
unsigned long status;
u32 irq_type;
int ret;
@@ -240,11 +241,12 @@ static void rtd1625_gpio_irq_handle(struct irq_desc *desc)
chained_irq_enter(chip, desc);
- for (i = 0; i < data->info->num_gpios; i += 32) {
+ for (unsigned int i = 0; i < data->info->num_gpios; i += 32) {
reg_offset = get_reg_offset(data, i);
ret = regmap_read(data->regmap, reg_offset, &val);
if (ret) {
- pr_err_ratelimited("Failed to read IRQ status for GPIO %u: %d\n", i, ret);
+ dev_err_ratelimited(dev, "Failed to read IRQ status for GPIO %u: %d\n",
+ i, ret);
continue;
}
@@ -261,12 +263,14 @@ static void rtd1625_gpio_irq_handle(struct irq_desc *desc)
if (irq != data->irqs[RTD1625_IRQ_LEVEL]) {
ret = regmap_write(data->regmap, reg_offset, status);
if (ret)
- pr_err_ratelimited("Failed to clear edge IRQ for GPIO %u: %d\n",
- i, ret);
+ dev_err_ratelimited(dev,
+ "Failed to clear edge IRQ for GPIO %u: %d\n",
+ i, ret);
}
for_each_set_bit(j, &status, 32) {
- hwirq = i + j;
+ irq_hw_number_t hwirq = i + j;
+
irq_type = irq_get_trigger_type(irq_find_mapping(domain, hwirq));
/*
@@ -486,7 +490,6 @@ static int rtd1625_gpio_setup_irq(struct platform_device *pdev, struct rtd1625_g
return irq;
num_irqs = (data->info->irq_type_support & IRQ_TYPE_LEVEL_MASK) ? 3 : 2;
-
for (unsigned int i = 0; i < num_irqs; i++) {
irq = platform_get_irq(pdev, i);
if (irq < 0)
@@ -612,8 +615,7 @@ static const struct rtd1625_gpio_info rtd1625_iso_gpio_info = {
static const struct rtd1625_gpio_info rtd1625_isom_gpio_info = {
.num_gpios = 4,
- .irq_type_support = IRQ_TYPE_EDGE_BOTH | IRQ_TYPE_LEVEL_LOW |
- IRQ_TYPE_LEVEL_HIGH,
+ .irq_type_support = IRQ_TYPE_DEFAULT,
.base_offset = 0x20,
.gpa_offset = 0x00,
.gpda_offset = 0x04,
diff --git a/drivers/gpio/gpio-siox.c b/drivers/gpio/gpio-siox.c
index 958034b9f3f3..d60680ace144 100644
--- a/drivers/gpio/gpio-siox.c
+++ b/drivers/gpio/gpio-siox.c
@@ -148,11 +148,11 @@ static int gpio_siox_get(struct gpio_chip *chip, unsigned int offset)
if (offset >= 12) {
unsigned int bitpos = 19 - offset;
- ret = ddata->setdata[0] & (1 << bitpos);
+ ret = !!(ddata->setdata[0] & (1 << bitpos));
} else {
unsigned int bitpos = 11 - offset;
- ret = ddata->getdata[bitpos / 8] & (1 << (bitpos % 8));
+ ret = !!(ddata->getdata[bitpos / 8] & (1 << (bitpos % 8)));
}
mutex_unlock(&ddata->lock);
diff --git a/drivers/gpio/gpio-sloppy-logic-analyzer.c b/drivers/gpio/gpio-sloppy-logic-analyzer.c
index 044d81e7cafb..0717649a6820 100644
--- a/drivers/gpio/gpio-sloppy-logic-analyzer.c
+++ b/drivers/gpio/gpio-sloppy-logic-analyzer.c
@@ -299,8 +299,8 @@ static int gpio_la_poll_probe(struct platform_device *pdev)
debugfs_create_blob("meta_data", 0400, priv->debug_dir, &priv->meta);
debugfs_create_ulong("delay_ns", 0600, priv->debug_dir, &priv->delay_ns);
debugfs_create_ulong("delay_ns_acquisition", 0400, priv->debug_dir, &priv->acq_delay);
- debugfs_create_file_unsafe("buf_size", 0600, priv->debug_dir, priv, &fops_buf_size);
- debugfs_create_file_unsafe("capture", 0200, priv->debug_dir, priv, &fops_capture);
+ debugfs_create_file("buf_size", 0600, priv->debug_dir, priv, &fops_buf_size);
+ debugfs_create_file("capture", 0200, priv->debug_dir, priv, &fops_capture);
debugfs_create_file("trigger", 0200, priv->debug_dir, priv, &fops_trigger);
return 0;
diff --git a/drivers/gpio/gpio-tegra186.c b/drivers/gpio/gpio-tegra186.c
index d9a2dedf50ea..34e084c95d7a 100644
--- a/drivers/gpio/gpio-tegra186.c
+++ b/drivers/gpio/gpio-tegra186.c
@@ -1419,6 +1419,71 @@ static const struct tegra_gpio_soc tegra264_uphy_soc = {
.has_vm_support = true,
};
+static const struct tegra_gpio_port tegra268_main_ports[] = {
+ TEGRA264_MAIN_GPIO_PORT(F, 3, 0, 8),
+ TEGRA264_MAIN_GPIO_PORT(G, 3, 1, 5),
+ TEGRA264_MAIN_GPIO_PORT(H, 1, 0, 8),
+ TEGRA264_MAIN_GPIO_PORT(J, 1, 1, 8),
+ TEGRA264_MAIN_GPIO_PORT(K, 1, 2, 8),
+ TEGRA264_MAIN_GPIO_PORT(L, 1, 3, 8),
+ TEGRA264_MAIN_GPIO_PORT(M, 1, 4, 2),
+ TEGRA264_MAIN_GPIO_PORT(P, 2, 0, 8),
+ TEGRA264_MAIN_GPIO_PORT(Q, 2, 1, 8),
+ TEGRA264_MAIN_GPIO_PORT(R, 2, 2, 8),
+ TEGRA264_MAIN_GPIO_PORT(S, 2, 3, 2),
+ TEGRA264_MAIN_GPIO_PORT(T, 0, 0, 7),
+ TEGRA264_MAIN_GPIO_PORT(U, 0, 1, 8),
+ TEGRA264_MAIN_GPIO_PORT(V, 0, 2, 8),
+ TEGRA264_MAIN_GPIO_PORT(W, 0, 3, 8),
+ TEGRA264_MAIN_GPIO_PORT(Y, 0, 5, 8),
+ TEGRA264_MAIN_GPIO_PORT(Z, 0, 6, 8),
+ TEGRA264_MAIN_GPIO_PORT(AL, 0, 4, 3),
+};
+
+static const struct tegra_gpio_soc tegra268_main_soc = {
+ .num_ports = ARRAY_SIZE(tegra268_main_ports),
+ .ports = tegra268_main_ports,
+ .name = "tegra268-gpio",
+ .instance = 0,
+ .num_irqs_per_bank = 8,
+ .has_vm_support = true,
+};
+
+static const struct tegra_gpio_port tegra268_aon_ports[] = {
+ TEGRA264_AON_GPIO_PORT(AA, 0, 0, 8),
+ TEGRA264_AON_GPIO_PORT(BB, 0, 1, 2),
+ TEGRA264_AON_GPIO_PORT(CC, 0, 2, 8),
+ TEGRA264_AON_GPIO_PORT(DD, 0, 3, 8),
+ TEGRA264_AON_GPIO_PORT(EE, 0, 4, 2)
+};
+
+static const struct tegra_gpio_soc tegra268_aon_soc = {
+ .num_ports = ARRAY_SIZE(tegra268_aon_ports),
+ .ports = tegra268_aon_ports,
+ .name = "tegra268-gpio-aon",
+ .instance = 1,
+ .num_irqs_per_bank = 8,
+ .has_gte = true,
+ .has_vm_support = true,
+};
+
+static const struct tegra_gpio_port tegra268_uphy_ports[] = {
+ TEGRA264_UPHY_GPIO_PORT(A, 0, 0, 6),
+ TEGRA264_UPHY_GPIO_PORT(B, 0, 1, 8),
+ TEGRA264_UPHY_GPIO_PORT(C, 0, 2, 3),
+ TEGRA264_UPHY_GPIO_PORT(D, 1, 0, 6),
+ TEGRA264_UPHY_GPIO_PORT(E, 1, 1, 4),
+};
+
+static const struct tegra_gpio_soc tegra268_uphy_soc = {
+ .num_ports = ARRAY_SIZE(tegra268_uphy_ports),
+ .ports = tegra268_uphy_ports,
+ .name = "tegra268-gpio-uphy",
+ .instance = 2,
+ .num_irqs_per_bank = 8,
+ .has_vm_support = true,
+};
+
#define TEGRA256_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
TEGRA_GPIO_PORT(TEGRA256_MAIN, _name, _bank, _port, _pins)
@@ -1529,6 +1594,15 @@ static const struct of_device_id tegra186_gpio_of_match[] = {
.compatible = "nvidia,tegra264-gpio-uphy",
.data = &tegra264_uphy_soc
}, {
+ .compatible = "nvidia,tegra268-gpio",
+ .data = &tegra268_main_soc
+ }, {
+ .compatible = "nvidia,tegra268-gpio-aon",
+ .data = &tegra268_aon_soc
+ }, {
+ .compatible = "nvidia,tegra268-gpio-uphy",
+ .data = &tegra268_uphy_soc
+ }, {
/* sentinel */
}
};
diff --git a/drivers/gpio/gpiolib-acpi-core.c b/drivers/gpio/gpiolib-acpi-core.c
index 220f0ac4204e..f94f456dbe5f 100644
--- a/drivers/gpio/gpiolib-acpi-core.c
+++ b/drivers/gpio/gpiolib-acpi-core.c
@@ -116,7 +116,8 @@ static int acpi_gpiochip_find(struct gpio_chip *gc, const void *data)
/**
* acpi_get_gpiod() - Translate ACPI GPIO pin to GPIO descriptor usable with GPIO API
- * @path: ACPI GPIO controller full path name, (e.g. "\\_SB.GPO1")
+ * @parent: parent ACPI handle to resolve relative path
+ * @path: ACPI GPIO controller path name, (e.g. "\\_SB.GPO1" or "^GP01")
* @pin: ACPI GPIO pin number (0-based, controller-relative)
*
* Returns:
@@ -128,12 +129,12 @@ static int acpi_gpiochip_find(struct gpio_chip *gc, const void *data)
* controller does not have GPIO chip registered at the moment. This is to
* support probe deferral.
*/
-static struct gpio_desc *acpi_get_gpiod(char *path, unsigned int pin)
+static struct gpio_desc *acpi_get_gpiod(acpi_handle parent, char *path, unsigned int pin)
{
acpi_handle handle;
acpi_status status;
- status = acpi_get_handle(NULL, path, &handle);
+ status = acpi_get_handle(parent, path, &handle);
if (ACPI_FAILURE(status))
return ERR_PTR(-ENODEV);
@@ -733,7 +734,8 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
if (info->quirks & ACPI_GPIO_QUIRK_ABSOLUTE_NUMBER)
desc = gpio_to_desc(agpio->pin_table[pin_index]);
else
- desc = acpi_get_gpiod(agpio->resource_source.string_ptr,
+ desc = acpi_get_gpiod(acpi_device_handle(info->adev),
+ agpio->resource_source.string_ptr,
agpio->pin_table[pin_index]);
lookup->desc = desc;
info->pin_config = agpio->pin_config;
diff --git a/drivers/gpio/gpiolib-acpi-quirks.c b/drivers/gpio/gpiolib-acpi-quirks.c
index a0116f004975..9a523871800d 100644
--- a/drivers/gpio/gpiolib-acpi-quirks.c
+++ b/drivers/gpio/gpiolib-acpi-quirks.c
@@ -345,6 +345,22 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = {
},
{
/*
+ * GPIO 11 is the power enable of the internal USB camera.
+ * The firmware drives it high but also lists it in _AEI with
+ * an empty handler. Requesting it as an input turns the camera
+ * off, so it never enumerates.
+ * Found in BIOS V1.14
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_FAMILY, "Acer Nitro V 16S AI"),
+ },
+ .driver_data = &(struct acpi_gpiolib_dmi_quirk) {
+ .ignore_interrupt = "AMDI0030:00@11",
+ },
+ },
+ {
+ /*
* Wakeup only works when keyboard backlight is turned off
* https://gitlab.freedesktop.org/drm/amd/-/issues/4169
*/
@@ -392,6 +408,32 @@ static const struct dmi_system_id gpiolib_acpi_quirks[] __initconst = {
.ignore_wake = "VEN_0488:00@355",
},
},
+ {
+ /*
+ * Spurious wakeups from GPP3 PCIe bridge interrupt
+ * Found in BIOS UX425UA.301
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX425UA_UM425UA"),
+ },
+ .driver_data = &(struct acpi_gpiolib_dmi_quirk) {
+ .ignore_wake = "AMDI0030:00@24",
+ },
+ },
+ {
+ /*
+ * Lenovo IdeaPad Slim 3 15ABR8: the touchscreen wake IRQ fires as soon
+ * as the system enters suspend, causing an immediate spurious resume.
+ */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "82XM"),
+ },
+ .driver_data = &(struct acpi_gpiolib_dmi_quirk) {
+ .ignore_wake = "GTCH7503:00@10",
+ },
+ },
{} /* Terminating entry */
};
diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c
index a127e6efd7a1..a30564d1525a 100644
--- a/drivers/gpio/gpiolib-cdev.c
+++ b/drivers/gpio/gpiolib-cdev.c
@@ -715,6 +715,8 @@ static int hte_edge_setup(struct line *line, u64 eflags)
line->desc);
ret = hte_ts_get(NULL, hdesc, 0);
+ if (ret == -ENODEV)
+ return -EOPNOTSUPP;
if (ret)
return ret;
diff --git a/drivers/gpio/gpiolib-kunit.c b/drivers/gpio/gpiolib-kunit.c
index 6a2c8b34f34b..f005854e7ff7 100644
--- a/drivers/gpio/gpiolib-kunit.c
+++ b/drivers/gpio/gpiolib-kunit.c
@@ -566,13 +566,6 @@ static int gpio_unbind_notify(struct notifier_block *nb, unsigned long action,
return NOTIFY_OK;
}
-static void gpio_unbind_unregister_notifier(void *data)
-{
- struct notifier_block *nb = data;
-
- blocking_notifier_chain_unregister(&gpio_unbind_notifier, nb);
-}
-
static int gpio_unbind_consumer_probe(struct platform_device *pdev)
{
struct gpio_unbind_consumer_drvdata *data;
@@ -590,11 +583,8 @@ static int gpio_unbind_consumer_probe(struct platform_device *pdev)
return PTR_ERR(data->desc);
data->nb.notifier_call = gpio_unbind_notify;
- ret = blocking_notifier_chain_register(&gpio_unbind_notifier, &data->nb);
- if (ret)
- return ret;
-
- ret = devm_add_action_or_reset(dev, gpio_unbind_unregister_notifier, &data->nb);
+ ret = devm_blocking_notifier_chain_register(dev, &gpio_unbind_notifier,
+ &data->nb);
if (ret)
return ret;
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index c0394c7d6279..c880686f5fd7 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -2447,6 +2447,27 @@ int gpiochip_generic_config(struct gpio_chip *gc, unsigned int offset,
}
EXPORT_SYMBOL_GPL(gpiochip_generic_config);
+/**
+ * gpiochip_generic_get_config() - read back the configuration of a pin
+ * @gc: the gpiochip owning the GPIO
+ * @offset: the offset of the GPIO to query
+ * @config: the packed parameter to query, replaced by its bare argument
+ *
+ * Returns:
+ * 0 on success, or negative errno on failure.
+ */
+int gpiochip_generic_get_config(struct gpio_chip *gc, unsigned int offset,
+ unsigned long *config)
+{
+#ifdef CONFIG_PINCTRL
+ if (list_empty(&gc->gpiodev->pin_ranges))
+ return -ENOTSUPP;
+#endif
+
+ return pinctrl_gpio_get_config(gc, offset, config);
+}
+EXPORT_SYMBOL_GPL(gpiochip_generic_get_config);
+
#ifdef CONFIG_PINCTRL
/**
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index e4fd4572472f..439dbf5ee110 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -2226,7 +2226,7 @@ config MFD_ROHM_BD718XX
and emergency shut down as well as 32,768KHz clock output.
config MFD_ROHM_BD71828
- tristate "ROHM BD718[15/28/79], BD72720 and BD73900 PMICs"
+ tristate "ROHM BD718[15/28/79], BD72720 and BD73[8/9]00 PMICs"
depends on I2C=y
depends on OF
select REGMAP_I2C
@@ -2235,14 +2235,17 @@ config MFD_ROHM_BD71828
select MFD_ROHM_PWRBUTTON
help
Select this option to get support for the ROHM BD71815, BD71828,
- BD71879, BD72720 and BD73900 Power Management ICs (PMICs). These are
- single-chip Power Management ICs (PMIC), mainly for battery-powered
- portable devices.
+ BD71879, BD72720, BD73800 and BD73900 Power Management ICs (PMICs).
+ These are single-chip Power Management ICs (PMIC), mainly for
+ battery-powered portable devices.
The BD71815 has 5 bucks, 7 LDOs, and a boost for driving LEDs.
The BD718[28/79] have 7 buck converters and 7 LDOs.
The BD72720 and the BD73900 have 10 bucks and 11 LDOs.
- All ICs provide a single-cell linear charger, a Coulomb counter,
- a Real-Time Clock (RTC), GPIOs and a 32.768 kHz clock gate.
+ All the above ICs provide a single-cell linear charger, and a Coulomb
+ counter.
+ The BD73800 has 8 bucks, 4 LDOs and ADC for power/current
+ accumulation but no charging logic. All these PMICs integrate also a
+ Real-Time Clock (RTC), GPIOs and a 32.768 kHz clock gate.
config MFD_ROHM_BD957XMUF
tristate "ROHM BD9576MUF and BD9573MUF Power Management ICs"
diff --git a/drivers/mfd/rohm-bd71828.c b/drivers/mfd/rohm-bd71828.c
index bf4df8e1b34c..46faef7058f8 100644
--- a/drivers/mfd/rohm-bd71828.c
+++ b/drivers/mfd/rohm-bd71828.c
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
- * Copyright (C) 2019 ROHM Semiconductors
+ * Copyright (C) 2019, 2026 ROHM Semiconductor
*
- * ROHM BD718[15/28/79] and BD72720 PMIC driver
+ * ROHM BD718[15/28/79], BD72720 and BD73[8/9]00 PMIC driver
*/
#include <linux/i2c.h>
@@ -14,6 +14,7 @@
#include <linux/mfd/rohm-bd71815.h>
#include <linux/mfd/rohm-bd71828.h>
#include <linux/mfd/rohm-bd72720.h>
+#include <linux/mfd/rohm-bd73800.h>
#include <linux/mfd/rohm-generic.h>
#include <linux/module.h>
#include <linux/of.h>
@@ -56,6 +57,12 @@ static const struct resource bd72720_rtc_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD72720_INT_RTC2, "bd70528-rtc-alm-2"),
};
+static const struct resource bd73800_rtc_irqs[] = {
+ DEFINE_RES_IRQ_NAMED(BD73800_INT_RTC0, "bd70528-rtc-alm-0"),
+ DEFINE_RES_IRQ_NAMED(BD73800_INT_RTC1, "bd70528-rtc-alm-1"),
+ DEFINE_RES_IRQ_NAMED(BD73800_INT_RTC2, "bd70528-rtc-alm-2"),
+};
+
static const struct resource bd71815_power_irqs[] = {
DEFINE_RES_IRQ_NAMED(BD71815_INT_DCIN_RMV, "bd71815-dcin-rmv"),
DEFINE_RES_IRQ_NAMED(BD71815_INT_CLPS_OUT, "bd71815-dcin-clps-out"),
@@ -230,6 +237,17 @@ static const struct mfd_cell bd72720_mfd_cells[] = {
/* Power button is registered separately */
};
+static const struct mfd_cell bd73800_mfd_cells[] = {
+ { .name = "bd73800-pmic", },
+ { .name = "bd73800-clk", },
+ { .name = "bd73800-gpio", },
+ {
+ .name = "bd73800-rtc",
+ .num_resources = ARRAY_SIZE(bd73800_rtc_irqs),
+ .resources = bd73800_rtc_irqs,
+ },
+};
+
static const struct regmap_range bd71815_volatile_ranges[] = {
regmap_reg_range(BD71815_REG_SEC, BD71815_REG_YEAR),
regmap_reg_range(BD71815_REG_CONF, BD71815_REG_BAT_TEMP),
@@ -339,6 +357,17 @@ static const struct regmap_range bd72720_volatile_ranges_4c[] = {
BD72720_UNWRAP_REG_RANGE(BD72720_REG_IMPCHK_CTRL, BD72720_REG_IMPCHK_CTRL),
};
+static const struct regmap_range bd73800_volatile_ranges[] = {
+ regmap_reg_range(BD73800_REG_POR_REASON, BD73800_REG_POW_STATE),
+ regmap_reg_range(BD73800_REG_PS_CTRL_1, BD73800_REG_PS_CTRL_2),
+ regmap_reg_range(BD73800_REG_RCVNUM, BD73800_REG_RCVNUM),
+ regmap_reg_range(BD73800_REG_RTC_SEC, BD73800_REG_RTC_YEAR),
+ regmap_reg_range(BD73800_REG_RTC_CONF, BD73800_REG_RTC_CONF),
+ regmap_reg_range(BD73800_REG_ADC_ACCUM_KICK, BD73800_REG_ADC_TEMP_VAL0),
+ regmap_reg_range(BD73800_REG_INT_MAIN_STAT, BD73800_REG_INT_5_STAT),
+ regmap_reg_range(BD73800_REG_INT_MAIN_SRC, BD73800_REG_INT_5_SRC),
+};
+
static const struct regmap_access_table bd71815_volatile_regs = {
.yes_ranges = &bd71815_volatile_ranges[0],
.n_yes_ranges = ARRAY_SIZE(bd71815_volatile_ranges),
@@ -364,6 +393,24 @@ static const struct regmap_access_table bd72720_volatile_regs_4c = {
.n_yes_ranges = ARRAY_SIZE(bd72720_volatile_ranges_4c),
};
+static const struct regmap_access_table bd73800_volatile_regs = {
+ .yes_ranges = bd73800_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(bd73800_volatile_ranges),
+};
+
+static const struct regmap_range bd73800_read_only_ranges[] = {
+ regmap_reg_range(BD73800_REG_PRODUCT_ID, BD73800_REG_NVMVERSION),
+ regmap_reg_range(BD73800_REG_POW_STATE, BD73800_REG_POW_STATE),
+ regmap_reg_range(BD73800_REG_ADC_ACCUM_CNT2, BD73800_REG_ADC_TEMP_VAL0),
+ regmap_reg_range(BD73800_REG_INT_MAIN_STAT, BD73800_REG_INT_MAIN_STAT),
+ regmap_reg_range(BD73800_REG_INT_MAIN_SRC, BD73800_REG_INT_5_SRC),
+};
+
+static const struct regmap_access_table bd73800_ro_regs = {
+ .no_ranges = bd73800_read_only_ranges,
+ .n_no_ranges = ARRAY_SIZE(bd73800_read_only_ranges),
+};
+
static const struct regmap_config bd71815_regmap = {
.reg_bits = 8,
.val_bits = 8,
@@ -448,6 +495,15 @@ static const struct regmap_config bd72720_regmap_4c = {
.cache_type = REGCACHE_MAPLE,
};
+static const struct regmap_config bd73800_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &bd73800_ro_regs,
+ .volatile_table = &bd73800_volatile_regs,
+ .max_register = BD73800_MAX_REGISTER - 1,
+ .cache_type = REGCACHE_MAPLE,
+};
+
/*
* Mapping of main IRQ register bits to sub-IRQ register offsets so that we can
* access corect sub-IRQ registers based on bits that are set in main IRQ
@@ -779,6 +835,60 @@ static int bd72720_set_type_config(unsigned int **buf, unsigned int type,
return regmap_irq_set_type_config_simple(buf, type, irq_data, idx, irq_drv_data);
}
+static const struct regmap_irq bd73800_irqs[] = {
+ /* INT_STAT_1 register IRQs, ADC and RTC */
+ REGMAP_IRQ_REG(BD73800_INT_ADC_ACCUM_DONE, 0, BD73800_INT_ADC_ACCUM_DONE_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_ADC_ACCUM_OVF, 0, BD73800_INT_ADC_ACCUM_OVF_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_ADC_ACCUM_VAL, 0, BD73800_INT_ADC_ACCUM_VAL_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_ADC_ACCUM_TW, 0, BD73800_INT_ADC_ACCUM_TW_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_ADC_POW_VAL, 0, BD73800_INT_ADC_POW_VAL_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_RTC0, 0, BD73800_INT_RTC0_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_RTC1, 0, BD73800_INT_RTC1_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_RTC2, 0, BD73800_INT_RTC2_MASK),
+
+ /* BUCK reg interrupts */
+ /* INT_STAT_2 IRQs */
+ REGMAP_IRQ_REG(BD73800_INT_BUCK1_DVS_DONE, 1, BD73800_INT_BUCK1_DVS_DONE_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK2_DVS_DONE, 1, BD73800_INT_BUCK2_DVS_DONE_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK3_DVS_DONE, 1, BD73800_INT_BUCK3_DVS_DONE_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK4_DVS_DONE, 1, BD73800_INT_BUCK4_DVS_DONE_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK5_DVS_DONE, 1, BD73800_INT_BUCK5_DVS_DONE_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK6_DVS_DONE, 1, BD73800_INT_BUCK6_DVS_DONE_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK7_DVS_DONE, 1, BD73800_INT_BUCK7_DVS_DONE_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK8_DVS_DONE, 1, BD73800_INT_BUCK8_DVS_DONE_MASK),
+ /* INT_STAT_3 IRQs */
+ REGMAP_IRQ_REG(BD73800_INT_BUCK1_OCP, 2, BD73800_INT_BUCK1_OCP_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK2_OCP, 2, BD73800_INT_BUCK2_OCP_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK3_OCP, 2, BD73800_INT_BUCK3_OCP_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK4_OCP, 2, BD73800_INT_BUCK4_OCP_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK5_OCP, 2, BD73800_INT_BUCK5_OCP_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK6_OCP, 2, BD73800_INT_BUCK6_OCP_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK7_OCP, 2, BD73800_INT_BUCK7_OCP_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_BUCK8_OCP, 2, BD73800_INT_BUCK8_OCP_MASK),
+
+ /* INT_STAT_4 IRQs, power-button, WDG and reset */
+ REGMAP_IRQ_REG(BD73800_INT_PBTN_LONG_PRESS, 3, BD73800_INT_PBTN_LONG_PRESS_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_PBTN_MID_PRESS, 3, BD73800_INT_PBTN_MID_PRESS_MASK),
+ /*
+ * The SHORT_PUSH is generated when button is first pressed (longer
+ * than configured time limit), and then released before the MID_PRESS
+ * time limit. The SHORT_PRESS is generated immediately when button is
+ * pressed for longer than configured limit, whether it is released or
+ * not.
+ */
+ REGMAP_IRQ_REG(BD73800_INT_PBTN_SHORT_PUSH, 3, BD73800_INT_PBTN_SHORT_PUSH_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_PBTN_SHORT_PRESS, 3, BD73800_INT_PBTN_SHORT_PRESS_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_WDG, 3, BD73800_INT_WDG_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_SWRESET, 3, BD73800_INT_SWRESET_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_SEQ_DONE, 3, BD73800_INT_SEQ_DONE_MASK),
+
+ /* INT_STAT_5 IRQs, GPIO */
+ REGMAP_IRQ_REG(BD73800_INT_GPIO1, 4, BD73800_INT_GPIO1_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_GPIO2, 4, BD73800_INT_GPIO2_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_GPIO3, 4, BD73800_INT_GPIO3_MASK),
+ REGMAP_IRQ_REG(BD73800_INT_GPIO4, 4, BD73800_INT_GPIO4_MASK),
+};
+
static const struct regmap_irq_chip bd71828_irq_chip = {
.name = "bd71828_irq",
.main_status = BD71828_REG_INT_MAIN,
@@ -833,6 +943,25 @@ static const struct regmap_irq_chip bd72720_irq_chip = {
.irq_reg_stride = 1,
};
+static const struct regmap_irq_chip bd73800_irq_chip = {
+ .name = "bd73800_irq",
+ .main_status = BD73800_REG_INT_MAIN_STAT,
+ .irqs = bd73800_irqs,
+ .num_irqs = ARRAY_SIZE(bd73800_irqs),
+ .status_base = BD73800_REG_INT_1_STAT,
+ .unmask_base = BD73800_REG_INT_1_EN,
+ .ack_base = BD73800_REG_INT_1_STAT,
+ .init_ack_masked = true,
+ .num_regs = 5,
+ .num_main_regs = 1,
+ /*
+ * Ignore mirrored bits [7:5]. They are handled as part of normal INT_4
+ * handling.
+ */
+ .num_main_status_bits = 5,
+ .irq_reg_stride = 1,
+};
+
static int set_clk_mode(struct device *dev, struct regmap *regmap,
int clkmode_reg)
{
@@ -971,6 +1100,17 @@ static int bd71828_i2c_probe(struct i2c_client *i2c)
main_lvl_val = BD72720_MASK_LVL1_EN_ALL;
break;
}
+ case ROHM_CHIP_TYPE_BD73800:
+ mfd = bd73800_mfd_cells;
+ cells = ARRAY_SIZE(bd73800_mfd_cells);
+ regmap_config = &bd73800_regmap;
+ irqchip = &bd73800_irq_chip;
+ clkmode_reg = BD73800_REG_OUT32K;
+ button_irq = BD73800_INT_PBTN_SHORT_PUSH;
+ main_lvl_mask_reg = BD73800_REG_INT_MAIN_EN;
+ main_lvl_val = BD73800_INT_MAIN_EN_ALL;
+ break;
+
default:
dev_err(&i2c->dev, "Unknown device type");
return -EINVAL;
@@ -1051,6 +1191,9 @@ static const struct of_device_id bd71828_of_match[] = {
}, {
.compatible = "rohm,bd72720",
.data = (void *)ROHM_CHIP_TYPE_BD72720,
+ }, {
+ .compatible = "rohm,bd73800",
+ .data = (void *)ROHM_CHIP_TYPE_BD73800,
},
{ },
};
diff --git a/drivers/pinctrl/freescale/pinctrl-imx.c b/drivers/pinctrl/freescale/pinctrl-imx.c
index 9a45b376d36f..f08ef52bf4f8 100644
--- a/drivers/pinctrl/freescale/pinctrl-imx.c
+++ b/drivers/pinctrl/freescale/pinctrl-imx.c
@@ -21,6 +21,7 @@
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
@@ -291,8 +292,8 @@ struct pinmux_ops imx_pmx_ops = {
.set_mux = imx_pmx_set,
};
-static int imx_pinconf_get_mmio(struct pinctrl_dev *pctldev, unsigned pin_id,
- unsigned long *config)
+static int imx_pinconf_get_raw_mmio(struct pinctrl_dev *pctldev,
+ unsigned int pin_id, unsigned long *config)
{
struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
const struct imx_pinctrl_soc_info *info = ipctl->info;
@@ -312,6 +313,39 @@ static int imx_pinconf_get_mmio(struct pinctrl_dev *pctldev, unsigned pin_id,
return 0;
}
+static int imx_pinconf_get_mmio(struct pinctrl_dev *pctldev,
+ unsigned int pin_id, unsigned long *config)
+{
+ struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct imx_pinctrl_soc_info *info = ipctl->info;
+ const struct imx_pin_reg *pin_reg = &ipctl->pin_regs[pin_id];
+ enum pin_config_param param = pinconf_to_config_param(*config);
+ unsigned int mask;
+ u32 raw;
+
+ switch (param) {
+ case PIN_CONFIG_OUTPUT_ENABLE:
+ mask = info->obe_mask;
+ break;
+ case PIN_CONFIG_INPUT_ENABLE:
+ mask = info->ibe_mask;
+ break;
+ default:
+ mask = 0;
+ break;
+ }
+
+ if (!mask)
+ return -ENOTSUPP;
+ if (pin_reg->conf_reg == -1)
+ return -EINVAL;
+
+ raw = readl(ipctl->base + pin_reg->conf_reg);
+ *config = pinconf_to_config_packed(param, !!(raw & mask));
+
+ return 0;
+}
+
static int imx_pinconf_get(struct pinctrl_dev *pctldev,
unsigned pin_id, unsigned long *config)
{
@@ -319,9 +353,21 @@ static int imx_pinconf_get(struct pinctrl_dev *pctldev,
const struct imx_pinctrl_soc_info *info = ipctl->info;
if (info->flags & IMX_USE_SCU)
+ return -ENOTSUPP;
+
+ return imx_pinconf_get_mmio(pctldev, pin_id, config);
+}
+
+static int imx_pinconf_get_raw(struct pinctrl_dev *pctldev,
+ unsigned int pin_id, unsigned long *config)
+{
+ struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+ const struct imx_pinctrl_soc_info *info = ipctl->info;
+
+ if (info->flags & IMX_USE_SCU)
return info->imx_pinconf_get(pctldev, pin_id, config);
else
- return imx_pinconf_get_mmio(pctldev, pin_id, config);
+ return imx_pinconf_get_raw_mmio(pctldev, pin_id, config);
}
static int imx_pinconf_set_mmio(struct pinctrl_dev *pctldev,
@@ -426,7 +472,7 @@ static void imx_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
struct imx_pin *pin = &((struct imx_pin *)(grp->data))[i];
name = pin_get_name(pctldev, pin->pin);
- ret = imx_pinconf_get(pctldev, pin->pin, &config);
+ ret = imx_pinconf_get_raw(pctldev, pin->pin, &config);
if (ret)
return;
seq_printf(s, " %s: 0x%lx\n", name, config);
diff --git a/drivers/pinctrl/freescale/pinctrl-imx.h b/drivers/pinctrl/freescale/pinctrl-imx.h
index f65ff45b4003..8fa7e1e2521d 100644
--- a/drivers/pinctrl/freescale/pinctrl-imx.h
+++ b/drivers/pinctrl/freescale/pinctrl-imx.h
@@ -91,6 +91,10 @@ struct imx_pinctrl_soc_info {
unsigned int mux_mask;
u8 mux_shift;
+ /* OBE/IBE bits in the conf register, 0 if the pad does not have them */
+ unsigned int obe_mask;
+ unsigned int ibe_mask;
+
int (*gpio_set_direction)(struct pinctrl_dev *pctldev,
struct pinctrl_gpio_range *range,
unsigned offset,
diff --git a/drivers/pinctrl/freescale/pinctrl-vf610.c b/drivers/pinctrl/freescale/pinctrl-vf610.c
index 76a4bc0181a0..77d077618782 100644
--- a/drivers/pinctrl/freescale/pinctrl-vf610.c
+++ b/drivers/pinctrl/freescale/pinctrl-vf610.c
@@ -319,6 +319,8 @@ static const struct imx_pinctrl_soc_info vf610_pinctrl_info = {
.gpio_set_direction = vf610_pmx_gpio_set_direction,
.mux_mask = 0x700000,
.mux_shift = 20,
+ .obe_mask = 0x2,
+ .ibe_mask = 0x1,
};
static const struct of_device_id vf610_pinctrl_of_match[] = {
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 89789ac7a786..13928af8cad4 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -241,12 +241,12 @@ config REGULATOR_BD71815
will be called bd71815-regulator.
config REGULATOR_BD71828
- tristate "ROHM BD71828, BD72720 and BD73900 Power Regulators"
+ tristate "ROHM BD71828, BD72720 and BD73[8/9]00 Power Regulators"
depends on MFD_ROHM_BD71828
select REGULATOR_ROHM
help
This driver supports voltage regulators on ROHM BD71828,
- BD71879, BD72720 and BD73900 PMICs. This will enable
+ BD71879, BD72720 and BD73[8/9]00 PMICs. This will enable
support for the software controllable buck and LDO regulators.
This driver can also be built as a module. If so, the module
diff --git a/drivers/regulator/bd71828-regulator.c b/drivers/regulator/bd71828-regulator.c
index 2ced81df0c02..dea551cf658a 100644
--- a/drivers/regulator/bd71828-regulator.c
+++ b/drivers/regulator/bd71828-regulator.c
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2019 ROHM Semiconductors
// bd71828-regulator.c ROHM BD71828GW-DS1 regulator driver
-//
#include <linux/cleanup.h>
#include <linux/delay.h>
@@ -10,6 +9,7 @@
#include <linux/kernel.h>
#include <linux/mfd/rohm-bd71828.h>
#include <linux/mfd/rohm-bd72720.h>
+#include <linux/mfd/rohm-bd73800.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -31,6 +31,13 @@ struct bd71828_regulator_data {
int reg_init_amnt;
};
+/*
+ * LDO1 and LDO3 node names are used also in the driver to find OTP setting for
+ * the used voltage range.
+ */
+#define LDO1_NODE_NAME "ldo1"
+#define LDO3_NODE_NAME "ldo3"
+
static const struct reg_init bd71828_buck1_inits[] = {
/*
* DVS Buck voltages can be changed by register values or via GPIO.
@@ -172,6 +179,55 @@ static const struct linear_range bd72720_ldo6_volts[] = {
REGULATOR_LINEAR_RANGE(1800000, 0x79, 0x7f, 0),
};
+static const struct linear_range bd73800_buck12348_volts[] = {
+ REGULATOR_LINEAR_RANGE(500000, 0x00, 0x50, 10000),
+ REGULATOR_LINEAR_RANGE(1300000, 0x51, 0xff, 0),
+};
+
+static const unsigned int bd73800_buck5_volt_range_sel[] = {
+ 0x0, 0x0, 0x1, 0x1,
+};
+
+static const struct linear_range bd73800_buck5_volts[] = {
+ /* range selector 0 */
+ REGULATOR_LINEAR_RANGE(500000, 0x00, 0x50, 10000),
+ REGULATOR_LINEAR_RANGE(1300000, 0x51, 0x7f, 0),
+ /* range selector 1 */
+ REGULATOR_LINEAR_RANGE(300000, 0x00, 0x46, 10000),
+ REGULATOR_LINEAR_RANGE(1000000, 0x47, 0x7f, 0),
+};
+
+/*
+ * BUCK5 has two different pickable ranges, each having voltages matching
+ * selectors 0x0 to 0x7f. This gives 0x7f + 1 different voltages for each of
+ * the ranges.
+ */
+#define BD73800_NUM_BUCK5_VOLTS ((0x7f + 1) * 2)
+
+static const struct linear_range bd73800_buck67_volts[] = {
+ REGULATOR_LINEAR_RANGE(1500000, 0x00, 0xc8, 10000),
+ REGULATOR_LINEAR_RANGE(3500000, 0xc9, 0xff, 0),
+};
+
+/*
+ * On the BD73800 the LDO1 and LDO3 support two different voltage areas.
+ * Whether to use 'high' or 'low' ranges is configured by OTP. There seems
+ * to be no register to read the configured area so it must be given via
+ * device-tree properties.
+ */
+static const struct linear_range bd73800_ldo13_low_volts[] = {
+ REGULATOR_LINEAR_RANGE(600000, 0x00, 0x78, 10000),
+ REGULATOR_LINEAR_RANGE(1800000, 0x79, 0xff, 0),
+};
+
+static const struct linear_range bd73800_ldo13_high_volts[] = {
+ REGULATOR_LINEAR_RANGE(750000, 0x00, 0xff, 10000),
+};
+
+static const struct linear_range bd73800_ldo24_volts[] = {
+ REGULATOR_LINEAR_RANGE(750000, 0x00, 0xff, 10000),
+};
+
static const unsigned int bd71828_ramp_delay[] = { 2500, 5000, 10000, 20000 };
/*
@@ -225,6 +281,36 @@ static int bd71828_ldo6_parse_dt(struct device_node *np,
return 0;
}
+/* Operations for all other BUCKs but BUCK 5 */
+static const struct regulator_ops bd73800_buck_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
+};
+
+static const struct regulator_ops bd73800_buck5_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_pickable_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_pickable_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,
+ .set_ramp_delay = regulator_set_ramp_delay_regmap,
+};
+
+static const struct regulator_ops bd73800_ldo_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .list_voltage = regulator_list_voltage_linear_range,
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+};
+
static const struct regulator_ops bd71828_buck_ops = {
.enable = regulator_enable_regmap,
.disable = regulator_disable_regmap,
@@ -1565,6 +1651,412 @@ static const struct bd71828_regulator_data bd72720_rdata[] = {
},
};
+static const struct bd71828_regulator_data bd73800_rdata[] = {
+ {
+ .desc = {
+ .name = "buck1",
+ .of_match = of_match_ptr("buck1"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD73800_BUCK1,
+ .ops = &bd73800_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_ranges = bd73800_buck12348_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd73800_buck12348_volts),
+ .n_voltages = BD73800_NUM_VOLTS,
+ .enable_reg = BD73800_REG_BUCK1_ON,
+ .enable_mask = BD73800_MASK_RUN_EN,
+ .vsel_reg = BD73800_REG_BUCK1_VOLT_RUN,
+ .vsel_mask = BD73800_MASK_VOLT,
+ .ramp_delay_table = bd71828_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay),
+ .ramp_reg = BD73800_REG_BUCK1_MODE,
+ .ramp_mask = BD73800_MASK_RAMP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD73800_REG_BUCK1_VOLT_RUN,
+ .run_mask = BD73800_MASK_VOLT,
+ .idle_reg = BD73800_REG_BUCK1_VOLT_IDLE,
+ .idle_mask = BD73800_MASK_VOLT,
+ .idle_on_mask = BD73800_MASK_IDLE_EN,
+ .suspend_reg = BD73800_REG_BUCK1_VOLT_SUSP,
+ .suspend_mask = BD73800_MASK_VOLT,
+ .suspend_on_mask = BD73800_MASK_SUSP_EN,
+ },
+ }, {
+ .desc = {
+ .name = "buck2",
+ .of_match = of_match_ptr("buck2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD73800_BUCK2,
+ .ops = &bd73800_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_ranges = bd73800_buck12348_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd73800_buck12348_volts),
+ .n_voltages = BD73800_NUM_VOLTS,
+ .enable_reg = BD73800_REG_BUCK2_ON,
+ .enable_mask = BD73800_MASK_RUN_EN,
+ .vsel_reg = BD73800_REG_BUCK2_VOLT_RUN,
+ .vsel_mask = BD73800_MASK_VOLT,
+ .ramp_delay_table = bd71828_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay),
+ .ramp_reg = BD73800_REG_BUCK2_MODE,
+ .ramp_mask = BD73800_MASK_RAMP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD73800_REG_BUCK2_VOLT_RUN,
+ .run_mask = BD73800_MASK_VOLT,
+ .idle_reg = BD73800_REG_BUCK2_VOLT_IDLE,
+ .idle_mask = BD73800_MASK_VOLT,
+ .idle_on_mask = BD73800_MASK_IDLE_EN,
+ .suspend_reg = BD73800_REG_BUCK2_VOLT_SUSP,
+ .suspend_mask = BD73800_MASK_VOLT,
+ .suspend_on_mask = BD73800_MASK_SUSP_EN,
+ },
+ }, {
+ .desc = {
+ .name = "buck3",
+ .of_match = of_match_ptr("buck3"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD73800_BUCK3,
+ .ops = &bd73800_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_ranges = bd73800_buck12348_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd73800_buck12348_volts),
+ .n_voltages = BD73800_NUM_VOLTS,
+ .enable_reg = BD73800_REG_BUCK3_ON,
+ .enable_mask = BD73800_MASK_RUN_EN,
+ .vsel_reg = BD73800_REG_BUCK3_VOLT_RUN,
+ .vsel_mask = BD73800_MASK_VOLT,
+ .ramp_delay_table = bd71828_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay),
+ .ramp_reg = BD73800_REG_BUCK3_MODE,
+ .ramp_mask = BD73800_MASK_RAMP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD73800_REG_BUCK3_VOLT_RUN,
+ .run_mask = BD73800_MASK_VOLT,
+ .idle_reg = BD73800_REG_BUCK3_VOLT_IDLE,
+ .idle_mask = BD73800_MASK_VOLT,
+ .idle_on_mask = BD73800_MASK_IDLE_EN,
+ .suspend_reg = BD73800_REG_BUCK3_VOLT_SUSP,
+ .suspend_mask = BD73800_MASK_VOLT,
+ .suspend_on_mask = BD73800_MASK_SUSP_EN,
+ },
+
+ }, {
+ .desc = {
+ .name = "buck4",
+ .of_match = of_match_ptr("buck4"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD73800_BUCK4,
+ .ops = &bd73800_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_ranges = bd73800_buck12348_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd73800_buck12348_volts),
+ .n_voltages = BD73800_NUM_VOLTS,
+ .enable_reg = BD73800_REG_BUCK4_ON,
+ .enable_mask = BD73800_MASK_RUN_EN,
+ .vsel_reg = BD73800_REG_BUCK4_VOLT_RUN,
+ .vsel_mask = BD73800_MASK_VOLT,
+ .ramp_delay_table = bd71828_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay),
+ .ramp_reg = BD73800_REG_BUCK4_MODE,
+ .ramp_mask = BD73800_MASK_RAMP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD73800_REG_BUCK4_VOLT_RUN,
+ .run_mask = BD73800_MASK_VOLT,
+ .idle_reg = BD73800_REG_BUCK4_VOLT_IDLE,
+ .idle_mask = BD73800_MASK_VOLT,
+ .idle_on_mask = BD73800_MASK_IDLE_EN,
+ .suspend_reg = BD73800_REG_BUCK4_VOLT_SUSP,
+ .suspend_mask = BD73800_MASK_VOLT,
+ .suspend_on_mask = BD73800_MASK_SUSP_EN,
+ },
+ }, {
+ .desc = {
+ .name = "buck5",
+ .of_match = of_match_ptr("buck5"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD73800_BUCK5,
+ .ops = &bd73800_buck5_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_ranges = bd73800_buck5_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd73800_buck5_volts),
+ .linear_range_selectors_bitfield =
+ bd73800_buck5_volt_range_sel,
+ .n_voltages = BD73800_NUM_BUCK5_VOLTS,
+ .enable_reg = BD73800_REG_BUCK5_ON,
+ .enable_mask = BD73800_MASK_RUN_EN,
+ .vsel_reg = BD73800_REG_BUCK5_VOLT_RUN,
+ .vsel_mask = BD73800_MASK_BUCK5_VOLT,
+ .vsel_range_reg = BD73800_REG_BUCK5_MODE,
+ .vsel_range_mask = BD73800_BUCK5_RANGE_MASK,
+ .range_applied_by_vsel = true,
+ .ramp_delay_table = bd71828_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay),
+ .ramp_reg = BD73800_REG_BUCK5_MODE,
+ .ramp_mask = BD73800_MASK_RAMP_DELAY,
+ .owner = THIS_MODULE,
+ },
+ .dvs = {
+ /*
+ * We don't support setting the IDLE / SUSPEND voltages
+ * for the BUCK 5 for now. Reason is that the pickable
+ * range selector bit is common for all states (RUN,
+ * SUSPEND and IDLE), and toggling it for one impacts
+ * also others.
+ * Furthermore, writing the BD73800 range selector does
+ * not impact the RUN voltage until a new voltage value
+ * is also written to the vsel register. Hence the
+ * voltage change can be done without any 'intermediate
+ * voltages', even when the range is changed when the
+ * BUCK is enabled.
+ * It, however, is not documented how the IDLE / SUSPEND
+ * states work in this regard. I expect the full
+ * combination of the range selector and vsel is taken
+ * into account at the state change, no matter when they
+ * have been written to.
+ */
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD73800_REG_BUCK5_VOLT_RUN,
+ .run_mask = BD73800_MASK_VOLT,
+ .idle_on_mask = BD73800_MASK_IDLE_EN,
+ .suspend_on_mask = BD73800_MASK_SUSP_EN,
+ },
+
+ }, {
+ .desc = {
+ .name = "buck6",
+ .of_match = of_match_ptr("buck6"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD73800_BUCK6,
+ .ops = &bd73800_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_ranges = bd73800_buck67_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd73800_buck67_volts),
+ .n_voltages = BD73800_NUM_VOLTS,
+ .enable_reg = BD73800_REG_BUCK6_ON,
+ .enable_mask = BD73800_MASK_RUN_EN,
+ .vsel_reg = BD73800_REG_BUCK6_VOLT_RUN,
+ .vsel_mask = BD73800_MASK_VOLT,
+ .ramp_delay_table = bd71828_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay),
+ .ramp_reg = BD73800_REG_BUCK6_MODE,
+ .ramp_mask = BD73800_MASK_RAMP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD73800_REG_BUCK6_VOLT_RUN,
+ .run_mask = BD73800_MASK_VOLT,
+ .idle_reg = BD73800_REG_BUCK6_VOLT_IDLE,
+ .idle_mask = BD73800_MASK_VOLT,
+ .idle_on_mask = BD73800_MASK_IDLE_EN,
+ .suspend_reg = BD73800_REG_BUCK6_VOLT_SUSP,
+ .suspend_mask = BD73800_MASK_VOLT,
+ .suspend_on_mask = BD73800_MASK_SUSP_EN,
+ },
+ }, {
+ .desc = {
+ .name = "buck7",
+ .of_match = of_match_ptr("buck7"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD73800_BUCK7,
+ .ops = &bd73800_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_ranges = bd73800_buck67_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd73800_buck67_volts),
+ .n_voltages = BD73800_NUM_VOLTS,
+ .enable_reg = BD73800_REG_BUCK7_ON,
+ .enable_mask = BD73800_MASK_RUN_EN,
+ .vsel_reg = BD73800_REG_BUCK7_VOLT_RUN,
+ .vsel_mask = BD73800_MASK_VOLT,
+ .ramp_delay_table = bd71828_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay),
+ .ramp_reg = BD73800_REG_BUCK7_MODE,
+ .ramp_mask = BD73800_MASK_RAMP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD73800_REG_BUCK7_VOLT_RUN,
+ .run_mask = BD73800_MASK_VOLT,
+ .idle_reg = BD73800_REG_BUCK7_VOLT_IDLE,
+ .idle_mask = BD73800_MASK_VOLT,
+ .idle_on_mask = BD73800_MASK_IDLE_EN,
+ .suspend_reg = BD73800_REG_BUCK7_VOLT_SUSP,
+ .suspend_mask = BD73800_MASK_VOLT,
+ .suspend_on_mask = BD73800_MASK_SUSP_EN,
+ },
+ }, {
+ .desc = {
+ .name = "buck8",
+ .of_match = of_match_ptr("buck8"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD73800_BUCK8,
+ .ops = &bd73800_buck_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_ranges = bd73800_buck12348_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd73800_buck12348_volts),
+ .n_voltages = BD73800_NUM_VOLTS,
+ .enable_reg = BD73800_REG_BUCK8_ON,
+ .enable_mask = BD73800_MASK_RUN_EN,
+ .vsel_reg = BD73800_REG_BUCK8_VOLT_RUN,
+ .vsel_mask = BD73800_MASK_VOLT,
+ .ramp_delay_table = bd71828_ramp_delay,
+ .n_ramp_values = ARRAY_SIZE(bd71828_ramp_delay),
+ .ramp_reg = BD73800_REG_BUCK8_MODE,
+ .ramp_mask = BD73800_MASK_RAMP_DELAY,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD73800_REG_BUCK8_VOLT_RUN,
+ .run_mask = BD73800_MASK_VOLT,
+ .idle_reg = BD73800_REG_BUCK8_VOLT_IDLE,
+ .idle_mask = BD73800_MASK_VOLT,
+ .idle_on_mask = BD73800_MASK_IDLE_EN,
+ .suspend_reg = BD73800_REG_BUCK8_VOLT_SUSP,
+ .suspend_mask = BD73800_MASK_VOLT,
+ .suspend_on_mask = BD73800_MASK_SUSP_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo1",
+ .of_match = of_match_ptr(LDO1_NODE_NAME),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD73800_LDO1,
+ .ops = &bd73800_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_ranges = bd73800_ldo13_low_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd73800_ldo13_low_volts),
+ .n_voltages = BD73800_NUM_VOLTS,
+ .enable_reg = BD73800_REG_LDO1_ON,
+ .enable_mask = BD73800_MASK_RUN_EN,
+ .vsel_reg = BD73800_REG_LDO1_VOLT,
+ .vsel_mask = BD73800_MASK_VOLT,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ /*
+ * LDOs support only single voltage for all states.
+ * Voltage can be individually enabled for each state
+ * though. Allow setting enable/disable config by
+ * setting the <state>_on_mask for all supported states.
+ */
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD73800_REG_LDO1_VOLT,
+ .run_mask = BD73800_MASK_VOLT,
+ .idle_on_mask = BD73800_MASK_IDLE_EN,
+ .suspend_on_mask = BD73800_MASK_SUSP_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo2",
+ .of_match = of_match_ptr("ldo2"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD73800_LDO2,
+ .ops = &bd73800_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_ranges = bd73800_ldo24_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd73800_ldo24_volts),
+ .n_voltages = BD73800_NUM_VOLTS,
+ .enable_reg = BD73800_REG_LDO2_ON,
+ .enable_mask = BD73800_MASK_RUN_EN,
+ .vsel_reg = BD73800_REG_LDO2_VOLT,
+ .vsel_mask = BD73800_MASK_VOLT,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD73800_REG_LDO2_VOLT,
+ .run_mask = BD73800_MASK_VOLT,
+ .idle_on_mask = BD73800_MASK_IDLE_EN,
+ .suspend_on_mask = BD73800_MASK_SUSP_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo3",
+ .of_match = of_match_ptr(LDO3_NODE_NAME),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD73800_LDO3,
+ .ops = &bd73800_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_ranges = bd73800_ldo13_low_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd73800_ldo13_low_volts),
+ .n_voltages = BD73800_NUM_VOLTS,
+ .enable_reg = BD73800_REG_LDO3_ON,
+ .enable_mask = BD73800_MASK_RUN_EN,
+ .vsel_reg = BD73800_REG_LDO3_VOLT,
+ .vsel_mask = BD73800_MASK_VOLT,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD73800_REG_LDO3_VOLT,
+ .run_mask = BD73800_MASK_VOLT,
+ .idle_on_mask = BD73800_MASK_IDLE_EN,
+ .suspend_on_mask = BD73800_MASK_SUSP_EN,
+ },
+ }, {
+ .desc = {
+ .name = "ldo4",
+ .of_match = of_match_ptr("ldo4"),
+ .regulators_node = of_match_ptr("regulators"),
+ .id = BD73800_LDO4,
+ .ops = &bd73800_ldo_ops,
+ .type = REGULATOR_VOLTAGE,
+ .linear_ranges = bd73800_ldo24_volts,
+ .n_linear_ranges = ARRAY_SIZE(bd73800_ldo24_volts),
+ .n_voltages = BD73800_NUM_VOLTS,
+ .enable_reg = BD73800_REG_LDO4_ON,
+ .enable_mask = BD73800_MASK_RUN_EN,
+ .vsel_reg = BD73800_REG_LDO4_VOLT,
+ .vsel_mask = BD73800_MASK_VOLT,
+ .owner = THIS_MODULE,
+ .of_parse_cb = buck_set_hw_dvs_levels,
+ },
+ .dvs = {
+ .level_map = ROHM_DVS_LEVEL_RUN | ROHM_DVS_LEVEL_IDLE |
+ ROHM_DVS_LEVEL_SUSPEND,
+ .run_reg = BD73800_REG_LDO4_VOLT,
+ .run_mask = BD73800_MASK_VOLT,
+ .idle_on_mask = BD73800_MASK_IDLE_EN,
+ .suspend_on_mask = BD73800_MASK_SUSP_EN,
+ },
+ },
+};
+
static int bd72720_buck10_ldon_head_mode(struct device *dev,
struct device_node *npreg,
struct regmap *regmap,
@@ -1619,6 +2111,52 @@ static int bd72720_dt_parse(struct device *dev,
return bd72720_buck10_ldon_head_mode(dev, nproot, regmap, buck10_desc);
}
+static bool bd73800_use_ldo_high_range(struct device_node *nproot,
+ const char *ldo_node)
+{
+ struct device_node *np __free(device_node) =
+ of_get_child_by_name(nproot, ldo_node);
+
+ if (!np)
+ return false;
+
+ return of_property_read_bool(np, "rohm,ldo-range-high");
+}
+
+static int bd73800_check_ldo_otp_options(struct device *dev,
+ struct bd71828_regulator_data *d,
+ unsigned int num_reg_data)
+{
+ struct device_node *nproot __free(device_node) =
+ of_get_child_by_name(dev->parent->of_node, "regulators");
+
+ /*
+ * The code assumes regulator IDs to start from 0 and to match the
+ * indexing of regulator data arrays. WARN if someone changes this.
+ */
+ WARN_ON(d[BD73800_LDO1].desc.id != BD73800_LDO1);
+ WARN_ON(d[BD73800_LDO3].desc.id != BD73800_LDO3);
+
+ if (!nproot) {
+ dev_err(dev, "failed to find regulators node\n");
+ return -ENODEV;
+ }
+
+ if (bd73800_use_ldo_high_range(nproot, LDO1_NODE_NAME)) {
+ d[BD73800_LDO1].desc.linear_ranges = bd73800_ldo13_high_volts;
+ d[BD73800_LDO1].desc.n_linear_ranges =
+ ARRAY_SIZE(bd73800_ldo13_high_volts);
+ }
+
+ if (bd73800_use_ldo_high_range(nproot, LDO3_NODE_NAME)) {
+ d[BD73800_LDO3].desc.linear_ranges = bd73800_ldo13_high_volts;
+ d[BD73800_LDO3].desc.n_linear_ranges =
+ ARRAY_SIZE(bd73800_ldo13_high_volts);
+ }
+
+ return 0;
+}
+
static int bd71828_probe(struct platform_device *pdev)
{
int i, j, ret, num_regulators;
@@ -1656,6 +2194,23 @@ static int bd71828_probe(struct platform_device *pdev)
num_regulators = ARRAY_SIZE(bd71828_rdata);
break;
+
+ case ROHM_CHIP_TYPE_BD73800:
+ {
+ rdata = devm_kmemdup(&pdev->dev, bd73800_rdata,
+ sizeof(bd73800_rdata), GFP_KERNEL);
+ if (!rdata)
+ return -ENOMEM;
+
+ num_regulators = ARRAY_SIZE(bd73800_rdata);
+
+ ret = bd73800_check_ldo_otp_options(&pdev->dev, rdata,
+ num_regulators);
+ if (ret)
+ return ret;
+
+ break;
+ }
default:
return dev_err_probe(&pdev->dev, -EINVAL,
"Unsupported device\n");
@@ -1692,6 +2247,7 @@ static int bd71828_probe(struct platform_device *pdev)
static const struct platform_device_id bd71828_pmic_id[] = {
{ .name = "bd71828-pmic", .driver_data = ROHM_CHIP_TYPE_BD71828 },
{ .name = "bd72720-pmic", .driver_data = ROHM_CHIP_TYPE_BD72720 },
+ { .name = "bd73800-pmic", .driver_data = ROHM_CHIP_TYPE_BD73800 },
{ }
};
MODULE_DEVICE_TABLE(platform, bd71828_pmic_id);
diff --git a/drivers/rtc/rtc-bd70528.c b/drivers/rtc/rtc-bd70528.c
index 482810b61495..fd415e327ea6 100644
--- a/drivers/rtc/rtc-bd70528.c
+++ b/drivers/rtc/rtc-bd70528.c
@@ -8,6 +8,7 @@
#include <linux/mfd/rohm-bd71815.h>
#include <linux/mfd/rohm-bd71828.h>
#include <linux/mfd/rohm-bd72720.h>
+#include <linux/mfd/rohm-bd73800.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
@@ -284,6 +285,12 @@ static int bd70528_probe(struct platform_device *pdev)
bd_rtc->bd718xx_alm_block_start = BD72720_REG_RTC_ALM_START;
hour_reg = BD72720_REG_RTC_HOUR;
break;
+ case ROHM_CHIP_TYPE_BD73800:
+ bd_rtc->reg_time_start = BD73800_REG_RTC_START;
+ bd_rtc->bd718xx_alm_block_start = BD73800_REG_RTC_ALM_START;
+ hour_reg = BD73800_REG_RTC_HOUR;
+ break;
+
default:
dev_err(&pdev->dev, "Unknown chip\n");
return -ENOENT;
@@ -344,6 +351,7 @@ static const struct platform_device_id bd718x7_rtc_id[] = {
{ .name = "bd71828-rtc", .driver_data = ROHM_CHIP_TYPE_BD71828 },
{ .name = "bd71815-rtc", .driver_data = ROHM_CHIP_TYPE_BD71815 },
{ .name = "bd72720-rtc", .driver_data = ROHM_CHIP_TYPE_BD72720 },
+ { .name = "bd73800-rtc", .driver_data = ROHM_CHIP_TYPE_BD73800 },
{ }
};
MODULE_DEVICE_TABLE(platform, bd718x7_rtc_id);