|
33 | 33 | #include <linux/of_platform.h>
|
34 | 34 | #include <linux/percpu.h>
|
35 | 35 | #include <linux/slab.h>
|
| 36 | +#include <linux/syscore_ops.h> |
36 | 37 |
|
37 | 38 | #include <linux/irqchip.h>
|
38 | 39 | #include <linux/irqchip/arm-gic-v3.h>
|
|
46 | 47 | #define ITS_FLAGS_CMDQ_NEEDS_FLUSHING (1ULL << 0)
|
47 | 48 | #define ITS_FLAGS_WORKAROUND_CAVIUM_22375 (1ULL << 1)
|
48 | 49 | #define ITS_FLAGS_WORKAROUND_CAVIUM_23144 (1ULL << 2)
|
| 50 | +#define ITS_FLAGS_SAVE_SUSPEND_STATE (1ULL << 3) |
49 | 51 |
|
50 | 52 | #define RDIST_FLAGS_PROPBASE_NEEDS_FLUSHING (1 << 0)
|
51 | 53 |
|
@@ -101,6 +103,8 @@ struct its_node {
|
101 | 103 | struct its_collection *collections;
|
102 | 104 | struct fwnode_handle *fwnode_handle;
|
103 | 105 | u64 (*get_msi_base)(struct its_device *its_dev);
|
| 106 | + u64 cbaser_save; |
| 107 | + u32 ctlr_save; |
104 | 108 | struct list_head its_device_list;
|
105 | 109 | u64 flags;
|
106 | 110 | unsigned long list_nr;
|
@@ -3042,6 +3046,104 @@ static void its_enable_quirks(struct its_node *its)
|
3042 | 3046 | gic_enable_quirks(iidr, its_quirks, its);
|
3043 | 3047 | }
|
3044 | 3048 |
|
| 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 | + |
3045 | 3147 | static int its_init_domain(struct fwnode_handle *handle, struct its_node *its)
|
3046 | 3148 | {
|
3047 | 3149 | struct irq_domain *inner_domain;
|
@@ -3261,6 +3363,9 @@ static int __init its_probe_one(struct resource *res,
|
3261 | 3363 | ctlr |= GITS_CTLR_ImDe;
|
3262 | 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 | 3369 | err = its_init_domain(handle, its);
|
3265 | 3370 | if (err)
|
3266 | 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 | 3627 | return 0;
|
3521 | 3628 | }
|
0 commit comments