From: Conor Dooley <conor.dooley@microchip.com>
On Polarfire SoC, iomux0 is responsible for routing functions to either
MSS (multiprocessor subsystem) IOs or to the FPGA fabric, where they
can either interface with custom RTL or be routed to the FPGA fabric's
IOs. Add a driver for it.
Signed-off-by: Conor Dooley <conor.dooley@microchip.com>
---
.../microchip,mpfs-mss-top-sysreg.yaml | 2 +-
drivers/pinctrl/Kconfig | 7 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-mpfs-iomux0.c | 252 ++++++++++++++++++
4 files changed, 261 insertions(+), 1 deletion(-)
create mode 100644 drivers/pinctrl/pinctrl-mpfs-iomux0.c
diff --git a/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml b/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml
index 1b737a3fcd33..cb5784ec5ac5 100644
--- a/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml
+++ b/Documentation/devicetree/bindings/soc/microchip/microchip,mpfs-mss-top-sysreg.yaml
@@ -55,7 +55,7 @@ additionalProperties: false
examples:
- |
syscon@20002000 {
- compatible = "microchip,mpfs-mss-top-sysreg", "syscon";
+ compatible = "microchip,mpfs-mss-top-sysreg", "syscon", "simple-mfd";
reg = <0x20002000 0x1000>;
#reset-cells = <1>;
};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 33db9104df17..f85ccbc2a0e2 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -472,6 +472,13 @@ config PINCTRL_PISTACHIO
help
This support pinctrl and GPIO driver for IMG Pistachio SoC.
+config PINCTRL_POLARFIRE_SOC
+ bool "Polarfire SoC pinctrl driver"
+ depends on ARCH_MICROCHIP
+ default y
+ help
+ This selects the pinctrl driver for Microchip Polarfire SoC.
+
config PINCTRL_RK805
tristate "Pinctrl and GPIO driver for RK805 PMIC"
depends on MFD_RK8XX
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index ac27e88677d1..8af119804f77 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_PINCTRL_PALMAS) += pinctrl-palmas.o
obj-$(CONFIG_PINCTRL_PEF2256) += pinctrl-pef2256.o
obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-pic32.o
obj-$(CONFIG_PINCTRL_PISTACHIO) += pinctrl-pistachio.o
+obj-$(CONFIG_PINCTRL_POLARFIRE_SOC) += pinctrl-mpfs-iomux0.o
obj-$(CONFIG_PINCTRL_RK805) += pinctrl-rk805.o
obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
obj-$(CONFIG_PINCTRL_SCMI) += pinctrl-scmi.o
diff --git a/drivers/pinctrl/pinctrl-mpfs-iomux0.c b/drivers/pinctrl/pinctrl-mpfs-iomux0.c
new file mode 100644
index 000000000000..93a17c9c299d
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-mpfs-iomux0.c
@@ -0,0 +1,252 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/seq_file.h>
+
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include "core.h"
+#include "pinctrl-utils.h"
+#include "pinconf.h"
+#include "pinmux.h"
+
+#define MPFS_PINCTRL_DT_FUNC_MASK GENMASK(3, 0);
+#define MPFS_PINCTRL_DT_PIN_OFFSET 16
+
+#define MPFS_PINCTRL_IOMUX0_REG 0x200
+
+struct mpfs_iomux0_pinctrl_mux_config {
+ u8 pin;
+ u32 config;
+};
+
+struct mpfs_iomux0_pinctrl {
+ struct pinctrl_dev *pctrl;
+ struct device *dev;
+ struct regmap *regmap;
+ struct mutex mutex;
+ struct pinctrl_desc desc;
+};
+
+static void mpfs_iomux0_pinctrl_dbg_show(struct pinctrl_dev *pctrl_dev, struct seq_file *seq,
+ unsigned int pin)
+{
+ struct mpfs_iomux0_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+ u32 val;
+
+ seq_printf(seq, "reg: %x, pin: %u ", MPFS_PINCTRL_IOMUX0_REG, pin);
+
+ regmap_read(pctrl->regmap, MPFS_PINCTRL_IOMUX0_REG, &val);
+ val = (val & BIT(pin)) >> pin;
+
+ seq_printf(seq, "val: %x\n", val);
+}
+
+static int mpfs_iomux0_pinctrl_dt_node_to_map(struct pinctrl_dev *pctrl_dev, struct device_node *np,
+ struct pinctrl_map **maps, unsigned int *num_maps)
+{
+ struct mpfs_iomux0_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+ struct device *dev = pctrl->dev;
+ struct mpfs_iomux0_pinctrl_mux_config *pinmuxs;
+ struct pinctrl_map *map;
+ const char **grpnames;
+ const char *grpname;
+ int ret, i, npins;
+ unsigned int config, *pins;
+
+ map = kcalloc(1, sizeof(*map), GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+
+ guard(mutex)(&pctrl->mutex);
+
+ npins = of_property_count_u32_elems(np, "pinmux");
+ if (npins < 1) {
+ dev_err(dev, "invalid pinctrl group %pOFn\n", np);
+ return -EINVAL;
+ }
+
+ grpnames = devm_kmalloc(dev, sizeof(*grpnames), GFP_KERNEL);
+ if (!grpnames)
+ return -ENOMEM;
+
+ grpname = devm_kasprintf(dev, GFP_KERNEL, "%pOFn", np);
+ if (!grpname)
+ return -ENOMEM;
+
+ *grpnames = grpname;
+
+ pins = devm_kcalloc(dev, npins, sizeof(*pins), GFP_KERNEL);
+ if (!pins)
+ return -ENOMEM;
+
+ pinmuxs = devm_kcalloc(dev, npins, sizeof(*pinmuxs), GFP_KERNEL);
+ if (!pinmuxs)
+ return -ENOMEM;
+
+ for (i = 0; i < npins; i++) {
+ ret = of_property_read_u32_index(np, "pinmux", i, &config);
+ if (ret)
+ return -EINVAL;
+
+ pins[i] = config >> MPFS_PINCTRL_DT_PIN_OFFSET;
+ pinmuxs[i].config = config & MPFS_PINCTRL_DT_FUNC_MASK;
+ pinmuxs[i].pin = config >> MPFS_PINCTRL_DT_PIN_OFFSET;
+ }
+
+ map->type = PIN_MAP_TYPE_MUX_GROUP;
+ map->data.mux.function = np->name;
+ map->data.mux.group = grpname;
+
+ ret = pinctrl_generic_add_group(pctrl_dev, grpname, pins, npins, pinmuxs);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "failed to add group %s: %d\n", grpname, ret);
+
+ ret = pinmux_generic_add_function(pctrl_dev, np->name, grpnames, 1, NULL);
+ if (ret < 0) {
+ pinctrl_utils_free_map(pctrl_dev, map, 1);
+ return dev_err_probe(dev, ret, "error adding function %s\n", np->name);
+ }
+
+ *maps = map;
+ *num_maps = 1;
+
+ return 0;
+};
+
+static struct pinctrl_ops mpfs_iomux0_pinctrl_ops = {
+ .get_groups_count = pinctrl_generic_get_group_count,
+ .get_group_name = pinctrl_generic_get_group_name,
+ .get_group_pins = pinctrl_generic_get_group_pins,
+ .pin_dbg_show = mpfs_iomux0_pinctrl_dbg_show,
+ .dt_node_to_map = mpfs_iomux0_pinctrl_dt_node_to_map,
+ .dt_free_map = pinctrl_utils_free_map,
+};
+
+static int mpfs_iomux0_pinctrl_set_pin_func(struct mpfs_iomux0_pinctrl *pctrl, u8 pin, u32 config)
+{
+ struct device *dev = pctrl->dev;
+ u32 state;
+
+ state = config & MPFS_PINCTRL_DT_FUNC_MASK;
+ state <<= pin;
+
+ dev_dbg(dev, "Setting pin %u reg %x offset %u func %x\n", pin, MPFS_PINCTRL_IOMUX0_REG, pin, state);
+
+ regmap_set_bits(pctrl->regmap, MPFS_PINCTRL_IOMUX0_REG, state);
+
+ return 0;
+}
+
+static int mpfs_iomux0_pinctrl_set_mux(struct pinctrl_dev *pctrl_dev, unsigned int fsel, unsigned int gsel)
+{
+ struct mpfs_iomux0_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrl_dev);
+ const struct group_desc *group;
+ struct mpfs_iomux0_pinctrl_mux_config *configs;
+ int ret = -EINVAL;
+
+ group = pinctrl_generic_get_group(pctrl_dev, gsel);
+ if (!group)
+ return -EINVAL;
+
+ configs = group->data;
+
+ for (int i = 0; i < group->grp.npins; i++) {
+ u8 pin = configs[i].pin;
+ u32 config = configs[i].config;
+
+ ret = mpfs_iomux0_pinctrl_set_pin_func(pctrl, pin, config);
+ }
+
+ return ret;
+}
+
+static const struct pinmux_ops mpfs_iomux0_pinctrl_pinmux_ops = {
+ .get_functions_count = pinmux_generic_get_function_count,
+ .get_function_name = pinmux_generic_get_function_name,
+ .get_function_groups = pinmux_generic_get_function_groups,
+ .set_mux = mpfs_iomux0_pinctrl_set_mux,
+};
+
+static const struct pinctrl_pin_desc mpfs_iomux0_pinctrl_pins[] = {
+ PINCTRL_PIN(0, "spi0"),
+ PINCTRL_PIN(1, "spi1"),
+ PINCTRL_PIN(2, "i2c0"),
+ PINCTRL_PIN(3, "i2c1"),
+ PINCTRL_PIN(4, "can0"),
+ PINCTRL_PIN(5, "can1"),
+ PINCTRL_PIN(6, "qspi"),
+ PINCTRL_PIN(7, "uart0"),
+ PINCTRL_PIN(8, "uart1"),
+ PINCTRL_PIN(9, "uart2"),
+ PINCTRL_PIN(10, "uart3"),
+ PINCTRL_PIN(11, "uart4"),
+ PINCTRL_PIN(12, "mdio0"),
+ PINCTRL_PIN(13, "mdio1"),
+
+};
+
+static int mpfs_iomux0_pinctrl_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mpfs_iomux0_pinctrl *pctrl;
+ int ret;
+
+ pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL);
+ if (!pctrl)
+ return -ENOMEM;
+
+ pctrl->regmap = device_node_to_regmap(pdev->dev.parent->of_node);
+ if (IS_ERR(pctrl->regmap))
+ dev_err_probe(dev, PTR_ERR(pctrl->regmap), "Failed to find syscon regmap\n");
+
+ pctrl->desc.name = dev_name(dev);
+ pctrl->desc.pins = mpfs_iomux0_pinctrl_pins;
+ pctrl->desc.npins = ARRAY_SIZE(mpfs_iomux0_pinctrl_pins);
+ pctrl->desc.pctlops = &mpfs_iomux0_pinctrl_ops;
+ pctrl->desc.pmxops = &mpfs_iomux0_pinctrl_pinmux_ops;
+ pctrl->desc.owner = THIS_MODULE;
+
+ pctrl->dev = dev;
+
+ ret = devm_mutex_init(dev, &pctrl->mutex);
+ if (ret)
+ return ret;
+
+ platform_set_drvdata(pdev, pctrl);
+
+ pctrl->pctrl = devm_pinctrl_register(&pdev->dev, &pctrl->desc, pctrl);
+ if (IS_ERR(pctrl->pctrl))
+ return PTR_ERR(pctrl->pctrl);
+
+ return 0;
+}
+
+static const struct of_device_id mpfs_iomux0_pinctrl_of_match[] = {
+ { .compatible = "microchip,mpfs-pinctrl-iomux0" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mpfs_iomux0_pinctrl_of_match);
+
+static struct platform_driver mpfs_iomux0_pinctrl_driver = {
+ .driver = {
+ .name = "mpfs-pinctrl-iomux0",
+ .of_match_table = mpfs_iomux0_pinctrl_of_match,
+ },
+ .probe = mpfs_iomux0_pinctrl_probe,
+};
+module_platform_driver(mpfs_iomux0_pinctrl_driver);
+
+MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>");
+MODULE_DESCRIPTION("Polarfire SoC iomux0 pinctrl driver");
+MODULE_LICENSE("GPL");
--
2.47.3
On Fri, Sep 26, 2025 at 4:33 PM Conor Dooley <conor@kernel.org> wrote:
> +static const struct pinctrl_pin_desc mpfs_iomux0_pinctrl_pins[] = {
> + PINCTRL_PIN(0, "spi0"),
> + PINCTRL_PIN(1, "spi1"),
> + PINCTRL_PIN(2, "i2c0"),
> + PINCTRL_PIN(3, "i2c1"),
> + PINCTRL_PIN(4, "can0"),
> + PINCTRL_PIN(5, "can1"),
> + PINCTRL_PIN(6, "qspi"),
> + PINCTRL_PIN(7, "uart0"),
> + PINCTRL_PIN(8, "uart1"),
> + PINCTRL_PIN(9, "uart2"),
> + PINCTRL_PIN(10, "uart3"),
> + PINCTRL_PIN(11, "uart4"),
> + PINCTRL_PIN(12, "mdio0"),
> + PINCTRL_PIN(13, "mdio1"),
This looks like it is abusing the API. These things do not look like
"pins" at all, rather these are all groups, right?
Yours,
Linus Walleij
On Wed, Oct 1, 2025 at 1:34 PM Linus Walleij <linus.walleij@linaro.org> wrote:
> On Fri, Sep 26, 2025 at 4:33 PM Conor Dooley <conor@kernel.org> wrote:
>
> > +static const struct pinctrl_pin_desc mpfs_iomux0_pinctrl_pins[] = {
> > + PINCTRL_PIN(0, "spi0"),
> > + PINCTRL_PIN(1, "spi1"),
> > + PINCTRL_PIN(2, "i2c0"),
> > + PINCTRL_PIN(3, "i2c1"),
> > + PINCTRL_PIN(4, "can0"),
> > + PINCTRL_PIN(5, "can1"),
> > + PINCTRL_PIN(6, "qspi"),
> > + PINCTRL_PIN(7, "uart0"),
> > + PINCTRL_PIN(8, "uart1"),
> > + PINCTRL_PIN(9, "uart2"),
> > + PINCTRL_PIN(10, "uart3"),
> > + PINCTRL_PIN(11, "uart4"),
> > + PINCTRL_PIN(12, "mdio0"),
> > + PINCTRL_PIN(13, "mdio1"),
>
> This looks like it is abusing the API. These things do not look like
> "pins" at all, rather these are all groups, right?
Or maybe they are rather functions. Like there is a function spi0
that can be mapped to a set of pins such as spi0_grp = <1, 2, 3, 4...>
I recommend a refresher with
https://docs.kernel.org/driver-api/pin-control.html
and work from there, and avoid looking too much at other
drivers that don't necessarily do the right thing.
Yours,
Linus Walleij
On Wed, Oct 01, 2025 at 01:36:49PM +0200, Linus Walleij wrote:
> On Wed, Oct 1, 2025 at 1:34 PM Linus Walleij <linus.walleij@linaro.org> wrote:
> > On Fri, Sep 26, 2025 at 4:33 PM Conor Dooley <conor@kernel.org> wrote:
> >
> > > +static const struct pinctrl_pin_desc mpfs_iomux0_pinctrl_pins[] = {
> > > + PINCTRL_PIN(0, "spi0"),
> > > + PINCTRL_PIN(1, "spi1"),
> > > + PINCTRL_PIN(2, "i2c0"),
> > > + PINCTRL_PIN(3, "i2c1"),
> > > + PINCTRL_PIN(4, "can0"),
> > > + PINCTRL_PIN(5, "can1"),
> > > + PINCTRL_PIN(6, "qspi"),
> > > + PINCTRL_PIN(7, "uart0"),
> > > + PINCTRL_PIN(8, "uart1"),
> > > + PINCTRL_PIN(9, "uart2"),
> > > + PINCTRL_PIN(10, "uart3"),
> > > + PINCTRL_PIN(11, "uart4"),
> > > + PINCTRL_PIN(12, "mdio0"),
> > > + PINCTRL_PIN(13, "mdio1"),
> >
> > This looks like it is abusing the API. These things do not look like
> > "pins" at all, rather these are all groups, right?
>
> Or maybe they are rather functions. Like there is a function spi0
> that can be mapped to a set of pins such as spi0_grp = <1, 2, 3, 4...>
They're not actually package pins at all that are being configured here,
it's internal routing inside the FPGA. It does operate on a function
level, but I don't think there's a neat mapping to the pinctrl subsystem
which (AFAIU) considers functions to contain groups, which in turn
contain pins. I suppose it could be thought of that, for example, spi0
is actually a function containing 4 (or 5, don't ask - or do if you want
to read a rant about pointlessly confusing design) "pins" in 1 group.
If I could just work in terms of functions only, and avoid groups or
pins at all, feels (to me ofc) like it'd maybe match the purpose of this
aspect of the hardware better.
> I recommend a refresher with
> https://docs.kernel.org/driver-api/pin-control.html
> and work from there, and avoid looking too much at other
> drivers that don't necessarily do the right thing.
On Wed, Oct 1, 2025 at 5:45 PM Conor Dooley <conor@kernel.org> wrote:
> They're not actually package pins at all that are being configured here,
> it's internal routing inside the FPGA. It does operate on a function
> level, but I don't think there's a neat mapping to the pinctrl subsystem
> which (AFAIU) considers functions to contain groups, which in turn
> contain pins. I suppose it could be thought of that, for example, spi0
> is actually a function containing 4 (or 5, don't ask - or do if you want
> to read a rant about pointlessly confusing design) "pins" in 1 group.
>
> If I could just work in terms of functions only, and avoid groups or
> pins at all, feels (to me ofc) like it'd maybe match the purpose of this
> aspect of the hardware better.
What I would ask myself is whether the abstraction fits the bill,
like if there is a natural set of functions in the silicon, then the code
should reflect that.
When it comes to those:
+static const struct pinctrl_pin_desc mpfs_iomux0_pinctrl_pins[] = {
+ PINCTRL_PIN(0, "spi0"),
+ PINCTRL_PIN(1, "spi1"),
At least be careful with the nouns used: are they really "pins"?
Should they be described as "pins"?
Maybe it is best to come up with some custom struct if not?
Yours,
Linus Walleij
On Mon, Oct 13, 2025 at 01:02:35PM +0200, Linus Walleij wrote:
> On Wed, Oct 1, 2025 at 5:45 PM Conor Dooley <conor@kernel.org> wrote:
>
> > They're not actually package pins at all that are being configured here,
> > it's internal routing inside the FPGA. It does operate on a function
> > level, but I don't think there's a neat mapping to the pinctrl subsystem
> > which (AFAIU) considers functions to contain groups, which in turn
> > contain pins. I suppose it could be thought of that, for example, spi0
> > is actually a function containing 4 (or 5, don't ask - or do if you want
> > to read a rant about pointlessly confusing design) "pins" in 1 group.
> >
> > If I could just work in terms of functions only, and avoid groups or
> > pins at all, feels (to me ofc) like it'd maybe match the purpose of this
> > aspect of the hardware better.
>
> What I would ask myself is whether the abstraction fits the bill,
> like if there is a natural set of functions in the silicon, then the code
> should reflect that.
>
> When it comes to those:
>
> +static const struct pinctrl_pin_desc mpfs_iomux0_pinctrl_pins[] = {
> + PINCTRL_PIN(0, "spi0"),
> + PINCTRL_PIN(1, "spi1"),
>
> At least be careful with the nouns used: are they really "pins"?
> Should they be described as "pins"?
I think that my choices if talking to someone outside of the context of
the structure of the pinctrl subsystem would be functions (for what's in
the driver as pins) and routings (instead of groups). pinctrl's function
doesn't really do very much in this context, although the devicetree
function and groups I think actually work fairly well: "function ABC is
assigned to pin 1".
Regarding nouns, I think it depends on how far you go with that...
> Maybe it is best to come up with some custom struct if not?
...because taking that to an extreme means forsaking a lot (all) of the
common pinctrl infrastructure, no? If it's just choosing more apt names
for variables/functions in the driver or redefining PINCTRL_PIN to be
something more suitably named, then sure. But if I have to avoid using
pinctrl_pin_desc et al, am I going to lose out on a bunch of useful
common code?
I'll send my v2 on tomorrow probably, and we can talk about what exact
changes should be made there?
On Mon, Oct 13, 2025 at 1:42 PM Conor Dooley <conor@kernel.org> wrote: > I'll send my v2 on tomorrow probably, and we can talk about what exact > changes should be made there? Sounds like a deal, I try to go with the IETF motto "rough consensus and running code" when it comes to pin control. Yours, Linus Walleij
© 2016 - 2025 Red Hat, Inc.