Skip to content

Commit dba0bc7

Browse files
dbasehoreMarc Zyngier
authored andcommitted
irqchip/gic-v3-its: Add ability to save/restore ITS state
Some platforms power off GIC logic in suspend, so we need to save/restore state. The distributor and redistributor registers need to be handled in firmware code due to access permissions on those registers, but the ITS registers can be restored in the kernel. We limit this to systems where the ITS collections are implemented in HW (as opposed to being backed by memory tables), as they are the only ones that cannot be dealt with by the firmware. Signed-off-by: Derek Basehore <dbasehore@chromium.org> [maz: fixed changelog, dropped DT property, limited to HCC being >0] Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
1 parent f736d65 commit dba0bc7

File tree

2 files changed

+109
-1
lines changed

2 files changed

+109
-1
lines changed

drivers/irqchip/irq-gic-v3-its.c

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <linux/of_platform.h>
3434
#include <linux/percpu.h>
3535
#include <linux/slab.h>
36+
#include <linux/syscore_ops.h>
3637

3738
#include <linux/irqchip.h>
3839
#include <linux/irqchip/arm-gic-v3.h>
@@ -46,6 +47,7 @@
4647
#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0)
4748
#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1)
4849
#define ITS_FLAGS_WORKAROUND_CAVIUM_23144 (1ULL << 2)
50+
#define ITS_FLAGS_SAVE_SUSPEND_STATE (1ULL << 3)
4951

5052
#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0)
5153

@@ -101,6 +103,8 @@ struct its_node {
101103
struct its_collection *collections;
102104
struct fwnode_handle *fwnode_handle;
103105
u64 (*get_msi_base)(struct its_device *its_dev);
106+
u64 cbaser_save;
107+
u32 ctlr_save;
104108
struct list_head its_device_list;
105109
u64 flags;
106110
unsigned long list_nr;
@@ -3042,6 +3046,104 @@ static void its_enable_quirks(struct its_node *its)
30423046
gic_enable_quirks(iidr, its_quirks, its);
30433047
}
30443048

3049+
static int its_save_disable(void)
3050+
{
3051+
struct its_node *its;
3052+
int err = 0;
3053+
3054+
spin_lock(&its_lock);
3055+
list_for_each_entry(its, &its_nodes, entry) {
3056+
void __iomem *base;
3057+
3058+
if (!(its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE))
3059+
continue;
3060+
3061+
base = its->base;
3062+
its->ctlr_save = readl_relaxed(base + GITS_CTLR);
3063+
err = its_force_quiescent(base);
3064+
if (err) {
3065+
pr_err("ITS@%pa: failed to quiesce: %d\n",
3066+
&its->phys_base, err);
3067+
writel_relaxed(its->ctlr_save, base + GITS_CTLR);
3068+
goto err;
3069+
}
3070+
3071+
its->cbaser_save = gits_read_cbaser(base + GITS_CBASER);
3072+
}
3073+
3074+
err:
3075+
if (err) {
3076+
list_for_each_entry_continue_reverse(its, &its_nodes, entry) {
3077+
void __iomem *base;
3078+
3079+
if (!(its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE))
3080+
continue;
3081+
3082+
base = its->base;
3083+
writel_relaxed(its->ctlr_save, base + GITS_CTLR);
3084+
}
3085+
}
3086+
spin_unlock(&its_lock);
3087+
3088+
return err;
3089+
}
3090+
3091+
static void its_restore_enable(void)
3092+
{
3093+
struct its_node *its;
3094+
int ret;
3095+
3096+
spin_lock(&its_lock);
3097+
list_for_each_entry(its, &its_nodes, entry) {
3098+
void __iomem *base;
3099+
int i;
3100+
3101+
if (!(its->flags & ITS_FLAGS_SAVE_SUSPEND_STATE))
3102+
continue;
3103+
3104+
base = its->base;
3105+
3106+
/*
3107+
* Make sure that the ITS is disabled. If it fails to quiesce,
3108+
* don't restore it since writing to CBASER or BASER<n>
3109+
* registers is undefined according to the GIC v3 ITS
3110+
* Specification.
3111+
*/
3112+
ret = its_force_quiescent(base);
3113+
if (ret) {
3114+
pr_err("ITS@%pa: failed to quiesce on resume: %d\n",
3115+
&its->phys_base, ret);
3116+
continue;
3117+
}
3118+
3119+
gits_write_cbaser(its->cbaser_save, base + GITS_CBASER);
3120+
3121+
/*
3122+
* Writing CBASER resets CREADR to 0, so make CWRITER and
3123+
* cmd_write line up with it.
3124+
*/
3125+
its->cmd_write = its->cmd_base;
3126+
gits_write_cwriter(0, base + GITS_CWRITER);
3127+
3128+
/* Restore GITS_BASER from the value cache. */
3129+
for (i = 0; i < GITS_BASER_NR_REGS; i++) {
3130+
struct its_baser *baser = &its->tables[i];
3131+
3132+
if (!(baser->val & GITS_BASER_VALID))
3133+
continue;
3134+
3135+
its_write_baser(its, baser, baser->val);
3136+
}
3137+
writel_relaxed(its->ctlr_save, base + GITS_CTLR);
3138+
}
3139+
spin_unlock(&its_lock);
3140+
}
3141+
3142+
static struct syscore_ops its_syscore_ops = {
3143+
.suspend = its_save_disable,
3144+
.resume = its_restore_enable,
3145+
};
3146+
30453147
static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
30463148
{
30473149
struct irq_domain *inner_domain;
@@ -3261,6 +3363,9 @@ static int __init its_probe_one(struct resource *res,
32613363
ctlr |= GITS_CTLR_ImDe;
32623364
writel_relaxed(ctlr, its->base + GITS_CTLR);
32633365

3366+
if (GITS_TYPER_HCC(typer))
3367+
its->flags |= ITS_FLAGS_SAVE_SUSPEND_STATE;
3368+
32643369
err = its_init_domain(handle, its);
32653370
if (err)
32663371
goto out_free_tables;
@@ -3517,5 +3622,7 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
35173622
}
35183623
}
35193624

3625+
register_syscore_ops(&its_syscore_ops);
3626+
35203627
return 0;
35213628
}

include/linux/irqchip/arm-gic-v3.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,8 @@
312312
#define GITS_TYPER_DEVBITS_SHIFT 13
313313
#define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
314314
#define GITS_TYPER_PTA (1UL << 19)
315-
#define GITS_TYPER_HWCOLLCNT_SHIFT 24
315+
#define GITS_TYPER_HCC_SHIFT 24
316+
#define GITS_TYPER_HCC(r) (((r) >> GITS_TYPER_HCC_SHIFT) & 0xff)
316317
#define GITS_TYPER_VMOVP (1ULL << 37)
317318

318319
#define GITS_IIDR_REV_SHIFT 12

0 commit comments

Comments
 (0)