diff options
Diffstat (limited to 'drivers/pinctrl/intel/pinctrl-intel.c')
-rw-r--r-- | drivers/pinctrl/intel/pinctrl-intel.c | 378 |
1 files changed, 216 insertions, 162 deletions
diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index a18d6eefe672..74fdfd2b9ff5 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -8,12 +8,13 @@ */ #include <linux/acpi.h> -#include <linux/module.h> -#include <linux/interrupt.h> #include <linux/gpio/driver.h> +#include <linux/interrupt.h> #include <linux/log2.h> +#include <linux/module.h> #include <linux/platform_device.h> #include <linux/property.h> +#include <linux/time.h> #include <linux/pinctrl/pinctrl.h> #include <linux/pinctrl/pinmux.h> @@ -29,7 +30,6 @@ #define REVID_MASK GENMASK(31, 16) #define PADBAR 0x00c -#define GPI_IS 0x100 #define PADOWN_BITS 4 #define PADOWN_SHIFT(p) ((p) % 8 * PADOWN_BITS) @@ -52,6 +52,7 @@ #define PADCFG0_GPIROUTNMI BIT(17) #define PADCFG0_PMODE_SHIFT 10 #define PADCFG0_PMODE_MASK GENMASK(13, 10) +#define PADCFG0_PMODE_GPIO 0 #define PADCFG0_GPIORXDIS BIT(9) #define PADCFG0_GPIOTXDIS BIT(8) #define PADCFG0_GPIORXSTATE BIT(1) @@ -71,7 +72,7 @@ #define PADCFG2_DEBOUNCE_SHIFT 1 #define PADCFG2_DEBOUNCE_MASK GENMASK(4, 1) -#define DEBOUNCE_PERIOD 31250 /* ns */ +#define DEBOUNCE_PERIOD_NSEC 31250 struct intel_pad_context { u32 padcfg0; @@ -84,37 +85,6 @@ struct intel_community_context { u32 *hostown; }; -struct intel_pinctrl_context { - struct intel_pad_context *pads; - struct intel_community_context *communities; -}; - -/** - * struct intel_pinctrl - Intel pinctrl private structure - * @dev: Pointer to the device structure - * @lock: Lock to serialize register access - * @pctldesc: Pin controller description - * @pctldev: Pointer to the pin controller device - * @chip: GPIO chip in this pin controller - * @soc: SoC/PCH specific pin configuration data - * @communities: All communities in this pin controller - * @ncommunities: Number of communities in this pin controller - * @context: Configuration saved over system sleep - * @irq: pinctrl/GPIO chip irq number - */ -struct intel_pinctrl { - struct device *dev; - raw_spinlock_t lock; - struct pinctrl_desc pctldesc; - struct pinctrl_dev *pctldev; - struct gpio_chip chip; - const struct intel_pinctrl_soc_data *soc; - struct intel_community *communities; - size_t ncommunities; - struct intel_pinctrl_context context; - int irq; -}; - #define pin_to_padno(c, p) ((p) - (c)->pin_base) #define padgroup_offset(g, p) ((p) - (g)->base) @@ -165,7 +135,7 @@ static void __iomem *intel_get_padcfg(struct intel_pinctrl *pctrl, padno = pin_to_padno(community, pin); nregs = (community->features & PINCTRL_FEATURE_DEBOUNCE) ? 4 : 2; - if (reg == PADCFG2 && !(community->features & PINCTRL_FEATURE_DEBOUNCE)) + if (reg >= nregs * 4) return NULL; return community->pad_regs + reg + padno * nregs * 4; @@ -220,47 +190,71 @@ static bool intel_pad_acpi_mode(struct intel_pinctrl *pctrl, unsigned int pin) return !(readl(hostown) & BIT(gpp_offset)); } -static bool intel_pad_locked(struct intel_pinctrl *pctrl, unsigned int pin) +/** + * 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, +}; + +static int intel_pad_locked(struct intel_pinctrl *pctrl, unsigned int pin) { struct intel_community *community; const struct intel_padgroup *padgrp; unsigned int offset, gpp_offset; u32 value; + int ret = PAD_UNLOCKED; community = intel_get_community(pctrl, pin); if (!community) - return true; + return PAD_LOCKED_FULL; if (!community->padcfglock_offset) - return false; + return PAD_UNLOCKED; padgrp = intel_community_get_padgroup(community, pin); if (!padgrp) - return true; + 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 and we don't touch it. + * either fully or partially locked. */ - offset = community->padcfglock_offset + padgrp->reg_num * 8; + offset = community->padcfglock_offset + 0 + padgrp->reg_num * 8; value = readl(community->regs + offset); if (value & BIT(gpp_offset)) - return true; + ret |= PAD_LOCKED; offset = community->padcfglock_offset + 4 + padgrp->reg_num * 8; value = readl(community->regs + offset); if (value & BIT(gpp_offset)) - return true; + ret |= PAD_LOCKED_TX; - return false; + return ret; +} + +static bool intel_pad_is_unlocked(struct intel_pinctrl *pctrl, unsigned int pin) +{ + return (intel_pad_locked(pctrl, pin) & PAD_LOCKED) == PAD_UNLOCKED; } static bool intel_pad_usable(struct intel_pinctrl *pctrl, unsigned int pin) { - return intel_pad_owned_by_host(pctrl, pin) && - !intel_pad_locked(pctrl, pin); + return intel_pad_owned_by_host(pctrl, pin) && intel_pad_is_unlocked(pctrl, pin); } static int intel_get_groups_count(struct pinctrl_dev *pctldev) @@ -294,7 +288,8 @@ static void intel_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, struct intel_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); void __iomem *padcfg; u32 cfg0, cfg1, mode; - bool locked, acpi; + int locked; + bool acpi; if (!intel_pad_owned_by_host(pctrl, pin)) { seq_puts(s, "not available"); @@ -305,7 +300,7 @@ static void intel_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, cfg1 = readl(intel_get_padcfg(pctrl, pin, PADCFG1)); mode = (cfg0 & PADCFG0_PMODE_MASK) >> PADCFG0_PMODE_SHIFT; - if (!mode) + if (mode == PADCFG0_PMODE_GPIO) seq_puts(s, "GPIO "); else seq_printf(s, "mode %d ", mode); @@ -322,11 +317,16 @@ static void intel_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, if (locked || acpi) { seq_puts(s, " ["); - if (locked) { + if (locked) seq_puts(s, "LOCKED"); - if (acpi) - seq_puts(s, ", "); - } + if ((locked & PAD_LOCKED_FULL) == PAD_LOCKED_TX) + seq_puts(s, " tx"); + else if ((locked & PAD_LOCKED_FULL) == PAD_LOCKED_FULL) + seq_puts(s, " full"); + + if (locked && acpi) + seq_puts(s, ", "); + if (acpi) seq_puts(s, "ACPI"); seq_puts(s, "]"); @@ -426,6 +426,11 @@ static void __intel_gpio_set_direction(void __iomem *padcfg0, bool input) writel(value, padcfg0); } +static int intel_gpio_get_gpio_mode(void __iomem *padcfg0) +{ + return (readl(padcfg0) & PADCFG0_PMODE_MASK) >> PADCFG0_PMODE_SHIFT; +} + static void intel_gpio_set_gpio_mode(void __iomem *padcfg0) { u32 value; @@ -448,13 +453,31 @@ static int intel_gpio_request_enable(struct pinctrl_dev *pctldev, raw_spin_lock_irqsave(&pctrl->lock, flags); - if (!intel_pad_usable(pctrl, pin)) { + if (!intel_pad_owned_by_host(pctrl, pin)) { raw_spin_unlock_irqrestore(&pctrl->lock, flags); return -EBUSY; } + if (!intel_pad_is_unlocked(pctrl, pin)) { + raw_spin_unlock_irqrestore(&pctrl->lock, flags); + return 0; + } + padcfg0 = intel_get_padcfg(pctrl, pin, PADCFG0); + + /* + * 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) { + raw_spin_unlock_irqrestore(&pctrl->lock, flags); + return 0; + } + intel_gpio_set_gpio_mode(padcfg0); + /* Disable TX buffer and enable RX (this will be input) */ __intel_gpio_set_direction(padcfg0, true); @@ -566,7 +589,7 @@ static int intel_config_get(struct pinctrl_dev *pctldev, unsigned int pin, return -EINVAL; v = (v & PADCFG2_DEBOUNCE_MASK) >> PADCFG2_DEBOUNCE_SHIFT; - arg = BIT(v) * DEBOUNCE_PERIOD / 1000; + arg = BIT(v) * DEBOUNCE_PERIOD_NSEC / NSEC_PER_USEC; break; } @@ -683,7 +706,7 @@ static int intel_config_set_debounce(struct intel_pinctrl *pctrl, if (debounce) { unsigned long v; - v = order_base_2(debounce * 1000 / DEBOUNCE_PERIOD); + v = order_base_2(debounce * NSEC_PER_USEC / DEBOUNCE_PERIOD_NSEC); if (v < 3 || v > 15) { ret = -EINVAL; goto exit_unlock; @@ -796,6 +819,29 @@ static int intel_gpio_to_pin(struct intel_pinctrl *pctrl, unsigned int offset, 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 + */ +static __maybe_unused int intel_pin_to_gpio(struct intel_pinctrl *pctrl, int pin) +{ + const struct intel_community *community; + const struct intel_padgroup *padgrp; + + community = intel_get_community(pctrl, pin); + if (!community) + return -EINVAL; + + padgrp = intel_community_get_padgroup(community, pin); + if (!padgrp) + return -EINVAL; + + return pin - padgrp->base + padgrp->gpio_base; +} + static int intel_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct intel_pinctrl *pctrl = gpiochip_get_data(chip); @@ -865,7 +911,10 @@ static int intel_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) if (padcfg0 & PADCFG0_PMODE_MASK) return -EINVAL; - return !!(padcfg0 & PADCFG0_GPIOTXDIS); + if (padcfg0 & PADCFG0_GPIOTXDIS) + return GPIO_LINE_DIRECTION_IN; + + return GPIO_LINE_DIRECTION_OUT; } static int intel_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) @@ -1052,7 +1101,7 @@ static irqreturn_t intel_gpio_community_irq_handler(struct intel_pinctrl *pctrl, pending &= enabled; for_each_set_bit(gpp_offset, &pending, padgrp->size) { - unsigned irq; + unsigned int irq; irq = irq_find_mapping(gc->irq.domain, padgrp->gpio_base + gpp_offset); @@ -1081,18 +1130,8 @@ static irqreturn_t intel_gpio_irq(int irq, void *data) return ret; } -static struct irq_chip intel_gpio_irqchip = { - .name = "intel-gpio", - .irq_ack = intel_gpio_irq_ack, - .irq_mask = intel_gpio_irq_mask, - .irq_unmask = intel_gpio_irq_unmask, - .irq_set_type = intel_gpio_irq_type, - .irq_set_wake = intel_gpio_irq_wake, - .flags = IRQCHIP_MASK_ON_SUSPEND, -}; - -static int intel_gpio_add_pin_ranges(struct intel_pinctrl *pctrl, - const struct intel_community *community) +static int intel_gpio_add_community_ranges(struct intel_pinctrl *pctrl, + const struct intel_community *community) { int ret = 0, i; @@ -1112,7 +1151,25 @@ static int intel_gpio_add_pin_ranges(struct intel_pinctrl *pctrl, return ret; } -static unsigned intel_gpio_ngpio(const struct intel_pinctrl *pctrl) +static int intel_gpio_add_pin_ranges(struct gpio_chip *gc) +{ + struct intel_pinctrl *pctrl = gpiochip_get_data(gc); + int ret, i; + + for (i = 0; i < pctrl->ncommunities; i++) { + struct intel_community *community = &pctrl->communities[i]; + + ret = intel_gpio_add_community_ranges(pctrl, community); + if (ret) { + dev_err(pctrl->dev, "failed to add GPIO pin range\n"); + return ret; + } + } + + return 0; +} + +static unsigned int intel_gpio_ngpio(const struct intel_pinctrl *pctrl) { const struct intel_community *community; unsigned int ngpio = 0; @@ -1136,36 +1193,31 @@ static unsigned intel_gpio_ngpio(const struct intel_pinctrl *pctrl) static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq) { - int ret, i; + int ret; + struct gpio_irq_chip *girq; pctrl->chip = intel_gpio_chip; + /* Setup GPIO chip */ pctrl->chip.ngpio = intel_gpio_ngpio(pctrl); pctrl->chip.label = dev_name(pctrl->dev); pctrl->chip.parent = pctrl->dev; pctrl->chip.base = -1; + pctrl->chip.add_pin_ranges = intel_gpio_add_pin_ranges; pctrl->irq = irq; - ret = devm_gpiochip_add_data(pctrl->dev, &pctrl->chip, pctrl); - if (ret) { - dev_err(pctrl->dev, "failed to register gpiochip\n"); - return ret; - } - - for (i = 0; i < pctrl->ncommunities; i++) { - struct intel_community *community = &pctrl->communities[i]; - - ret = intel_gpio_add_pin_ranges(pctrl, community); - if (ret) { - dev_err(pctrl->dev, "failed to add GPIO pin range\n"); - return ret; - } - } + /* Setup IRQ chip */ + pctrl->irqchip.name = dev_name(pctrl->dev); + pctrl->irqchip.irq_ack = intel_gpio_irq_ack; + pctrl->irqchip.irq_mask = intel_gpio_irq_mask; + pctrl->irqchip.irq_unmask = intel_gpio_irq_unmask; + pctrl->irqchip.irq_set_type = intel_gpio_irq_type; + pctrl->irqchip.irq_set_wake = intel_gpio_irq_wake; + pctrl->irqchip.flags = IRQCHIP_MASK_ON_SUSPEND; /* - * We need to request the interrupt here (instead of providing chip - * to the irq directly) because on some platforms several GPIO - * controllers share the same interrupt line. + * 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, @@ -1175,15 +1227,20 @@ static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq) return ret; } - ret = gpiochip_irqchip_add(&pctrl->chip, &intel_gpio_irqchip, 0, - handle_bad_irq, IRQ_TYPE_NONE); + girq = &pctrl->chip.irq; + girq->chip = &pctrl->irqchip; + /* 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; + + ret = devm_gpiochip_add_data(pctrl->dev, &pctrl->chip, pctrl); if (ret) { - dev_err(pctrl->dev, "failed to add irqchip\n"); + dev_err(pctrl->dev, "failed to register gpiochip\n"); return ret; } - gpiochip_set_chained_irqchip(&pctrl->chip, &intel_gpio_irqchip, irq, - NULL); return 0; } @@ -1313,15 +1370,12 @@ static int intel_pinctrl_probe(struct platform_device *pdev, for (i = 0; i < pctrl->ncommunities; i++) { struct intel_community *community = &pctrl->communities[i]; - struct resource *res; void __iomem *regs; u32 padbar; *community = pctrl->soc->communities[i]; - res = platform_get_resource(pdev, IORESOURCE_MEM, - community->barno); - regs = devm_ioremap_resource(&pdev->dev, res); + regs = devm_platform_ioremap_resource(pdev, community->barno); if (IS_ERR(regs)) return PTR_ERR(regs); @@ -1345,19 +1399,14 @@ static int intel_pinctrl_probe(struct platform_device *pdev, community->regs = regs; community->pad_regs = regs + padbar; - if (!community->is_offset) - community->is_offset = GPI_IS; - ret = intel_pinctrl_add_padgroups(pctrl, community); if (ret) return ret; } irq = platform_get_irq(pdev, 0); - if (irq < 0) { - dev_err(&pdev->dev, "failed to get interrupt number\n"); + if (irq < 0) return irq; - } ret = intel_pinctrl_pm_init(pctrl); if (ret) @@ -1421,8 +1470,6 @@ int intel_pinctrl_probe_by_uid(struct platform_device *pdev) table = (const struct intel_pinctrl_soc_data **)id->driver_data; data = table[pdev->id]; } - if (!data) - return -ENODEV; return intel_pinctrl_probe(pdev, data); } @@ -1443,7 +1490,7 @@ static bool intel_pinctrl_should_save(struct intel_pinctrl *pctrl, unsigned int * them alone. */ if (pd->mux_owner || pd->gpio_owner || - gpiochip_line_is_irq(&pctrl->chip, pin)) + gpiochip_line_is_irq(&pctrl->chip, intel_pin_to_gpio(pctrl, pin))) return true; return false; @@ -1527,16 +1574,65 @@ intel_gpio_is_requested(struct gpio_chip *chip, int base, unsigned int size) return requested; } -static u32 -intel_gpio_update_pad_mode(void __iomem *hostown, u32 mask, u32 value) +static bool intel_gpio_update_reg(void __iomem *reg, u32 mask, u32 value) { u32 curr, updated; - curr = readl(hostown); + curr = readl(reg); + updated = (curr & ~mask) | (value & mask); - writel(updated, hostown); + if (curr == updated) + return false; - return curr; + writel(updated, reg); + return true; +} + +static void intel_restore_hostown(struct intel_pinctrl *pctrl, unsigned int c, + void __iomem *base, unsigned int gpp, u32 saved) +{ + const struct intel_community *community = &pctrl->communities[c]; + const struct intel_padgroup *padgrp = &community->gpps[gpp]; + struct device *dev = pctrl->dev; + u32 requested; + + if (padgrp->gpio_base < 0) + return; + + requested = intel_gpio_is_requested(&pctrl->chip, padgrp->gpio_base, padgrp->size); + if (!intel_gpio_update_reg(base + gpp * 4, requested, saved)) + return; + + dev_dbg(dev, "restored hostown %u/%u %#08x\n", c, gpp, readl(base + gpp * 4)); +} + +static void intel_restore_intmask(struct intel_pinctrl *pctrl, unsigned int c, + void __iomem *base, unsigned int gpp, u32 saved) +{ + struct device *dev = pctrl->dev; + + if (!intel_gpio_update_reg(base + gpp * 4, ~0U, saved)) + return; + + dev_dbg(dev, "restored mask %u/%u %#08x\n", c, gpp, readl(base + gpp * 4)); +} + +static void intel_restore_padcfg(struct intel_pinctrl *pctrl, unsigned int pin, + unsigned int reg, u32 saved) +{ + u32 mask = (reg == PADCFG0) ? PADCFG0_GPIORXSTATE : 0; + unsigned int n = reg / sizeof(u32); + struct device *dev = pctrl->dev; + void __iomem *padcfg; + + padcfg = intel_get_padcfg(pctrl, pin, reg); + if (!padcfg) + return; + + if (!intel_gpio_update_reg(padcfg, ~mask, saved)) + return; + + dev_dbg(dev, "restored pin %u padcfg%u %#08x\n", pin, n, readl(padcfg)); } int intel_pinctrl_resume_noirq(struct device *dev) @@ -1552,37 +1648,13 @@ int intel_pinctrl_resume_noirq(struct device *dev) pads = pctrl->context.pads; for (i = 0; i < pctrl->soc->npins; i++) { const struct pinctrl_pin_desc *desc = &pctrl->soc->pins[i]; - void __iomem *padcfg; - u32 val; if (!intel_pinctrl_should_save(pctrl, desc->number)) continue; - padcfg = intel_get_padcfg(pctrl, desc->number, PADCFG0); - val = readl(padcfg) & ~PADCFG0_GPIORXSTATE; - if (val != pads[i].padcfg0) { - writel(pads[i].padcfg0, padcfg); - dev_dbg(dev, "restored pin %u padcfg0 %#08x\n", - desc->number, readl(padcfg)); - } - - padcfg = intel_get_padcfg(pctrl, desc->number, PADCFG1); - val = readl(padcfg); - if (val != pads[i].padcfg1) { - writel(pads[i].padcfg1, padcfg); - dev_dbg(dev, "restored pin %u padcfg1 %#08x\n", - desc->number, readl(padcfg)); - } - - padcfg = intel_get_padcfg(pctrl, desc->number, PADCFG2); - if (padcfg) { - val = readl(padcfg); - if (val != pads[i].padcfg2) { - writel(pads[i].padcfg2, padcfg); - dev_dbg(dev, "restored pin %u padcfg2 %#08x\n", - desc->number, readl(padcfg)); - } - } + intel_restore_padcfg(pctrl, desc->number, PADCFG0, pads[i].padcfg0); + intel_restore_padcfg(pctrl, desc->number, PADCFG1, pads[i].padcfg1); + intel_restore_padcfg(pctrl, desc->number, PADCFG2, pads[i].padcfg2); } communities = pctrl->context.communities; @@ -1592,30 +1664,12 @@ int intel_pinctrl_resume_noirq(struct device *dev) unsigned int gpp; base = community->regs + community->ie_offset; - for (gpp = 0; gpp < community->ngpps; gpp++) { - writel(communities[i].intmask[gpp], base + gpp * 4); - dev_dbg(dev, "restored mask %d/%u %#08x\n", i, gpp, - readl(base + gpp * 4)); - } + for (gpp = 0; gpp < community->ngpps; gpp++) + intel_restore_intmask(pctrl, i, base, gpp, communities[i].intmask[gpp]); base = community->regs + community->hostown_offset; - for (gpp = 0; gpp < community->ngpps; gpp++) { - const struct intel_padgroup *padgrp = &community->gpps[gpp]; - u32 requested = 0, value = 0; - u32 saved = communities[i].hostown[gpp]; - - if (padgrp->gpio_base < 0) - continue; - - requested = intel_gpio_is_requested(&pctrl->chip, - padgrp->gpio_base, padgrp->size); - value = intel_gpio_update_pad_mode(base + gpp * 4, - requested, saved); - if ((value ^ saved) & requested) { - dev_warn(dev, "restore hostown %d/%u %#8x->%#8x\n", - i, gpp, value, saved); - } - } + for (gpp = 0; gpp < community->ngpps; gpp++) + intel_restore_hostown(pctrl, i, base, gpp, communities[i].hostown[gpp]); } return 0; |