diff options
Diffstat (limited to 'drivers/pinctrl/intel/pinctrl-intel.c')
-rw-r--r-- | drivers/pinctrl/intel/pinctrl-intel.c | 194 |
1 files changed, 142 insertions, 52 deletions
diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index 592b465e981e..78c48497c9e6 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -117,6 +117,7 @@ struct intel_pinctrl { }; #define pin_to_padno(c, p) ((p) - (c)->pin_base) +#define padgroup_offset(g, p) ((p) - (g)->base) static struct intel_community *intel_get_community(struct intel_pinctrl *pctrl, unsigned pin) @@ -135,6 +136,22 @@ static struct intel_community *intel_get_community(struct intel_pinctrl *pctrl, return NULL; } +static const struct intel_padgroup * +intel_community_get_padgroup(const struct intel_community *community, + unsigned pin) +{ + int i; + + for (i = 0; i < community->ngpps; i++) { + const struct intel_padgroup *padgrp = &community->gpps[i]; + + if (pin >= padgrp->base && pin < padgrp->base + padgrp->size) + return padgrp; + } + + return NULL; +} + static void __iomem *intel_get_padcfg(struct intel_pinctrl *pctrl, unsigned pin, unsigned reg) { @@ -158,7 +175,8 @@ static void __iomem *intel_get_padcfg(struct intel_pinctrl *pctrl, unsigned pin, static bool intel_pad_owned_by_host(struct intel_pinctrl *pctrl, unsigned pin) { const struct intel_community *community; - unsigned padno, gpp, offset, group; + const struct intel_padgroup *padgrp; + unsigned gpp, offset, gpp_offset; void __iomem *padown; community = intel_get_community(pctrl, pin); @@ -167,19 +185,23 @@ static bool intel_pad_owned_by_host(struct intel_pinctrl *pctrl, unsigned pin) if (!community->padown_offset) return true; - padno = pin_to_padno(community, pin); - group = padno / community->gpp_size; - gpp = PADOWN_GPP(padno % community->gpp_size); - offset = community->padown_offset + 0x10 * group + gpp * 4; + padgrp = intel_community_get_padgroup(community, pin); + if (!padgrp) + return false; + + gpp_offset = padgroup_offset(padgrp, pin); + gpp = PADOWN_GPP(gpp_offset); + offset = community->padown_offset + padgrp->padown_num * 4 + gpp * 4; padown = community->regs + offset; - return !(readl(padown) & PADOWN_MASK(padno)); + return !(readl(padown) & PADOWN_MASK(gpp_offset)); } static bool intel_pad_acpi_mode(struct intel_pinctrl *pctrl, unsigned pin) { const struct intel_community *community; - unsigned padno, gpp, offset; + const struct intel_padgroup *padgrp; + unsigned offset, gpp_offset; void __iomem *hostown; community = intel_get_community(pctrl, pin); @@ -188,18 +210,22 @@ static bool intel_pad_acpi_mode(struct intel_pinctrl *pctrl, unsigned pin) if (!community->hostown_offset) return false; - padno = pin_to_padno(community, pin); - gpp = padno / community->gpp_size; - offset = community->hostown_offset + gpp * 4; + padgrp = intel_community_get_padgroup(community, pin); + if (!padgrp) + return true; + + gpp_offset = padgroup_offset(padgrp, pin); + offset = community->hostown_offset + padgrp->reg_num * 4; hostown = community->regs + offset; - return !(readl(hostown) & BIT(padno % community->gpp_size)); + return !(readl(hostown) & BIT(gpp_offset)); } static bool intel_pad_locked(struct intel_pinctrl *pctrl, unsigned pin) { struct intel_community *community; - unsigned padno, gpp, offset; + const struct intel_padgroup *padgrp; + unsigned offset, gpp_offset; u32 value; community = intel_get_community(pctrl, pin); @@ -208,22 +234,25 @@ static bool intel_pad_locked(struct intel_pinctrl *pctrl, unsigned pin) if (!community->padcfglock_offset) return false; - padno = pin_to_padno(community, pin); - gpp = padno / community->gpp_size; + padgrp = intel_community_get_padgroup(community, pin); + if (!padgrp) + return true; + + 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. */ - offset = community->padcfglock_offset + gpp * 8; + offset = community->padcfglock_offset + padgrp->reg_num * 8; value = readl(community->regs + offset); - if (value & BIT(pin % community->gpp_size)) + if (value & BIT(gpp_offset)) return true; - offset = community->padcfglock_offset + 4 + gpp * 8; + offset = community->padcfglock_offset + 4 + padgrp->reg_num * 8; value = readl(community->regs + offset); - if (value & BIT(pin % community->gpp_size)) + if (value & BIT(gpp_offset)) return true; return false; @@ -777,18 +806,22 @@ static void intel_gpio_irq_ack(struct irq_data *d) const struct intel_community *community; unsigned pin = irqd_to_hwirq(d); - raw_spin_lock(&pctrl->lock); - community = intel_get_community(pctrl, pin); if (community) { - unsigned padno = pin_to_padno(community, pin); - unsigned gpp_offset = padno % community->gpp_size; - unsigned gpp = padno / community->gpp_size; + const struct intel_padgroup *padgrp; + unsigned gpp, gpp_offset; + + padgrp = intel_community_get_padgroup(community, pin); + if (!padgrp) + return; + + gpp = padgrp->reg_num; + gpp_offset = padgroup_offset(padgrp, pin); + raw_spin_lock(&pctrl->lock); writel(BIT(gpp_offset), community->regs + GPI_IS + gpp * 4); + raw_spin_unlock(&pctrl->lock); } - - raw_spin_unlock(&pctrl->lock); } static void intel_gpio_irq_enable(struct irq_data *d) @@ -797,27 +830,30 @@ static void intel_gpio_irq_enable(struct irq_data *d) struct intel_pinctrl *pctrl = gpiochip_get_data(gc); const struct intel_community *community; unsigned pin = irqd_to_hwirq(d); - unsigned long flags; - - raw_spin_lock_irqsave(&pctrl->lock, flags); community = intel_get_community(pctrl, pin); if (community) { - unsigned padno = pin_to_padno(community, pin); - unsigned gpp_size = community->gpp_size; - unsigned gpp_offset = padno % gpp_size; - unsigned gpp = padno / gpp_size; + const struct intel_padgroup *padgrp; + unsigned gpp, gpp_offset; + unsigned long flags; u32 value; + padgrp = intel_community_get_padgroup(community, pin); + if (!padgrp) + return; + + gpp = padgrp->reg_num; + gpp_offset = padgroup_offset(padgrp, pin); + + raw_spin_lock_irqsave(&pctrl->lock, flags); /* Clear interrupt status first to avoid unexpected interrupt */ writel(BIT(gpp_offset), community->regs + GPI_IS + gpp * 4); value = readl(community->regs + community->ie_offset + gpp * 4); value |= BIT(gpp_offset); writel(value, community->regs + community->ie_offset + gpp * 4); + raw_spin_unlock_irqrestore(&pctrl->lock, flags); } - - raw_spin_unlock_irqrestore(&pctrl->lock, flags); } static void intel_gpio_irq_mask_unmask(struct irq_data *d, bool mask) @@ -826,28 +862,33 @@ static void intel_gpio_irq_mask_unmask(struct irq_data *d, bool mask) struct intel_pinctrl *pctrl = gpiochip_get_data(gc); const struct intel_community *community; unsigned pin = irqd_to_hwirq(d); - unsigned long flags; - - raw_spin_lock_irqsave(&pctrl->lock, flags); community = intel_get_community(pctrl, pin); if (community) { - unsigned padno = pin_to_padno(community, pin); - unsigned gpp_offset = padno % community->gpp_size; - unsigned gpp = padno / community->gpp_size; + const struct intel_padgroup *padgrp; + unsigned gpp, gpp_offset; + unsigned long flags; void __iomem *reg; u32 value; + padgrp = intel_community_get_padgroup(community, pin); + if (!padgrp) + return; + + gpp = padgrp->reg_num; + gpp_offset = padgroup_offset(padgrp, pin); + reg = community->regs + community->ie_offset + gpp * 4; + + raw_spin_lock_irqsave(&pctrl->lock, flags); value = readl(reg); if (mask) value &= ~BIT(gpp_offset); else value |= BIT(gpp_offset); writel(value, reg); + raw_spin_unlock_irqrestore(&pctrl->lock, flags); } - - raw_spin_unlock_irqrestore(&pctrl->lock, flags); } static void intel_gpio_irq_mask(struct irq_data *d) @@ -938,23 +979,20 @@ static irqreturn_t intel_gpio_community_irq_handler(struct intel_pinctrl *pctrl, int gpp; for (gpp = 0; gpp < community->ngpps; gpp++) { + const struct intel_padgroup *padgrp = &community->gpps[gpp]; unsigned long pending, enabled, gpp_offset; - pending = readl(community->regs + GPI_IS + gpp * 4); + pending = readl(community->regs + GPI_IS + padgrp->reg_num * 4); enabled = readl(community->regs + community->ie_offset + - gpp * 4); + padgrp->reg_num * 4); /* Only interrupts that are enabled */ pending &= enabled; - for_each_set_bit(gpp_offset, &pending, community->gpp_size) { + for_each_set_bit(gpp_offset, &pending, padgrp->size) { unsigned padno, irq; - /* - * The last group in community can have less pins - * than NPADS_IN_GPP. - */ - padno = gpp_offset + gpp * community->gpp_size; + padno = padgrp->base - community->pin_base + gpp_offset; if (padno >= community->npins) break; @@ -1045,6 +1083,56 @@ static int intel_gpio_probe(struct intel_pinctrl *pctrl, int irq) return 0; } +static int intel_pinctrl_add_padgroups(struct intel_pinctrl *pctrl, + struct intel_community *community) +{ + struct intel_padgroup *gpps; + unsigned npins = community->npins; + unsigned padown_num = 0; + size_t ngpps, i; + + if (community->gpps) + ngpps = community->ngpps; + else + ngpps = DIV_ROUND_UP(community->npins, community->gpp_size); + + gpps = devm_kcalloc(pctrl->dev, ngpps, sizeof(*gpps), GFP_KERNEL); + if (!gpps) + return -ENOMEM; + + for (i = 0; i < ngpps; i++) { + if (community->gpps) { + gpps[i] = community->gpps[i]; + } else { + unsigned gpp_size = community->gpp_size; + + gpps[i].reg_num = i; + gpps[i].base = community->pin_base + i * gpp_size; + gpps[i].size = min(gpp_size, npins); + npins -= gpps[i].size; + } + + if (gpps[i].size > 32) + return -EINVAL; + + gpps[i].padown_num = padown_num; + + /* + * In older hardware the number of padown registers per + * group is fixed regardless of the group size. + */ + if (community->gpp_num_padown_regs) + padown_num += community->gpp_num_padown_regs; + else + padown_num += DIV_ROUND_UP(gpps[i].size * 4, 32); + } + + community->ngpps = ngpps; + community->gpps = gpps; + + return 0; +} + static int intel_pinctrl_pm_init(struct intel_pinctrl *pctrl) { #ifdef CONFIG_PM_SLEEP @@ -1142,8 +1230,10 @@ int intel_pinctrl_probe(struct platform_device *pdev, community->regs = regs; community->pad_regs = regs + padbar; - community->ngpps = DIV_ROUND_UP(community->npins, - community->gpp_size); + + ret = intel_pinctrl_add_padgroups(pctrl, community); + if (ret) + return ret; } irq = platform_get_irq(pdev, 0); |