[PATCH v3 1/2] pinctrl: milbeaut: Add Milbeaut M10V pinctrl

From: Sugaya Taichi
Date: Thu Nov 25 2021 - 05:00:07 EST


From: Jassi Brar <jaswinder.singh@xxxxxxxxxx>

Add Milbeaut M10V pinctrl.
The M10V has the pins that can be used GPIOs or take multiple other
functions.

Signed-off-by: Jassi Brar <jaswinder.singh@xxxxxxxxxx>
Signed-off-by: Sugaya Taichi <sugaya.taichi@xxxxxxxxxxxxx>
---
drivers/pinctrl/Kconfig | 8 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-milbeaut.c | 406 +++++++++++++++++++++++++++++++++++++
3 files changed, 415 insertions(+)
create mode 100644 drivers/pinctrl/pinctrl-milbeaut.c

diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 6a961d5..65f2038 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -223,6 +223,14 @@ config PINCTRL_OXNAS
select GPIOLIB_IRQCHIP
select MFD_SYSCON

+config PINCTRL_MILBEAUT
+ bool
+ select PINMUX
+ select PINCONF
+ select GENERIC_PINCONF
+ select OF_GPIO
+ select REGMAP_MMIO
+
config PINCTRL_ROCKCHIP
tristate "Rockchip gpio and pinctrl driver"
depends on ARCH_ROCKCHIP || COMPILE_TEST
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 5e63de2..9016094 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_PINCTRL_MCP23S08_I2C) += pinctrl-mcp23s08_i2c.o
obj-$(CONFIG_PINCTRL_MCP23S08_SPI) += pinctrl-mcp23s08_spi.o
obj-$(CONFIG_PINCTRL_MCP23S08) += pinctrl-mcp23s08.o
obj-$(CONFIG_PINCTRL_MESON) += meson/
+obj-$(CONFIG_PINCTRL_MILBEAUT) += pinctrl-milbeaut.o
obj-$(CONFIG_PINCTRL_OXNAS) += pinctrl-oxnas.o
obj-$(CONFIG_PINCTRL_PALMAS) += pinctrl-palmas.o
obj-$(CONFIG_PINCTRL_PIC32) += pinctrl-pic32.o
diff --git a/drivers/pinctrl/pinctrl-milbeaut.c b/drivers/pinctrl/pinctrl-milbeaut.c
new file mode 100644
index 0000000..474ef001
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-milbeaut.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018 Socionext Inc.
+ * Copyright (C) 2015 Linaro Ltd.
+ * Author: Jassi Brar <jaswinder.singh@xxxxxxxxxx>
+ * Author: Taichi Sugaya <sugaya.taichi@xxxxxxxxxxxxx>
+ */
+
+#include <linux/gpio/driver.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include "pinctrl-utils.h"
+
+#define PDR 0xc
+#define DDR 0x10c
+#define EPCR 0x20c
+#define PUDER 0x30c
+#define PUDCR 0x40c
+
+#define M10V_BANKS 20
+#define PINS_PER_BANK 8
+#define M10V_TOTAL_PINS (M10V_BANKS * PINS_PER_BANK)
+#define PINS_PER_REG 16
+
+struct m10v_pinctrl {
+ void __iomem *base;
+ struct device *dev;
+ struct gpio_chip gc;
+ struct pinctrl_desc pd;
+ char pin_names[4 * M10V_TOTAL_PINS];
+ struct pinctrl_pin_desc pins[M10V_TOTAL_PINS];
+ unsigned int gpins[M10V_TOTAL_PINS][1]; /* 1 pin-per-group */
+ spinlock_t lock;
+};
+
+struct milbeaut_function {
+ const char *name;
+ const char * const *groups;
+ unsigned int ngroups;
+};
+
+enum mlb_reg_type {
+ E_MLB_GPIO_PDR,
+ E_MLB_GPIO_DDR,
+ E_MLB_GPIO_EPCR,
+ E_MLB_GPIO_PUDER,
+ E_MLB_GPIO_PUDCR,
+};
+
+static const unsigned long
+ m10v_gpio_reg_offset[] = {PDR, DDR, EPCR, PUDER, PUDCR};
+
+static const char m10v_bank_name[] = {'6', '7', '8', '9', 'A', 'B', 'C', 'D',
+ 'E', 'F', 'G', 'H', 'W', 'J', 'K', 'L',
+ 'M', 'N', 'Y', 'P'};
+static const char * const usio0_m10v_grps[] = {"PE2", "PE3", "PF0"};
+static const char * const usio1_m10v_grps[] = {"PE4", "PE5", "PF1"};
+static const char * const usio2_m10v_grps[] = {"PE0", "PE1"};
+static const char * const usio3_m10v_grps[] = {"PY0", "PY1", "PY2"};
+static const char * const usio4_m10v_grps[] = {"PP0", "PP1", "PP2"};
+static const char * const usio5_m10v_grps[] = {"PM0", "PM1", "PM3"};
+static const char * const usio6_m10v_grps[] = {"PN0", "PN1", "PN3"};
+static const char * const usio7_m10v_grps[] = {"PY3", "PY5", "PY6"};
+static const char *gpio_m10v_grps[M10V_TOTAL_PINS];
+
+static const struct milbeaut_function m10v_functions[] = {
+#define FUNC_M10V(fname) \
+ { \
+ .name = #fname, \
+ .groups = fname##_m10v_grps, \
+ .ngroups = ARRAY_SIZE(fname##_m10v_grps), \
+ }
+ FUNC_M10V(gpio), /* GPIO always at index 0 */
+ FUNC_M10V(usio0),
+ FUNC_M10V(usio1),
+ FUNC_M10V(usio2),
+ FUNC_M10V(usio3),
+ FUNC_M10V(usio4),
+ FUNC_M10V(usio5),
+ FUNC_M10V(usio6),
+ FUNC_M10V(usio7),
+};
+
+static const struct milbeaut_function *milbeaut_functions;
+
+static void m10v_gpio_reg_write(struct m10v_pinctrl *pctl, int pin,
+ int set, enum mlb_reg_type type)
+{
+ u32 reg, shift, val;
+ unsigned long flags;
+
+ reg = m10v_gpio_reg_offset[type] + pin / PINS_PER_REG * 4;
+ shift = pin % PINS_PER_REG;
+
+ switch (type) {
+ case E_MLB_GPIO_PDR:
+ val = BIT(shift + 16) | (set << shift);
+ writel_relaxed(val, pctl->base + reg);
+ break;
+ case E_MLB_GPIO_DDR:
+ case E_MLB_GPIO_EPCR:
+ case E_MLB_GPIO_PUDER:
+ case E_MLB_GPIO_PUDCR:
+ spin_lock_irqsave(&pctl->lock, flags);
+ val = readl_relaxed(pctl->base + reg);
+ if (set)
+ val |= BIT(shift);
+ else
+ val &= ~BIT(shift);
+ writel_relaxed(val, pctl->base + reg);
+ spin_unlock_irqrestore(&pctl->lock, flags);
+ break;
+ default:
+ break;
+ }
+}
+
+static int m10v_gpio_reg_read(struct m10v_pinctrl *pctl, int pin,
+ enum mlb_reg_type type)
+{
+ u32 reg, shift, val;
+
+ reg = m10v_gpio_reg_offset[type] + pin / PINS_PER_REG * 4;
+ shift = pin % PINS_PER_REG;
+
+ val = readl_relaxed(pctl->base + reg);
+ return !!(val & BIT(shift));
+}
+
+static int m10v_pconf_group_set(struct pinctrl_dev *pctldev,
+ unsigned int group,
+ unsigned long *configs,
+ unsigned int num_configs)
+{
+ struct m10v_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ u32 pin;
+ int i;
+
+ pin = pctl->gpins[group][0];
+ for (i = 0; i < num_configs; i++) {
+ switch (pinconf_to_config_param(configs[i])) {
+ case PIN_CONFIG_BIAS_PULL_UP:
+ /* select "Up" before "Pull" enabled */
+ m10v_gpio_reg_write(pctl, pin, 1, E_MLB_GPIO_PUDCR);
+ m10v_gpio_reg_write(pctl, pin, 1, E_MLB_GPIO_PUDER);
+ break;
+ case PIN_CONFIG_BIAS_PULL_DOWN:
+ /* select "Down" before "Pull" enabled */
+ m10v_gpio_reg_write(pctl, pin, 0, E_MLB_GPIO_PUDCR);
+ m10v_gpio_reg_write(pctl, pin, 1, E_MLB_GPIO_PUDER);
+ break;
+ case PIN_CONFIG_BIAS_DISABLE:
+ m10v_gpio_reg_write(pctl, pin, 0, E_MLB_GPIO_PUDER);
+ break;
+ default:
+ break;
+ }
+ }
+ return 0;
+}
+
+static const struct pinconf_ops m10v_pconf_ops = {
+ .pin_config_group_set = m10v_pconf_group_set,
+};
+
+static int m10v_pctrl_get_groups_count(struct pinctrl_dev *pctldev)
+{
+ return M10V_TOTAL_PINS;
+}
+
+static const char *m10v_pctrl_get_group_name(struct pinctrl_dev *pctldev,
+ unsigned int pin)
+{
+ struct m10v_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+ return &pctl->pin_names[4 * pin];
+}
+
+static int m10v_pctrl_get_group_pins(struct pinctrl_dev *pctldev,
+ unsigned int group,
+ const unsigned int **pins,
+ unsigned int *num_pins)
+{
+ struct m10v_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+ *pins = pctl->gpins[group];
+ *num_pins = 1;
+ return 0;
+}
+
+static const struct pinctrl_ops m10v_pctrl_ops = {
+ .get_groups_count = m10v_pctrl_get_groups_count,
+ .get_group_name = m10v_pctrl_get_group_name,
+ .get_group_pins = m10v_pctrl_get_group_pins,
+ .dt_node_to_map = pinconf_generic_dt_node_to_map_group,
+ .dt_free_map = pinctrl_utils_free_map,
+};
+
+static int m10v_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev)
+{
+ return ARRAY_SIZE(m10v_functions);
+}
+
+static const char *m10v_pmx_get_func_name(struct pinctrl_dev *pctldev,
+ unsigned int function)
+{
+ return milbeaut_functions[function].name;
+}
+
+static int m10v_pmx_get_func_groups(struct pinctrl_dev *pctldev,
+ unsigned int function,
+ const char * const **groups,
+ unsigned * const num_groups)
+{
+ *groups = milbeaut_functions[function].groups;
+ *num_groups = milbeaut_functions[function].ngroups;
+ return 0;
+}
+
+static void m10v_pin_to_function(struct m10v_pinctrl *pctl,
+ unsigned int pin,
+ bool en)
+{
+ /*
+ * true: pin to functional purpose
+ * false: pin to gpio
+ */
+ m10v_gpio_reg_write(pctl, pin, en, E_MLB_GPIO_EPCR);
+}
+
+static int m10v_pmx_set_mux(struct pinctrl_dev *pctldev,
+ unsigned int function,
+ unsigned int group)
+{
+ struct m10v_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+ u32 pin = pctl->gpins[group][0]; /* each group has exactly 1 pin */
+
+ m10v_pin_to_function(pctl, pin, !!function);
+ return 0;
+}
+
+static int
+m10v_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int pin, bool input)
+{
+ struct m10v_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+ m10v_gpio_reg_write(pctl, pin, !input, E_MLB_GPIO_DDR);
+ return 0;
+}
+
+static int
+m10v_pmx_gpio_request_enable(struct pinctrl_dev *pctldev,
+ struct pinctrl_gpio_range *range,
+ unsigned int pin)
+{
+ struct m10v_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+
+ m10v_pin_to_function(pctl, pin, false);
+ return 0;
+}
+
+static const struct pinmux_ops m10v_pmx_ops = {
+ .get_functions_count = m10v_pmx_get_funcs_cnt,
+ .get_function_name = m10v_pmx_get_func_name,
+ .get_function_groups = m10v_pmx_get_func_groups,
+ .set_mux = m10v_pmx_set_mux,
+ .gpio_set_direction = m10v_pmx_gpio_set_direction,
+ .gpio_request_enable = m10v_pmx_gpio_request_enable,
+ .strict = true,
+};
+
+static int m10v_gpio_get(struct gpio_chip *gc, unsigned int group)
+{
+ struct m10v_pinctrl *pctl = gpiochip_get_data(gc);
+ u32 pin = pctl->gpins[group][0];
+
+ return m10v_gpio_reg_read(pctl, pin, E_MLB_GPIO_PDR);
+}
+
+static void m10v_gpio_set(struct gpio_chip *gc, unsigned int group, int set)
+{
+ struct m10v_pinctrl *pctl = gpiochip_get_data(gc);
+ u32 pin = pctl->gpins[group][0];
+
+ m10v_gpio_reg_write(pctl, pin, set, E_MLB_GPIO_PDR);
+}
+
+static int m10v_gpio_direction_input(struct gpio_chip *gc,
+ unsigned int offset)
+{
+ return pinctrl_gpio_direction_input(gc->base + offset);
+}
+
+static int m10v_gpio_direction_output(struct gpio_chip *gc,
+ unsigned int offset, int value)
+{
+ int ret;
+
+ ret = pinctrl_gpio_direction_output(gc->base + offset);
+ if (!ret)
+ m10v_gpio_set(gc, offset, value);
+
+ return ret;
+}
+
+static const struct of_device_id m10v_pmatch[] = {
+ { .compatible = "socionext,milbeaut-m10v-pinctrl" },
+ {},
+};
+
+static int m10v_pinctrl_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct pinctrl_dev *pctl_dev;
+ struct m10v_pinctrl *pctl;
+ struct pinctrl_desc *pd;
+ struct gpio_chip *gc;
+ struct resource *res;
+ int i, ret, tpins;
+
+ pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl),
+ GFP_KERNEL);
+ if (!pctl)
+ return -ENOMEM;
+
+ pctl->dev = &pdev->dev;
+ milbeaut_functions = m10v_functions;
+ tpins = M10V_TOTAL_PINS;
+
+ pd = &pctl->pd;
+ gc = &pctl->gc;
+ spin_lock_init(&pctl->lock);
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pinctrl");
+ pctl->base = devm_ioremap_resource(&pdev->dev, res);
+ if (!pctl->base)
+ return -EINVAL;
+
+ for (i = 0; i < tpins; i++) {
+ pctl->pins[i].number = i;
+ pctl->pins[i].name = &pctl->pin_names[4 * i];
+ snprintf(&pctl->pin_names[4 * i], 4, "P%c%d",
+ m10v_bank_name[i / PINS_PER_BANK], i % PINS_PER_BANK);
+ gpio_m10v_grps[i] = &pctl->pin_names[4 * i];
+ pctl->gpins[i][0] = i;
+ }
+ /* absent or incomplete entries allow all access */
+ pd->name = dev_name(&pdev->dev);
+ pd->pins = pctl->pins;
+ pd->npins = tpins;
+ pd->pctlops = &m10v_pctrl_ops;
+ pd->pmxops = &m10v_pmx_ops;
+ pd->confops = &m10v_pconf_ops;
+ pd->owner = THIS_MODULE;
+
+ pctl_dev = pinctrl_register(pd, &pdev->dev, pctl);
+ if (!pctl_dev) {
+ dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
+ return -EINVAL;
+ }
+
+ gc->base = -1;
+ gc->ngpio = tpins;
+ gc->label = dev_name(&pdev->dev);
+ gc->owner = THIS_MODULE;
+ gc->of_node = np;
+ gc->direction_input = m10v_gpio_direction_input;
+ gc->direction_output = m10v_gpio_direction_output;
+ gc->get = m10v_gpio_get;
+ gc->set = m10v_gpio_set;
+ gc->request = gpiochip_generic_request;
+ gc->free = gpiochip_generic_free;
+ ret = devm_gpiochip_add_data(&pdev->dev, gc, pctl);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed register gpiochip\n");
+ return ret;
+ }
+
+ ret = gpiochip_add_pin_range(gc, dev_name(&pdev->dev),
+ 0, 0, tpins);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to add pin range\n");
+ gpiochip_remove(gc);
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver m10v_pinctrl_driver = {
+ .probe = m10v_pinctrl_probe,
+ .driver = {
+ .name = "m10v-pinctrl",
+ .of_match_table = m10v_pmatch,
+ },
+};
+builtin_platform_driver(m10v_pinctrl_driver);
--
2.7.4