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