From: Rob Herring <robh@kernel.org>
To: "Théo Lebrun" <theo.lebrun@bootlin.com>
Cc: "Gregory CLEMENT" <gregory.clement@bootlin.com>,
"Michael Turquette" <mturquette@baylibre.com>,
"Stephen Boyd" <sboyd@kernel.org>,
"Krzysztof Kozlowski" <krzysztof.kozlowski+dt@linaro.org>,
"Conor Dooley" <conor+dt@kernel.org>,
"Thomas Bogendoerfer" <tsbogend@alpha.franken.de>,
"Linus Walleij" <linus.walleij@linaro.org>,
"Rafał Miłecki" <rafal@milecki.pl>,
"Philipp Zabel" <p.zabel@pengutronix.de>,
"Vladimir Kondratiev" <vladimir.kondratiev@mobileye.com>,
linux-mips@vger.kernel.org, linux-clk@vger.kernel.org,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
"Thomas Petazzoni" <thomas.petazzoni@bootlin.com>,
"Tawfik Bayouk" <tawfik.bayouk@mobileye.com>,
linux-gpio@vger.kernel.org
Subject: Re: [PATCH v3 10/17] pinctrl: eyeq5: add platform driver
Date: Wed, 24 Jan 2024 09:19:49 -0600 [thread overview]
Message-ID: <20240124151949.GB930997-robh@kernel.org> (raw)
In-Reply-To: <20240123-mbly-clk-v3-10-392b010b8281@bootlin.com>
On Tue, Jan 23, 2024 at 07:46:55PM +0100, Théo Lebrun wrote:
> Add the Mobileye EyeQ5 pin controller driver. It might grow to add later
> support of other platforms from Mobileye. It belongs to a syscon region
> called OLB.
>
> Existing pins and their function live statically in the driver code
> rather than in the devicetree, see compatible match data.
>
> Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>
> ---
> MAINTAINERS | 1 +
> drivers/pinctrl/Kconfig | 15 +
> drivers/pinctrl/Makefile | 1 +
> drivers/pinctrl/pinctrl-eyeq5.c | 595 ++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 612 insertions(+)
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index dd3b5834386f..9c423a4feb86 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -14794,6 +14794,7 @@ F: arch/mips/boot/dts/mobileye/
> F: arch/mips/configs/eyeq5_defconfig
> F: arch/mips/mobileye/board-epm5.its.S
> F: drivers/clk/clk-eyeq5.c
> +F: drivers/pinctrl/pinctrl-eyeq5.c
> F: drivers/reset/reset-eyeq5.c
> F: include/dt-bindings/clock/mobileye,eyeq5-clk.h
> F: include/dt-bindings/soc/mobileye,eyeq5.h
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 8163a5983166..abe94de85b3d 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -195,6 +195,21 @@ config PINCTRL_EQUILIBRIUM
> desired pin functions, configure GPIO attributes for LGM SoC pins.
> Pin muxing and pin config settings are retrieved from device tree.
>
> +config PINCTRL_EYEQ5
> + bool "Mobileye EyeQ5 pinctrl driver"
> + depends on OF
> + depends on MACH_EYEQ5 || COMPILE_TEST
> + select PINMUX
> + select GENERIC_PINCONF
> + select MFD_SYSCON
> + default MACH_EYEQ5
> + help
> + Pin controller driver for the Mobileye EyeQ5 platform. It does both
> + pin config & pin muxing. It does not handle GPIO.
> +
> + Pin muxing supports two functions for each pin: first is GPIO, second
> + is pin-dependent. Pin config is about bias & drive strength.
> +
> config PINCTRL_GEMINI
> bool
> depends on ARCH_GEMINI
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index 1071f301cc70..0033940914d9 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -22,6 +22,7 @@ obj-$(CONFIG_PINCTRL_DA850_PUPD) += pinctrl-da850-pupd.o
> obj-$(CONFIG_PINCTRL_DA9062) += pinctrl-da9062.o
> obj-$(CONFIG_PINCTRL_DIGICOLOR) += pinctrl-digicolor.o
> obj-$(CONFIG_PINCTRL_EQUILIBRIUM) += pinctrl-equilibrium.o
> +obj-$(CONFIG_PINCTRL_EYEQ5) += pinctrl-eyeq5.o
> obj-$(CONFIG_PINCTRL_GEMINI) += pinctrl-gemini.o
> obj-$(CONFIG_PINCTRL_INGENIC) += pinctrl-ingenic.o
> obj-$(CONFIG_PINCTRL_K210) += pinctrl-k210.o
> diff --git a/drivers/pinctrl/pinctrl-eyeq5.c b/drivers/pinctrl/pinctrl-eyeq5.c
> new file mode 100644
> index 000000000000..2d8e5b2168bd
> --- /dev/null
> +++ b/drivers/pinctrl/pinctrl-eyeq5.c
> @@ -0,0 +1,595 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Pinctrl driver for the Mobileye EyeQ5 platform.
> + *
> + * The registers are located in a syscon region called OLB. There are two pin
> + * banks, each being controlled by 5 registers (see enum eq5p_regs) for
> + * pull-down, pull-up, drive strength and muxing.
> + *
> + * For each pin, muxing is between two functions: (0) GPIO or (1) another one
> + * that is pin-dependent. Functions are declared statically in this driver.
> + *
> + * We create pinctrl groups that are 1:1 equivalent to pins: each group has a
> + * single pin, and its index/selector is the pin number/offset.
> + *
> + * We use eq5p_ as prefix, as-in "EyeQ5 Pinctrl", but way shorter.
> + *
> + * Copyright (C) 2024 Mobileye Vision Technologies Ltd.
> + */
> +#include <linux/mfd/syscon.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/pinctrl/pinconf-generic.h>
> +#include <linux/pinctrl/pinconf.h>
> +#include <linux/pinctrl/pinctrl.h>
> +#include <linux/pinctrl/pinmux.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/seq_file.h>
> +
> +#include "core.h"
> +#include "pinctrl-utils.h"
> +
> +#define GPIO_FUNC_SELECTOR 0
> +
> +struct eq5p_pinctrl {
> + struct pinctrl_desc desc;
> +
> + struct regmap *olb;
> + const unsigned int *regs; /* array of size EQ5P_REG_MAX */
> +
> + const struct eq5p_function *funcs;
> + unsigned int nfuncs;
> +};
> +
> +struct eq5p_function {
> + const char *name;
> + const char * const *groups;
> + unsigned int ngroups;
> +};
> +
> +/* OLB registers; those are offsets in an array of address offsets. */
> +enum eq5p_regs {
> + EQ5P_PD,
> + EQ5P_PU,
> + EQ5P_DS_LOW,
> + EQ5P_DS_HIGH,
> + EQ5P_IOCR,
> +
> + EQ5P_REG_MAX
> +};
> +
> +static int eq5p_pinctrl_get_groups_count(struct pinctrl_dev *pctldev)
> +{
> + return pctldev->desc->npins;
> +}
> +
> +static const char *eq5p_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
> + unsigned int selector)
> +{
> + return pctldev->desc->pins[selector].name;
> +}
> +
> +static int eq5p_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
> + unsigned int selector,
> + const unsigned int **pins,
> + unsigned int *num_pins)
> +{
> + *pins = &pctldev->desc->pins[selector].number;
> + *num_pins = 1;
> + return 0;
> +}
> +
> +static int eq5p_pinconf_get(struct pinctrl_dev *pctldev, unsigned int offset,
> + unsigned long *config);
> +
> +static void eq5p_pinctrl_pin_dbg_show(struct pinctrl_dev *pctldev,
> + struct seq_file *s,
> + unsigned int offset)
> +{
> + struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> + const char *pin_name = pctrl->desc.pins[offset].name;
> + const char *func_name, *bias;
> + unsigned int val_pd, val_pu, val_iocr;
> + unsigned long ds_config;
> + u32 drive_strength;
> + bool pd, pu;
> + int i, j;
> +
> + /* First, let's get the function name. */
> + regmap_read(pctrl->olb, pctrl->regs[EQ5P_IOCR], &val_iocr);
> + if ((val_iocr & BIT(offset)) == 0) {
> + func_name = pctrl->funcs[GPIO_FUNC_SELECTOR].name;
> + } else {
> + /* All pins have only two functions: GPIO and something else. We
> + * look for this something else.
> + */
> + func_name = NULL;
> + for (i = 0; i < pctrl->nfuncs; i++) {
> + if (i == GPIO_FUNC_SELECTOR)
> + continue;
> +
> + for (j = 0; j < pctrl->funcs[i].ngroups; j++) {
> + /* Groups and pins are the same thing for us. */
> + const char *x = pctrl->funcs[i].groups[j];
> +
> + if (strcmp(x, pin_name) == 0) {
> + func_name = pctrl->funcs[i].name;
> + break;
> + }
> + }
> +
> + if (func_name)
> + break;
> + }
> +
> + /* We have not found the function attached to this pin, this
> + * should never occur as all pins have exactly two functions.
> + */
> + if (!func_name)
> + func_name = "unknown";
> + }
> +
> + /* Second, we retrieve the bias. */
> + regmap_read(pctrl->olb, pctrl->regs[EQ5P_PD], &val_pd);
> + pd = (val_pd & BIT(offset)) != 0;
> + regmap_read(pctrl->olb, pctrl->regs[EQ5P_PU], &val_pu);
> + pu = (val_pu & BIT(offset)) != 0;
> + if (pd && pu)
> + bias = "both";
> + else if (pd && !pu)
> + bias = "pulldown";
> + else if (!pd && pu)
> + bias = "pullup";
> + else
> + bias = "none";
> +
> + /* Third, we get the drive strength. */
> + ds_config = pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH, 0);
> + eq5p_pinconf_get(pctldev, offset, &ds_config);
> + drive_strength = pinconf_to_config_argument(ds_config);
> +
> + seq_printf(s, "function=%s bias=%s drive_strength=%d",
> + func_name, bias, drive_strength);
> +}
> +
> +static const struct pinctrl_ops eq5p_pinctrl_ops = {
> + .get_groups_count = eq5p_pinctrl_get_groups_count,
> + .get_group_name = eq5p_pinctrl_get_group_name,
> + .get_group_pins = eq5p_pinctrl_get_group_pins,
> + .pin_dbg_show = eq5p_pinctrl_pin_dbg_show,
> + .dt_node_to_map = pinconf_generic_dt_node_to_map_pin,
> + .dt_free_map = pinctrl_utils_free_map,
> +};
> +
> +static int eq5p_pinmux_get_functions_count(struct pinctrl_dev *pctldev)
> +{
> + struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +
> + return pctrl->nfuncs;
> +}
> +
> +static const char *eq5p_pinmux_get_function_name(struct pinctrl_dev *pctldev,
> + unsigned int selector)
> +{
> + struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +
> + return pctrl->funcs[selector].name;
> +}
> +
> +static int eq5p_pinmux_get_function_groups(struct pinctrl_dev *pctldev,
> + unsigned int selector,
> + const char * const **groups,
> + unsigned int *num_groups)
> +{
> + struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> +
> + *groups = pctrl->funcs[selector].groups;
> + *num_groups = pctrl->funcs[selector].ngroups;
> + return 0;
> +}
> +
> +static int eq5p_pinmux_set_mux(struct pinctrl_dev *pctldev,
> + unsigned int func_selector, unsigned int offset)
> +{
> + const char *group_name = pctldev->desc->pins[offset].name;
> + struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> + const char *func_name = pctrl->funcs[func_selector].name;
> + bool is_gpio = func_selector == GPIO_FUNC_SELECTOR;
> + unsigned int reg, mask, val;
> +
> + dev_dbg(pctldev->dev, "%s: func=%s group=%s\n", __func__, func_name,
> + group_name);
> +
> + reg = pctrl->regs[EQ5P_IOCR];
> + mask = BIT(offset);
> + val = is_gpio ? 0 : U32_MAX;
> +
> + regmap_update_bits(pctrl->olb, reg, mask, val);
> +
> + return 0;
> +}
> +
> +static int eq5p_pinmux_gpio_request_enable(struct pinctrl_dev *pctldev,
> + struct pinctrl_gpio_range *range,
> + unsigned int offset)
> +{
> + /* Pin offsets and group selectors are the same thing in our case. */
> + return eq5p_pinmux_set_mux(pctldev, GPIO_FUNC_SELECTOR, offset);
> +}
> +
> +static const struct pinmux_ops eq5p_pinmux_ops = {
> + .get_functions_count = eq5p_pinmux_get_functions_count,
> + .get_function_name = eq5p_pinmux_get_function_name,
> + .get_function_groups = eq5p_pinmux_get_function_groups,
> + .set_mux = eq5p_pinmux_set_mux,
> + .gpio_request_enable = eq5p_pinmux_gpio_request_enable,
> + .strict = true,
> +};
> +
> +static int eq5p_pinconf_get(struct pinctrl_dev *pctldev, unsigned int offset,
> + unsigned long *config)
> +{
> + enum pin_config_param param = pinconf_to_config_param(*config);
> + struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> + unsigned int reg, val_pd, val_pu, val_ds;
> + bool pd, pu;
> + u32 arg = 0;
> +
> + regmap_read(pctrl->olb, pctrl->regs[EQ5P_PD], &val_pd);
> + pd = (val_pd & BIT(offset)) != 0;
> + regmap_read(pctrl->olb, pctrl->regs[EQ5P_PU], &val_pu);
> + pu = (val_pu & BIT(offset)) != 0;
> +
> + switch (param) {
> + case PIN_CONFIG_BIAS_DISABLE:
> + arg = !(pd || pu);
> + break;
> + case PIN_CONFIG_BIAS_PULL_DOWN:
> + arg = pd;
> + break;
> + case PIN_CONFIG_BIAS_PULL_UP:
> + arg = pu;
> + break;
> + case PIN_CONFIG_DRIVE_STRENGTH:
> + offset *= 2; /* two bits per pin */
> + if (offset >= 32) {
> + reg = pctrl->regs[EQ5P_DS_HIGH];
> + offset -= 32;
> + } else {
> + reg = pctrl->regs[EQ5P_DS_LOW];
> + }
> + regmap_read(pctrl->olb, reg, &val_ds);
> + arg = (val_ds >> offset) & 0b11;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + *config = pinconf_to_config_packed(param, arg);
> + return 0;
> +}
> +
> +static int eq5p_pinconf_set_drive_strength(struct pinctrl_dev *pctldev,
> + unsigned int offset, u32 arg)
> +{
> + struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> + unsigned int reg, mask, val;
> +
> + if (arg > 3) {
> + dev_err(pctldev->dev, "Unsupported drive strength: %u\n", arg);
> + return -EINVAL;
> + }
> +
> + offset *= 2; /* two bits per pin */
> +
> + if (offset >= 32) {
> + reg = pctrl->regs[EQ5P_DS_HIGH];
> + offset -= 32;
> + } else {
> + reg = pctrl->regs[EQ5P_DS_LOW];
> + }
> +
> + mask = 0b11 << offset;
> + val = arg << offset;
> + regmap_update_bits(pctrl->olb, reg, mask, val);
> + return 0;
> +}
> +
> +static int eq5p_pinconf_set(struct pinctrl_dev *pctldev, unsigned int offset,
> + unsigned long *configs, unsigned int num_configs)
> +{
> + struct eq5p_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
> + const char *pin_name = pctldev->desc->pins[offset].name;
> + struct device *dev = pctldev->dev;
> + unsigned int i, val, reg_pd, reg_pu;
> +
> + val = BIT(offset);
> + reg_pd = pctrl->regs[EQ5P_PD];
> + reg_pu = pctrl->regs[EQ5P_PU];
> +
> + for (i = 0; i < num_configs; i++) {
> + enum pin_config_param param = pinconf_to_config_param(configs[i]);
> + u32 arg = pinconf_to_config_argument(configs[i]);
> +
> + switch (param) {
> + case PIN_CONFIG_BIAS_DISABLE:
> + dev_dbg(dev, "%s: pin=%s BIAS_DISABLE\n",
> + __func__, pin_name);
> + regmap_clear_bits(pctrl->olb, reg_pd, val);
> + regmap_clear_bits(pctrl->olb, reg_pu, val);
> + break;
> + case PIN_CONFIG_BIAS_PULL_DOWN:
> + dev_dbg(dev, "%s: pin=%s BIAS_PULL_DOWN arg=%u\n",
> + __func__, pin_name, arg);
> + if (arg == 0) /* cannot connect to GND */
> + return -EOPNOTSUPP;
> + regmap_set_bits(pctrl->olb, reg_pd, val);
> + regmap_clear_bits(pctrl->olb, reg_pu, val);
> + break;
> + case PIN_CONFIG_BIAS_PULL_UP:
> + dev_dbg(dev, "%s: pin=%s BIAS_PULL_UP arg=%u\n",
> + __func__, pin_name, arg);
> + if (arg == 0) /* cannot connect to VDD */
> + return -EOPNOTSUPP;
> + regmap_clear_bits(pctrl->olb, reg_pd, val);
> + regmap_set_bits(pctrl->olb, reg_pu, val);
> + break;
> + case PIN_CONFIG_DRIVE_STRENGTH:
> + dev_dbg(dev, "%s: pin=%s DRIVE_STRENGTH arg=%u\n",
> + __func__, pin_name, arg);
> + eq5p_pinconf_set_drive_strength(pctldev, offset, arg);
> + break;
> + default:
> + dev_err(dev, "Unsupported pinconf %u\n", param);
> + return -EOPNOTSUPP;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static const struct pinconf_ops eq5p_pinconf_ops = {
> + .is_generic = true,
> + .pin_config_get = eq5p_pinconf_get,
> + .pin_config_set = eq5p_pinconf_set,
> + /* Pins and groups are equivalent in this driver. */
> + .pin_config_group_get = eq5p_pinconf_get,
> + .pin_config_group_set = eq5p_pinconf_set,
> +};
> +
> +/*
> + * Comments to the right of each pin are the "signal name" in the datasheet.
> + */
> +
> +static const struct pinctrl_pin_desc eq5p_pins_a[] = { /* Bank A */
> + PINCTRL_PIN(0, "PA0"), /* A0_TIMER0_CK */
> + PINCTRL_PIN(1, "PA1"), /* A1_TIMER0_EOC */
> + PINCTRL_PIN(2, "PA2"), /* A2_TIMER1_CK */
> + PINCTRL_PIN(3, "PA3"), /* A3_TIMER1_EOC */
> + PINCTRL_PIN(4, "PA4"), /* A4_TIMER2_CK */
> + PINCTRL_PIN(5, "PA5"), /* A5_TIMER2_EOC */
> + PINCTRL_PIN(6, "PA6"), /* A6_TIMER5_EXT_INCAP1 */
> + PINCTRL_PIN(7, "PA7"), /* A7_TIMER5_EXT_INCAP2 */
> + PINCTRL_PIN(8, "PA8"), /* A8_TIMER5_EXT_OUTCMP1 */
> + PINCTRL_PIN(9, "PA9"), /* A9_TIMER5_EXT_OUTCMP2 */
> + PINCTRL_PIN(10, "PA10"), /* A10_UART_0_TX */
> + PINCTRL_PIN(11, "PA11"), /* A11_UART_0_RX */
> + PINCTRL_PIN(12, "PA12"), /* A12_UART_1_TX */
> + PINCTRL_PIN(13, "PA13"), /* A13_UART_1_RX */
> + PINCTRL_PIN(14, "PA14"), /* A14_CAN_0_TX */
> + PINCTRL_PIN(15, "PA15"), /* A15_CAN_0_RX */
> + PINCTRL_PIN(16, "PA16"), /* A16_CAN_1_TX */
> + PINCTRL_PIN(17, "PA17"), /* A17_CAN_1_RX */
> + PINCTRL_PIN(18, "PA18"), /* A18_SPI_0_DO */
> + PINCTRL_PIN(19, "PA19"), /* A19_SPI_0_DI */
> + PINCTRL_PIN(20, "PA20"), /* A20_SPI_0_CK */
> + PINCTRL_PIN(21, "PA21"), /* A21_SPI_0_CS0 */
> + PINCTRL_PIN(22, "PA22"), /* A22_SPI_0_CS1 */
> + PINCTRL_PIN(23, "PA23"), /* A23_SPI_1_DO */
> + PINCTRL_PIN(24, "PA24"), /* A24_SPI_1_DI */
> + PINCTRL_PIN(25, "PA25"), /* A25_SPI_1_CK */
> + PINCTRL_PIN(26, "PA26"), /* A26_SPI_1_CS0 */
> + PINCTRL_PIN(27, "PA27"), /* A27_SPI_1_CS1 */
> + PINCTRL_PIN(28, "PA28"), /* A28_REF_CLK0 */
> +};
> +
> +static const struct pinctrl_pin_desc eq5p_pins_b[] = { /* Bank B */
> + PINCTRL_PIN(0, "PB0"), /* B0_TIMER3_CK */
> + PINCTRL_PIN(1, "PB1"), /* B1_TIMER3_EOC */
> + PINCTRL_PIN(2, "PB2"), /* B2_TIMER4_CK */
> + PINCTRL_PIN(3, "PB3"), /* B3_TIMER4_EOC */
> + PINCTRL_PIN(4, "PB4"), /* B4_TIMER6_EXT_INCAP1 */
> + PINCTRL_PIN(5, "PB5"), /* B5_TIMER6_EXT_INCAP2 */
> + PINCTRL_PIN(6, "PB6"), /* B6_TIMER6_EXT_OUTCMP1 */
> + PINCTRL_PIN(7, "PB7"), /* B7_TIMER6_EXT_OUTCMP2 */
> + PINCTRL_PIN(8, "PB8"), /* B8_UART_2_TX */
> + PINCTRL_PIN(9, "PB9"), /* B9_UART_2_RX */
> + PINCTRL_PIN(10, "PB10"), /* B10_CAN_2_TX */
> + PINCTRL_PIN(11, "PB11"), /* B11_CAN_2_RX */
> + PINCTRL_PIN(12, "PB12"), /* B12_SPI_2_DO */
> + PINCTRL_PIN(13, "PB13"), /* B13_SPI_2_DI */
> + PINCTRL_PIN(14, "PB14"), /* B14_SPI_2_CK */
> + PINCTRL_PIN(15, "PB15"), /* B15_SPI_2_CS0 */
> + PINCTRL_PIN(16, "PB16"), /* B16_SPI_2_CS1 */
> + PINCTRL_PIN(17, "PB17"), /* B17_SPI_3_DO */
> + PINCTRL_PIN(18, "PB18"), /* B18_SPI_3_DI */
> + PINCTRL_PIN(19, "PB19"), /* B19_SPI_3_CK */
> + PINCTRL_PIN(20, "PB20"), /* B20_SPI_3_CS0 */
> + PINCTRL_PIN(21, "PB21"), /* B21_SPI_3_CS1 */
> + PINCTRL_PIN(22, "PB22"), /* B22_MCLK0 */
> +};
> +
> +/* Groups of functions on bank A */
> +static const char * const gpioa_groups[] = {
> + "PA0", "PA1", "PA2", "PA3", "PA4", "PA5", "PA6", "PA7", "PA8", "PA9",
> + "PA10", "PA11", "PA12", "PA13", "PA14", "PA15", "PA16", "PA17", "PA18",
> + "PA19", "PA20", "PA21", "PA22", "PA23", "PA24", "PA25", "PA26", "PA27",
> + "PA28",
> +};
> +static const char * const timer0_groups[] = { "PA0", "PA1" };
> +static const char * const timer1_groups[] = { "PA2", "PA3" };
> +static const char * const timer2_groups[] = { "PA4", "PA5" };
> +static const char * const timer5_groups[] = { "PA6", "PA7", "PA8", "PA9" };
> +static const char * const uart0_groups[] = { "PA10", "PA11" };
> +static const char * const uart1_groups[] = { "PA12", "PA13" };
> +static const char * const can0_groups[] = { "PA14", "PA15" };
> +static const char * const can1_groups[] = { "PA16", "PA17" };
> +static const char * const spi0_groups[] = { "PA18", "PA19", "PA20", "PA21", "PA22" };
> +static const char * const spi1_groups[] = { "PA23", "PA24", "PA25", "PA26", "PA27" };
> +static const char * const refclk0_groups[] = { "PA28" };
> +
> +/* Groups of functions on bank B */
> +static const char * const gpiob_groups[] = {
> + "PB0", "PB1", "PB2", "PB3", "PB4", "PB5", "PB6", "PB7", "PB8", "PB9",
> + "PB10", "PB11", "PB12", "PB13", "PB14", "PB15", "PB16", "PB17", "PB18",
> + "PB19", "PB20", "PB21", "PB22",
> +};
> +static const char * const timer3_groups[] = { "PB0", "PB1" };
> +static const char * const timer4_groups[] = { "PB2", "PB3" };
> +static const char * const timer6_groups[] = { "PB4", "PB5", "PB6", "PB7" };
> +static const char * const uart2_groups[] = { "PB8", "PB9" };
> +static const char * const can2_groups[] = { "PB10", "PB11" };
> +static const char * const spi2_groups[] = { "PB12", "PB13", "PB14", "PB15", "PB16" };
> +static const char * const spi3_groups[] = { "PB17", "PB18", "PB19", "PB20", "PB21" };
> +static const char * const mclk0_groups[] = { "PB22" };
> +
> +#define FUNCTION(a, b) { .name = a, .groups = b, .ngroups = ARRAY_SIZE(b) }
> +
> +static const struct eq5p_function eq5p_functions_a[] = {
> + /* GPIO having a fixed index is depended upon, see GPIO_FUNC_SELECTOR. */
> + FUNCTION("gpio", gpioa_groups),
> +
> + FUNCTION("timer0", timer0_groups),
> + FUNCTION("timer1", timer1_groups),
> + FUNCTION("timer2", timer2_groups),
> + FUNCTION("timer5", timer5_groups),
> + FUNCTION("uart0", uart0_groups),
> + FUNCTION("uart1", uart1_groups),
> + FUNCTION("can0", can0_groups),
> + FUNCTION("can1", can1_groups),
> + FUNCTION("spi0", spi0_groups),
> + FUNCTION("spi1", spi1_groups),
> + FUNCTION("refclk0", refclk0_groups),
> +};
> +
> +static const struct eq5p_function eq5p_functions_b[] = {
> + /* GPIO having a fixed index is depended upon, see GPIO_FUNC_SELECTOR. */
> + FUNCTION("gpio", gpiob_groups),
> +
> + FUNCTION("timer3", timer3_groups),
> + FUNCTION("timer4", timer4_groups),
> + FUNCTION("timer6", timer6_groups),
> + FUNCTION("uart2", uart2_groups),
> + FUNCTION("can2", can2_groups),
> + FUNCTION("spi2", spi2_groups),
> + FUNCTION("spi3", spi3_groups),
> + FUNCTION("mclk0", mclk0_groups),
> +};
> +
> +struct eq5p_match {
> + unsigned int regs[EQ5P_REG_MAX];
> + const struct pinctrl_pin_desc *pins;
> + unsigned int npins;
> + const struct eq5p_function *funcs;
> + unsigned int nfuncs;
> +};
> +
> +static int eq5p_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + struct device_node *parent_np = of_get_parent(np);
> + const struct eq5p_match *match = of_device_get_match_data(dev);
> + struct pinctrl_dev *pctldev;
> + struct eq5p_pinctrl *pctrl;
> + int ret;
> +
> + pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL);
> + if (!pctrl)
> + return -ENOMEM;
> +
> + pctrl->olb = ERR_PTR(-ENODEV);
> + if (parent_np)
> + pctrl->olb = syscon_node_to_regmap(parent_np);
> + if (IS_ERR(pctrl->olb))
> + pctrl->olb = syscon_regmap_lookup_by_phandle(np, "mobileye,olb");
> + if (IS_ERR(pctrl->olb))
> + return PTR_ERR(pctrl->olb);
> +
> + pctrl->regs = match->regs;
> + pctrl->funcs = match->funcs;
> + pctrl->nfuncs = match->nfuncs;
> +
> + pctrl->desc.name = dev_name(dev);
> + pctrl->desc.pins = match->pins;
> + pctrl->desc.npins = match->npins;
> + pctrl->desc.pctlops = &eq5p_pinctrl_ops;
> + pctrl->desc.pmxops = &eq5p_pinmux_ops;
> + pctrl->desc.confops = &eq5p_pinconf_ops;
> + pctrl->desc.owner = THIS_MODULE;
> +
> + ret = devm_pinctrl_register_and_init(dev, &pctrl->desc, pctrl, &pctldev);
> + if (ret) {
> + dev_err(dev, "Failed registering pinctrl device: %d\n", ret);
> + return ret;
> + }
> +
> + ret = pinctrl_enable(pctldev);
> + if (ret) {
> + dev_err(dev, "Failed enabling pinctrl device: %d\n", ret);
> + return ret;
> + }
> +
> + dev_info(dev, "probed\n");
> +
> + return 0;
> +}
> +
> +static const struct eq5p_match eq5p_match_a = {
> + .regs = {
> + [EQ5P_PD] = 0x0C0,
> + [EQ5P_PU] = 0x0C4,
> + [EQ5P_DS_LOW] = 0x0D0,
> + [EQ5P_DS_HIGH] = 0x0D4,
> + [EQ5P_IOCR] = 0x0B0,
> + },
> + .pins = eq5p_pins_a,
> + .npins = ARRAY_SIZE(eq5p_pins_a),
> + .funcs = eq5p_functions_a,
> + .nfuncs = ARRAY_SIZE(eq5p_functions_a),
> +};
> +
> +static const struct eq5p_match eq5p_match_b = {
> + .regs = {
> + [EQ5P_PD] = 0x0C8,
> + [EQ5P_PU] = 0x0CC,
> + [EQ5P_DS_LOW] = 0x0D8,
> + [EQ5P_DS_HIGH] = 0x0DC,
> + [EQ5P_IOCR] = 0x0B4,
> + },
These are all the same relative offsets, so you really only need to
store the base offset.
The use of 2 compatibles is a bit questionable as the programming model
appears to be the same and only which pins differ. Surely there are
some other pinctrl drivers handling mutiple instances.
Rob
next prev parent reply other threads:[~2024-01-24 15:19 UTC|newest]
Thread overview: 54+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-01-23 18:46 [PATCH v3 00/17] Add support for Mobileye EyeQ5 system controller Théo Lebrun
2024-01-23 18:46 ` [PATCH v3 01/17] clk: fixed-factor: add optional accuracy support Théo Lebrun
2024-01-23 18:46 ` [PATCH v3 02/17] clk: fixed-factor: add fwname-based constructor functions Théo Lebrun
2024-01-23 18:46 ` [PATCH v3 03/17] dt-bindings: pinctrl: allow pin controller device without unit address Théo Lebrun
2024-01-23 20:57 ` Rob Herring
2024-01-23 18:46 ` [PATCH v3 04/17] dt-bindings: soc: mobileye: add EyeQ5 OLB system controller Théo Lebrun
2024-01-23 20:58 ` Rob Herring
2024-01-24 15:14 ` Rob Herring
2024-01-24 17:28 ` Théo Lebrun
2024-01-24 17:40 ` Théo Lebrun
2024-01-24 19:22 ` Rob Herring
2024-01-25 11:40 ` Théo Lebrun
2024-01-26 11:52 ` Krzysztof Kozlowski
2024-01-26 12:28 ` Théo Lebrun
2024-01-26 14:54 ` Krzysztof Kozlowski
2024-01-25 7:51 ` Krzysztof Kozlowski
2024-01-25 11:01 ` Théo Lebrun
2024-01-25 14:33 ` Andrew Davis
2024-01-25 14:49 ` Théo Lebrun
2024-01-25 15:11 ` Andrew Davis
2024-01-26 11:51 ` Krzysztof Kozlowski
2024-01-23 18:46 ` [PATCH v3 05/17] dt-bindings: clock: mobileye,eyeq5-clk: add bindings Théo Lebrun
2024-01-24 6:45 ` Krzysztof Kozlowski
2024-01-23 18:46 ` [PATCH v3 06/17] dt-bindings: reset: mobileye,eyeq5-reset: " Théo Lebrun
2024-01-24 6:47 ` Krzysztof Kozlowski
2024-01-23 18:46 ` [PATCH v3 07/17] dt-bindings: pinctrl: mobileye,eyeq5-pinctrl: " Théo Lebrun
2024-01-24 6:52 ` Krzysztof Kozlowski
2024-01-23 18:46 ` [PATCH v3 08/17] clk: eyeq5: add platform driver Théo Lebrun
2024-01-24 7:05 ` Krzysztof Kozlowski
2024-01-24 16:41 ` Théo Lebrun
2024-01-25 7:46 ` Krzysztof Kozlowski
2024-01-25 11:53 ` Théo Lebrun
2024-01-26 11:56 ` Krzysztof Kozlowski
2024-01-23 18:46 ` [PATCH v3 09/17] reset: " Théo Lebrun
2024-01-24 7:00 ` Krzysztof Kozlowski
2024-01-24 16:52 ` Théo Lebrun
2024-01-24 10:54 ` Philipp Zabel
2024-01-24 17:07 ` Théo Lebrun
2024-01-23 18:46 ` [PATCH v3 10/17] pinctrl: " Théo Lebrun
2024-01-24 7:03 ` Krzysztof Kozlowski
2024-01-24 16:55 ` Théo Lebrun
2024-01-24 15:19 ` Rob Herring [this message]
2024-01-24 17:31 ` Théo Lebrun
2024-01-23 18:46 ` [PATCH v3 11/17] MIPS: mobileye: eyeq5: rename olb@e00000 to system-controller@e00000 Théo Lebrun
2024-01-23 18:46 ` [PATCH v3 12/17] MIPS: mobileye: eyeq5: remove reg-io-width property from OLB syscon Théo Lebrun
2024-01-24 8:33 ` Sergei Shtylyov
2024-01-24 16:56 ` Théo Lebrun
2024-01-23 18:46 ` [PATCH v3 13/17] MIPS: mobileye: eyeq5: use OLB clocks controller Théo Lebrun
2024-01-23 18:46 ` [PATCH v3 14/17] MIPS: mobileye: eyeq5: add OLB reset controller node Théo Lebrun
2024-01-23 18:47 ` [PATCH v3 15/17] MIPS: mobileye: eyeq5: add reset properties to uarts Théo Lebrun
2024-01-23 18:47 ` [PATCH v3 16/17] MIPS: mobileye: eyeq5: add pinctrl nodes & pinmux function nodes Théo Lebrun
2024-01-23 18:47 ` [PATCH v3 17/17] MIPS: mobileye: eyeq5: add pinctrl properties to UART nodes Théo Lebrun
2024-01-24 6:43 ` [PATCH v3 00/17] Add support for Mobileye EyeQ5 system controller Krzysztof Kozlowski
2024-01-24 16:44 ` Théo Lebrun
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20240124151949.GB930997-robh@kernel.org \
--to=robh@kernel.org \
--cc=conor+dt@kernel.org \
--cc=devicetree@vger.kernel.org \
--cc=gregory.clement@bootlin.com \
--cc=krzysztof.kozlowski+dt@linaro.org \
--cc=linus.walleij@linaro.org \
--cc=linux-clk@vger.kernel.org \
--cc=linux-gpio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mips@vger.kernel.org \
--cc=mturquette@baylibre.com \
--cc=p.zabel@pengutronix.de \
--cc=rafal@milecki.pl \
--cc=sboyd@kernel.org \
--cc=tawfik.bayouk@mobileye.com \
--cc=theo.lebrun@bootlin.com \
--cc=thomas.petazzoni@bootlin.com \
--cc=tsbogend@alpha.franken.de \
--cc=vladimir.kondratiev@mobileye.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).