aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDerek Basehore <dbasehore@chromium.org>2018-03-01 00:48:18 -0500
committerMarc Zyngier <marc.zyngier@arm.com>2018-03-14 07:11:29 -0400
commitdba0bc7b76dcf80f82f5a7542605d4abc52808f2 (patch)
tree947b98b992ecb190e62b4b0c06bb1fbfd4ea6572
parentf736d65df0acefcb50f7f7c6ad6070e7b954c79a (diff)
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>
-rw-r--r--drivers/irqchip/irq-gic-v3-its.c107
-rw-r--r--include/linux/irqchip/arm-gic-v3.h3
2 files changed, 109 insertions, 1 deletions
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c
index 1d3056f53747..06682c33acba 100644
--- a/drivers/irqchip/irq-gic-v3-its.c
+++ b/drivers/irqchip/irq-gic-v3-its.c
@@ -33,6 +33,7 @@
33#include <linux/of_platform.h> 33#include <linux/of_platform.h>
34#include <linux/percpu.h> 34#include <linux/percpu.h>
35#include <linux/slab.h> 35#include <linux/slab.h>
36#include <linux/syscore_ops.h>
36 37
37#include <linux/irqchip.h> 38#include <linux/irqchip.h>
38#include <linux/irqchip/arm-gic-v3.h> 39#include <linux/irqchip/arm-gic-v3.h>
@@ -46,6 +47,7 @@
46#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0) 47#define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0)
47#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1) 48#define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1)
48#define ITS_FLAGS_WORKAROUND_CAVIUM_23144 (1ULL << 2) 49#define ITS_FLAGS_WORKAROUND_CAVIUM_23144 (1ULL << 2)
50#define ITS_FLAGS_SAVE_SUSPEND_STATE (1ULL << 3)
49 51
50#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0) 52#define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0)
51 53
@@ -101,6 +103,8 @@ struct its_node {
101 struct its_collection *collections; 103 struct its_collection *collections;
102 struct fwnode_handle *fwnode_handle; 104 struct fwnode_handle *fwnode_handle;
103 u64 (*get_msi_base)(struct its_device *its_dev); 105 u64 (*get_msi_base)(struct its_device *its_dev);
106 u64 cbaser_save;
107 u32 ctlr_save;
104 struct list_head its_device_list; 108 struct list_head its_device_list;
105 u64 flags; 109 u64 flags;
106 unsigned long list_nr; 110 unsigned long list_nr;
@@ -3042,6 +3046,104 @@ static void its_enable_quirks(struct its_node *its)
3042 gic_enable_quirks(iidr, its_quirks, its); 3046 gic_enable_quirks(iidr, its_quirks, its);
3043} 3047}
3044 3048
3049static 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
3074err:
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
3091static 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
3142static struct syscore_ops its_syscore_ops = {
3143 .suspend = its_save_disable,
3144 .resume = its_restore_enable,
3145};
3146
3045static int its_init_domain(struct fwnode_handle *handle, struct its_node *its) 3147static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
3046{ 3148{
3047 struct irq_domain *inner_domain; 3149 struct irq_domain *inner_domain;
@@ -3261,6 +3363,9 @@ static int __init its_probe_one(struct resource *res,
3261 ctlr |= GITS_CTLR_ImDe; 3363 ctlr |= GITS_CTLR_ImDe;
3262 writel_relaxed(ctlr, its->base + GITS_CTLR); 3364 writel_relaxed(ctlr, its->base + GITS_CTLR);
3263 3365
3366 if (GITS_TYPER_HCC(typer))
3367 its->flags |= ITS_FLAGS_SAVE_SUSPEND_STATE;
3368
3264 err = its_init_domain(handle, its); 3369 err = its_init_domain(handle, its);
3265 if (err) 3370 if (err)
3266 goto out_free_tables; 3371 goto out_free_tables;
@@ -3517,5 +3622,7 @@ int __init its_init(struct fwnode_handle *handle, struct rdists *rdists,
3517 } 3622 }
3518 } 3623 }
3519 3624
3625 register_syscore_ops(&its_syscore_ops);
3626
3520 return 0; 3627 return 0;
3521} 3628}
diff --git a/include/linux/irqchip/arm-gic-v3.h b/include/linux/irqchip/arm-gic-v3.h
index c00c4c33e432..9aacea2aa938 100644
--- a/include/linux/irqchip/arm-gic-v3.h
+++ b/include/linux/irqchip/arm-gic-v3.h
@@ -312,7 +312,8 @@
312#define GITS_TYPER_DEVBITS_SHIFT 13 312#define GITS_TYPER_DEVBITS_SHIFT 13
313#define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1) 313#define GITS_TYPER_DEVBITS(r) ((((r) >> GITS_TYPER_DEVBITS_SHIFT) & 0x1f) + 1)
314#define GITS_TYPER_PTA (1UL << 19) 314#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)
316#define GITS_TYPER_VMOVP (1ULL << 37) 317#define GITS_TYPER_VMOVP (1ULL << 37)
317 318
318#define GITS_IIDR_REV_SHIFT 12 319#define GITS_IIDR_REV_SHIFT 12