| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * OWL SoC's Pinctrl driver | |
| * | |
| * Copyright (c) 2014 Actions Semi Inc. | |
| * Author: David Liu <liuwei@actions-semi.com> | |
| * | |
| * Copyright (c) 2018 Linaro Ltd. | |
| * Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org> | |
| */ | |
| #include <linux/clk.h> | |
| #include <linux/err.h> | |
| #include <linux/gpio/driver.h> | |
| #include <linux/io.h> | |
| #include <linux/irq.h> | |
| #include <linux/module.h> | |
| #include <linux/of.h> | |
| #include <linux/platform_device.h> | |
| #include <linux/seq_file.h> | |
| #include <linux/slab.h> | |
| #include <linux/spinlock.h> | |
| #include <linux/pinctrl/machine.h> | |
| #include <linux/pinctrl/pinconf-generic.h> | |
| #include <linux/pinctrl/pinconf.h> | |
| #include <linux/pinctrl/pinctrl.h> | |
| #include <linux/pinctrl/pinmux.h> | |
| #include "../core.h" | |
| #include "../pinctrl-utils.h" | |
| #include "pinctrl-owl.h" | |
| /** | |
| * struct owl_pinctrl - pinctrl state of the device | |
| * @dev: device handle | |
| * @pctrldev: pinctrl handle | |
| * @chip: gpio chip | |
| * @lock: spinlock to protect registers | |
| * @clk: clock control | |
| * @soc: reference to soc_data | |
| * @base: pinctrl register base address | |
| * @num_irq: number of possible interrupts | |
| * @irq: interrupt numbers | |
| */ | |
| struct owl_pinctrl { | |
| struct device *dev; | |
| struct pinctrl_dev *pctrldev; | |
| struct gpio_chip chip; | |
| raw_spinlock_t lock; | |
| struct clk *clk; | |
| const struct owl_pinctrl_soc_data *soc; | |
| void __iomem *base; | |
| unsigned int num_irq; | |
| unsigned int *irq; | |
| }; | |
| static void owl_update_bits(void __iomem *base, u32 mask, u32 val) | |
| { | |
| u32 reg_val; | |
| reg_val = readl_relaxed(base); | |
| reg_val = (reg_val & ~mask) | (val & mask); | |
| writel_relaxed(reg_val, base); | |
| } | |
| static u32 owl_read_field(struct owl_pinctrl *pctrl, u32 reg, | |
| u32 bit, u32 width) | |
| { | |
| u32 tmp, mask; | |
| tmp = readl_relaxed(pctrl->base + reg); | |
| mask = (1 << width) - 1; | |
| return (tmp >> bit) & mask; | |
| } | |
| static void owl_write_field(struct owl_pinctrl *pctrl, u32 reg, u32 arg, | |
| u32 bit, u32 width) | |
| { | |
| u32 mask; | |
| mask = (1 << width) - 1; | |
| mask = mask << bit; | |
| owl_update_bits(pctrl->base + reg, mask, (arg << bit)); | |
| } | |
| static int owl_get_groups_count(struct pinctrl_dev *pctrldev) | |
| { | |
| struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); | |
| return pctrl->soc->ngroups; | |
| } | |
| static const char *owl_get_group_name(struct pinctrl_dev *pctrldev, | |
| unsigned int group) | |
| { | |
| struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); | |
| return pctrl->soc->groups[group].name; | |
| } | |
| static int owl_get_group_pins(struct pinctrl_dev *pctrldev, | |
| unsigned int group, | |
| const unsigned int **pins, | |
| unsigned int *num_pins) | |
| { | |
| struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); | |
| *pins = pctrl->soc->groups[group].pads; | |
| *num_pins = pctrl->soc->groups[group].npads; | |
| return 0; | |
| } | |
| static void owl_pin_dbg_show(struct pinctrl_dev *pctrldev, | |
| struct seq_file *s, | |
| unsigned int offset) | |
| { | |
| struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); | |
| seq_printf(s, "%s", dev_name(pctrl->dev)); | |
| } | |
| static const struct pinctrl_ops owl_pinctrl_ops = { | |
| .get_groups_count = owl_get_groups_count, | |
| .get_group_name = owl_get_group_name, | |
| .get_group_pins = owl_get_group_pins, | |
| .pin_dbg_show = owl_pin_dbg_show, | |
| .dt_node_to_map = pinconf_generic_dt_node_to_map_all, | |
| .dt_free_map = pinctrl_utils_free_map, | |
| }; | |
| static int owl_get_funcs_count(struct pinctrl_dev *pctrldev) | |
| { | |
| struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); | |
| return pctrl->soc->nfunctions; | |
| } | |
| static const char *owl_get_func_name(struct pinctrl_dev *pctrldev, | |
| unsigned int function) | |
| { | |
| struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); | |
| return pctrl->soc->functions[function].name; | |
| } | |
| static int owl_get_func_groups(struct pinctrl_dev *pctrldev, | |
| unsigned int function, | |
| const char * const **groups, | |
| unsigned int * const num_groups) | |
| { | |
| struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); | |
| *groups = pctrl->soc->functions[function].groups; | |
| *num_groups = pctrl->soc->functions[function].ngroups; | |
| return 0; | |
| } | |
| static inline int get_group_mfp_mask_val(const struct owl_pingroup *g, | |
| int function, | |
| u32 *mask, | |
| u32 *val) | |
| { | |
| int id; | |
| u32 option_num; | |
| u32 option_mask; | |
| for (id = 0; id < g->nfuncs; id++) { | |
| if (g->funcs[id] == function) | |
| break; | |
| } | |
| if (WARN_ON(id == g->nfuncs)) | |
| return -EINVAL; | |
| option_num = (1 << g->mfpctl_width); | |
| if (id > option_num) | |
| id -= option_num; | |
| option_mask = option_num - 1; | |
| *mask = (option_mask << g->mfpctl_shift); | |
| *val = (id << g->mfpctl_shift); | |
| return 0; | |
| } | |
| static int owl_set_mux(struct pinctrl_dev *pctrldev, | |
| unsigned int function, | |
| unsigned int group) | |
| { | |
| struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); | |
| const struct owl_pingroup *g; | |
| unsigned long flags; | |
| u32 val, mask; | |
| g = &pctrl->soc->groups[group]; | |
| if (get_group_mfp_mask_val(g, function, &mask, &val)) | |
| return -EINVAL; | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| owl_update_bits(pctrl->base + g->mfpctl_reg, mask, val); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| return 0; | |
| } | |
| static const struct pinmux_ops owl_pinmux_ops = { | |
| .get_functions_count = owl_get_funcs_count, | |
| .get_function_name = owl_get_func_name, | |
| .get_function_groups = owl_get_func_groups, | |
| .set_mux = owl_set_mux, | |
| }; | |
| static int owl_pad_pinconf_reg(const struct owl_padinfo *info, | |
| unsigned int param, | |
| u32 *reg, | |
| u32 *bit, | |
| u32 *width) | |
| { | |
| switch (param) { | |
| case PIN_CONFIG_BIAS_BUS_HOLD: | |
| case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: | |
| case PIN_CONFIG_BIAS_PULL_DOWN: | |
| case PIN_CONFIG_BIAS_PULL_UP: | |
| if (!info->pullctl) | |
| return -EINVAL; | |
| *reg = info->pullctl->reg; | |
| *bit = info->pullctl->shift; | |
| *width = info->pullctl->width; | |
| break; | |
| case PIN_CONFIG_INPUT_SCHMITT_ENABLE: | |
| if (!info->st) | |
| return -EINVAL; | |
| *reg = info->st->reg; | |
| *bit = info->st->shift; | |
| *width = info->st->width; | |
| break; | |
| default: | |
| return -ENOTSUPP; | |
| } | |
| return 0; | |
| } | |
| static int owl_pin_config_get(struct pinctrl_dev *pctrldev, | |
| unsigned int pin, | |
| unsigned long *config) | |
| { | |
| int ret = 0; | |
| struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); | |
| const struct owl_padinfo *info; | |
| unsigned int param = pinconf_to_config_param(*config); | |
| u32 reg, bit, width, arg; | |
| info = &pctrl->soc->padinfo[pin]; | |
| ret = owl_pad_pinconf_reg(info, param, ®, &bit, &width); | |
| if (ret) | |
| return ret; | |
| arg = owl_read_field(pctrl, reg, bit, width); | |
| if (!pctrl->soc->padctl_val2arg) | |
| return -ENOTSUPP; | |
| ret = pctrl->soc->padctl_val2arg(info, param, &arg); | |
| if (ret) | |
| return ret; | |
| *config = pinconf_to_config_packed(param, arg); | |
| return ret; | |
| } | |
| static int owl_pin_config_set(struct pinctrl_dev *pctrldev, | |
| unsigned int pin, | |
| unsigned long *configs, | |
| unsigned int num_configs) | |
| { | |
| struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); | |
| const struct owl_padinfo *info; | |
| unsigned long flags; | |
| unsigned int param; | |
| u32 reg, bit, width, arg; | |
| int ret = 0, i; | |
| info = &pctrl->soc->padinfo[pin]; | |
| for (i = 0; i < num_configs; i++) { | |
| param = pinconf_to_config_param(configs[i]); | |
| arg = pinconf_to_config_argument(configs[i]); | |
| ret = owl_pad_pinconf_reg(info, param, ®, &bit, &width); | |
| if (ret) | |
| return ret; | |
| if (!pctrl->soc->padctl_arg2val) | |
| return -ENOTSUPP; | |
| ret = pctrl->soc->padctl_arg2val(info, param, &arg); | |
| if (ret) | |
| return ret; | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| owl_write_field(pctrl, reg, arg, bit, width); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| } | |
| return ret; | |
| } | |
| static int owl_group_pinconf_reg(const struct owl_pingroup *g, | |
| unsigned int param, | |
| u32 *reg, | |
| u32 *bit, | |
| u32 *width) | |
| { | |
| switch (param) { | |
| case PIN_CONFIG_DRIVE_STRENGTH: | |
| if (g->drv_reg < 0) | |
| return -EINVAL; | |
| *reg = g->drv_reg; | |
| *bit = g->drv_shift; | |
| *width = g->drv_width; | |
| break; | |
| case PIN_CONFIG_SLEW_RATE: | |
| if (g->sr_reg < 0) | |
| return -EINVAL; | |
| *reg = g->sr_reg; | |
| *bit = g->sr_shift; | |
| *width = g->sr_width; | |
| break; | |
| default: | |
| return -ENOTSUPP; | |
| } | |
| return 0; | |
| } | |
| static int owl_group_pinconf_arg2val(const struct owl_pingroup *g, | |
| unsigned int param, | |
| u32 *arg) | |
| { | |
| switch (param) { | |
| case PIN_CONFIG_DRIVE_STRENGTH: | |
| switch (*arg) { | |
| case 2: | |
| *arg = OWL_PINCONF_DRV_2MA; | |
| break; | |
| case 4: | |
| *arg = OWL_PINCONF_DRV_4MA; | |
| break; | |
| case 8: | |
| *arg = OWL_PINCONF_DRV_8MA; | |
| break; | |
| case 12: | |
| *arg = OWL_PINCONF_DRV_12MA; | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| break; | |
| case PIN_CONFIG_SLEW_RATE: | |
| if (*arg) | |
| *arg = OWL_PINCONF_SLEW_FAST; | |
| else | |
| *arg = OWL_PINCONF_SLEW_SLOW; | |
| break; | |
| default: | |
| return -ENOTSUPP; | |
| } | |
| return 0; | |
| } | |
| static int owl_group_pinconf_val2arg(const struct owl_pingroup *g, | |
| unsigned int param, | |
| u32 *arg) | |
| { | |
| switch (param) { | |
| case PIN_CONFIG_DRIVE_STRENGTH: | |
| switch (*arg) { | |
| case OWL_PINCONF_DRV_2MA: | |
| *arg = 2; | |
| break; | |
| case OWL_PINCONF_DRV_4MA: | |
| *arg = 4; | |
| break; | |
| case OWL_PINCONF_DRV_8MA: | |
| *arg = 8; | |
| break; | |
| case OWL_PINCONF_DRV_12MA: | |
| *arg = 12; | |
| break; | |
| default: | |
| return -EINVAL; | |
| } | |
| break; | |
| case PIN_CONFIG_SLEW_RATE: | |
| if (*arg) | |
| *arg = 1; | |
| else | |
| *arg = 0; | |
| break; | |
| default: | |
| return -ENOTSUPP; | |
| } | |
| return 0; | |
| } | |
| static int owl_group_config_get(struct pinctrl_dev *pctrldev, | |
| unsigned int group, | |
| unsigned long *config) | |
| { | |
| const struct owl_pingroup *g; | |
| struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); | |
| unsigned int param = pinconf_to_config_param(*config); | |
| u32 reg, bit, width, arg; | |
| int ret; | |
| g = &pctrl->soc->groups[group]; | |
| ret = owl_group_pinconf_reg(g, param, ®, &bit, &width); | |
| if (ret) | |
| return ret; | |
| arg = owl_read_field(pctrl, reg, bit, width); | |
| ret = owl_group_pinconf_val2arg(g, param, &arg); | |
| if (ret) | |
| return ret; | |
| *config = pinconf_to_config_packed(param, arg); | |
| return ret; | |
| } | |
| static int owl_group_config_set(struct pinctrl_dev *pctrldev, | |
| unsigned int group, | |
| unsigned long *configs, | |
| unsigned int num_configs) | |
| { | |
| const struct owl_pingroup *g; | |
| struct owl_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctrldev); | |
| unsigned long flags; | |
| unsigned int param; | |
| u32 reg, bit, width, arg; | |
| int ret, i; | |
| g = &pctrl->soc->groups[group]; | |
| for (i = 0; i < num_configs; i++) { | |
| param = pinconf_to_config_param(configs[i]); | |
| arg = pinconf_to_config_argument(configs[i]); | |
| ret = owl_group_pinconf_reg(g, param, ®, &bit, &width); | |
| if (ret) | |
| return ret; | |
| ret = owl_group_pinconf_arg2val(g, param, &arg); | |
| if (ret) | |
| return ret; | |
| /* Update register */ | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| owl_write_field(pctrl, reg, arg, bit, width); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| } | |
| return 0; | |
| } | |
| static const struct pinconf_ops owl_pinconf_ops = { | |
| .is_generic = true, | |
| .pin_config_get = owl_pin_config_get, | |
| .pin_config_set = owl_pin_config_set, | |
| .pin_config_group_get = owl_group_config_get, | |
| .pin_config_group_set = owl_group_config_set, | |
| }; | |
| static struct pinctrl_desc owl_pinctrl_desc = { | |
| .pctlops = &owl_pinctrl_ops, | |
| .pmxops = &owl_pinmux_ops, | |
| .confops = &owl_pinconf_ops, | |
| .owner = THIS_MODULE, | |
| }; | |
| static const struct owl_gpio_port * | |
| owl_gpio_get_port(struct owl_pinctrl *pctrl, unsigned int *pin) | |
| { | |
| unsigned int start = 0, i; | |
| for (i = 0; i < pctrl->soc->nports; i++) { | |
| const struct owl_gpio_port *port = &pctrl->soc->ports[i]; | |
| if (*pin >= start && *pin < start + port->pins) { | |
| *pin -= start; | |
| return port; | |
| } | |
| start += port->pins; | |
| } | |
| return NULL; | |
| } | |
| static void owl_gpio_update_reg(void __iomem *base, unsigned int pin, int flag) | |
| { | |
| u32 val; | |
| val = readl_relaxed(base); | |
| if (flag) | |
| val |= BIT(pin); | |
| else | |
| val &= ~BIT(pin); | |
| writel_relaxed(val, base); | |
| } | |
| static int owl_gpio_request(struct gpio_chip *chip, unsigned int offset) | |
| { | |
| struct owl_pinctrl *pctrl = gpiochip_get_data(chip); | |
| const struct owl_gpio_port *port; | |
| void __iomem *gpio_base; | |
| unsigned long flags; | |
| port = owl_gpio_get_port(pctrl, &offset); | |
| if (WARN_ON(port == NULL)) | |
| return -ENODEV; | |
| gpio_base = pctrl->base + port->offset; | |
| /* | |
| * GPIOs have higher priority over other modules, so either setting | |
| * them as OUT or IN is sufficient | |
| */ | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| owl_gpio_update_reg(gpio_base + port->outen, offset, true); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| return 0; | |
| } | |
| static void owl_gpio_free(struct gpio_chip *chip, unsigned int offset) | |
| { | |
| struct owl_pinctrl *pctrl = gpiochip_get_data(chip); | |
| const struct owl_gpio_port *port; | |
| void __iomem *gpio_base; | |
| unsigned long flags; | |
| port = owl_gpio_get_port(pctrl, &offset); | |
| if (WARN_ON(port == NULL)) | |
| return; | |
| gpio_base = pctrl->base + port->offset; | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| /* disable gpio output */ | |
| owl_gpio_update_reg(gpio_base + port->outen, offset, false); | |
| /* disable gpio input */ | |
| owl_gpio_update_reg(gpio_base + port->inen, offset, false); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| } | |
| static int owl_gpio_get(struct gpio_chip *chip, unsigned int offset) | |
| { | |
| struct owl_pinctrl *pctrl = gpiochip_get_data(chip); | |
| const struct owl_gpio_port *port; | |
| void __iomem *gpio_base; | |
| unsigned long flags; | |
| u32 val; | |
| port = owl_gpio_get_port(pctrl, &offset); | |
| if (WARN_ON(port == NULL)) | |
| return -ENODEV; | |
| gpio_base = pctrl->base + port->offset; | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| val = readl_relaxed(gpio_base + port->dat); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| return !!(val & BIT(offset)); | |
| } | |
| static int owl_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) | |
| { | |
| struct owl_pinctrl *pctrl = gpiochip_get_data(chip); | |
| const struct owl_gpio_port *port; | |
| void __iomem *gpio_base; | |
| unsigned long flags; | |
| port = owl_gpio_get_port(pctrl, &offset); | |
| if (WARN_ON(port == NULL)) | |
| return -ENODEV; | |
| gpio_base = pctrl->base + port->offset; | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| owl_gpio_update_reg(gpio_base + port->dat, offset, value); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| return 0; | |
| } | |
| static int owl_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) | |
| { | |
| struct owl_pinctrl *pctrl = gpiochip_get_data(chip); | |
| const struct owl_gpio_port *port; | |
| void __iomem *gpio_base; | |
| unsigned long flags; | |
| port = owl_gpio_get_port(pctrl, &offset); | |
| if (WARN_ON(port == NULL)) | |
| return -ENODEV; | |
| gpio_base = pctrl->base + port->offset; | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| owl_gpio_update_reg(gpio_base + port->outen, offset, false); | |
| owl_gpio_update_reg(gpio_base + port->inen, offset, true); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| return 0; | |
| } | |
| static int owl_gpio_direction_output(struct gpio_chip *chip, | |
| unsigned int offset, int value) | |
| { | |
| struct owl_pinctrl *pctrl = gpiochip_get_data(chip); | |
| const struct owl_gpio_port *port; | |
| void __iomem *gpio_base; | |
| unsigned long flags; | |
| port = owl_gpio_get_port(pctrl, &offset); | |
| if (WARN_ON(port == NULL)) | |
| return -ENODEV; | |
| gpio_base = pctrl->base + port->offset; | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| owl_gpio_update_reg(gpio_base + port->inen, offset, false); | |
| owl_gpio_update_reg(gpio_base + port->outen, offset, true); | |
| owl_gpio_update_reg(gpio_base + port->dat, offset, value); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| return 0; | |
| } | |
| static void irq_set_type(struct owl_pinctrl *pctrl, int gpio, unsigned int type) | |
| { | |
| const struct owl_gpio_port *port; | |
| void __iomem *gpio_base; | |
| unsigned long flags; | |
| unsigned int offset, value, irq_type = 0; | |
| switch (type) { | |
| case IRQ_TYPE_EDGE_BOTH: | |
| /* | |
| * Since the hardware doesn't support interrupts on both edges, | |
| * emulate it in the software by setting the single edge | |
| * interrupt and switching to the opposite edge while ACKing | |
| * the interrupt | |
| */ | |
| if (owl_gpio_get(&pctrl->chip, gpio)) | |
| irq_type = OWL_GPIO_INT_EDGE_FALLING; | |
| else | |
| irq_type = OWL_GPIO_INT_EDGE_RISING; | |
| break; | |
| case IRQ_TYPE_EDGE_RISING: | |
| irq_type = OWL_GPIO_INT_EDGE_RISING; | |
| break; | |
| case IRQ_TYPE_EDGE_FALLING: | |
| irq_type = OWL_GPIO_INT_EDGE_FALLING; | |
| break; | |
| case IRQ_TYPE_LEVEL_HIGH: | |
| irq_type = OWL_GPIO_INT_LEVEL_HIGH; | |
| break; | |
| case IRQ_TYPE_LEVEL_LOW: | |
| irq_type = OWL_GPIO_INT_LEVEL_LOW; | |
| break; | |
| default: | |
| break; | |
| } | |
| port = owl_gpio_get_port(pctrl, &gpio); | |
| if (WARN_ON(port == NULL)) | |
| return; | |
| gpio_base = pctrl->base + port->offset; | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| offset = (gpio < 16) ? 4 : 0; | |
| value = readl_relaxed(gpio_base + port->intc_type + offset); | |
| value &= ~(OWL_GPIO_INT_MASK << ((gpio % 16) * 2)); | |
| value |= irq_type << ((gpio % 16) * 2); | |
| writel_relaxed(value, gpio_base + port->intc_type + offset); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| } | |
| static void owl_gpio_irq_mask(struct irq_data *data) | |
| { | |
| struct gpio_chip *gc = irq_data_get_irq_chip_data(data); | |
| struct owl_pinctrl *pctrl = gpiochip_get_data(gc); | |
| irq_hw_number_t hwirq = irqd_to_hwirq(data); | |
| const struct owl_gpio_port *port; | |
| unsigned int gpio = hwirq; | |
| void __iomem *gpio_base; | |
| unsigned long flags; | |
| u32 val; | |
| port = owl_gpio_get_port(pctrl, &gpio); | |
| if (WARN_ON(port == NULL)) | |
| return; | |
| gpio_base = pctrl->base + port->offset; | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| owl_gpio_update_reg(gpio_base + port->intc_msk, gpio, false); | |
| /* disable port interrupt if no interrupt pending bit is active */ | |
| val = readl_relaxed(gpio_base + port->intc_msk); | |
| if (val == 0) | |
| owl_gpio_update_reg(gpio_base + port->intc_ctl, | |
| OWL_GPIO_CTLR_ENABLE + port->shared_ctl_offset * 5, false); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| gpiochip_disable_irq(gc, hwirq); | |
| } | |
| static void owl_gpio_irq_unmask(struct irq_data *data) | |
| { | |
| struct gpio_chip *gc = irq_data_get_irq_chip_data(data); | |
| struct owl_pinctrl *pctrl = gpiochip_get_data(gc); | |
| irq_hw_number_t hwirq = irqd_to_hwirq(data); | |
| const struct owl_gpio_port *port; | |
| unsigned int gpio = hwirq; | |
| void __iomem *gpio_base; | |
| unsigned long flags; | |
| u32 value; | |
| port = owl_gpio_get_port(pctrl, &gpio); | |
| if (WARN_ON(port == NULL)) | |
| return; | |
| gpiochip_enable_irq(gc, hwirq); | |
| gpio_base = pctrl->base + port->offset; | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| /* enable port interrupt */ | |
| value = readl_relaxed(gpio_base + port->intc_ctl); | |
| value |= ((BIT(OWL_GPIO_CTLR_ENABLE) | BIT(OWL_GPIO_CTLR_SAMPLE_CLK_24M)) | |
| << port->shared_ctl_offset * 5); | |
| writel_relaxed(value, gpio_base + port->intc_ctl); | |
| /* enable GPIO interrupt */ | |
| owl_gpio_update_reg(gpio_base + port->intc_msk, gpio, true); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| } | |
| static void owl_gpio_irq_ack(struct irq_data *data) | |
| { | |
| struct gpio_chip *gc = irq_data_get_irq_chip_data(data); | |
| struct owl_pinctrl *pctrl = gpiochip_get_data(gc); | |
| irq_hw_number_t hwirq = irqd_to_hwirq(data); | |
| const struct owl_gpio_port *port; | |
| unsigned int gpio = hwirq; | |
| void __iomem *gpio_base; | |
| unsigned long flags; | |
| /* | |
| * Switch the interrupt edge to the opposite edge of the interrupt | |
| * which got triggered for the case of emulating both edges | |
| */ | |
| if (irqd_get_trigger_type(data) == IRQ_TYPE_EDGE_BOTH) { | |
| if (owl_gpio_get(gc, hwirq)) | |
| irq_set_type(pctrl, hwirq, IRQ_TYPE_EDGE_FALLING); | |
| else | |
| irq_set_type(pctrl, hwirq, IRQ_TYPE_EDGE_RISING); | |
| } | |
| port = owl_gpio_get_port(pctrl, &gpio); | |
| if (WARN_ON(port == NULL)) | |
| return; | |
| gpio_base = pctrl->base + port->offset; | |
| raw_spin_lock_irqsave(&pctrl->lock, flags); | |
| owl_gpio_update_reg(gpio_base + port->intc_ctl, | |
| OWL_GPIO_CTLR_PENDING + port->shared_ctl_offset * 5, true); | |
| raw_spin_unlock_irqrestore(&pctrl->lock, flags); | |
| } | |
| static int owl_gpio_irq_set_type(struct irq_data *data, unsigned int type) | |
| { | |
| struct gpio_chip *gc = irq_data_get_irq_chip_data(data); | |
| struct owl_pinctrl *pctrl = gpiochip_get_data(gc); | |
| if (type & IRQ_TYPE_LEVEL_MASK) | |
| irq_set_handler_locked(data, handle_level_irq); | |
| else | |
| irq_set_handler_locked(data, handle_edge_irq); | |
| irq_set_type(pctrl, data->hwirq, type); | |
| return 0; | |
| } | |
| static const struct irq_chip owl_gpio_irqchip = { | |
| .name = "owl-irq", | |
| .irq_ack = owl_gpio_irq_ack, | |
| .irq_mask = owl_gpio_irq_mask, | |
| .irq_unmask = owl_gpio_irq_unmask, | |
| .irq_set_type = owl_gpio_irq_set_type, | |
| .flags = IRQCHIP_IMMUTABLE, | |
| GPIOCHIP_IRQ_RESOURCE_HELPERS, | |
| }; | |
| static void owl_gpio_irq_handler(struct irq_desc *desc) | |
| { | |
| struct owl_pinctrl *pctrl = irq_desc_get_handler_data(desc); | |
| struct irq_chip *chip = irq_desc_get_chip(desc); | |
| struct irq_domain *domain = pctrl->chip.irq.domain; | |
| unsigned int parent = irq_desc_get_irq(desc); | |
| const struct owl_gpio_port *port; | |
| void __iomem *base; | |
| unsigned int pin, offset = 0, i; | |
| unsigned long pending_irq; | |
| chained_irq_enter(chip, desc); | |
| for (i = 0; i < pctrl->soc->nports; i++) { | |
| port = &pctrl->soc->ports[i]; | |
| base = pctrl->base + port->offset; | |
| /* skip ports that are not associated with this irq */ | |
| if (parent != pctrl->irq[i]) | |
| goto skip; | |
| pending_irq = readl_relaxed(base + port->intc_pd); | |
| for_each_set_bit(pin, &pending_irq, port->pins) { | |
| generic_handle_domain_irq(domain, offset + pin); | |
| /* clear pending interrupt */ | |
| owl_gpio_update_reg(base + port->intc_pd, pin, true); | |
| } | |
| skip: | |
| offset += port->pins; | |
| } | |
| chained_irq_exit(chip, desc); | |
| } | |
| static int owl_gpio_init(struct owl_pinctrl *pctrl) | |
| { | |
| struct gpio_chip *chip; | |
| struct gpio_irq_chip *gpio_irq; | |
| int ret, i, j, offset; | |
| chip = &pctrl->chip; | |
| chip->base = -1; | |
| chip->ngpio = pctrl->soc->ngpios; | |
| chip->label = dev_name(pctrl->dev); | |
| chip->parent = pctrl->dev; | |
| chip->owner = THIS_MODULE; | |
| gpio_irq = &chip->irq; | |
| gpio_irq_chip_set_chip(gpio_irq, &owl_gpio_irqchip); | |
| gpio_irq->handler = handle_simple_irq; | |
| gpio_irq->default_type = IRQ_TYPE_NONE; | |
| gpio_irq->parent_handler = owl_gpio_irq_handler; | |
| gpio_irq->parent_handler_data = pctrl; | |
| gpio_irq->num_parents = pctrl->num_irq; | |
| gpio_irq->parents = pctrl->irq; | |
| gpio_irq->map = devm_kcalloc(pctrl->dev, chip->ngpio, | |
| sizeof(*gpio_irq->map), GFP_KERNEL); | |
| if (!gpio_irq->map) | |
| return -ENOMEM; | |
| for (i = 0, offset = 0; i < pctrl->soc->nports; i++) { | |
| const struct owl_gpio_port *port = &pctrl->soc->ports[i]; | |
| for (j = 0; j < port->pins; j++) | |
| gpio_irq->map[offset + j] = gpio_irq->parents[i]; | |
| offset += port->pins; | |
| } | |
| ret = gpiochip_add_data(&pctrl->chip, pctrl); | |
| if (ret) { | |
| dev_err(pctrl->dev, "failed to register gpiochip\n"); | |
| return ret; | |
| } | |
| return 0; | |
| } | |
| int owl_pinctrl_probe(struct platform_device *pdev, | |
| struct owl_pinctrl_soc_data *soc_data) | |
| { | |
| struct owl_pinctrl *pctrl; | |
| int ret, i; | |
| pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); | |
| if (!pctrl) | |
| return -ENOMEM; | |
| pctrl->base = devm_platform_ioremap_resource(pdev, 0); | |
| if (IS_ERR(pctrl->base)) | |
| return PTR_ERR(pctrl->base); | |
| /* enable GPIO/MFP clock */ | |
| pctrl->clk = devm_clk_get(&pdev->dev, NULL); | |
| if (IS_ERR(pctrl->clk)) { | |
| dev_err(&pdev->dev, "no clock defined\n"); | |
| return PTR_ERR(pctrl->clk); | |
| } | |
| ret = clk_prepare_enable(pctrl->clk); | |
| if (ret) { | |
| dev_err(&pdev->dev, "clk enable failed\n"); | |
| return ret; | |
| } | |
| raw_spin_lock_init(&pctrl->lock); | |
| owl_pinctrl_desc.name = dev_name(&pdev->dev); | |
| owl_pinctrl_desc.pins = soc_data->pins; | |
| owl_pinctrl_desc.npins = soc_data->npins; | |
| pctrl->chip.direction_input = owl_gpio_direction_input; | |
| pctrl->chip.direction_output = owl_gpio_direction_output; | |
| pctrl->chip.get = owl_gpio_get; | |
| pctrl->chip.set = owl_gpio_set; | |
| pctrl->chip.request = owl_gpio_request; | |
| pctrl->chip.free = owl_gpio_free; | |
| pctrl->soc = soc_data; | |
| pctrl->dev = &pdev->dev; | |
| pctrl->pctrldev = devm_pinctrl_register(&pdev->dev, | |
| &owl_pinctrl_desc, pctrl); | |
| if (IS_ERR(pctrl->pctrldev)) { | |
| dev_err(&pdev->dev, "could not register Actions OWL pinmux driver\n"); | |
| ret = PTR_ERR(pctrl->pctrldev); | |
| goto err_exit; | |
| } | |
| ret = platform_irq_count(pdev); | |
| if (ret < 0) | |
| goto err_exit; | |
| pctrl->num_irq = ret; | |
| pctrl->irq = devm_kcalloc(&pdev->dev, pctrl->num_irq, | |
| sizeof(*pctrl->irq), GFP_KERNEL); | |
| if (!pctrl->irq) { | |
| ret = -ENOMEM; | |
| goto err_exit; | |
| } | |
| for (i = 0; i < pctrl->num_irq ; i++) { | |
| ret = platform_get_irq(pdev, i); | |
| if (ret < 0) | |
| goto err_exit; | |
| pctrl->irq[i] = ret; | |
| } | |
| ret = owl_gpio_init(pctrl); | |
| if (ret) | |
| goto err_exit; | |
| platform_set_drvdata(pdev, pctrl); | |
| return 0; | |
| err_exit: | |
| clk_disable_unprepare(pctrl->clk); | |
| return ret; | |
| } |