Skip to content

Commit

Permalink
spi: atmel-quadspi: Add verbose debug facilities to monitor register …
Browse files Browse the repository at this point in the history
…accesses

This feature should not be enabled in release but can be useful for
developers who need to monitor register accesses at some specific places.

Helped me identify a bug in u-boot, by comparing the register accesses
from the linux driver with the ones from its u-boot variant.

Signed-off-by: Tudor Ambarus <[email protected]>
Link: https://lore.kernel.org/r/[email protected]
Signed-off-by: Mark Brown <[email protected]>
  • Loading branch information
ambarus authored and broonie committed Mar 20, 2020
1 parent 138f56e commit c528ecf
Showing 1 changed file with 97 additions and 22 deletions.
119 changes: 97 additions & 22 deletions drivers/spi/atmel-quadspi.c
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,81 @@ static const struct atmel_qspi_mode atmel_qspi_modes[] = {
{ 4, 4, 4, QSPI_IFR_WIDTH_QUAD_CMD },
};

#ifdef VERBOSE_DEBUG
static const char *atmel_qspi_reg_name(u32 offset, char *tmp, size_t sz)
{
switch (offset) {
case QSPI_CR:
return "CR";
case QSPI_MR:
return "MR";
case QSPI_RD:
return "MR";
case QSPI_TD:
return "TD";
case QSPI_SR:
return "SR";
case QSPI_IER:
return "IER";
case QSPI_IDR:
return "IDR";
case QSPI_IMR:
return "IMR";
case QSPI_SCR:
return "SCR";
case QSPI_IAR:
return "IAR";
case QSPI_ICR:
return "ICR/WICR";
case QSPI_IFR:
return "IFR";
case QSPI_RICR:
return "RICR";
case QSPI_SMR:
return "SMR";
case QSPI_SKR:
return "SKR";
case QSPI_WPMR:
return "WPMR";
case QSPI_WPSR:
return "WPSR";
case QSPI_VERSION:
return "VERSION";
default:
snprintf(tmp, sz, "0x%02x", offset);
break;
}

return tmp;
}
#endif /* VERBOSE_DEBUG */

static u32 atmel_qspi_read(struct atmel_qspi *aq, u32 offset)
{
u32 value = readl_relaxed(aq->regs + offset);

#ifdef VERBOSE_DEBUG
char tmp[8];

dev_vdbg(&aq->pdev->dev, "read 0x%08x from %s\n", value,
atmel_qspi_reg_name(offset, tmp, sizeof(tmp)));
#endif /* VERBOSE_DEBUG */

return value;
}

static void atmel_qspi_write(u32 value, struct atmel_qspi *aq, u32 offset)
{
#ifdef VERBOSE_DEBUG
char tmp[8];

dev_vdbg(&aq->pdev->dev, "write 0x%08x into %s\n", value,
atmel_qspi_reg_name(offset, tmp, sizeof(tmp)));
#endif /* VERBOSE_DEBUG */

writel_relaxed(value, aq->regs + offset);
}

static inline bool atmel_qspi_is_compatible(const struct spi_mem_op *op,
const struct atmel_qspi_mode *mode)
{
Expand Down Expand Up @@ -293,32 +368,32 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq,
* Serial Memory Mode (SMM).
*/
if (aq->mr != QSPI_MR_SMM) {
writel_relaxed(QSPI_MR_SMM, aq->regs + QSPI_MR);
atmel_qspi_write(QSPI_MR_SMM, aq, QSPI_MR);
aq->mr = QSPI_MR_SMM;
}

/* Clear pending interrupts */
(void)readl_relaxed(aq->regs + QSPI_SR);
(void)atmel_qspi_read(aq, QSPI_SR);

if (aq->caps->has_ricr) {
if (!op->addr.nbytes && op->data.dir == SPI_MEM_DATA_IN)
ifr |= QSPI_IFR_APBTFRTYP_READ;

/* Set QSPI Instruction Frame registers */
writel_relaxed(iar, aq->regs + QSPI_IAR);
atmel_qspi_write(iar, aq, QSPI_IAR);
if (op->data.dir == SPI_MEM_DATA_IN)
writel_relaxed(icr, aq->regs + QSPI_RICR);
atmel_qspi_write(icr, aq, QSPI_RICR);
else
writel_relaxed(icr, aq->regs + QSPI_WICR);
writel_relaxed(ifr, aq->regs + QSPI_IFR);
atmel_qspi_write(icr, aq, QSPI_WICR);
atmel_qspi_write(ifr, aq, QSPI_IFR);
} else {
if (op->data.dir == SPI_MEM_DATA_OUT)
ifr |= QSPI_IFR_SAMA5D2_WRITE_TRSFR;

/* Set QSPI Instruction Frame registers */
writel_relaxed(iar, aq->regs + QSPI_IAR);
writel_relaxed(icr, aq->regs + QSPI_ICR);
writel_relaxed(ifr, aq->regs + QSPI_IFR);
atmel_qspi_write(iar, aq, QSPI_IAR);
atmel_qspi_write(icr, aq, QSPI_ICR);
atmel_qspi_write(ifr, aq, QSPI_IFR);
}

return 0;
Expand All @@ -345,7 +420,7 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
/* Skip to the final steps if there is no data */
if (op->data.nbytes) {
/* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */
(void)readl_relaxed(aq->regs + QSPI_IFR);
(void)atmel_qspi_read(aq, QSPI_IFR);

/* Send/Receive data */
if (op->data.dir == SPI_MEM_DATA_IN)
Expand All @@ -356,22 +431,22 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
op->data.nbytes);

/* Release the chip-select */
writel_relaxed(QSPI_CR_LASTXFER, aq->regs + QSPI_CR);
atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR);
}

/* Poll INSTRuction End status */
sr = readl_relaxed(aq->regs + QSPI_SR);
sr = atmel_qspi_read(aq, QSPI_SR);
if ((sr & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED)
return err;

/* Wait for INSTRuction End interrupt */
reinit_completion(&aq->cmd_completion);
aq->pending = sr & QSPI_SR_CMD_COMPLETED;
writel_relaxed(QSPI_SR_CMD_COMPLETED, aq->regs + QSPI_IER);
atmel_qspi_write(QSPI_SR_CMD_COMPLETED, aq, QSPI_IER);
if (!wait_for_completion_timeout(&aq->cmd_completion,
msecs_to_jiffies(1000)))
err = -ETIMEDOUT;
writel_relaxed(QSPI_SR_CMD_COMPLETED, aq->regs + QSPI_IDR);
atmel_qspi_write(QSPI_SR_CMD_COMPLETED, aq, QSPI_IDR);

return err;
}
Expand Down Expand Up @@ -410,31 +485,31 @@ static int atmel_qspi_setup(struct spi_device *spi)
scbr--;

aq->scr = QSPI_SCR_SCBR(scbr);
writel_relaxed(aq->scr, aq->regs + QSPI_SCR);
atmel_qspi_write(aq->scr, aq, QSPI_SCR);

return 0;
}

static void atmel_qspi_init(struct atmel_qspi *aq)
{
/* Reset the QSPI controller */
writel_relaxed(QSPI_CR_SWRST, aq->regs + QSPI_CR);
atmel_qspi_write(QSPI_CR_SWRST, aq, QSPI_CR);

/* Set the QSPI controller by default in Serial Memory Mode */
writel_relaxed(QSPI_MR_SMM, aq->regs + QSPI_MR);
atmel_qspi_write(QSPI_MR_SMM, aq, QSPI_MR);
aq->mr = QSPI_MR_SMM;

/* Enable the QSPI controller */
writel_relaxed(QSPI_CR_QSPIEN, aq->regs + QSPI_CR);
atmel_qspi_write(QSPI_CR_QSPIEN, aq, QSPI_CR);
}

static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
{
struct atmel_qspi *aq = dev_id;
u32 status, mask, pending;

status = readl_relaxed(aq->regs + QSPI_SR);
mask = readl_relaxed(aq->regs + QSPI_IMR);
status = atmel_qspi_read(aq, QSPI_SR);
mask = atmel_qspi_read(aq, QSPI_IMR);
pending = status & mask;

if (!pending)
Expand Down Expand Up @@ -569,7 +644,7 @@ static int atmel_qspi_remove(struct platform_device *pdev)
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);

spi_unregister_controller(ctrl);
writel_relaxed(QSPI_CR_QSPIDIS, aq->regs + QSPI_CR);
atmel_qspi_write(QSPI_CR_QSPIDIS, aq, QSPI_CR);
clk_disable_unprepare(aq->qspick);
clk_disable_unprepare(aq->pclk);
return 0;
Expand All @@ -596,7 +671,7 @@ static int __maybe_unused atmel_qspi_resume(struct device *dev)

atmel_qspi_init(aq);

writel_relaxed(aq->scr, aq->regs + QSPI_SCR);
atmel_qspi_write(aq->scr, aq, QSPI_SCR);

return 0;
}
Expand Down

0 comments on commit c528ecf

Please sign in to comment.