Commit d52daa86 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'pinctrl-v5.8-4' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl

Pull pin control fix from Linus Walleij:
 "A single last minute pin control fix to the Qualcomm driver fixing
  missing dual edge PCH interrupts"

* tag 'pinctrl-v5.8-4' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl:
  pinctrl: qcom: Handle broken/missing PDC dual edge IRQs on sc7180
parents 7dc6fd0f c3c0c2e1
...@@ -7,6 +7,8 @@ config PINCTRL_MSM ...@@ -7,6 +7,8 @@ config PINCTRL_MSM
select PINCONF select PINCONF
select GENERIC_PINCONF select GENERIC_PINCONF
select GPIOLIB_IRQCHIP select GPIOLIB_IRQCHIP
select IRQ_DOMAIN_HIERARCHY
select IRQ_FASTEOI_HIERARCHY_HANDLERS
config PINCTRL_APQ8064 config PINCTRL_APQ8064
tristate "Qualcomm APQ8064 pin controller driver" tristate "Qualcomm APQ8064 pin controller driver"
......
...@@ -832,6 +832,52 @@ static void msm_gpio_irq_unmask(struct irq_data *d) ...@@ -832,6 +832,52 @@ static void msm_gpio_irq_unmask(struct irq_data *d)
msm_gpio_irq_clear_unmask(d, false); msm_gpio_irq_clear_unmask(d, false);
} }
/**
* msm_gpio_update_dual_edge_parent() - Prime next edge for IRQs handled by parent.
* @d: The irq dta.
*
* This is much like msm_gpio_update_dual_edge_pos() but for IRQs that are
* normally handled by the parent irqchip. The logic here is slightly
* different due to what's easy to do with our parent, but in principle it's
* the same.
*/
static void msm_gpio_update_dual_edge_parent(struct irq_data *d)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
const struct msm_pingroup *g = &pctrl->soc->groups[d->hwirq];
int loop_limit = 100;
unsigned int val;
unsigned int type;
/* Read the value and make a guess about what edge we need to catch */
val = msm_readl_io(pctrl, g) & BIT(g->in_bit);
type = val ? IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
do {
/* Set the parent to catch the next edge */
irq_chip_set_type_parent(d, type);
/*
* Possibly the line changed between when we last read "val"
* (and decided what edge we needed) and when set the edge.
* If the value didn't change (or changed and then changed
* back) then we're done.
*/
val = msm_readl_io(pctrl, g) & BIT(g->in_bit);
if (type == IRQ_TYPE_EDGE_RISING) {
if (!val)
return;
type = IRQ_TYPE_EDGE_FALLING;
} else if (type == IRQ_TYPE_EDGE_FALLING) {
if (val)
return;
type = IRQ_TYPE_EDGE_RISING;
}
} while (loop_limit-- > 0);
dev_warn_once(pctrl->dev, "dual-edge irq failed to stabilize\n");
}
static void msm_gpio_irq_ack(struct irq_data *d) static void msm_gpio_irq_ack(struct irq_data *d)
{ {
struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
...@@ -840,8 +886,11 @@ static void msm_gpio_irq_ack(struct irq_data *d) ...@@ -840,8 +886,11 @@ static void msm_gpio_irq_ack(struct irq_data *d)
unsigned long flags; unsigned long flags;
u32 val; u32 val;
if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) {
if (test_bit(d->hwirq, pctrl->dual_edge_irqs))
msm_gpio_update_dual_edge_parent(d);
return; return;
}
g = &pctrl->soc->groups[d->hwirq]; g = &pctrl->soc->groups[d->hwirq];
...@@ -860,6 +909,17 @@ static void msm_gpio_irq_ack(struct irq_data *d) ...@@ -860,6 +909,17 @@ static void msm_gpio_irq_ack(struct irq_data *d)
raw_spin_unlock_irqrestore(&pctrl->lock, flags); raw_spin_unlock_irqrestore(&pctrl->lock, flags);
} }
static bool msm_gpio_needs_dual_edge_parent_workaround(struct irq_data *d,
unsigned int type)
{
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
return type == IRQ_TYPE_EDGE_BOTH &&
pctrl->soc->wakeirq_dual_edge_errata && d->parent_data &&
test_bit(d->hwirq, pctrl->skip_wake_irqs);
}
static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)
{ {
struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
...@@ -868,11 +928,21 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) ...@@ -868,11 +928,21 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)
unsigned long flags; unsigned long flags;
u32 val; u32 val;
if (msm_gpio_needs_dual_edge_parent_workaround(d, type)) {
set_bit(d->hwirq, pctrl->dual_edge_irqs);
irq_set_handler_locked(d, handle_fasteoi_ack_irq);
msm_gpio_update_dual_edge_parent(d);
return 0;
}
if (d->parent_data) if (d->parent_data)
irq_chip_set_type_parent(d, type); irq_chip_set_type_parent(d, type);
if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) {
clear_bit(d->hwirq, pctrl->dual_edge_irqs);
irq_set_handler_locked(d, handle_fasteoi_irq);
return 0; return 0;
}
g = &pctrl->soc->groups[d->hwirq]; g = &pctrl->soc->groups[d->hwirq];
......
...@@ -113,6 +113,9 @@ struct msm_gpio_wakeirq_map { ...@@ -113,6 +113,9 @@ struct msm_gpio_wakeirq_map {
* @pull_no_keeper: The SoC does not support keeper bias. * @pull_no_keeper: The SoC does not support keeper bias.
* @wakeirq_map: The map of wakeup capable GPIOs and the pin at PDC/MPM * @wakeirq_map: The map of wakeup capable GPIOs and the pin at PDC/MPM
* @nwakeirq_map: The number of entries in @wakeirq_map * @nwakeirq_map: The number of entries in @wakeirq_map
* @wakeirq_dual_edge_errata: If true then GPIOs using the wakeirq_map need
* to be aware that their parent can't handle dual
* edge interrupts.
*/ */
struct msm_pinctrl_soc_data { struct msm_pinctrl_soc_data {
const struct pinctrl_pin_desc *pins; const struct pinctrl_pin_desc *pins;
...@@ -128,6 +131,7 @@ struct msm_pinctrl_soc_data { ...@@ -128,6 +131,7 @@ struct msm_pinctrl_soc_data {
const int *reserved_gpios; const int *reserved_gpios;
const struct msm_gpio_wakeirq_map *wakeirq_map; const struct msm_gpio_wakeirq_map *wakeirq_map;
unsigned int nwakeirq_map; unsigned int nwakeirq_map;
bool wakeirq_dual_edge_errata;
}; };
extern const struct dev_pm_ops msm_pinctrl_dev_pm_ops; extern const struct dev_pm_ops msm_pinctrl_dev_pm_ops;
......
...@@ -1147,6 +1147,7 @@ static const struct msm_pinctrl_soc_data sc7180_pinctrl = { ...@@ -1147,6 +1147,7 @@ static const struct msm_pinctrl_soc_data sc7180_pinctrl = {
.ntiles = ARRAY_SIZE(sc7180_tiles), .ntiles = ARRAY_SIZE(sc7180_tiles),
.wakeirq_map = sc7180_pdc_map, .wakeirq_map = sc7180_pdc_map,
.nwakeirq_map = ARRAY_SIZE(sc7180_pdc_map), .nwakeirq_map = ARRAY_SIZE(sc7180_pdc_map),
.wakeirq_dual_edge_errata = true,
}; };
static int sc7180_pinctrl_probe(struct platform_device *pdev) static int sc7180_pinctrl_probe(struct platform_device *pdev)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment