[linux-yocto] [PATCH 31/52] arch/arm/mach-axxia: fixed hang in preempt kernel
Daniel Dragomir
daniel.dragomir at windriver.com
Wed Jan 28 09:18:45 PST 2015
From: John Jacques <john.jacques at lsi.com>
Fixed the hang in the affinity code. The selection of a cpu on a
clear affinity did not have the right mask, causing the affinity
to be moved to a cpu that was not powered up.
Signed-off-by: John Jacques <john.jacques at lsi.com>
---
arch/arm/mach-axxia/axxia-gic.c | 242 +++++++++++++++++++++-------------------
1 file changed, 130 insertions(+), 112 deletions(-)
diff --git a/arch/arm/mach-axxia/axxia-gic.c b/arch/arm/mach-axxia/axxia-gic.c
index 6345a99..34764b0 100644
--- a/arch/arm/mach-axxia/axxia-gic.c
+++ b/arch/arm/mach-axxia/axxia-gic.c
@@ -228,22 +228,23 @@ static void axxia_gic_flush_affinity_queue(struct work_struct *dummy)
void *qdata;
struct gic_rpc_data *rpc_data;
-
while (axxia_get_item(&axxia_circ_q, &qdata) != -1) {
+
rpc_data = (struct gic_rpc_data *) qdata;
if (rpc_data->func_mask == SET_AFFINITY) {
- smp_call_function_single(rpc_data->cpu,
- gic_set_affinity_remote,
- qdata, 1);
-
+ if (cpu_online(rpc_data->cpu)) {
+ smp_call_function_single(rpc_data->cpu, gic_set_affinity_remote,
+ qdata, 1);
+ }
} else if (rpc_data->func_mask == CLR_AFFINITY) {
-
- smp_call_function_single(rpc_data->cpu,
- gic_clr_affinity_remote,
- qdata, 1);
+ if (cpu_online(rpc_data->cpu)) {
+ smp_call_function_single(rpc_data->cpu, gic_clr_affinity_remote,
+ qdata, 1);
+ }
}
kfree(qdata);
}
+
}
/*
@@ -482,35 +483,45 @@ static int gic_retrigger(struct irq_data *d)
return -ENXIO;
}
-static int _gic_clear_affinity(struct irq_data *d, u32 cpu, bool update_enable)
+static void gic_set_irq_target(void __iomem *dist_base,
+ u32 irqid, u32 cpu, bool set)
{
-
- void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3);
- unsigned int shift = (gic_irq(d) % 4) * 8;
+ void __iomem *reg;
+ unsigned int shift;
u32 val;
u32 mask = 0;
- u32 enable_mask, enable_offset;
+ u32 bit;
+ reg = dist_base + GIC_DIST_TARGET + (irqid & ~3);
+ shift = (irqid % 4) * 8;
mask = 0xff << shift;
- enable_mask = 1 << (gic_irq(d) % 32);
- enable_offset = 4 * (gic_irq(d) / 32);
+ val = readl_relaxed(reg) & ~mask;
+
+ if (!set)
+ /* Clear affinity, mask IRQ. */
+ writel_relaxed(val, reg);
+ else {
+ bit = 1 << ((cpu_logical_map(cpu) % CORES_PER_CLUSTER) + shift);
+ writel_relaxed(val | bit, reg);
+ }
+
+}
+
+static int _gic_clear_affinity(struct irq_data *d, u32 cpu, bool update_enable)
+{
+
+ u32 enable_mask, enable_offset;
raw_spin_lock(&irq_controller_lock);
- val = readl_relaxed(reg) & ~mask;
- /* Clear affinity, mask IRQ. */
- writel_relaxed(val, reg);
+ gic_set_irq_target(gic_dist_base(d), gic_irq(d), cpu, false);
if (update_enable) {
-
- writel_relaxed(enable_mask,
- gic_data_dist_base(&gic_data) + GIC_DIST_PENDING_CLEAR + enable_offset);
- writel_relaxed(enable_mask,
- gic_data_dist_base(&gic_data) + GIC_DIST_ACTIVE_CLEAR + enable_offset);
+ enable_mask = 1 << (gic_irq(d) % 32);
+ enable_offset = 4 * (gic_irq(d) / 32);
writel_relaxed(enable_mask,
gic_data_dist_base(&gic_data) + GIC_DIST_ENABLE_CLEAR + enable_offset);
-
}
raw_spin_unlock(&irq_controller_lock);
@@ -523,33 +534,17 @@ static int _gic_set_affinity(struct irq_data *d,
u32 cpu,
bool update_enable)
{
- void __iomem *reg = gic_dist_base(d) + GIC_DIST_TARGET + (gic_irq(d) & ~3);
- unsigned int shift = (gic_irq(d) % 4) * 8;
- u32 val;
- u32 mask = 0;
- u32 bit;
u32 enable_mask, enable_offset;
- /*
- * Normalize the cpu number as seen by Linux (0-15) to a
- * number as seen by a cluster (0-3).
- */
- bit = 1 << ((cpu_logical_map(cpu) % CORES_PER_CLUSTER) + shift);
- mask = 0xff << shift;
-
- enable_mask = 1 << (gic_irq(d) % 32);
- enable_offset = 4 * (gic_irq(d) / 32);
-
raw_spin_lock(&irq_controller_lock);
- val = readl_relaxed(reg) & ~mask;
- /* Set affinity, mask IRQ. */
- writel_relaxed(val | bit, reg);
+ gic_set_irq_target(gic_dist_base(d), gic_irq(d), cpu, true);
if (update_enable) {
+ enable_mask = 1 << (gic_irq(d) % 32);
+ enable_offset = 4 * (gic_irq(d) / 32);
writel_relaxed(enable_mask,
- gic_data_dist_base(&gic_data) + GIC_DIST_ENABLE_SET
- + enable_offset);
+ gic_data_dist_base(&gic_data) + GIC_DIST_ENABLE_SET + enable_offset);
}
raw_spin_unlock(&irq_controller_lock);
@@ -580,11 +575,11 @@ static int gic_set_affinity(struct irq_data *d,
const struct cpumask *mask_val,
bool force)
{
- u32 pcpu = cpu_logical_map(smp_processor_id());
- unsigned int irqid = gic_irq(d);
- struct cpumask *affinity_mask = (struct cpumask *)mask_val;
+ u32 pcpu;
+ unsigned int irqid;
+ struct cpumask *affinity_mask;
u32 mask;
- u32 oldcpu = irq_cpuid[irqid];
+ u32 oldcpu;
struct gic_rpc_data *gic_rpc_ptr;
int rval;
bool new_same_core = false;
@@ -597,6 +592,12 @@ static int gic_set_affinity(struct irq_data *d,
BUG_ON(!irqs_disabled());
+
+ pcpu = cpu_logical_map(smp_processor_id());
+ irqid = gic_irq(d);
+ affinity_mask = (struct cpumask *)mask_val;
+ oldcpu = irq_cpuid[irqid];
+
if (irqid >= MAX_GIC_INTERRUPTS)
return -EINVAL;
@@ -604,7 +605,6 @@ static int gic_set_affinity(struct irq_data *d,
if ((irqid >= IPI0_CPU0) && (irqid < MAX_AXM_IPI_NUM))
return IRQ_SET_MASK_OK;
-
if (force)
add_cpu = cpumask_any(cpu_online_mask);
else
@@ -650,25 +650,21 @@ static int gic_set_affinity(struct irq_data *d,
}
}
- mutex_lock(&affinity_lock);
-
- /* Update Axxia IRQ affinity table with the new physical CPU number. */
- irq_cpuid[irqid] = cpu_logical_map(add_cpu);
/*
* We clear first to make sure the affinity mask always has a bit set,
* especially when the two cpus are in the same cluster.
*/
if (irqid != IRQ_PMU) {
-
if (clear_needed == AFFINITY_CLEAR_LOCAL) {
_gic_clear_affinity(d, del_cpu, update_enable);
} else if (clear_needed == AFFINITY_CLEAR_OTHER_CLUSTER) {
- mask = 0xf << (oldcpu / CORES_PER_CLUSTER);
- del_cpu = cpumask_any_and((struct cpumask *)&mask, cpu_online_mask);
+ mask = 0xf << ((oldcpu / CORES_PER_CLUSTER) * 4);
+ del_cpu = cpumask_any_and((struct cpumask *)&mask,
+ cpu_online_mask);
if (del_cpu < nr_cpu_ids) {
@@ -685,19 +681,23 @@ static int gic_set_affinity(struct irq_data *d,
gic_rpc_ptr->oldcpu = oldcpu;
gic_rpc_ptr->d = d;
gic_rpc_ptr->update_enable = update_enable;
+ get_cpu();
rval = axxia_put_item(&axxia_circ_q, (void *) gic_rpc_ptr);
+ put_cpu();
if (rval) {
pr_err(
"ERROR: failed to add CLR_AFFINITY request for cpu: %d\n",
del_cpu);
kfree((void *) gic_rpc_ptr);
+ mutex_unlock(&affinity_lock);
+ return rval;
}
schedule_work_on(0, &axxia_gic_affinity_work);
- }
+ } else
+ pr_err("ERROR: no CPUs left\n");
}
}
-
if (set_needed == AFFINITY_SET_LOCAL) {
_gic_set_affinity(d, add_cpu, update_enable);
@@ -716,19 +716,22 @@ static int gic_set_affinity(struct irq_data *d,
gic_rpc_ptr->cpu = add_cpu;
gic_rpc_ptr->update_enable = update_enable;
gic_rpc_ptr->d = d;
+ get_cpu();
rval = axxia_put_item(&axxia_circ_q, (void *) gic_rpc_ptr);
+ put_cpu();
if (rval) {
pr_err("ERROR: failed to add SET_AFFINITY request for cpu: %d\n",
add_cpu);
kfree((void *) gic_rpc_ptr);
+ mutex_unlock(&affinity_lock);
+ return rval;
}
schedule_work_on(0, &axxia_gic_affinity_work);
}
-
- mutex_unlock(&affinity_lock);
-
+ /* Update Axxia IRQ affinity table with the new physical CPU number. */
+ irq_cpuid[irqid] = cpu_logical_map(add_cpu);
return IRQ_SET_MASK_OK;
}
@@ -1206,10 +1209,9 @@ static void __init gic_axxia_init(struct gic_chip_data *gic)
writel_relaxed(cpumask, ipi_mask_reg_base + 0x40 + i * 4);
}
-static void __cpuinit gic_dist_init(struct gic_chip_data *gic)
+static void gic_dist_init(struct gic_chip_data *gic)
{
unsigned int i;
- u32 cpumask;
unsigned int gic_irqs = gic->gic_irqs;
void __iomem *base = gic_data_dist_base(gic);
u32 cpu = cpu_logical_map(smp_processor_id());
@@ -1220,17 +1222,17 @@ static void __cpuinit gic_dist_init(struct gic_chip_data *gic)
u32 enableoff;
u32 val;
u32 this_cluster = get_cluster_id();
+ u32 powered_on;
+ u32 ccpu;
/* Initialize the distributor interface once per CPU cluster */
if ((test_and_set_bit(get_cluster_id(), &gic->dist_init_done)) && (!cluster_power_up[this_cluster]))
return;
- cpumask = 1 << cpu;
- cpumask |= cpumask << 8;
- cpumask |= cpumask << 16;
-
writel_relaxed(0, base + GIC_DIST_CTRL);
+ /*################################# CONFIG IRQS ####################################*/
+
/*
* Set all global interrupts to be level triggered, active low.
*/
@@ -1238,13 +1240,23 @@ static void __cpuinit gic_dist_init(struct gic_chip_data *gic)
writel_relaxed(0, base + GIC_DIST_CONFIG + i * 4 / 16);
/*
- * Set all global interrupts to this CPU only.
- * (Only do this for the first core on cluster 0).
+ * Set Axxia IPI interrupts to be edge triggered.
*/
- if (cpu == 0)
- for (i = 32; i < gic_irqs; i += 4)
- writel_relaxed(cpumask,
- base + GIC_DIST_TARGET + i * 4 / 4);
+ for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
+ confmask = 0x2 << ((i % 16) * 2);
+ confoff = (i / 16) * 4;
+ val = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
+ val |= confmask;
+ writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);
+ }
+
+ /*################################# PRIORITY ####################################*/
+ /*
+ * Set priority on PPI and SGI interrupts
+ */
+ for (i = 0; i < 32; i += 4)
+ writel_relaxed(0xa0a0a0a0,
+ base + GIC_DIST_PRI + i * 4 / 4);
/*
* Set priority on all global interrupts.
@@ -1252,52 +1264,43 @@ static void __cpuinit gic_dist_init(struct gic_chip_data *gic)
for (i = 32; i < gic_irqs; i += 4)
writel_relaxed(0xa0a0a0a0, base + GIC_DIST_PRI + i * 4 / 4);
+
+ /*################################# TARGET ####################################*/
/*
- * Disable all interrupts. Leave the PPI and SGIs alone
- * as these enables are banked registers.
+ * Set all global interrupts to this CPU only.
+ * (Only do this for the first core on cluster 0).
*/
- for (i = 32; i < gic_irqs; i += 32) {
- writel_relaxed(0xffffffff,
- base + GIC_DIST_ACTIVE_CLEAR + i * 4 / 32);
- writel_relaxed(0xffffffff,
- base + GIC_DIST_PENDING_CLEAR + i * 4 / 32);
- writel_relaxed(0xffffffff,
- base + GIC_DIST_ENABLE_CLEAR + i * 4 / 32);
- }
+ if (cpu == 0)
+ for (i = 32; i < gic_irqs; i += 4)
+ writel_relaxed(0x01010101, base + GIC_DIST_TARGET + i * 4 / 4);
/*
* Set Axxia IPI interrupts for all CPUs in this cluster.
*/
+ powered_on = (~pm_cpu_powered_down) & 0xFFFF;
for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
cpumask_8 = 1 << ((i - IPI0_CPU0) % 4);
- writeb_relaxed(cpumask_8, base + GIC_DIST_TARGET + i);
- }
-
- /*
- * Set the PMU IRQ to the first cpu in this cluster.
- */
- writeb_relaxed(0x01, base + GIC_DIST_TARGET + IRQ_PMU);
-
- /*
- * Set Axxia IPI interrupts to be edge triggered.
- */
- for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
- confmask = 0x2 << ((i % 16) * 2);
- confoff = (i / 16) * 4;
- val = readl_relaxed(base + GIC_DIST_CONFIG + confoff);
- val |= confmask;
- writel_relaxed(val, base + GIC_DIST_CONFIG + confoff);
+ ccpu = (this_cluster * 4) + ((i - IPI0_CPU0) % CORES_PER_CLUSTER);
+ if ((1 << ccpu) & powered_on)
+ writeb_relaxed(cpumask_8, base + GIC_DIST_TARGET + i);
+ else
+ writeb_relaxed(0x00, base + GIC_DIST_TARGET + i);
}
+ /*################################# ENABLE IRQS ####################################*/
/*
* Do the initial enable of the Axxia IPI interrupts here.
* NOTE: Writing a 0 to this register has no effect, so
* no need to read and OR in bits, just writing is OK.
*/
+
+ powered_on = (~pm_cpu_powered_down) & 0xFFFF;
for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
enablemask = 1 << (i % 32);
enableoff = (i / 32) * 4;
- writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
+ ccpu = (this_cluster * 4) + ((i - IPI0_CPU0) % CORES_PER_CLUSTER);
+ if ((1 << ccpu) & powered_on)
+ writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
}
/*
@@ -1307,32 +1310,47 @@ static void __cpuinit gic_dist_init(struct gic_chip_data *gic)
enableoff = (IRQ_PMU / 32) * 4;
writel_relaxed(enablemask, base + GIC_DIST_ENABLE_SET + enableoff);
+
writel_relaxed(1, base + GIC_DIST_CTRL);
+
}
-static void __cpuinit gic_cpu_init(struct gic_chip_data *gic)
+static void gic_cpu_init(struct gic_chip_data *gic)
{
void __iomem *dist_base = gic_data_dist_base(gic);
void __iomem *base = gic_data_cpu_base(gic);
int i;
-
+ u32 enablemask;
+ u32 enableoff;
+ u32 ccpu;
+ u32 cpu = smp_processor_id();
+ u32 cluster = cpu / CORES_PER_CLUSTER;
+ u32 cpumask_8;
/*
* Deal with the banked PPI and SGI interrupts - disable all
* PPI interrupts, and also all SGI interrupts (we don't use
* SGIs in the Axxia).
*/
-
writel_relaxed(0xffffffff, dist_base + GIC_DIST_ENABLE_CLEAR);
- /*
- * Set priority on PPI and SGI interrupts
- */
- for (i = 0; i < 32; i += 4)
- writel_relaxed(0xa0a0a0a0,
- dist_base + GIC_DIST_PRI + i * 4 / 4);
+ if (!cluster_power_up[cluster]) {
+ writel_relaxed(0, dist_base + GIC_DIST_CTRL);
+ for (i = IPI0_CPU0; i < MAX_AXM_IPI_NUM; i++) {
+ cpumask_8 = 1 << ((i - IPI0_CPU0) % 4);
+ enablemask = 1 << (i % 32);
+ enableoff = (i / 32) * 4;
+ ccpu = (cluster * 4) + ((i - IPI0_CPU0) % CORES_PER_CLUSTER);
+ if (ccpu == cpu) {
+ writeb_relaxed(cpumask_8, dist_base + GIC_DIST_TARGET + i);
+ writel_relaxed(enablemask, dist_base + GIC_DIST_ENABLE_SET + enableoff);
+ }
+ }
+ writel_relaxed(1, dist_base + GIC_DIST_CTRL);
+ }
writel_relaxed(0xf0, base + GIC_CPU_PRIMASK);
+
writel_relaxed(1, base + GIC_CPU_CTRL);
}
@@ -1526,7 +1544,7 @@ void __init axxia_gic_init_bases(int irq_start,
}
-void __cpuinit axxia_gic_secondary_init(void)
+void axxia_gic_secondary_init(void)
{
struct gic_chip_data *gic = &gic_data;
--
1.8.1.4
More information about the linux-yocto
mailing list