/** * enum - Locking variants of the pad configuration * @PAD_UNLOCKED: pad is fully controlled by the configuration registers * @PAD_LOCKED: pad configuration registers, except TX state, are locked * @PAD_LOCKED_TX: pad configuration TX state is locked * @PAD_LOCKED_FULL: pad configuration registers are locked completely * * Locking is considered as read-only mode for corresponding registers and * their respective fields. That said, TX state bit is locked separately from * the main locking scheme.
*/ enum {
PAD_UNLOCKED = 0,
PAD_LOCKED = 1,
PAD_LOCKED_TX = 2,
PAD_LOCKED_FULL = PAD_LOCKED | PAD_LOCKED_TX,
};
community = intel_get_community(pctrl, pin); if (!community) return PAD_LOCKED_FULL; if (!community->padcfglock_offset) return PAD_UNLOCKED;
padgrp = intel_community_get_padgroup(community, pin); if (!padgrp) return PAD_LOCKED_FULL;
gpp_offset = padgroup_offset(padgrp, pin);
/* * If PADCFGLOCK and PADCFGLOCKTX bits are both clear for this pad, * the pad is considered unlocked. Any other case means that it is * either fully or partially locked.
*/
offset = community->padcfglock_offset + 0 + padgrp->reg_num * 8;
value = readl(community->regs + offset); if (value & BIT(gpp_offset))
ret |= PAD_LOCKED;
offset = community->padcfglock_offset + 4 + padgrp->reg_num * 8;
value = readl(community->regs + offset); if (value & BIT(gpp_offset))
ret |= PAD_LOCKED_TX;
/* Dump the additional PADCFG registers if available */
padcfg = intel_get_padcfg(pctrl, pin, PADCFG2); if (padcfg)
seq_printf(s, " 0x%08x", readl(padcfg));
/* * All pins in the groups needs to be accessible and writable * before we can enable the mux for this group.
*/ for (i = 0; i < grp->grp.npins; i++) { if (!intel_pad_usable(pctrl, grp->grp.pins[i])) return -EBUSY;
}
/* Now enable the mux setting for each pin in the group */ for (i = 0; i < grp->grp.npins; i++) { void __iomem *padcfg0;
u32 value, pmode;
value = readl(padcfg0);
value &= ~PADCFG0_PMODE_MASK;
if (grp->modes)
pmode = grp->modes[i]; else
pmode = grp->mode;
value |= pmode << PADCFG0_PMODE_SHIFT;
writel(value, padcfg0);
}
return0;
}
/** * enum - Possible pad physical connections * @PAD_CONNECT_NONE: pad is fully disconnected * @PAD_CONNECT_INPUT: pad is in input only mode * @PAD_CONNECT_OUTPUT: pad is in output only mode * @PAD_CONNECT_FULL: pad is fully connected
*/ enum {
PAD_CONNECT_NONE = 0,
PAD_CONNECT_INPUT = 1,
PAD_CONNECT_OUTPUT = 2,
PAD_CONNECT_FULL = PAD_CONNECT_INPUT | PAD_CONNECT_OUTPUT,
};
staticint __intel_gpio_get_direction(u32 value)
{ switch ((value & PADCFG0_GPIODIS_MASK) >> PADCFG0_GPIODIS_SHIFT) { case PADCFG0_GPIODIS_FULL: return PAD_CONNECT_NONE; case PADCFG0_GPIODIS_OUTPUT: return PAD_CONNECT_INPUT; case PADCFG0_GPIODIS_INPUT: return PAD_CONNECT_OUTPUT; case PADCFG0_GPIODIS_NONE: return PAD_CONNECT_FULL; default: return -ENOTSUPP;
};
}
static u32 __intel_gpio_set_direction(u32 value, bool input, bool output)
{ if (input)
value &= ~PADCFG0_GPIORXDIS; else
value |= PADCFG0_GPIORXDIS;
if (output)
value &= ~PADCFG0_GPIOTXDIS; else
value |= PADCFG0_GPIOTXDIS;
if (!intel_pad_owned_by_host(pctrl, pin)) return -EBUSY;
if (!intel_pad_is_unlocked(pctrl, pin)) return0;
/* * If pin is already configured in GPIO mode, we assume that * firmware provides correct settings. In such case we avoid * potential glitches on the pin. Otherwise, for the pin in * alternative mode, consumer has to supply respective flags.
*/ if (intel_gpio_get_gpio_mode(padcfg0) == PADCFG0_PMODE_GPIO) return0;
value = readl(padcfg0); if (input)
value = __intel_gpio_set_direction(value, true, false); else
value = __intel_gpio_set_direction(value, false, true);
writel(value, padcfg0);
if (!intel_pad_owned_by_host(pctrl, pin)) return -ENOTSUPP;
switch (param) { case PIN_CONFIG_BIAS_DISABLE: case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN:
ret = intel_config_get_pull(pctrl, pin, param, &arg); if (ret) return ret; break;
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
ret = intel_config_get_high_impedance(pctrl, pin, param, &arg); if (ret) return ret; break;
case PIN_CONFIG_INPUT_DEBOUNCE:
ret = intel_config_get_debounce(pctrl, pin, param, &arg); if (ret) return ret; break;
switch (param) { case PIN_CONFIG_BIAS_DISABLE: break;
case PIN_CONFIG_BIAS_PULL_UP: switch (arg) { case20000:
term = PADCFG1_TERM_20K; break; case1: /* Set default strength value in case none is given */ case5000:
term = PADCFG1_TERM_5K; break; case4000:
term = PADCFG1_TERM_4K; break; case1000:
term = PADCFG1_TERM_1K; break; case833:
term = PADCFG1_TERM_833; break; default: return -EINVAL;
}
up = PADCFG1_TERM_UP; break;
case PIN_CONFIG_BIAS_PULL_DOWN: { conststruct intel_community *community = intel_get_community(pctrl, pin);
switch (arg) { case20000:
term = PADCFG1_TERM_20K; break; case1: /* Set default strength value in case none is given */ case5000:
term = PADCFG1_TERM_5K; break; case4000:
term = PADCFG1_TERM_4K; break; case1000: if (!(community->features & PINCTRL_FEATURE_1K_PD)) return -EINVAL;
term = PADCFG1_TERM_1K; break; case833: if (!(community->features & PINCTRL_FEATURE_1K_PD)) return -EINVAL;
term = PADCFG1_TERM_833; break; default: return -EINVAL;
}
break;
}
default: return -EINVAL;
}
padcfg1 = intel_get_padcfg(pctrl, pin, PADCFG1);
guard(raw_spinlock_irqsave)(&pctrl->lock);
value = readl(padcfg1);
value = (value & ~PADCFG1_TERM_MASK) | (term << PADCFG1_TERM_SHIFT);
value = (value & ~PADCFG1_TERM_UP) | up;
writel(value, padcfg1);
staticint intel_config_set(struct pinctrl_dev *pctldev, unsignedint pin, unsignedlong *configs, unsignedint nconfigs)
{ struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); int i, ret;
if (!intel_pad_usable(pctrl, pin)) return -ENOTSUPP;
for (i = 0; i < nconfigs; i++) { switch (pinconf_to_config_param(configs[i])) { case PIN_CONFIG_BIAS_DISABLE: case PIN_CONFIG_BIAS_PULL_UP: case PIN_CONFIG_BIAS_PULL_DOWN:
ret = intel_config_set_pull(pctrl, pin, configs[i]); if (ret) return ret; break;
case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
intel_gpio_set_high_impedance(pctrl, pin); break;
case PIN_CONFIG_INPUT_DEBOUNCE:
ret = intel_config_set_debounce(pctrl, pin,
pinconf_to_config_argument(configs[i])); if (ret) return ret; break;
/** * intel_gpio_to_pin() - Translate from GPIO offset to pin number * @pctrl: Pinctrl structure * @offset: GPIO offset from gpiolib * @community: Community is filled here if not %NULL * @padgrp: Pad group is filled here if not %NULL * * When coming through gpiolib irqchip, the GPIO offset is not * automatically translated to pinctrl pin number. This function can be * used to find out the corresponding pinctrl pin. * * Return: a pin number and pointers to the community and pad group, which * the pin belongs to, or negative error code if translation can't be done.
*/ staticint intel_gpio_to_pin(conststruct intel_pinctrl *pctrl, unsignedint offset, conststruct intel_community **community, conststruct intel_padgroup **padgrp)
{ conststruct intel_community *comm; conststruct intel_padgroup *grp;
for_each_intel_gpio_group(pctrl, comm, grp) { if (offset >= grp->gpio_base && offset < grp->gpio_base + grp->size) { if (community)
*community = comm; if (padgrp)
*padgrp = grp;
return grp->base + offset - grp->gpio_base;
}
}
return -EINVAL;
}
/** * intel_pin_to_gpio() - Translate from pin number to GPIO offset * @pctrl: Pinctrl structure * @pin: pin number * * Translate the pin number of pinctrl to GPIO offset * * Return: a GPIO offset, or negative error code if translation can't be done.
*/ staticint intel_pin_to_gpio(conststruct intel_pinctrl *pctrl, int pin)
{ conststruct intel_community *community; conststruct intel_padgroup *padgrp;
community = intel_get_community(pctrl, pin); if (!community) return -EINVAL;
padgrp = intel_community_get_padgroup(community, pin); if (!padgrp) return -EINVAL;
reg = intel_get_padcfg(pctrl, pin, PADCFG0); if (!reg) return -EINVAL;
/* * If the pin is in ACPI mode it is still usable as a GPIO but it * cannot be used as IRQ because GPI_IS status bit will not be * updated by the host controller hardware.
*/ if (intel_pad_acpi_mode(pctrl, pin)) {
dev_warn(pctrl->dev, "pin %u cannot be used as IRQ\n", pin); return -EPERM;
}
/* * On some platforms several GPIO controllers share the same interrupt * line.
*/
ret = devm_request_irq(pctrl->dev, irq, intel_gpio_irq,
IRQF_SHARED | IRQF_NO_THREAD,
dev_name(pctrl->dev), pctrl); if (ret) {
dev_err(pctrl->dev, "failed to request interrupt\n"); return ret;
}
/* Setup IRQ chip */
girq = &pctrl->chip.irq;
gpio_irq_chip_set_chip(girq, &intel_gpio_irq_chip); /* This will let us handle the IRQ in the driver */
girq->parent_handler = NULL;
girq->num_parents = 0;
girq->default_type = IRQ_TYPE_NONE;
girq->handler = handle_bad_irq;
girq->init_hw = intel_gpio_irq_init_hw;
ret = devm_gpiochip_add_data(pctrl->dev, &pctrl->chip, pctrl); if (ret) {
dev_err(pctrl->dev, "failed to register gpiochip\n"); return ret;
}
gpps = devm_kcalloc(pctrl->dev, ngpps, sizeof(*gpps), GFP_KERNEL); if (!gpps) return -ENOMEM;
for (i = 0; i < ngpps; i++) {
gpps[i] = community->gpps[i];
if (gpps[i].size > INTEL_PINCTRL_MAX_GPP_SIZE) return -EINVAL;
/* Special treatment for GPIO base */ switch (gpps[i].gpio_base) { case INTEL_GPIO_BASE_MATCH:
gpps[i].gpio_base = gpps[i].base; break; case INTEL_GPIO_BASE_ZERO:
gpps[i].gpio_base = 0; break; case INTEL_GPIO_BASE_NOMAP: break; default: break;
}
/* * Make a copy of the communities which we can use to hold pointers * to the registers.
*/
pctrl->ncommunities = pctrl->soc->ncommunities;
pctrl->communities = devm_kmemdup_array(dev, pctrl->soc->communities, pctrl->ncommunities, sizeof(*pctrl->soc->communities), GFP_KERNEL); if (!pctrl->communities) return -ENOMEM;
for (i = 0; i < pctrl->ncommunities; i++) { struct intel_community *community = &pctrl->communities[i]; void __iomem *regs;
u32 offset;
u32 value;
regs = devm_platform_ioremap_resource(pdev, community->barno); if (IS_ERR(regs)) return PTR_ERR(regs);
/* * Determine community features based on the revision. * A value of all ones means the device is not present.
*/
value = readl(regs + REVID); if (value == ~0u) return -ENODEV; if (((value & REVID_MASK) >> REVID_SHIFT) >= 0x94) {
community->features |= PINCTRL_FEATURE_DEBOUNCE;
community->features |= PINCTRL_FEATURE_1K_PD;
}
/* Determine community features based on the capabilities */
offset = CAPLIST; do {
value = readl(regs + offset); switch ((value & CAPLIST_ID_MASK) >> CAPLIST_ID_SHIFT) { case CAPLIST_ID_GPIO_HW_INFO:
community->features |= PINCTRL_FEATURE_GPIO_HW_INFO; break; case CAPLIST_ID_PWM:
community->features |= PINCTRL_FEATURE_PWM; break; case CAPLIST_ID_BLINK:
community->features |= PINCTRL_FEATURE_BLINK; break; case CAPLIST_ID_EXP:
community->features |= PINCTRL_FEATURE_EXP; break; default: break;
}
offset = (value & CAPLIST_NEXT_MASK) >> CAPLIST_NEXT_SHIFT;
} while (offset);
dev_dbg(dev, "Community%d features: %#08x\n", i, community->features);
/* Read offset of the pad configuration registers */
offset = readl(regs + PADBAR);
if (community->gpps)
ret = intel_pinctrl_add_padgroups_by_gpps(pctrl, community); else
ret = intel_pinctrl_add_padgroups_by_size(pctrl, community); if (ret) return ret;
ret = intel_pinctrl_probe_pwm(pctrl, community); if (ret) return ret;
}
irq = platform_get_irq(pdev, 0); if (irq < 0) return irq;
ret = intel_pinctrl_pm_init(pctrl); if (ret) return ret;
if (!pd || !intel_pad_usable(pctrl, pin)) returnfalse;
/* * Only restore the pin if it is actually in use by the kernel (or * by userspace). It is possible that some pins are used by the * BIOS during resume and those are not always locked down so leave * them alone.
*/ if (pd->mux_owner || pd->gpio_owner ||
gpiochip_line_is_irq(&pctrl->chip, intel_pin_to_gpio(pctrl, pin))) returntrue;
/* * The firmware on some systems may configure GPIO pins to be * an interrupt source in so called "direct IRQ" mode. In such * cases the GPIO controller driver has no idea if those pins * are being used or not. At the same time, there is a known bug * in the firmwares that don't restore the pin settings correctly * after suspend, i.e. by an unknown reason the Rx value becomes * inverted. * * Hence, let's save and restore the pins that are configured * as GPIOs in the input mode with GPIROUTIOXAPIC bit set. * * See https://bugzilla.kernel.org/show_bug.cgi?id=214749.
*/
value = readl(intel_get_padcfg(pctrl, pin, PADCFG0)); if (__intel_gpio_is_direct_irq(value)) returntrue;
/* Mask all interrupts */
intel_gpio_irq_init(pctrl);
pads = pctrl->context.pads; for (i = 0; i < pctrl->soc->npins; i++) { conststruct pinctrl_pin_desc *desc = &pctrl->soc->pins[i];
if (!(intel_pinctrl_should_save(pctrl, desc->number) || /* * If the firmware mangled the register contents too much, * check the saved value for the Direct IRQ mode.
*/
__intel_gpio_is_direct_irq(pads[i].padcfg0))) continue;
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.