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 <[email protected]>
---
 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


Reply via email to