diff mbox series

[1/6] pinctrl: ls1012a: Add pinctrl driver support

Message ID 8cd0b743-4fff-e17f-9543-d2d4d7879758@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:05 a.m. UTC
Add QorIQ LS1012A pinctrl driver, allowing i2c-core to exert
GPIO control over the second I2C bus.

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

Comments

Alexander Stein Aug. 27, 2024, 6:32 a.m. UTC | #1
Am Dienstag, 27. August 2024, 04:05:24 CEST schrieb David Leonard:
> Add QorIQ LS1012A pinctrl driver, allowing i2c-core to exert
> GPIO control over the second I2C bus.

Can you please elaborate? AFAIK the pinmuxing is set by the RCW on layerscape.
How can you change the pinmuxing at runtime. How is this related to i2c
controlling GPIO?

Best regards,
Alexander

> Signed-off-by: David Leonard <David.Leonard@digi.com>
> ---
>   drivers/pinctrl/freescale/Kconfig           |   8 +
>   drivers/pinctrl/freescale/Makefile          |   1 +
>   drivers/pinctrl/freescale/pinctrl-ls1012a.c | 381 ++++++++++++++++++++
>   3 files changed, 390 insertions(+)
>   create mode 100644 drivers/pinctrl/freescale/pinctrl-ls1012a.c
> 
> diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig
> index 3b59d7189004..a2038042eeae 100644
> --- a/drivers/pinctrl/freescale/Kconfig
> +++ b/drivers/pinctrl/freescale/Kconfig
> @@ -209,6 +209,14 @@ config PINCTRL_IMX93
>   	help
>   	  Say Y here to enable the imx93 pinctrl driver
> 
> +config PINCTRL_LS1012A
> +	tristate "LS1012A pinctrl driver"
> +	depends on ARCH_LAYERSCAPE && OF || COMPILE_TEST
> +	select PINMUX
> +	select GENERIC_PINCONF
> +	help
> +	  Say Y here to enable the ls1012a 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 d27085c2b4c4..6926529d8635 100644
> --- a/drivers/pinctrl/freescale/Makefile
> +++ b/drivers/pinctrl/freescale/Makefile
> @@ -35,3 +35,4 @@ obj-$(CONFIG_PINCTRL_IMX25)	+= pinctrl-imx25.o
>   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
> diff --git a/drivers/pinctrl/freescale/pinctrl-ls1012a.c b/drivers/pinctrl/freescale/pinctrl-ls1012a.c
> new file mode 100644
> index 000000000000..d4c535ed6c07
> --- /dev/null
> +++ b/drivers/pinctrl/freescale/pinctrl-ls1012a.c
> @@ -0,0 +1,381 @@
> +// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
> +/*
> + * Pin controller for NXP QorIQ LS1012A.
> + *
> + * 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/regmap.h>
> +#include <linux/platform_device.h>
> +#include <linux/sys_soc.h>
> +
> +struct ls1012a_pinctrl_pdata {
> +	struct pinctrl_dev *pctl_dev;
> +	void __iomem *cr0mem;
> +	bool big_endian;
> +	u32 ssc;
> +};
> +
> +/* Bitfield macros for masks and values that follow the datasheet's
> + * bit numbering schemes for registers of different bit-endianess. */
> +#define BITV_BE(hi, v)	((v) << (31 - (hi) % 32))
> +#define BITM_BE(hi, lo)	(((1 << ((hi) - (lo) + 1)) - 1) << (31 - (hi) % 32))
> +#define BITV_LE(lo, v)	((v) << ((lo) % 32))
> +#define BITM_LE(lo, hi)	(((1 << ((hi) - (lo) + 1)) - 1) << ((lo) % 32))
> +
> +/* SCFG PMUXCR0 pinmux control register */
> +#define SCFG_PMUXCR0			0x430
> +#define QSPI_MUX_OVRD_MASK		BITM_BE(0, 0)	/* [0] */
> +#define QSPI_MUX_DISABLE		BITV_BE(0, 0)	/*  use RCW */
> +#define QSPI_MUX_ENABLE			BITV_BE(0, 1)	/*  use PMUXCR0 */
> +#define QSPI_DATA0_GPIO_OVR_MASK	BITM_BE(1, 1)	/* [1] */
> +#define QSPI_DATA0_GPIO_SEL_SPI		BITV_BE(1, 0)	/*  DATA0,SCK,CS0 */
> +#define QSPI_DATA0_GPIO_SEL_GPIO	BITV_BE(1, 1)	/*  GPIO1[4,11,5] */
> +#define QSPI_DATA1_GPIO_OVR_MASK	BITM_BE(3, 2)	/* [3:2] */
> +#define QSPI_DATA1_GPIO_SEL_SPI		BITV_BE(3, 0)	/*  DATA1 */
> +#define QSPI_DATA1_GPIO_SEL_GPIO	BITV_BE(3, 1)	/*  GPIO1[12] */
> +#define QSPI_IIC2_OVR_MASK		BITM_BE(5, 4)	/* [5:4] */
> +#define QSPI_IIC2_SEL_GPIO		BITV_BE(5, 0)	/*  GPIO1[13,14] */
> +#define QSPI_IIC2_SEL_I2C		BITV_BE(5, 1)	/*  SCL,SDA (rev0) */
> +#define QSPI_IIC2_SEL_SPI		BITV_BE(5, 2)	/*  DATA2,DATA3 */
> +
> +/* RCW SoC-specific configuration (read-only) */
> +#define DCFG_RCWSR			0x100
> +#define SOC_SPEC_CONFIG			416		/* word 13 */
> +#define DCFG_SSC_REG			(DCFG_RCWSR + SOC_SPEC_CONFIG / 8)
> +#define SSC_DATA0_GPIO_MASK		BITM_LE(421, 421)
> +#define SSC_DATA0_GPIO_SEL_SPI		BITV_LE(421, 0)	/*  DATA0,SCK,CS0 */
> +#define SSC_DATA0_GPIO_SEL_GPIO		BITV_LE(421, 1)	/*  GPIO1[11,4,5] */
> +#define SSC_DATA1_GPIO_MASK		BITM_LE(422, 423)
> +#define SSC_DATA1_GPIO_SEL_SPI		BITV_LE(422, 0)	/*  DATA1 */
> +#define SSC_DATA1_GPIO_SEL_GPIO		BITV_LE(422, 2)	/*  GPIO1[12] */
> +#define SSC_IIC2_MASK			BITM_LE(424, 425)
> +#define SSC_IIC2_SEL_GPIO		BITV_LE(424, 0)	/*  GPIO1[13,14] */
> +#define SSC_IIC2_SEL_I2C		BITV_LE(424, 2)	/*  SCL,SDA */
> +#define SSC_IIC2_SEL_SPI		BITV_LE(424, 1)	/*  DATA2,DATA3 */
> +#define SSC_IIC2_SEL_GPIO_RESET		BITV_LE(424, 3)	/*  GPIO1[13],RESET_REQ_B*/
> +
> +const struct pinctrl_pin_desc ls1012a_pins[] = {
> +	PINCTRL_PIN(60, "QSPI_A_DATA3/GPIO1_14/IIC2_SDA/RESET_REQ_B"),
> +	PINCTRL_PIN(61, "QSPI_A_DATA1/GPIO1_12"),
> +	PINCTRL_PIN(62, "QSPI_A_SCK/GPIO1_04"),
> +	PINCTRL_PIN(122, "QSPI_A_DATA2/GPIO1_13/IIC2_SCL"),
> +	PINCTRL_PIN(123, "QSPI_A_DATA0/GPIO1_11"),
> +	PINCTRL_PIN(124, "QSPI_A_CS0/GPIO1_05"),
> +};
> +
> +static const unsigned int qspi_1_grp[] = { 62, 123, 124 };
> +static const unsigned int qspi_2_grp[] = { 61 };
> +static const unsigned int qspi_3_grp[] = { 122, 60 };
> +
> +#define GRP_qspi_1	0	/* SCK,CS0,DATA0 */
> +#define GRP_qspi_2	1	/* DATA1 */
> +#define GRP_qspi_3	2	/* DATA2,DATA3 */
> +#define _GRP_max	3
> +
> +#define _PINGROUP(name) \
> +	[GRP_##name] = PINCTRL_PINGROUP(#name "_grp", name##_grp, ARRAY_SIZE(name##_grp))
> +static const struct pingroup ls1012a_groups[] = {
> +	_PINGROUP(qspi_1),
> +	_PINGROUP(qspi_2),
> +	_PINGROUP(qspi_3),
> +};
> +
> +
> +static void ls1012a_write_cr0(struct ls1012a_pinctrl_pdata *pd, u32 val)
> +{
> +	if (pd->big_endian)
> +		iowrite32be(val, pd->cr0mem);
> +	else
> +		iowrite32(val, pd->cr0mem);
> +}
> +
> +static u32 ls1012a_read_cr0(struct ls1012a_pinctrl_pdata *pd)
> +{
> +	return pd->big_endian ? ioread32be(pd->cr0mem) : ioread32(pd->cr0mem);
> +}
> +
> +static int ls1012a_get_groups_count(struct pinctrl_dev *pcdev)
> +{
> +	return ARRAY_SIZE(ls1012a_groups);
> +}
> +
> +static const char *ls1012a_get_group_name(struct pinctrl_dev *pcdev,
> +	unsigned int selector)
> +{
> +	return ls1012a_groups[selector].name;
> +}
> +
> +static int ls1012a_get_group_pins(struct pinctrl_dev *pcdev,
> +	unsigned int selector, const unsigned int **pins, unsigned int *npins)
> +{
> +	*pins = ls1012a_groups[selector].pins;
> +	*npins = ls1012a_groups[selector].npins;
> +	return 0;
> +}
> +
> +static const struct pinctrl_ops ls1012a_pinctrl_ops = {
> +	.get_groups_count = ls1012a_get_groups_count,
> +	.get_group_name = ls1012a_get_group_name,
> +	.get_group_pins = ls1012a_get_group_pins,
> +	.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
> +	.dt_free_map = pinconf_generic_dt_free_map,
> +};
> +
> +static const char * const i2c_groups[] = { "qspi_3_grp" };
> +static const char * const spi_groups[] = { "qspi_1_grp", "qspi_2_grp", "qspi_3_grp" };
> +static const char * const gpio_groups[] = { "qspi_1_grp", "qspi_2_grp", "qspi_3_grp" };
> +static const char * const gpio_reset_groups[] = { "qspi_3_grp" };
> +
> +#define FUNC_i2c	0
> +#define FUNC_spi	1
> +#define FUNC_gpio	2
> +#define FUNC_gpio_reset 3
> +#define _FUNC_max	4
> +
> +#define _PINFUNC(name) \
> +	[FUNC_##name] = PINCTRL_PINFUNCTION(#name, name##_groups, ARRAY_SIZE(name##_groups))
> +static const struct pinfunction ls1012a_functions[] = {
> +	_PINFUNC(i2c),
> +	_PINFUNC(spi),
> +	_PINFUNC(gpio),
> +	_PINFUNC(gpio_reset),
> +};
> +
> +static int ls1012a_get_functions_count(struct pinctrl_dev *pctldev)
> +{
> +	return ARRAY_SIZE(ls1012a_functions);
> +}
> +
> +static const char *ls1012a_get_function_name(struct pinctrl_dev *pctldev, unsigned int func)
> +{
> +	return ls1012a_functions[func].name;
> +}
> +
> +static int ls1012a_get_function_groups(struct pinctrl_dev *pctldev, unsigned int func,
> +	const char * const **groups,
> +	unsigned int * const ngroups)
> +{
> +	*groups = ls1012a_functions[func].groups;
> +	*ngroups = ls1012a_functions[func].ngroups;
> +	return 0;
> +}
> +
> +/*
> + * LS1012A
> + *    Group: qspi_1             qspi_2      qspi_3
> + *           ================== =========== =============
> + *    Pin:   62    123    124   61          122    60
> + *           ----- ------ ----- ----------- ------ ------
> + * i2c                                      SCL    SDA    (RCW only)
> + * spi       SCK   DATA0
> + * spi       SCK   DATA0        DATA1
> + * spi       SCK   DATA0        DATA1       DATA2  DATA3
> + * gpio      GPIO4 GPIO11 GPIO5
> + * gpio                         GPIO12
> + * gpio                                     GPIO13 GPIO14
> + * gpio_reset                               GPIO13 REQ_B  (RCW only)
> + */
> +
> +static const struct ls1012a_func_mux {
> +	u32 cr0mask, cr0; /* mux control */
> +	u32 sscmask, ssc; /* equivalent in RCW */
> +} ls1012a_func_mux[_FUNC_max][_GRP_max] = {
> +	[FUNC_i2c] = {
> +		[GRP_qspi_3] = {
> +			.sscmask = SSC_IIC2_MASK,
> +			.ssc =     SSC_IIC2_SEL_I2C,
> +		},
> +	},
> +	[FUNC_spi] = {
> +		[GRP_qspi_1] = {
> +			.cr0mask = QSPI_DATA0_GPIO_OVR_MASK,
> +			.cr0 =     QSPI_DATA0_GPIO_SEL_SPI,
> +			.sscmask = SSC_DATA0_GPIO_MASK,
> +			.ssc =     SSC_DATA0_GPIO_SEL_SPI
> +		},
> +		[GRP_qspi_2] = {
> +			.cr0mask = QSPI_DATA1_GPIO_OVR_MASK,
> +			.cr0 =     QSPI_DATA1_GPIO_SEL_SPI,
> +			.sscmask = SSC_DATA1_GPIO_MASK,
> +			.ssc =     SSC_DATA1_GPIO_SEL_SPI,
> +		},
> +		[GRP_qspi_3] = {
> +			.cr0mask = QSPI_IIC2_OVR_MASK,
> +			.cr0 =     QSPI_IIC2_SEL_SPI,
> +			.sscmask = SSC_IIC2_MASK,
> +			.ssc =     SSC_IIC2_SEL_SPI,
> +		},
> +	},
> +	[FUNC_gpio] = {
> +		[GRP_qspi_1] = {
> +			.cr0mask = QSPI_DATA0_GPIO_OVR_MASK,
> +			.cr0 =     QSPI_DATA0_GPIO_SEL_GPIO,
> +			.sscmask = SSC_DATA0_GPIO_MASK,
> +			.ssc =     SSC_DATA0_GPIO_SEL_GPIO,
> +		},
> +		[GRP_qspi_2] = {
> +			.cr0mask = QSPI_DATA1_GPIO_OVR_MASK,
> +			.cr0 =     QSPI_DATA1_GPIO_SEL_GPIO,
> +			.sscmask = SSC_DATA1_GPIO_MASK,
> +			.ssc =     SSC_DATA1_GPIO_SEL_GPIO,
> +		},
> +		[GRP_qspi_3] = {
> +			.cr0mask = QSPI_IIC2_OVR_MASK,
> +			.cr0 =     QSPI_IIC2_SEL_GPIO,
> +			.sscmask = SSC_IIC2_MASK,
> +			.ssc =     SSC_IIC2_SEL_GPIO,
> +		},
> +	},
> +	[FUNC_gpio_reset] = {
> +		[GRP_qspi_3] = {
> +			.sscmask = SSC_IIC2_MASK,
> +			.ssc =     SSC_IIC2_SEL_GPIO_RESET,
> +		},
> +	},
> +};
> +
> +static int ls1012a_set_mux(struct pinctrl_dev *pcdev,
> +	unsigned int func, unsigned int group)
> +{
> +	struct ls1012a_pinctrl_pdata *pd = pinctrl_dev_get_drvdata(pcdev);
> +	const struct ls1012a_func_mux *fm = &ls1012a_func_mux[func][group];
> +	u32 cr0 = ls1012a_read_cr0(pd);
> +
> +	if (!fm->cr0mask) {
> +		if ((pd->ssc & fm->sscmask) != fm->ssc)
> +			return -EOPNOTSUPP;
> +		cr0 = (cr0 & ~QSPI_MUX_OVRD_MASK) | QSPI_MUX_DISABLE;
> +	} else {
> +		cr0 = (cr0 & ~fm->cr0mask) | fm->cr0;
> +		if ((pd->ssc & fm->sscmask) != fm->ssc)
> +			cr0 = (cr0 & ~QSPI_MUX_OVRD_MASK) | QSPI_MUX_ENABLE;
> +	}
> +	ls1012a_write_cr0(pd, cr0);
> +	return 0;
> +}
> +
> +static void ls1012a_init_mux(struct ls1012a_pinctrl_pdata *pd)
> +{
> +	unsigned int func, group;
> +	const struct ls1012a_func_mux *fm;
> +	u32 cr0;
> +
> +	cr0 = ls1012a_read_cr0(pd);
> +	if ((cr0 & QSPI_MUX_OVRD_MASK) == QSPI_MUX_DISABLE) {
> +		/*
> +		 * Prepare a disabled MUXCR0 to have a same/similar
> +		 * configuration as RCW SSC, and leave it disabled.
> +		 */
> +		for (func = 0; func < _FUNC_max; func++) {
> +			for (group = 0; group < _GRP_max; group++) {
> +				fm = &ls1012a_func_mux[func][group];
> +				if (fm->sscmask &&
> +				    fm->ssc == (pd->ssc & fm->sscmask)) {
> +					cr0 &= ~fm->cr0mask;
> +					cr0 |= fm->cr0;
> +				}
> +			}
> +		}
> +		ls1012a_write_cr0(pd, cr0);
> +	}
> +}
> +
> +static const struct pinmux_ops ls1012a_pinmux_ops = {
> +	.get_functions_count = ls1012a_get_functions_count,
> +	.get_function_name = ls1012a_get_function_name,
> +	.get_function_groups = ls1012a_get_function_groups,
> +	.set_mux = ls1012a_set_mux,
> +};
> +
> +static struct pinctrl_desc ls1012a_pinctrl_desc = {
> +	.name = "ls1012a",
> +	.pins = ls1012a_pins,
> +	.npins = ARRAY_SIZE(ls1012a_pins),
> +	.pctlops = &ls1012a_pinctrl_ops,
> +	.pmxops = &ls1012a_pinmux_ops,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int ls1012a_pinctrl_probe(struct platform_device *pdev)
> +{
> +	struct ls1012a_pinctrl_pdata *pd;
> +	int ret;
> +	u32 dcfg_ssc;
> +	struct regmap *dcfg_regmap;
> +
> +	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");
> +
> +	/* DCFG RCW SSC */
> +	dcfg_regmap = syscon_regmap_lookup_by_phandle(
> +		dev_of_node(&pdev->dev), "dcfg-regmap");
> +	if (IS_ERR(dcfg_regmap)) {
> +		dev_err(&pdev->dev, "dcfg regmap: %pe\n", dcfg_regmap);
> +		return -EINVAL;
> +	}
> +	ret = regmap_read(dcfg_regmap, DCFG_SSC_REG, &dcfg_ssc);
> +	if (ret) {
> +		dev_err(&pdev->dev, "dcfg-regmap read: %d\n", ret);
> +		return ret;
> +	}
> +	pd->ssc = swab32(dcfg_ssc); /* untwist RCW fields */
> +
> +	dev_dbg(&pdev->dev, "dcfg ssc = %08x (grp1=%s grp2=%s grp3=%s)\n",
> +		pd->ssc,
> +		(pd->ssc & SSC_DATA0_GPIO_MASK) == SSC_DATA0_GPIO_SEL_SPI ? "spi" : "gpio",
> +		(pd->ssc & SSC_DATA1_GPIO_MASK) == SSC_DATA1_GPIO_SEL_SPI ? "spi"
> +		: (pd->ssc & SSC_DATA1_GPIO_MASK) == SSC_DATA1_GPIO_SEL_GPIO ? "gpio"
> +		: (pd->ssc & SSC_DATA1_GPIO_MASK) == 0x80 ? "10" : "11",
> +		(pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_GPIO ? "gpio"
> +		: (pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_I2C ? "i2c"
> +		: (pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_SPI ? "spi"
> +		: "gpio+reset");
> +
> +	ls1012a_init_mux(pd);
> +
> +	ret = devm_pinctrl_register_and_init(&pdev->dev, &ls1012a_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 ls1012a_pinctrl_match_table[] = {
> +	{ .compatible = "fsl,ls1012a-pinctrl" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver ls1012a_pinctrl_driver = {
> +	.driver = {
> +		.name = "ls1012a_pinctrl",
> +		.of_match_table = ls1012a_pinctrl_match_table,
> +	},
> +	.probe = ls1012a_pinctrl_probe,
> +};
> +
> +builtin_platform_driver(ls1012a_pinctrl_driver)
> +
> +MODULE_DESCRIPTION("LS1012A pinctrl driver");
> +MODULE_LICENSE("GPL");
>
David Leonard Aug. 27, 2024, 12:23 p.m. UTC | #2
On Tue, 27 Aug 2024, Alexander Stein wrote:
> Am Dienstag, 27. August 2024, 04:05:24 CEST schrieb David Leonard:
>> Add QorIQ LS1012A pinctrl driver, allowing i2c-core to exert
>> GPIO control over the second I2C bus.
>
> Can you please elaborate? AFAIK the pinmuxing is set by the RCW on layerscape.
> How can you change the pinmuxing at runtime. How is this related to i2c
> controlling GPIO?

Sure. Two of the Layerscape SoCs have pinmux control registers that
override/extend the RCW configuration string.

These pinmux registers are not consistent across the family, only a
very few pins are muxable, and they're all related to I2C.

i2c-core has a bus recovery feature that relies on pinctrl to switch
the I2C pins into GPIO mode.

Our motivating case is efficient use of the Atmel ATECCx08 crypto
processor. It has a peculiar requirement that SDA be held low for a
certain amount of time. That kind of control also needs the pins in
GPIO mode.

>> Signed-off-by: David Leonard <David.Leonard@digi.com>
>> ---
>>   drivers/pinctrl/freescale/Kconfig           |   8 +
>>   drivers/pinctrl/freescale/Makefile          |   1 +
>>   drivers/pinctrl/freescale/pinctrl-ls1012a.c | 381 ++++++++++++++++++++
>>   3 files changed, 390 insertions(+)
>>   create mode 100644 drivers/pinctrl/freescale/pinctrl-ls1012a.c
>>
>> diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig
>> index 3b59d7189004..a2038042eeae 100644
>> --- a/drivers/pinctrl/freescale/Kconfig
>> +++ b/drivers/pinctrl/freescale/Kconfig
>> @@ -209,6 +209,14 @@ config PINCTRL_IMX93
>>       help
>>         Say Y here to enable the imx93 pinctrl driver
>>
>> +config PINCTRL_LS1012A
>> +     tristate "LS1012A pinctrl driver"
>> +     depends on ARCH_LAYERSCAPE && OF || COMPILE_TEST
>> +     select PINMUX
>> +     select GENERIC_PINCONF
>> +     help
>> +       Say Y here to enable the ls1012a 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 d27085c2b4c4..6926529d8635 100644
>> --- a/drivers/pinctrl/freescale/Makefile
>> +++ b/drivers/pinctrl/freescale/Makefile
>> @@ -35,3 +35,4 @@ obj-$(CONFIG_PINCTRL_IMX25) += pinctrl-imx25.o
>>   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
>> diff --git a/drivers/pinctrl/freescale/pinctrl-ls1012a.c b/drivers/pinctrl/freescale/pinctrl-ls1012a.c
>> new file mode 100644
>> index 000000000000..d4c535ed6c07
>> --- /dev/null
>> +++ b/drivers/pinctrl/freescale/pinctrl-ls1012a.c
>> @@ -0,0 +1,381 @@
>> +// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
>> +/*
>> + * Pin controller for NXP QorIQ LS1012A.
>> + *
>> + * 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/regmap.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/sys_soc.h>
>> +
>> +struct ls1012a_pinctrl_pdata {
>> +     struct pinctrl_dev *pctl_dev;
>> +     void __iomem *cr0mem;
>> +     bool big_endian;
>> +     u32 ssc;
>> +};
>> +
>> +/* Bitfield macros for masks and values that follow the datasheet's
>> + * bit numbering schemes for registers of different bit-endianess. */
>> +#define BITV_BE(hi, v)       ((v) << (31 - (hi) % 32))
>> +#define BITM_BE(hi, lo)      (((1 << ((hi) - (lo) + 1)) - 1) << (31 - (hi) % 32))
>> +#define BITV_LE(lo, v)       ((v) << ((lo) % 32))
>> +#define BITM_LE(lo, hi)      (((1 << ((hi) - (lo) + 1)) - 1) << ((lo) % 32))
>> +
>> +/* SCFG PMUXCR0 pinmux control register */
>> +#define SCFG_PMUXCR0                 0x430
>> +#define QSPI_MUX_OVRD_MASK           BITM_BE(0, 0)   /* [0] */
>> +#define QSPI_MUX_DISABLE             BITV_BE(0, 0)   /*  use RCW */
>> +#define QSPI_MUX_ENABLE                      BITV_BE(0, 1)   /*  use PMUXCR0 */
>> +#define QSPI_DATA0_GPIO_OVR_MASK     BITM_BE(1, 1)   /* [1] */
>> +#define QSPI_DATA0_GPIO_SEL_SPI              BITV_BE(1, 0)   /*  DATA0,SCK,CS0 */
>> +#define QSPI_DATA0_GPIO_SEL_GPIO     BITV_BE(1, 1)   /*  GPIO1[4,11,5] */
>> +#define QSPI_DATA1_GPIO_OVR_MASK     BITM_BE(3, 2)   /* [3:2] */
>> +#define QSPI_DATA1_GPIO_SEL_SPI              BITV_BE(3, 0)   /*  DATA1 */
>> +#define QSPI_DATA1_GPIO_SEL_GPIO     BITV_BE(3, 1)   /*  GPIO1[12] */
>> +#define QSPI_IIC2_OVR_MASK           BITM_BE(5, 4)   /* [5:4] */
>> +#define QSPI_IIC2_SEL_GPIO           BITV_BE(5, 0)   /*  GPIO1[13,14] */
>> +#define QSPI_IIC2_SEL_I2C            BITV_BE(5, 1)   /*  SCL,SDA (rev0) */
>> +#define QSPI_IIC2_SEL_SPI            BITV_BE(5, 2)   /*  DATA2,DATA3 */
>> +
>> +/* RCW SoC-specific configuration (read-only) */
>> +#define DCFG_RCWSR                   0x100
>> +#define SOC_SPEC_CONFIG                      416             /* word 13 */
>> +#define DCFG_SSC_REG                 (DCFG_RCWSR + SOC_SPEC_CONFIG / 8)
>> +#define SSC_DATA0_GPIO_MASK          BITM_LE(421, 421)
>> +#define SSC_DATA0_GPIO_SEL_SPI               BITV_LE(421, 0) /*  DATA0,SCK,CS0 */
>> +#define SSC_DATA0_GPIO_SEL_GPIO              BITV_LE(421, 1) /*  GPIO1[11,4,5] */
>> +#define SSC_DATA1_GPIO_MASK          BITM_LE(422, 423)
>> +#define SSC_DATA1_GPIO_SEL_SPI               BITV_LE(422, 0) /*  DATA1 */
>> +#define SSC_DATA1_GPIO_SEL_GPIO              BITV_LE(422, 2) /*  GPIO1[12] */
>> +#define SSC_IIC2_MASK                        BITM_LE(424, 425)
>> +#define SSC_IIC2_SEL_GPIO            BITV_LE(424, 0) /*  GPIO1[13,14] */
>> +#define SSC_IIC2_SEL_I2C             BITV_LE(424, 2) /*  SCL,SDA */
>> +#define SSC_IIC2_SEL_SPI             BITV_LE(424, 1) /*  DATA2,DATA3 */
>> +#define SSC_IIC2_SEL_GPIO_RESET              BITV_LE(424, 3) /*  GPIO1[13],RESET_REQ_B*/
>> +
>> +const struct pinctrl_pin_desc ls1012a_pins[] = {
>> +     PINCTRL_PIN(60, "QSPI_A_DATA3/GPIO1_14/IIC2_SDA/RESET_REQ_B"),
>> +     PINCTRL_PIN(61, "QSPI_A_DATA1/GPIO1_12"),
>> +     PINCTRL_PIN(62, "QSPI_A_SCK/GPIO1_04"),
>> +     PINCTRL_PIN(122, "QSPI_A_DATA2/GPIO1_13/IIC2_SCL"),
>> +     PINCTRL_PIN(123, "QSPI_A_DATA0/GPIO1_11"),
>> +     PINCTRL_PIN(124, "QSPI_A_CS0/GPIO1_05"),
>> +};
>> +
>> +static const unsigned int qspi_1_grp[] = { 62, 123, 124 };
>> +static const unsigned int qspi_2_grp[] = { 61 };
>> +static const unsigned int qspi_3_grp[] = { 122, 60 };
>> +
>> +#define GRP_qspi_1   0       /* SCK,CS0,DATA0 */
>> +#define GRP_qspi_2   1       /* DATA1 */
>> +#define GRP_qspi_3   2       /* DATA2,DATA3 */
>> +#define _GRP_max     3
>> +
>> +#define _PINGROUP(name) \
>> +     [GRP_##name] = PINCTRL_PINGROUP(#name "_grp", name##_grp, ARRAY_SIZE(name##_grp))
>> +static const struct pingroup ls1012a_groups[] = {
>> +     _PINGROUP(qspi_1),
>> +     _PINGROUP(qspi_2),
>> +     _PINGROUP(qspi_3),
>> +};
>> +
>> +
>> +static void ls1012a_write_cr0(struct ls1012a_pinctrl_pdata *pd, u32 val)
>> +{
>> +     if (pd->big_endian)
>> +             iowrite32be(val, pd->cr0mem);
>> +     else
>> +             iowrite32(val, pd->cr0mem);
>> +}
>> +
>> +static u32 ls1012a_read_cr0(struct ls1012a_pinctrl_pdata *pd)
>> +{
>> +     return pd->big_endian ? ioread32be(pd->cr0mem) : ioread32(pd->cr0mem);
>> +}
>> +
>> +static int ls1012a_get_groups_count(struct pinctrl_dev *pcdev)
>> +{
>> +     return ARRAY_SIZE(ls1012a_groups);
>> +}
>> +
>> +static const char *ls1012a_get_group_name(struct pinctrl_dev *pcdev,
>> +     unsigned int selector)
>> +{
>> +     return ls1012a_groups[selector].name;
>> +}
>> +
>> +static int ls1012a_get_group_pins(struct pinctrl_dev *pcdev,
>> +     unsigned int selector, const unsigned int **pins, unsigned int *npins)
>> +{
>> +     *pins = ls1012a_groups[selector].pins;
>> +     *npins = ls1012a_groups[selector].npins;
>> +     return 0;
>> +}
>> +
>> +static const struct pinctrl_ops ls1012a_pinctrl_ops = {
>> +     .get_groups_count = ls1012a_get_groups_count,
>> +     .get_group_name = ls1012a_get_group_name,
>> +     .get_group_pins = ls1012a_get_group_pins,
>> +     .dt_node_to_map = pinconf_generic_dt_node_to_map_group,
>> +     .dt_free_map = pinconf_generic_dt_free_map,
>> +};
>> +
>> +static const char * const i2c_groups[] = { "qspi_3_grp" };
>> +static const char * const spi_groups[] = { "qspi_1_grp", "qspi_2_grp", "qspi_3_grp" };
>> +static const char * const gpio_groups[] = { "qspi_1_grp", "qspi_2_grp", "qspi_3_grp" };
>> +static const char * const gpio_reset_groups[] = { "qspi_3_grp" };
>> +
>> +#define FUNC_i2c     0
>> +#define FUNC_spi     1
>> +#define FUNC_gpio    2
>> +#define FUNC_gpio_reset 3
>> +#define _FUNC_max    4
>> +
>> +#define _PINFUNC(name) \
>> +     [FUNC_##name] = PINCTRL_PINFUNCTION(#name, name##_groups, ARRAY_SIZE(name##_groups))
>> +static const struct pinfunction ls1012a_functions[] = {
>> +     _PINFUNC(i2c),
>> +     _PINFUNC(spi),
>> +     _PINFUNC(gpio),
>> +     _PINFUNC(gpio_reset),
>> +};
>> +
>> +static int ls1012a_get_functions_count(struct pinctrl_dev *pctldev)
>> +{
>> +     return ARRAY_SIZE(ls1012a_functions);
>> +}
>> +
>> +static const char *ls1012a_get_function_name(struct pinctrl_dev *pctldev, unsigned int func)
>> +{
>> +     return ls1012a_functions[func].name;
>> +}
>> +
>> +static int ls1012a_get_function_groups(struct pinctrl_dev *pctldev, unsigned int func,
>> +     const char * const **groups,
>> +     unsigned int * const ngroups)
>> +{
>> +     *groups = ls1012a_functions[func].groups;
>> +     *ngroups = ls1012a_functions[func].ngroups;
>> +     return 0;
>> +}
>> +
>> +/*
>> + * LS1012A
>> + *    Group: qspi_1             qspi_2      qspi_3
>> + *           ================== =========== =============
>> + *    Pin:   62    123    124   61          122    60
>> + *           ----- ------ ----- ----------- ------ ------
>> + * i2c                                      SCL    SDA    (RCW only)
>> + * spi       SCK   DATA0
>> + * spi       SCK   DATA0        DATA1
>> + * spi       SCK   DATA0        DATA1       DATA2  DATA3
>> + * gpio      GPIO4 GPIO11 GPIO5
>> + * gpio                         GPIO12
>> + * gpio                                     GPIO13 GPIO14
>> + * gpio_reset                               GPIO13 REQ_B  (RCW only)
>> + */
>> +
>> +static const struct ls1012a_func_mux {
>> +     u32 cr0mask, cr0; /* mux control */
>> +     u32 sscmask, ssc; /* equivalent in RCW */
>> +} ls1012a_func_mux[_FUNC_max][_GRP_max] = {
>> +     [FUNC_i2c] = {
>> +             [GRP_qspi_3] = {
>> +                     .sscmask = SSC_IIC2_MASK,
>> +                     .ssc =     SSC_IIC2_SEL_I2C,
>> +             },
>> +     },
>> +     [FUNC_spi] = {
>> +             [GRP_qspi_1] = {
>> +                     .cr0mask = QSPI_DATA0_GPIO_OVR_MASK,
>> +                     .cr0 =     QSPI_DATA0_GPIO_SEL_SPI,
>> +                     .sscmask = SSC_DATA0_GPIO_MASK,
>> +                     .ssc =     SSC_DATA0_GPIO_SEL_SPI
>> +             },
>> +             [GRP_qspi_2] = {
>> +                     .cr0mask = QSPI_DATA1_GPIO_OVR_MASK,
>> +                     .cr0 =     QSPI_DATA1_GPIO_SEL_SPI,
>> +                     .sscmask = SSC_DATA1_GPIO_MASK,
>> +                     .ssc =     SSC_DATA1_GPIO_SEL_SPI,
>> +             },
>> +             [GRP_qspi_3] = {
>> +                     .cr0mask = QSPI_IIC2_OVR_MASK,
>> +                     .cr0 =     QSPI_IIC2_SEL_SPI,
>> +                     .sscmask = SSC_IIC2_MASK,
>> +                     .ssc =     SSC_IIC2_SEL_SPI,
>> +             },
>> +     },
>> +     [FUNC_gpio] = {
>> +             [GRP_qspi_1] = {
>> +                     .cr0mask = QSPI_DATA0_GPIO_OVR_MASK,
>> +                     .cr0 =     QSPI_DATA0_GPIO_SEL_GPIO,
>> +                     .sscmask = SSC_DATA0_GPIO_MASK,
>> +                     .ssc =     SSC_DATA0_GPIO_SEL_GPIO,
>> +             },
>> +             [GRP_qspi_2] = {
>> +                     .cr0mask = QSPI_DATA1_GPIO_OVR_MASK,
>> +                     .cr0 =     QSPI_DATA1_GPIO_SEL_GPIO,
>> +                     .sscmask = SSC_DATA1_GPIO_MASK,
>> +                     .ssc =     SSC_DATA1_GPIO_SEL_GPIO,
>> +             },
>> +             [GRP_qspi_3] = {
>> +                     .cr0mask = QSPI_IIC2_OVR_MASK,
>> +                     .cr0 =     QSPI_IIC2_SEL_GPIO,
>> +                     .sscmask = SSC_IIC2_MASK,
>> +                     .ssc =     SSC_IIC2_SEL_GPIO,
>> +             },
>> +     },
>> +     [FUNC_gpio_reset] = {
>> +             [GRP_qspi_3] = {
>> +                     .sscmask = SSC_IIC2_MASK,
>> +                     .ssc =     SSC_IIC2_SEL_GPIO_RESET,
>> +             },
>> +     },
>> +};
>> +
>> +static int ls1012a_set_mux(struct pinctrl_dev *pcdev,
>> +     unsigned int func, unsigned int group)
>> +{
>> +     struct ls1012a_pinctrl_pdata *pd = pinctrl_dev_get_drvdata(pcdev);
>> +     const struct ls1012a_func_mux *fm = &ls1012a_func_mux[func][group];
>> +     u32 cr0 = ls1012a_read_cr0(pd);
>> +
>> +     if (!fm->cr0mask) {
>> +             if ((pd->ssc & fm->sscmask) != fm->ssc)
>> +                     return -EOPNOTSUPP;
>> +             cr0 = (cr0 & ~QSPI_MUX_OVRD_MASK) | QSPI_MUX_DISABLE;
>> +     } else {
>> +             cr0 = (cr0 & ~fm->cr0mask) | fm->cr0;
>> +             if ((pd->ssc & fm->sscmask) != fm->ssc)
>> +                     cr0 = (cr0 & ~QSPI_MUX_OVRD_MASK) | QSPI_MUX_ENABLE;
>> +     }
>> +     ls1012a_write_cr0(pd, cr0);
>> +     return 0;
>> +}
>> +
>> +static void ls1012a_init_mux(struct ls1012a_pinctrl_pdata *pd)
>> +{
>> +     unsigned int func, group;
>> +     const struct ls1012a_func_mux *fm;
>> +     u32 cr0;
>> +
>> +     cr0 = ls1012a_read_cr0(pd);
>> +     if ((cr0 & QSPI_MUX_OVRD_MASK) == QSPI_MUX_DISABLE) {
>> +             /*
>> +              * Prepare a disabled MUXCR0 to have a same/similar
>> +              * configuration as RCW SSC, and leave it disabled.
>> +              */
>> +             for (func = 0; func < _FUNC_max; func++) {
>> +                     for (group = 0; group < _GRP_max; group++) {
>> +                             fm = &ls1012a_func_mux[func][group];
>> +                             if (fm->sscmask &&
>> +                                 fm->ssc == (pd->ssc & fm->sscmask)) {
>> +                                     cr0 &= ~fm->cr0mask;
>> +                                     cr0 |= fm->cr0;
>> +                             }
>> +                     }
>> +             }
>> +             ls1012a_write_cr0(pd, cr0);
>> +     }
>> +}
>> +
>> +static const struct pinmux_ops ls1012a_pinmux_ops = {
>> +     .get_functions_count = ls1012a_get_functions_count,
>> +     .get_function_name = ls1012a_get_function_name,
>> +     .get_function_groups = ls1012a_get_function_groups,
>> +     .set_mux = ls1012a_set_mux,
>> +};
>> +
>> +static struct pinctrl_desc ls1012a_pinctrl_desc = {
>> +     .name = "ls1012a",
>> +     .pins = ls1012a_pins,
>> +     .npins = ARRAY_SIZE(ls1012a_pins),
>> +     .pctlops = &ls1012a_pinctrl_ops,
>> +     .pmxops = &ls1012a_pinmux_ops,
>> +     .owner = THIS_MODULE,
>> +};
>> +
>> +static int ls1012a_pinctrl_probe(struct platform_device *pdev)
>> +{
>> +     struct ls1012a_pinctrl_pdata *pd;
>> +     int ret;
>> +     u32 dcfg_ssc;
>> +     struct regmap *dcfg_regmap;
>> +
>> +     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");
>> +
>> +     /* DCFG RCW SSC */
>> +     dcfg_regmap = syscon_regmap_lookup_by_phandle(
>> +             dev_of_node(&pdev->dev), "dcfg-regmap");
>> +     if (IS_ERR(dcfg_regmap)) {
>> +             dev_err(&pdev->dev, "dcfg regmap: %pe\n", dcfg_regmap);
>> +             return -EINVAL;
>> +     }
>> +     ret = regmap_read(dcfg_regmap, DCFG_SSC_REG, &dcfg_ssc);
>> +     if (ret) {
>> +             dev_err(&pdev->dev, "dcfg-regmap read: %d\n", ret);
>> +             return ret;
>> +     }
>> +     pd->ssc = swab32(dcfg_ssc); /* untwist RCW fields */
>> +
>> +     dev_dbg(&pdev->dev, "dcfg ssc = %08x (grp1=%s grp2=%s grp3=%s)\n",
>> +             pd->ssc,
>> +             (pd->ssc & SSC_DATA0_GPIO_MASK) == SSC_DATA0_GPIO_SEL_SPI ? "spi" : "gpio",
>> +             (pd->ssc & SSC_DATA1_GPIO_MASK) == SSC_DATA1_GPIO_SEL_SPI ? "spi"
>> +             : (pd->ssc & SSC_DATA1_GPIO_MASK) == SSC_DATA1_GPIO_SEL_GPIO ? "gpio"
>> +             : (pd->ssc & SSC_DATA1_GPIO_MASK) == 0x80 ? "10" : "11",
>> +             (pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_GPIO ? "gpio"
>> +             : (pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_I2C ? "i2c"
>> +             : (pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_SPI ? "spi"
>> +             : "gpio+reset");
>> +
>> +     ls1012a_init_mux(pd);
>> +
>> +     ret = devm_pinctrl_register_and_init(&pdev->dev, &ls1012a_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 ls1012a_pinctrl_match_table[] = {
>> +     { .compatible = "fsl,ls1012a-pinctrl" },
>> +     { /* sentinel */ }
>> +};
>> +
>> +static struct platform_driver ls1012a_pinctrl_driver = {
>> +     .driver = {
>> +             .name = "ls1012a_pinctrl",
>> +             .of_match_table = ls1012a_pinctrl_match_table,
>> +     },
>> +     .probe = ls1012a_pinctrl_probe,
>> +};
>> +
>> +builtin_platform_driver(ls1012a_pinctrl_driver)
>> +
>> +MODULE_DESCRIPTION("LS1012A pinctrl driver");
>> +MODULE_LICENSE("GPL");
>>
>
>
> --
> TQ-Systems GmbH | Mühlstraße 2, Gut Delling | 82229 Seefeld, Germany
> Amtsgericht München, HRB 105018
> Geschäftsführer: Detlef Schneider, Rüdiger Stahl, Stefan Schneider
> https://linkprotect.cudasvc.com/url?a=http%3a%2f%2fwww.tq-group.com%2f&c=E,1,cVMx__vg5fEF9T8hc39kPIKQSP2rTuv_7tLSPVEOmXyD87IrvYM0OP8NYwIfDnnSUGCAVzkTBOPCUCOCeZryyztWlYAicjWaEwH2S14xgFbq58j7P7TreOLl&typo=1
>
>
>
Frank Li Aug. 27, 2024, 6:12 p.m. UTC | #3
On Tue, Aug 27, 2024 at 12:05:24PM +1000, David Leonard wrote:
>
> Add QorIQ LS1012A pinctrl driver, allowing i2c-core to exert
> GPIO control over the second I2C bus.
>
> Signed-off-by: David Leonard <David.Leonard@digi.com>
> ---

Why not use pinctrl-single ?

You can ref arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi

It did similar thing to use GPIO recover i2c bus.

Just need change dts file.

Frank


>  drivers/pinctrl/freescale/Kconfig           |   8 +
>  drivers/pinctrl/freescale/Makefile          |   1 +
>  drivers/pinctrl/freescale/pinctrl-ls1012a.c | 381 ++++++++++++++++++++
>  3 files changed, 390 insertions(+)
>  create mode 100644 drivers/pinctrl/freescale/pinctrl-ls1012a.c
>
> diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig
> index 3b59d7189004..a2038042eeae 100644
> --- a/drivers/pinctrl/freescale/Kconfig
> +++ b/drivers/pinctrl/freescale/Kconfig
> @@ -209,6 +209,14 @@ config PINCTRL_IMX93
>  	help
>  	  Say Y here to enable the imx93 pinctrl driver
>
> +config PINCTRL_LS1012A
> +	tristate "LS1012A pinctrl driver"
> +	depends on ARCH_LAYERSCAPE && OF || COMPILE_TEST
> +	select PINMUX
> +	select GENERIC_PINCONF
> +	help
> +	  Say Y here to enable the ls1012a 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 d27085c2b4c4..6926529d8635 100644
> --- a/drivers/pinctrl/freescale/Makefile
> +++ b/drivers/pinctrl/freescale/Makefile
> @@ -35,3 +35,4 @@ obj-$(CONFIG_PINCTRL_IMX25)	+= pinctrl-imx25.o
>  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
> diff --git a/drivers/pinctrl/freescale/pinctrl-ls1012a.c b/drivers/pinctrl/freescale/pinctrl-ls1012a.c
> new file mode 100644
> index 000000000000..d4c535ed6c07
> --- /dev/null
> +++ b/drivers/pinctrl/freescale/pinctrl-ls1012a.c
> @@ -0,0 +1,381 @@
> +// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
> +/*
> + * Pin controller for NXP QorIQ LS1012A.
> + *
> + * 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/regmap.h>
> +#include <linux/platform_device.h>
> +#include <linux/sys_soc.h>
> +
> +struct ls1012a_pinctrl_pdata {
> +	struct pinctrl_dev *pctl_dev;
> +	void __iomem *cr0mem;
> +	bool big_endian;
> +	u32 ssc;
> +};
> +
> +/* Bitfield macros for masks and values that follow the datasheet's
> + * bit numbering schemes for registers of different bit-endianess. */
> +#define BITV_BE(hi, v)	((v) << (31 - (hi) % 32))
> +#define BITM_BE(hi, lo)	(((1 << ((hi) - (lo) + 1)) - 1) << (31 - (hi) % 32))
> +#define BITV_LE(lo, v)	((v) << ((lo) % 32))
> +#define BITM_LE(lo, hi)	(((1 << ((hi) - (lo) + 1)) - 1) << ((lo) % 32))
> +
> +/* SCFG PMUXCR0 pinmux control register */
> +#define SCFG_PMUXCR0			0x430
> +#define QSPI_MUX_OVRD_MASK		BITM_BE(0, 0)	/* [0] */
> +#define QSPI_MUX_DISABLE		BITV_BE(0, 0)	/*  use RCW */
> +#define QSPI_MUX_ENABLE			BITV_BE(0, 1)	/*  use PMUXCR0 */
> +#define QSPI_DATA0_GPIO_OVR_MASK	BITM_BE(1, 1)	/* [1] */
> +#define QSPI_DATA0_GPIO_SEL_SPI		BITV_BE(1, 0)	/*  DATA0,SCK,CS0 */
> +#define QSPI_DATA0_GPIO_SEL_GPIO	BITV_BE(1, 1)	/*  GPIO1[4,11,5] */
> +#define QSPI_DATA1_GPIO_OVR_MASK	BITM_BE(3, 2)	/* [3:2] */
> +#define QSPI_DATA1_GPIO_SEL_SPI		BITV_BE(3, 0)	/*  DATA1 */
> +#define QSPI_DATA1_GPIO_SEL_GPIO	BITV_BE(3, 1)	/*  GPIO1[12] */
> +#define QSPI_IIC2_OVR_MASK		BITM_BE(5, 4)	/* [5:4] */
> +#define QSPI_IIC2_SEL_GPIO		BITV_BE(5, 0)	/*  GPIO1[13,14] */
> +#define QSPI_IIC2_SEL_I2C		BITV_BE(5, 1)	/*  SCL,SDA (rev0) */
> +#define QSPI_IIC2_SEL_SPI		BITV_BE(5, 2)	/*  DATA2,DATA3 */
> +
> +/* RCW SoC-specific configuration (read-only) */
> +#define DCFG_RCWSR			0x100
> +#define SOC_SPEC_CONFIG			416		/* word 13 */
> +#define DCFG_SSC_REG			(DCFG_RCWSR + SOC_SPEC_CONFIG / 8)
> +#define SSC_DATA0_GPIO_MASK		BITM_LE(421, 421)
> +#define SSC_DATA0_GPIO_SEL_SPI		BITV_LE(421, 0)	/*  DATA0,SCK,CS0 */
> +#define SSC_DATA0_GPIO_SEL_GPIO		BITV_LE(421, 1)	/*  GPIO1[11,4,5] */
> +#define SSC_DATA1_GPIO_MASK		BITM_LE(422, 423)
> +#define SSC_DATA1_GPIO_SEL_SPI		BITV_LE(422, 0)	/*  DATA1 */
> +#define SSC_DATA1_GPIO_SEL_GPIO		BITV_LE(422, 2)	/*  GPIO1[12] */
> +#define SSC_IIC2_MASK			BITM_LE(424, 425)
> +#define SSC_IIC2_SEL_GPIO		BITV_LE(424, 0)	/*  GPIO1[13,14] */
> +#define SSC_IIC2_SEL_I2C		BITV_LE(424, 2)	/*  SCL,SDA */
> +#define SSC_IIC2_SEL_SPI		BITV_LE(424, 1)	/*  DATA2,DATA3 */
> +#define SSC_IIC2_SEL_GPIO_RESET		BITV_LE(424, 3)	/*  GPIO1[13],RESET_REQ_B*/
> +
> +const struct pinctrl_pin_desc ls1012a_pins[] = {
> +	PINCTRL_PIN(60, "QSPI_A_DATA3/GPIO1_14/IIC2_SDA/RESET_REQ_B"),
> +	PINCTRL_PIN(61, "QSPI_A_DATA1/GPIO1_12"),
> +	PINCTRL_PIN(62, "QSPI_A_SCK/GPIO1_04"),
> +	PINCTRL_PIN(122, "QSPI_A_DATA2/GPIO1_13/IIC2_SCL"),
> +	PINCTRL_PIN(123, "QSPI_A_DATA0/GPIO1_11"),
> +	PINCTRL_PIN(124, "QSPI_A_CS0/GPIO1_05"),
> +};
> +
> +static const unsigned int qspi_1_grp[] = { 62, 123, 124 };
> +static const unsigned int qspi_2_grp[] = { 61 };
> +static const unsigned int qspi_3_grp[] = { 122, 60 };
> +
> +#define GRP_qspi_1	0	/* SCK,CS0,DATA0 */
> +#define GRP_qspi_2	1	/* DATA1 */
> +#define GRP_qspi_3	2	/* DATA2,DATA3 */
> +#define _GRP_max	3
> +
> +#define _PINGROUP(name) \
> +	[GRP_##name] = PINCTRL_PINGROUP(#name "_grp", name##_grp, ARRAY_SIZE(name##_grp))
> +static const struct pingroup ls1012a_groups[] = {
> +	_PINGROUP(qspi_1),
> +	_PINGROUP(qspi_2),
> +	_PINGROUP(qspi_3),
> +};
> +
> +
> +static void ls1012a_write_cr0(struct ls1012a_pinctrl_pdata *pd, u32 val)
> +{
> +	if (pd->big_endian)
> +		iowrite32be(val, pd->cr0mem);
> +	else
> +		iowrite32(val, pd->cr0mem);
> +}
> +
> +static u32 ls1012a_read_cr0(struct ls1012a_pinctrl_pdata *pd)
> +{
> +	return pd->big_endian ? ioread32be(pd->cr0mem) : ioread32(pd->cr0mem);
> +}
> +
> +static int ls1012a_get_groups_count(struct pinctrl_dev *pcdev)
> +{
> +	return ARRAY_SIZE(ls1012a_groups);
> +}
> +
> +static const char *ls1012a_get_group_name(struct pinctrl_dev *pcdev,
> +	unsigned int selector)
> +{
> +	return ls1012a_groups[selector].name;
> +}
> +
> +static int ls1012a_get_group_pins(struct pinctrl_dev *pcdev,
> +	unsigned int selector, const unsigned int **pins, unsigned int *npins)
> +{
> +	*pins = ls1012a_groups[selector].pins;
> +	*npins = ls1012a_groups[selector].npins;
> +	return 0;
> +}
> +
> +static const struct pinctrl_ops ls1012a_pinctrl_ops = {
> +	.get_groups_count = ls1012a_get_groups_count,
> +	.get_group_name = ls1012a_get_group_name,
> +	.get_group_pins = ls1012a_get_group_pins,
> +	.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
> +	.dt_free_map = pinconf_generic_dt_free_map,
> +};
> +
> +static const char * const i2c_groups[] = { "qspi_3_grp" };
> +static const char * const spi_groups[] = { "qspi_1_grp", "qspi_2_grp", "qspi_3_grp" };
> +static const char * const gpio_groups[] = { "qspi_1_grp", "qspi_2_grp", "qspi_3_grp" };
> +static const char * const gpio_reset_groups[] = { "qspi_3_grp" };
> +
> +#define FUNC_i2c	0
> +#define FUNC_spi	1
> +#define FUNC_gpio	2
> +#define FUNC_gpio_reset 3
> +#define _FUNC_max	4
> +
> +#define _PINFUNC(name) \
> +	[FUNC_##name] = PINCTRL_PINFUNCTION(#name, name##_groups, ARRAY_SIZE(name##_groups))
> +static const struct pinfunction ls1012a_functions[] = {
> +	_PINFUNC(i2c),
> +	_PINFUNC(spi),
> +	_PINFUNC(gpio),
> +	_PINFUNC(gpio_reset),
> +};
> +
> +static int ls1012a_get_functions_count(struct pinctrl_dev *pctldev)
> +{
> +	return ARRAY_SIZE(ls1012a_functions);
> +}
> +
> +static const char *ls1012a_get_function_name(struct pinctrl_dev *pctldev, unsigned int func)
> +{
> +	return ls1012a_functions[func].name;
> +}
> +
> +static int ls1012a_get_function_groups(struct pinctrl_dev *pctldev, unsigned int func,
> +	const char * const **groups,
> +	unsigned int * const ngroups)
> +{
> +	*groups = ls1012a_functions[func].groups;
> +	*ngroups = ls1012a_functions[func].ngroups;
> +	return 0;
> +}
> +
> +/*
> + * LS1012A
> + *    Group: qspi_1             qspi_2      qspi_3
> + *           ================== =========== =============
> + *    Pin:   62    123    124   61          122    60
> + *           ----- ------ ----- ----------- ------ ------
> + * i2c                                      SCL    SDA    (RCW only)
> + * spi       SCK   DATA0
> + * spi       SCK   DATA0        DATA1
> + * spi       SCK   DATA0        DATA1       DATA2  DATA3
> + * gpio      GPIO4 GPIO11 GPIO5
> + * gpio                         GPIO12
> + * gpio                                     GPIO13 GPIO14
> + * gpio_reset                               GPIO13 REQ_B  (RCW only)
> + */
> +
> +static const struct ls1012a_func_mux {
> +	u32 cr0mask, cr0; /* mux control */
> +	u32 sscmask, ssc; /* equivalent in RCW */
> +} ls1012a_func_mux[_FUNC_max][_GRP_max] = {
> +	[FUNC_i2c] = {
> +		[GRP_qspi_3] = {
> +			.sscmask = SSC_IIC2_MASK,
> +			.ssc =     SSC_IIC2_SEL_I2C,
> +		},
> +	},
> +	[FUNC_spi] = {
> +		[GRP_qspi_1] = {
> +			.cr0mask = QSPI_DATA0_GPIO_OVR_MASK,
> +			.cr0 =     QSPI_DATA0_GPIO_SEL_SPI,
> +			.sscmask = SSC_DATA0_GPIO_MASK,
> +			.ssc =     SSC_DATA0_GPIO_SEL_SPI
> +		},
> +		[GRP_qspi_2] = {
> +			.cr0mask = QSPI_DATA1_GPIO_OVR_MASK,
> +			.cr0 =     QSPI_DATA1_GPIO_SEL_SPI,
> +			.sscmask = SSC_DATA1_GPIO_MASK,
> +			.ssc =     SSC_DATA1_GPIO_SEL_SPI,
> +		},
> +		[GRP_qspi_3] = {
> +			.cr0mask = QSPI_IIC2_OVR_MASK,
> +			.cr0 =     QSPI_IIC2_SEL_SPI,
> +			.sscmask = SSC_IIC2_MASK,
> +			.ssc =     SSC_IIC2_SEL_SPI,
> +		},
> +	},
> +	[FUNC_gpio] = {
> +		[GRP_qspi_1] = {
> +			.cr0mask = QSPI_DATA0_GPIO_OVR_MASK,
> +			.cr0 =     QSPI_DATA0_GPIO_SEL_GPIO,
> +			.sscmask = SSC_DATA0_GPIO_MASK,
> +			.ssc =     SSC_DATA0_GPIO_SEL_GPIO,
> +		},
> +		[GRP_qspi_2] = {
> +			.cr0mask = QSPI_DATA1_GPIO_OVR_MASK,
> +			.cr0 =     QSPI_DATA1_GPIO_SEL_GPIO,
> +			.sscmask = SSC_DATA1_GPIO_MASK,
> +			.ssc =     SSC_DATA1_GPIO_SEL_GPIO,
> +		},
> +		[GRP_qspi_3] = {
> +			.cr0mask = QSPI_IIC2_OVR_MASK,
> +			.cr0 =     QSPI_IIC2_SEL_GPIO,
> +			.sscmask = SSC_IIC2_MASK,
> +			.ssc =     SSC_IIC2_SEL_GPIO,
> +		},
> +	},
> +	[FUNC_gpio_reset] = {
> +		[GRP_qspi_3] = {
> +			.sscmask = SSC_IIC2_MASK,
> +			.ssc =     SSC_IIC2_SEL_GPIO_RESET,
> +		},
> +	},
> +};
> +
> +static int ls1012a_set_mux(struct pinctrl_dev *pcdev,
> +	unsigned int func, unsigned int group)
> +{
> +	struct ls1012a_pinctrl_pdata *pd = pinctrl_dev_get_drvdata(pcdev);
> +	const struct ls1012a_func_mux *fm = &ls1012a_func_mux[func][group];
> +	u32 cr0 = ls1012a_read_cr0(pd);
> +
> +	if (!fm->cr0mask) {
> +		if ((pd->ssc & fm->sscmask) != fm->ssc)
> +			return -EOPNOTSUPP;
> +		cr0 = (cr0 & ~QSPI_MUX_OVRD_MASK) | QSPI_MUX_DISABLE;
> +	} else {
> +		cr0 = (cr0 & ~fm->cr0mask) | fm->cr0;
> +		if ((pd->ssc & fm->sscmask) != fm->ssc)
> +			cr0 = (cr0 & ~QSPI_MUX_OVRD_MASK) | QSPI_MUX_ENABLE;
> +	}
> +	ls1012a_write_cr0(pd, cr0);
> +	return 0;
> +}
> +
> +static void ls1012a_init_mux(struct ls1012a_pinctrl_pdata *pd)
> +{
> +	unsigned int func, group;
> +	const struct ls1012a_func_mux *fm;
> +	u32 cr0;
> +
> +	cr0 = ls1012a_read_cr0(pd);
> +	if ((cr0 & QSPI_MUX_OVRD_MASK) == QSPI_MUX_DISABLE) {
> +		/*
> +		 * Prepare a disabled MUXCR0 to have a same/similar
> +		 * configuration as RCW SSC, and leave it disabled.
> +		 */
> +		for (func = 0; func < _FUNC_max; func++) {
> +			for (group = 0; group < _GRP_max; group++) {
> +				fm = &ls1012a_func_mux[func][group];
> +				if (fm->sscmask &&
> +				    fm->ssc == (pd->ssc & fm->sscmask)) {
> +					cr0 &= ~fm->cr0mask;
> +					cr0 |= fm->cr0;
> +				}
> +			}
> +		}
> +		ls1012a_write_cr0(pd, cr0);
> +	}
> +}
> +
> +static const struct pinmux_ops ls1012a_pinmux_ops = {
> +	.get_functions_count = ls1012a_get_functions_count,
> +	.get_function_name = ls1012a_get_function_name,
> +	.get_function_groups = ls1012a_get_function_groups,
> +	.set_mux = ls1012a_set_mux,
> +};
> +
> +static struct pinctrl_desc ls1012a_pinctrl_desc = {
> +	.name = "ls1012a",
> +	.pins = ls1012a_pins,
> +	.npins = ARRAY_SIZE(ls1012a_pins),
> +	.pctlops = &ls1012a_pinctrl_ops,
> +	.pmxops = &ls1012a_pinmux_ops,
> +	.owner = THIS_MODULE,
> +};
> +
> +static int ls1012a_pinctrl_probe(struct platform_device *pdev)
> +{
> +	struct ls1012a_pinctrl_pdata *pd;
> +	int ret;
> +	u32 dcfg_ssc;
> +	struct regmap *dcfg_regmap;
> +
> +	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");
> +
> +	/* DCFG RCW SSC */
> +	dcfg_regmap = syscon_regmap_lookup_by_phandle(
> +		dev_of_node(&pdev->dev), "dcfg-regmap");
> +	if (IS_ERR(dcfg_regmap)) {
> +		dev_err(&pdev->dev, "dcfg regmap: %pe\n", dcfg_regmap);
> +		return -EINVAL;
> +	}
> +	ret = regmap_read(dcfg_regmap, DCFG_SSC_REG, &dcfg_ssc);
> +	if (ret) {
> +		dev_err(&pdev->dev, "dcfg-regmap read: %d\n", ret);
> +		return ret;
> +	}
> +	pd->ssc = swab32(dcfg_ssc); /* untwist RCW fields */
> +
> +	dev_dbg(&pdev->dev, "dcfg ssc = %08x (grp1=%s grp2=%s grp3=%s)\n",
> +		pd->ssc,
> +		(pd->ssc & SSC_DATA0_GPIO_MASK) == SSC_DATA0_GPIO_SEL_SPI ? "spi" : "gpio",
> +		(pd->ssc & SSC_DATA1_GPIO_MASK) == SSC_DATA1_GPIO_SEL_SPI ? "spi"
> +		: (pd->ssc & SSC_DATA1_GPIO_MASK) == SSC_DATA1_GPIO_SEL_GPIO ? "gpio"
> +		: (pd->ssc & SSC_DATA1_GPIO_MASK) == 0x80 ? "10" : "11",
> +		(pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_GPIO ? "gpio"
> +		: (pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_I2C ? "i2c"
> +		: (pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_SPI ? "spi"
> +		: "gpio+reset");
> +
> +	ls1012a_init_mux(pd);
> +
> +	ret = devm_pinctrl_register_and_init(&pdev->dev, &ls1012a_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 ls1012a_pinctrl_match_table[] = {
> +	{ .compatible = "fsl,ls1012a-pinctrl" },
> +	{ /* sentinel */ }
> +};
> +
> +static struct platform_driver ls1012a_pinctrl_driver = {
> +	.driver = {
> +		.name = "ls1012a_pinctrl",
> +		.of_match_table = ls1012a_pinctrl_match_table,
> +	},
> +	.probe = ls1012a_pinctrl_probe,
> +};
> +
> +builtin_platform_driver(ls1012a_pinctrl_driver)
> +
> +MODULE_DESCRIPTION("LS1012A pinctrl driver");
> +MODULE_LICENSE("GPL");
> --
> 2.43.0
>
Frank Li Aug. 27, 2024, 6:14 p.m. UTC | #4
On Tue, Aug 27, 2024 at 02:12:04PM -0400, Frank Li wrote:
> On Tue, Aug 27, 2024 at 12:05:24PM +1000, David Leonard wrote:
> >
> > Add QorIQ LS1012A pinctrl driver, allowing i2c-core to exert
> > GPIO control over the second I2C bus.
> >
> > Signed-off-by: David Leonard <David.Leonard@digi.com>
> > ---
>
> Why not use pinctrl-single ?
>
> You can ref arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
>
> It did similar thing to use GPIO recover i2c bus.
>
> Just need change dts file.

Next time, please cc: imx@lists.linux.dev

Frank

>
> Frank
>
>
> >  drivers/pinctrl/freescale/Kconfig           |   8 +
> >  drivers/pinctrl/freescale/Makefile          |   1 +
> >  drivers/pinctrl/freescale/pinctrl-ls1012a.c | 381 ++++++++++++++++++++
> >  3 files changed, 390 insertions(+)
> >  create mode 100644 drivers/pinctrl/freescale/pinctrl-ls1012a.c
> >
> > diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig
> > index 3b59d7189004..a2038042eeae 100644
> > --- a/drivers/pinctrl/freescale/Kconfig
> > +++ b/drivers/pinctrl/freescale/Kconfig
> > @@ -209,6 +209,14 @@ config PINCTRL_IMX93
> >  	help
> >  	  Say Y here to enable the imx93 pinctrl driver
> >
> > +config PINCTRL_LS1012A
> > +	tristate "LS1012A pinctrl driver"
> > +	depends on ARCH_LAYERSCAPE && OF || COMPILE_TEST
> > +	select PINMUX
> > +	select GENERIC_PINCONF
> > +	help
> > +	  Say Y here to enable the ls1012a 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 d27085c2b4c4..6926529d8635 100644
> > --- a/drivers/pinctrl/freescale/Makefile
> > +++ b/drivers/pinctrl/freescale/Makefile
> > @@ -35,3 +35,4 @@ obj-$(CONFIG_PINCTRL_IMX25)	+= pinctrl-imx25.o
> >  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
> > diff --git a/drivers/pinctrl/freescale/pinctrl-ls1012a.c b/drivers/pinctrl/freescale/pinctrl-ls1012a.c
> > new file mode 100644
> > index 000000000000..d4c535ed6c07
> > --- /dev/null
> > +++ b/drivers/pinctrl/freescale/pinctrl-ls1012a.c
> > @@ -0,0 +1,381 @@
> > +// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
> > +/*
> > + * Pin controller for NXP QorIQ LS1012A.
> > + *
> > + * 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/regmap.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/sys_soc.h>
> > +
> > +struct ls1012a_pinctrl_pdata {
> > +	struct pinctrl_dev *pctl_dev;
> > +	void __iomem *cr0mem;
> > +	bool big_endian;
> > +	u32 ssc;
> > +};
> > +
> > +/* Bitfield macros for masks and values that follow the datasheet's
> > + * bit numbering schemes for registers of different bit-endianess. */
> > +#define BITV_BE(hi, v)	((v) << (31 - (hi) % 32))
> > +#define BITM_BE(hi, lo)	(((1 << ((hi) - (lo) + 1)) - 1) << (31 - (hi) % 32))
> > +#define BITV_LE(lo, v)	((v) << ((lo) % 32))
> > +#define BITM_LE(lo, hi)	(((1 << ((hi) - (lo) + 1)) - 1) << ((lo) % 32))
> > +
> > +/* SCFG PMUXCR0 pinmux control register */
> > +#define SCFG_PMUXCR0			0x430
> > +#define QSPI_MUX_OVRD_MASK		BITM_BE(0, 0)	/* [0] */
> > +#define QSPI_MUX_DISABLE		BITV_BE(0, 0)	/*  use RCW */
> > +#define QSPI_MUX_ENABLE			BITV_BE(0, 1)	/*  use PMUXCR0 */
> > +#define QSPI_DATA0_GPIO_OVR_MASK	BITM_BE(1, 1)	/* [1] */
> > +#define QSPI_DATA0_GPIO_SEL_SPI		BITV_BE(1, 0)	/*  DATA0,SCK,CS0 */
> > +#define QSPI_DATA0_GPIO_SEL_GPIO	BITV_BE(1, 1)	/*  GPIO1[4,11,5] */
> > +#define QSPI_DATA1_GPIO_OVR_MASK	BITM_BE(3, 2)	/* [3:2] */
> > +#define QSPI_DATA1_GPIO_SEL_SPI		BITV_BE(3, 0)	/*  DATA1 */
> > +#define QSPI_DATA1_GPIO_SEL_GPIO	BITV_BE(3, 1)	/*  GPIO1[12] */
> > +#define QSPI_IIC2_OVR_MASK		BITM_BE(5, 4)	/* [5:4] */
> > +#define QSPI_IIC2_SEL_GPIO		BITV_BE(5, 0)	/*  GPIO1[13,14] */
> > +#define QSPI_IIC2_SEL_I2C		BITV_BE(5, 1)	/*  SCL,SDA (rev0) */
> > +#define QSPI_IIC2_SEL_SPI		BITV_BE(5, 2)	/*  DATA2,DATA3 */
> > +
> > +/* RCW SoC-specific configuration (read-only) */
> > +#define DCFG_RCWSR			0x100
> > +#define SOC_SPEC_CONFIG			416		/* word 13 */
> > +#define DCFG_SSC_REG			(DCFG_RCWSR + SOC_SPEC_CONFIG / 8)
> > +#define SSC_DATA0_GPIO_MASK		BITM_LE(421, 421)
> > +#define SSC_DATA0_GPIO_SEL_SPI		BITV_LE(421, 0)	/*  DATA0,SCK,CS0 */
> > +#define SSC_DATA0_GPIO_SEL_GPIO		BITV_LE(421, 1)	/*  GPIO1[11,4,5] */
> > +#define SSC_DATA1_GPIO_MASK		BITM_LE(422, 423)
> > +#define SSC_DATA1_GPIO_SEL_SPI		BITV_LE(422, 0)	/*  DATA1 */
> > +#define SSC_DATA1_GPIO_SEL_GPIO		BITV_LE(422, 2)	/*  GPIO1[12] */
> > +#define SSC_IIC2_MASK			BITM_LE(424, 425)
> > +#define SSC_IIC2_SEL_GPIO		BITV_LE(424, 0)	/*  GPIO1[13,14] */
> > +#define SSC_IIC2_SEL_I2C		BITV_LE(424, 2)	/*  SCL,SDA */
> > +#define SSC_IIC2_SEL_SPI		BITV_LE(424, 1)	/*  DATA2,DATA3 */
> > +#define SSC_IIC2_SEL_GPIO_RESET		BITV_LE(424, 3)	/*  GPIO1[13],RESET_REQ_B*/
> > +
> > +const struct pinctrl_pin_desc ls1012a_pins[] = {
> > +	PINCTRL_PIN(60, "QSPI_A_DATA3/GPIO1_14/IIC2_SDA/RESET_REQ_B"),
> > +	PINCTRL_PIN(61, "QSPI_A_DATA1/GPIO1_12"),
> > +	PINCTRL_PIN(62, "QSPI_A_SCK/GPIO1_04"),
> > +	PINCTRL_PIN(122, "QSPI_A_DATA2/GPIO1_13/IIC2_SCL"),
> > +	PINCTRL_PIN(123, "QSPI_A_DATA0/GPIO1_11"),
> > +	PINCTRL_PIN(124, "QSPI_A_CS0/GPIO1_05"),
> > +};
> > +
> > +static const unsigned int qspi_1_grp[] = { 62, 123, 124 };
> > +static const unsigned int qspi_2_grp[] = { 61 };
> > +static const unsigned int qspi_3_grp[] = { 122, 60 };
> > +
> > +#define GRP_qspi_1	0	/* SCK,CS0,DATA0 */
> > +#define GRP_qspi_2	1	/* DATA1 */
> > +#define GRP_qspi_3	2	/* DATA2,DATA3 */
> > +#define _GRP_max	3
> > +
> > +#define _PINGROUP(name) \
> > +	[GRP_##name] = PINCTRL_PINGROUP(#name "_grp", name##_grp, ARRAY_SIZE(name##_grp))
> > +static const struct pingroup ls1012a_groups[] = {
> > +	_PINGROUP(qspi_1),
> > +	_PINGROUP(qspi_2),
> > +	_PINGROUP(qspi_3),
> > +};
> > +
> > +
> > +static void ls1012a_write_cr0(struct ls1012a_pinctrl_pdata *pd, u32 val)
> > +{
> > +	if (pd->big_endian)
> > +		iowrite32be(val, pd->cr0mem);
> > +	else
> > +		iowrite32(val, pd->cr0mem);
> > +}
> > +
> > +static u32 ls1012a_read_cr0(struct ls1012a_pinctrl_pdata *pd)
> > +{
> > +	return pd->big_endian ? ioread32be(pd->cr0mem) : ioread32(pd->cr0mem);
> > +}
> > +
> > +static int ls1012a_get_groups_count(struct pinctrl_dev *pcdev)
> > +{
> > +	return ARRAY_SIZE(ls1012a_groups);
> > +}
> > +
> > +static const char *ls1012a_get_group_name(struct pinctrl_dev *pcdev,
> > +	unsigned int selector)
> > +{
> > +	return ls1012a_groups[selector].name;
> > +}
> > +
> > +static int ls1012a_get_group_pins(struct pinctrl_dev *pcdev,
> > +	unsigned int selector, const unsigned int **pins, unsigned int *npins)
> > +{
> > +	*pins = ls1012a_groups[selector].pins;
> > +	*npins = ls1012a_groups[selector].npins;
> > +	return 0;
> > +}
> > +
> > +static const struct pinctrl_ops ls1012a_pinctrl_ops = {
> > +	.get_groups_count = ls1012a_get_groups_count,
> > +	.get_group_name = ls1012a_get_group_name,
> > +	.get_group_pins = ls1012a_get_group_pins,
> > +	.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
> > +	.dt_free_map = pinconf_generic_dt_free_map,
> > +};
> > +
> > +static const char * const i2c_groups[] = { "qspi_3_grp" };
> > +static const char * const spi_groups[] = { "qspi_1_grp", "qspi_2_grp", "qspi_3_grp" };
> > +static const char * const gpio_groups[] = { "qspi_1_grp", "qspi_2_grp", "qspi_3_grp" };
> > +static const char * const gpio_reset_groups[] = { "qspi_3_grp" };
> > +
> > +#define FUNC_i2c	0
> > +#define FUNC_spi	1
> > +#define FUNC_gpio	2
> > +#define FUNC_gpio_reset 3
> > +#define _FUNC_max	4
> > +
> > +#define _PINFUNC(name) \
> > +	[FUNC_##name] = PINCTRL_PINFUNCTION(#name, name##_groups, ARRAY_SIZE(name##_groups))
> > +static const struct pinfunction ls1012a_functions[] = {
> > +	_PINFUNC(i2c),
> > +	_PINFUNC(spi),
> > +	_PINFUNC(gpio),
> > +	_PINFUNC(gpio_reset),
> > +};
> > +
> > +static int ls1012a_get_functions_count(struct pinctrl_dev *pctldev)
> > +{
> > +	return ARRAY_SIZE(ls1012a_functions);
> > +}
> > +
> > +static const char *ls1012a_get_function_name(struct pinctrl_dev *pctldev, unsigned int func)
> > +{
> > +	return ls1012a_functions[func].name;
> > +}
> > +
> > +static int ls1012a_get_function_groups(struct pinctrl_dev *pctldev, unsigned int func,
> > +	const char * const **groups,
> > +	unsigned int * const ngroups)
> > +{
> > +	*groups = ls1012a_functions[func].groups;
> > +	*ngroups = ls1012a_functions[func].ngroups;
> > +	return 0;
> > +}
> > +
> > +/*
> > + * LS1012A
> > + *    Group: qspi_1             qspi_2      qspi_3
> > + *           ================== =========== =============
> > + *    Pin:   62    123    124   61          122    60
> > + *           ----- ------ ----- ----------- ------ ------
> > + * i2c                                      SCL    SDA    (RCW only)
> > + * spi       SCK   DATA0
> > + * spi       SCK   DATA0        DATA1
> > + * spi       SCK   DATA0        DATA1       DATA2  DATA3
> > + * gpio      GPIO4 GPIO11 GPIO5
> > + * gpio                         GPIO12
> > + * gpio                                     GPIO13 GPIO14
> > + * gpio_reset                               GPIO13 REQ_B  (RCW only)
> > + */
> > +
> > +static const struct ls1012a_func_mux {
> > +	u32 cr0mask, cr0; /* mux control */
> > +	u32 sscmask, ssc; /* equivalent in RCW */
> > +} ls1012a_func_mux[_FUNC_max][_GRP_max] = {
> > +	[FUNC_i2c] = {
> > +		[GRP_qspi_3] = {
> > +			.sscmask = SSC_IIC2_MASK,
> > +			.ssc =     SSC_IIC2_SEL_I2C,
> > +		},
> > +	},
> > +	[FUNC_spi] = {
> > +		[GRP_qspi_1] = {
> > +			.cr0mask = QSPI_DATA0_GPIO_OVR_MASK,
> > +			.cr0 =     QSPI_DATA0_GPIO_SEL_SPI,
> > +			.sscmask = SSC_DATA0_GPIO_MASK,
> > +			.ssc =     SSC_DATA0_GPIO_SEL_SPI
> > +		},
> > +		[GRP_qspi_2] = {
> > +			.cr0mask = QSPI_DATA1_GPIO_OVR_MASK,
> > +			.cr0 =     QSPI_DATA1_GPIO_SEL_SPI,
> > +			.sscmask = SSC_DATA1_GPIO_MASK,
> > +			.ssc =     SSC_DATA1_GPIO_SEL_SPI,
> > +		},
> > +		[GRP_qspi_3] = {
> > +			.cr0mask = QSPI_IIC2_OVR_MASK,
> > +			.cr0 =     QSPI_IIC2_SEL_SPI,
> > +			.sscmask = SSC_IIC2_MASK,
> > +			.ssc =     SSC_IIC2_SEL_SPI,
> > +		},
> > +	},
> > +	[FUNC_gpio] = {
> > +		[GRP_qspi_1] = {
> > +			.cr0mask = QSPI_DATA0_GPIO_OVR_MASK,
> > +			.cr0 =     QSPI_DATA0_GPIO_SEL_GPIO,
> > +			.sscmask = SSC_DATA0_GPIO_MASK,
> > +			.ssc =     SSC_DATA0_GPIO_SEL_GPIO,
> > +		},
> > +		[GRP_qspi_2] = {
> > +			.cr0mask = QSPI_DATA1_GPIO_OVR_MASK,
> > +			.cr0 =     QSPI_DATA1_GPIO_SEL_GPIO,
> > +			.sscmask = SSC_DATA1_GPIO_MASK,
> > +			.ssc =     SSC_DATA1_GPIO_SEL_GPIO,
> > +		},
> > +		[GRP_qspi_3] = {
> > +			.cr0mask = QSPI_IIC2_OVR_MASK,
> > +			.cr0 =     QSPI_IIC2_SEL_GPIO,
> > +			.sscmask = SSC_IIC2_MASK,
> > +			.ssc =     SSC_IIC2_SEL_GPIO,
> > +		},
> > +	},
> > +	[FUNC_gpio_reset] = {
> > +		[GRP_qspi_3] = {
> > +			.sscmask = SSC_IIC2_MASK,
> > +			.ssc =     SSC_IIC2_SEL_GPIO_RESET,
> > +		},
> > +	},
> > +};
> > +
> > +static int ls1012a_set_mux(struct pinctrl_dev *pcdev,
> > +	unsigned int func, unsigned int group)
> > +{
> > +	struct ls1012a_pinctrl_pdata *pd = pinctrl_dev_get_drvdata(pcdev);
> > +	const struct ls1012a_func_mux *fm = &ls1012a_func_mux[func][group];
> > +	u32 cr0 = ls1012a_read_cr0(pd);
> > +
> > +	if (!fm->cr0mask) {
> > +		if ((pd->ssc & fm->sscmask) != fm->ssc)
> > +			return -EOPNOTSUPP;
> > +		cr0 = (cr0 & ~QSPI_MUX_OVRD_MASK) | QSPI_MUX_DISABLE;
> > +	} else {
> > +		cr0 = (cr0 & ~fm->cr0mask) | fm->cr0;
> > +		if ((pd->ssc & fm->sscmask) != fm->ssc)
> > +			cr0 = (cr0 & ~QSPI_MUX_OVRD_MASK) | QSPI_MUX_ENABLE;
> > +	}
> > +	ls1012a_write_cr0(pd, cr0);
> > +	return 0;
> > +}
> > +
> > +static void ls1012a_init_mux(struct ls1012a_pinctrl_pdata *pd)
> > +{
> > +	unsigned int func, group;
> > +	const struct ls1012a_func_mux *fm;
> > +	u32 cr0;
> > +
> > +	cr0 = ls1012a_read_cr0(pd);
> > +	if ((cr0 & QSPI_MUX_OVRD_MASK) == QSPI_MUX_DISABLE) {
> > +		/*
> > +		 * Prepare a disabled MUXCR0 to have a same/similar
> > +		 * configuration as RCW SSC, and leave it disabled.
> > +		 */
> > +		for (func = 0; func < _FUNC_max; func++) {
> > +			for (group = 0; group < _GRP_max; group++) {
> > +				fm = &ls1012a_func_mux[func][group];
> > +				if (fm->sscmask &&
> > +				    fm->ssc == (pd->ssc & fm->sscmask)) {
> > +					cr0 &= ~fm->cr0mask;
> > +					cr0 |= fm->cr0;
> > +				}
> > +			}
> > +		}
> > +		ls1012a_write_cr0(pd, cr0);
> > +	}
> > +}
> > +
> > +static const struct pinmux_ops ls1012a_pinmux_ops = {
> > +	.get_functions_count = ls1012a_get_functions_count,
> > +	.get_function_name = ls1012a_get_function_name,
> > +	.get_function_groups = ls1012a_get_function_groups,
> > +	.set_mux = ls1012a_set_mux,
> > +};
> > +
> > +static struct pinctrl_desc ls1012a_pinctrl_desc = {
> > +	.name = "ls1012a",
> > +	.pins = ls1012a_pins,
> > +	.npins = ARRAY_SIZE(ls1012a_pins),
> > +	.pctlops = &ls1012a_pinctrl_ops,
> > +	.pmxops = &ls1012a_pinmux_ops,
> > +	.owner = THIS_MODULE,
> > +};
> > +
> > +static int ls1012a_pinctrl_probe(struct platform_device *pdev)
> > +{
> > +	struct ls1012a_pinctrl_pdata *pd;
> > +	int ret;
> > +	u32 dcfg_ssc;
> > +	struct regmap *dcfg_regmap;
> > +
> > +	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");
> > +
> > +	/* DCFG RCW SSC */
> > +	dcfg_regmap = syscon_regmap_lookup_by_phandle(
> > +		dev_of_node(&pdev->dev), "dcfg-regmap");
> > +	if (IS_ERR(dcfg_regmap)) {
> > +		dev_err(&pdev->dev, "dcfg regmap: %pe\n", dcfg_regmap);
> > +		return -EINVAL;
> > +	}
> > +	ret = regmap_read(dcfg_regmap, DCFG_SSC_REG, &dcfg_ssc);
> > +	if (ret) {
> > +		dev_err(&pdev->dev, "dcfg-regmap read: %d\n", ret);
> > +		return ret;
> > +	}
> > +	pd->ssc = swab32(dcfg_ssc); /* untwist RCW fields */
> > +
> > +	dev_dbg(&pdev->dev, "dcfg ssc = %08x (grp1=%s grp2=%s grp3=%s)\n",
> > +		pd->ssc,
> > +		(pd->ssc & SSC_DATA0_GPIO_MASK) == SSC_DATA0_GPIO_SEL_SPI ? "spi" : "gpio",
> > +		(pd->ssc & SSC_DATA1_GPIO_MASK) == SSC_DATA1_GPIO_SEL_SPI ? "spi"
> > +		: (pd->ssc & SSC_DATA1_GPIO_MASK) == SSC_DATA1_GPIO_SEL_GPIO ? "gpio"
> > +		: (pd->ssc & SSC_DATA1_GPIO_MASK) == 0x80 ? "10" : "11",
> > +		(pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_GPIO ? "gpio"
> > +		: (pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_I2C ? "i2c"
> > +		: (pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_SPI ? "spi"
> > +		: "gpio+reset");
> > +
> > +	ls1012a_init_mux(pd);
> > +
> > +	ret = devm_pinctrl_register_and_init(&pdev->dev, &ls1012a_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 ls1012a_pinctrl_match_table[] = {
> > +	{ .compatible = "fsl,ls1012a-pinctrl" },
> > +	{ /* sentinel */ }
> > +};
> > +
> > +static struct platform_driver ls1012a_pinctrl_driver = {
> > +	.driver = {
> > +		.name = "ls1012a_pinctrl",
> > +		.of_match_table = ls1012a_pinctrl_match_table,
> > +	},
> > +	.probe = ls1012a_pinctrl_probe,
> > +};
> > +
> > +builtin_platform_driver(ls1012a_pinctrl_driver)
> > +
> > +MODULE_DESCRIPTION("LS1012A pinctrl driver");
> > +MODULE_LICENSE("GPL");
> > --
> > 2.43.0
> >
David Leonard Aug. 28, 2024, 1:13 a.m. UTC | #5
On Tue, 27 Aug 2024, Frank Li wrote:

> On Tue, Aug 27, 2024 at 12:05:24PM +1000, David Leonard wrote:
>> Add QorIQ LS1012A pinctrl driver, allowing i2c-core to exert
>> GPIO control over the second I2C bus.
>>
>> Signed-off-by: David Leonard <David.Leonard@digi.com>
>> ---
>
> Why not use pinctrl-single ?
>
> You can ref arch/arm64/boot/dts/freescale/fsl-lx2160a.dtsi
>
> It did similar thing to use GPIO recover i2c bus.
>
> Just need change dts file.

This is a great suggestion for the LS1046A and means I could
withdraw the "fsl,ls1046a-pinctrl" driver, which I'll do.

But I don't think it's suitable for use with the LS1012A.
The reason is that the the LS1012A's pinmux register PMUXCR0 is more
complicated. It has a global override bit, overriding the "backing"
RCW configuration for 3 pingroups. In addition, when overriding, the
PMUXCR0 can only supply a subset of the functions that RCW can.

/*
  * LS1012A
  *    Group: qspi_1             qspi_2      qspi_3
  *           ================== =========== =============
  *    Pin:   62    123    124   61          122    60
  *           ----- ------ ----- ----------- ------ ------
  * i2c                                      SCL    SDA    (RCW only)
  * spi       SCK   DATA0
  * spi       SCK   DATA0        DATA1
  * spi       SCK   DATA0        DATA1       DATA2  DATA3
  * gpio      GPIO4 GPIO11 GPIO5
  * gpio                         GPIO12
  * gpio                                     GPIO13 GPIO14
  * gpio_reset                               GPIO13 REQ_B  (RCW only)
  */

In particular, when PMUXCR0 is overriding RCW, it can't provide "i2c"
or "gpio_reset" functions for qspi_3. It can only provide "spi" and "gpio".

The fsl,ls1012a-pinctrl driver recognises when you are asking
for the configuration that the RCW can provide, and diables the
override.

Cheers,

David
diff mbox series

Patch

diff --git a/drivers/pinctrl/freescale/Kconfig b/drivers/pinctrl/freescale/Kconfig
index 3b59d7189004..a2038042eeae 100644
--- a/drivers/pinctrl/freescale/Kconfig
+++ b/drivers/pinctrl/freescale/Kconfig
@@ -209,6 +209,14 @@  config PINCTRL_IMX93
  	help
  	  Say Y here to enable the imx93 pinctrl driver

+config PINCTRL_LS1012A
+	tristate "LS1012A pinctrl driver"
+	depends on ARCH_LAYERSCAPE && OF || COMPILE_TEST
+	select PINMUX
+	select GENERIC_PINCONF
+	help
+	  Say Y here to enable the ls1012a 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 d27085c2b4c4..6926529d8635 100644
--- a/drivers/pinctrl/freescale/Makefile
+++ b/drivers/pinctrl/freescale/Makefile
@@ -35,3 +35,4 @@  obj-$(CONFIG_PINCTRL_IMX25)	+= pinctrl-imx25.o
  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
diff --git a/drivers/pinctrl/freescale/pinctrl-ls1012a.c b/drivers/pinctrl/freescale/pinctrl-ls1012a.c
new file mode 100644
index 000000000000..d4c535ed6c07
--- /dev/null
+++ b/drivers/pinctrl/freescale/pinctrl-ls1012a.c
@@ -0,0 +1,381 @@ 
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Pin controller for NXP QorIQ LS1012A.
+ *
+ * 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/regmap.h>
+#include <linux/platform_device.h>
+#include <linux/sys_soc.h>
+
+struct ls1012a_pinctrl_pdata {
+	struct pinctrl_dev *pctl_dev;
+	void __iomem *cr0mem;
+	bool big_endian;
+	u32 ssc;
+};
+
+/* Bitfield macros for masks and values that follow the datasheet's
+ * bit numbering schemes for registers of different bit-endianess. */
+#define BITV_BE(hi, v)	((v) << (31 - (hi) % 32))
+#define BITM_BE(hi, lo)	(((1 << ((hi) - (lo) + 1)) - 1) << (31 - (hi) % 32))
+#define BITV_LE(lo, v)	((v) << ((lo) % 32))
+#define BITM_LE(lo, hi)	(((1 << ((hi) - (lo) + 1)) - 1) << ((lo) % 32))
+
+/* SCFG PMUXCR0 pinmux control register */
+#define SCFG_PMUXCR0			0x430
+#define QSPI_MUX_OVRD_MASK		BITM_BE(0, 0)	/* [0] */
+#define QSPI_MUX_DISABLE		BITV_BE(0, 0)	/*  use RCW */
+#define QSPI_MUX_ENABLE			BITV_BE(0, 1)	/*  use PMUXCR0 */
+#define QSPI_DATA0_GPIO_OVR_MASK	BITM_BE(1, 1)	/* [1] */
+#define QSPI_DATA0_GPIO_SEL_SPI		BITV_BE(1, 0)	/*  DATA0,SCK,CS0 */
+#define QSPI_DATA0_GPIO_SEL_GPIO	BITV_BE(1, 1)	/*  GPIO1[4,11,5] */
+#define QSPI_DATA1_GPIO_OVR_MASK	BITM_BE(3, 2)	/* [3:2] */
+#define QSPI_DATA1_GPIO_SEL_SPI		BITV_BE(3, 0)	/*  DATA1 */
+#define QSPI_DATA1_GPIO_SEL_GPIO	BITV_BE(3, 1)	/*  GPIO1[12] */
+#define QSPI_IIC2_OVR_MASK		BITM_BE(5, 4)	/* [5:4] */
+#define QSPI_IIC2_SEL_GPIO		BITV_BE(5, 0)	/*  GPIO1[13,14] */
+#define QSPI_IIC2_SEL_I2C		BITV_BE(5, 1)	/*  SCL,SDA (rev0) */
+#define QSPI_IIC2_SEL_SPI		BITV_BE(5, 2)	/*  DATA2,DATA3 */
+
+/* RCW SoC-specific configuration (read-only) */
+#define DCFG_RCWSR			0x100
+#define SOC_SPEC_CONFIG			416		/* word 13 */
+#define DCFG_SSC_REG			(DCFG_RCWSR + SOC_SPEC_CONFIG / 8)
+#define SSC_DATA0_GPIO_MASK		BITM_LE(421, 421)
+#define SSC_DATA0_GPIO_SEL_SPI		BITV_LE(421, 0)	/*  DATA0,SCK,CS0 */
+#define SSC_DATA0_GPIO_SEL_GPIO		BITV_LE(421, 1)	/*  GPIO1[11,4,5] */
+#define SSC_DATA1_GPIO_MASK		BITM_LE(422, 423)
+#define SSC_DATA1_GPIO_SEL_SPI		BITV_LE(422, 0)	/*  DATA1 */
+#define SSC_DATA1_GPIO_SEL_GPIO		BITV_LE(422, 2)	/*  GPIO1[12] */
+#define SSC_IIC2_MASK			BITM_LE(424, 425)
+#define SSC_IIC2_SEL_GPIO		BITV_LE(424, 0)	/*  GPIO1[13,14] */
+#define SSC_IIC2_SEL_I2C		BITV_LE(424, 2)	/*  SCL,SDA */
+#define SSC_IIC2_SEL_SPI		BITV_LE(424, 1)	/*  DATA2,DATA3 */
+#define SSC_IIC2_SEL_GPIO_RESET		BITV_LE(424, 3)	/*  GPIO1[13],RESET_REQ_B*/
+
+const struct pinctrl_pin_desc ls1012a_pins[] = {
+	PINCTRL_PIN(60, "QSPI_A_DATA3/GPIO1_14/IIC2_SDA/RESET_REQ_B"),
+	PINCTRL_PIN(61, "QSPI_A_DATA1/GPIO1_12"),
+	PINCTRL_PIN(62, "QSPI_A_SCK/GPIO1_04"),
+	PINCTRL_PIN(122, "QSPI_A_DATA2/GPIO1_13/IIC2_SCL"),
+	PINCTRL_PIN(123, "QSPI_A_DATA0/GPIO1_11"),
+	PINCTRL_PIN(124, "QSPI_A_CS0/GPIO1_05"),
+};
+
+static const unsigned int qspi_1_grp[] = { 62, 123, 124 };
+static const unsigned int qspi_2_grp[] = { 61 };
+static const unsigned int qspi_3_grp[] = { 122, 60 };
+
+#define GRP_qspi_1	0	/* SCK,CS0,DATA0 */
+#define GRP_qspi_2	1	/* DATA1 */
+#define GRP_qspi_3	2	/* DATA2,DATA3 */
+#define _GRP_max	3
+
+#define _PINGROUP(name) \
+	[GRP_##name] = PINCTRL_PINGROUP(#name "_grp", name##_grp, ARRAY_SIZE(name##_grp))
+static const struct pingroup ls1012a_groups[] = {
+	_PINGROUP(qspi_1),
+	_PINGROUP(qspi_2),
+	_PINGROUP(qspi_3),
+};
+
+
+static void ls1012a_write_cr0(struct ls1012a_pinctrl_pdata *pd, u32 val)
+{
+	if (pd->big_endian)
+		iowrite32be(val, pd->cr0mem);
+	else
+		iowrite32(val, pd->cr0mem);
+}
+
+static u32 ls1012a_read_cr0(struct ls1012a_pinctrl_pdata *pd)
+{
+	return pd->big_endian ? ioread32be(pd->cr0mem) : ioread32(pd->cr0mem);
+}
+
+static int ls1012a_get_groups_count(struct pinctrl_dev *pcdev)
+{
+	return ARRAY_SIZE(ls1012a_groups);
+}
+
+static const char *ls1012a_get_group_name(struct pinctrl_dev *pcdev,
+	unsigned int selector)
+{
+	return ls1012a_groups[selector].name;
+}
+
+static int ls1012a_get_group_pins(struct pinctrl_dev *pcdev,
+	unsigned int selector, const unsigned int **pins, unsigned int *npins)
+{
+	*pins = ls1012a_groups[selector].pins;
+	*npins = ls1012a_groups[selector].npins;
+	return 0;
+}
+
+static const struct pinctrl_ops ls1012a_pinctrl_ops = {
+	.get_groups_count = ls1012a_get_groups_count,
+	.get_group_name = ls1012a_get_group_name,
+	.get_group_pins = ls1012a_get_group_pins,
+	.dt_node_to_map = pinconf_generic_dt_node_to_map_group,
+	.dt_free_map = pinconf_generic_dt_free_map,
+};
+
+static const char * const i2c_groups[] = { "qspi_3_grp" };
+static const char * const spi_groups[] = { "qspi_1_grp", "qspi_2_grp", "qspi_3_grp" };
+static const char * const gpio_groups[] = { "qspi_1_grp", "qspi_2_grp", "qspi_3_grp" };
+static const char * const gpio_reset_groups[] = { "qspi_3_grp" };
+
+#define FUNC_i2c	0
+#define FUNC_spi	1
+#define FUNC_gpio	2
+#define FUNC_gpio_reset 3
+#define _FUNC_max	4
+
+#define _PINFUNC(name) \
+	[FUNC_##name] = PINCTRL_PINFUNCTION(#name, name##_groups, ARRAY_SIZE(name##_groups))
+static const struct pinfunction ls1012a_functions[] = {
+	_PINFUNC(i2c),
+	_PINFUNC(spi),
+	_PINFUNC(gpio),
+	_PINFUNC(gpio_reset),
+};
+
+static int ls1012a_get_functions_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(ls1012a_functions);
+}
+
+static const char *ls1012a_get_function_name(struct pinctrl_dev *pctldev, unsigned int func)
+{
+	return ls1012a_functions[func].name;
+}
+
+static int ls1012a_get_function_groups(struct pinctrl_dev *pctldev, unsigned int func,
+	const char * const **groups,
+	unsigned int * const ngroups)
+{
+	*groups = ls1012a_functions[func].groups;
+	*ngroups = ls1012a_functions[func].ngroups;
+	return 0;
+}
+
+/*
+ * LS1012A
+ *    Group: qspi_1             qspi_2      qspi_3
+ *           ================== =========== =============
+ *    Pin:   62    123    124   61          122    60
+ *           ----- ------ ----- ----------- ------ ------
+ * i2c                                      SCL    SDA    (RCW only)
+ * spi       SCK   DATA0
+ * spi       SCK   DATA0        DATA1
+ * spi       SCK   DATA0        DATA1       DATA2  DATA3
+ * gpio      GPIO4 GPIO11 GPIO5
+ * gpio                         GPIO12
+ * gpio                                     GPIO13 GPIO14
+ * gpio_reset                               GPIO13 REQ_B  (RCW only)
+ */
+
+static const struct ls1012a_func_mux {
+	u32 cr0mask, cr0; /* mux control */
+	u32 sscmask, ssc; /* equivalent in RCW */
+} ls1012a_func_mux[_FUNC_max][_GRP_max] = {
+	[FUNC_i2c] = {
+		[GRP_qspi_3] = {
+			.sscmask = SSC_IIC2_MASK,
+			.ssc =     SSC_IIC2_SEL_I2C,
+		},
+	},
+	[FUNC_spi] = {
+		[GRP_qspi_1] = {
+			.cr0mask = QSPI_DATA0_GPIO_OVR_MASK,
+			.cr0 =     QSPI_DATA0_GPIO_SEL_SPI,
+			.sscmask = SSC_DATA0_GPIO_MASK,
+			.ssc =     SSC_DATA0_GPIO_SEL_SPI
+		},
+		[GRP_qspi_2] = {
+			.cr0mask = QSPI_DATA1_GPIO_OVR_MASK,
+			.cr0 =     QSPI_DATA1_GPIO_SEL_SPI,
+			.sscmask = SSC_DATA1_GPIO_MASK,
+			.ssc =     SSC_DATA1_GPIO_SEL_SPI,
+		},
+		[GRP_qspi_3] = {
+			.cr0mask = QSPI_IIC2_OVR_MASK,
+			.cr0 =     QSPI_IIC2_SEL_SPI,
+			.sscmask = SSC_IIC2_MASK,
+			.ssc =     SSC_IIC2_SEL_SPI,
+		},
+	},
+	[FUNC_gpio] = {
+		[GRP_qspi_1] = {
+			.cr0mask = QSPI_DATA0_GPIO_OVR_MASK,
+			.cr0 =     QSPI_DATA0_GPIO_SEL_GPIO,
+			.sscmask = SSC_DATA0_GPIO_MASK,
+			.ssc =     SSC_DATA0_GPIO_SEL_GPIO,
+		},
+		[GRP_qspi_2] = {
+			.cr0mask = QSPI_DATA1_GPIO_OVR_MASK,
+			.cr0 =     QSPI_DATA1_GPIO_SEL_GPIO,
+			.sscmask = SSC_DATA1_GPIO_MASK,
+			.ssc =     SSC_DATA1_GPIO_SEL_GPIO,
+		},
+		[GRP_qspi_3] = {
+			.cr0mask = QSPI_IIC2_OVR_MASK,
+			.cr0 =     QSPI_IIC2_SEL_GPIO,
+			.sscmask = SSC_IIC2_MASK,
+			.ssc =     SSC_IIC2_SEL_GPIO,
+		},
+	},
+	[FUNC_gpio_reset] = {
+		[GRP_qspi_3] = {
+			.sscmask = SSC_IIC2_MASK,
+			.ssc =     SSC_IIC2_SEL_GPIO_RESET,
+		},
+	},
+};
+
+static int ls1012a_set_mux(struct pinctrl_dev *pcdev,
+	unsigned int func, unsigned int group)
+{
+	struct ls1012a_pinctrl_pdata *pd = pinctrl_dev_get_drvdata(pcdev);
+	const struct ls1012a_func_mux *fm = &ls1012a_func_mux[func][group];
+	u32 cr0 = ls1012a_read_cr0(pd);
+
+	if (!fm->cr0mask) {
+		if ((pd->ssc & fm->sscmask) != fm->ssc)
+			return -EOPNOTSUPP;
+		cr0 = (cr0 & ~QSPI_MUX_OVRD_MASK) | QSPI_MUX_DISABLE;
+	} else {
+		cr0 = (cr0 & ~fm->cr0mask) | fm->cr0;
+		if ((pd->ssc & fm->sscmask) != fm->ssc)
+			cr0 = (cr0 & ~QSPI_MUX_OVRD_MASK) | QSPI_MUX_ENABLE;
+	}
+	ls1012a_write_cr0(pd, cr0);
+	return 0;
+}
+
+static void ls1012a_init_mux(struct ls1012a_pinctrl_pdata *pd)
+{
+	unsigned int func, group;
+	const struct ls1012a_func_mux *fm;
+	u32 cr0;
+
+	cr0 = ls1012a_read_cr0(pd);
+	if ((cr0 & QSPI_MUX_OVRD_MASK) == QSPI_MUX_DISABLE) {
+		/*
+		 * Prepare a disabled MUXCR0 to have a same/similar
+		 * configuration as RCW SSC, and leave it disabled.
+		 */
+		for (func = 0; func < _FUNC_max; func++) {
+			for (group = 0; group < _GRP_max; group++) {
+				fm = &ls1012a_func_mux[func][group];
+				if (fm->sscmask &&
+				    fm->ssc == (pd->ssc & fm->sscmask)) {
+					cr0 &= ~fm->cr0mask;
+					cr0 |= fm->cr0;
+				}
+			}
+		}
+		ls1012a_write_cr0(pd, cr0);
+	}
+}
+
+static const struct pinmux_ops ls1012a_pinmux_ops = {
+	.get_functions_count = ls1012a_get_functions_count,
+	.get_function_name = ls1012a_get_function_name,
+	.get_function_groups = ls1012a_get_function_groups,
+	.set_mux = ls1012a_set_mux,
+};
+
+static struct pinctrl_desc ls1012a_pinctrl_desc = {
+	.name = "ls1012a",
+	.pins = ls1012a_pins,
+	.npins = ARRAY_SIZE(ls1012a_pins),
+	.pctlops = &ls1012a_pinctrl_ops,
+	.pmxops = &ls1012a_pinmux_ops,
+	.owner = THIS_MODULE,
+};
+
+static int ls1012a_pinctrl_probe(struct platform_device *pdev)
+{
+	struct ls1012a_pinctrl_pdata *pd;
+	int ret;
+	u32 dcfg_ssc;
+	struct regmap *dcfg_regmap;
+
+	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");
+
+	/* DCFG RCW SSC */
+	dcfg_regmap = syscon_regmap_lookup_by_phandle(
+		dev_of_node(&pdev->dev), "dcfg-regmap");
+	if (IS_ERR(dcfg_regmap)) {
+		dev_err(&pdev->dev, "dcfg regmap: %pe\n", dcfg_regmap);
+		return -EINVAL;
+	}
+	ret = regmap_read(dcfg_regmap, DCFG_SSC_REG, &dcfg_ssc);
+	if (ret) {
+		dev_err(&pdev->dev, "dcfg-regmap read: %d\n", ret);
+		return ret;
+	}
+	pd->ssc = swab32(dcfg_ssc); /* untwist RCW fields */
+
+	dev_dbg(&pdev->dev, "dcfg ssc = %08x (grp1=%s grp2=%s grp3=%s)\n",
+		pd->ssc,
+		(pd->ssc & SSC_DATA0_GPIO_MASK) == SSC_DATA0_GPIO_SEL_SPI ? "spi" : "gpio",
+		(pd->ssc & SSC_DATA1_GPIO_MASK) == SSC_DATA1_GPIO_SEL_SPI ? "spi"
+		: (pd->ssc & SSC_DATA1_GPIO_MASK) == SSC_DATA1_GPIO_SEL_GPIO ? "gpio"
+		: (pd->ssc & SSC_DATA1_GPIO_MASK) == 0x80 ? "10" : "11",
+		(pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_GPIO ? "gpio"
+		: (pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_I2C ? "i2c"
+		: (pd->ssc & SSC_IIC2_MASK) == SSC_IIC2_SEL_SPI ? "spi"
+		: "gpio+reset");
+
+	ls1012a_init_mux(pd);
+
+	ret = devm_pinctrl_register_and_init(&pdev->dev, &ls1012a_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 ls1012a_pinctrl_match_table[] = {
+	{ .compatible = "fsl,ls1012a-pinctrl" },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver ls1012a_pinctrl_driver = {
+	.driver = {
+		.name = "ls1012a_pinctrl",
+		.of_match_table = ls1012a_pinctrl_match_table,
+	},
+	.probe = ls1012a_pinctrl_probe,
+};
+
+builtin_platform_driver(ls1012a_pinctrl_driver)
+
+MODULE_DESCRIPTION("LS1012A pinctrl driver");
+MODULE_LICENSE("GPL");