Fix miscellaneous irqchip bugs.
Signed-off-by: Ingo Molnar <mingo@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAme51TARHG1pbmdvQGtl cm5lbC5vcmcACgkQEnMQ0APhK1i9gg/+O/yXZ8k/2hl4Kc7NvlhszHDNRLht6Sd9 ZdRtn6N6yY5JkQhGEzXv/gFVRJWtxHwOF7cHJysgqRzsv4YFufqSMZN3CaMnbKOd ZgK3Kmt0YunamEvdKDrJJ6oPN0Ul7f4ipcD9qfuh2TM2YrLzBwTKuMR5ZEASEmgn yF3FqDHTyqdhUi/ti5HKRcF4ElWh0ATALeHtjDmVWQq4pv7bsOOu5F438ZIomVPF r+2R9M1nPghozZ00wHcsn86TyEWcF63TVKLoDrTG+mC3xyFOwoP+E/P20Kad6V3f 14IhQyVteClUy7BwD5mSPZ86s52t1RQngaQF8ofDiEipUFP5/AsLsTAtzy/rOaPg PheHVdYNNuxxRW63pJHpAGr+sWYGT4eYAWBodF6VxWmDNtLeKDf2MQyNW6Ip3QqF l9FADyyCl4ItyU65py1W0oAbdhSzN7qIco30flWlIv6t6QLPTORmob6ogY5r2Npj fn0/lHBkR2/cDlK0EV3JoFgy6AI58t+ThvvEXBfj7a7pxDve7Li5X2U6Hw7lSbn5 3r6URYXn4jUERV9+vMPGBvrXpeE+itDzT3NDl1Qm7uHq22k9o8TaLEgUD7tq/K0V a2HKxsDaqi0esdVM2bndVKBRsjgSOkt+jhmppmQd4PJ4GfjZxT9egdgn6GVs6WtI VOPejBKFBpM= =Kfhc -----END PGP SIGNATURE----- Merge tag 'irq-urgent-2025-02-22' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull irq fixes from Ingo Molnar: "Fix miscellaneous irqchip bugs" * tag 'irq-urgent-2025-02-22' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: irqchip/qcom-pdc: Workaround hardware register bug on X1E80100 irqchip/jcore-aic, clocksource/drivers/jcore: Fix jcore-pit interrupt request irqchip/gic-v3: Fix rk3399 workaround when secure interrupts are enabled
This commit is contained in:
commit
f112eea3cc
4 changed files with 117 additions and 16 deletions
|
@ -114,6 +114,18 @@ static int jcore_pit_local_init(unsigned cpu)
|
|||
pit->periodic_delta = DIV_ROUND_CLOSEST(NSEC_PER_SEC, HZ * buspd);
|
||||
|
||||
clockevents_config_and_register(&pit->ced, freq, 1, ULONG_MAX);
|
||||
enable_percpu_irq(pit->ced.irq, IRQ_TYPE_NONE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int jcore_pit_local_teardown(unsigned cpu)
|
||||
{
|
||||
struct jcore_pit *pit = this_cpu_ptr(jcore_pit_percpu);
|
||||
|
||||
pr_info("Local J-Core PIT teardown on cpu %u\n", cpu);
|
||||
|
||||
disable_percpu_irq(pit->ced.irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -168,6 +180,7 @@ static int __init jcore_pit_init(struct device_node *node)
|
|||
return -ENOMEM;
|
||||
}
|
||||
|
||||
irq_set_percpu_devid(pit_irq);
|
||||
err = request_percpu_irq(pit_irq, jcore_timer_interrupt,
|
||||
"jcore_pit", jcore_pit_percpu);
|
||||
if (err) {
|
||||
|
@ -237,7 +250,7 @@ static int __init jcore_pit_init(struct device_node *node)
|
|||
|
||||
cpuhp_setup_state(CPUHP_AP_JCORE_TIMER_STARTING,
|
||||
"clockevents/jcore:starting",
|
||||
jcore_pit_local_init, NULL);
|
||||
jcore_pit_local_init, jcore_pit_local_teardown);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@ static u8 dist_prio_nmi __ro_after_init = GICV3_PRIO_NMI;
|
|||
#define FLAGS_WORKAROUND_GICR_WAKER_MSM8996 (1ULL << 0)
|
||||
#define FLAGS_WORKAROUND_CAVIUM_ERRATUM_38539 (1ULL << 1)
|
||||
#define FLAGS_WORKAROUND_ASR_ERRATUM_8601001 (1ULL << 2)
|
||||
#define FLAGS_WORKAROUND_INSECURE (1ULL << 3)
|
||||
|
||||
#define GIC_IRQ_TYPE_PARTITION (GIC_IRQ_TYPE_LPI + 1)
|
||||
|
||||
|
@ -83,6 +84,8 @@ static DEFINE_STATIC_KEY_TRUE(supports_deactivate_key);
|
|||
#define GIC_LINE_NR min(GICD_TYPER_SPIS(gic_data.rdists.gicd_typer), 1020U)
|
||||
#define GIC_ESPI_NR GICD_TYPER_ESPIS(gic_data.rdists.gicd_typer)
|
||||
|
||||
static bool nmi_support_forbidden;
|
||||
|
||||
/*
|
||||
* There are 16 SGIs, though we only actually use 8 in Linux. The other 8 SGIs
|
||||
* are potentially stolen by the secure side. Some code, especially code dealing
|
||||
|
@ -163,21 +166,27 @@ static void __init gic_prio_init(void)
|
|||
{
|
||||
bool ds;
|
||||
|
||||
cpus_have_group0 = gic_has_group0();
|
||||
|
||||
ds = gic_dist_security_disabled();
|
||||
if (!ds) {
|
||||
u32 val;
|
||||
if ((gic_data.flags & FLAGS_WORKAROUND_INSECURE) && !ds) {
|
||||
if (cpus_have_group0) {
|
||||
u32 val;
|
||||
|
||||
val = readl_relaxed(gic_data.dist_base + GICD_CTLR);
|
||||
val |= GICD_CTLR_DS;
|
||||
writel_relaxed(val, gic_data.dist_base + GICD_CTLR);
|
||||
val = readl_relaxed(gic_data.dist_base + GICD_CTLR);
|
||||
val |= GICD_CTLR_DS;
|
||||
writel_relaxed(val, gic_data.dist_base + GICD_CTLR);
|
||||
|
||||
ds = gic_dist_security_disabled();
|
||||
if (ds)
|
||||
pr_warn("Broken GIC integration, security disabled");
|
||||
ds = gic_dist_security_disabled();
|
||||
if (ds)
|
||||
pr_warn("Broken GIC integration, security disabled\n");
|
||||
} else {
|
||||
pr_warn("Broken GIC integration, pNMI forbidden\n");
|
||||
nmi_support_forbidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
cpus_have_security_disabled = ds;
|
||||
cpus_have_group0 = gic_has_group0();
|
||||
|
||||
/*
|
||||
* How priority values are used by the GIC depends on two things:
|
||||
|
@ -209,7 +218,7 @@ static void __init gic_prio_init(void)
|
|||
* be in the non-secure range, we program the non-secure values into
|
||||
* the distributor to match the PMR values we want.
|
||||
*/
|
||||
if (cpus_have_group0 & !cpus_have_security_disabled) {
|
||||
if (cpus_have_group0 && !cpus_have_security_disabled) {
|
||||
dist_prio_irq = __gicv3_prio_to_ns(dist_prio_irq);
|
||||
dist_prio_nmi = __gicv3_prio_to_ns(dist_prio_nmi);
|
||||
}
|
||||
|
@ -1922,6 +1931,18 @@ static bool gic_enable_quirk_arm64_2941627(void *data)
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool gic_enable_quirk_rk3399(void *data)
|
||||
{
|
||||
struct gic_chip_data *d = data;
|
||||
|
||||
if (of_machine_is_compatible("rockchip,rk3399")) {
|
||||
d->flags |= FLAGS_WORKAROUND_INSECURE;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool rd_set_non_coherent(void *data)
|
||||
{
|
||||
struct gic_chip_data *d = data;
|
||||
|
@ -1996,6 +2017,12 @@ static const struct gic_quirk gic_quirks[] = {
|
|||
.property = "dma-noncoherent",
|
||||
.init = rd_set_non_coherent,
|
||||
},
|
||||
{
|
||||
.desc = "GICv3: Insecure RK3399 integration",
|
||||
.iidr = 0x0000043b,
|
||||
.mask = 0xff000fff,
|
||||
.init = gic_enable_quirk_rk3399,
|
||||
},
|
||||
{
|
||||
}
|
||||
};
|
||||
|
@ -2004,7 +2031,7 @@ static void gic_enable_nmi_support(void)
|
|||
{
|
||||
int i;
|
||||
|
||||
if (!gic_prio_masking_enabled())
|
||||
if (!gic_prio_masking_enabled() || nmi_support_forbidden)
|
||||
return;
|
||||
|
||||
rdist_nmi_refs = kcalloc(gic_data.ppi_nr + SGI_NR,
|
||||
|
|
|
@ -38,7 +38,7 @@ static struct irq_chip jcore_aic;
|
|||
static void handle_jcore_irq(struct irq_desc *desc)
|
||||
{
|
||||
if (irqd_is_per_cpu(irq_desc_get_irq_data(desc)))
|
||||
handle_percpu_irq(desc);
|
||||
handle_percpu_devid_irq(desc);
|
||||
else
|
||||
handle_simple_irq(desc);
|
||||
}
|
||||
|
|
|
@ -21,9 +21,11 @@
|
|||
#include <linux/types.h>
|
||||
|
||||
#define PDC_MAX_GPIO_IRQS 256
|
||||
#define PDC_DRV_OFFSET 0x10000
|
||||
|
||||
/* Valid only on HW version < 3.2 */
|
||||
#define IRQ_ENABLE_BANK 0x10
|
||||
#define IRQ_ENABLE_BANK_MAX (IRQ_ENABLE_BANK + BITS_TO_BYTES(PDC_MAX_GPIO_IRQS))
|
||||
#define IRQ_i_CFG 0x110
|
||||
|
||||
/* Valid only on HW version >= 3.2 */
|
||||
|
@ -46,13 +48,20 @@ struct pdc_pin_region {
|
|||
|
||||
static DEFINE_RAW_SPINLOCK(pdc_lock);
|
||||
static void __iomem *pdc_base;
|
||||
static void __iomem *pdc_prev_base;
|
||||
static struct pdc_pin_region *pdc_region;
|
||||
static int pdc_region_cnt;
|
||||
static unsigned int pdc_version;
|
||||
static bool pdc_x1e_quirk;
|
||||
|
||||
static void pdc_base_reg_write(void __iomem *base, int reg, u32 i, u32 val)
|
||||
{
|
||||
writel_relaxed(val, base + reg + i * sizeof(u32));
|
||||
}
|
||||
|
||||
static void pdc_reg_write(int reg, u32 i, u32 val)
|
||||
{
|
||||
writel_relaxed(val, pdc_base + reg + i * sizeof(u32));
|
||||
pdc_base_reg_write(pdc_base, reg, i, val);
|
||||
}
|
||||
|
||||
static u32 pdc_reg_read(int reg, u32 i)
|
||||
|
@ -60,6 +69,34 @@ static u32 pdc_reg_read(int reg, u32 i)
|
|||
return readl_relaxed(pdc_base + reg + i * sizeof(u32));
|
||||
}
|
||||
|
||||
static void pdc_x1e_irq_enable_write(u32 bank, u32 enable)
|
||||
{
|
||||
void __iomem *base;
|
||||
|
||||
/* Remap the write access to work around a hardware bug on X1E */
|
||||
switch (bank) {
|
||||
case 0 ... 1:
|
||||
/* Use previous DRV (client) region and shift to bank 3-4 */
|
||||
base = pdc_prev_base;
|
||||
bank += 3;
|
||||
break;
|
||||
case 2 ... 4:
|
||||
/* Use our own region and shift to bank 0-2 */
|
||||
base = pdc_base;
|
||||
bank -= 2;
|
||||
break;
|
||||
case 5:
|
||||
/* No fixup required for bank 5 */
|
||||
base = pdc_base;
|
||||
break;
|
||||
default:
|
||||
WARN_ON(1);
|
||||
return;
|
||||
}
|
||||
|
||||
pdc_base_reg_write(base, IRQ_ENABLE_BANK, bank, enable);
|
||||
}
|
||||
|
||||
static void __pdc_enable_intr(int pin_out, bool on)
|
||||
{
|
||||
unsigned long enable;
|
||||
|
@ -72,7 +109,11 @@ static void __pdc_enable_intr(int pin_out, bool on)
|
|||
|
||||
enable = pdc_reg_read(IRQ_ENABLE_BANK, index);
|
||||
__assign_bit(mask, &enable, on);
|
||||
pdc_reg_write(IRQ_ENABLE_BANK, index, enable);
|
||||
|
||||
if (pdc_x1e_quirk)
|
||||
pdc_x1e_irq_enable_write(index, enable);
|
||||
else
|
||||
pdc_reg_write(IRQ_ENABLE_BANK, index, enable);
|
||||
} else {
|
||||
enable = pdc_reg_read(IRQ_i_CFG, pin_out);
|
||||
__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)
|
|||
if (res_size > resource_size(&res))
|
||||
pr_warn("%pOF: invalid reg size, please fix DT\n", node);
|
||||
|
||||
/*
|
||||
* PDC has multiple DRV regions, each one provides the same set of
|
||||
* registers for a particular client in the system. Due to a hardware
|
||||
* bug on X1E, some writes to the IRQ_ENABLE_BANK register must be
|
||||
* issued inside the previous region. This region belongs to
|
||||
* a different client and is not described in the device tree. Map the
|
||||
* region with the expected offset to preserve support for old DTs.
|
||||
*/
|
||||
if (of_device_is_compatible(node, "qcom,x1e80100-pdc")) {
|
||||
pdc_prev_base = ioremap(res.start - PDC_DRV_OFFSET, IRQ_ENABLE_BANK_MAX);
|
||||
if (!pdc_prev_base) {
|
||||
pr_err("%pOF: unable to map previous PDC DRV region\n", node);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
pdc_x1e_quirk = true;
|
||||
}
|
||||
|
||||
pdc_base = ioremap(res.start, res_size);
|
||||
if (!pdc_base) {
|
||||
pr_err("%pOF: unable to map PDC registers\n", node);
|
||||
return -ENXIO;
|
||||
ret = -ENXIO;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
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)
|
|||
fail:
|
||||
kfree(pdc_region);
|
||||
iounmap(pdc_base);
|
||||
iounmap(pdc_prev_base);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue