[PATCH v1 05/10] pinctrl: ocelot: port Microsemi/Microchip Ocelot pinctrl from Linux

Oleksij Rempel o.rempel at pengutronix.de
Thu Jun 11 22:59:21 PDT 2026


Port drivers/pinctrl/pinctrl-ocelot.c from the Linux kernel as a
pinmux + GPIO controller for the Microsemi/Microchip Ocelot SoC
family. The drop is reduced to the LAN969X (lan9691) subset that
barebox needs to bring the board up; the other SoC variants are
dropped to keep the file reviewable. Identifier and structure layout
are kept compatible with Linux so future feature backports stay
mechanical.

Ported from Linux drivers/pinctrl/pinctrl-ocelot.c at tag v7.1-rc7

Signed-off-by: Oleksij Rempel <o.rempel at pengutronix.de>
---
 drivers/pinctrl/Kconfig          |  10 +
 drivers/pinctrl/Makefile         |   1 +
 drivers/pinctrl/pinctrl-ocelot.c | 574 +++++++++++++++++++++++++++++++
 3 files changed, 585 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-ocelot.c

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 1d237db106ba..8cb6e2efebdf 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -111,6 +111,16 @@ config PINCTRL_STM32
 	help
 	  Pinmux and GPIO controller found on STM32 family
 
+config PINCTRL_OCELOT
+	bool "Pinctrl driver for the Microsemi Ocelot family of SoCs"
+	depends on OFDEVICE
+	select GPIOLIB
+	help
+	  Pinmux + GPIO controller driver for the Microsemi/Microchip Ocelot
+	  family. Currently implements the LAN969X (lan9691) subset matched
+	  with the same identifiers as Linux's drivers/pinctrl/pinctrl-ocelot.c
+	  so future Linux patches backport with minimal churn.
+
 source "drivers/pinctrl/sunxi/Kconfig"
 endif
 
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 3bc718d35539..6dd11c0d64b0 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o
 obj-$(CONFIG_PINCTRL_TEGRA_XUSB) += pinctrl-tegra-xusb.o
 obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o
 obj-$(CONFIG_PINCTRL_STM32) += pinctrl-stm32.o
+obj-$(CONFIG_PINCTRL_OCELOT) += pinctrl-ocelot.o
 
 obj-$(CONFIG_ARCH_MVEBU) += mvebu/
 obj-$(CONFIG_ARCH_SUNXI) += sunxi/
diff --git a/drivers/pinctrl/pinctrl-ocelot.c b/drivers/pinctrl/pinctrl-ocelot.c
new file mode 100644
index 000000000000..9445c7bd85d7
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-ocelot.c
@@ -0,0 +1,574 @@
+// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
+/*
+ * Microchip ocelot family pinmux driver — LAN969X subset.
+ *
+ * Copyright (c) 2017 Microsemi Corporation
+ * Copyright (c) 2025 Microchip Technology Inc.
+ *
+ * Ported from Linux drivers/pinctrl/pinctrl-ocelot.c. Identifiers, struct
+ * layouts, and pin/function tables are kept aligned with Linux so future
+ * fixes can be backported with minimal context churn.
+ */
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <gpio.h>
+#include <io.h>
+#include <of.h>
+#include <pinctrl.h>
+#include <linux/bitfield.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+
+#define OCELOT_FUNC_PER_PIN	4
+
+/* GPIO standard registers - byte offsets, identical to Linux. REG/REG_ALT
+ * scale these by info->stride to step over the 32-bit-bank groups.
+ */
+#define OCELOT_GPIO_OUT_SET	0x0
+#define OCELOT_GPIO_OUT_CLR	0x4
+#define OCELOT_GPIO_OUT		0x8
+#define OCELOT_GPIO_IN		0xc
+#define OCELOT_GPIO_OE		0x10
+#define OCELOT_GPIO_INTR	0x14
+#define OCELOT_GPIO_INTR_ENA	0x18
+#define OCELOT_GPIO_INTR_IDENT	0x1c
+#define OCELOT_GPIO_ALT0	0x20
+#define OCELOT_GPIO_ALT1	0x24
+#define OCELOT_GPIO_SD_MAP	0x28
+
+/*
+ * Function enum — LAN969X subset of Linux's full enum, kept in Linux's
+ * ordering. Other SoCs' FUNC_* entries are intentionally omitted; add
+ * them when porting more SoCs.
+ */
+enum {
+	FUNC_CAN0_a,
+	FUNC_CAN0_b,
+	FUNC_CAN1,
+	FUNC_CLKMON,
+	FUNC_NONE,
+	FUNC_FAN,
+	FUNC_FC,
+	FUNC_FC_SHRD,
+	FUNC_FUSA,
+	FUNC_GPIO,
+	FUNC_IRQ0,
+	FUNC_IRQ1,
+	FUNC_IRQ3,
+	FUNC_IRQ4,
+	FUNC_MIIM,
+	FUNC_MIIM_Sa,
+	FUNC_MIIM_IRQ,
+	FUNC_PCIE_PERST,
+	FUNC_PTPSYNC_0,
+	FUNC_PTPSYNC_1,
+	FUNC_PTPSYNC_2,
+	FUNC_PTPSYNC_3,
+	FUNC_PTPSYNC_4,
+	FUNC_PTPSYNC_5,
+	FUNC_PTPSYNC_6,
+	FUNC_PTPSYNC_7,
+	FUNC_QSPI1,
+	FUNC_R,
+	FUNC_SD,
+	FUNC_SFP_SD,
+	FUNC_SGPIO_a,
+	FUNC_SYNCE,
+	FUNC_TWI,
+	FUNC_USB_POWER,
+	FUNC_USB2PHY_RST,
+	FUNC_USB_OVER_DETECT,
+	FUNC_USB_ULPI,
+	FUNC_EMMC_SD,
+	FUNC_MAX
+};
+
+static const char *const ocelot_function_names[] = {
+	[FUNC_CAN0_a]		= "can0_a",
+	[FUNC_CAN0_b]		= "can0_b",
+	[FUNC_CAN1]		= "can1",
+	[FUNC_CLKMON]		= "clkmon",
+	[FUNC_NONE]		= "none",
+	[FUNC_FAN]		= "fan",
+	[FUNC_FC]		= "fc",
+	[FUNC_FC_SHRD]		= "fc_shrd",
+	[FUNC_FUSA]		= "fusa",
+	[FUNC_GPIO]		= "gpio",
+	[FUNC_IRQ0]		= "irq0",
+	[FUNC_IRQ1]		= "irq1",
+	[FUNC_IRQ3]		= "irq3",
+	[FUNC_IRQ4]		= "irq4",
+	[FUNC_MIIM]		= "miim",
+	[FUNC_MIIM_Sa]		= "miim_slave_a",
+	[FUNC_MIIM_IRQ]		= "miim_irq",
+	[FUNC_PCIE_PERST]	= "pcie_perst",
+	[FUNC_PTPSYNC_0]	= "ptpsync_0",
+	[FUNC_PTPSYNC_1]	= "ptpsync_1",
+	[FUNC_PTPSYNC_2]	= "ptpsync_2",
+	[FUNC_PTPSYNC_3]	= "ptpsync_3",
+	[FUNC_PTPSYNC_4]	= "ptpsync_4",
+	[FUNC_PTPSYNC_5]	= "ptpsync_5",
+	[FUNC_PTPSYNC_6]	= "ptpsync_6",
+	[FUNC_PTPSYNC_7]	= "ptpsync_7",
+	[FUNC_QSPI1]		= "qspi1",
+	[FUNC_R]		= "reserved",
+	[FUNC_SD]		= "sd",
+	[FUNC_SFP_SD]		= "sfp_sd",
+	[FUNC_SGPIO_a]		= "sgpio_a",
+	[FUNC_SYNCE]		= "synce",
+	[FUNC_TWI]		= "twi",
+	[FUNC_USB_POWER]	= "usb_power",
+	[FUNC_USB2PHY_RST]	= "usb2phy_rst",
+	[FUNC_USB_OVER_DETECT]	= "usb_over_detect",
+	[FUNC_USB_ULPI]		= "usb_ulpi",
+	[FUNC_EMMC_SD]		= "emmc_sd",
+};
+
+struct ocelot_pin_caps {
+	unsigned int pin;
+	unsigned char functions[OCELOT_FUNC_PER_PIN];
+	unsigned char a_functions[OCELOT_FUNC_PER_PIN];	/* Additional functions */
+};
+
+struct ocelot_pin_desc {
+	unsigned int number;
+	const char *name;
+	struct ocelot_pin_caps *drv_data;
+};
+
+#define LAN969X_P(p, f0, f1, f2, f3, f4, f5, f6, f7)           \
+static struct ocelot_pin_caps lan969x_pin_##p = {              \
+	.pin = p,                                              \
+	.functions = {                                         \
+		FUNC_##f0, FUNC_##f1, FUNC_##f2,               \
+		FUNC_##f3                                      \
+	},                                                     \
+	.a_functions = {                                       \
+		FUNC_##f4, FUNC_##f5, FUNC_##f6,               \
+		FUNC_##f7                                      \
+	},                                                     \
+}
+
+/* Pinmuxing table taken from data sheet */
+/*        Pin   FUNC0      FUNC1   FUNC2         FUNC3                  FUNC4     FUNC5      FUNC6        FUNC7 */
+LAN969X_P(0,    GPIO,      IRQ0,   FC_SHRD,      PCIE_PERST,            NONE,     NONE,      NONE,        R);
+LAN969X_P(1,    GPIO,      IRQ1,   FC_SHRD,       USB_POWER,            NONE,     NONE,      NONE,        R);
+LAN969X_P(2,    GPIO,        FC,      NONE,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(3,    GPIO,        FC,      NONE,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(4,    GPIO,        FC,      NONE,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(5,    GPIO,   SGPIO_a,      NONE,          CLKMON,            NONE,     NONE,      NONE,        R);
+LAN969X_P(6,    GPIO,   SGPIO_a,      NONE,          CLKMON,            NONE,     NONE,      NONE,        R);
+LAN969X_P(7,    GPIO,   SGPIO_a,      NONE,          CLKMON,            NONE,     NONE,      NONE,        R);
+LAN969X_P(8,    GPIO,   SGPIO_a,      NONE,          CLKMON,            NONE,     NONE,      NONE,        R);
+LAN969X_P(9,    GPIO,      MIIM,   MIIM_Sa,          CLKMON,            NONE,     NONE,      NONE,        R);
+LAN969X_P(10,   GPIO,      MIIM,   MIIM_Sa,          CLKMON,            NONE,     NONE,      NONE,        R);
+LAN969X_P(11,   GPIO,  MIIM_IRQ,   MIIM_Sa,          CLKMON,            NONE,     NONE,      NONE,        R);
+LAN969X_P(12,   GPIO,      IRQ3,   FC_SHRD,     USB2PHY_RST,            NONE,     NONE,      NONE,        R);
+LAN969X_P(13,   GPIO,      IRQ4,   FC_SHRD, USB_OVER_DETECT,            NONE,     NONE,      NONE,        R);
+LAN969X_P(14,   GPIO,   EMMC_SD,     QSPI1,              FC,            NONE,     NONE,      NONE,        R);
+LAN969X_P(15,   GPIO,   EMMC_SD,     QSPI1,              FC,            NONE,     NONE,      NONE,        R);
+LAN969X_P(16,   GPIO,   EMMC_SD,     QSPI1,              FC,            NONE,     NONE,      NONE,        R);
+LAN969X_P(17,   GPIO,   EMMC_SD,     QSPI1,       PTPSYNC_0,       USB_POWER,     NONE,      NONE,        R);
+LAN969X_P(18,   GPIO,   EMMC_SD,     QSPI1,       PTPSYNC_1,     USB2PHY_RST,     NONE,      NONE,        R);
+LAN969X_P(19,   GPIO,   EMMC_SD,     QSPI1,       PTPSYNC_2, USB_OVER_DETECT,     NONE,      NONE,        R);
+LAN969X_P(20,   GPIO,   EMMC_SD,      NONE,         FC_SHRD,            NONE,     NONE,      NONE,        R);
+LAN969X_P(21,   GPIO,   EMMC_SD,      NONE,         FC_SHRD,            NONE,     NONE,      NONE,        R);
+LAN969X_P(22,   GPIO,   EMMC_SD,      NONE,         FC_SHRD,            NONE,     NONE,      NONE,        R);
+LAN969X_P(23,   GPIO,   EMMC_SD,      NONE,         FC_SHRD,            NONE,     NONE,      NONE,        R);
+LAN969X_P(24,   GPIO,   EMMC_SD,      NONE,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(25,   GPIO,       FAN,      FUSA,          CAN0_a,           QSPI1,     NONE,      NONE,        R);
+LAN969X_P(26,   GPIO,       FAN,      FUSA,          CAN0_a,           QSPI1,     NONE,      NONE,        R);
+LAN969X_P(27,   GPIO,     SYNCE,        FC,            MIIM,           QSPI1,     NONE,      NONE,        R);
+LAN969X_P(28,   GPIO,     SYNCE,        FC,            MIIM,           QSPI1,     NONE,      NONE,        R);
+LAN969X_P(29,   GPIO,     SYNCE,        FC,        MIIM_IRQ,           QSPI1,     NONE,      NONE,        R);
+LAN969X_P(30,   GPIO, PTPSYNC_0,  USB_ULPI,         FC_SHRD,           QSPI1,     NONE,      NONE,        R);
+LAN969X_P(31,   GPIO, PTPSYNC_1,  USB_ULPI,         FC_SHRD,            NONE,     NONE,      NONE,        R);
+LAN969X_P(32,   GPIO, PTPSYNC_2,  USB_ULPI,         FC_SHRD,            NONE,     NONE,      NONE,        R);
+LAN969X_P(33,   GPIO,        SD,  USB_ULPI,         FC_SHRD,            NONE,     NONE,      NONE,        R);
+LAN969X_P(34,   GPIO,        SD,  USB_ULPI,            CAN1,         FC_SHRD,     NONE,      NONE,        R);
+LAN969X_P(35,   GPIO,        SD,  USB_ULPI,            CAN1,         FC_SHRD,     NONE,      NONE,        R);
+LAN969X_P(36,   GPIO,        SD,  USB_ULPI,      PCIE_PERST,         FC_SHRD,     NONE,      NONE,        R);
+LAN969X_P(37,   GPIO,        SD,  USB_ULPI,          CAN0_b,            NONE,     NONE,      NONE,        R);
+LAN969X_P(38,   GPIO,        SD,  USB_ULPI,          CAN0_b,            NONE,     NONE,      NONE,        R);
+LAN969X_P(39,   GPIO,        SD,  USB_ULPI,            MIIM,            NONE,     NONE,      NONE,        R);
+LAN969X_P(40,   GPIO,        SD,  USB_ULPI,            MIIM,            NONE,     NONE,      NONE,        R);
+LAN969X_P(41,   GPIO,        SD,  USB_ULPI,        MIIM_IRQ,            NONE,     NONE,      NONE,        R);
+LAN969X_P(42,   GPIO, PTPSYNC_3,      CAN1,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(43,   GPIO, PTPSYNC_4,      CAN1,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(44,   GPIO, PTPSYNC_5,    SFP_SD,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(45,   GPIO, PTPSYNC_6,    SFP_SD,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(46,   GPIO, PTPSYNC_7,    SFP_SD,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(47,   GPIO,      NONE,    SFP_SD,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(48,   GPIO,      NONE,    SFP_SD,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(49,   GPIO,      NONE,    SFP_SD,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(50,   GPIO,      NONE,    SFP_SD,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(51,   GPIO,      NONE,    SFP_SD,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(52,   GPIO,       FAN,    SFP_SD,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(53,   GPIO,       FAN,    SFP_SD,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(54,   GPIO,     SYNCE,        FC,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(55,   GPIO,     SYNCE,        FC,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(56,   GPIO,     SYNCE,        FC,            NONE,            NONE,     NONE,      NONE,        R);
+LAN969X_P(57,   GPIO,    SFP_SD,   FC_SHRD,             TWI,       PTPSYNC_3,     NONE,      NONE,        R);
+LAN969X_P(58,   GPIO,    SFP_SD,   FC_SHRD,             TWI,       PTPSYNC_4,     NONE,      NONE,        R);
+LAN969X_P(59,   GPIO,    SFP_SD,   FC_SHRD,             TWI,       PTPSYNC_5,     NONE,      NONE,        R);
+LAN969X_P(60,   GPIO,    SFP_SD,   FC_SHRD,             TWI,       PTPSYNC_6,     NONE,      NONE,        R);
+LAN969X_P(61,   GPIO,      MIIM,   FC_SHRD,             TWI,            NONE,     NONE,      NONE,        R);
+LAN969X_P(62,   GPIO,      MIIM,   FC_SHRD,             TWI,            NONE,     NONE,      NONE,        R);
+LAN969X_P(63,   GPIO,  MIIM_IRQ,   FC_SHRD,             TWI,            NONE,     NONE,      NONE,        R);
+LAN969X_P(64,   GPIO,        FC,   FC_SHRD,             TWI,            NONE,     NONE,      NONE,        R);
+LAN969X_P(65,   GPIO,        FC,   FC_SHRD,             TWI,            NONE,     NONE,      NONE,        R);
+LAN969X_P(66,   GPIO,        FC,   FC_SHRD,             TWI,            NONE,     NONE,      NONE,        R);
+
+#define LAN969X_PIN(n) {                                       \
+	.number = n,                                           \
+	.name = "GPIO_"#n,                                     \
+	.drv_data = &lan969x_pin_##n                           \
+}
+
+static const struct ocelot_pin_desc lan969x_pins[] = {
+	LAN969X_PIN(0),  LAN969X_PIN(1),  LAN969X_PIN(2),  LAN969X_PIN(3),
+	LAN969X_PIN(4),  LAN969X_PIN(5),  LAN969X_PIN(6),  LAN969X_PIN(7),
+	LAN969X_PIN(8),  LAN969X_PIN(9),  LAN969X_PIN(10), LAN969X_PIN(11),
+	LAN969X_PIN(12), LAN969X_PIN(13), LAN969X_PIN(14), LAN969X_PIN(15),
+	LAN969X_PIN(16), LAN969X_PIN(17), LAN969X_PIN(18), LAN969X_PIN(19),
+	LAN969X_PIN(20), LAN969X_PIN(21), LAN969X_PIN(22), LAN969X_PIN(23),
+	LAN969X_PIN(24), LAN969X_PIN(25), LAN969X_PIN(26), LAN969X_PIN(27),
+	LAN969X_PIN(28), LAN969X_PIN(29), LAN969X_PIN(30), LAN969X_PIN(31),
+	LAN969X_PIN(32), LAN969X_PIN(33), LAN969X_PIN(34), LAN969X_PIN(35),
+	LAN969X_PIN(36), LAN969X_PIN(37), LAN969X_PIN(38), LAN969X_PIN(39),
+	LAN969X_PIN(40), LAN969X_PIN(41), LAN969X_PIN(42), LAN969X_PIN(43),
+	LAN969X_PIN(44), LAN969X_PIN(45), LAN969X_PIN(46), LAN969X_PIN(47),
+	LAN969X_PIN(48), LAN969X_PIN(49), LAN969X_PIN(50), LAN969X_PIN(51),
+	LAN969X_PIN(52), LAN969X_PIN(53), LAN969X_PIN(54), LAN969X_PIN(55),
+	LAN969X_PIN(56), LAN969X_PIN(57), LAN969X_PIN(58), LAN969X_PIN(59),
+	LAN969X_PIN(60), LAN969X_PIN(61), LAN969X_PIN(62), LAN969X_PIN(63),
+	LAN969X_PIN(64), LAN969X_PIN(65), LAN969X_PIN(66),
+};
+
+struct ocelot_pinctrl_desc {
+	const char *name;
+	const struct ocelot_pin_desc *pins;
+	unsigned int npins;
+};
+
+struct ocelot_pinctrl {
+	struct device *dev;
+	struct pinctrl_device pctl;
+	struct gpio_chip gpio_chip;
+	struct regmap *map;
+	struct regmap *pincfg;
+	const struct ocelot_pinctrl_desc *desc;
+	u8 stride;
+	u8 altm_stride;
+};
+
+#define to_ocelot_pctl(pdev) container_of(pdev, struct ocelot_pinctrl, pctl)
+#define to_ocelot_gpio(gc)   container_of(gc,   struct ocelot_pinctrl, gpio_chip)
+
+static const struct ocelot_pinctrl_desc lan969x_desc = {
+	.name = "lan969x-pinctrl",
+	.pins = lan969x_pins,
+	.npins = ARRAY_SIZE(lan969x_pins),
+};
+
+/*
+ * Register stride: each ALT0/ALT1/ALT2 group of pin-mux bits spans
+ * (info->stride) 32-bit words to cover all pins; the LAN969X 78-pin layout
+ * also uses altm_stride == stride.
+ */
+#define REG(r, info, p) ((r) * (info)->stride + (4 * ((p) / 32)))
+#define REG_ALT(msb, info, p) \
+	(OCELOT_GPIO_ALT0 * (info)->stride + 4 * ((msb) + ((info)->altm_stride * ((p) / 32))))
+
+static int ocelot_pin_function_idx(struct ocelot_pinctrl *info,
+				   unsigned int pin, unsigned int function)
+{
+	struct ocelot_pin_caps *p = info->desc->pins[pin].drv_data;
+	int i;
+
+	for (i = 0; i < OCELOT_FUNC_PER_PIN; i++) {
+		if (function == p->functions[i])
+			return i;
+		if (function == p->a_functions[i])
+			return i + OCELOT_FUNC_PER_PIN;
+	}
+
+	return -1;
+}
+
+/* 3-bit pinmux encoding across ALT0/ALT1/ALT2, used by LAN966X and LAN969X. */
+static int lan969x_pinmux_set_mux(struct ocelot_pinctrl *info,
+				  unsigned int group, unsigned int selector)
+{
+	struct ocelot_pin_caps *pin = info->desc->pins[group].drv_data;
+	unsigned int p = pin->pin % 32;
+	int f;
+
+	f = ocelot_pin_function_idx(info, group, selector);
+	if (f < 0)
+		return -EINVAL;
+
+	/*
+	 * f is encoded on three bits.
+	 * bit 0 of f goes in BIT(pin) of ALT[0], bit 1 of f goes in BIT(pin) of
+	 * ALT[1], bit 2 of f goes in BIT(pin) of ALT[2]
+	 * Note: ALT0/ALT1/ALT2 are organized specially for 78 gpio targets
+	 */
+	regmap_update_bits(info->map, REG_ALT(0, info, pin->pin),
+			   BIT(p), f << p);
+	regmap_update_bits(info->map, REG_ALT(1, info, pin->pin),
+			   BIT(p), (f >> 1) << p);
+	regmap_update_bits(info->map, REG_ALT(2, info, pin->pin),
+			   BIT(p), (f >> 2) << p);
+
+	return 0;
+}
+
+/* ----- GPIO ops ----- */
+
+static int ocelot_gpio_get(struct gpio_chip *chip, unsigned int offset)
+{
+	struct ocelot_pinctrl *info = to_ocelot_gpio(chip);
+	unsigned int val;
+
+	regmap_read(info->map, REG(OCELOT_GPIO_IN, info, offset), &val);
+
+	return !!(val & BIT(offset % 32));
+}
+
+static int ocelot_gpio_set(struct gpio_chip *chip, unsigned int offset,
+			   int value)
+{
+	struct ocelot_pinctrl *info = to_ocelot_gpio(chip);
+
+	if (value)
+		regmap_write(info->map, REG(OCELOT_GPIO_OUT_SET, info, offset),
+			     BIT(offset % 32));
+	else
+		regmap_write(info->map, REG(OCELOT_GPIO_OUT_CLR, info, offset),
+			     BIT(offset % 32));
+
+	return 0;
+}
+
+static int ocelot_gpio_get_direction(struct gpio_chip *chip,
+				     unsigned int offset)
+{
+	struct ocelot_pinctrl *info = to_ocelot_gpio(chip);
+	unsigned int val;
+
+	regmap_read(info->map, REG(OCELOT_GPIO_OE, info, offset), &val);
+
+	/* GPIOF_DIR_OUT == 0, GPIOF_DIR_IN == 1 */
+	return (val & BIT(offset % 32)) ? 0 : 1;
+}
+
+static int ocelot_gpio_direction_input(struct gpio_chip *chip,
+				       unsigned int offset)
+{
+	struct ocelot_pinctrl *info = to_ocelot_gpio(chip);
+	unsigned int pin = BIT(offset % 32);
+
+	regmap_update_bits(info->map, REG(OCELOT_GPIO_OE, info, offset),
+			   pin, 0);
+
+	return 0;
+}
+
+static int ocelot_gpio_direction_output(struct gpio_chip *chip,
+					unsigned int offset, int value)
+{
+	struct ocelot_pinctrl *info = to_ocelot_gpio(chip);
+	unsigned int pin = BIT(offset % 32);
+
+	if (value)
+		regmap_write(info->map, REG(OCELOT_GPIO_OUT_SET, info, offset),
+			     pin);
+	else
+		regmap_write(info->map, REG(OCELOT_GPIO_OUT_CLR, info, offset),
+			     pin);
+
+	regmap_update_bits(info->map, REG(OCELOT_GPIO_OE, info, offset),
+			   pin, pin);
+
+	return 0;
+}
+
+static struct gpio_ops ocelot_gpio_ops = {
+	.direction_input = ocelot_gpio_direction_input,
+	.direction_output = ocelot_gpio_direction_output,
+	.get_direction = ocelot_gpio_get_direction,
+	.get = ocelot_gpio_get,
+	.set = ocelot_gpio_set,
+};
+
+/* ----- pinctrl ops ----- */
+
+static int ocelot_lookup_function(const char *name)
+{
+	int i;
+
+	for (i = 0; i < FUNC_MAX; i++) {
+		if (ocelot_function_names[i] &&
+		    strcmp(name, ocelot_function_names[i]) == 0)
+			return i;
+	}
+	return -1;
+}
+
+static int ocelot_lookup_pin(struct ocelot_pinctrl *info, const char *name)
+{
+	unsigned int i;
+
+	for (i = 0; i < info->desc->npins; i++)
+		if (strcmp(info->desc->pins[i].name, name) == 0)
+			return i;
+	return -1;
+}
+
+static int ocelot_pinctrl_set_state(struct pinctrl_device *pdev,
+				    struct device_node *np)
+{
+	struct ocelot_pinctrl *info = to_ocelot_pctl(pdev);
+	const char *func_name;
+	const char **pin_names;
+	int func, ret, n, i;
+
+	ret = of_property_read_string(np, "function", &func_name);
+	if (ret < 0)
+		return 0;
+
+	func = ocelot_lookup_function(func_name);
+	if (func < 0) {
+		dev_err(pdev->dev, "unknown function %s\n", func_name);
+		return -EINVAL;
+	}
+
+	n = of_property_count_strings(np, "pins");
+	if (n <= 0)
+		return 0;
+
+	pin_names = xzalloc(n * sizeof(*pin_names));
+	ret = of_property_read_string_array(np, "pins", pin_names, n);
+	if (ret < 0)
+		goto out;
+
+	for (i = 0; i < n; i++) {
+		int idx = ocelot_lookup_pin(info, pin_names[i]);
+
+		if (idx < 0) {
+			dev_err(pdev->dev, "unknown pin %s\n", pin_names[i]);
+			ret = -EINVAL;
+			goto out;
+		}
+		ret = lan969x_pinmux_set_mux(info, idx, func);
+		if (ret < 0) {
+			dev_err(pdev->dev,
+				"failed to set pin %s to function %s\n",
+				pin_names[i], func_name);
+			goto out;
+		}
+	}
+	ret = 0;
+out:
+	free(pin_names);
+	return ret;
+}
+
+static struct pinctrl_ops ocelot_pinctrl_ops = {
+	.set_state = ocelot_pinctrl_set_state,
+};
+
+/* ----- probe ----- */
+
+static const struct regmap_config ocelot_pinctrl_regmap_config = {
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 4,
+};
+
+static int ocelot_pinctrl_probe(struct device *dev)
+{
+	struct ocelot_pinctrl *info;
+	const struct ocelot_pinctrl_desc *desc;
+	struct resource *iores;
+	void __iomem *base;
+	int ret;
+
+	desc = device_get_match_data(dev);
+	if (!desc)
+		return -EINVAL;
+
+	info = xzalloc(sizeof(*info));
+	info->dev = dev;
+	info->desc = desc;
+	info->stride = 1 + (desc->npins - 1) / 32;
+	info->altm_stride = info->stride;
+
+	iores = dev_request_mem_resource(dev, 0);
+	if (IS_ERR(iores)) {
+		ret = PTR_ERR(iores);
+		goto err_free;
+	}
+	base = IOMEM(iores->start);
+
+	info->map = regmap_init_mmio(dev, base, &ocelot_pinctrl_regmap_config);
+	if (IS_ERR(info->map)) {
+		ret = PTR_ERR(info->map);
+		goto err_free;
+	}
+
+	/* PINCFG resource (optional). */
+	iores = dev_request_mem_resource(dev, 1);
+	if (!IS_ERR(iores)) {
+		void __iomem *pincfg_base = IOMEM(iores->start);
+
+		info->pincfg = regmap_init_mmio(dev, pincfg_base,
+						&ocelot_pinctrl_regmap_config);
+		if (IS_ERR(info->pincfg))
+			info->pincfg = NULL;
+	}
+
+	info->pctl.dev = dev;
+	info->pctl.ops = &ocelot_pinctrl_ops;
+	info->pctl.base = 0;
+	info->pctl.npins = desc->npins;
+	ret = pinctrl_register(&info->pctl);
+	if (ret) {
+		dev_err(dev, "failed to register pinctrl: %d\n", ret);
+		goto err_free;
+	}
+
+	info->gpio_chip.dev = dev;
+	info->gpio_chip.base = -1;
+	info->gpio_chip.ngpio = desc->npins;
+	info->gpio_chip.ops = &ocelot_gpio_ops;
+	ret = gpiochip_add(&info->gpio_chip);
+	if (ret) {
+		dev_err(dev, "failed to register gpio chip: %d\n", ret);
+		pinctrl_unregister(&info->pctl);
+		goto err_free;
+	}
+
+	dev_info(dev, "%s registered: %u pins\n", desc->name, desc->npins);
+	return 0;
+
+err_free:
+	free(info);
+	return ret;
+}
+
+static const struct of_device_id ocelot_pinctrl_of_match[] = {
+	{ .compatible = "microchip,lan9691-pinctrl", .data = &lan969x_desc },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ocelot_pinctrl_of_match);
+
+static struct driver ocelot_pinctrl_driver = {
+	.name = "ocelot-pinctrl",
+	.probe = ocelot_pinctrl_probe,
+	.of_compatible = DRV_OF_COMPAT(ocelot_pinctrl_of_match),
+};
+coredevice_platform_driver(ocelot_pinctrl_driver);
-- 
2.47.3




More information about the barebox mailing list