| // SPDX-License-Identifier: GPL-2.0 |
| /* |
| * Copyright (C) 2024 Nuvoton Technology Corp. |
| * |
| * Author: Shan-Chun Hung <schung@nuvoton.com> |
| * * Jacky Huang <ychuang3@nuvoton.com> |
| */ |
| |
| #include <linux/bitfield.h> |
| #include <linux/bitops.h> |
| #include <linux/cleanup.h> |
| #include <linux/clk.h> |
| #include <linux/gpio/driver.h> |
| #include <linux/mfd/syscon.h> |
| #include <linux/of.h> |
| #include <linux/platform_device.h> |
| #include <linux/property.h> |
| #include <linux/regmap.h> |
| |
| #include <linux/pinctrl/pinconf.h> |
| #include <linux/pinctrl/pinctrl.h> |
| #include "../core.h" |
| #include "../pinconf.h" |
| #include "pinctrl-ma35.h" |
| |
| #define MA35_MFP_REG_BASE 0x80 |
| #define MA35_MFP_REG_SZ_PER_BANK 8 |
| #define MA35_MFP_BITS_PER_PORT 4 |
| |
| #define MA35_GPIO_BANK_MAX 14 |
| #define MA35_GPIO_PORT_MAX 16 |
| |
| /* GPIO control registers */ |
| #define MA35_GP_REG_MODE 0x00 |
| #define MA35_GP_REG_DINOFF 0x04 |
| #define MA35_GP_REG_DOUT 0x08 |
| #define MA35_GP_REG_DATMSK 0x0c |
| #define MA35_GP_REG_PIN 0x10 |
| #define MA35_GP_REG_DBEN 0x14 |
| #define MA35_GP_REG_INTTYPE 0x18 |
| #define MA35_GP_REG_INTEN 0x1c |
| #define MA35_GP_REG_INTSRC 0x20 |
| #define MA35_GP_REG_SMTEN 0x24 |
| #define MA35_GP_REG_SLEWCTL 0x28 |
| #define MA35_GP_REG_SPW 0x2c |
| #define MA35_GP_REG_PUSEL 0x30 |
| #define MA35_GP_REG_DSL 0x38 |
| #define MA35_GP_REG_DSH 0x3c |
| |
| /* GPIO mode control */ |
| #define MA35_GP_MODE_INPUT 0x0 |
| #define MA35_GP_MODE_OUTPUT 0x1 |
| #define MA35_GP_MODE_OPEN_DRAIN 0x2 |
| #define MA35_GP_MODE_QUASI 0x3 |
| #define MA35_GP_MODE_MASK(n) GENMASK(n * 2 + 1, n * 2) |
| |
| #define MA35_GP_SLEWCTL_MASK(n) GENMASK(n * 2 + 1, n * 2) |
| |
| /* GPIO pull-up and pull-down selection control */ |
| #define MA35_GP_PUSEL_DISABLE 0x0 |
| #define MA35_GP_PUSEL_PULL_UP 0x1 |
| #define MA35_GP_PUSEL_PULL_DOWN 0x2 |
| #define MA35_GP_PUSEL_MASK(n) GENMASK(n * 2 + 1, n * 2) |
| |
| /* |
| * The MA35_GP_REG_INTEN bits 0 ~ 15 control low-level or falling edge trigger, |
| * while bits 16 ~ 31 control high-level or rising edge trigger. |
| */ |
| #define MA35_GP_INTEN_L(n) BIT(n) |
| #define MA35_GP_INTEN_H(n) BIT(n + 16) |
| #define MA35_GP_INTEN_BOTH(n) (MA35_GP_INTEN_H(n) | MA35_GP_INTEN_L(n)) |
| |
| /* |
| * The MA35_GP_REG_DSL register controls ports 0 to 7, while the MA35_GP_REG_DSH |
| * register controls ports 8 to 15. Each port occupies a width of 4 bits, with 3 |
| * bits being effective. |
| */ |
| #define MA35_GP_DS_REG(n) (n < 8 ? MA35_GP_REG_DSL : MA35_GP_REG_DSH) |
| #define MA35_GP_DS_MASK(n) GENMASK((n % 8) * 4 + 3, (n % 8) * 4) |
| |
| #define MVOLT_1800 0 |
| #define MVOLT_3300 1 |
| |
| /* Non-constant mask variant of FIELD_GET() and FIELD_PREP() */ |
| #define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1)) |
| #define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask)) |
| |
| static const char * const gpio_group_name[] = { |
| "gpioa", "gpiob", "gpioc", "gpiod", "gpioe", "gpiof", "gpiog", |
| "gpioh", "gpioi", "gpioj", "gpiok", "gpiol", "gpiom", "gpion", |
| }; |
| |
| static const u32 ds_1800mv_tbl[] = { |
| 2900, 4400, 5800, 7300, 8600, 10100, 11500, 13000, |
| }; |
| |
| static const u32 ds_3300mv_tbl[] = { |
| 17100, 25600, 34100, 42800, 48000, 56000, 77000, 82000, |
| }; |
| |
| struct ma35_pin_func { |
| const char *name; |
| const char **groups; |
| u32 ngroups; |
| }; |
| |
| struct ma35_pin_setting { |
| u32 offset; |
| u32 shift; |
| u32 muxval; |
| unsigned long *configs; |
| unsigned int nconfigs; |
| }; |
| |
| struct ma35_pin_group { |
| const char *name; |
| unsigned int npins; |
| unsigned int *pins; |
| struct ma35_pin_setting *settings; |
| }; |
| |
| struct ma35_pin_bank { |
| void __iomem *reg_base; |
| struct clk *clk; |
| int irq; |
| u8 bank_num; |
| u8 nr_pins; |
| bool valid; |
| const char *name; |
| struct fwnode_handle *fwnode; |
| struct gpio_chip chip; |
| u32 irqtype; |
| u32 irqinten; |
| struct regmap *regmap; |
| struct device *dev; |
| }; |
| |
| struct ma35_pin_ctrl { |
| struct ma35_pin_bank *pin_banks; |
| u32 nr_banks; |
| u32 nr_pins; |
| }; |
| |
| struct ma35_pinctrl { |
| struct device *dev; |
| struct ma35_pin_ctrl *ctrl; |
| struct pinctrl_dev *pctl; |
| const struct ma35_pinctrl_soc_info *info; |
| struct regmap *regmap; |
| struct ma35_pin_group *groups; |
| unsigned int ngroups; |
| struct ma35_pin_func *functions; |
| unsigned int nfunctions; |
| }; |
| |
| static DEFINE_RAW_SPINLOCK(ma35_lock); |
| |
| static int ma35_get_groups_count(struct pinctrl_dev *pctldev) |
| { |
| struct ma35_pinctrl *npctl = pinctrl_dev_get_drvdata(pctldev); |
| |
| return npctl->ngroups; |
| } |
| |
| static const char *ma35_get_group_name(struct pinctrl_dev *pctldev, unsigned int selector) |
| { |
| struct ma35_pinctrl *npctl = pinctrl_dev_get_drvdata(pctldev); |
| |
| return npctl->groups[selector].name; |
| } |
| |
| static int ma35_get_group_pins(struct pinctrl_dev *pctldev, unsigned int selector, |
| const unsigned int **pins, unsigned int *npins) |
| { |
| struct ma35_pinctrl *npctl = pinctrl_dev_get_drvdata(pctldev); |
| |
| if (selector >= npctl->ngroups) |
| return -EINVAL; |
| |
| *pins = npctl->groups[selector].pins; |
| *npins = npctl->groups[selector].npins; |
| |
| return 0; |
| } |
| |
| static struct ma35_pin_group *ma35_pinctrl_find_group_by_name( |
| const struct ma35_pinctrl *npctl, const char *name) |
| { |
| int i; |
| |
| for (i = 0; i < npctl->ngroups; i++) { |
| if (!strcmp(npctl->groups[i].name, name)) |
| return &npctl->groups[i]; |
| } |
| return NULL; |
| } |
| |
| static int ma35_pinctrl_dt_node_to_map_func(struct pinctrl_dev *pctldev, |
| struct device_node *np, |
| struct pinctrl_map **map, |
| unsigned int *num_maps) |
| { |
| struct ma35_pinctrl *npctl = pinctrl_dev_get_drvdata(pctldev); |
| struct ma35_pin_group *grp; |
| struct pinctrl_map *new_map; |
| struct device_node *parent; |
| int map_num = 1; |
| int i; |
| |
| /* |
| * first find the group of this node and check if we need create |
| * config maps for pins |
| */ |
| grp = ma35_pinctrl_find_group_by_name(npctl, np->name); |
| if (!grp) { |
| dev_err(npctl->dev, "unable to find group for node %s\n", np->name); |
| return -EINVAL; |
| } |
| |
| map_num += grp->npins; |
| new_map = kcalloc(map_num, sizeof(*new_map), GFP_KERNEL); |
| if (!new_map) |
| return -ENOMEM; |
| |
| *map = new_map; |
| *num_maps = map_num; |
| /* create mux map */ |
| parent = of_get_parent(np); |
| if (!parent) |
| return -EINVAL; |
| |
| new_map[0].type = PIN_MAP_TYPE_MUX_GROUP; |
| new_map[0].data.mux.function = parent->name; |
| new_map[0].data.mux.group = np->name; |
| of_node_put(parent); |
| |
| new_map++; |
| for (i = 0; i < grp->npins; i++) { |
| new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN; |
| new_map[i].data.configs.group_or_pin = pin_get_name(pctldev, grp->pins[i]); |
| new_map[i].data.configs.configs = grp->settings[i].configs; |
| new_map[i].data.configs.num_configs = grp->settings[i].nconfigs; |
| } |
| dev_dbg(pctldev->dev, "maps: function %s group %s num %d\n", |
| (*map)->data.mux.function, (*map)->data.mux.group, map_num); |
| |
| return 0; |
| } |
| |
| static const struct pinctrl_ops ma35_pctrl_ops = { |
| .get_groups_count = ma35_get_groups_count, |
| .get_group_name = ma35_get_group_name, |
| .get_group_pins = ma35_get_group_pins, |
| .dt_node_to_map = ma35_pinctrl_dt_node_to_map_func, |
| .dt_free_map = pinconf_generic_dt_free_map, |
| }; |
| |
| static int ma35_pinmux_get_func_count(struct pinctrl_dev *pctldev) |
| { |
| struct ma35_pinctrl *npctl = pinctrl_dev_get_drvdata(pctldev); |
| |
| return npctl->nfunctions; |
| } |
| |
| static const char *ma35_pinmux_get_func_name(struct pinctrl_dev *pctldev, |
| unsigned int selector) |
| { |
| struct ma35_pinctrl *npctl = pinctrl_dev_get_drvdata(pctldev); |
| |
| return npctl->functions[selector].name; |
| } |
| |
| static int ma35_pinmux_get_func_groups(struct pinctrl_dev *pctldev, |
| unsigned int function, |
| const char *const **groups, |
| unsigned int *const num_groups) |
| { |
| struct ma35_pinctrl *npctl = pinctrl_dev_get_drvdata(pctldev); |
| |
| *groups = npctl->functions[function].groups; |
| *num_groups = npctl->functions[function].ngroups; |
| |
| return 0; |
| } |
| |
| static int ma35_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int selector, |
| unsigned int group) |
| { |
| struct ma35_pinctrl *npctl = pinctrl_dev_get_drvdata(pctldev); |
| struct ma35_pin_group *grp = &npctl->groups[group]; |
| struct ma35_pin_setting *setting = grp->settings; |
| u32 i, regval; |
| |
| dev_dbg(npctl->dev, "enable function %s group %s\n", |
| npctl->functions[selector].name, npctl->groups[group].name); |
| |
| for (i = 0; i < grp->npins; i++) { |
| regmap_read(npctl->regmap, setting->offset, ®val); |
| regval &= ~GENMASK(setting->shift + MA35_MFP_BITS_PER_PORT - 1, |
| setting->shift); |
| regval |= setting->muxval << setting->shift; |
| regmap_write(npctl->regmap, setting->offset, regval); |
| setting++; |
| } |
| return 0; |
| } |
| |
| static const struct pinmux_ops ma35_pmx_ops = { |
| .get_functions_count = ma35_pinmux_get_func_count, |
| .get_function_name = ma35_pinmux_get_func_name, |
| .get_function_groups = ma35_pinmux_get_func_groups, |
| .set_mux = ma35_pinmux_set_mux, |
| .strict = true, |
| }; |
| |
| static void ma35_gpio_set_mode(void __iomem *reg_mode, unsigned int gpio, u32 mode) |
| { |
| u32 regval = readl(reg_mode); |
| |
| regval &= ~MA35_GP_MODE_MASK(gpio); |
| regval |= field_prep(MA35_GP_MODE_MASK(gpio), mode); |
| |
| writel(regval, reg_mode); |
| } |
| |
| static u32 ma35_gpio_get_mode(void __iomem *reg_mode, unsigned int gpio) |
| { |
| u32 regval = readl(reg_mode); |
| |
| return field_get(MA35_GP_MODE_MASK(gpio), regval); |
| } |
| |
| static int ma35_gpio_core_direction_in(struct gpio_chip *gc, unsigned int gpio) |
| { |
| struct ma35_pin_bank *bank = gpiochip_get_data(gc); |
| void __iomem *reg_mode = bank->reg_base + MA35_GP_REG_MODE; |
| |
| guard(raw_spinlock_irqsave)(&ma35_lock); |
| |
| ma35_gpio_set_mode(reg_mode, gpio, MA35_GP_MODE_INPUT); |
| |
| return 0; |
| } |
| |
| static int ma35_gpio_core_direction_out(struct gpio_chip *gc, unsigned int gpio, int val) |
| { |
| struct ma35_pin_bank *bank = gpiochip_get_data(gc); |
| void __iomem *reg_dout = bank->reg_base + MA35_GP_REG_DOUT; |
| void __iomem *reg_mode = bank->reg_base + MA35_GP_REG_MODE; |
| unsigned int regval; |
| |
| guard(raw_spinlock_irqsave)(&ma35_lock); |
| |
| regval = readl(reg_dout); |
| if (val) |
| regval |= BIT(gpio); |
| else |
| regval &= ~BIT(gpio); |
| writel(regval, reg_dout); |
| |
| ma35_gpio_set_mode(reg_mode, gpio, MA35_GP_MODE_OUTPUT); |
| |
| return 0; |
| } |
| |
| static int ma35_gpio_core_get(struct gpio_chip *gc, unsigned int gpio) |
| { |
| struct ma35_pin_bank *bank = gpiochip_get_data(gc); |
| void __iomem *reg_pin = bank->reg_base + MA35_GP_REG_PIN; |
| |
| return !!(readl(reg_pin) & BIT(gpio)); |
| } |
| |
| static void ma35_gpio_core_set(struct gpio_chip *gc, unsigned int gpio, int val) |
| { |
| struct ma35_pin_bank *bank = gpiochip_get_data(gc); |
| void __iomem *reg_dout = bank->reg_base + MA35_GP_REG_DOUT; |
| u32 regval; |
| |
| if (val) |
| regval = readl(reg_dout) | BIT(gpio); |
| else |
| regval = readl(reg_dout) & ~BIT(gpio); |
| |
| writel(regval, reg_dout); |
| } |
| |
| static int ma35_gpio_core_to_request(struct gpio_chip *gc, unsigned int gpio) |
| { |
| struct ma35_pin_bank *bank = gpiochip_get_data(gc); |
| u32 reg_offs, bit_offs, regval; |
| |
| if (gpio < 8) { |
| /* The MFP low register controls port 0 ~ 7 */ |
| reg_offs = bank->bank_num * MA35_MFP_REG_SZ_PER_BANK; |
| bit_offs = gpio * MA35_MFP_BITS_PER_PORT; |
| } else { |
| /* The MFP high register controls port 8 ~ 15 */ |
| reg_offs = bank->bank_num * MA35_MFP_REG_SZ_PER_BANK + 4; |
| bit_offs = (gpio - 8) * MA35_MFP_BITS_PER_PORT; |
| } |
| |
| regmap_read(bank->regmap, MA35_MFP_REG_BASE + reg_offs, ®val); |
| regval &= ~GENMASK(bit_offs + MA35_MFP_BITS_PER_PORT - 1, bit_offs); |
| regmap_write(bank->regmap, MA35_MFP_REG_BASE + reg_offs, regval); |
| |
| return 0; |
| } |
| |
| static void ma35_irq_gpio_ack(struct irq_data *d) |
| { |
| struct ma35_pin_bank *bank = gpiochip_get_data(irq_data_get_irq_chip_data(d)); |
| void __iomem *reg_intsrc = bank->reg_base + MA35_GP_REG_INTSRC; |
| irq_hw_number_t hwirq = irqd_to_hwirq(d); |
| |
| writel(BIT(hwirq), reg_intsrc); |
| } |
| |
| static void ma35_irq_gpio_mask(struct irq_data *d) |
| { |
| struct ma35_pin_bank *bank = gpiochip_get_data(irq_data_get_irq_chip_data(d)); |
| void __iomem *reg_ien = bank->reg_base + MA35_GP_REG_INTEN; |
| irq_hw_number_t hwirq = irqd_to_hwirq(d); |
| u32 regval; |
| |
| regval = readl(reg_ien); |
| |
| regval &= ~MA35_GP_INTEN_BOTH(hwirq); |
| |
| writel(regval, reg_ien); |
| } |
| |
| static void ma35_irq_gpio_unmask(struct irq_data *d) |
| { |
| struct ma35_pin_bank *bank = gpiochip_get_data(irq_data_get_irq_chip_data(d)); |
| void __iomem *reg_itype = bank->reg_base + MA35_GP_REG_INTTYPE; |
| void __iomem *reg_ien = bank->reg_base + MA35_GP_REG_INTEN; |
| irq_hw_number_t hwirq = irqd_to_hwirq(d); |
| u32 bval, regval; |
| |
| bval = bank->irqtype & BIT(hwirq); |
| regval = readl(reg_itype); |
| regval &= ~BIT(hwirq); |
| writel(regval | bval, reg_itype); |
| |
| bval = bank->irqinten & MA35_GP_INTEN_BOTH(hwirq); |
| regval = readl(reg_ien); |
| regval &= ~MA35_GP_INTEN_BOTH(hwirq); |
| writel(regval | bval, reg_ien); |
| } |
| |
| static int ma35_irq_irqtype(struct irq_data *d, unsigned int type) |
| { |
| struct ma35_pin_bank *bank = gpiochip_get_data(irq_data_get_irq_chip_data(d)); |
| irq_hw_number_t hwirq = irqd_to_hwirq(d); |
| |
| switch (type) { |
| case IRQ_TYPE_EDGE_BOTH: |
| irq_set_handler_locked(d, handle_edge_irq); |
| bank->irqtype &= ~BIT(hwirq); |
| bank->irqinten |= MA35_GP_INTEN_BOTH(hwirq); |
| break; |
| case IRQ_TYPE_EDGE_RISING: |
| case IRQ_TYPE_LEVEL_HIGH: |
| irq_set_handler_locked(d, handle_edge_irq); |
| bank->irqtype &= ~BIT(hwirq); |
| bank->irqinten |= MA35_GP_INTEN_H(hwirq); |
| bank->irqinten &= ~MA35_GP_INTEN_L(hwirq); |
| break; |
| case IRQ_TYPE_EDGE_FALLING: |
| case IRQ_TYPE_LEVEL_LOW: |
| irq_set_handler_locked(d, handle_edge_irq); |
| bank->irqtype &= ~BIT(hwirq); |
| bank->irqinten |= MA35_GP_INTEN_L(hwirq); |
| bank->irqinten &= ~MA35_GP_INTEN_H(hwirq); |
| break; |
| default: |
| return -EINVAL; |
| } |
| |
| writel(bank->irqtype, bank->reg_base + MA35_GP_REG_INTTYPE); |
| writel(bank->irqinten, bank->reg_base + MA35_GP_REG_INTEN); |
| |
| return 0; |
| } |
| |
| static struct irq_chip ma35_gpio_irqchip = { |
| .name = "MA35-GPIO-IRQ", |
| .irq_disable = ma35_irq_gpio_mask, |
| .irq_enable = ma35_irq_gpio_unmask, |
| .irq_ack = ma35_irq_gpio_ack, |
| .irq_mask = ma35_irq_gpio_mask, |
| .irq_unmask = ma35_irq_gpio_unmask, |
| .irq_set_type = ma35_irq_irqtype, |
| .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_IMMUTABLE, |
| GPIOCHIP_IRQ_RESOURCE_HELPERS, |
| }; |
| |
| static void ma35_irq_demux_intgroup(struct irq_desc *desc) |
| { |
| struct ma35_pin_bank *bank = gpiochip_get_data(irq_desc_get_handler_data(desc)); |
| struct irq_domain *irqdomain = bank->chip.irq.domain; |
| struct irq_chip *irqchip = irq_desc_get_chip(desc); |
| unsigned long isr; |
| int offset; |
| |
| chained_irq_enter(irqchip, desc); |
| |
| isr = readl(bank->reg_base + MA35_GP_REG_INTSRC); |
| |
| for_each_set_bit(offset, &isr, bank->nr_pins) |
| generic_handle_irq(irq_find_mapping(irqdomain, offset)); |
| |
| chained_irq_exit(irqchip, desc); |
| } |
| |
| static int ma35_gpiolib_register(struct platform_device *pdev, struct ma35_pinctrl *npctl) |
| { |
| struct ma35_pin_ctrl *ctrl = npctl->ctrl; |
| struct ma35_pin_bank *bank = ctrl->pin_banks; |
| int ret; |
| int i; |
| |
| for (i = 0; i < ctrl->nr_banks; i++, bank++) { |
| if (!bank->valid) { |
| dev_warn(&pdev->dev, "%pfw: bank is not valid\n", bank->fwnode); |
| continue; |
| } |
| bank->irqtype = 0; |
| bank->irqinten = 0; |
| bank->chip.label = bank->name; |
| bank->chip.of_gpio_n_cells = 2; |
| bank->chip.parent = &pdev->dev; |
| bank->chip.request = ma35_gpio_core_to_request; |
| bank->chip.direction_input = ma35_gpio_core_direction_in; |
| bank->chip.direction_output = ma35_gpio_core_direction_out; |
| bank->chip.get = ma35_gpio_core_get; |
| bank->chip.set = ma35_gpio_core_set; |
| bank->chip.base = -1; |
| bank->chip.ngpio = bank->nr_pins; |
| bank->chip.can_sleep = false; |
| |
| if (bank->irq > 0) { |
| struct gpio_irq_chip *girq; |
| |
| girq = &bank->chip.irq; |
| gpio_irq_chip_set_chip(girq, &ma35_gpio_irqchip); |
| girq->parent_handler = ma35_irq_demux_intgroup; |
| girq->num_parents = 1; |
| |
| girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents, |
| sizeof(*girq->parents), GFP_KERNEL); |
| if (!girq->parents) |
| return -ENOMEM; |
| |
| girq->parents[0] = bank->irq; |
| girq->default_type = IRQ_TYPE_NONE; |
| girq->handler = handle_bad_irq; |
| } |
| |
| ret = devm_gpiochip_add_data(&pdev->dev, &bank->chip, bank); |
| if (ret) { |
| dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n", |
| bank->chip.label, ret); |
| return ret; |
| } |
| } |
| return 0; |
| } |
| |
| static int ma35_get_bank_data(struct ma35_pin_bank *bank) |
| { |
| bank->reg_base = fwnode_iomap(bank->fwnode, 0); |
| if (!bank->reg_base) |
| return -ENOMEM; |
| |
| bank->irq = fwnode_irq_get(bank->fwnode, 0); |
| |
| bank->nr_pins = MA35_GPIO_PORT_MAX; |
| |
| bank->clk = of_clk_get(to_of_node(bank->fwnode), 0); |
| if (IS_ERR(bank->clk)) |
| return PTR_ERR(bank->clk); |
| |
| return clk_prepare_enable(bank->clk); |
| } |
| |
| static int ma35_pinctrl_get_soc_data(struct ma35_pinctrl *pctl, struct platform_device *pdev) |
| { |
| struct fwnode_handle *child; |
| struct ma35_pin_ctrl *ctrl; |
| struct ma35_pin_bank *bank; |
| int i, id = 0; |
| |
| ctrl = pctl->ctrl; |
| ctrl->nr_banks = MA35_GPIO_BANK_MAX; |
| |
| ctrl->pin_banks = devm_kcalloc(&pdev->dev, ctrl->nr_banks, |
| sizeof(*ctrl->pin_banks), GFP_KERNEL); |
| if (!ctrl->pin_banks) |
| return -ENOMEM; |
| |
| for (i = 0; i < ctrl->nr_banks; i++) { |
| ctrl->pin_banks[i].bank_num = i; |
| ctrl->pin_banks[i].name = gpio_group_name[i]; |
| } |
| |
| for_each_gpiochip_node(&pdev->dev, child) { |
| bank = &ctrl->pin_banks[id]; |
| bank->fwnode = child; |
| bank->regmap = pctl->regmap; |
| bank->dev = &pdev->dev; |
| if (!ma35_get_bank_data(bank)) |
| bank->valid = true; |
| id++; |
| } |
| return 0; |
| } |
| |
| static void ma35_gpio_cla_port(unsigned int gpio_num, unsigned int *group, |
| unsigned int *num) |
| { |
| *group = gpio_num / MA35_GPIO_PORT_MAX; |
| *num = gpio_num % MA35_GPIO_PORT_MAX; |
| } |
| |
| static int ma35_pinconf_set_pull(struct ma35_pinctrl *npctl, unsigned int pin, |
| int pull_up) |
| { |
| unsigned int port, group_num; |
| void __iomem *base; |
| u32 regval, pull_sel = MA35_GP_PUSEL_DISABLE; |
| |
| ma35_gpio_cla_port(pin, &group_num, &port); |
| base = npctl->ctrl->pin_banks[group_num].reg_base; |
| |
| regval = readl(base + MA35_GP_REG_PUSEL); |
| regval &= ~MA35_GP_PUSEL_MASK(port); |
| |
| switch (pull_up) { |
| case PIN_CONFIG_BIAS_PULL_UP: |
| pull_sel = MA35_GP_PUSEL_PULL_UP; |
| break; |
| |
| case PIN_CONFIG_BIAS_PULL_DOWN: |
| pull_sel = MA35_GP_PUSEL_PULL_DOWN; |
| break; |
| |
| case PIN_CONFIG_BIAS_DISABLE: |
| pull_sel = MA35_GP_PUSEL_DISABLE; |
| break; |
| } |
| |
| regval |= field_prep(MA35_GP_PUSEL_MASK(port), pull_sel); |
| writel(regval, base + MA35_GP_REG_PUSEL); |
| |
| return 0; |
| } |
| |
| static int ma35_pinconf_get_output(struct ma35_pinctrl *npctl, unsigned int pin) |
| { |
| unsigned int port, group_num; |
| void __iomem *base; |
| u32 mode; |
| |
| ma35_gpio_cla_port(pin, &group_num, &port); |
| base = npctl->ctrl->pin_banks[group_num].reg_base; |
| |
| mode = ma35_gpio_get_mode(base + MA35_GP_REG_MODE, port); |
| if (mode == MA35_GP_MODE_OUTPUT) |
| return 1; |
| |
| return 0; |
| } |
| |
| static int ma35_pinconf_get_pull(struct ma35_pinctrl *npctl, unsigned int pin) |
| { |
| unsigned int port, group_num; |
| void __iomem *base; |
| u32 regval, pull_sel; |
| |
| ma35_gpio_cla_port(pin, &group_num, &port); |
| base = npctl->ctrl->pin_banks[group_num].reg_base; |
| |
| regval = readl(base + MA35_GP_REG_PUSEL); |
| |
| pull_sel = field_get(MA35_GP_PUSEL_MASK(port), regval); |
| |
| switch (pull_sel) { |
| case MA35_GP_PUSEL_PULL_UP: |
| return PIN_CONFIG_BIAS_PULL_UP; |
| |
| case MA35_GP_PUSEL_PULL_DOWN: |
| return PIN_CONFIG_BIAS_PULL_DOWN; |
| |
| case MA35_GP_PUSEL_DISABLE: |
| return PIN_CONFIG_BIAS_DISABLE; |
| } |
| |
| return PIN_CONFIG_BIAS_DISABLE; |
| } |
| |
| static int ma35_pinconf_set_output(struct ma35_pinctrl *npctl, unsigned int pin, bool out) |
| { |
| unsigned int port, group_num; |
| void __iomem *base; |
| |
| ma35_gpio_cla_port(pin, &group_num, &port); |
| base = npctl->ctrl->pin_banks[group_num].reg_base; |
| |
| ma35_gpio_set_mode(base + MA35_GP_REG_MODE, port, MA35_GP_MODE_OUTPUT); |
| |
| return 0; |
| } |
| |
| static int ma35_pinconf_get_power_source(struct ma35_pinctrl *npctl, unsigned int pin) |
| { |
| unsigned int port, group_num; |
| void __iomem *base; |
| u32 regval; |
| |
| ma35_gpio_cla_port(pin, &group_num, &port); |
| base = npctl->ctrl->pin_banks[group_num].reg_base; |
| |
| regval = readl(base + MA35_GP_REG_SPW); |
| |
| if (regval & BIT(port)) |
| return MVOLT_3300; |
| else |
| return MVOLT_1800; |
| } |
| |
| static int ma35_pinconf_set_power_source(struct ma35_pinctrl *npctl, |
| unsigned int pin, int arg) |
| { |
| unsigned int port, group_num; |
| void __iomem *base; |
| u32 regval; |
| |
| if ((arg != MVOLT_1800) && (arg != MVOLT_3300)) |
| return -EINVAL; |
| |
| ma35_gpio_cla_port(pin, &group_num, &port); |
| base = npctl->ctrl->pin_banks[group_num].reg_base; |
| |
| regval = readl(base + MA35_GP_REG_SPW); |
| |
| if (arg == MVOLT_1800) |
| regval &= ~BIT(port); |
| else |
| regval |= BIT(port); |
| |
| writel(regval, base + MA35_GP_REG_SPW); |
| |
| return 0; |
| } |
| |
| static int ma35_pinconf_get_drive_strength(struct ma35_pinctrl *npctl, unsigned int pin, |
| u32 *strength) |
| { |
| unsigned int port, group_num; |
| void __iomem *base; |
| u32 regval, ds_val; |
| |
| ma35_gpio_cla_port(pin, &group_num, &port); |
| base = npctl->ctrl->pin_banks[group_num].reg_base; |
| |
| regval = readl(base + MA35_GP_DS_REG(port)); |
| ds_val = field_get(MA35_GP_DS_MASK(port), regval); |
| |
| if (ma35_pinconf_get_power_source(npctl, pin) == MVOLT_1800) |
| *strength = ds_1800mv_tbl[ds_val]; |
| else |
| *strength = ds_3300mv_tbl[ds_val]; |
| |
| return 0; |
| } |
| |
| static int ma35_pinconf_set_drive_strength(struct ma35_pinctrl *npctl, unsigned int pin, |
| int strength) |
| { |
| unsigned int port, group_num; |
| void __iomem *base; |
| int i, ds_val = -1; |
| u32 regval; |
| |
| if (ma35_pinconf_get_power_source(npctl, pin) == MVOLT_1800) { |
| for (i = 0; i < ARRAY_SIZE(ds_1800mv_tbl); i++) { |
| if (ds_1800mv_tbl[i] == strength) { |
| ds_val = i; |
| break; |
| } |
| } |
| } else { |
| for (i = 0; i < ARRAY_SIZE(ds_3300mv_tbl); i++) { |
| if (ds_3300mv_tbl[i] == strength) { |
| ds_val = i; |
| break; |
| } |
| } |
| } |
| if (ds_val == -1) |
| return -EINVAL; |
| |
| ma35_gpio_cla_port(pin, &group_num, &port); |
| base = npctl->ctrl->pin_banks[group_num].reg_base; |
| |
| regval = readl(base + MA35_GP_DS_REG(port)); |
| regval &= ~MA35_GP_DS_MASK(port); |
| regval |= field_prep(MA35_GP_DS_MASK(port), ds_val); |
| |
| writel(regval, base + MA35_GP_DS_REG(port)); |
| |
| return 0; |
| } |
| |
| static int ma35_pinconf_get_schmitt_enable(struct ma35_pinctrl *npctl, unsigned int pin) |
| { |
| unsigned int port, group_num; |
| void __iomem *base; |
| u32 regval; |
| |
| ma35_gpio_cla_port(pin, &group_num, &port); |
| base = npctl->ctrl->pin_banks[group_num].reg_base; |
| |
| regval = readl(base + MA35_GP_REG_SMTEN); |
| |
| return !!(regval & BIT(port)); |
| } |
| |
| static int ma35_pinconf_set_schmitt(struct ma35_pinctrl *npctl, unsigned int pin, int enable) |
| { |
| unsigned int port, group_num; |
| void __iomem *base; |
| u32 regval; |
| |
| ma35_gpio_cla_port(pin, &group_num, &port); |
| base = npctl->ctrl->pin_banks[group_num].reg_base; |
| |
| regval = readl(base + MA35_GP_REG_SMTEN); |
| |
| if (enable) |
| regval |= BIT(port); |
| else |
| regval &= ~BIT(port); |
| |
| writel(regval, base + MA35_GP_REG_SMTEN); |
| |
| return 0; |
| } |
| |
| static int ma35_pinconf_get_slew_rate(struct ma35_pinctrl *npctl, unsigned int pin) |
| { |
| unsigned int port, group_num; |
| void __iomem *base; |
| u32 regval; |
| |
| ma35_gpio_cla_port(pin, &group_num, &port); |
| base = npctl->ctrl->pin_banks[group_num].reg_base; |
| |
| regval = readl(base + MA35_GP_REG_SLEWCTL); |
| |
| return field_get(MA35_GP_SLEWCTL_MASK(port), regval); |
| } |
| |
| static int ma35_pinconf_set_slew_rate(struct ma35_pinctrl *npctl, unsigned int pin, int rate) |
| { |
| unsigned int port, group_num; |
| void __iomem *base; |
| u32 regval; |
| |
| ma35_gpio_cla_port(pin, &group_num, &port); |
| base = npctl->ctrl->pin_banks[group_num].reg_base; |
| |
| regval = readl(base + MA35_GP_REG_SLEWCTL); |
| regval &= ~MA35_GP_SLEWCTL_MASK(port); |
| regval |= field_prep(MA35_GP_SLEWCTL_MASK(port), rate); |
| |
| writel(regval, base + MA35_GP_REG_SLEWCTL); |
| |
| return 0; |
| } |
| |
| static int ma35_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *config) |
| { |
| struct ma35_pinctrl *npctl = pinctrl_dev_get_drvdata(pctldev); |
| enum pin_config_param param = pinconf_to_config_param(*config); |
| u32 arg; |
| int ret; |
| |
| switch (param) { |
| case PIN_CONFIG_BIAS_DISABLE: |
| case PIN_CONFIG_BIAS_PULL_DOWN: |
| case PIN_CONFIG_BIAS_PULL_UP: |
| if (ma35_pinconf_get_pull(npctl, pin) != param) |
| return -EINVAL; |
| arg = 1; |
| break; |
| |
| case PIN_CONFIG_DRIVE_STRENGTH: |
| ret = ma35_pinconf_get_drive_strength(npctl, pin, &arg); |
| if (ret) |
| return ret; |
| break; |
| |
| case PIN_CONFIG_INPUT_SCHMITT_ENABLE: |
| arg = ma35_pinconf_get_schmitt_enable(npctl, pin); |
| break; |
| |
| case PIN_CONFIG_SLEW_RATE: |
| arg = ma35_pinconf_get_slew_rate(npctl, pin); |
| break; |
| |
| case PIN_CONFIG_OUTPUT_ENABLE: |
| arg = ma35_pinconf_get_output(npctl, pin); |
| break; |
| |
| case PIN_CONFIG_POWER_SOURCE: |
| arg = ma35_pinconf_get_power_source(npctl, pin); |
| break; |
| |
| default: |
| return -EINVAL; |
| } |
| *config = pinconf_to_config_packed(param, arg); |
| |
| return 0; |
| } |
| |
| static int ma35_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, |
| unsigned long *configs, unsigned int num_configs) |
| { |
| struct ma35_pinctrl *npctl = pinctrl_dev_get_drvdata(pctldev); |
| enum pin_config_param param; |
| unsigned int arg = 0; |
| int i, ret = 0; |
| |
| for (i = 0; i < num_configs; i++) { |
| param = pinconf_to_config_param(configs[i]); |
| arg = pinconf_to_config_argument(configs[i]); |
| |
| switch (param) { |
| case PIN_CONFIG_BIAS_DISABLE: |
| case PIN_CONFIG_BIAS_PULL_UP: |
| case PIN_CONFIG_BIAS_PULL_DOWN: |
| ret = ma35_pinconf_set_pull(npctl, pin, param); |
| break; |
| |
| case PIN_CONFIG_DRIVE_STRENGTH: |
| ret = ma35_pinconf_set_drive_strength(npctl, pin, arg); |
| break; |
| |
| case PIN_CONFIG_INPUT_SCHMITT_ENABLE: |
| ret = ma35_pinconf_set_schmitt(npctl, pin, 1); |
| break; |
| |
| case PIN_CONFIG_INPUT_SCHMITT: |
| ret = ma35_pinconf_set_schmitt(npctl, pin, arg); |
| break; |
| |
| case PIN_CONFIG_SLEW_RATE: |
| ret = ma35_pinconf_set_slew_rate(npctl, pin, arg); |
| break; |
| |
| case PIN_CONFIG_OUTPUT_ENABLE: |
| ret = ma35_pinconf_set_output(npctl, pin, arg); |
| break; |
| |
| case PIN_CONFIG_POWER_SOURCE: |
| ret = ma35_pinconf_set_power_source(npctl, pin, arg); |
| break; |
| |
| default: |
| return -EINVAL; |
| } |
| |
| if (ret) |
| break; |
| } |
| return ret; |
| } |
| |
| static const struct pinconf_ops ma35_pinconf_ops = { |
| .pin_config_get = ma35_pinconf_get, |
| .pin_config_set = ma35_pinconf_set, |
| .is_generic = true, |
| }; |
| |
| static int ma35_pinctrl_parse_groups(struct device_node *np, struct ma35_pin_group *grp, |
| struct ma35_pinctrl *npctl, u32 index) |
| { |
| struct ma35_pin_setting *pin; |
| unsigned long *configs; |
| unsigned int nconfigs; |
| int i, j, count, ret; |
| u32 *elems; |
| |
| grp->name = np->name; |
| |
| ret = pinconf_generic_parse_dt_config(np, NULL, &configs, &nconfigs); |
| if (ret) |
| return ret; |
| |
| count = of_property_count_elems_of_size(np, "nuvoton,pins", sizeof(u32)); |
| if (!count || count % 3) |
| return -EINVAL; |
| |
| elems = devm_kmalloc_array(npctl->dev, count, sizeof(u32), GFP_KERNEL); |
| if (!elems) |
| return -ENOMEM; |
| |
| ret = of_property_read_u32_array(np, "nuvoton,pins", elems, count); |
| if (ret) |
| return -EINVAL; |
| |
| grp->npins = count / 3; |
| |
| grp->pins = devm_kcalloc(npctl->dev, grp->npins, sizeof(*grp->pins), GFP_KERNEL); |
| if (!grp->pins) |
| return -ENOMEM; |
| |
| grp->settings = devm_kcalloc(npctl->dev, grp->npins, sizeof(*grp->settings), GFP_KERNEL); |
| if (!grp->settings) |
| return -ENOMEM; |
| |
| pin = grp->settings; |
| |
| for (i = 0, j = 0; i < count; i += 3, j++) { |
| pin->offset = elems[i] * MA35_MFP_REG_SZ_PER_BANK + MA35_MFP_REG_BASE; |
| pin->shift = (elems[i + 1] * MA35_MFP_BITS_PER_PORT) % 32; |
| pin->muxval = elems[i + 2]; |
| pin->configs = configs; |
| pin->nconfigs = nconfigs; |
| grp->pins[j] = npctl->info->get_pin_num(pin->offset, pin->shift); |
| pin++; |
| } |
| return 0; |
| } |
| |
| static int ma35_pinctrl_parse_functions(struct device_node *np, struct ma35_pinctrl *npctl, |
| u32 index) |
| { |
| struct device_node *child; |
| struct ma35_pin_func *func; |
| struct ma35_pin_group *grp; |
| static u32 grp_index; |
| u32 ret, i = 0; |
| |
| dev_dbg(npctl->dev, "parse function(%d): %s\n", index, np->name); |
| |
| func = &npctl->functions[index]; |
| func->name = np->name; |
| func->ngroups = of_get_child_count(np); |
| |
| if (func->ngroups <= 0) |
| return 0; |
| |
| func->groups = devm_kcalloc(npctl->dev, func->ngroups, sizeof(char *), GFP_KERNEL); |
| if (!func->groups) |
| return -ENOMEM; |
| |
| for_each_child_of_node(np, child) { |
| func->groups[i] = child->name; |
| grp = &npctl->groups[grp_index++]; |
| ret = ma35_pinctrl_parse_groups(child, grp, npctl, i++); |
| if (ret) { |
| of_node_put(child); |
| return ret; |
| } |
| } |
| return 0; |
| } |
| |
| static int ma35_pinctrl_probe_dt(struct platform_device *pdev, struct ma35_pinctrl *npctl) |
| { |
| struct fwnode_handle *child; |
| u32 idx = 0; |
| int ret; |
| |
| device_for_each_child_node(&pdev->dev, child) { |
| if (fwnode_property_present(child, "gpio-controller")) |
| continue; |
| npctl->nfunctions++; |
| npctl->ngroups += of_get_child_count(to_of_node(child)); |
| } |
| |
| if (!npctl->nfunctions) |
| return -EINVAL; |
| |
| npctl->functions = devm_kcalloc(&pdev->dev, npctl->nfunctions, |
| sizeof(*npctl->functions), GFP_KERNEL); |
| if (!npctl->functions) |
| return -ENOMEM; |
| |
| npctl->groups = devm_kcalloc(&pdev->dev, npctl->ngroups, |
| sizeof(*npctl->groups), GFP_KERNEL); |
| if (!npctl->groups) |
| return -ENOMEM; |
| |
| device_for_each_child_node(&pdev->dev, child) { |
| if (fwnode_property_present(child, "gpio-controller")) |
| continue; |
| |
| ret = ma35_pinctrl_parse_functions(to_of_node(child), npctl, idx++); |
| if (ret) { |
| fwnode_handle_put(child); |
| dev_err(&pdev->dev, "failed to parse function\n"); |
| return ret; |
| } |
| } |
| return 0; |
| } |
| |
| int ma35_pinctrl_probe(struct platform_device *pdev, const struct ma35_pinctrl_soc_info *info) |
| { |
| struct pinctrl_desc *ma35_pinctrl_desc; |
| struct device *dev = &pdev->dev; |
| struct ma35_pinctrl *npctl; |
| int ret; |
| |
| if (!info || !info->pins || !info->npins) { |
| dev_err(&pdev->dev, "wrong pinctrl info\n"); |
| return -EINVAL; |
| } |
| |
| npctl = devm_kzalloc(&pdev->dev, sizeof(*npctl), GFP_KERNEL); |
| if (!npctl) |
| return -ENOMEM; |
| |
| ma35_pinctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*ma35_pinctrl_desc), GFP_KERNEL); |
| if (!ma35_pinctrl_desc) |
| return -ENOMEM; |
| |
| npctl->ctrl = devm_kzalloc(&pdev->dev, sizeof(*npctl->ctrl), GFP_KERNEL); |
| if (!npctl->ctrl) |
| return -ENOMEM; |
| |
| ma35_pinctrl_desc->name = dev_name(&pdev->dev); |
| ma35_pinctrl_desc->pins = info->pins; |
| ma35_pinctrl_desc->npins = info->npins; |
| ma35_pinctrl_desc->pctlops = &ma35_pctrl_ops; |
| ma35_pinctrl_desc->pmxops = &ma35_pmx_ops; |
| ma35_pinctrl_desc->confops = &ma35_pinconf_ops; |
| ma35_pinctrl_desc->owner = THIS_MODULE; |
| |
| npctl->info = info; |
| npctl->dev = &pdev->dev; |
| |
| npctl->regmap = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "nuvoton,sys"); |
| if (IS_ERR(npctl->regmap)) |
| return dev_err_probe(&pdev->dev, PTR_ERR(npctl->regmap), |
| "No syscfg phandle specified\n"); |
| |
| ret = ma35_pinctrl_get_soc_data(npctl, pdev); |
| if (ret) |
| return dev_err_probe(&pdev->dev, ret, "fail to get soc data\n"); |
| |
| platform_set_drvdata(pdev, npctl); |
| |
| ret = ma35_pinctrl_probe_dt(pdev, npctl); |
| if (ret) |
| return dev_err_probe(&pdev->dev, ret, "fail to probe MA35 pinctrl dt\n"); |
| |
| ret = devm_pinctrl_register_and_init(dev, ma35_pinctrl_desc, npctl, &npctl->pctl); |
| if (ret) |
| return dev_err_probe(&pdev->dev, ret, "fail to register MA35 pinctrl\n"); |
| |
| ret = pinctrl_enable(npctl->pctl); |
| if (ret) |
| return dev_err_probe(&pdev->dev, ret, "fail to enable MA35 pinctrl\n"); |
| |
| return ma35_gpiolib_register(pdev, npctl); |
| } |
| |
| int ma35_pinctrl_suspend(struct device *dev) |
| { |
| struct ma35_pinctrl *npctl = dev_get_drvdata(dev); |
| |
| return pinctrl_force_sleep(npctl->pctl); |
| } |
| |
| int ma35_pinctrl_resume(struct device *dev) |
| { |
| struct ma35_pinctrl *npctl = dev_get_drvdata(dev); |
| |
| return pinctrl_force_default(npctl->pctl); |
| } |