/* * Marvell 37xx SoC pinctrl driver * * Copyright (C) 2017 Marvell * * Gregory CLEMENT <gregory.clement@free-electrons.com> * * This file is licensed under the terms of the GNU General Public * License version 2 or later. This program is licensed "as is" * without any warranty of any kind, whether express or implied.
*/
/** * struct armada_37xx_pin_group: represents group of pins of a pinmux function. * The pins of a pinmux groups are composed of one or two groups of contiguous * pins. * @name: Name of the pin group, used to lookup the group. * @start_pin: Index of the first pin of the main range of pins belonging to * the group * @npins: Number of pins included in the first range * @reg_mask: Bit mask matching the group in the selection register * @val: Value to write to the registers for a given function * @extra_pin: Index of the first pin of the optional second range of pins * belonging to the group * @extra_npins:Number of pins included in the second optional range * @funcs: A list of pinmux functions that can be selected for this group. * @pins: List of the pins included in the group
*/ struct armada_37xx_pin_group { constchar *name; unsignedint start_pin; unsignedint npins;
u32 reg_mask;
u32 val[NB_FUNCS]; unsignedint extra_pin; unsignedint extra_npins; constchar *funcs[NB_FUNCS]; unsignedint *pins;
};
staticinlinevoid armada_37xx_update_reg(unsignedint *reg, unsignedint *offset)
{ /* We never have more than 2 registers */ if (*offset >= GPIO_PER_REG) {
*offset -= GPIO_PER_REG;
*reg += sizeof(u32);
}
}
staticstruct armada_37xx_pin_group *armada_37xx_find_next_grp_by_pin( struct armada_37xx_pinctrl *info, int pin, int *grp)
{ while (*grp < info->ngroups) { struct armada_37xx_pin_group *group = &info->groups[*grp]; int j;
raw_spin_lock_irqsave(&info->irq_lock, flags);
p = readl(info->base + IRQ_POL + 4 * reg_idx); if ((p ^ l) & (1 << bit_num)) { /* * For the gpios which are used for both-edge irqs, when their * interrupts happen, their input levels are changed, * yet their interrupt polarities are kept in old values, we * should synchronize their interrupt polarities; for example, * at first a gpio's input level is low and its interrupt * polarity control is "Detect rising edge", then the gpio has * a interrupt , its level turns to high, we should change its * polarity control to "Detect falling edge" correspondingly.
*/
p ^= 1 << bit_num;
writel(p, info->base + IRQ_POL + 4 * reg_idx);
ret = 0;
} else { /* Spurious irq */
ret = -1;
}
chained_irq_enter(chip, desc); for (i = 0; i <= d->revmap_size / GPIO_PER_REG; i++) {
u32 status; unsignedlong flags;
raw_spin_lock_irqsave(&info->irq_lock, flags);
status = readl_relaxed(info->base + IRQ_STATUS + 4 * i); /* Manage only the interrupt that was enabled */
status &= readl_relaxed(info->base + IRQ_EN + 4 * i);
raw_spin_unlock_irqrestore(&info->irq_lock, flags); while (status) {
u32 hwirq = ffs(status) - 1;
u32 virq = irq_find_mapping(d, hwirq +
i * GPIO_PER_REG);
u32 t = irq_get_trigger_type(virq);
if ((t & IRQ_TYPE_SENSE_MASK) == IRQ_TYPE_EDGE_BOTH) { /* Swap polarity (race with GPIO line) */ if (armada_37xx_edge_both_irq_swap_pol(info,
hwirq + i * GPIO_PER_REG)) { /* * For spurious irq, which gpio level * is not as expected after incoming * edge, just ack the gpio irq.
*/
writel(1 << hwirq,
info->base +
IRQ_STATUS + 4 * i); goto update_status;
}
}
generic_handle_irq(virq);
update_status: /* Update status in case a new IRQ appears */
raw_spin_lock_irqsave(&info->irq_lock, flags);
status = readl_relaxed(info->base +
IRQ_STATUS + 4 * i); /* Manage only the interrupt that was enabled */
status &= readl_relaxed(info->base + IRQ_EN + 4 * i);
raw_spin_unlock_irqrestore(&info->irq_lock, flags);
}
}
chained_irq_exit(chip, desc);
}
staticunsignedint armada_37xx_irq_startup(struct irq_data *d)
{ /* * The mask field is a "precomputed bitmask for accessing the * chip registers" which was introduced for the generic * irqchip framework. As we don't use this framework, we can * reuse this field for our own usage.
*/
d->mask = BIT(d->hwirq % GPIO_PER_REG);
nr_irq_parent = of_irq_count(np); if (!nr_irq_parent) {
dev_err(dev, "invalid or no IRQ\n"); return 0;
}
info->base = devm_platform_ioremap_resource(pdev, 1); if (IS_ERR(info->base)) return PTR_ERR(info->base);
gpio_irq_chip_set_chip(girq, &armada_37xx_irqchip);
girq->parent_handler = armada_37xx_irq_handler; /* * Many interrupts are connected to the parent interrupt * controller. But we do not take advantage of this and use * the chained irq with all of them.
*/
girq->num_parents = nr_irq_parent;
girq->parents = devm_kcalloc(dev, nr_irq_parent, sizeof(*girq->parents), GFP_KERNEL); if (!girq->parents) return -ENOMEM; for (i = 0; i < nr_irq_parent; i++) { int irq = irq_of_parse_and_map(np, i);
ret = armada_37xx_irqchip_register(pdev, info); if (ret) return ret;
return devm_gpiochip_add_data(dev, gc, info);
}
/** * armada_37xx_add_function() - Add a new function to the list * @funcs: array of function to add the new one * @funcsize: size of the remaining space for the function * @name: name of the function to add * * If it is a new function then create it by adding its name else * increment the number of group associated to this function.
*/ staticint armada_37xx_add_function(struct armada_37xx_pmx_func *funcs, int *funcsize, constchar *name)
{ if (*funcsize <= 0) return -EOVERFLOW;
while (funcs->ngroups) { /* function already there */ if (strcmp(funcs->name, name) == 0) {
funcs->ngroups++;
return -EEXIST;
}
funcs++;
}
/* append new unique function */
funcs->name = name;
funcs->ngroups = 1;
(*funcsize)--;
return 0;
}
/** * armada_37xx_fill_group() - complete the group array * @info: info driver instance * * Based on the data available from the armada_37xx_pin_group array * completes the last member of the struct for each function: the list * of the groups associated to this function. *
*/ staticint armada_37xx_fill_group(struct armada_37xx_pinctrl *info)
{ int n, num = 0, funcsize = info->data->nr_pins; struct device *dev = info->dev;
for (n = 0; n < info->ngroups; n++) { struct armada_37xx_pin_group *grp = &info->groups[n]; int i, j, f;
for (f = 0; (f < NB_FUNCS) && grp->funcs[f]; f++) { int ret; /* check for unique functions and count groups */
ret = armada_37xx_add_function(info->funcs, &funcsize,
grp->funcs[f]); if (ret == -EOVERFLOW)
dev_err(dev, "More functions than pins(%d)\n",
info->data->nr_pins); if (ret < 0) continue;
num++;
}
}
info->nfuncs = num;
return 0;
}
/** * armada_37xx_fill_func() - complete the funcs array * @info: info driver instance * * Based on the data available from the armada_37xx_pin_group array * completes the last two member of the struct for each group: * - the list of the pins included in the group * - the list of pinmux functions that can be selected for this group *
*/ staticint armada_37xx_fill_func(struct armada_37xx_pinctrl *info)
{ struct armada_37xx_pmx_func *funcs = info->funcs; struct device *dev = info->dev; int n;
for (n = 0; n < info->nfuncs; n++) { constchar *name = funcs[n].name; constchar **groups; int g;
funcs[n].groups = devm_kcalloc(dev, funcs[n].ngroups, sizeof(*(funcs[n].groups)),
GFP_KERNEL); if (!funcs[n].groups) return -ENOMEM;
groups = funcs[n].groups;
for (g = 0; g < info->ngroups; g++) { struct armada_37xx_pin_group *gp = &info->groups[g]; int f;
f = match_string(gp->funcs, NB_FUNCS, name); if (f < 0) continue;
/* * we allocate functions for number of pins and hope there are * fewer unique functions than pins available
*/
info->funcs = devm_kcalloc(dev, pin_data->nr_pins, sizeof(*info->funcs), GFP_KERNEL); if (!info->funcs) return -ENOMEM;
ret = armada_37xx_fill_group(info); if (ret) return ret;
ret = armada_37xx_fill_func(info); if (ret) return ret;
info->pctl_dev = devm_pinctrl_register(dev, ctrldesc, info); if (IS_ERR(info->pctl_dev)) return dev_err_probe(dev, PTR_ERR(info->pctl_dev), "could not register pinctrl driver\n");
/* * Input levels may change during suspend, which is not monitored at * that time. GPIOs used for both-edge IRQs may not be synchronized * anymore with their polarities (rising/falling edge) and must be * re-configured manually.
*/
gc = &info->gpio_chip;
d = gc->irq.domain; for (i = 0; i < gc->ngpio; i++) {
u32 irq_bit = BIT(i % GPIO_PER_REG);
u32 mask, *irq_pol, input_reg, virq, type, level;
/* Restore pinctrl state */
regmap_write(info->regmap, SELECTION, info->pm.selection);
return 0;
}
/* * Since pinctrl is an infrastructure module, its resume should be issued prior * to other IO drivers.
*/ static DEFINE_NOIRQ_DEV_PM_OPS(armada_3700_pinctrl_pm_ops,
armada_3700_pinctrl_suspend, armada_3700_pinctrl_resume);
base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL); if (IS_ERR(base)) {
dev_err(dev, "failed to ioremap base address: %pe\n", base); return PTR_ERR(base);
}
regmap = devm_regmap_init_mmio(dev, base,
&armada_37xx_pinctrl_regmap_config); if (IS_ERR(regmap)) {
dev_err(dev, "failed to create regmap: %pe\n", regmap); return PTR_ERR(regmap);
}
info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); if (!info) return -ENOMEM;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.