[PATCH v6 1/4] irqchip: mips-gic: Add context saving for MIPS_REMOTEPROC

2017-03-23 Thread Matt Redfearn
The MIPS remote processor driver allows non-Linux firmware to take
control of and execute on one of the systems VPEs. If that VPE is
brought back under Linux, it is necessary to ensure that all GIC
interrupts are routed and masked as Linux expects them, as the firmware
can have done anything it likes with the GIC configuration (hopefully
just for that VPEs local interrupt sources, but allow for shared
external interrupts as well).

The configuration of shared and local CPU interrupts is maintained and
updated every time a change is made. When a CPU is brought online, the
saved configuration is restored.

These functions will also be useful for restoring GIC context after a
suspend to RAM.

Signed-off-by: Matt Redfearn 
---

Changes in v6:
Rebase on Linux 4.11-rc3

Changes in v5: None
Changes in v4:
Fix inconsistency of Linux CPU number and VP ID

Changes in v3:
Update GIC context saving to use CPU hotplug state machine

Changes in v2:
Add dependence on additional patches to mips-gic in commit log
Incorporate changes from Marc Zynger's review:
- Remove CONTEXT_SAVING define.
- Make saved local state a per-cpu variable
- Make gic_save_* static functions when enabled, and do { } while(0)
  otherwise

 drivers/irqchip/irq-mips-gic.c | 207 +++--
 1 file changed, 200 insertions(+), 7 deletions(-)

diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
index 11d12bccc4e7..15c0feb8a52f 100644
--- a/drivers/irqchip/irq-mips-gic.c
+++ b/drivers/irqchip/irq-mips-gic.c
@@ -8,6 +8,7 @@
  */
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -56,6 +57,79 @@ static unsigned int timer_cpu_pin;
 static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller;
 DECLARE_BITMAP(ipi_resrv, GIC_MAX_INTRS);
 
+#ifdef CONFIG_MIPS_REMOTEPROC
+struct gic_local_state_t {
+   u8 mask;
+};
+
+DEFINE_PER_CPU(struct gic_local_state_t, gic_local_state);
+
+static void gic_save_local_rmask(int cpu, int mask)
+{
+   struct gic_local_state_t *state = per_cpu_ptr(_local_state, cpu);
+
+   state->mask &= mask;
+}
+
+static void gic_save_local_smask(int cpu, int mask)
+{
+   struct gic_local_state_t *state = per_cpu_ptr(_local_state, cpu);
+
+   state->mask |= mask;
+}
+
+static struct {
+   unsigned vpe:   8;
+   unsigned pin:   4;
+
+   unsigned polarity:  1;
+   unsigned trigger:   1;
+   unsigned dual_edge: 1;
+   unsigned mask:  1;
+} gic_shared_state[GIC_MAX_INTRS];
+
+static void gic_save_shared_vpe(int intr, int vpe)
+{
+   gic_shared_state[intr].vpe = vpe;
+}
+
+static void gic_save_shared_pin(int intr, int pin)
+{
+   gic_shared_state[intr].pin = pin;
+}
+
+static void gic_save_shared_polarity(int intr, int polarity)
+{
+   gic_shared_state[intr].polarity = polarity;
+}
+
+static void gic_save_shared_trigger(int intr, int trigger)
+{
+   gic_shared_state[intr].trigger = trigger;
+}
+
+static void gic_save_shared_dual_edge(int intr, int dual_edge)
+{
+   gic_shared_state[intr].dual_edge = dual_edge;
+}
+
+static void gic_save_shared_mask(int intr, int mask)
+{
+   gic_shared_state[intr].mask = mask;
+}
+
+#else
+#define gic_save_local_rmask(cpu, i)   do { } while (0)
+#define gic_save_local_smask(cpu, i)   do { } while (0)
+
+#define gic_save_shared_vpe(i, v)  do { } while (0)
+#define gic_save_shared_pin(i, p)  do { } while (0)
+#define gic_save_shared_polarity(i, p) do { } while (0)
+#define gic_save_shared_trigger(i, t)  do { } while (0)
+#define gic_save_shared_dual_edge(i, d)do { } while (0)
+#define gic_save_shared_mask(i, m) do { } while (0)
+#endif /* CONFIG_MIPS_REMOTEPROC */
+
 static void __gic_irq_dispatch(void);
 
 static inline u32 gic_read32(unsigned int reg)
@@ -105,52 +179,94 @@ static inline void gic_update_bits(unsigned int reg, 
unsigned long mask,
gic_write(reg, regval);
 }
 
-static inline void gic_reset_mask(unsigned int intr)
+static inline void gic_write_reset_mask(unsigned int intr)
 {
gic_write(GIC_REG(SHARED, GIC_SH_RMASK) + GIC_INTR_OFS(intr),
  1ul << GIC_INTR_BIT(intr));
 }
 
-static inline void gic_set_mask(unsigned int intr)
+static inline void gic_reset_mask(unsigned int intr)
+{
+   gic_save_shared_mask(intr, 0);
+   gic_write_reset_mask(intr);
+}
+
+static inline void gic_write_set_mask(unsigned int intr)
 {
gic_write(GIC_REG(SHARED, GIC_SH_SMASK) + GIC_INTR_OFS(intr),
  1ul << GIC_INTR_BIT(intr));
 }
 
-static inline void gic_set_polarity(unsigned int intr, unsigned int pol)
+static inline void gic_set_mask(unsigned int intr)
+{
+   gic_save_shared_mask(intr, 1);
+   gic_write_set_mask(intr);
+}
+
+static inline void gic_write_polarity(unsigned int intr, unsigned int pol)
 {
gic_update_bits(GIC_REG(SHARED, GIC_SH_SET_POLARITY) +
GIC_INTR_OFS(intr), 1ul << 

[PATCH v6 1/4] irqchip: mips-gic: Add context saving for MIPS_REMOTEPROC

2017-03-23 Thread Matt Redfearn
The MIPS remote processor driver allows non-Linux firmware to take
control of and execute on one of the systems VPEs. If that VPE is
brought back under Linux, it is necessary to ensure that all GIC
interrupts are routed and masked as Linux expects them, as the firmware
can have done anything it likes with the GIC configuration (hopefully
just for that VPEs local interrupt sources, but allow for shared
external interrupts as well).

The configuration of shared and local CPU interrupts is maintained and
updated every time a change is made. When a CPU is brought online, the
saved configuration is restored.

These functions will also be useful for restoring GIC context after a
suspend to RAM.

Signed-off-by: Matt Redfearn 
---

Changes in v6:
Rebase on Linux 4.11-rc3

Changes in v5: None
Changes in v4:
Fix inconsistency of Linux CPU number and VP ID

Changes in v3:
Update GIC context saving to use CPU hotplug state machine

Changes in v2:
Add dependence on additional patches to mips-gic in commit log
Incorporate changes from Marc Zynger's review:
- Remove CONTEXT_SAVING define.
- Make saved local state a per-cpu variable
- Make gic_save_* static functions when enabled, and do { } while(0)
  otherwise

 drivers/irqchip/irq-mips-gic.c | 207 +++--
 1 file changed, 200 insertions(+), 7 deletions(-)

diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
index 11d12bccc4e7..15c0feb8a52f 100644
--- a/drivers/irqchip/irq-mips-gic.c
+++ b/drivers/irqchip/irq-mips-gic.c
@@ -8,6 +8,7 @@
  */
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -56,6 +57,79 @@ static unsigned int timer_cpu_pin;
 static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller;
 DECLARE_BITMAP(ipi_resrv, GIC_MAX_INTRS);
 
+#ifdef CONFIG_MIPS_REMOTEPROC
+struct gic_local_state_t {
+   u8 mask;
+};
+
+DEFINE_PER_CPU(struct gic_local_state_t, gic_local_state);
+
+static void gic_save_local_rmask(int cpu, int mask)
+{
+   struct gic_local_state_t *state = per_cpu_ptr(_local_state, cpu);
+
+   state->mask &= mask;
+}
+
+static void gic_save_local_smask(int cpu, int mask)
+{
+   struct gic_local_state_t *state = per_cpu_ptr(_local_state, cpu);
+
+   state->mask |= mask;
+}
+
+static struct {
+   unsigned vpe:   8;
+   unsigned pin:   4;
+
+   unsigned polarity:  1;
+   unsigned trigger:   1;
+   unsigned dual_edge: 1;
+   unsigned mask:  1;
+} gic_shared_state[GIC_MAX_INTRS];
+
+static void gic_save_shared_vpe(int intr, int vpe)
+{
+   gic_shared_state[intr].vpe = vpe;
+}
+
+static void gic_save_shared_pin(int intr, int pin)
+{
+   gic_shared_state[intr].pin = pin;
+}
+
+static void gic_save_shared_polarity(int intr, int polarity)
+{
+   gic_shared_state[intr].polarity = polarity;
+}
+
+static void gic_save_shared_trigger(int intr, int trigger)
+{
+   gic_shared_state[intr].trigger = trigger;
+}
+
+static void gic_save_shared_dual_edge(int intr, int dual_edge)
+{
+   gic_shared_state[intr].dual_edge = dual_edge;
+}
+
+static void gic_save_shared_mask(int intr, int mask)
+{
+   gic_shared_state[intr].mask = mask;
+}
+
+#else
+#define gic_save_local_rmask(cpu, i)   do { } while (0)
+#define gic_save_local_smask(cpu, i)   do { } while (0)
+
+#define gic_save_shared_vpe(i, v)  do { } while (0)
+#define gic_save_shared_pin(i, p)  do { } while (0)
+#define gic_save_shared_polarity(i, p) do { } while (0)
+#define gic_save_shared_trigger(i, t)  do { } while (0)
+#define gic_save_shared_dual_edge(i, d)do { } while (0)
+#define gic_save_shared_mask(i, m) do { } while (0)
+#endif /* CONFIG_MIPS_REMOTEPROC */
+
 static void __gic_irq_dispatch(void);
 
 static inline u32 gic_read32(unsigned int reg)
@@ -105,52 +179,94 @@ static inline void gic_update_bits(unsigned int reg, 
unsigned long mask,
gic_write(reg, regval);
 }
 
-static inline void gic_reset_mask(unsigned int intr)
+static inline void gic_write_reset_mask(unsigned int intr)
 {
gic_write(GIC_REG(SHARED, GIC_SH_RMASK) + GIC_INTR_OFS(intr),
  1ul << GIC_INTR_BIT(intr));
 }
 
-static inline void gic_set_mask(unsigned int intr)
+static inline void gic_reset_mask(unsigned int intr)
+{
+   gic_save_shared_mask(intr, 0);
+   gic_write_reset_mask(intr);
+}
+
+static inline void gic_write_set_mask(unsigned int intr)
 {
gic_write(GIC_REG(SHARED, GIC_SH_SMASK) + GIC_INTR_OFS(intr),
  1ul << GIC_INTR_BIT(intr));
 }
 
-static inline void gic_set_polarity(unsigned int intr, unsigned int pol)
+static inline void gic_set_mask(unsigned int intr)
+{
+   gic_save_shared_mask(intr, 1);
+   gic_write_set_mask(intr);
+}
+
+static inline void gic_write_polarity(unsigned int intr, unsigned int pol)
 {
gic_update_bits(GIC_REG(SHARED, GIC_SH_SET_POLARITY) +
GIC_INTR_OFS(intr), 1ul << GIC_INTR_BIT(intr),