Skip to content

Commit d52daa8

Browse files
committed
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
2 parents 7dc6fd0 + c3c0c2e commit d52daa8

File tree

4 files changed

+79
-2
lines changed

4 files changed

+79
-2
lines changed

drivers/pinctrl/qcom/Kconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ config PINCTRL_MSM
77
select PINCONF
88
select GENERIC_PINCONF
99
select GPIOLIB_IRQCHIP
10+
select IRQ_DOMAIN_HIERARCHY
11+
select IRQ_FASTEOI_HIERARCHY_HANDLERS
1012

1113
config PINCTRL_APQ8064
1214
tristate "Qualcomm APQ8064 pin controller driver"

drivers/pinctrl/qcom/pinctrl-msm.c

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -832,6 +832,52 @@ static void msm_gpio_irq_unmask(struct irq_data *d)
832832
msm_gpio_irq_clear_unmask(d, false);
833833
}
834834

835+
/**
836+
* msm_gpio_update_dual_edge_parent() - Prime next edge for IRQs handled by parent.
837+
* @d: The irq dta.
838+
*
839+
* This is much like msm_gpio_update_dual_edge_pos() but for IRQs that are
840+
* normally handled by the parent irqchip. The logic here is slightly
841+
* different due to what's easy to do with our parent, but in principle it's
842+
* the same.
843+
*/
844+
static void msm_gpio_update_dual_edge_parent(struct irq_data *d)
845+
{
846+
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
847+
struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
848+
const struct msm_pingroup *g = &pctrl->soc->groups[d->hwirq];
849+
int loop_limit = 100;
850+
unsigned int val;
851+
unsigned int type;
852+
853+
/* Read the value and make a guess about what edge we need to catch */
854+
val = msm_readl_io(pctrl, g) & BIT(g->in_bit);
855+
type = val ? IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
856+
857+
do {
858+
/* Set the parent to catch the next edge */
859+
irq_chip_set_type_parent(d, type);
860+
861+
/*
862+
* Possibly the line changed between when we last read "val"
863+
* (and decided what edge we needed) and when set the edge.
864+
* If the value didn't change (or changed and then changed
865+
* back) then we're done.
866+
*/
867+
val = msm_readl_io(pctrl, g) & BIT(g->in_bit);
868+
if (type == IRQ_TYPE_EDGE_RISING) {
869+
if (!val)
870+
return;
871+
type = IRQ_TYPE_EDGE_FALLING;
872+
} else if (type == IRQ_TYPE_EDGE_FALLING) {
873+
if (val)
874+
return;
875+
type = IRQ_TYPE_EDGE_RISING;
876+
}
877+
} while (loop_limit-- > 0);
878+
dev_warn_once(pctrl->dev, "dual-edge irq failed to stabilize\n");
879+
}
880+
835881
static void msm_gpio_irq_ack(struct irq_data *d)
836882
{
837883
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)
840886
unsigned long flags;
841887
u32 val;
842888

843-
if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
889+
if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) {
890+
if (test_bit(d->hwirq, pctrl->dual_edge_irqs))
891+
msm_gpio_update_dual_edge_parent(d);
844892
return;
893+
}
845894

846895
g = &pctrl->soc->groups[d->hwirq];
847896

@@ -860,6 +909,17 @@ static void msm_gpio_irq_ack(struct irq_data *d)
860909
raw_spin_unlock_irqrestore(&pctrl->lock, flags);
861910
}
862911

912+
static bool msm_gpio_needs_dual_edge_parent_workaround(struct irq_data *d,
913+
unsigned int type)
914+
{
915+
struct gpio_chip *gc = irq_data_get_irq_chip_data(d);
916+
struct msm_pinctrl *pctrl = gpiochip_get_data(gc);
917+
918+
return type == IRQ_TYPE_EDGE_BOTH &&
919+
pctrl->soc->wakeirq_dual_edge_errata && d->parent_data &&
920+
test_bit(d->hwirq, pctrl->skip_wake_irqs);
921+
}
922+
863923
static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type)
864924
{
865925
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)
868928
unsigned long flags;
869929
u32 val;
870930

931+
if (msm_gpio_needs_dual_edge_parent_workaround(d, type)) {
932+
set_bit(d->hwirq, pctrl->dual_edge_irqs);
933+
irq_set_handler_locked(d, handle_fasteoi_ack_irq);
934+
msm_gpio_update_dual_edge_parent(d);
935+
return 0;
936+
}
937+
871938
if (d->parent_data)
872939
irq_chip_set_type_parent(d, type);
873940

874-
if (test_bit(d->hwirq, pctrl->skip_wake_irqs))
941+
if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) {
942+
clear_bit(d->hwirq, pctrl->dual_edge_irqs);
943+
irq_set_handler_locked(d, handle_fasteoi_irq);
875944
return 0;
945+
}
876946

877947
g = &pctrl->soc->groups[d->hwirq];
878948

drivers/pinctrl/qcom/pinctrl-msm.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ struct msm_gpio_wakeirq_map {
113113
* @pull_no_keeper: The SoC does not support keeper bias.
114114
* @wakeirq_map: The map of wakeup capable GPIOs and the pin at PDC/MPM
115115
* @nwakeirq_map: The number of entries in @wakeirq_map
116+
* @wakeirq_dual_edge_errata: If true then GPIOs using the wakeirq_map need
117+
* to be aware that their parent can't handle dual
118+
* edge interrupts.
116119
*/
117120
struct msm_pinctrl_soc_data {
118121
const struct pinctrl_pin_desc *pins;
@@ -128,6 +131,7 @@ struct msm_pinctrl_soc_data {
128131
const int *reserved_gpios;
129132
const struct msm_gpio_wakeirq_map *wakeirq_map;
130133
unsigned int nwakeirq_map;
134+
bool wakeirq_dual_edge_errata;
131135
};
132136

133137
extern const struct dev_pm_ops msm_pinctrl_dev_pm_ops;

drivers/pinctrl/qcom/pinctrl-sc7180.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,6 +1147,7 @@ static const struct msm_pinctrl_soc_data sc7180_pinctrl = {
11471147
.ntiles = ARRAY_SIZE(sc7180_tiles),
11481148
.wakeirq_map = sc7180_pdc_map,
11491149
.nwakeirq_map = ARRAY_SIZE(sc7180_pdc_map),
1150+
.wakeirq_dual_edge_errata = true,
11501151
};
11511152

11521153
static int sc7180_pinctrl_probe(struct platform_device *pdev)

0 commit comments

Comments
 (0)