/* * Warning: * In order to not introduce confusion between Atmel PIO groups and pinctrl * framework groups, Atmel PIO groups will be called banks, line is kept to * designed the pin id into this bank.
*/
/** * struct atmel_pioctrl_data - Atmel PIO controller (pinmux + gpio) data struct * @nbanks: number of PIO banks * @last_bank_count: number of lines in the last bank (can be less than * the rest of the banks). * @slew_rate_support: slew rate support
*/ struct atmel_pioctrl_data { unsignedint nbanks; unsignedint last_bank_count; unsignedint slew_rate_support;
};
/** * struct atmel_pioctrl - Atmel PIO controller (pinmux + gpio) * @reg_base: base address of the controller. * @clk: clock of the controller. * @nbanks: number of PIO groups, it can vary depending on the SoC. * @pinctrl_dev: pinctrl device registered. * @groups: groups table to provide group name and pin in the group to pinctrl. * @group_names: group names table to provide all the group/pin names to * pinctrl or gpio. * @pins: pins table used for both pinctrl and gpio. pin_id, bank and line * fields are set at probe time. Other ones are set when parsing dt * pinctrl. * @npins: number of pins. * @gpio_chip: gpio chip registered. * @irq_domain: irq domain for the gpio controller. * @irqs: table containing the hw irq number of the bank. The index of the * table is the bank id. * @pm_wakeup_sources: bitmap of wakeup sources (lines) * @pm_suspend_backup: backup/restore register values on suspend/resume * @dev: device entry for the Atmel PIO controller. * @node: node of the Atmel PIO controller. * @slew_rate_support: slew rate support
*/ struct atmel_pioctrl { void __iomem *reg_base; struct clk *clk; unsignedint nbanks; struct pinctrl_dev *pinctrl_dev; struct atmel_group *groups; constchar * const *group_names; struct atmel_pin **pins; unsignedint npins; struct gpio_chip *gpio_chip; struct irq_domain *irq_domain; int *irqs; unsignedint *pm_wakeup_sources; struct {
u32 imr;
u32 odsr;
u32 cfgr[ATMEL_PIO_NPINS_PER_BANK];
} *pm_suspend_backup; struct device *dev; struct device_node *node; unsignedint slew_rate_support;
};
staticint atmel_gpio_irq_set_wake(struct irq_data *d, unsignedint on)
{ struct atmel_pioctrl *atmel_pioctrl = irq_data_get_irq_chip_data(d); int bank = ATMEL_PIO_BANK(d->hwirq); int line = ATMEL_PIO_LINE(d->hwirq);
/* The gpio controller has one interrupt line per bank. */
irq_set_irq_wake(atmel_pioctrl->irqs[bank], on);
if (on)
atmel_pioctrl->pm_wakeup_sources[bank] |= BIT(line); else
atmel_pioctrl->pm_wakeup_sources[bank] &= ~(BIT(line));
staticvoid atmel_gpio_irq_handler(struct irq_desc *desc)
{ unsignedint irq = irq_desc_get_irq(desc); struct atmel_pioctrl *atmel_pioctrl = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); unsignedlong isr; int n, bank = -1;
/* Find from which bank is the irq received. */ for (n = 0; n < atmel_pioctrl->nbanks; n++) { if (atmel_pioctrl->irqs[n] == irq) {
bank = n; break;
}
}
if (bank < 0) {
dev_err(atmel_pioctrl->dev, "no bank associated to irq %u\n", irq); return;
}
chained_irq_enter(chip, desc);
for (;;) {
isr = (unsignedlong)atmel_gpio_read(atmel_pioctrl, bank,
ATMEL_PIO_ISR);
isr &= (unsignedlong)atmel_gpio_read(atmel_pioctrl, bank,
ATMEL_PIO_IMR); if (!isr) break;
for (bank = 0; bank < atmel_pioctrl->nbanks; bank++) { unsignedint bitmask; unsignedint word = bank;
/* * On a 64-bit platform, BITS_PER_LONG is 64 so it is necessary to iterate over * two 32bit words to handle the whole bitmask
*/ #if ATMEL_PIO_NPINS_PER_BANK != BITS_PER_LONG
word = BIT_WORD(bank * ATMEL_PIO_NPINS_PER_BANK); #endif if (!mask[word]) continue;
bitmask = mask[word] & bits[word];
atmel_gpio_write(atmel_pioctrl, bank, ATMEL_PIO_SODR, bitmask);
bitmask = mask[word] & ~bits[word];
atmel_gpio_write(atmel_pioctrl, bank, ATMEL_PIO_CODR, bitmask);
writel_relaxed(BIT(line), addr + ATMEL_PIO_MSKR); /* Have to set MSKR first, to access the right pin CFGR. */
wmb();
writel_relaxed(conf, addr + ATMEL_PIO_CFGR);
}
atmel_pioctrl->pins[pin_id]->mux = func_id;
atmel_pioctrl->pins[pin_id]->ioset = ATMEL_GET_PIN_IOSET(pinfunc); /* Want the device name not the group one. */ if (np->parent == atmel_pioctrl->node)
atmel_pioctrl->pins[pin_id]->device = np->name; else
atmel_pioctrl->pins[pin_id]->device = np->parent->name;
pins = of_find_property(np, "pinmux", NULL); if (!pins) return -EINVAL;
ret = pinconf_generic_parse_dt_config(np, pctldev, &configs,
&num_configs); if (ret < 0) {
dev_err(pctldev->dev, "%pOF: could not parse node property\n",
np); return ret;
}
num_pins = pins->length / sizeof(u32); if (!num_pins) {
dev_err(pctldev->dev, "no pins found in node %pOF\n", np);
ret = -EINVAL; gotoexit;
}
/* * Reserve maps, at least there is a mux map and an optional conf * map for each pin.
*/
reserve = 1; if (num_configs)
reserve++;
reserve *= num_pins;
ret = pinctrl_utils_reserve_map(pctldev, map, reserved_maps, num_maps,
reserve); if (ret < 0) gotoexit;
for (i = 0; i < num_pins; i++) { constchar *group, *func;
ret = of_property_read_u32_index(np, "pinmux", i, &pinfunc); if (ret) gotoexit;
ret = atmel_pctl_xlate_pinfunc(pctldev, np, pinfunc, &group,
&func); if (ret) gotoexit;
ret = pinctrl_utils_add_map_mux(pctldev, map, reserved_maps, num_maps,
group, func); if (ret) gotoexit;
if (num_configs) {
ret = pinctrl_utils_add_map_configs(pctldev, map,
reserved_maps, num_maps, group,
configs, num_configs,
PIN_MAP_TYPE_CONFIGS_GROUP); if (ret < 0) gotoexit;
}
}
/* * If all the pins of a device have the same configuration (or no one), * it is useless to add a subnode, so directly parse node referenced by * phandle.
*/
ret = atmel_pctl_dt_subnode_to_map(pctldev, np_config, map,
&reserved_maps, num_maps); if (ret) {
for_each_child_of_node_scoped(np_config, np) {
ret = atmel_pctl_dt_subnode_to_map(pctldev, np, map,
&reserved_maps, num_maps); if (ret < 0) break;
}
}
if (ret < 0) {
pinctrl_utils_free_map(pctldev, *map, *num_maps);
dev_err(pctldev->dev, "can't create maps for node %pOF\n",
np_config);
}
switch (param) { case PIN_CONFIG_BIAS_DISABLE:
conf &= (~ATMEL_PIO_PUEN_MASK);
conf &= (~ATMEL_PIO_PDEN_MASK); break; case PIN_CONFIG_BIAS_PULL_UP:
conf |= ATMEL_PIO_PUEN_MASK;
conf &= (~ATMEL_PIO_PDEN_MASK); break; case PIN_CONFIG_BIAS_PULL_DOWN:
conf |= ATMEL_PIO_PDEN_MASK;
conf &= (~ATMEL_PIO_PUEN_MASK); break; case PIN_CONFIG_DRIVE_OPEN_DRAIN:
conf |= ATMEL_PIO_OPD_MASK; break; case PIN_CONFIG_DRIVE_PUSH_PULL:
conf &= ~ATMEL_PIO_OPD_MASK; break; case PIN_CONFIG_INPUT_SCHMITT_ENABLE: if (arg == 0)
conf |= ATMEL_PIO_SCHMITT_MASK; else
conf &= (~ATMEL_PIO_SCHMITT_MASK); break; case PIN_CONFIG_INPUT_DEBOUNCE: if (arg == 0) {
conf &= (~ATMEL_PIO_IFEN_MASK);
conf &= (~ATMEL_PIO_IFSCEN_MASK);
} else { /* * We don't care about the debounce value for several reasons: * - can't have different debounce periods inside a same group, * - the register to configure this period is a secure register. * The debouncing filter can filter a pulse with a duration of less * than 1/2 slow clock period.
*/
conf |= ATMEL_PIO_IFEN_MASK;
conf |= ATMEL_PIO_IFSCEN_MASK;
} break; case PIN_CONFIG_OUTPUT:
conf |= ATMEL_PIO_DIR_MASK;
bank = ATMEL_PIO_BANK(pin_id);
pin = ATMEL_PIO_LINE(pin_id);
mask = 1 << pin;
if (arg == 0) {
writel_relaxed(mask, atmel_pioctrl->reg_base +
bank * ATMEL_PIO_BANK_OFFSET +
ATMEL_PIO_CODR);
} else {
writel_relaxed(mask, atmel_pioctrl->reg_base +
bank * ATMEL_PIO_BANK_OFFSET +
ATMEL_PIO_SODR);
} break; case PIN_CONFIG_SLEW_RATE: if (!atmel_pioctrl->slew_rate_support) break; /* And remove it if explicitly requested. */ if (arg == 0)
conf &= ~ATMEL_PIO_SR_MASK; break; case ATMEL_PIN_CONFIG_DRIVE_STRENGTH: switch (arg) { case ATMEL_PIO_DRVSTR_LO: case ATMEL_PIO_DRVSTR_ME: case ATMEL_PIO_DRVSTR_HI:
conf &= (~ATMEL_PIO_DRVSTR_MASK);
conf |= arg << ATMEL_PIO_DRVSTR_OFFSET; break; default:
dev_warn(pctldev->dev, "drive strength not updated (incorrect value)\n");
} break; case PIN_CONFIG_PERSIST_STATE: return -ENOTSUPP; default:
dev_warn(pctldev->dev, "unsupported configuration parameter: %u\n",
param); continue;
}
}
staticint __maybe_unused atmel_pctrl_suspend(struct device *dev)
{ struct atmel_pioctrl *atmel_pioctrl = dev_get_drvdata(dev); int i, j;
/* * For each bank, save IMR to restore it later and disable all GPIO * interrupts excepting the ones marked as wakeup sources.
*/ for (i = 0; i < atmel_pioctrl->nbanks; i++) {
atmel_pioctrl->pm_suspend_backup[i].imr =
atmel_gpio_read(atmel_pioctrl, i, ATMEL_PIO_IMR);
atmel_gpio_write(atmel_pioctrl, i, ATMEL_PIO_IDR,
~atmel_pioctrl->pm_wakeup_sources[i]);
atmel_pioctrl->pm_suspend_backup[i].odsr =
atmel_gpio_read(atmel_pioctrl, i, ATMEL_PIO_ODSR); for (j = 0; j < ATMEL_PIO_NPINS_PER_BANK; j++) {
atmel_gpio_write(atmel_pioctrl, i,
ATMEL_PIO_MSKR, BIT(j));
atmel_pioctrl->pm_suspend_backup[i].cfgr[j] =
atmel_gpio_read(atmel_pioctrl, i,
ATMEL_PIO_CFGR);
}
}
return 0;
}
staticint __maybe_unused atmel_pctrl_resume(struct device *dev)
{ struct atmel_pioctrl *atmel_pioctrl = dev_get_drvdata(dev); int i, j;
for (i = 0; i < atmel_pioctrl->nbanks; i++) {
atmel_gpio_write(atmel_pioctrl, i, ATMEL_PIO_IER,
atmel_pioctrl->pm_suspend_backup[i].imr);
atmel_gpio_write(atmel_pioctrl, i, ATMEL_PIO_SODR,
atmel_pioctrl->pm_suspend_backup[i].odsr); for (j = 0; j < ATMEL_PIO_NPINS_PER_BANK; j++) {
atmel_gpio_write(atmel_pioctrl, i,
ATMEL_PIO_MSKR, BIT(j));
atmel_gpio_write(atmel_pioctrl, i, ATMEL_PIO_CFGR,
atmel_pioctrl->pm_suspend_backup[i].cfgr[j]);
}
}
/* * The number of banks can be different from a SoC to another one. * We can have up to 16 banks.
*/ staticconststruct atmel_pioctrl_data atmel_sama5d2_pioctrl_data = {
.nbanks = 4,
.last_bank_count = ATMEL_PIO_NPINS_PER_BANK,
};
staticconststruct atmel_pioctrl_data microchip_sama7g5_pioctrl_data = {
.nbanks = 5,
.last_bank_count = 8, /* sama7g5 has only PE0 to PE7 */
.slew_rate_support = 1,
};
/* * This lock class allows to tell lockdep that parent IRQ and children IRQ do * not share the same class so it does not raise false positive
*/ staticstruct lock_class_key atmel_lock_key; staticstruct lock_class_key atmel_request_key;
atmel_pioctrl->nbanks = atmel_pioctrl_data->nbanks;
atmel_pioctrl->npins = atmel_pioctrl->nbanks * ATMEL_PIO_NPINS_PER_BANK; /* if last bank has limited number of pins, adjust accordingly */ if (atmel_pioctrl_data->last_bank_count != ATMEL_PIO_NPINS_PER_BANK) {
atmel_pioctrl->npins -= ATMEL_PIO_NPINS_PER_BANK;
atmel_pioctrl->npins += atmel_pioctrl_data->last_bank_count;
}
atmel_pioctrl->slew_rate_support = atmel_pioctrl_data->slew_rate_support;
atmel_pioctrl->reg_base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(atmel_pioctrl->reg_base)) return PTR_ERR(atmel_pioctrl->reg_base);
atmel_pioctrl->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(atmel_pioctrl->clk)) return dev_err_probe(dev, PTR_ERR(atmel_pioctrl->clk), "failed to get clock\n");
atmel_pioctrl->pins = devm_kcalloc(dev,
atmel_pioctrl->npins, sizeof(*atmel_pioctrl->pins),
GFP_KERNEL); if (!atmel_pioctrl->pins) return -ENOMEM;
/* One pin is one group since a pin can achieve all functions. */
group_names = devm_kcalloc(dev,
atmel_pioctrl->npins, sizeof(*group_names),
GFP_KERNEL); if (!group_names) return -ENOMEM;
atmel_pioctrl->group_names = group_names;
atmel_pioctrl->groups = devm_kcalloc(&pdev->dev,
atmel_pioctrl->npins, sizeof(*atmel_pioctrl->groups),
GFP_KERNEL); if (!atmel_pioctrl->groups) return -ENOMEM; for (i = 0 ; i < atmel_pioctrl->npins; i++) { struct atmel_group *group = atmel_pioctrl->groups + i; unsignedint bank = ATMEL_PIO_BANK(i); unsignedint line = ATMEL_PIO_LINE(i);
atmel_pioctrl->pins[i] = devm_kzalloc(dev, sizeof(**atmel_pioctrl->pins), GFP_KERNEL); if (!atmel_pioctrl->pins[i]) return -ENOMEM;
atmel_pioctrl->pm_wakeup_sources = devm_kcalloc(dev,
atmel_pioctrl->nbanks, sizeof(*atmel_pioctrl->pm_wakeup_sources),
GFP_KERNEL); if (!atmel_pioctrl->pm_wakeup_sources) return -ENOMEM;
atmel_pioctrl->pm_suspend_backup = devm_kcalloc(dev,
atmel_pioctrl->nbanks, sizeof(*atmel_pioctrl->pm_suspend_backup),
GFP_KERNEL); if (!atmel_pioctrl->pm_suspend_backup) return -ENOMEM;
atmel_pioctrl->irqs = devm_kcalloc(dev,
atmel_pioctrl->nbanks, sizeof(*atmel_pioctrl->irqs),
GFP_KERNEL); if (!atmel_pioctrl->irqs) return -ENOMEM;
/* There is one controller but each bank has its own irq line. */ for (i = 0; i < atmel_pioctrl->nbanks; i++) {
ret = platform_get_irq(pdev, i); if (ret < 0) {
dev_dbg(dev, "missing irq resource for group %c\n", 'A' + i); return ret;
}
atmel_pioctrl->irqs[i] = ret;
irq_set_chained_handler_and_data(ret, atmel_gpio_irq_handler, atmel_pioctrl);
dev_dbg(dev, "bank %i: irq=%d\n", i, ret);
}
atmel_pioctrl->irq_domain = irq_domain_create_linear(dev_fwnode(dev),
atmel_pioctrl->gpio_chip->ngpio,
&irq_domain_simple_ops, NULL); if (!atmel_pioctrl->irq_domain) return dev_err_probe(dev, -ENODEV, "can't add the irq domain\n");
for (i = 0; i < atmel_pioctrl->npins; i++) { int irq = irq_create_mapping(atmel_pioctrl->irq_domain, i);
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.