lkml.org 
[lkml]   [2016]   [Sep]   [2]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    /
    SubjectRe: [PATCH 1/6] irqchip: mips-gic: Add context saving for MIPS_REMOTEPROC
    From
    Date


    On 02/09/16 10:59, Matt Redfearn wrote:
    > 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 <matt.redfearn@imgtec.com>
    > ---
    >
    > drivers/irqchip/irq-mips-gic.c | 185 +++++++++++++++++++++++++++++++++++++++--
    > 1 file changed, 178 insertions(+), 7 deletions(-)
    >
    > diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c
    > index 83f498393a7f..5ba1fe1468ce 100644
    > --- a/drivers/irqchip/irq-mips-gic.c
    > +++ b/drivers/irqchip/irq-mips-gic.c
    > @@ -8,6 +8,7 @@
    > */
    > #include <linux/bitmap.h>
    > #include <linux/clocksource.h>
    > +#include <linux/cpu.h>
    > #include <linux/init.h>
    > #include <linux/interrupt.h>
    > #include <linux/irq.h>
    > @@ -56,6 +57,47 @@ 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);
    >
    > +#if defined(CONFIG_MIPS_RPROC)

    Spot the mistake - this should, of course, be CONFIG_MIPS_REMOTEPROC.
    I'll fix that in v2.

    Matt

    > +#define CONTEXT_SAVING
    > +#endif
    > +
    > +#ifdef CONTEXT_SAVING
    > +static struct {
    > + unsigned mask: GIC_NUM_LOCAL_INTRS;
    > +} gic_local_state[NR_CPUS];
    > +
    > +#define gic_save_local_rmask(vpe, i) (gic_local_state[vpe].mask &= i)
    > +#define gic_save_local_smask(vpe, i) (gic_local_state[vpe].mask |= i)
    > +
    > +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];
    > +
    > +#define gic_save_shared_vpe(i, v) gic_shared_state[i].vpe = v
    > +#define gic_save_shared_pin(i, p) gic_shared_state[i].pin = p
    > +#define gic_save_shared_polarity(i, p) gic_shared_state[i].polarity = p
    > +#define gic_save_shared_trigger(i, t) gic_shared_state[i].trigger = t
    > +#define gic_save_shared_dual_edge(i, d) gic_shared_state[i].dual_edge = d
    > +#define gic_save_shared_mask(i, m) gic_shared_state[i].mask = m
    > +
    > +#else
    > +#define gic_save_local_rmask(vpe, i)
    > +#define gic_save_local_smask(vpe, i)
    > +
    > +#define gic_save_shared_vpe(i, v)
    > +#define gic_save_shared_pin(i, p)
    > +#define gic_save_shared_polarity(i, p)
    > +#define gic_save_shared_trigger(i, t)
    > +#define gic_save_shared_dual_edge(i, d)
    > +#define gic_save_shared_mask(i, m)
    > +#endif /* CONTEXT_SAVING */
    > +
    > static void __gic_irq_dispatch(void);
    >
    > static inline u32 gic_read32(unsigned int reg)
    > @@ -105,52 +147,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),
    > (unsigned long)pol << GIC_INTR_BIT(intr));
    > }
    >
    > -static inline void gic_set_trigger(unsigned int intr, unsigned int trig)
    > +static inline void gic_set_polarity(unsigned int intr, unsigned int pol)
    > +{
    > + gic_save_shared_polarity(intr, pol);
    > + gic_write_polarity(intr, pol);
    > +}
    > +
    > +static inline void gic_write_trigger(unsigned int intr, unsigned int trig)
    > {
    > gic_update_bits(GIC_REG(SHARED, GIC_SH_SET_TRIGGER) +
    > GIC_INTR_OFS(intr), 1ul << GIC_INTR_BIT(intr),
    > (unsigned long)trig << GIC_INTR_BIT(intr));
    > }
    >
    > -static inline void gic_set_dual_edge(unsigned int intr, unsigned int dual)
    > +static inline void gic_set_trigger(unsigned int intr, unsigned int trig)
    > +{
    > + gic_save_shared_trigger(intr, trig);
    > + gic_write_trigger(intr, trig);
    > +}
    > +
    > +static inline void gic_write_dual_edge(unsigned int intr, unsigned int dual)
    > {
    > gic_update_bits(GIC_REG(SHARED, GIC_SH_SET_DUAL) + GIC_INTR_OFS(intr),
    > 1ul << GIC_INTR_BIT(intr),
    > (unsigned long)dual << GIC_INTR_BIT(intr));
    > }
    >
    > -static inline void gic_map_to_pin(unsigned int intr, unsigned int pin)
    > +static inline void gic_set_dual_edge(unsigned int intr, unsigned int dual)
    > +{
    > + gic_save_shared_dual_edge(intr, dual);
    > + gic_write_dual_edge(intr, dual);
    > +}
    > +
    > +static inline void gic_write_map_to_pin(unsigned int intr, unsigned int pin)
    > {
    > gic_write32(GIC_REG(SHARED, GIC_SH_INTR_MAP_TO_PIN_BASE) +
    > GIC_SH_MAP_TO_PIN(intr), GIC_MAP_TO_PIN_MSK | pin);
    > }
    >
    > -static inline void gic_map_to_vpe(unsigned int intr, unsigned int vpe)
    > +static inline void gic_map_to_pin(unsigned int intr, unsigned int pin)
    > +{
    > + gic_save_shared_pin(intr, pin);
    > + gic_write_map_to_pin(intr, pin);
    > +}
    > +
    > +static inline void gic_write_map_to_vpe(unsigned int intr, unsigned int vpe)
    > {
    > gic_write(GIC_REG(SHARED, GIC_SH_INTR_MAP_TO_VPE_BASE) +
    > GIC_SH_MAP_TO_VPE_REG_OFF(intr, vpe),
    > GIC_SH_MAP_TO_VPE_REG_BIT(vpe));
    > }
    >
    > +static inline void gic_map_to_vpe(unsigned int intr, unsigned int vpe)
    > +{
    > + gic_save_shared_vpe(intr, vpe);
    > + gic_write_map_to_vpe(intr, vpe);
    > +}
    > +
    > #ifdef CONFIG_CLKSRC_MIPS_GIC
    > cycle_t gic_read_count(void)
    > {
    > @@ -537,6 +621,7 @@ static void gic_mask_local_irq(struct irq_data *d)
    > {
    > int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
    >
    > + gic_save_local_rmask(smp_processor_id(), (1 << intr));
    > gic_write32(GIC_REG(VPE_LOCAL, GIC_VPE_RMASK), 1 << intr);
    > }
    >
    > @@ -544,6 +629,7 @@ static void gic_unmask_local_irq(struct irq_data *d)
    > {
    > int intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
    >
    > + gic_save_local_smask(smp_processor_id(), (1 << intr));
    > gic_write32(GIC_REG(VPE_LOCAL, GIC_VPE_SMASK), 1 << intr);
    > }
    >
    > @@ -561,6 +647,7 @@ static void gic_mask_local_irq_all_vpes(struct irq_data *d)
    >
    > spin_lock_irqsave(&gic_lock, flags);
    > for (i = 0; i < gic_vpes; i++) {
    > + gic_save_local_rmask(mips_cm_vp_id(i), 1 << intr);
    > gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR),
    > mips_cm_vp_id(i));
    > gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_RMASK), 1 << intr);
    > @@ -576,6 +663,7 @@ static void gic_unmask_local_irq_all_vpes(struct irq_data *d)
    >
    > spin_lock_irqsave(&gic_lock, flags);
    > for (i = 0; i < gic_vpes; i++) {
    > + gic_save_local_smask(mips_cm_vp_id(i), 1 << intr);
    > gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR),
    > mips_cm_vp_id(i));
    > gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_SMASK), 1 << intr);
    > @@ -983,6 +1071,85 @@ static struct irq_domain_ops gic_ipi_domain_ops = {
    > .match = gic_ipi_domain_match,
    > };
    >
    > +#ifdef CONTEXT_SAVING
    > +static void gic_restore_shared(void)
    > +{
    > + unsigned long flags;
    > + int i;
    > +
    > + spin_lock_irqsave(&gic_lock, flags);
    > + for (i = 0; i < gic_shared_intrs; i++) {
    > + gic_write_polarity(i, gic_shared_state[i].polarity);
    > + gic_write_trigger(i, gic_shared_state[i].trigger);
    > + gic_write_dual_edge(i, gic_shared_state[i].dual_edge);
    > + gic_write_map_to_vpe(i, gic_shared_state[i].vpe);
    > + gic_write_map_to_pin(i, gic_shared_state[i].pin);
    > +
    > + if (gic_shared_state[i].mask)
    > + gic_write_set_mask(i);
    > + else
    > + gic_write_reset_mask(i);
    > + }
    > + spin_unlock_irqrestore(&gic_lock, flags);
    > +}
    > +
    > +static void gic_restore_local(unsigned int vpe)
    > +{
    > + int hw, virq, intr, mask;
    > + unsigned long flags;
    > +
    > + for (hw = 0; hw < GIC_NUM_LOCAL_INTRS; hw++) {
    > + intr = GIC_LOCAL_TO_HWIRQ(hw);
    > + virq = irq_linear_revmap(gic_irq_domain, intr);
    > + gic_local_irq_domain_map(gic_irq_domain, virq, hw);
    > + }
    > +
    > + local_irq_save(flags);
    > + gic_write(GIC_REG(VPE_LOCAL, GIC_VPE_OTHER_ADDR), vpe);
    > +
    > + /* Enable EIC mode if necessary */
    > + gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_CTL), cpu_has_veic);
    > +
    > + /* Restore interrupt masks */
    > + mask = gic_local_state[vpe].mask;
    > + gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_RMASK), ~mask);
    > + gic_write32(GIC_REG(VPE_OTHER, GIC_VPE_SMASK), mask);
    > +
    > + local_irq_restore(flags);
    > +}
    > +#endif /* CONTEXT_SAVING */
    > +
    > +#ifdef CONFIG_MIPS_RPROC
    > +/*
    > + * 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).
    > + */
    > +static int gic_cpu_notify(struct notifier_block *nfb, unsigned long action,
    > + void *hcpu)
    > +{
    > + unsigned int cpu = mips_cm_vp_id((unsigned long)hcpu);
    > +
    > + switch (action) {
    > + case CPU_UP_PREPARE:
    > + case CPU_DOWN_FAILED:
    > + gic_restore_shared();
    > + gic_restore_local(cpu);
    > + default:
    > + break;
    > + }
    > +
    > + return NOTIFY_OK;
    > +}
    > +
    > +static struct notifier_block gic_cpu_notifier __refdata = {
    > + .notifier_call = gic_cpu_notify
    > +};
    > +#endif /* CONFIG_MIPS_RPROC */
    > +
    > static void __init __gic_init(unsigned long gic_base_addr,
    > unsigned long gic_addrspace_size,
    > unsigned int cpu_vec, unsigned int irqbase,
    > @@ -1082,6 +1249,10 @@ static void __init __gic_init(unsigned long gic_base_addr,
    > }
    >
    > gic_basic_init();
    > +
    > +#ifdef CONFIG_MIPS_RPROC
    > + register_hotcpu_notifier(&gic_cpu_notifier);
    > +#endif /* CONFIG_MIPS_RPROC */
    > }
    >
    > void __init gic_init(unsigned long gic_base_addr,

    \
     
     \ /
      Last update: 2016-09-17 09:58    [W:2.727 / U:0.428 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site