Skip to content

Commit e9a48ea

Browse files
stephan-ghKAGA-KOKO
authored andcommitted
irqchip/qcom-pdc: Workaround hardware register bug on X1E80100
On X1E80100, there is a hardware bug in the register logic of the IRQ_ENABLE_BANK register: While read accesses work on the normal address, all write accesses must be made to a shifted address. Without a workaround for this, the wrong interrupt gets enabled in the PDC and it is impossible to wakeup from deep suspend (CX collapse). This has not caused problems so far, because the deep suspend state was not enabled. A workaround is required now since work is ongoing to fix this. The PDC has multiple "DRV" regions, each one has a size of 0x10000 and provides the same set of registers for a particular client in the system. Linux is one the clients and uses DRV region 2 on X1E. Each "bank" inside the DRV region consists of 32 interrupt pins that can be enabled using the IRQ_ENABLE_BANK register: IRQ_ENABLE_BANK[bank] = base + IRQ_ENABLE_BANK + bank * sizeof(u32) On X1E, this works as intended for read access. However, write access to most banks is shifted by 2: IRQ_ENABLE_BANK_X1E[0] = IRQ_ENABLE_BANK[-2] IRQ_ENABLE_BANK_X1E[1] = IRQ_ENABLE_BANK[-1] IRQ_ENABLE_BANK_X1E[2] = IRQ_ENABLE_BANK[0] = IRQ_ENABLE_BANK[2 - 2] IRQ_ENABLE_BANK_X1E[3] = IRQ_ENABLE_BANK[1] = IRQ_ENABLE_BANK[3 - 2] IRQ_ENABLE_BANK_X1E[4] = IRQ_ENABLE_BANK[2] = IRQ_ENABLE_BANK[4 - 2] IRQ_ENABLE_BANK_X1E[5] = IRQ_ENABLE_BANK[5] (this one works as intended) The negative indexes underflow to banks of the previous DRV/client region: IRQ_ENABLE_BANK_X1E[drv 2][bank 0] = IRQ_ENABLE_BANK[drv 2][bank -2] = IRQ_ENABLE_BANK[drv 1][bank 5-2] = IRQ_ENABLE_BANK[drv 1][bank 3] = IRQ_ENABLE_BANK[drv 1][bank 0 + 3] IRQ_ENABLE_BANK_X1E[drv 2][bank 1] = IRQ_ENABLE_BANK[drv 2][bank -1] = IRQ_ENABLE_BANK[drv 1][bank 5-1] = IRQ_ENABLE_BANK[drv 1][bank 4] = IRQ_ENABLE_BANK[drv 1][bank 1 + 3] Introduce a workaround for the bug by matching the qcom,x1e80100-pdc compatible and apply the offsets as shown above: - Bank 0...1: previous DRV region, bank += 3 - Bank 1...4: our DRV region, bank -= 2 - Bank 5: our DRV region, no fixup required The PDC node in the device tree only describes the DRV region for the Linux client, but the workaround also requires to map parts of the previous DRV region to issue writes there. To maintain compatibility with old device trees, obtain the base address of the preceeding region by applying the -0x10000 offset. Note that this is also more correct from a conceptual point of view: It does not really make use of the other region; it just issues shifted writes that end up in the registers of the Linux associated DRV region 2. Signed-off-by: Stephan Gerhold <stephan.gerhold@linaro.org> Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Tested-by: Johan Hovold <johan+linaro@kernel.org> Link: https://lore.kernel.org/all/20250218-x1e80100-pdc-hw-wa-v2-1-29be4c98e355@linaro.org
1 parent d7e3fd6 commit e9a48ea

File tree

1 file changed

+64
-3
lines changed

1 file changed

+64
-3
lines changed

drivers/irqchip/qcom-pdc.c

Lines changed: 64 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,11 @@
2121
#include <linux/types.h>
2222

2323
#define PDC_MAX_GPIO_IRQS 256
24+
#define PDC_DRV_OFFSET 0x10000
2425

2526
/* Valid only on HW version < 3.2 */
2627
#define IRQ_ENABLE_BANK 0x10
28+
#define IRQ_ENABLE_BANK_MAX (IRQ_ENABLE_BANK + BITS_TO_BYTES(PDC_MAX_GPIO_IRQS))
2729
#define IRQ_i_CFG 0x110
2830

2931
/* Valid only on HW version >= 3.2 */
@@ -46,20 +48,55 @@ struct pdc_pin_region {
4648

4749
static DEFINE_RAW_SPINLOCK(pdc_lock);
4850
static void __iomem *pdc_base;
51+
static void __iomem *pdc_prev_base;
4952
static struct pdc_pin_region *pdc_region;
5053
static int pdc_region_cnt;
5154
static unsigned int pdc_version;
55+
static bool pdc_x1e_quirk;
56+
57+
static void pdc_base_reg_write(void __iomem *base, int reg, u32 i, u32 val)
58+
{
59+
writel_relaxed(val, base + reg + i * sizeof(u32));
60+
}
5261

5362
static void pdc_reg_write(int reg, u32 i, u32 val)
5463
{
55-
writel_relaxed(val, pdc_base + reg + i * sizeof(u32));
64+
pdc_base_reg_write(pdc_base, reg, i, val);
5665
}
5766

5867
static u32 pdc_reg_read(int reg, u32 i)
5968
{
6069
return readl_relaxed(pdc_base + reg + i * sizeof(u32));
6170
}
6271

72+
static void pdc_x1e_irq_enable_write(u32 bank, u32 enable)
73+
{
74+
void __iomem *base;
75+
76+
/* Remap the write access to work around a hardware bug on X1E */
77+
switch (bank) {
78+
case 0 ... 1:
79+
/* Use previous DRV (client) region and shift to bank 3-4 */
80+
base = pdc_prev_base;
81+
bank += 3;
82+
break;
83+
case 2 ... 4:
84+
/* Use our own region and shift to bank 0-2 */
85+
base = pdc_base;
86+
bank -= 2;
87+
break;
88+
case 5:
89+
/* No fixup required for bank 5 */
90+
base = pdc_base;
91+
break;
92+
default:
93+
WARN_ON(1);
94+
return;
95+
}
96+
97+
pdc_base_reg_write(base, IRQ_ENABLE_BANK, bank, enable);
98+
}
99+
63100
static void __pdc_enable_intr(int pin_out, bool on)
64101
{
65102
unsigned long enable;
@@ -72,7 +109,11 @@ static void __pdc_enable_intr(int pin_out, bool on)
72109

73110
enable = pdc_reg_read(IRQ_ENABLE_BANK, index);
74111
__assign_bit(mask, &enable, on);
75-
pdc_reg_write(IRQ_ENABLE_BANK, index, enable);
112+
113+
if (pdc_x1e_quirk)
114+
pdc_x1e_irq_enable_write(index, enable);
115+
else
116+
pdc_reg_write(IRQ_ENABLE_BANK, index, enable);
76117
} else {
77118
enable = pdc_reg_read(IRQ_i_CFG, pin_out);
78119
__assign_bit(IRQ_i_CFG_IRQ_ENABLE, &enable, on);
@@ -324,10 +365,29 @@ static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
324365
if (res_size > resource_size(&res))
325366
pr_warn("%pOF: invalid reg size, please fix DT\n", node);
326367

368+
/*
369+
* PDC has multiple DRV regions, each one provides the same set of
370+
* registers for a particular client in the system. Due to a hardware
371+
* bug on X1E, some writes to the IRQ_ENABLE_BANK register must be
372+
* issued inside the previous region. This region belongs to
373+
* a different client and is not described in the device tree. Map the
374+
* region with the expected offset to preserve support for old DTs.
375+
*/
376+
if (of_device_is_compatible(node, "qcom,x1e80100-pdc")) {
377+
pdc_prev_base = ioremap(res.start - PDC_DRV_OFFSET, IRQ_ENABLE_BANK_MAX);
378+
if (!pdc_prev_base) {
379+
pr_err("%pOF: unable to map previous PDC DRV region\n", node);
380+
return -ENXIO;
381+
}
382+
383+
pdc_x1e_quirk = true;
384+
}
385+
327386
pdc_base = ioremap(res.start, res_size);
328387
if (!pdc_base) {
329388
pr_err("%pOF: unable to map PDC registers\n", node);
330-
return -ENXIO;
389+
ret = -ENXIO;
390+
goto fail;
331391
}
332392

333393
pdc_version = pdc_reg_read(PDC_VERSION_REG, 0);
@@ -363,6 +423,7 @@ static int qcom_pdc_init(struct device_node *node, struct device_node *parent)
363423
fail:
364424
kfree(pdc_region);
365425
iounmap(pdc_base);
426+
iounmap(pdc_prev_base);
366427
return ret;
367428
}
368429

0 commit comments

Comments
 (0)