diff options
Diffstat (limited to 'drivers/pinctrl/sunxi/pinctrl-sunxi.c')
| -rw-r--r-- | drivers/pinctrl/sunxi/pinctrl-sunxi.c | 573 |
1 files changed, 425 insertions, 148 deletions
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c index 0dfd7fa66c48..0fb057a07dcc 100644 --- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c @@ -10,32 +10,115 @@ * warranty of any kind, whether express or implied. */ -#include <linux/io.h> #include <linux/clk.h> +#include <linux/export.h> #include <linux/gpio/driver.h> -#include <linux/irqdomain.h> +#include <linux/interrupt.h> +#include <linux/io.h> #include <linux/irqchip/chained_irq.h> -#include <linux/export.h> +#include <linux/irqdomain.h> #include <linux/of.h> -#include <linux/of_address.h> -#include <linux/of_device.h> -#include <linux/of_irq.h> +#include <linux/of_clk.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + #include <linux/pinctrl/consumer.h> #include <linux/pinctrl/machine.h> -#include <linux/pinctrl/pinctrl.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/slab.h> #include <dt-bindings/pinctrl/sun4i-a10.h> #include "../core.h" #include "pinctrl-sunxi.h" +/* + * These lock classes tell lockdep that GPIO IRQs are in a different + * category than their parents, so it won't report false recursion. + */ +static struct lock_class_key sunxi_pinctrl_irq_lock_class; +static struct lock_class_key sunxi_pinctrl_irq_request_class; + static struct irq_chip sunxi_pinctrl_edge_irq_chip; static struct irq_chip sunxi_pinctrl_level_irq_chip; +/* + * The sunXi PIO registers are organized as a series of banks, with registers + * for each bank in the following order: + * - Mux config + * - Data value + * - Drive level + * - Pull direction + * + * Multiple consecutive registers are used for fields wider than one bit. + * + * The following functions calculate the register and the bit offset to access. + * They take a pin number which is relative to the start of the current device. + */ + +/* + * When using the extended register layout, Bank K does not fit into the + * space used for the other banks. Instead it lives at offset 0x500. + */ +static u32 sunxi_bank_offset(const struct sunxi_pinctrl *pctl, u32 pin) +{ + u32 offset = 0; + + if (pin >= PK_BASE) { + pin -= PK_BASE; + offset = PIO_BANK_K_OFFSET; + } + + return offset + (pin / PINS_PER_BANK) * pctl->bank_mem_size; +} + +static void sunxi_mux_reg(const struct sunxi_pinctrl *pctl, + u32 pin, u32 *reg, u32 *shift, u32 *mask) +{ + u32 offset = pin % PINS_PER_BANK * MUX_FIELD_WIDTH; + + *reg = sunxi_bank_offset(pctl, pin) + MUX_REGS_OFFSET + + offset / BITS_PER_TYPE(u32) * sizeof(u32); + *shift = offset % BITS_PER_TYPE(u32); + *mask = (BIT(MUX_FIELD_WIDTH) - 1) << *shift; +} + +static void sunxi_data_reg(const struct sunxi_pinctrl *pctl, + u32 pin, u32 *reg, u32 *shift, u32 *mask) +{ + u32 offset = pin % PINS_PER_BANK * DATA_FIELD_WIDTH; + + *reg = sunxi_bank_offset(pctl, pin) + DATA_REGS_OFFSET + + offset / BITS_PER_TYPE(u32) * sizeof(u32); + *shift = offset % BITS_PER_TYPE(u32); + *mask = (BIT(DATA_FIELD_WIDTH) - 1) << *shift; +} + +static void sunxi_dlevel_reg(const struct sunxi_pinctrl *pctl, + u32 pin, u32 *reg, u32 *shift, u32 *mask) +{ + u32 offset = pin % PINS_PER_BANK * pctl->dlevel_field_width; + + *reg = sunxi_bank_offset(pctl, pin) + DLEVEL_REGS_OFFSET + + offset / BITS_PER_TYPE(u32) * sizeof(u32); + *shift = offset % BITS_PER_TYPE(u32); + *mask = (BIT(pctl->dlevel_field_width) - 1) << *shift; +} + +static void sunxi_pull_reg(const struct sunxi_pinctrl *pctl, + u32 pin, u32 *reg, u32 *shift, u32 *mask) +{ + u32 offset = pin % PINS_PER_BANK * PULL_FIELD_WIDTH; + + *reg = sunxi_bank_offset(pctl, pin) + pctl->pull_regs_offset + + offset / BITS_PER_TYPE(u32) * sizeof(u32); + *shift = offset % BITS_PER_TYPE(u32); + *mask = (BIT(PULL_FIELD_WIDTH) - 1) << *shift; +} + static struct sunxi_pinctrl_group * sunxi_pinctrl_find_group_by_name(struct sunxi_pinctrl *pctl, const char *group) { @@ -83,7 +166,9 @@ sunxi_pinctrl_desc_find_function_by_name(struct sunxi_pinctrl *pctl, struct sunxi_desc_function *func = pin->functions; while (func->name) { - if (!strcmp(func->name, func_name)) + if (!strcmp(func->name, func_name) && + (!func->variant || + func->variant & pctl->variant)) return func; func++; @@ -149,16 +234,16 @@ static int sunxi_pctrl_get_group_pins(struct pinctrl_dev *pctldev, static bool sunxi_pctrl_has_bias_prop(struct device_node *node) { - return of_find_property(node, "bias-pull-up", NULL) || - of_find_property(node, "bias-pull-down", NULL) || - of_find_property(node, "bias-disable", NULL) || - of_find_property(node, "allwinner,pull", NULL); + return of_property_present(node, "bias-pull-up") || + of_property_present(node, "bias-pull-down") || + of_property_present(node, "bias-disable") || + of_property_present(node, "allwinner,pull"); } static bool sunxi_pctrl_has_drive_prop(struct device_node *node) { - return of_find_property(node, "drive-strength", NULL) || - of_find_property(node, "allwinner,drive", NULL); + return of_property_present(node, "drive-strength") || + of_property_present(node, "allwinner,drive"); } static int sunxi_pctrl_parse_bias_prop(struct device_node *node) @@ -166,13 +251,13 @@ static int sunxi_pctrl_parse_bias_prop(struct device_node *node) u32 val; /* Try the new style binding */ - if (of_find_property(node, "bias-pull-up", NULL)) + if (of_property_present(node, "bias-pull-up")) return PIN_CONFIG_BIAS_PULL_UP; - if (of_find_property(node, "bias-pull-down", NULL)) + if (of_property_present(node, "bias-pull-down")) return PIN_CONFIG_BIAS_PULL_DOWN; - if (of_find_property(node, "bias-disable", NULL)) + if (of_property_present(node, "bias-disable")) return PIN_CONFIG_BIAS_DISABLE; /* And fall back to the old binding */ @@ -274,7 +359,7 @@ static unsigned long *sunxi_pctrl_build_pin_config(struct device_node *node, if (!configlen) return NULL; - pinconfig = kzalloc(configlen * sizeof(*pinconfig), GFP_KERNEL); + pinconfig = kcalloc(configlen, sizeof(*pinconfig), GFP_KERNEL); if (!pinconfig) return ERR_PTR(-ENOMEM); @@ -323,21 +408,22 @@ static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, const char *function, *pin_prop; const char *group; int ret, npins, nmaps, configlen = 0, i = 0; + struct pinctrl_map *new_map; *map = NULL; *num_maps = 0; function = sunxi_pctrl_parse_function_prop(node); if (!function) { - dev_err(pctl->dev, "missing function property in node %s\n", - node->name); + dev_err(pctl->dev, "missing function property in node %pOFn\n", + node); return -EINVAL; } pin_prop = sunxi_pctrl_find_pins_prop(node, &npins); if (!pin_prop) { - dev_err(pctl->dev, "missing pins property in node %s\n", - node->name); + dev_err(pctl->dev, "missing pins property in node %pOFn\n", + node); return -EINVAL; } @@ -349,7 +435,7 @@ static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, * any configuration. */ nmaps = npins * 2; - *map = kmalloc(nmaps * sizeof(struct pinctrl_map), GFP_KERNEL); + *map = kmalloc_array(nmaps, sizeof(struct pinctrl_map), GFP_KERNEL); if (!*map) return -ENOMEM; @@ -397,9 +483,13 @@ static int sunxi_pctrl_dt_node_to_map(struct pinctrl_dev *pctldev, * We know have the number of maps we need, we can resize our * map array */ - *map = krealloc(*map, i * sizeof(struct pinctrl_map), GFP_KERNEL); - if (!*map) - return -ENOMEM; + new_map = krealloc(*map, i * sizeof(struct pinctrl_map), GFP_KERNEL); + if (!new_map) { + ret = -ENOMEM; + goto err_free_map; + } + + *map = new_map; return 0; @@ -439,22 +529,19 @@ static const struct pinctrl_ops sunxi_pctrl_ops = { .get_group_pins = sunxi_pctrl_get_group_pins, }; -static int sunxi_pconf_reg(unsigned pin, enum pin_config_param param, - u32 *offset, u32 *shift, u32 *mask) +static int sunxi_pconf_reg(const struct sunxi_pinctrl *pctl, + u32 pin, enum pin_config_param param, + u32 *reg, u32 *shift, u32 *mask) { switch (param) { case PIN_CONFIG_DRIVE_STRENGTH: - *offset = sunxi_dlevel_reg(pin); - *shift = sunxi_dlevel_offset(pin); - *mask = DLEVEL_PINS_MASK; + sunxi_dlevel_reg(pctl, pin, reg, shift, mask); break; case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN: case PIN_CONFIG_BIAS_DISABLE: - *offset = sunxi_pull_reg(pin); - *shift = sunxi_pull_offset(pin); - *mask = PULL_PINS_MASK; + sunxi_pull_reg(pctl, pin, reg, shift, mask); break; default: @@ -469,17 +556,17 @@ static int sunxi_pconf_get(struct pinctrl_dev *pctldev, unsigned pin, { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); enum pin_config_param param = pinconf_to_config_param(*config); - u32 offset, shift, mask, val; + u32 reg, shift, mask, val; u16 arg; int ret; pin -= pctl->desc->pin_base; - ret = sunxi_pconf_reg(pin, param, &offset, &shift, &mask); + ret = sunxi_pconf_reg(pctl, pin, param, ®, &shift, &mask); if (ret < 0) return ret; - val = (readl(pctl->membase + offset) >> shift) & mask; + val = (readl(pctl->membase + reg) & mask) >> shift; switch (pinconf_to_config_param(*config)) { case PIN_CONFIG_DRIVE_STRENGTH: @@ -526,27 +613,24 @@ static int sunxi_pconf_group_get(struct pinctrl_dev *pctldev, return sunxi_pconf_get(pctldev, g->pin, config); } -static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, - unsigned group, - unsigned long *configs, - unsigned num_configs) +static int sunxi_pconf_set(struct pinctrl_dev *pctldev, unsigned pin, + unsigned long *configs, unsigned num_configs) { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); - struct sunxi_pinctrl_group *g = &pctl->groups[group]; - unsigned pin = g->pin - pctl->desc->pin_base; int i; + pin -= pctl->desc->pin_base; + for (i = 0; i < num_configs; i++) { + u32 arg, reg, shift, mask, val; enum pin_config_param param; unsigned long flags; - u32 offset, shift, mask, reg; - u32 arg, val; int ret; param = pinconf_to_config_param(configs[i]); arg = pinconf_to_config_argument(configs[i]); - ret = sunxi_pconf_reg(pin, param, &offset, &shift, &mask); + ret = sunxi_pconf_reg(pctl, pin, param, ®, &shift, &mask); if (ret < 0) return ret; @@ -564,7 +648,8 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, val = arg / 10 - 1; break; case PIN_CONFIG_BIAS_DISABLE: - continue; + val = 0; + break; case PIN_CONFIG_BIAS_PULL_UP: if (arg == 0) return -EINVAL; @@ -582,22 +667,103 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, } raw_spin_lock_irqsave(&pctl->lock, flags); - reg = readl(pctl->membase + offset); - reg &= ~(mask << shift); - writel(reg | val << shift, pctl->membase + offset); + writel((readl(pctl->membase + reg) & ~mask) | val << shift, + pctl->membase + reg); raw_spin_unlock_irqrestore(&pctl->lock, flags); } /* for each config */ return 0; } +static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev, unsigned group, + unsigned long *configs, unsigned num_configs) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + struct sunxi_pinctrl_group *g = &pctl->groups[group]; + + /* We only support 1 pin per group. Chain it to the pin callback */ + return sunxi_pconf_set(pctldev, g->pin, configs, num_configs); +} + static const struct pinconf_ops sunxi_pconf_ops = { .is_generic = true, .pin_config_get = sunxi_pconf_get, + .pin_config_set = sunxi_pconf_set, .pin_config_group_get = sunxi_pconf_group_get, .pin_config_group_set = sunxi_pconf_group_set, }; +static int sunxi_pinctrl_set_io_bias_cfg(struct sunxi_pinctrl *pctl, + unsigned pin, + struct regulator *supply) +{ + unsigned short bank; + unsigned long flags; + u32 val, reg; + int uV; + + if (!pctl->desc->io_bias_cfg_variant) + return 0; + + uV = regulator_get_voltage(supply); + if (uV < 0) + return uV; + + /* Might be dummy regulator with no voltage set */ + if (uV == 0) + return 0; + + pin -= pctl->desc->pin_base; + bank = pin / PINS_PER_BANK; + + switch (pctl->desc->io_bias_cfg_variant) { + case BIAS_VOLTAGE_GRP_CONFIG: + /* + * Configured value must be equal or greater to actual + * voltage. + */ + if (uV <= 1800000) + val = 0x0; /* 1.8V */ + else if (uV <= 2500000) + val = 0x6; /* 2.5V */ + else if (uV <= 2800000) + val = 0x9; /* 2.8V */ + else if (uV <= 3000000) + val = 0xA; /* 3.0V */ + else + val = 0xD; /* 3.3V */ + + reg = readl(pctl->membase + sunxi_grp_config_reg(pin)); + reg &= ~IO_BIAS_MASK; + writel(reg | val, pctl->membase + sunxi_grp_config_reg(pin)); + return 0; + case BIAS_VOLTAGE_PIO_POW_MODE_CTL: + val = uV > 1800000 && uV <= 2500000 ? BIT(bank) : 0; + + raw_spin_lock_irqsave(&pctl->lock, flags); + reg = readl(pctl->membase + pctl->pow_mod_sel_offset + + PIO_POW_MOD_CTL_OFS); + reg &= ~BIT(bank); + writel(reg | val, pctl->membase + pctl->pow_mod_sel_offset + + PIO_POW_MOD_CTL_OFS); + raw_spin_unlock_irqrestore(&pctl->lock, flags); + + fallthrough; + case BIAS_VOLTAGE_PIO_POW_MODE_SEL: + val = uV <= 1800000 ? 1 : 0; + + raw_spin_lock_irqsave(&pctl->lock, flags); + reg = readl(pctl->membase + pctl->pow_mod_sel_offset); + reg &= ~(1 << bank); + writel(reg | val << bank, + pctl->membase + pctl->pow_mod_sel_offset); + raw_spin_unlock_irqrestore(&pctl->lock, flags); + return 0; + default: + return -EINVAL; + } +} + static int sunxi_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev) { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); @@ -631,16 +797,16 @@ static void sunxi_pmx_set(struct pinctrl_dev *pctldev, u8 config) { struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + u32 reg, shift, mask; unsigned long flags; - u32 val, mask; + + pin -= pctl->desc->pin_base; + sunxi_mux_reg(pctl, pin, ®, &shift, &mask); raw_spin_lock_irqsave(&pctl->lock, flags); - pin -= pctl->desc->pin_base; - val = readl(pctl->membase + sunxi_mux_reg(pin)); - mask = MUX_PINS_MASK << sunxi_mux_offset(pin); - writel((val & ~mask) | config << sunxi_mux_offset(pin), - pctl->membase + sunxi_mux_reg(pin)); + writel((readl(pctl->membase + reg) & ~mask) | config << shift, + pctl->membase + reg); raw_spin_unlock_irqrestore(&pctl->lock, flags); } @@ -689,69 +855,144 @@ sunxi_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, return 0; } +static int sunxi_pmx_request(struct pinctrl_dev *pctldev, unsigned offset) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + unsigned short bank = offset / PINS_PER_BANK; + unsigned short bank_offset = bank - pctl->desc->pin_base / + PINS_PER_BANK; + struct sunxi_pinctrl_regulator *s_reg = &pctl->regulators[bank_offset]; + struct regulator *reg = s_reg->regulator; + char supply[16]; + int ret; + + if (WARN_ON_ONCE(bank_offset >= ARRAY_SIZE(pctl->regulators))) + return -EINVAL; + + if (reg) { + refcount_inc(&s_reg->refcount); + return 0; + } + + snprintf(supply, sizeof(supply), "vcc-p%c", 'a' + bank); + reg = regulator_get(pctl->dev, supply); + if (IS_ERR(reg)) + return dev_err_probe(pctl->dev, PTR_ERR(reg), + "Couldn't get bank P%c regulator\n", + 'A' + bank); + + ret = regulator_enable(reg); + if (ret) { + dev_err(pctl->dev, + "Couldn't enable bank P%c regulator\n", 'A' + bank); + goto out; + } + + sunxi_pinctrl_set_io_bias_cfg(pctl, offset, reg); + + s_reg->regulator = reg; + refcount_set(&s_reg->refcount, 1); + + return 0; + +out: + regulator_put(s_reg->regulator); + + return ret; +} + +static int sunxi_pmx_free(struct pinctrl_dev *pctldev, unsigned offset) +{ + struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev); + unsigned short bank = offset / PINS_PER_BANK; + unsigned short bank_offset = bank - pctl->desc->pin_base / + PINS_PER_BANK; + struct sunxi_pinctrl_regulator *s_reg = &pctl->regulators[bank_offset]; + + if (!refcount_dec_and_test(&s_reg->refcount)) + return 0; + + regulator_disable(s_reg->regulator); + regulator_put(s_reg->regulator); + s_reg->regulator = NULL; + + return 0; +} + static const struct pinmux_ops sunxi_pmx_ops = { .get_functions_count = sunxi_pmx_get_funcs_cnt, .get_function_name = sunxi_pmx_get_func_name, .get_function_groups = sunxi_pmx_get_func_groups, .set_mux = sunxi_pmx_set_mux, .gpio_set_direction = sunxi_pmx_gpio_set_direction, + .request = sunxi_pmx_request, + .free = sunxi_pmx_free, + .strict = true, }; static int sunxi_pinctrl_gpio_direction_input(struct gpio_chip *chip, unsigned offset) { - return pinctrl_gpio_direction_input(chip->base + offset); + struct sunxi_pinctrl *pctl = gpiochip_get_data(chip); + + return sunxi_pmx_gpio_set_direction(pctl->pctl_dev, NULL, + chip->base + offset, true); } static int sunxi_pinctrl_gpio_get(struct gpio_chip *chip, unsigned offset) { struct sunxi_pinctrl *pctl = gpiochip_get_data(chip); - u32 reg = sunxi_data_reg(offset); - u8 index = sunxi_data_offset(offset); bool set_mux = pctl->desc->irq_read_needs_mux && gpiochip_line_is_irq(chip, offset); u32 pin = offset + chip->base; - u32 val; + u32 reg, shift, mask, val; + + sunxi_data_reg(pctl, offset, ®, &shift, &mask); if (set_mux) sunxi_pmx_set(pctl->pctl_dev, pin, SUN4I_FUNC_INPUT); - val = (readl(pctl->membase + reg) >> index) & DATA_PINS_MASK; + val = (readl(pctl->membase + reg) & mask) >> shift; if (set_mux) sunxi_pmx_set(pctl->pctl_dev, pin, SUN4I_FUNC_IRQ); - return !!val; + return val; } -static void sunxi_pinctrl_gpio_set(struct gpio_chip *chip, - unsigned offset, int value) +static int sunxi_pinctrl_gpio_set(struct gpio_chip *chip, unsigned int offset, + int value) { struct sunxi_pinctrl *pctl = gpiochip_get_data(chip); - u32 reg = sunxi_data_reg(offset); - u8 index = sunxi_data_offset(offset); + u32 reg, shift, mask, val; unsigned long flags; - u32 regval; + + sunxi_data_reg(pctl, offset, ®, &shift, &mask); raw_spin_lock_irqsave(&pctl->lock, flags); - regval = readl(pctl->membase + reg); + val = readl(pctl->membase + reg); if (value) - regval |= BIT(index); + val |= mask; else - regval &= ~(BIT(index)); + val &= ~mask; - writel(regval, pctl->membase + reg); + writel(val, pctl->membase + reg); raw_spin_unlock_irqrestore(&pctl->lock, flags); + + return 0; } static int sunxi_pinctrl_gpio_direction_output(struct gpio_chip *chip, unsigned offset, int value) { + struct sunxi_pinctrl *pctl = gpiochip_get_data(chip); + sunxi_pinctrl_gpio_set(chip, offset, value); - return pinctrl_gpio_direction_output(chip->base + offset); + return sunxi_pmx_gpio_set_direction(pctl->pctl_dev, NULL, + chip->base + offset, false); } static int sunxi_pinctrl_gpio_of_xlate(struct gpio_chip *gc, @@ -830,7 +1071,7 @@ static void sunxi_pinctrl_irq_release_resources(struct irq_data *d) static int sunxi_pinctrl_irq_set_type(struct irq_data *d, unsigned int type) { struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); - u32 reg = sunxi_irq_cfg_reg(d->hwirq, pctl->desc->irq_bank_base); + u32 reg = sunxi_irq_cfg_reg(pctl->desc, d->hwirq); u8 index = sunxi_irq_cfg_offset(d->hwirq); unsigned long flags; u32 regval; @@ -877,8 +1118,7 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d, unsigned int type) static void sunxi_pinctrl_irq_ack(struct irq_data *d) { struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); - u32 status_reg = sunxi_irq_status_reg(d->hwirq, - pctl->desc->irq_bank_base); + u32 status_reg = sunxi_irq_status_reg(pctl->desc, d->hwirq); u8 status_idx = sunxi_irq_status_offset(d->hwirq); /* Clear the IRQ */ @@ -888,7 +1128,7 @@ static void sunxi_pinctrl_irq_ack(struct irq_data *d) static void sunxi_pinctrl_irq_mask(struct irq_data *d) { struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); - u32 reg = sunxi_irq_ctrl_reg(d->hwirq, pctl->desc->irq_bank_base); + u32 reg = sunxi_irq_ctrl_reg(pctl->desc, d->hwirq); u8 idx = sunxi_irq_ctrl_offset(d->hwirq); unsigned long flags; u32 val; @@ -905,7 +1145,7 @@ static void sunxi_pinctrl_irq_mask(struct irq_data *d) static void sunxi_pinctrl_irq_unmask(struct irq_data *d) { struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); - u32 reg = sunxi_irq_ctrl_reg(d->hwirq, pctl->desc->irq_bank_base); + u32 reg = sunxi_irq_ctrl_reg(pctl->desc, d->hwirq); u8 idx = sunxi_irq_ctrl_offset(d->hwirq); unsigned long flags; u32 val; @@ -925,6 +1165,14 @@ static void sunxi_pinctrl_irq_ack_unmask(struct irq_data *d) sunxi_pinctrl_irq_unmask(d); } +static int sunxi_pinctrl_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d); + u8 bank = d->hwirq / IRQ_PER_BANK; + + return irq_set_irq_wake(pctl->irq[bank], on); +} + static struct irq_chip sunxi_pinctrl_edge_irq_chip = { .name = "sunxi_pio_edge", .irq_ack = sunxi_pinctrl_irq_ack, @@ -933,7 +1181,8 @@ static struct irq_chip sunxi_pinctrl_edge_irq_chip = { .irq_request_resources = sunxi_pinctrl_irq_request_resources, .irq_release_resources = sunxi_pinctrl_irq_release_resources, .irq_set_type = sunxi_pinctrl_irq_set_type, - .flags = IRQCHIP_SKIP_SET_WAKE, + .irq_set_wake = sunxi_pinctrl_irq_set_wake, + .flags = IRQCHIP_MASK_ON_SUSPEND, }; static struct irq_chip sunxi_pinctrl_level_irq_chip = { @@ -948,7 +1197,9 @@ static struct irq_chip sunxi_pinctrl_level_irq_chip = { .irq_request_resources = sunxi_pinctrl_irq_request_resources, .irq_release_resources = sunxi_pinctrl_irq_release_resources, .irq_set_type = sunxi_pinctrl_irq_set_type, - .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_EOI_THREADED | + .irq_set_wake = sunxi_pinctrl_irq_set_wake, + .flags = IRQCHIP_EOI_THREADED | + IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_EOI_IF_HANDLED, }; @@ -994,23 +1245,22 @@ static void sunxi_pinctrl_irq_handler(struct irq_desc *desc) if (irq == pctl->irq[bank]) break; - if (bank == pctl->desc->irq_banks) - return; + WARN_ON(bank == pctl->desc->irq_banks); + + chained_irq_enter(chip, desc); - reg = sunxi_irq_status_reg_from_bank(bank, pctl->desc->irq_bank_base); + reg = sunxi_irq_status_reg_from_bank(pctl->desc, bank); val = readl(pctl->membase + reg); if (val) { int irqoffset; - chained_irq_enter(chip, desc); - for_each_set_bit(irqoffset, &val, IRQ_PER_BANK) { - int pin_irq = irq_find_mapping(pctl->domain, - bank * IRQ_PER_BANK + irqoffset); - generic_handle_irq(pin_irq); - } - chained_irq_exit(chip, desc); + for_each_set_bit(irqoffset, &val, IRQ_PER_BANK) + generic_handle_domain_irq(pctl->domain, + bank * IRQ_PER_BANK + irqoffset); } + + chained_irq_exit(chip, desc); } static int sunxi_pinctrl_add_function(struct sunxi_pinctrl *pctl, @@ -1038,6 +1288,7 @@ static int sunxi_pinctrl_add_function(struct sunxi_pinctrl *pctl, static int sunxi_pinctrl_build_state(struct platform_device *pdev) { struct sunxi_pinctrl *pctl = platform_get_drvdata(pdev); + void *ptr; int i; /* @@ -1051,8 +1302,8 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev) * this means that the number of pins is the maximum group * number we will ever see. */ - pctl->groups = devm_kzalloc(&pdev->dev, - pctl->desc->npins * sizeof(*pctl->groups), + pctl->groups = devm_kcalloc(&pdev->dev, + pctl->desc->npins, sizeof(*pctl->groups), GFP_KERNEL); if (!pctl->groups) return -ENOMEM; @@ -1072,12 +1323,14 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev) } /* - * We suppose that we won't have any more functions than pins, - * we'll reallocate that later anyway + * Find an upper bound for the maximum number of functions: in + * the worst case we have gpio_in, gpio_out, irq and up to seven + * special functions per pin, plus one entry for the sentinel. + * We'll reallocate that later anyway. */ - pctl->functions = devm_kzalloc(&pdev->dev, - pctl->ngroups * sizeof(*pctl->functions), - GFP_KERNEL); + pctl->functions = kcalloc(7 * pctl->ngroups + 4, + sizeof(*pctl->functions), + GFP_KERNEL); if (!pctl->functions) return -ENOMEM; @@ -1104,13 +1357,15 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev) } /* And now allocated and fill the array for real */ - pctl->functions = krealloc(pctl->functions, - pctl->nfunctions * sizeof(*pctl->functions), - GFP_KERNEL); - if (!pctl->functions) { + ptr = krealloc(pctl->functions, + pctl->nfunctions * sizeof(*pctl->functions), + GFP_KERNEL); + if (!ptr) { kfree(pctl->functions); + pctl->functions = NULL; return -ENOMEM; } + pctl->functions = ptr; for (i = 0; i < pctl->desc->npins; i++) { const struct sunxi_desc_pin *pin = pctl->desc->pins + i; @@ -1128,16 +1383,21 @@ static int sunxi_pinctrl_build_state(struct platform_device *pdev) func_item = sunxi_pinctrl_find_function_by_name(pctl, func->name); - if (!func_item) + if (!func_item) { + kfree(pctl->functions); return -EINVAL; + } if (!func_item->groups) { func_item->groups = - devm_kzalloc(&pdev->dev, - func_item->ngroups * sizeof(*func_item->groups), + devm_kcalloc(&pdev->dev, + func_item->ngroups, + sizeof(*func_item->groups), GFP_KERNEL); - if (!func_item->groups) + if (!func_item->groups) { + kfree(pctl->functions); return -ENOMEM; + } } func_grp = func_item->groups; @@ -1183,11 +1443,11 @@ static int sunxi_pinctrl_setup_debounce(struct sunxi_pinctrl *pctl, int i, ret; /* Deal with old DTs that didn't have the oscillators */ - if (of_count_phandle_with_args(node, "clocks", "#clock-cells") != 3) + if (of_clk_get_parent_count(node) != 3) return 0; /* If we don't have any setup, bail out */ - if (!of_find_property(node, "input-debounce", NULL)) + if (!of_property_present(node, "input-debounce")) return 0; losc = devm_clk_get(pctl->dev, "losc"); @@ -1229,22 +1489,21 @@ static int sunxi_pinctrl_setup_debounce(struct sunxi_pinctrl *pctl, writel(src | div << 4, pctl->membase + - sunxi_irq_debounce_reg_from_bank(i, - pctl->desc->irq_bank_base)); + sunxi_irq_debounce_reg_from_bank(pctl->desc, i)); } return 0; } -int sunxi_pinctrl_init_with_variant(struct platform_device *pdev, - const struct sunxi_pinctrl_desc *desc, - unsigned long variant) +int sunxi_pinctrl_init_with_flags(struct platform_device *pdev, + const struct sunxi_pinctrl_desc *desc, + unsigned long flags) { struct device_node *node = pdev->dev.of_node; struct pinctrl_desc *pctrl_desc; struct pinctrl_pin_desc *pins; struct sunxi_pinctrl *pctl; - struct resource *res; + struct pinmux_ops *pmxops; int i, ret, last_pin, pin_idx; struct clk *clk; @@ -1255,14 +1514,26 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev, raw_spin_lock_init(&pctl->lock); - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - pctl->membase = devm_ioremap_resource(&pdev->dev, res); + pctl->membase = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(pctl->membase)) return PTR_ERR(pctl->membase); pctl->dev = &pdev->dev; pctl->desc = desc; - pctl->variant = variant; + pctl->variant = flags & SUNXI_PINCTRL_VARIANT_MASK; + if (flags & SUNXI_PINCTRL_NEW_REG_LAYOUT) { + pctl->bank_mem_size = D1_BANK_MEM_SIZE; + pctl->pull_regs_offset = D1_PULL_REGS_OFFSET; + pctl->dlevel_field_width = D1_DLEVEL_FIELD_WIDTH; + } else { + pctl->bank_mem_size = BANK_MEM_SIZE; + pctl->pull_regs_offset = PULL_REGS_OFFSET; + pctl->dlevel_field_width = DLEVEL_FIELD_WIDTH; + } + if (flags & SUNXI_PINCTRL_ELEVEN_BANKS) + pctl->pow_mod_sel_offset = PIO_11B_POW_MOD_SEL_REG; + else + pctl->pow_mod_sel_offset = PIO_POW_MOD_SEL_REG; pctl->irq_array = devm_kcalloc(&pdev->dev, IRQ_PER_BANK * pctl->desc->irq_banks, @@ -1277,8 +1548,8 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev, return ret; } - pins = devm_kzalloc(&pdev->dev, - pctl->desc->npins * sizeof(*pins), + pins = devm_kcalloc(&pdev->dev, + pctl->desc->npins, sizeof(*pins), GFP_KERNEL); if (!pins) return -ENOMEM; @@ -1304,7 +1575,16 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev, pctrl_desc->npins = pctl->ngroups; pctrl_desc->confops = &sunxi_pconf_ops; pctrl_desc->pctlops = &sunxi_pctrl_ops; - pctrl_desc->pmxops = &sunxi_pmx_ops; + + pmxops = devm_kmemdup(&pdev->dev, &sunxi_pmx_ops, sizeof(sunxi_pmx_ops), + GFP_KERNEL); + if (!pmxops) + return -ENOMEM; + + if (desc->disable_strict_mode) + pmxops->strict = false; + + pctrl_desc->pmxops = pmxops; pctl->pctl_dev = devm_pinctrl_register(&pdev->dev, pctrl_desc, pctl); if (IS_ERR(pctl->pctl_dev)) { @@ -1318,16 +1598,17 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev, last_pin = pctl->desc->pins[pctl->desc->npins - 1].pin.number; pctl->chip->owner = THIS_MODULE; - pctl->chip->request = gpiochip_generic_request, - pctl->chip->free = gpiochip_generic_free, - pctl->chip->direction_input = sunxi_pinctrl_gpio_direction_input, - pctl->chip->direction_output = sunxi_pinctrl_gpio_direction_output, - pctl->chip->get = sunxi_pinctrl_gpio_get, - pctl->chip->set = sunxi_pinctrl_gpio_set, - pctl->chip->of_xlate = sunxi_pinctrl_gpio_of_xlate, - pctl->chip->to_irq = sunxi_pinctrl_gpio_to_irq, - pctl->chip->of_gpio_n_cells = 3, - pctl->chip->can_sleep = false, + pctl->chip->request = gpiochip_generic_request; + pctl->chip->free = gpiochip_generic_free; + pctl->chip->set_config = gpiochip_generic_config; + pctl->chip->direction_input = sunxi_pinctrl_gpio_direction_input; + pctl->chip->direction_output = sunxi_pinctrl_gpio_direction_output; + pctl->chip->get = sunxi_pinctrl_gpio_get; + pctl->chip->set = sunxi_pinctrl_gpio_set; + pctl->chip->of_xlate = sunxi_pinctrl_gpio_of_xlate; + pctl->chip->to_irq = sunxi_pinctrl_gpio_to_irq; + pctl->chip->of_gpio_n_cells = 3; + pctl->chip->can_sleep = false; pctl->chip->ngpio = round_up(last_pin, PINS_PER_BANK) - pctl->desc->pin_base; pctl->chip->label = dev_name(&pdev->dev); @@ -1348,46 +1629,44 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev, goto gpiochip_error; } - clk = devm_clk_get(&pdev->dev, NULL); + ret = of_clk_get_parent_count(node); + clk = devm_clk_get_enabled(&pdev->dev, ret == 1 ? NULL : "apb"); if (IS_ERR(clk)) { ret = PTR_ERR(clk); goto gpiochip_error; } - ret = clk_prepare_enable(clk); - if (ret) - goto gpiochip_error; - pctl->irq = devm_kcalloc(&pdev->dev, pctl->desc->irq_banks, sizeof(*pctl->irq), GFP_KERNEL); if (!pctl->irq) { ret = -ENOMEM; - goto clk_error; + goto gpiochip_error; } for (i = 0; i < pctl->desc->irq_banks; i++) { pctl->irq[i] = platform_get_irq(pdev, i); if (pctl->irq[i] < 0) { ret = pctl->irq[i]; - goto clk_error; + goto gpiochip_error; } } - pctl->domain = irq_domain_add_linear(node, - pctl->desc->irq_banks * IRQ_PER_BANK, - &sunxi_pinctrl_irq_domain_ops, - pctl); + pctl->domain = irq_domain_create_linear(dev_fwnode(&pdev->dev), + pctl->desc->irq_banks * IRQ_PER_BANK, + &sunxi_pinctrl_irq_domain_ops, pctl); if (!pctl->domain) { dev_err(&pdev->dev, "Couldn't register IRQ domain\n"); ret = -ENOMEM; - goto clk_error; + goto gpiochip_error; } for (i = 0; i < (pctl->desc->irq_banks * IRQ_PER_BANK); i++) { int irqno = irq_create_mapping(pctl->domain, i); + irq_set_lockdep_class(irqno, &sunxi_pinctrl_irq_lock_class, + &sunxi_pinctrl_irq_request_class); irq_set_chip_and_handler(irqno, &sunxi_pinctrl_edge_irq_chip, handle_edge_irq); irq_set_chip_data(irqno, pctl); @@ -1395,11 +1674,11 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev, for (i = 0; i < pctl->desc->irq_banks; i++) { /* Mask and clear all IRQs before registering a handler */ - writel(0, pctl->membase + sunxi_irq_ctrl_reg_from_bank(i, - pctl->desc->irq_bank_base)); + writel(0, pctl->membase + + sunxi_irq_ctrl_reg_from_bank(pctl->desc, i)); writel(0xffffffff, - pctl->membase + sunxi_irq_status_reg_from_bank(i, - pctl->desc->irq_bank_base)); + pctl->membase + + sunxi_irq_status_reg_from_bank(pctl->desc, i)); irq_set_chained_handler_and_data(pctl->irq[i], sunxi_pinctrl_irq_handler, @@ -1412,8 +1691,6 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev, return 0; -clk_error: - clk_disable_unprepare(clk); gpiochip_error: gpiochip_remove(pctl->chip); return ret; |
