Commit 7d356745 authored by Federico Vaga's avatar Federico Vaga

sw:kernel: [bugfix] proper locking on CPU selection

In the debug interrupt handler we were locking the single CPU
instance, but actually we are using a global resource which is
then used also to load/dump firmware.

This patch add a new global spin lock for CPU selection.
Signed-off-by: Federico Vaga's avatarFederico Vaga <federico.vaga@cern.ch>
Reported-by: Tomasz Wlostowski's avatarTomasz Wlostowski <tomasz.wlostowski@cern.ch>
parent 75121efa
......@@ -551,6 +551,7 @@ int trtl_probe(struct fmc_device *fmc)
trtl = devm_kzalloc(&fmc->dev, sizeof(struct trtl_dev), GFP_KERNEL);
if (!trtl)
return -ENOMEM;
spin_lock_init(&trtl->lock_cpu_sel);
fmc_set_drvdata(fmc, trtl);
err = fmc_scan_sdb_tree(fmc, 0x0);
......
......@@ -184,10 +184,20 @@ static int trtl_cpu_firmware_load(struct trtl_cpu *cpu, void *fw_buf,
size_t count, loff_t off)
{
struct trtl_dev *trtl = to_trtl_dev(cpu->dev.parent);
uint32_t *fw = fw_buf, word, word_rb;
int size, offset, i, cpu_memsize;
uint32_t *fw = fw_buf, word, word_rb, irq_dbg_old;
int size, offset, i, cpu_memsize, err;
/*
* The Debug console uses the CORE select. In order to avoid an IRQ burst
* due to the loading of a new firmware, we temporary disable the IRQs.
* The Debug IRQ handler, can be called anyway right before we disable it
* but the spinlock will prevent any conflict on the core selection
*/
irq_dbg_old = trtl_ioread(trtl, trtl->base_csr + WRN_CPU_CSR_REG_DBG_IMSK);
trtl_iowrite(trtl, 0, trtl->base_csr + WRN_CPU_CSR_REG_DBG_IMSK);
/* Select the CPU memory to write */
spin_lock(&trtl->lock_cpu_sel);
trtl_iowrite(trtl, cpu->index, trtl->base_csr + WRN_CPU_CSR_REG_CORE_SEL);
cpu_memsize = trtl_ioread(trtl, trtl->base_csr + WRN_CPU_CSR_REG_CORE_MEMSIZE );
......@@ -195,7 +205,8 @@ static int trtl_cpu_firmware_load(struct trtl_cpu *cpu, void *fw_buf,
dev_err(&cpu->dev,
"Cannot load firmware: size limit %d byte\n",
cpu_memsize);
return -ENOMEM;
err = -ENOMEM;
goto out;
}
/* Calculate code size in 32bit word*/
......@@ -228,11 +239,15 @@ static int trtl_cpu_firmware_load(struct trtl_cpu *cpu, void *fw_buf,
dev_err(&cpu->dev,
"failed to load firmware (byte %d | 0x%x != 0x%x)\n",
i, word, word_rb);
return -EFAULT;
err = -EFAULT;
goto out;
}
}
return 0;
out:
spin_unlock(&trtl->lock_cpu_sel);
trtl_iowrite(trtl, irq_dbg_old,
trtl->base_csr + WRN_CPU_CSR_REG_DBG_IMSK);
return err;
}
static int trtl_cpu_firmware_dump(struct trtl_cpu *cpu, void *fw_buf,
......@@ -240,7 +255,7 @@ static int trtl_cpu_firmware_dump(struct trtl_cpu *cpu, void *fw_buf,
{
struct trtl_dev *trtl = to_trtl_dev(cpu->dev.parent);
uint32_t *fw = fw_buf, word, enable;
int size, offset, i, cpu_memsize;
int size, offset, i, cpu_memsize, err = 0;
/* Calculate code size in 32bit word*/
......@@ -248,6 +263,7 @@ static int trtl_cpu_firmware_dump(struct trtl_cpu *cpu, void *fw_buf,
offset = off / 4;
/* Select the CPU memory to write */
spin_lock(&trtl->lock_cpu_sel);
trtl_iowrite(trtl, cpu->index, trtl->base_csr + WRN_CPU_CSR_REG_CORE_SEL);
cpu_memsize = trtl_ioread(trtl, trtl->base_csr + WRN_CPU_CSR_REG_CORE_MEMSIZE );
......@@ -255,7 +271,8 @@ static int trtl_cpu_firmware_dump(struct trtl_cpu *cpu, void *fw_buf,
if (off + count > cpu_memsize) {
dev_err(&cpu->dev, "Cannot dump firmware: size limit %d byte\n",
cpu_memsize);
return -ENOMEM;
err = -ENOMEM;
goto out;
}
/* Stop the CPU before dumping its memory */
......@@ -274,8 +291,9 @@ static int trtl_cpu_firmware_dump(struct trtl_cpu *cpu, void *fw_buf,
/* Restore the previous enable status */
trtl_iowrite(trtl, enable, trtl->base_csr + WRN_CPU_CSR_REG_ENABLE);
return 0;
out:
spin_unlock(&trtl->lock_cpu_sel);
return err;
}
......
......@@ -133,19 +133,20 @@ irqreturn_t trtl_irq_handler_debug(int irq_core_base, void *arg)
struct circ_buf *cb;
uint32_t status;
char c;
int i;
int i, lock;
status = trtl_ioread(trtl, trtl->base_csr + WRN_CPU_CSR_REG_DBG_POLL);
do_irq:
i = -1;
while (status && ++i < trtl->n_cpu) {
if (!(status & 0x1)) {
lock = spin_is_locked(&trtl->lock_cpu_sel);
if (!(status & 0x1) || lock) {
status >>= 1;
continue;
}
/* Select the CPU to use */
spin_lock(&trtl->cpu[i].lock);
spin_lock(&trtl->lock_cpu_sel);
trtl_iowrite(trtl, i, trtl->base_csr + WRN_CPU_CSR_REG_CORE_SEL);
c = trtl_ioread(trtl,
trtl->base_csr + WRN_CPU_CSR_REG_DBG_MSG);
......@@ -159,7 +160,7 @@ do_irq:
cb->tail = (cb->tail + 1) & (dbg_max_msg - 1);
}
}
spin_unlock(&trtl->cpu[i].lock);
spin_unlock(&trtl->lock_cpu_sel);
}
status = trtl_ioread(trtl, trtl->base_csr + WRN_CPU_CSR_REG_DBG_POLL);
......
......@@ -191,6 +191,7 @@ struct trtl_dev {
struct dentry *dbg_dir; /**< root debug directory */
uint32_t message_sequence; /**< message sequence number */
struct spinlock lock_cpu_sel;
};
static inline u32 trtl_ioread(struct trtl_dev *trtl, int addr)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment