Message ID | 20241101080614.1070819-3-andrei.stefanescu@oss.nxp.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | gpio: siul2-s32g2: add initial GPIO driver | expand |
Hi Andrei, kernel test robot noticed the following build warnings: [auto build test WARNING on linusw-pinctrl/devel] [also build test WARNING on linusw-pinctrl/for-next lee-mfd/for-mfd-next shawnguo/for-next linus/master lee-mfd/for-mfd-fixes v6.12-rc5 next-20241101] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Andrei-Stefanescu/dt-bindings-mfd-add-support-for-the-NXP-SIUL2-module/20241101-160940 base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git devel patch link: https://lore.kernel.org/r/20241101080614.1070819-3-andrei.stefanescu%40oss.nxp.com patch subject: [PATCH v5 2/7] mfd: nxp-siul2: add support for NXP SIUL2 config: m68k-allmodconfig (https://download.01.org/0day-ci/archive/20241102/202411020003.C5suQQDX-lkp@intel.com/config) compiler: m68k-linux-gcc (GCC) 14.1.0 reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241102/202411020003.C5suQQDX-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202411020003.C5suQQDX-lkp@intel.com/ All warnings (new ones prefixed by >>): drivers/mfd/nxp-siul2.c: In function 'nxp_siul2_init_regmap': >> drivers/mfd/nxp-siul2.c:318:1: warning: the frame size of 1116 bytes is larger than 1024 bytes [-Wframe-larger-than=] 318 | } | ^ -- >> drivers/mfd/nxp-siul2.c:50: warning: Function parameter or struct member 'reg_name' not described in 'nxp_siul2_reg_range_info' Kconfig warnings: (for reference only) WARNING: unmet direct dependencies detected for GET_FREE_REGION Depends on [n]: SPARSEMEM [=n] Selected by [m]: - RESOURCE_KUNIT_TEST [=m] && RUNTIME_TESTING_MENU [=y] && KUNIT [=m] vim +318 drivers/mfd/nxp-siul2.c 266 267 static int nxp_siul2_init_regmap(struct platform_device *pdev, 268 void __iomem *base, int siul) 269 { 270 struct regmap_config regmap_configs[SIUL2_NUM_REG_TYPES] = { 271 [SIUL2_MPIDR] = nxp_siul2_regmap_generic_conf, 272 [SIUL2_IRQ] = nxp_siul2_regmap_irq_conf, 273 [SIUL2_MSCR] = nxp_siul2_regmap_generic_conf, 274 [SIUL2_IMCR] = nxp_siul2_regmap_generic_conf, 275 [SIUL2_PGPDO] = nxp_siul2_regmap_pgpdo_conf, 276 [SIUL2_PGPDI] = nxp_siul2_regmap_pgpdi_conf, 277 }; 278 const struct nxp_siul2_reg_range_info *tmp_range; 279 struct regmap_config *tmp_conf; 280 struct nxp_siul2_info *info; 281 struct nxp_siul2_mfd *priv; 282 void __iomem *reg_start; 283 int i, ret; 284 285 priv = platform_get_drvdata(pdev); 286 info = &priv->siul2[siul]; 287 288 for (i = 0; i < SIUL2_NUM_REG_TYPES; i++) { 289 if (!s32g2_reg_ranges[siul][i].valid) 290 continue; 291 292 tmp_range = &s32g2_reg_ranges[siul][i]; 293 tmp_conf = ®map_configs[i]; 294 tmp_conf->name = tmp_range->reg_name; 295 tmp_conf->max_register = 296 tmp_range->reg_end_offset - tmp_range->reg_start_offset; 297 298 if (tmp_conf->cache_type != REGCACHE_NONE) 299 tmp_conf->num_reg_defaults_raw = 300 tmp_conf->max_register / tmp_conf->reg_stride; 301 302 if (tmp_range->reg_access) { 303 tmp_conf->wr_table = tmp_range->reg_access; 304 tmp_conf->rd_table = tmp_range->reg_access; 305 } 306 307 reg_start = base + tmp_range->reg_start_offset; 308 info->regmaps[i] = devm_regmap_init_mmio(&pdev->dev, reg_start, 309 tmp_conf); 310 if (IS_ERR(info->regmaps[i])) { 311 dev_err(&pdev->dev, "regmap %d init failed: %d\n", i, 312 ret); 313 return PTR_ERR(info->regmaps[i]); 314 } 315 } 316 317 return 0; > 318 } 319
On Fri, Nov 01, 2024 at 10:06:08AM +0200, Andrei Stefanescu wrote: > SIUL2 (System Integration Unit Lite) is a hardware module which > implements various functionalities: > - reading SoC information > - pinctrl > - GPIO (including interrupts) > > There are multiple register types in the SIUL2 module: > - MIDR (MCU ID Register) > * contains information about the SoC. > - Interrupt related registers > * There are 32 interrupts named EIRQ. An EIRQ > may be routed to one or more GPIOs. Not all > GPIOs have EIRQs associated with them > - MSCR (Multiplexed Signal Configuration Register) > * handle pinmuxing and pinconf > - IMCR (Input Multiplexed Signal Configuration Register) > * are part of pinmuxing > - PGPDO/PGPDI (Parallel GPIO Pad Data Out/In Register) > * Write/Read the GPIO value > > There are two SIUL2 modules in the S32G SoC. This driver handles > both because functionality is shared between them. For example: > some GPIOs in SIUL2_0 have interrupt capability but the registers > configuring this are in SIUL2_1. > > Signed-off-by: Andrei Stefanescu <andrei.stefanescu@oss.nxp.com> > --- > drivers/mfd/Kconfig | 12 + > drivers/mfd/Makefile | 1 + > drivers/mfd/nxp-siul2.c | 411 ++++++++++++++++++++++++++++++++++ > include/linux/mfd/nxp-siul2.h | 55 +++++ > 4 files changed, 479 insertions(+) > create mode 100644 drivers/mfd/nxp-siul2.c > create mode 100644 include/linux/mfd/nxp-siul2.h > > diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig > index f9325bcce1b9..fc590789e8b3 100644 > --- a/drivers/mfd/Kconfig > +++ b/drivers/mfd/Kconfig > @@ -1098,6 +1098,18 @@ config MFD_NTXEC > certain e-book readers designed by the original design manufacturer > Netronix. > > +config MFD_NXP_SIUL2 > + tristate "NXP SIUL2 MFD driver" > + select MFD_CORE > + select REGMAP_MMIO > + depends on ARCH_S32 || COMPILE_TEST > + help > + Select this to get support for the NXP SIUL2 (System Integration > + Unit Lite) module. This hardware block contains registers for > + SoC information, pinctrl and GPIO functionality. This will > + probe a MFD driver which will contain cells for a combined > + pinctrl&GPIO driver and nvmem drivers for the SoC information. > + > config MFD_RETU > tristate "Nokia Retu and Tahvo multi-function device" > select MFD_CORE > diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile > index 2a9f91e81af8..7b19ea014221 100644 > --- a/drivers/mfd/Makefile > +++ b/drivers/mfd/Makefile > @@ -226,6 +226,7 @@ obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o > obj-$(CONFIG_MFD_PALMAS) += palmas.o > obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o > obj-$(CONFIG_MFD_NTXEC) += ntxec.o > +obj-$(CONFIG_MFD_NXP_SIUL2) += nxp-siul2.o > obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o > obj-$(CONFIG_MFD_RK8XX) += rk8xx-core.o > obj-$(CONFIG_MFD_RK8XX_I2C) += rk8xx-i2c.o > diff --git a/drivers/mfd/nxp-siul2.c b/drivers/mfd/nxp-siul2.c > new file mode 100644 > index 000000000000..ba13d1beb244 > --- /dev/null > +++ b/drivers/mfd/nxp-siul2.c > @@ -0,0 +1,411 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * SIUL2(System Integration Unit Lite) MFD driver > + * > + * Copyright 2024 NXP > + */ > +#include <linux/module.h> > +#include <linux/init.h> > +#include <linux/of.h> > +#include <linux/mfd/core.h> > +#include <linux/mfd/nxp-siul2.h> order alphabet > + > +#define S32G_NUM_SIUL2 2 > + > +#define S32_REG_RANGE(start, end, name, access) \ > + { \ > + .reg_name = (name), \ > + .reg_start_offset = (start), \ > + .reg_end_offset = (end), \ > + .reg_access = (access), \ > + .valid = true, \ > + } > + > +#define S32_INVALID_REG_RANGE \ > + { \ > + .reg_name = NULL, \ > + .reg_access = NULL, \ > + .valid = false, \ > + } > + > +static const struct mfd_cell nxp_siul2_devs[] = { > + { > + .name = "s32g-siul2-pinctrl", > + } Only one device? If add later, commit message should said add more mfd later. Frank > +}; > + > +/** > + * struct nxp_siul2_reg_range_info: a register range in SIUL2 > + * @reg_start_offset: the first valid register offset > + * @reg_end_offset: the last valid register offset > + * @reg_access: the read/write access tables if not NULL > + * @valid: whether the register range is valid or not > + */ > +struct nxp_siul2_reg_range_info { > + const char *reg_name; > + unsigned int reg_start_offset; > + unsigned int reg_end_offset; > + const struct regmap_access_table *reg_access; > + bool valid; > +}; > + > +static const struct regmap_range s32g2_siul2_0_imcr_reg_ranges[] = { > + /* IMCR0 - IMCR1 */ > + regmap_reg_range(0, 4), > + /* IMCR3 - IMCR61 */ > + regmap_reg_range(0xC, 0xF4), > + /* IMCR68 - IMCR83 */ > + regmap_reg_range(0x110, 0x14C) > +}; > + > +static const struct regmap_access_table s32g2_siul2_0_imcr = { > + .yes_ranges = s32g2_siul2_0_imcr_reg_ranges, > + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_0_imcr_reg_ranges) > +}; > + > +static const struct regmap_range s32g2_siul2_0_pgpd_reg_ranges[] = { > + /* PGPD*0 - PGPD*5 */ > + regmap_reg_range(0, 0xA), > + /* PGPD*6 - PGPD*6 */ > + regmap_reg_range(0xE, 0xE), > +}; > + > +static const struct regmap_access_table s32g2_siul2_0_pgpd = { > + .yes_ranges = s32g2_siul2_0_pgpd_reg_ranges, > + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_0_pgpd_reg_ranges) > +}; > + > +static const struct regmap_range s32g2_siul2_1_irq_reg_ranges[] = { > + /* DISR0 */ > + regmap_reg_range(0x10, 0x10), > + /* DIRER0 */ > + regmap_reg_range(0x18, 0x18), > + /* DIRSR0 */ > + regmap_reg_range(0x20, 0x20), > + /* IREER0 */ > + regmap_reg_range(0x28, 0x28), > + /* IFEER0 */ > + regmap_reg_range(0x30, 0x30), > +}; > + > +static const struct regmap_access_table s32g2_siul2_1_irq = { > + .yes_ranges = s32g2_siul2_1_irq_reg_ranges, > + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_1_irq_reg_ranges), > +}; > + > +static const struct regmap_range s32g2_siul2_1_irq_volatile_reg_range[] = { > + /* DISR0 */ > + regmap_reg_range(0x10, 0x10) > +}; > + > +static const struct regmap_access_table s32g2_siul2_1_irq_volatile = { > + .yes_ranges = s32g2_siul2_1_irq_volatile_reg_range, > + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_1_irq_volatile_reg_range), > +}; > + > +static const struct regmap_range s32g2_siul2_1_mscr_reg_ranges[] = { > + /* MSCR112 - MSCR122 */ > + regmap_reg_range(0, 0x28), > + /* MSCR144 - MSCR190 */ > + regmap_reg_range(0x80, 0x138) > +}; > + > +static const struct regmap_access_table s32g2_siul2_1_mscr = { > + .yes_ranges = s32g2_siul2_1_mscr_reg_ranges, > + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_1_mscr_reg_ranges), > +}; > + > +static const struct regmap_range s32g2_siul2_1_imcr_reg_ranges[] = { > + /* IMCR119 - IMCR121 */ > + regmap_reg_range(0, 8), > + /* IMCR128 - IMCR129 */ > + regmap_reg_range(0x24, 0x28), > + /* IMCR143 - IMCR151 */ > + regmap_reg_range(0x60, 0x80), > + /* IMCR153 - IMCR161 */ > + regmap_reg_range(0x88, 0xA8), > + /* IMCR205 - IMCR212 */ > + regmap_reg_range(0x158, 0x174), > + /* IMCR224 - IMCR225 */ > + regmap_reg_range(0x1A4, 0x1A8), > + /* IMCR233 - IMCR248 */ > + regmap_reg_range(0x1C8, 0x204), > + /* IMCR273 - IMCR274 */ > + regmap_reg_range(0x268, 0x26C), > + /* IMCR278 - IMCR281 */ > + regmap_reg_range(0x27C, 0x288), > + /* IMCR283 - IMCR286 */ > + regmap_reg_range(0x290, 0x29C), > + /* IMCR288 - IMCR294 */ > + regmap_reg_range(0x2A4, 0x2BC), > + /* IMCR296 - IMCR302 */ > + regmap_reg_range(0x2C4, 0x2DC), > + /* IMCR304 - IMCR310 */ > + regmap_reg_range(0x2E4, 0x2FC), > + /* IMCR312 - IMCR314 */ > + regmap_reg_range(0x304, 0x30C), > + /* IMCR316 */ > + regmap_reg_range(0x314, 0x314), > + /* IMCR 318 */ > + regmap_reg_range(0x31C, 0x31C), > + /* IMCR322 - IMCR340 */ > + regmap_reg_range(0x32C, 0x374), > + /* IMCR343 - IMCR360 */ > + regmap_reg_range(0x380, 0x3C4), > + /* IMCR363 - IMCR380 */ > + regmap_reg_range(0x3D0, 0x414), > + /* IMCR383 - IMCR393 */ > + regmap_reg_range(0x420, 0x448), > + /* IMCR398 - IMCR433 */ > + regmap_reg_range(0x45C, 0x4E8), > + /* IMCR467 - IMCR470 */ > + regmap_reg_range(0x570, 0x57C), > + /* IMCR473 - IMCR475 */ > + regmap_reg_range(0x588, 0x590), > + /* IMCR478 - IMCR480*/ > + regmap_reg_range(0x59C, 0x5A4), > + /* IMCR483 - IMCR485 */ > + regmap_reg_range(0x5B0, 0x5B8), > + /* IMCR488 - IMCR490 */ > + regmap_reg_range(0x5C4, 0x5CC), > + /* IMCR493 - IMCR495 */ > + regmap_reg_range(0x5D8, 0x5E0), > +}; > + > +static const struct regmap_access_table s32g2_siul2_1_imcr = { > + .yes_ranges = s32g2_siul2_1_imcr_reg_ranges, > + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_1_imcr_reg_ranges) > +}; > + > +static const struct regmap_range s32g2_siul2_1_pgpd_reg_ranges[] = { > + /* PGPD*7 */ > + regmap_reg_range(0xC, 0xC), > + /* PGPD*9 */ > + regmap_reg_range(0x10, 0x10), > + /* PDPG*10 - PGPD*11 */ > + regmap_reg_range(0x14, 0x16), > +}; > + > +static const struct regmap_access_table s32g2_siul2_1_pgpd = { > + .yes_ranges = s32g2_siul2_1_pgpd_reg_ranges, > + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_1_pgpd_reg_ranges) > +}; > + > +static const struct nxp_siul2_reg_range_info > +s32g2_reg_ranges[S32G_NUM_SIUL2][SIUL2_NUM_REG_TYPES] = { > + /* SIUL2_0 */ > + { > + [SIUL2_MPIDR] = S32_REG_RANGE(4, 8, "SIUL2_0_MPIDR", NULL), > + /* Interrupts are to be controlled from SIUL2_1 */ > + [SIUL2_IRQ] = S32_INVALID_REG_RANGE, > + [SIUL2_MSCR] = S32_REG_RANGE(0x240, 0x3D4, "SIUL2_0_MSCR", > + NULL), > + [SIUL2_IMCR] = S32_REG_RANGE(0xA40, 0xB8C, "SIUL2_0_IMCR", > + &s32g2_siul2_0_imcr), > + [SIUL2_PGPDO] = S32_REG_RANGE(0x1700, 0x170E, > + "SIUL2_0_PGPDO", > + &s32g2_siul2_0_pgpd), > + [SIUL2_PGPDI] = S32_REG_RANGE(0x1740, 0x174E, > + "SIUL2_0_PGPDI", > + &s32g2_siul2_0_pgpd), > + }, > + /* SIUL2_1 */ > + { > + [SIUL2_MPIDR] = S32_REG_RANGE(4, 8, "SIUL2_1_MPIDR", NULL), > + [SIUL2_IRQ] = S32_REG_RANGE(0x10, 0xC0, "SIUL2_1_IRQ", > + &s32g2_siul2_1_irq), > + [SIUL2_MSCR] = S32_REG_RANGE(0x400, 0x538, "SIUL2_1_MSCR", > + &s32g2_siul2_1_mscr), > + [SIUL2_IMCR] = S32_REG_RANGE(0xC1C, 0x11FC, "SIUL2_1_IMCR", > + &s32g2_siul2_1_imcr), > + [SIUL2_PGPDO] = S32_REG_RANGE(0x1700, 0x1716, > + "SIUL2_1_PGPDO", > + &s32g2_siul2_1_pgpd), > + [SIUL2_PGPDI] = S32_REG_RANGE(0x1740, 0x1756, > + "SIUL2_1_PGPDI", > + &s32g2_siul2_1_pgpd), > + }, > +}; > + > +static const struct regmap_config nxp_siul2_regmap_irq_conf = { > + .val_bits = 32, > + .val_format_endian = REGMAP_ENDIAN_LITTLE, > + .reg_bits = 32, > + .reg_stride = 4, > + .cache_type = REGCACHE_FLAT, > + .use_raw_spinlock = true, > + .volatile_table = &s32g2_siul2_1_irq_volatile, > +}; > + > +static const struct regmap_config nxp_siul2_regmap_generic_conf = { > + .val_bits = 32, > + .val_format_endian = REGMAP_ENDIAN_LITTLE, > + .reg_bits = 32, > + .reg_stride = 4, > + .cache_type = REGCACHE_FLAT, > + .use_raw_spinlock = true, > +}; > + > +static const struct regmap_config nxp_siul2_regmap_pgpdo_conf = { > + .val_bits = 16, > + .val_format_endian = REGMAP_ENDIAN_LITTLE, > + .reg_bits = 32, > + .reg_stride = 2, > + .cache_type = REGCACHE_FLAT, > + .use_raw_spinlock = true, > +}; > + > +static const struct regmap_config nxp_siul2_regmap_pgpdi_conf = { > + .val_bits = 16, > + .val_format_endian = REGMAP_ENDIAN_LITTLE, > + .reg_bits = 32, > + .reg_stride = 2, > + .cache_type = REGCACHE_NONE, > + .use_raw_spinlock = true, > +}; > + > +static int nxp_siul2_init_regmap(struct platform_device *pdev, > + void __iomem *base, int siul) > +{ > + struct regmap_config regmap_configs[SIUL2_NUM_REG_TYPES] = { > + [SIUL2_MPIDR] = nxp_siul2_regmap_generic_conf, > + [SIUL2_IRQ] = nxp_siul2_regmap_irq_conf, > + [SIUL2_MSCR] = nxp_siul2_regmap_generic_conf, > + [SIUL2_IMCR] = nxp_siul2_regmap_generic_conf, > + [SIUL2_PGPDO] = nxp_siul2_regmap_pgpdo_conf, > + [SIUL2_PGPDI] = nxp_siul2_regmap_pgpdi_conf, > + }; > + const struct nxp_siul2_reg_range_info *tmp_range; > + struct regmap_config *tmp_conf; > + struct nxp_siul2_info *info; > + struct nxp_siul2_mfd *priv; > + void __iomem *reg_start; > + int i, ret; > + > + priv = platform_get_drvdata(pdev); > + info = &priv->siul2[siul]; > + > + for (i = 0; i < SIUL2_NUM_REG_TYPES; i++) { > + if (!s32g2_reg_ranges[siul][i].valid) > + continue; > + > + tmp_range = &s32g2_reg_ranges[siul][i]; > + tmp_conf = ®map_configs[i]; > + tmp_conf->name = tmp_range->reg_name; > + tmp_conf->max_register = > + tmp_range->reg_end_offset - tmp_range->reg_start_offset; > + > + if (tmp_conf->cache_type != REGCACHE_NONE) > + tmp_conf->num_reg_defaults_raw = > + tmp_conf->max_register / tmp_conf->reg_stride; > + > + if (tmp_range->reg_access) { > + tmp_conf->wr_table = tmp_range->reg_access; > + tmp_conf->rd_table = tmp_range->reg_access; > + } > + > + reg_start = base + tmp_range->reg_start_offset; > + info->regmaps[i] = devm_regmap_init_mmio(&pdev->dev, reg_start, > + tmp_conf); > + if (IS_ERR(info->regmaps[i])) { > + dev_err(&pdev->dev, "regmap %d init failed: %d\n", i, > + ret); > + return PTR_ERR(info->regmaps[i]); > + } > + } > + > + return 0; > +} > + > +static int nxp_siul2_parse_dtb(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + struct of_phandle_args pinspec; > + struct nxp_siul2_mfd *priv; > + void __iomem *base; > + char reg_name[16]; > + int i, ret; > + > + priv = platform_get_drvdata(pdev); > + > + for (i = 0; i < priv->num_siul2; i++) { > + ret = snprintf(reg_name, ARRAY_SIZE(reg_name), "siul2%d", i); > + if (ret < 0 || ret >= ARRAY_SIZE(reg_name)) > + return ret; > + > + base = devm_platform_ioremap_resource_byname(pdev, reg_name); > + if (IS_ERR(base)) { > + dev_err(&pdev->dev, "Failed to get MEM resource: %s\n", > + reg_name); > + return PTR_ERR(base); return dev_err_probe() > + } > + > + ret = nxp_siul2_init_regmap(pdev, base, i); > + if (ret) > + return ret; > + > + ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, > + i, &pinspec); > + if (ret) > + return ret; > + > + of_node_put(pinspec.np); > + > + if (pinspec.args_count != 3) { > + dev_err(&pdev->dev, "Invalid pinspec count: %d\n", > + pinspec.args_count); > + return -EINVAL; return dev_err_probe(); > + } > + > + priv->siul2[i].gpio_base = pinspec.args[1]; > + priv->siul2[i].gpio_num = pinspec.args[2]; > + } > + > + return 0; > +} > + > +static int nxp_siul2_probe(struct platform_device *pdev) > +{ > + struct nxp_siul2_mfd *priv; > + int ret; > + > + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->num_siul2 = S32G_NUM_SIUL2; > + priv->siul2 = devm_kcalloc(&pdev->dev, priv->num_siul2, > + sizeof(*priv->siul2), GFP_KERNEL); > + if (!priv->siul2) > + return -ENOMEM; > + > + platform_set_drvdata(pdev, priv); > + ret = nxp_siul2_parse_dtb(pdev); > + if (ret) > + return ret; > + > + return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, > + nxp_siul2_devs, ARRAY_SIZE(nxp_siul2_devs), > + NULL, 0, NULL); > +} > + > +static const struct of_device_id nxp_siul2_dt_ids[] = { > + { .compatible = "nxp,s32g2-siul2" }, > + { .compatible = "nxp,s32g3-siul2" }, > + { }, > +}; > +MODULE_DEVICE_TABLE(of, nxp_siul2_dt_ids); > + > +static struct platform_driver nxp_siul2_mfd_driver = { > + .driver = { > + .name = "nxp-siul2-mfd", > + .of_match_table = nxp_siul2_dt_ids, > + }, > + .probe = nxp_siul2_probe, > +}; > + > +module_platform_driver(nxp_siul2_mfd_driver); > + > +MODULE_LICENSE("GPL"); > +MODULE_DESCRIPTION("NXP SIUL2 MFD driver"); > +MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@oss.nxp.com>"); > diff --git a/include/linux/mfd/nxp-siul2.h b/include/linux/mfd/nxp-siul2.h > new file mode 100644 > index 000000000000..238c812dba29 > --- /dev/null > +++ b/include/linux/mfd/nxp-siul2.h > @@ -0,0 +1,55 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later > + * > + * S32 SIUL2 core definitions > + * > + * Copyright 2024 NXP > + */ > + > +#ifndef __DRIVERS_MFD_NXP_SIUL2_H > +#define __DRIVERS_MFD_NXP_SIUL2_H > + > +#include <linux/regmap.h> > + > +/** > + * enum nxp_siul2_reg_type - an enum for SIUL2 reg types > + * @SIUL2_MPIDR - SoC info > + * @SIUL2_IRQ - IRQ related registers, only valid in SIUL2_1 > + * @SIUL2_MSCR - used for pinmuxing and pinconf > + * @SIUL2_IMCR - used for pinmuxing > + * @SIUL2_PGPDO - writing the GPIO value > + * @SIUL2_PGPDI - reading the GPIO value > + */ > +enum nxp_siul2_reg_type { > + SIUL2_MPIDR, > + SIUL2_IRQ, > + SIUL2_MSCR, > + SIUL2_IMCR, > + SIUL2_PGPDO, > + SIUL2_PGPDI, > + > + SIUL2_NUM_REG_TYPES > +}; > + > +/** > + * struct nxp_siul2_info - details about one SIUL2 hardware instance > + * @regmaps: the regmaps for each register type for a SIUL2 hardware instance > + * @gpio_base: the first GPIO in this SIUL2 module > + * @gpio_num: the number of GPIOs in this SIUL2 module > + */ > +struct nxp_siul2_info { > + struct regmap *regmaps[SIUL2_NUM_REG_TYPES]; > + u32 gpio_base; > + u32 gpio_num; > +}; > + > +/** > + * struct nxp_siul2_mfd - driver data > + * @siul2: info about the SIUL2 modules present > + * @num_siul2: number of siul2 modules > + */ > +struct nxp_siul2_mfd { > + struct nxp_siul2_info *siul2; > + u8 num_siul2; > +}; > + > +#endif /* __DRIVERS_MFD_NXP_SIUL2_H */ > -- > 2.45.2 >
Hi Andrei, kernel test robot noticed the following build warnings: [auto build test WARNING on linusw-pinctrl/devel] [also build test WARNING on linusw-pinctrl/for-next lee-mfd/for-mfd-next shawnguo/for-next linus/master lee-mfd/for-mfd-fixes v6.12-rc5 next-20241101] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Andrei-Stefanescu/dt-bindings-mfd-add-support-for-the-NXP-SIUL2-module/20241101-160940 base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git devel patch link: https://lore.kernel.org/r/20241101080614.1070819-3-andrei.stefanescu%40oss.nxp.com patch subject: [PATCH v5 2/7] mfd: nxp-siul2: add support for NXP SIUL2 config: hexagon-allmodconfig (https://download.01.org/0day-ci/archive/20241102/202411021558.f39S3DSV-lkp@intel.com/config) compiler: clang version 20.0.0git (https://github.com/llvm/llvm-project 639a7ac648f1e50ccd2556e17d401c04f9cce625) reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241102/202411021558.f39S3DSV-lkp@intel.com/reproduce) If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Closes: https://lore.kernel.org/oe-kbuild-all/202411021558.f39S3DSV-lkp@intel.com/ All warnings (new ones prefixed by >>): In file included from drivers/mfd/nxp-siul2.c:11: In file included from include/linux/mfd/nxp-siul2.h:11: In file included from include/linux/regmap.h:20: In file included from include/linux/iopoll.h:14: In file included from include/linux/io.h:14: In file included from arch/hexagon/include/asm/io.h:328: include/asm-generic/io.h:548:31: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 548 | val = __raw_readb(PCI_IOBASE + addr); | ~~~~~~~~~~ ^ include/asm-generic/io.h:561:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 561 | val = __le16_to_cpu((__le16 __force)__raw_readw(PCI_IOBASE + addr)); | ~~~~~~~~~~ ^ include/uapi/linux/byteorder/little_endian.h:37:51: note: expanded from macro '__le16_to_cpu' 37 | #define __le16_to_cpu(x) ((__force __u16)(__le16)(x)) | ^ In file included from drivers/mfd/nxp-siul2.c:11: In file included from include/linux/mfd/nxp-siul2.h:11: In file included from include/linux/regmap.h:20: In file included from include/linux/iopoll.h:14: In file included from include/linux/io.h:14: In file included from arch/hexagon/include/asm/io.h:328: include/asm-generic/io.h:574:61: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 574 | val = __le32_to_cpu((__le32 __force)__raw_readl(PCI_IOBASE + addr)); | ~~~~~~~~~~ ^ include/uapi/linux/byteorder/little_endian.h:35:51: note: expanded from macro '__le32_to_cpu' 35 | #define __le32_to_cpu(x) ((__force __u32)(__le32)(x)) | ^ In file included from drivers/mfd/nxp-siul2.c:11: In file included from include/linux/mfd/nxp-siul2.h:11: In file included from include/linux/regmap.h:20: In file included from include/linux/iopoll.h:14: In file included from include/linux/io.h:14: In file included from arch/hexagon/include/asm/io.h:328: include/asm-generic/io.h:585:33: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 585 | __raw_writeb(value, PCI_IOBASE + addr); | ~~~~~~~~~~ ^ include/asm-generic/io.h:595:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 595 | __raw_writew((u16 __force)cpu_to_le16(value), PCI_IOBASE + addr); | ~~~~~~~~~~ ^ include/asm-generic/io.h:605:59: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic] 605 | __raw_writel((u32 __force)cpu_to_le32(value), PCI_IOBASE + addr); | ~~~~~~~~~~ ^ >> drivers/mfd/nxp-siul2.c:312:5: warning: variable 'ret' is uninitialized when used here [-Wuninitialized] 312 | ret); | ^~~ include/linux/dev_printk.h:154:65: note: expanded from macro 'dev_err' 154 | dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__) | ^~~~~~~~~~~ include/linux/dev_printk.h:110:23: note: expanded from macro 'dev_printk_index_wrap' 110 | _p_func(dev, fmt, ##__VA_ARGS__); \ | ^~~~~~~~~~~ drivers/mfd/nxp-siul2.c:283:12: note: initialize the variable 'ret' to silence this warning 283 | int i, ret; | ^ | = 0 >> drivers/mfd/nxp-siul2.c:367:12: warning: stack frame size (1296) exceeds limit (1024) in 'nxp_siul2_probe' [-Wframe-larger-than] 367 | static int nxp_siul2_probe(struct platform_device *pdev) | ^ 8 warnings generated. Kconfig warnings: (for reference only) WARNING: unmet direct dependencies detected for MODVERSIONS Depends on [n]: MODULES [=y] && !COMPILE_TEST [=y] Selected by [y]: - RANDSTRUCT_FULL [=y] && (CC_HAS_RANDSTRUCT [=y] || GCC_PLUGINS [=n]) && MODULES [=y] WARNING: unmet direct dependencies detected for GET_FREE_REGION Depends on [n]: SPARSEMEM [=n] Selected by [m]: - RESOURCE_KUNIT_TEST [=m] && RUNTIME_TESTING_MENU [=y] && KUNIT [=m] vim +/ret +312 drivers/mfd/nxp-siul2.c 266 267 static int nxp_siul2_init_regmap(struct platform_device *pdev, 268 void __iomem *base, int siul) 269 { 270 struct regmap_config regmap_configs[SIUL2_NUM_REG_TYPES] = { 271 [SIUL2_MPIDR] = nxp_siul2_regmap_generic_conf, 272 [SIUL2_IRQ] = nxp_siul2_regmap_irq_conf, 273 [SIUL2_MSCR] = nxp_siul2_regmap_generic_conf, 274 [SIUL2_IMCR] = nxp_siul2_regmap_generic_conf, 275 [SIUL2_PGPDO] = nxp_siul2_regmap_pgpdo_conf, 276 [SIUL2_PGPDI] = nxp_siul2_regmap_pgpdi_conf, 277 }; 278 const struct nxp_siul2_reg_range_info *tmp_range; 279 struct regmap_config *tmp_conf; 280 struct nxp_siul2_info *info; 281 struct nxp_siul2_mfd *priv; 282 void __iomem *reg_start; 283 int i, ret; 284 285 priv = platform_get_drvdata(pdev); 286 info = &priv->siul2[siul]; 287 288 for (i = 0; i < SIUL2_NUM_REG_TYPES; i++) { 289 if (!s32g2_reg_ranges[siul][i].valid) 290 continue; 291 292 tmp_range = &s32g2_reg_ranges[siul][i]; 293 tmp_conf = ®map_configs[i]; 294 tmp_conf->name = tmp_range->reg_name; 295 tmp_conf->max_register = 296 tmp_range->reg_end_offset - tmp_range->reg_start_offset; 297 298 if (tmp_conf->cache_type != REGCACHE_NONE) 299 tmp_conf->num_reg_defaults_raw = 300 tmp_conf->max_register / tmp_conf->reg_stride; 301 302 if (tmp_range->reg_access) { 303 tmp_conf->wr_table = tmp_range->reg_access; 304 tmp_conf->rd_table = tmp_range->reg_access; 305 } 306 307 reg_start = base + tmp_range->reg_start_offset; 308 info->regmaps[i] = devm_regmap_init_mmio(&pdev->dev, reg_start, 309 tmp_conf); 310 if (IS_ERR(info->regmaps[i])) { 311 dev_err(&pdev->dev, "regmap %d init failed: %d\n", i, > 312 ret); 313 return PTR_ERR(info->regmaps[i]); 314 } 315 } 316 317 return 0; 318 } 319 320 static int nxp_siul2_parse_dtb(struct platform_device *pdev) 321 { 322 struct device_node *np = pdev->dev.of_node; 323 struct of_phandle_args pinspec; 324 struct nxp_siul2_mfd *priv; 325 void __iomem *base; 326 char reg_name[16]; 327 int i, ret; 328 329 priv = platform_get_drvdata(pdev); 330 331 for (i = 0; i < priv->num_siul2; i++) { 332 ret = snprintf(reg_name, ARRAY_SIZE(reg_name), "siul2%d", i); 333 if (ret < 0 || ret >= ARRAY_SIZE(reg_name)) 334 return ret; 335 336 base = devm_platform_ioremap_resource_byname(pdev, reg_name); 337 if (IS_ERR(base)) { 338 dev_err(&pdev->dev, "Failed to get MEM resource: %s\n", 339 reg_name); 340 return PTR_ERR(base); 341 } 342 343 ret = nxp_siul2_init_regmap(pdev, base, i); 344 if (ret) 345 return ret; 346 347 ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 348 i, &pinspec); 349 if (ret) 350 return ret; 351 352 of_node_put(pinspec.np); 353 354 if (pinspec.args_count != 3) { 355 dev_err(&pdev->dev, "Invalid pinspec count: %d\n", 356 pinspec.args_count); 357 return -EINVAL; 358 } 359 360 priv->siul2[i].gpio_base = pinspec.args[1]; 361 priv->siul2[i].gpio_num = pinspec.args[2]; 362 } 363 364 return 0; 365 } 366 > 367 static int nxp_siul2_probe(struct platform_device *pdev) 368 { 369 struct nxp_siul2_mfd *priv; 370 int ret; 371 372 priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); 373 if (!priv) 374 return -ENOMEM; 375 376 priv->num_siul2 = S32G_NUM_SIUL2; 377 priv->siul2 = devm_kcalloc(&pdev->dev, priv->num_siul2, 378 sizeof(*priv->siul2), GFP_KERNEL); 379 if (!priv->siul2) 380 return -ENOMEM; 381 382 platform_set_drvdata(pdev, priv); 383 ret = nxp_siul2_parse_dtb(pdev); 384 if (ret) 385 return ret; 386 387 return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, 388 nxp_siul2_devs, ARRAY_SIZE(nxp_siul2_devs), 389 NULL, 0, NULL); 390 } 391
Hi Andrei, kernel test robot noticed the following build warnings: https://git-scm.com/docs/git-format-patch#_base_tree_information] url: https://github.com/intel-lab-lkp/linux/commits/Andrei-Stefanescu/dt-bindings-mfd-add-support-for-the-NXP-SIUL2-module/20241101-160940 base: https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git devel patch link: https://lore.kernel.org/r/20241101080614.1070819-3-andrei.stefanescu%40oss.nxp.com patch subject: [PATCH v5 2/7] mfd: nxp-siul2: add support for NXP SIUL2 config: csky-randconfig-r072-20241102 (https://download.01.org/0day-ci/archive/20241102/202411021431.282g2yZy-lkp@intel.com/config) compiler: csky-linux-gcc (GCC) 14.1.0 If you fix the issue in a separate patch/commit (i.e. not just a new version of the same patch/commit), kindly add following tags | Reported-by: kernel test robot <lkp@intel.com> | Reported-by: Dan Carpenter <dan.carpenter@linaro.org> | Closes: https://lore.kernel.org/r/202411021431.282g2yZy-lkp@intel.com/ smatch warnings: drivers/mfd/nxp-siul2.c:311 nxp_siul2_init_regmap() error: uninitialized symbol 'ret'. vim +/ret +311 drivers/mfd/nxp-siul2.c 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 267 static int nxp_siul2_init_regmap(struct platform_device *pdev, 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 268 void __iomem *base, int siul) 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 269 { 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 270 struct regmap_config regmap_configs[SIUL2_NUM_REG_TYPES] = { 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 271 [SIUL2_MPIDR] = nxp_siul2_regmap_generic_conf, 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 272 [SIUL2_IRQ] = nxp_siul2_regmap_irq_conf, 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 273 [SIUL2_MSCR] = nxp_siul2_regmap_generic_conf, 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 274 [SIUL2_IMCR] = nxp_siul2_regmap_generic_conf, 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 275 [SIUL2_PGPDO] = nxp_siul2_regmap_pgpdo_conf, 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 276 [SIUL2_PGPDI] = nxp_siul2_regmap_pgpdi_conf, 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 277 }; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 278 const struct nxp_siul2_reg_range_info *tmp_range; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 279 struct regmap_config *tmp_conf; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 280 struct nxp_siul2_info *info; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 281 struct nxp_siul2_mfd *priv; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 282 void __iomem *reg_start; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 283 int i, ret; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 284 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 285 priv = platform_get_drvdata(pdev); 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 286 info = &priv->siul2[siul]; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 287 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 288 for (i = 0; i < SIUL2_NUM_REG_TYPES; i++) { 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 289 if (!s32g2_reg_ranges[siul][i].valid) 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 290 continue; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 291 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 292 tmp_range = &s32g2_reg_ranges[siul][i]; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 293 tmp_conf = ®map_configs[i]; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 294 tmp_conf->name = tmp_range->reg_name; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 295 tmp_conf->max_register = 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 296 tmp_range->reg_end_offset - tmp_range->reg_start_offset; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 297 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 298 if (tmp_conf->cache_type != REGCACHE_NONE) 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 299 tmp_conf->num_reg_defaults_raw = 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 300 tmp_conf->max_register / tmp_conf->reg_stride; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 301 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 302 if (tmp_range->reg_access) { 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 303 tmp_conf->wr_table = tmp_range->reg_access; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 304 tmp_conf->rd_table = tmp_range->reg_access; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 305 } 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 306 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 307 reg_start = base + tmp_range->reg_start_offset; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 308 info->regmaps[i] = devm_regmap_init_mmio(&pdev->dev, reg_start, 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 309 tmp_conf); 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 310 if (IS_ERR(info->regmaps[i])) { 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 @311 dev_err(&pdev->dev, "regmap %d init failed: %d\n", i, 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 312 ret); Do this like: dev_err(&pdev->dev, "regmap %d init failed: %pe\n", i, info->regmaps[i]); 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 313 return PTR_ERR(info->regmaps[i]); 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 314 } 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 315 } 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 316 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 317 return 0; 5c0b3edcf6df17 Andrei Stefanescu 2024-11-01 318 }
On Fri, Nov 01, 2024 at 10:06:08AM +0200, Andrei Stefanescu wrote: > +static int nxp_siul2_probe(struct platform_device *pdev) > +{ > + struct nxp_siul2_mfd *priv; > + int ret; > + > + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->num_siul2 = S32G_NUM_SIUL2; > + priv->siul2 = devm_kcalloc(&pdev->dev, priv->num_siul2, > + sizeof(*priv->siul2), GFP_KERNEL); > + if (!priv->siul2) > + return -ENOMEM; > + > + platform_set_drvdata(pdev, priv); > + ret = nxp_siul2_parse_dtb(pdev); > + if (ret) > + return ret; > + > + return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, > + nxp_siul2_devs, ARRAY_SIZE(nxp_siul2_devs), > + NULL, 0, NULL); > +} > + > +static const struct of_device_id nxp_siul2_dt_ids[] = { > + { .compatible = "nxp,s32g2-siul2" }, > + { .compatible = "nxp,s32g3-siul2" }, So devices are comaptible? Why doesn't your binding express it? > + { }, > +}; > +MODULE_DEVICE_TABLE(of, nxp_siul2_dt_ids); > + > +static struct platform_driver nxp_siul2_mfd_driver = { > + .driver = { > + .name = "nxp-siul2-mfd", > + .of_match_table = nxp_siul2_dt_ids, > + }, > + .probe = nxp_siul2_probe, > +}; Best regards, Krzysztof
Hi Dan, On 02/11/2024 10:37, Dan Carpenter wrote: > Hi Andrei, > > kernel test robot noticed the following build warnings: Thank you very much! I will fix this and all of the other kernel test robot errors in v6. Best regards, Andrei
Hi Krzysztof, On 02/11/2024 10:52, Krzysztof Kozlowski wrote: > On Fri, Nov 01, 2024 at 10:06:08AM +0200, Andrei Stefanescu wrote: >> +static int nxp_siul2_probe(struct platform_device *pdev) >> +{ >> + struct nxp_siul2_mfd *priv; >> + int ret; >> + >> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); >> + if (!priv) >> + return -ENOMEM; >> + >> + priv->num_siul2 = S32G_NUM_SIUL2; >> + priv->siul2 = devm_kcalloc(&pdev->dev, priv->num_siul2, >> + sizeof(*priv->siul2), GFP_KERNEL); >> + if (!priv->siul2) >> + return -ENOMEM; >> + >> + platform_set_drvdata(pdev, priv); >> + ret = nxp_siul2_parse_dtb(pdev); >> + if (ret) >> + return ret; >> + >> + return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, >> + nxp_siul2_devs, ARRAY_SIZE(nxp_siul2_devs), >> + NULL, 0, NULL); >> +} >> + >> +static const struct of_device_id nxp_siul2_dt_ids[] = { >> + { .compatible = "nxp,s32g2-siul2" }, >> + { .compatible = "nxp,s32g3-siul2" }, > > So devices are comaptible? Why doesn't your binding express it? Yes, as far as I know, there is no difference in the integration of the SIUL2 module for S32G2 and S32G3 SoCs. I am not sure how to express this compatibility. Should I mention the "nxp,s32g3-siul2" compatible as a fallback one? Best regards, Andrei > >> + { }, >> +}; >> +MODULE_DEVICE_TABLE(of, nxp_siul2_dt_ids); >> + >> +static struct platform_driver nxp_siul2_mfd_driver = { >> + .driver = { >> + .name = "nxp-siul2-mfd", >> + .of_match_table = nxp_siul2_dt_ids, >> + }, >> + .probe = nxp_siul2_probe, >> +}; > > Best regards, > Krzysztof >
On 04/11/2024 12:29, Andrei Stefanescu wrote: > Hi Krzysztof, > > On 02/11/2024 10:52, Krzysztof Kozlowski wrote: >> On Fri, Nov 01, 2024 at 10:06:08AM +0200, Andrei Stefanescu wrote: >>> +static int nxp_siul2_probe(struct platform_device *pdev) >>> +{ >>> + struct nxp_siul2_mfd *priv; >>> + int ret; >>> + >>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); >>> + if (!priv) >>> + return -ENOMEM; >>> + >>> + priv->num_siul2 = S32G_NUM_SIUL2; >>> + priv->siul2 = devm_kcalloc(&pdev->dev, priv->num_siul2, >>> + sizeof(*priv->siul2), GFP_KERNEL); >>> + if (!priv->siul2) >>> + return -ENOMEM; >>> + >>> + platform_set_drvdata(pdev, priv); >>> + ret = nxp_siul2_parse_dtb(pdev); >>> + if (ret) >>> + return ret; >>> + >>> + return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, >>> + nxp_siul2_devs, ARRAY_SIZE(nxp_siul2_devs), >>> + NULL, 0, NULL); >>> +} >>> + >>> +static const struct of_device_id nxp_siul2_dt_ids[] = { >>> + { .compatible = "nxp,s32g2-siul2" }, >>> + { .compatible = "nxp,s32g3-siul2" }, >> >> So devices are comaptible? Why doesn't your binding express it? > > Yes, as far as I know, there is no difference in the integration > of the SIUL2 module for S32G2 and S32G3 SoCs. I am not sure how > to express this compatibility. Should I mention the "nxp,s32g3-siul2" > compatible as a fallback one? See example schema. Or any other recent NXP IMX binding. Best regards, Krzysztof
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f9325bcce1b9..fc590789e8b3 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1098,6 +1098,18 @@ config MFD_NTXEC certain e-book readers designed by the original design manufacturer Netronix. +config MFD_NXP_SIUL2 + tristate "NXP SIUL2 MFD driver" + select MFD_CORE + select REGMAP_MMIO + depends on ARCH_S32 || COMPILE_TEST + help + Select this to get support for the NXP SIUL2 (System Integration + Unit Lite) module. This hardware block contains registers for + SoC information, pinctrl and GPIO functionality. This will + probe a MFD driver which will contain cells for a combined + pinctrl&GPIO driver and nvmem drivers for the SoC information. + config MFD_RETU tristate "Nokia Retu and Tahvo multi-function device" select MFD_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 2a9f91e81af8..7b19ea014221 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -226,6 +226,7 @@ obj-$(CONFIG_MFD_INTEL_PMC_BXT) += intel_pmc_bxt.o obj-$(CONFIG_MFD_PALMAS) += palmas.o obj-$(CONFIG_MFD_VIPERBOARD) += viperboard.o obj-$(CONFIG_MFD_NTXEC) += ntxec.o +obj-$(CONFIG_MFD_NXP_SIUL2) += nxp-siul2.o obj-$(CONFIG_MFD_RC5T583) += rc5t583.o rc5t583-irq.o obj-$(CONFIG_MFD_RK8XX) += rk8xx-core.o obj-$(CONFIG_MFD_RK8XX_I2C) += rk8xx-i2c.o diff --git a/drivers/mfd/nxp-siul2.c b/drivers/mfd/nxp-siul2.c new file mode 100644 index 000000000000..ba13d1beb244 --- /dev/null +++ b/drivers/mfd/nxp-siul2.c @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * SIUL2(System Integration Unit Lite) MFD driver + * + * Copyright 2024 NXP + */ +#include <linux/module.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/mfd/core.h> +#include <linux/mfd/nxp-siul2.h> + +#define S32G_NUM_SIUL2 2 + +#define S32_REG_RANGE(start, end, name, access) \ + { \ + .reg_name = (name), \ + .reg_start_offset = (start), \ + .reg_end_offset = (end), \ + .reg_access = (access), \ + .valid = true, \ + } + +#define S32_INVALID_REG_RANGE \ + { \ + .reg_name = NULL, \ + .reg_access = NULL, \ + .valid = false, \ + } + +static const struct mfd_cell nxp_siul2_devs[] = { + { + .name = "s32g-siul2-pinctrl", + } +}; + +/** + * struct nxp_siul2_reg_range_info: a register range in SIUL2 + * @reg_start_offset: the first valid register offset + * @reg_end_offset: the last valid register offset + * @reg_access: the read/write access tables if not NULL + * @valid: whether the register range is valid or not + */ +struct nxp_siul2_reg_range_info { + const char *reg_name; + unsigned int reg_start_offset; + unsigned int reg_end_offset; + const struct regmap_access_table *reg_access; + bool valid; +}; + +static const struct regmap_range s32g2_siul2_0_imcr_reg_ranges[] = { + /* IMCR0 - IMCR1 */ + regmap_reg_range(0, 4), + /* IMCR3 - IMCR61 */ + regmap_reg_range(0xC, 0xF4), + /* IMCR68 - IMCR83 */ + regmap_reg_range(0x110, 0x14C) +}; + +static const struct regmap_access_table s32g2_siul2_0_imcr = { + .yes_ranges = s32g2_siul2_0_imcr_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_0_imcr_reg_ranges) +}; + +static const struct regmap_range s32g2_siul2_0_pgpd_reg_ranges[] = { + /* PGPD*0 - PGPD*5 */ + regmap_reg_range(0, 0xA), + /* PGPD*6 - PGPD*6 */ + regmap_reg_range(0xE, 0xE), +}; + +static const struct regmap_access_table s32g2_siul2_0_pgpd = { + .yes_ranges = s32g2_siul2_0_pgpd_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_0_pgpd_reg_ranges) +}; + +static const struct regmap_range s32g2_siul2_1_irq_reg_ranges[] = { + /* DISR0 */ + regmap_reg_range(0x10, 0x10), + /* DIRER0 */ + regmap_reg_range(0x18, 0x18), + /* DIRSR0 */ + regmap_reg_range(0x20, 0x20), + /* IREER0 */ + regmap_reg_range(0x28, 0x28), + /* IFEER0 */ + regmap_reg_range(0x30, 0x30), +}; + +static const struct regmap_access_table s32g2_siul2_1_irq = { + .yes_ranges = s32g2_siul2_1_irq_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_1_irq_reg_ranges), +}; + +static const struct regmap_range s32g2_siul2_1_irq_volatile_reg_range[] = { + /* DISR0 */ + regmap_reg_range(0x10, 0x10) +}; + +static const struct regmap_access_table s32g2_siul2_1_irq_volatile = { + .yes_ranges = s32g2_siul2_1_irq_volatile_reg_range, + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_1_irq_volatile_reg_range), +}; + +static const struct regmap_range s32g2_siul2_1_mscr_reg_ranges[] = { + /* MSCR112 - MSCR122 */ + regmap_reg_range(0, 0x28), + /* MSCR144 - MSCR190 */ + regmap_reg_range(0x80, 0x138) +}; + +static const struct regmap_access_table s32g2_siul2_1_mscr = { + .yes_ranges = s32g2_siul2_1_mscr_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_1_mscr_reg_ranges), +}; + +static const struct regmap_range s32g2_siul2_1_imcr_reg_ranges[] = { + /* IMCR119 - IMCR121 */ + regmap_reg_range(0, 8), + /* IMCR128 - IMCR129 */ + regmap_reg_range(0x24, 0x28), + /* IMCR143 - IMCR151 */ + regmap_reg_range(0x60, 0x80), + /* IMCR153 - IMCR161 */ + regmap_reg_range(0x88, 0xA8), + /* IMCR205 - IMCR212 */ + regmap_reg_range(0x158, 0x174), + /* IMCR224 - IMCR225 */ + regmap_reg_range(0x1A4, 0x1A8), + /* IMCR233 - IMCR248 */ + regmap_reg_range(0x1C8, 0x204), + /* IMCR273 - IMCR274 */ + regmap_reg_range(0x268, 0x26C), + /* IMCR278 - IMCR281 */ + regmap_reg_range(0x27C, 0x288), + /* IMCR283 - IMCR286 */ + regmap_reg_range(0x290, 0x29C), + /* IMCR288 - IMCR294 */ + regmap_reg_range(0x2A4, 0x2BC), + /* IMCR296 - IMCR302 */ + regmap_reg_range(0x2C4, 0x2DC), + /* IMCR304 - IMCR310 */ + regmap_reg_range(0x2E4, 0x2FC), + /* IMCR312 - IMCR314 */ + regmap_reg_range(0x304, 0x30C), + /* IMCR316 */ + regmap_reg_range(0x314, 0x314), + /* IMCR 318 */ + regmap_reg_range(0x31C, 0x31C), + /* IMCR322 - IMCR340 */ + regmap_reg_range(0x32C, 0x374), + /* IMCR343 - IMCR360 */ + regmap_reg_range(0x380, 0x3C4), + /* IMCR363 - IMCR380 */ + regmap_reg_range(0x3D0, 0x414), + /* IMCR383 - IMCR393 */ + regmap_reg_range(0x420, 0x448), + /* IMCR398 - IMCR433 */ + regmap_reg_range(0x45C, 0x4E8), + /* IMCR467 - IMCR470 */ + regmap_reg_range(0x570, 0x57C), + /* IMCR473 - IMCR475 */ + regmap_reg_range(0x588, 0x590), + /* IMCR478 - IMCR480*/ + regmap_reg_range(0x59C, 0x5A4), + /* IMCR483 - IMCR485 */ + regmap_reg_range(0x5B0, 0x5B8), + /* IMCR488 - IMCR490 */ + regmap_reg_range(0x5C4, 0x5CC), + /* IMCR493 - IMCR495 */ + regmap_reg_range(0x5D8, 0x5E0), +}; + +static const struct regmap_access_table s32g2_siul2_1_imcr = { + .yes_ranges = s32g2_siul2_1_imcr_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_1_imcr_reg_ranges) +}; + +static const struct regmap_range s32g2_siul2_1_pgpd_reg_ranges[] = { + /* PGPD*7 */ + regmap_reg_range(0xC, 0xC), + /* PGPD*9 */ + regmap_reg_range(0x10, 0x10), + /* PDPG*10 - PGPD*11 */ + regmap_reg_range(0x14, 0x16), +}; + +static const struct regmap_access_table s32g2_siul2_1_pgpd = { + .yes_ranges = s32g2_siul2_1_pgpd_reg_ranges, + .n_yes_ranges = ARRAY_SIZE(s32g2_siul2_1_pgpd_reg_ranges) +}; + +static const struct nxp_siul2_reg_range_info +s32g2_reg_ranges[S32G_NUM_SIUL2][SIUL2_NUM_REG_TYPES] = { + /* SIUL2_0 */ + { + [SIUL2_MPIDR] = S32_REG_RANGE(4, 8, "SIUL2_0_MPIDR", NULL), + /* Interrupts are to be controlled from SIUL2_1 */ + [SIUL2_IRQ] = S32_INVALID_REG_RANGE, + [SIUL2_MSCR] = S32_REG_RANGE(0x240, 0x3D4, "SIUL2_0_MSCR", + NULL), + [SIUL2_IMCR] = S32_REG_RANGE(0xA40, 0xB8C, "SIUL2_0_IMCR", + &s32g2_siul2_0_imcr), + [SIUL2_PGPDO] = S32_REG_RANGE(0x1700, 0x170E, + "SIUL2_0_PGPDO", + &s32g2_siul2_0_pgpd), + [SIUL2_PGPDI] = S32_REG_RANGE(0x1740, 0x174E, + "SIUL2_0_PGPDI", + &s32g2_siul2_0_pgpd), + }, + /* SIUL2_1 */ + { + [SIUL2_MPIDR] = S32_REG_RANGE(4, 8, "SIUL2_1_MPIDR", NULL), + [SIUL2_IRQ] = S32_REG_RANGE(0x10, 0xC0, "SIUL2_1_IRQ", + &s32g2_siul2_1_irq), + [SIUL2_MSCR] = S32_REG_RANGE(0x400, 0x538, "SIUL2_1_MSCR", + &s32g2_siul2_1_mscr), + [SIUL2_IMCR] = S32_REG_RANGE(0xC1C, 0x11FC, "SIUL2_1_IMCR", + &s32g2_siul2_1_imcr), + [SIUL2_PGPDO] = S32_REG_RANGE(0x1700, 0x1716, + "SIUL2_1_PGPDO", + &s32g2_siul2_1_pgpd), + [SIUL2_PGPDI] = S32_REG_RANGE(0x1740, 0x1756, + "SIUL2_1_PGPDI", + &s32g2_siul2_1_pgpd), + }, +}; + +static const struct regmap_config nxp_siul2_regmap_irq_conf = { + .val_bits = 32, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .reg_bits = 32, + .reg_stride = 4, + .cache_type = REGCACHE_FLAT, + .use_raw_spinlock = true, + .volatile_table = &s32g2_siul2_1_irq_volatile, +}; + +static const struct regmap_config nxp_siul2_regmap_generic_conf = { + .val_bits = 32, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .reg_bits = 32, + .reg_stride = 4, + .cache_type = REGCACHE_FLAT, + .use_raw_spinlock = true, +}; + +static const struct regmap_config nxp_siul2_regmap_pgpdo_conf = { + .val_bits = 16, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .reg_bits = 32, + .reg_stride = 2, + .cache_type = REGCACHE_FLAT, + .use_raw_spinlock = true, +}; + +static const struct regmap_config nxp_siul2_regmap_pgpdi_conf = { + .val_bits = 16, + .val_format_endian = REGMAP_ENDIAN_LITTLE, + .reg_bits = 32, + .reg_stride = 2, + .cache_type = REGCACHE_NONE, + .use_raw_spinlock = true, +}; + +static int nxp_siul2_init_regmap(struct platform_device *pdev, + void __iomem *base, int siul) +{ + struct regmap_config regmap_configs[SIUL2_NUM_REG_TYPES] = { + [SIUL2_MPIDR] = nxp_siul2_regmap_generic_conf, + [SIUL2_IRQ] = nxp_siul2_regmap_irq_conf, + [SIUL2_MSCR] = nxp_siul2_regmap_generic_conf, + [SIUL2_IMCR] = nxp_siul2_regmap_generic_conf, + [SIUL2_PGPDO] = nxp_siul2_regmap_pgpdo_conf, + [SIUL2_PGPDI] = nxp_siul2_regmap_pgpdi_conf, + }; + const struct nxp_siul2_reg_range_info *tmp_range; + struct regmap_config *tmp_conf; + struct nxp_siul2_info *info; + struct nxp_siul2_mfd *priv; + void __iomem *reg_start; + int i, ret; + + priv = platform_get_drvdata(pdev); + info = &priv->siul2[siul]; + + for (i = 0; i < SIUL2_NUM_REG_TYPES; i++) { + if (!s32g2_reg_ranges[siul][i].valid) + continue; + + tmp_range = &s32g2_reg_ranges[siul][i]; + tmp_conf = ®map_configs[i]; + tmp_conf->name = tmp_range->reg_name; + tmp_conf->max_register = + tmp_range->reg_end_offset - tmp_range->reg_start_offset; + + if (tmp_conf->cache_type != REGCACHE_NONE) + tmp_conf->num_reg_defaults_raw = + tmp_conf->max_register / tmp_conf->reg_stride; + + if (tmp_range->reg_access) { + tmp_conf->wr_table = tmp_range->reg_access; + tmp_conf->rd_table = tmp_range->reg_access; + } + + reg_start = base + tmp_range->reg_start_offset; + info->regmaps[i] = devm_regmap_init_mmio(&pdev->dev, reg_start, + tmp_conf); + if (IS_ERR(info->regmaps[i])) { + dev_err(&pdev->dev, "regmap %d init failed: %d\n", i, + ret); + return PTR_ERR(info->regmaps[i]); + } + } + + return 0; +} + +static int nxp_siul2_parse_dtb(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct of_phandle_args pinspec; + struct nxp_siul2_mfd *priv; + void __iomem *base; + char reg_name[16]; + int i, ret; + + priv = platform_get_drvdata(pdev); + + for (i = 0; i < priv->num_siul2; i++) { + ret = snprintf(reg_name, ARRAY_SIZE(reg_name), "siul2%d", i); + if (ret < 0 || ret >= ARRAY_SIZE(reg_name)) + return ret; + + base = devm_platform_ioremap_resource_byname(pdev, reg_name); + if (IS_ERR(base)) { + dev_err(&pdev->dev, "Failed to get MEM resource: %s\n", + reg_name); + return PTR_ERR(base); + } + + ret = nxp_siul2_init_regmap(pdev, base, i); + if (ret) + return ret; + + ret = of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, + i, &pinspec); + if (ret) + return ret; + + of_node_put(pinspec.np); + + if (pinspec.args_count != 3) { + dev_err(&pdev->dev, "Invalid pinspec count: %d\n", + pinspec.args_count); + return -EINVAL; + } + + priv->siul2[i].gpio_base = pinspec.args[1]; + priv->siul2[i].gpio_num = pinspec.args[2]; + } + + return 0; +} + +static int nxp_siul2_probe(struct platform_device *pdev) +{ + struct nxp_siul2_mfd *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->num_siul2 = S32G_NUM_SIUL2; + priv->siul2 = devm_kcalloc(&pdev->dev, priv->num_siul2, + sizeof(*priv->siul2), GFP_KERNEL); + if (!priv->siul2) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + ret = nxp_siul2_parse_dtb(pdev); + if (ret) + return ret; + + return devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, + nxp_siul2_devs, ARRAY_SIZE(nxp_siul2_devs), + NULL, 0, NULL); +} + +static const struct of_device_id nxp_siul2_dt_ids[] = { + { .compatible = "nxp,s32g2-siul2" }, + { .compatible = "nxp,s32g3-siul2" }, + { }, +}; +MODULE_DEVICE_TABLE(of, nxp_siul2_dt_ids); + +static struct platform_driver nxp_siul2_mfd_driver = { + .driver = { + .name = "nxp-siul2-mfd", + .of_match_table = nxp_siul2_dt_ids, + }, + .probe = nxp_siul2_probe, +}; + +module_platform_driver(nxp_siul2_mfd_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("NXP SIUL2 MFD driver"); +MODULE_AUTHOR("Andrei Stefanescu <andrei.stefanescu@oss.nxp.com>"); diff --git a/include/linux/mfd/nxp-siul2.h b/include/linux/mfd/nxp-siul2.h new file mode 100644 index 000000000000..238c812dba29 --- /dev/null +++ b/include/linux/mfd/nxp-siul2.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * + * S32 SIUL2 core definitions + * + * Copyright 2024 NXP + */ + +#ifndef __DRIVERS_MFD_NXP_SIUL2_H +#define __DRIVERS_MFD_NXP_SIUL2_H + +#include <linux/regmap.h> + +/** + * enum nxp_siul2_reg_type - an enum for SIUL2 reg types + * @SIUL2_MPIDR - SoC info + * @SIUL2_IRQ - IRQ related registers, only valid in SIUL2_1 + * @SIUL2_MSCR - used for pinmuxing and pinconf + * @SIUL2_IMCR - used for pinmuxing + * @SIUL2_PGPDO - writing the GPIO value + * @SIUL2_PGPDI - reading the GPIO value + */ +enum nxp_siul2_reg_type { + SIUL2_MPIDR, + SIUL2_IRQ, + SIUL2_MSCR, + SIUL2_IMCR, + SIUL2_PGPDO, + SIUL2_PGPDI, + + SIUL2_NUM_REG_TYPES +}; + +/** + * struct nxp_siul2_info - details about one SIUL2 hardware instance + * @regmaps: the regmaps for each register type for a SIUL2 hardware instance + * @gpio_base: the first GPIO in this SIUL2 module + * @gpio_num: the number of GPIOs in this SIUL2 module + */ +struct nxp_siul2_info { + struct regmap *regmaps[SIUL2_NUM_REG_TYPES]; + u32 gpio_base; + u32 gpio_num; +}; + +/** + * struct nxp_siul2_mfd - driver data + * @siul2: info about the SIUL2 modules present + * @num_siul2: number of siul2 modules + */ +struct nxp_siul2_mfd { + struct nxp_siul2_info *siul2; + u8 num_siul2; +}; + +#endif /* __DRIVERS_MFD_NXP_SIUL2_H */
SIUL2 (System Integration Unit Lite) is a hardware module which implements various functionalities: - reading SoC information - pinctrl - GPIO (including interrupts) There are multiple register types in the SIUL2 module: - MIDR (MCU ID Register) * contains information about the SoC. - Interrupt related registers * There are 32 interrupts named EIRQ. An EIRQ may be routed to one or more GPIOs. Not all GPIOs have EIRQs associated with them - MSCR (Multiplexed Signal Configuration Register) * handle pinmuxing and pinconf - IMCR (Input Multiplexed Signal Configuration Register) * are part of pinmuxing - PGPDO/PGPDI (Parallel GPIO Pad Data Out/In Register) * Write/Read the GPIO value There are two SIUL2 modules in the S32G SoC. This driver handles both because functionality is shared between them. For example: some GPIOs in SIUL2_0 have interrupt capability but the registers configuring this are in SIUL2_1. Signed-off-by: Andrei Stefanescu <andrei.stefanescu@oss.nxp.com> --- drivers/mfd/Kconfig | 12 + drivers/mfd/Makefile | 1 + drivers/mfd/nxp-siul2.c | 411 ++++++++++++++++++++++++++++++++++ include/linux/mfd/nxp-siul2.h | 55 +++++ 4 files changed, 479 insertions(+) create mode 100644 drivers/mfd/nxp-siul2.c create mode 100644 include/linux/mfd/nxp-siul2.h