Skip to content

Commit c3c0c2e

Browse files
dianderslinusw
authored andcommitted
pinctrl: qcom: Handle broken/missing PDC dual edge IRQs on sc7180
Depending on how you look at it, you can either say that: a) There is a PDC hardware issue (with the specific IP rev that exists on sc7180) that causes the PDC not to work properly when configured to handle dual edges. b) The dual edge feature of the PDC hardware was only added in later HW revisions and thus isn't in all hardware. Regardless of how you look at it, let's work around the lack of dual edge support by only ever letting our parent see requests for single edge interrupts on affected hardware. NOTE: it's possible that a driver requesting a dual edge interrupt might get several edges coalesced into a single IRQ. For instance if a line starts low and then goes high and low again, the driver that requested the IRQ is not guaranteed to be called twice. However, it is guaranteed that once the driver's interrupt handler starts running its first instruction that any new edges coming in will cause the interrupt to fire again. This is relatively commonplace for dual-edge gpio interrupts (many gpio controllers require software to emulate dual edge with single edge) so client drivers should be setup to handle it. Fixes: e35a6ae ("pinctrl/msm: Setup GPIO chip in hierarchy") Signed-off-by: Douglas Anderson <[email protected]> Reviewed-by: Marc Zyngier <[email protected]> Link: https://lore.kernel.org/r/20200714080254.v3.1.Ie0d730120b232a86a4eac1e2909bcbec844d1766@changeid Signed-off-by: Linus Walleij <[email protected]>
1 parent b3a9e3b commit c3c0c2e

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)