[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