diff mbox series

[4/6] pinctrl: ls1046a: Add pinctrl driver support

Message ID c0ecf4f4-94f1-2efd-b05b-fc117c62e516@digi.com (mailing list archive)
State New, archived
Headers show
Series [1/6] pinctrl: ls1012a: Add pinctrl driver support | expand

Commit Message

David Leonard Aug. 27, 2024, 2:12 a.m. UTC
Add QorIQ LS1046A pinctrl driver allowing i2c-core to exert
GPIO control over the third and fourth I2C buses.

Signed-off-by: David Leonard <David.Leonard@digi.com>
---
  drivers/pinctrl/freescale/Kconfig           |   8 +
  drivers/pinctrl/freescale/Makefile          |   1 +
  drivers/pinctrl/freescale/pinctrl-ls1046a.c | 224 ++++++++++++++++++++
  3 files changed, 233 insertions(+)
  create mode 100644 drivers/pinctrl/freescale/pinctrl-ls1046a.c

Comments

Frank Li Aug. 27, 2024, 7:19 p.m. UTC | #1
On Tue, Aug 27, 2024 at 12:12:24PM +1000, David Leonard wrote:
>
> Add QorIQ LS1046A pinctrl driver allowing i2c-core to exert
> GPIO control over the third and fourth I2C buses.
>
> Signed-off-by: David Leonard <David.Leonard@digi.com>
> ---

Why not use pinctrl-single,  please ref fsl,lx2160x.dtsi for GPIO/i2c
recover.

Frank

>  drivers/pinctrl/freescale/Kconfig           |   8 +
>  drivers/pinctrl/freescale/Makefile          |   1 +
>  drivers/pinctrl/freescale/pinctrl-ls1046a.c | 224 ++++++++++++++++++++
>  3 files changed, 233 insertions(+)
>  create mode 100644 drivers/pinctrl/freescale/pinctrl-ls1046a.c
>
> diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig
> index a2038042eeae..2641db6c64c7 100644
> --- a/drivers/pinctrl/freescale/Kconfig
> +++ b/drivers/pinctrl/freescale/Kconfig
> @@ -217,6 +217,14 @@ config PINCTRL_LS1012A
>  	help
>  	  Say Y here to enable the ls1012a pinctrl driver
>
> +config PINCTRL_LS1046A
> +	tristate "LS1046A pinctrl driver"
> +	depends on ARCH_LAYERSCAPE && OF || COMPILE_TEST
> +	select PINMUX
> +	select GENERIC_PINCONF
> +	help
> +	  Say Y here to enable the ls1046a pinctrl driver
> +
>  config PINCTRL_VF610
>  	bool "Freescale Vybrid VF610 pinctrl driver"
>  	depends on SOC_VF610
> diff --git a/drivers/pinctrl/freescale/Makefile b/drivers/pinctrl/freescale/Makefile
> index 6926529d8635..66fec747af73 100644
> --- a/drivers/pinctrl/freescale/Makefile
> +++ b/drivers/pinctrl/freescale/Makefile
> @@ -36,3 +36,4 @@ obj-$(CONFIG_PINCTRL_IMX28)	+= pinctrl-imx28.o
>  obj-$(CONFIG_PINCTRL_IMXRT1050)	+= pinctrl-imxrt1050.o
>  obj-$(CONFIG_PINCTRL_IMXRT1170)	+= pinctrl-imxrt1170.o
>  obj-$(CONFIG_PINCTRL_LS1012A)	+= pinctrl-ls1012a.o
> +obj-$(CONFIG_PINCTRL_LS1046A)	+= pinctrl-ls1046a.o
> diff --git a/drivers/pinctrl/freescale/pinctrl-ls1046a.c b/drivers/pinctrl/freescale/pinctrl-ls1046a.c
> new file mode 100644
> index 000000000000..9f5ec31f1e05
> --- /dev/null
> +++ b/drivers/pinctrl/freescale/pinctrl-ls1046a.c
> @@ -0,0 +1,224 @@
> +// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
> +/*
> + * Pin controller for NXP QorIQ LS1046A.
> + *
> + * Copyright (c) 2024 Digi International, Inc.
> + * Author: David Leonard <David.Leonard@digi.com>
> + */
> +#include <linux/module.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/pinctrl/pinconf-generic.h>
> +#include <linux/of.h>
> +#include <linux/io.h>
> +#include <linux/platform_device.h>
> +#include <linux/sys_soc.h>
> +
> +struct ls1046a_pinctrl_pdata {
> +	struct pinctrl_dev *pctl_dev;
> +	void __iomem *cr0mem;
> +	bool big_endian;
> +	u32 ssc;
> +};
> +
> +/*
> + *       L4           M4            M3           N3
> + *  i2c  IIC3_SCL     IIC3_SDA      IIC4_SCL     IIC4_SDA
> + *  gpio GPIO_4[10]   GPIO_4[11]    GPIO_4[12]   GPIO_4[13]
> + *  evt  EVT_B[5]     EVT_B[6]      EVT_B[7]     EVT_B[8]
> + *  usb  USB2_DRVVBUS USB2_PWRFAULT USB3_DRVVBUS USB3_PWRFAULT
> + *  ftm  FTM8_CH0     FTM8_CH1      FTM3_FAULT   FTM_EXT3CLK
> + */
> +
> +/* SCFG_RCWPMUXCR0 pinmux control register */
> +#define SCFG_RCWPMUXCR0			0x0157040c
> +#define RCWPMUXCR0_FIELD(shift, func)	((u32)(func) << (29 - (shift)))
> +#define RCWPMUXCR0_MASK(shift)		RCWPMUXCR0_FIELD(shift, RCWPMUXCR0_FUNC_MASK)
> +#define RCWPMUXCR0_IIC3_SCL_SHIFT	17
> +#define RCWPMUXCR0_IIC3_SDA_SHIFT	21
> +#define RCWPMUXCR0_IIC4_SCL_SHIFT	25
> +#define RCWPMUXCR0_IIC4_SDA_SHIFT	29
> +#define RCWPMUXCR0_FUNC_I2C		0
> +#define RCWPMUXCR0_FUNC_GPIO		1
> +#define RCWPMUXCR0_FUNC_EVT		2
> +#define RCWPMUXCR0_FUNC_USB		3
> +#define RCWPMUXCR0_FUNC_FTM		5
> +#define RCWPMUXCR0_FUNC_CLK		6
> +#define RCWPMUXCR0_FUNC_MASK		7
> +
> +#define PIN_L4 0 /* IIC3_SCL/GPIO_4[10]/EVT_B[5]/USB2_DRVVBUS/FTM8_CH0 */
> +#define PIN_M4 1 /* IIC3_SDA/GPIO_4[11]/EVT_B[6]/USB2_PWRFAULT/FTM8_CH1 */
> +#define PIN_M3 2 /* IIC4_SCL/GPIO_4[12]/EVT_B[7]/USB3_DRVVBUS/FTM3_FAULT */
> +#define PIN_N3 3 /* IIC4_SDA/GPIO_4[13]/EVT_B[8]/USB3_PWRFAULT/FTM_EXT3CLK */
> +
> +const struct pinctrl_pin_desc ls1046a_pins[] = {
> +	PINCTRL_PIN(PIN_L4, "L4"),
> +	PINCTRL_PIN(PIN_M4, "M4"),
> +	PINCTRL_PIN(PIN_M3, "M3"),
> +	PINCTRL_PIN(PIN_N3, "N3"),
> +};
> +
> +/* Each pin is its own group */
> +static const char * const ls1046a_groups[] = { "L4", "M4", "M3", "N3" };
> +
> +static int ls1046a_get_groups_count(struct pinctrl_dev *pcdev)
> +{
> +	return ARRAY_SIZE(ls1046a_pins);
> +}
> +
> +static const char *ls1046a_get_group_name(struct pinctrl_dev *pcdev,
> +	unsigned int selector)
> +{
> +	return ls1046a_pins[selector].name;
> +}
> +
> +static int ls1046a_get_group_pins(struct pinctrl_dev *pcdev,
> +	unsigned int selector, const unsigned int **pins, unsigned int *npins)
> +{
> +	*pins = &ls1046a_pins[selector].number;
> +	*npins = 1;
> +	return 0;
> +}
> +
> +static const struct pinctrl_ops ls1046a_pinctrl_ops = {
> +	.get_groups_count = ls1046a_get_groups_count,
> +	.get_group_name = ls1046a_get_group_name,
> +	.get_group_pins = ls1046a_get_group_pins,
> +	.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
> +	.dt_free_map = pinconf_generic_dt_free_map,
> +};
> +
> +/* Every pin has the same set of functions */
> +#define FUNC_i2c	0
> +#define FUNC_gpio	1
> +#define FUNC_evt	2
> +#define FUNC_usb	3
> +#define FUNC_ftm	4
> +
> +#define _PINFUNC(name) \
> +	[FUNC_##name] = PINCTRL_PINFUNCTION(#name, ls1046a_groups, ARRAY_SIZE(ls1046a_groups))
> +static const struct pinfunction ls1046a_functions[] = {
> +	_PINFUNC(i2c),
> +	_PINFUNC(gpio),
> +	_PINFUNC(evt),
> +	_PINFUNC(usb),
> +	_PINFUNC(ftm),
> +};
> +
> +static int ls1046a_get_functions_count(struct pinctrl_dev *pctldev)
> +{
> +	return ARRAY_SIZE(ls1046a_functions);
> +}
> +
> +static const char *ls1046a_get_function_name(struct pinctrl_dev *pctldev, unsigned int func)
> +{
> +	return ls1046a_functions[func].name;
> +}
> +
> +static int ls1046a_get_function_groups(struct pinctrl_dev *pctldev, unsigned int func,
> +	const char * const **groups,
> +	unsigned int * const ngroups)
> +{
> +	*groups = ls1046a_functions[func].groups;
> +	*ngroups = ls1046a_functions[func].ngroups;
> +	return 0;
> +}
> +
> +static int ls1046a_set_mux(struct pinctrl_dev *pcdev,
> +	unsigned int func, unsigned int pin)
> +{
> +	struct ls1046a_pinctrl_pdata *pd = pinctrl_dev_get_drvdata(pcdev);
> +	static const u32 cr0_reg_func[] = {
> +		[FUNC_i2c] = RCWPMUXCR0_FUNC_I2C,
> +		[FUNC_gpio] = RCWPMUXCR0_FUNC_GPIO,
> +		[FUNC_evt] = RCWPMUXCR0_FUNC_EVT,
> +		[FUNC_usb] = RCWPMUXCR0_FUNC_USB,
> +		[FUNC_ftm] = RCWPMUXCR0_FUNC_FTM,
> +	};
> +	static const unsigned int cr0_pin_shift[] = {
> +		[PIN_L4] = RCWPMUXCR0_IIC3_SCL_SHIFT,
> +		[PIN_M4] = RCWPMUXCR0_IIC3_SDA_SHIFT,
> +		[PIN_M3] = RCWPMUXCR0_IIC4_SCL_SHIFT,
> +		[PIN_N3] = RCWPMUXCR0_IIC4_SDA_SHIFT,
> +	};
> +	u32 cr0;
> +
> +	if (pd->big_endian)
> +		cr0 = ioread32be(pd->cr0mem);
> +	else
> +		cr0 = ioread32(pd->cr0mem);
> +
> +	unsigned int pin_shift = cr0_pin_shift[pin];
> +	u32 reg_func = cr0_reg_func[func];
> +	u32 newcr0 = (cr0 & ~RCWPMUXCR0_MASK(pin_shift)) |
> +		RCWPMUXCR0_FIELD(pin_shift, reg_func);
> +
> +	if (pd->big_endian)
> +		iowrite32be(newcr0, pd->cr0mem);
> +	else
> +		iowrite32(newcr0, pd->cr0mem);
> +	return 0;
> +}
> +
> +static const struct pinmux_ops ls1046a_pinmux_ops = {
> +	.get_functions_count = ls1046a_get_functions_count,
> +	.get_function_name = ls1046a_get_function_name,
> +	.get_function_groups = ls1046a_get_function_groups,
> +	.set_mux = ls1046a_set_mux,
> +};
> +
> +static const struct pinctrl_desc ls1046a_pinctrl_desc = {
> +	.name = "ls1046a",
> +	.pins = ls1046a_pins,
> +	.npins = ARRAY_SIZE(ls1046a_pins),
> +	.pctlops = &ls1046a_pinctrl_ops,
> +	.pmxops = &ls1046a_pinmux_ops,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int ls1046a_pinctrl_probe(struct platform_device *pdev)
> +{
> +	struct ls1046a_pinctrl_pdata *pd;
> +	int ret;
> +
> +	pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
> +	if (!pd)
> +		return -ENOMEM;
> +	platform_set_drvdata(pdev, pd);
> +
> +	pd->big_endian = device_is_big_endian(&pdev->dev);
> +
> +	/* SCFG PMUX0 */
> +	pd->cr0mem = devm_platform_ioremap_resource(pdev, 0);
> +	if (IS_ERR(pd->cr0mem))
> +		return PTR_ERR(pd->cr0mem);
> +	dev_dbg(&pdev->dev, "scfg pmuxcr0 at %px %s", pd->cr0mem,
> +		pd->big_endian ? "be" : "le");
> +
> +	ret = devm_pinctrl_register_and_init(&pdev->dev, &ls1046a_pinctrl_desc,
> +		pd, &pd->pctl_dev);
> +	if (ret)
> +		return dev_err_probe(&pdev->dev, ret, "Failed pinctrl init\n");
> +
> +	pinctrl_enable(pd->pctl_dev);
> +	return ret;
> +}
> +
> +static const struct of_device_id ls1046a_pinctrl_match_table[] = {
> +	{ .compatible = "fsl,ls1046a-pinctrl" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver ls1046a_pinctrl_driver = {
> +	.driver = {
> +		.name = "ls1046a_pinctrl",
> +		.of_match_table = ls1046a_pinctrl_match_table,
> +	},
> +	.probe = ls1046a_pinctrl_probe,
> +};
> +
> +builtin_platform_driver(ls1046a_pinctrl_driver)
> +
> +MODULE_DESCRIPTION("LS1046A pinctrl driver");
> +MODULE_LICENSE("GPL");
> --
> 2.43.0
>
kernel test robot Aug. 27, 2024, 9:10 p.m. UTC | #2
Hi David,

kernel test robot noticed the following build errors:

[auto build test ERROR on linusw-pinctrl/devel]
[also build test ERROR on linusw-pinctrl/for-next shawnguo/for-next arm64/for-next/core kvmarm/next rockchip/for-next soc/for-next linus/master v6.11-rc5 next-20240827]
[cannot apply to arm/for-next arm/fixes]
[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/David-Leonard/arm64-dts-ls1012a-add-pinctrl-node/20240827-104431
base:   https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git devel
patch link:    https://lore.kernel.org/r/c0ecf4f4-94f1-2efd-b05b-fc117c62e516%40digi.com
patch subject: [PATCH 4/6] pinctrl: ls1046a: Add pinctrl driver support
config: s390-allmodconfig (https://download.01.org/0day-ci/archive/20240828/202408280509.Jr3oFGGy-lkp@intel.com/config)
compiler: clang version 20.0.0git (https://github.com/llvm/llvm-project 08e5a1de8227512d4774a534b91cb2353cef6284)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240828/202408280509.Jr3oFGGy-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/202408280509.Jr3oFGGy-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from drivers/pinctrl/freescale/pinctrl-ls1046a.c:8:
   In file included from include/linux/module.h:19:
   In file included from include/linux/elf.h:6:
   In file included from arch/s390/include/asm/elf.h:181:
   In file included from arch/s390/include/asm/mmu_context.h:11:
   In file included from arch/s390/include/asm/pgalloc.h:18:
   In file included from include/linux/mm.h:2228:
   include/linux/vmstat.h:500:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     500 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     501 |                            item];
         |                            ~~~~
   include/linux/vmstat.h:507:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     507 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     508 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:514:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
     514 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
   include/linux/vmstat.h:519:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     519 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     520 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:528:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     528 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     529 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   In file included from drivers/pinctrl/freescale/pinctrl-ls1046a.c:14:
   In file included from include/linux/io.h:14:
   In file included from arch/s390/include/asm/io.h:93:
   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/big_endian.h:37:59: note: expanded from macro '__le16_to_cpu'
      37 | #define __le16_to_cpu(x) __swab16((__force __u16)(__le16)(x))
         |                                                           ^
   include/uapi/linux/swab.h:102:54: note: expanded from macro '__swab16'
     102 | #define __swab16(x) (__u16)__builtin_bswap16((__u16)(x))
         |                                                      ^
   In file included from drivers/pinctrl/freescale/pinctrl-ls1046a.c:14:
   In file included from include/linux/io.h:14:
   In file included from arch/s390/include/asm/io.h:93:
   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/big_endian.h:35:59: note: expanded from macro '__le32_to_cpu'
      35 | #define __le32_to_cpu(x) __swab32((__force __u32)(__le32)(x))
         |                                                           ^
   include/uapi/linux/swab.h:115:54: note: expanded from macro '__swab32'
     115 | #define __swab32(x) (__u32)__builtin_bswap32((__u32)(x))
         |                                                      ^
   In file included from drivers/pinctrl/freescale/pinctrl-ls1046a.c:14:
   In file included from include/linux/io.h:14:
   In file included from arch/s390/include/asm/io.h:93:
   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);
         |                                                       ~~~~~~~~~~ ^
   include/asm-generic/io.h:693:20: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
     693 |         readsb(PCI_IOBASE + addr, buffer, count);
         |                ~~~~~~~~~~ ^
   include/asm-generic/io.h:701:20: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
     701 |         readsw(PCI_IOBASE + addr, buffer, count);
         |                ~~~~~~~~~~ ^
   include/asm-generic/io.h:709:20: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
     709 |         readsl(PCI_IOBASE + addr, buffer, count);
         |                ~~~~~~~~~~ ^
   include/asm-generic/io.h:718:21: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
     718 |         writesb(PCI_IOBASE + addr, buffer, count);
         |                 ~~~~~~~~~~ ^
   include/asm-generic/io.h:727:21: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
     727 |         writesw(PCI_IOBASE + addr, buffer, count);
         |                 ~~~~~~~~~~ ^
   include/asm-generic/io.h:736:21: warning: performing pointer arithmetic on a null pointer has undefined behavior [-Wnull-pointer-arithmetic]
     736 |         writesl(PCI_IOBASE + addr, buffer, count);
         |                 ~~~~~~~~~~ ^
>> drivers/pinctrl/freescale/pinctrl-ls1046a.c:199:51: error: passing 'const struct pinctrl_desc *' to parameter of type 'struct pinctrl_desc *' discards qualifiers [-Werror,-Wincompatible-pointer-types-discards-qualifiers]
     199 |         ret = devm_pinctrl_register_and_init(&pdev->dev, &ls1046a_pinctrl_desc,
         |                                                          ^~~~~~~~~~~~~~~~~~~~~
   include/linux/pinctrl/pinctrl.h:180:26: note: passing argument to parameter 'pctldesc' here
     180 |                                 struct pinctrl_desc *pctldesc,
         |                                                      ^
   17 warnings and 1 error generated.


vim +199 drivers/pinctrl/freescale/pinctrl-ls1046a.c

   179	
   180	static int ls1046a_pinctrl_probe(struct platform_device *pdev)
   181	{
   182		struct ls1046a_pinctrl_pdata *pd;
   183		int ret;
   184	
   185		pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
   186		if (!pd)
   187			return -ENOMEM;
   188		platform_set_drvdata(pdev, pd);
   189	
   190		pd->big_endian = device_is_big_endian(&pdev->dev);
   191	
   192		/* SCFG PMUX0 */
   193		pd->cr0mem = devm_platform_ioremap_resource(pdev, 0);
   194		if (IS_ERR(pd->cr0mem))
   195			return PTR_ERR(pd->cr0mem);
   196		dev_dbg(&pdev->dev, "scfg pmuxcr0 at %px %s", pd->cr0mem,
   197			pd->big_endian ? "be" : "le");
   198	
 > 199		ret = devm_pinctrl_register_and_init(&pdev->dev, &ls1046a_pinctrl_desc,
   200			pd, &pd->pctl_dev);
   201		if (ret)
   202			return dev_err_probe(&pdev->dev, ret, "Failed pinctrl init\n");
   203	
   204		pinctrl_enable(pd->pctl_dev);
   205		return ret;
   206	}
   207
kernel test robot Aug. 27, 2024, 9:31 p.m. UTC | #3
Hi David,

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 shawnguo/for-next arm64/for-next/core kvmarm/next rockchip/for-next soc/for-next linus/master v6.11-rc5 next-20240827]
[cannot apply to arm/for-next arm/fixes]
[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/David-Leonard/arm64-dts-ls1012a-add-pinctrl-node/20240827-104431
base:   https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git devel
patch link:    https://lore.kernel.org/r/c0ecf4f4-94f1-2efd-b05b-fc117c62e516%40digi.com
patch subject: [PATCH 4/6] pinctrl: ls1046a: Add pinctrl driver support
config: s390-allyesconfig (https://download.01.org/0day-ci/archive/20240828/202408280532.YGyLCCNL-lkp@intel.com/config)
compiler: s390-linux-gcc (GCC) 14.1.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20240828/202408280532.YGyLCCNL-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/202408280532.YGyLCCNL-lkp@intel.com/

All warnings (new ones prefixed by >>):

   drivers/pinctrl/freescale/pinctrl-ls1046a.c: In function 'ls1046a_pinctrl_probe':
>> drivers/pinctrl/freescale/pinctrl-ls1046a.c:199:58: warning: passing argument 2 of 'devm_pinctrl_register_and_init' discards 'const' qualifier from pointer target type [-Wdiscarded-qualifiers]
     199 |         ret = devm_pinctrl_register_and_init(&pdev->dev, &ls1046a_pinctrl_desc,
         |                                                          ^~~~~~~~~~~~~~~~~~~~~
   In file included from drivers/pinctrl/freescale/pinctrl-ls1046a.c:10:
   include/linux/pinctrl/pinctrl.h:180:54: note: expected 'struct pinctrl_desc *' but argument is of type 'const struct pinctrl_desc *'
     180 |                                 struct pinctrl_desc *pctldesc,
         |                                 ~~~~~~~~~~~~~~~~~~~~~^~~~~~~~


vim +199 drivers/pinctrl/freescale/pinctrl-ls1046a.c

   179	
   180	static int ls1046a_pinctrl_probe(struct platform_device *pdev)
   181	{
   182		struct ls1046a_pinctrl_pdata *pd;
   183		int ret;
   184	
   185		pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
   186		if (!pd)
   187			return -ENOMEM;
   188		platform_set_drvdata(pdev, pd);
   189	
   190		pd->big_endian = device_is_big_endian(&pdev->dev);
   191	
   192		/* SCFG PMUX0 */
   193		pd->cr0mem = devm_platform_ioremap_resource(pdev, 0);
   194		if (IS_ERR(pd->cr0mem))
   195			return PTR_ERR(pd->cr0mem);
   196		dev_dbg(&pdev->dev, "scfg pmuxcr0 at %px %s", pd->cr0mem,
   197			pd->big_endian ? "be" : "le");
   198	
 > 199		ret = devm_pinctrl_register_and_init(&pdev->dev, &ls1046a_pinctrl_desc,
   200			pd, &pd->pctl_dev);
   201		if (ret)
   202			return dev_err_probe(&pdev->dev, ret, "Failed pinctrl init\n");
   203	
   204		pinctrl_enable(pd->pctl_dev);
   205		return ret;
   206	}
   207
kernel test robot Aug. 28, 2024, 7:16 a.m. UTC | #4
Hi David,

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 shawnguo/for-next arm64/for-next/core kvmarm/next rockchip/for-next soc/for-next linus/master v6.11-rc5 next-20240827]
[cannot apply to arm/for-next arm/fixes]
[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/David-Leonard/arm64-dts-ls1012a-add-pinctrl-node/20240827-104431
base:   https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git devel
patch link:    https://lore.kernel.org/r/c0ecf4f4-94f1-2efd-b05b-fc117c62e516%40digi.com
patch subject: [PATCH 4/6] pinctrl: ls1046a: Add pinctrl driver support
config: sh-randconfig-r113-20240828 (https://download.01.org/0day-ci/archive/20240828/202408281442.Xos98wkO-lkp@intel.com/config)
compiler: sh4-linux-gcc (GCC) 14.1.0
reproduce: (https://download.01.org/0day-ci/archive/20240828/202408281442.Xos98wkO-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/202408281442.Xos98wkO-lkp@intel.com/

sparse warnings: (new ones prefixed by >>)
>> drivers/pinctrl/freescale/pinctrl-ls1046a.c:55:31: sparse: sparse: symbol 'ls1046a_pins' was not declared. Should it be static?
>> drivers/pinctrl/freescale/pinctrl-ls1046a.c:199:59: sparse: sparse: incorrect type in argument 2 (different modifiers) @@     expected struct pinctrl_desc *pctldesc @@     got struct pinctrl_desc const * @@
   drivers/pinctrl/freescale/pinctrl-ls1046a.c:199:59: sparse:     expected struct pinctrl_desc *pctldesc
   drivers/pinctrl/freescale/pinctrl-ls1046a.c:199:59: sparse:     got struct pinctrl_desc const *

vim +/ls1046a_pins +55 drivers/pinctrl/freescale/pinctrl-ls1046a.c

    54	
  > 55	const struct pinctrl_pin_desc ls1046a_pins[] = {
    56		PINCTRL_PIN(PIN_L4, "L4"),
    57		PINCTRL_PIN(PIN_M4, "M4"),
    58		PINCTRL_PIN(PIN_M3, "M3"),
    59		PINCTRL_PIN(PIN_N3, "N3"),
    60	};
    61	
    62	/* Each pin is its own group */
    63	static const char * const ls1046a_groups[] = { "L4", "M4", "M3", "N3" };
    64	
    65	static int ls1046a_get_groups_count(struct pinctrl_dev *pcdev)
    66	{
    67		return ARRAY_SIZE(ls1046a_pins);
    68	}
    69	
    70	static const char *ls1046a_get_group_name(struct pinctrl_dev *pcdev,
    71		unsigned int selector)
    72	{
    73		return ls1046a_pins[selector].name;
    74	}
    75	
    76	static int ls1046a_get_group_pins(struct pinctrl_dev *pcdev,
    77		unsigned int selector, const unsigned int **pins, unsigned int *npins)
    78	{
    79		*pins = &ls1046a_pins[selector].number;
    80		*npins = 1;
    81		return 0;
    82	}
    83	
    84	static const struct pinctrl_ops ls1046a_pinctrl_ops = {
    85		.get_groups_count = ls1046a_get_groups_count,
    86		.get_group_name = ls1046a_get_group_name,
    87		.get_group_pins = ls1046a_get_group_pins,
    88		.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
    89		.dt_free_map = pinconf_generic_dt_free_map,
    90	};
    91	
    92	/* Every pin has the same set of functions */
    93	#define FUNC_i2c	0
    94	#define FUNC_gpio	1
    95	#define FUNC_evt	2
    96	#define FUNC_usb	3
    97	#define FUNC_ftm	4
    98	
    99	#define _PINFUNC(name) \
   100		[FUNC_##name] = PINCTRL_PINFUNCTION(#name, ls1046a_groups, ARRAY_SIZE(ls1046a_groups))
   101	static const struct pinfunction ls1046a_functions[] = {
   102		_PINFUNC(i2c),
   103		_PINFUNC(gpio),
   104		_PINFUNC(evt),
   105		_PINFUNC(usb),
   106		_PINFUNC(ftm),
   107	};
   108	
   109	static int ls1046a_get_functions_count(struct pinctrl_dev *pctldev)
   110	{
   111		return ARRAY_SIZE(ls1046a_functions);
   112	}
   113	
   114	static const char *ls1046a_get_function_name(struct pinctrl_dev *pctldev, unsigned int func)
   115	{
   116		return ls1046a_functions[func].name;
   117	}
   118	
   119	static int ls1046a_get_function_groups(struct pinctrl_dev *pctldev, unsigned int func,
   120		const char * const **groups,
   121		unsigned int * const ngroups)
   122	{
   123		*groups = ls1046a_functions[func].groups;
   124		*ngroups = ls1046a_functions[func].ngroups;
   125		return 0;
   126	}
   127	
   128	static int ls1046a_set_mux(struct pinctrl_dev *pcdev,
   129		unsigned int func, unsigned int pin)
   130	{
   131		struct ls1046a_pinctrl_pdata *pd = pinctrl_dev_get_drvdata(pcdev);
   132		static const u32 cr0_reg_func[] = {
   133			[FUNC_i2c] = RCWPMUXCR0_FUNC_I2C,
   134			[FUNC_gpio] = RCWPMUXCR0_FUNC_GPIO,
   135			[FUNC_evt] = RCWPMUXCR0_FUNC_EVT,
   136			[FUNC_usb] = RCWPMUXCR0_FUNC_USB,
   137			[FUNC_ftm] = RCWPMUXCR0_FUNC_FTM,
   138		};
   139		static const unsigned int cr0_pin_shift[] = {
   140			[PIN_L4] = RCWPMUXCR0_IIC3_SCL_SHIFT,
   141			[PIN_M4] = RCWPMUXCR0_IIC3_SDA_SHIFT,
   142			[PIN_M3] = RCWPMUXCR0_IIC4_SCL_SHIFT,
   143			[PIN_N3] = RCWPMUXCR0_IIC4_SDA_SHIFT,
   144		};
   145		u32 cr0;
   146	
   147		if (pd->big_endian)
   148			cr0 = ioread32be(pd->cr0mem);
   149		else
   150			cr0 = ioread32(pd->cr0mem);
   151	
   152		unsigned int pin_shift = cr0_pin_shift[pin];
   153		u32 reg_func = cr0_reg_func[func];
   154		u32 newcr0 = (cr0 & ~RCWPMUXCR0_MASK(pin_shift)) |
   155			RCWPMUXCR0_FIELD(pin_shift, reg_func);
   156	
   157		if (pd->big_endian)
   158			iowrite32be(newcr0, pd->cr0mem);
   159		else
   160			iowrite32(newcr0, pd->cr0mem);
   161		return 0;
   162	}
   163	
   164	static const struct pinmux_ops ls1046a_pinmux_ops = {
   165		.get_functions_count = ls1046a_get_functions_count,
   166		.get_function_name = ls1046a_get_function_name,
   167		.get_function_groups = ls1046a_get_function_groups,
   168		.set_mux = ls1046a_set_mux,
   169	};
   170	
   171	static const struct pinctrl_desc ls1046a_pinctrl_desc = {
   172		.name = "ls1046a",
   173		.pins = ls1046a_pins,
   174		.npins = ARRAY_SIZE(ls1046a_pins),
   175		.pctlops = &ls1046a_pinctrl_ops,
   176		.pmxops = &ls1046a_pinmux_ops,
   177		.owner = THIS_MODULE,
   178	};
   179	
   180	static int ls1046a_pinctrl_probe(struct platform_device *pdev)
   181	{
   182		struct ls1046a_pinctrl_pdata *pd;
   183		int ret;
   184	
   185		pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
   186		if (!pd)
   187			return -ENOMEM;
   188		platform_set_drvdata(pdev, pd);
   189	
   190		pd->big_endian = device_is_big_endian(&pdev->dev);
   191	
   192		/* SCFG PMUX0 */
   193		pd->cr0mem = devm_platform_ioremap_resource(pdev, 0);
   194		if (IS_ERR(pd->cr0mem))
   195			return PTR_ERR(pd->cr0mem);
   196		dev_dbg(&pdev->dev, "scfg pmuxcr0 at %px %s", pd->cr0mem,
   197			pd->big_endian ? "be" : "le");
   198	
 > 199		ret = devm_pinctrl_register_and_init(&pdev->dev, &ls1046a_pinctrl_desc,
   200			pd, &pd->pctl_dev);
   201		if (ret)
   202			return dev_err_probe(&pdev->dev, ret, "Failed pinctrl init\n");
   203	
   204		pinctrl_enable(pd->pctl_dev);
   205		return ret;
   206	}
   207
Linus Walleij Aug. 30, 2024, 10:26 p.m. UTC | #5
On Tue, Aug 27, 2024 at 9:20 PM Frank Li <Frank.li@nxp.com> wrote:
> On Tue, Aug 27, 2024 at 12:12:24PM +1000, David Leonard wrote:
> >
> > Add QorIQ LS1046A pinctrl driver allowing i2c-core to exert
> > GPIO control over the third and fourth I2C buses.
> >
> > Signed-off-by: David Leonard <David.Leonard@digi.com>
> > ---
>
> Why not use pinctrl-single,  please ref fsl,lx2160x.dtsi for GPIO/i2c
> recover.

I'm not so sure about that. Since the 1012 need to use a unique
driver this will be confusing across the family: one chip has
registers and values defined in the device tree, another has
them in the driver, if possible I would advice to keep this
consistent across sibling SoCs.

I can't stop anyone from using pinctrl-single but it's a bit
seductive in it's simplicity, while not always so user-friendly
with all the magic that ends up in the device trees.

Yours,
Linus Walleij
Frank Li Aug. 31, 2024, 4:14 p.m. UTC | #6
On Sat, Aug 31, 2024 at 12:26:47AM +0200, Linus Walleij wrote:
> On Tue, Aug 27, 2024 at 9:20 PM Frank Li <Frank.li@nxp.com> wrote:
> > On Tue, Aug 27, 2024 at 12:12:24PM +1000, David Leonard wrote:
> > >
> > > Add QorIQ LS1046A pinctrl driver allowing i2c-core to exert
> > > GPIO control over the third and fourth I2C buses.
> > >
> > > Signed-off-by: David Leonard <David.Leonard@digi.com>
> > > ---
> >
> > Why not use pinctrl-single,  please ref fsl,lx2160x.dtsi for GPIO/i2c
> > recover.
>
> I'm not so sure about that. Since the 1012 need to use a unique
> driver this will be confusing across the family: one chip has
> registers and values defined in the device tree, another has
> them in the driver, if possible I would advice to keep this
> consistent across sibling SoCs.

There are not new productions of Layerscape. IMX will take over these.
There are only few I2C pins need mux to gpio. It is not wonth to add
new driver for it if pinctrl-single can work.

And 1012 pinctrl driver was not shared with 1046.

Frank

>
> I can't stop anyone from using pinctrl-single but it's a bit
> seductive in it's simplicity, while not always so user-friendly
> with all the magic that ends up in the device trees.
>
> Yours,
> Linus Walleij
diff mbox series

Patch

diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig
index a2038042eeae..2641db6c64c7 100644
--- a/drivers/pinctrl/freescale/Kconfig
+++ b/drivers/pinctrl/freescale/Kconfig
@@ -217,6 +217,14 @@  config PINCTRL_LS1012A
  	help
  	  Say Y here to enable the ls1012a pinctrl driver

+config PINCTRL_LS1046A
+	tristate "LS1046A pinctrl driver"
+	depends on ARCH_LAYERSCAPE && OF || COMPILE_TEST
+	select PINMUX
+	select GENERIC_PINCONF
+	help
+	  Say Y here to enable the ls1046a pinctrl driver
+
  config PINCTRL_VF610
  	bool "Freescale Vybrid VF610 pinctrl driver"
  	depends on SOC_VF610
diff --git a/drivers/pinctrl/freescale/Makefile b/drivers/pinctrl/freescale/Makefile
index 6926529d8635..66fec747af73 100644
--- a/drivers/pinctrl/freescale/Makefile
+++ b/drivers/pinctrl/freescale/Makefile
@@ -36,3 +36,4 @@  obj-$(CONFIG_PINCTRL_IMX28)	+= pinctrl-imx28.o
  obj-$(CONFIG_PINCTRL_IMXRT1050)	+= pinctrl-imxrt1050.o
  obj-$(CONFIG_PINCTRL_IMXRT1170)	+= pinctrl-imxrt1170.o
  obj-$(CONFIG_PINCTRL_LS1012A)	+= pinctrl-ls1012a.o
+obj-$(CONFIG_PINCTRL_LS1046A)	+= pinctrl-ls1046a.o
diff --git a/drivers/pinctrl/freescale/pinctrl-ls1046a.c b/drivers/pinctrl/freescale/pinctrl-ls1046a.c
new file mode 100644
index 000000000000..9f5ec31f1e05
--- /dev/null
+++ b/drivers/pinctrl/freescale/pinctrl-ls1046a.c
@@ -0,0 +1,224 @@ 
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Pin controller for NXP QorIQ LS1046A.
+ *
+ * Copyright (c) 2024 Digi International, Inc.
+ * Author: David Leonard <David.Leonard@digi.com>
+ */
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/sys_soc.h>
+
+struct ls1046a_pinctrl_pdata {
+	struct pinctrl_dev *pctl_dev;
+	void __iomem *cr0mem;
+	bool big_endian;
+	u32 ssc;
+};
+
+/*
+ *       L4           M4            M3           N3
+ *  i2c  IIC3_SCL     IIC3_SDA      IIC4_SCL     IIC4_SDA
+ *  gpio GPIO_4[10]   GPIO_4[11]    GPIO_4[12]   GPIO_4[13]
+ *  evt  EVT_B[5]     EVT_B[6]      EVT_B[7]     EVT_B[8]
+ *  usb  USB2_DRVVBUS USB2_PWRFAULT USB3_DRVVBUS USB3_PWRFAULT
+ *  ftm  FTM8_CH0     FTM8_CH1      FTM3_FAULT   FTM_EXT3CLK
+ */
+
+/* SCFG_RCWPMUXCR0 pinmux control register */
+#define SCFG_RCWPMUXCR0			0x0157040c
+#define RCWPMUXCR0_FIELD(shift, func)	((u32)(func) << (29 - (shift)))
+#define RCWPMUXCR0_MASK(shift)		RCWPMUXCR0_FIELD(shift, RCWPMUXCR0_FUNC_MASK)
+#define RCWPMUXCR0_IIC3_SCL_SHIFT	17
+#define RCWPMUXCR0_IIC3_SDA_SHIFT	21
+#define RCWPMUXCR0_IIC4_SCL_SHIFT	25
+#define RCWPMUXCR0_IIC4_SDA_SHIFT	29
+#define RCWPMUXCR0_FUNC_I2C		0
+#define RCWPMUXCR0_FUNC_GPIO		1
+#define RCWPMUXCR0_FUNC_EVT		2
+#define RCWPMUXCR0_FUNC_USB		3
+#define RCWPMUXCR0_FUNC_FTM		5
+#define RCWPMUXCR0_FUNC_CLK		6
+#define RCWPMUXCR0_FUNC_MASK		7
+
+#define PIN_L4 0 /* IIC3_SCL/GPIO_4[10]/EVT_B[5]/USB2_DRVVBUS/FTM8_CH0 */
+#define PIN_M4 1 /* IIC3_SDA/GPIO_4[11]/EVT_B[6]/USB2_PWRFAULT/FTM8_CH1 */
+#define PIN_M3 2 /* IIC4_SCL/GPIO_4[12]/EVT_B[7]/USB3_DRVVBUS/FTM3_FAULT */
+#define PIN_N3 3 /* IIC4_SDA/GPIO_4[13]/EVT_B[8]/USB3_PWRFAULT/FTM_EXT3CLK */
+
+const struct pinctrl_pin_desc ls1046a_pins[] = {
+	PINCTRL_PIN(PIN_L4, "L4"),
+	PINCTRL_PIN(PIN_M4, "M4"),
+	PINCTRL_PIN(PIN_M3, "M3"),
+	PINCTRL_PIN(PIN_N3, "N3"),
+};
+
+/* Each pin is its own group */
+static const char * const ls1046a_groups[] = { "L4", "M4", "M3", "N3" };
+
+static int ls1046a_get_groups_count(struct pinctrl_dev *pcdev)
+{
+	return ARRAY_SIZE(ls1046a_pins);
+}
+
+static const char *ls1046a_get_group_name(struct pinctrl_dev *pcdev,
+	unsigned int selector)
+{
+	return ls1046a_pins[selector].name;
+}
+
+static int ls1046a_get_group_pins(struct pinctrl_dev *pcdev,
+	unsigned int selector, const unsigned int **pins, unsigned int *npins)
+{
+	*pins = &ls1046a_pins[selector].number;
+	*npins = 1;
+	return 0;
+}
+
+static const struct pinctrl_ops ls1046a_pinctrl_ops = {
+	.get_groups_count = ls1046a_get_groups_count,
+	.get_group_name = ls1046a_get_group_name,
+	.get_group_pins = ls1046a_get_group_pins,
+	.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
+	.dt_free_map = pinconf_generic_dt_free_map,
+};
+
+/* Every pin has the same set of functions */
+#define FUNC_i2c	0
+#define FUNC_gpio	1
+#define FUNC_evt	2
+#define FUNC_usb	3
+#define FUNC_ftm	4
+
+#define _PINFUNC(name) \
+	[FUNC_##name] = PINCTRL_PINFUNCTION(#name, ls1046a_groups, ARRAY_SIZE(ls1046a_groups))
+static const struct pinfunction ls1046a_functions[] = {
+	_PINFUNC(i2c),
+	_PINFUNC(gpio),
+	_PINFUNC(evt),
+	_PINFUNC(usb),
+	_PINFUNC(ftm),
+};
+
+static int ls1046a_get_functions_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(ls1046a_functions);
+}
+
+static const char *ls1046a_get_function_name(struct pinctrl_dev *pctldev, unsigned int func)
+{
+	return ls1046a_functions[func].name;
+}
+
+static int ls1046a_get_function_groups(struct pinctrl_dev *pctldev, unsigned int func,
+	const char * const **groups,
+	unsigned int * const ngroups)
+{
+	*groups = ls1046a_functions[func].groups;
+	*ngroups = ls1046a_functions[func].ngroups;
+	return 0;
+}
+
+static int ls1046a_set_mux(struct pinctrl_dev *pcdev,
+	unsigned int func, unsigned int pin)
+{
+	struct ls1046a_pinctrl_pdata *pd = pinctrl_dev_get_drvdata(pcdev);
+	static const u32 cr0_reg_func[] = {
+		[FUNC_i2c] = RCWPMUXCR0_FUNC_I2C,
+		[FUNC_gpio] = RCWPMUXCR0_FUNC_GPIO,
+		[FUNC_evt] = RCWPMUXCR0_FUNC_EVT,
+		[FUNC_usb] = RCWPMUXCR0_FUNC_USB,
+		[FUNC_ftm] = RCWPMUXCR0_FUNC_FTM,
+	};
+	static const unsigned int cr0_pin_shift[] = {
+		[PIN_L4] = RCWPMUXCR0_IIC3_SCL_SHIFT,
+		[PIN_M4] = RCWPMUXCR0_IIC3_SDA_SHIFT,
+		[PIN_M3] = RCWPMUXCR0_IIC4_SCL_SHIFT,
+		[PIN_N3] = RCWPMUXCR0_IIC4_SDA_SHIFT,
+	};
+	u32 cr0;
+
+	if (pd->big_endian)
+		cr0 = ioread32be(pd->cr0mem);
+	else
+		cr0 = ioread32(pd->cr0mem);
+
+	unsigned int pin_shift = cr0_pin_shift[pin];
+	u32 reg_func = cr0_reg_func[func];
+	u32 newcr0 = (cr0 & ~RCWPMUXCR0_MASK(pin_shift)) |
+		RCWPMUXCR0_FIELD(pin_shift, reg_func);
+
+	if (pd->big_endian)
+		iowrite32be(newcr0, pd->cr0mem);
+	else
+		iowrite32(newcr0, pd->cr0mem);
+	return 0;
+}
+
+static const struct pinmux_ops ls1046a_pinmux_ops = {
+	.get_functions_count = ls1046a_get_functions_count,
+	.get_function_name = ls1046a_get_function_name,
+	.get_function_groups = ls1046a_get_function_groups,
+	.set_mux = ls1046a_set_mux,
+};
+
+static const struct pinctrl_desc ls1046a_pinctrl_desc = {
+	.name = "ls1046a",
+	.pins = ls1046a_pins,
+	.npins = ARRAY_SIZE(ls1046a_pins),
+	.pctlops = &ls1046a_pinctrl_ops,
+	.pmxops = &ls1046a_pinmux_ops,
+	.owner = THIS_MODULE,
+};
+
+static int ls1046a_pinctrl_probe(struct platform_device *pdev)
+{
+	struct ls1046a_pinctrl_pdata *pd;
+	int ret;
+
+	pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
+	if (!pd)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, pd);
+
+	pd->big_endian = device_is_big_endian(&pdev->dev);
+
+	/* SCFG PMUX0 */
+	pd->cr0mem = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(pd->cr0mem))
+		return PTR_ERR(pd->cr0mem);
+	dev_dbg(&pdev->dev, "scfg pmuxcr0 at %px %s", pd->cr0mem,
+		pd->big_endian ? "be" : "le");
+
+	ret = devm_pinctrl_register_and_init(&pdev->dev, &ls1046a_pinctrl_desc,
+		pd, &pd->pctl_dev);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret, "Failed pinctrl init\n");
+
+	pinctrl_enable(pd->pctl_dev);
+	return ret;
+}
+
+static const struct of_device_id ls1046a_pinctrl_match_table[] = {
+	{ .compatible = "fsl,ls1046a-pinctrl" },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver ls1046a_pinctrl_driver = {
+	.driver = {
+		.name = "ls1046a_pinctrl",
+		.of_match_table = ls1046a_pinctrl_match_table,
+	},
+	.probe = ls1046a_pinctrl_probe,
+};
+
+builtin_platform_driver(ls1046a_pinctrl_driver)
+
+MODULE_DESCRIPTION("LS1046A pinctrl driver");
+MODULE_LICENSE("GPL");