mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-05 02:21:52 +09:00
Merge 0986efc3c7 ("spi: atmel-qspi: Memory barriers after memory-mapped I/O") into android14-6.1-lts
Steps on the way to 6.1.130 Change-Id: I9fa2d1d29e6523458cd471d93d06bfba78e920ba Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
@@ -138,11 +138,15 @@
|
||||
#define QSPI_WPSR_WPVSRC_MASK GENMASK(15, 8)
|
||||
#define QSPI_WPSR_WPVSRC(src) (((src) << 8) & QSPI_WPSR_WPVSRC)
|
||||
|
||||
#define ATMEL_QSPI_TIMEOUT 1000 /* ms */
|
||||
|
||||
struct atmel_qspi_caps {
|
||||
bool has_qspick;
|
||||
bool has_ricr;
|
||||
};
|
||||
|
||||
struct atmel_qspi_ops;
|
||||
|
||||
struct atmel_qspi {
|
||||
void __iomem *regs;
|
||||
void __iomem *mem;
|
||||
@@ -150,13 +154,22 @@ struct atmel_qspi {
|
||||
struct clk *qspick;
|
||||
struct platform_device *pdev;
|
||||
const struct atmel_qspi_caps *caps;
|
||||
const struct atmel_qspi_ops *ops;
|
||||
resource_size_t mmap_size;
|
||||
u32 pending;
|
||||
u32 irq_mask;
|
||||
u32 mr;
|
||||
u32 scr;
|
||||
struct completion cmd_completion;
|
||||
};
|
||||
|
||||
struct atmel_qspi_ops {
|
||||
int (*set_cfg)(struct atmel_qspi *aq, const struct spi_mem_op *op,
|
||||
u32 *offset);
|
||||
int (*transfer)(struct spi_mem *mem, const struct spi_mem_op *op,
|
||||
u32 offset);
|
||||
};
|
||||
|
||||
struct atmel_qspi_mode {
|
||||
u8 cmd_buswidth;
|
||||
u8 addr_buswidth;
|
||||
@@ -404,10 +417,67 @@ static int atmel_qspi_set_cfg(struct atmel_qspi *aq,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_qspi_wait_for_completion(struct atmel_qspi *aq, u32 irq_mask)
|
||||
{
|
||||
int err = 0;
|
||||
u32 sr;
|
||||
|
||||
/* Poll INSTRuction End status */
|
||||
sr = atmel_qspi_read(aq, QSPI_SR);
|
||||
if ((sr & irq_mask) == irq_mask)
|
||||
return 0;
|
||||
|
||||
/* Wait for INSTRuction End interrupt */
|
||||
reinit_completion(&aq->cmd_completion);
|
||||
aq->pending = sr & irq_mask;
|
||||
aq->irq_mask = irq_mask;
|
||||
atmel_qspi_write(irq_mask, aq, QSPI_IER);
|
||||
if (!wait_for_completion_timeout(&aq->cmd_completion,
|
||||
msecs_to_jiffies(ATMEL_QSPI_TIMEOUT)))
|
||||
err = -ETIMEDOUT;
|
||||
atmel_qspi_write(irq_mask, aq, QSPI_IDR);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int atmel_qspi_transfer(struct spi_mem *mem,
|
||||
const struct spi_mem_op *op, u32 offset)
|
||||
{
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(mem->spi->controller);
|
||||
|
||||
/* Skip to the final steps if there is no data */
|
||||
if (!op->data.nbytes)
|
||||
return atmel_qspi_wait_for_completion(aq,
|
||||
QSPI_SR_CMD_COMPLETED);
|
||||
|
||||
/* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */
|
||||
(void)atmel_qspi_read(aq, QSPI_IFR);
|
||||
|
||||
/* Send/Receive data */
|
||||
if (op->data.dir == SPI_MEM_DATA_IN) {
|
||||
memcpy_fromio(op->data.buf.in, aq->mem + offset,
|
||||
op->data.nbytes);
|
||||
|
||||
/* Synchronize AHB and APB accesses again */
|
||||
rmb();
|
||||
} else {
|
||||
memcpy_toio(aq->mem + offset, op->data.buf.out,
|
||||
op->data.nbytes);
|
||||
|
||||
/* Synchronize AHB and APB accesses again */
|
||||
wmb();
|
||||
}
|
||||
|
||||
/* Release the chip-select */
|
||||
atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR);
|
||||
|
||||
return atmel_qspi_wait_for_completion(aq, QSPI_SR_CMD_COMPLETED);
|
||||
}
|
||||
|
||||
static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
{
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(mem->spi->master);
|
||||
u32 sr, offset;
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(mem->spi->controller);
|
||||
u32 offset;
|
||||
int err;
|
||||
|
||||
/*
|
||||
@@ -416,46 +486,20 @@ static int atmel_qspi_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
|
||||
* when the flash memories overrun the controller's memory space.
|
||||
*/
|
||||
if (op->addr.val + op->data.nbytes > aq->mmap_size)
|
||||
return -ENOTSUPP;
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (op->addr.nbytes > 4)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
err = pm_runtime_resume_and_get(&aq->pdev->dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = atmel_qspi_set_cfg(aq, op, &offset);
|
||||
err = aq->ops->set_cfg(aq, op, &offset);
|
||||
if (err)
|
||||
goto pm_runtime_put;
|
||||
|
||||
/* 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)atmel_qspi_read(aq, QSPI_IFR);
|
||||
|
||||
/* Send/Receive data */
|
||||
if (op->data.dir == SPI_MEM_DATA_IN)
|
||||
memcpy_fromio(op->data.buf.in, aq->mem + offset,
|
||||
op->data.nbytes);
|
||||
else
|
||||
memcpy_toio(aq->mem + offset, op->data.buf.out,
|
||||
op->data.nbytes);
|
||||
|
||||
/* Release the chip-select */
|
||||
atmel_qspi_write(QSPI_CR_LASTXFER, aq, QSPI_CR);
|
||||
}
|
||||
|
||||
/* Poll INSTRuction End status */
|
||||
sr = atmel_qspi_read(aq, QSPI_SR);
|
||||
if ((sr & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED)
|
||||
goto pm_runtime_put;
|
||||
|
||||
/* Wait for INSTRuction End interrupt */
|
||||
reinit_completion(&aq->cmd_completion);
|
||||
aq->pending = sr & QSPI_SR_CMD_COMPLETED;
|
||||
atmel_qspi_write(QSPI_SR_CMD_COMPLETED, aq, QSPI_IER);
|
||||
if (!wait_for_completion_timeout(&aq->cmd_completion,
|
||||
msecs_to_jiffies(1000)))
|
||||
err = -ETIMEDOUT;
|
||||
atmel_qspi_write(QSPI_SR_CMD_COMPLETED, aq, QSPI_IDR);
|
||||
err = aq->ops->transfer(mem, op, offset);
|
||||
|
||||
pm_runtime_put:
|
||||
pm_runtime_mark_last_busy(&aq->pdev->dev);
|
||||
@@ -476,7 +520,7 @@ static const struct spi_controller_mem_ops atmel_qspi_mem_ops = {
|
||||
|
||||
static int atmel_qspi_setup(struct spi_device *spi)
|
||||
{
|
||||
struct spi_controller *ctrl = spi->master;
|
||||
struct spi_controller *ctrl = spi->controller;
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
unsigned long src_rate;
|
||||
u32 scbr;
|
||||
@@ -510,6 +554,39 @@ static int atmel_qspi_setup(struct spi_device *spi)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int atmel_qspi_set_cs_timing(struct spi_device *spi)
|
||||
{
|
||||
struct spi_controller *ctrl = spi->controller;
|
||||
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
|
||||
unsigned long clk_rate;
|
||||
u32 cs_setup;
|
||||
int delay;
|
||||
int ret;
|
||||
|
||||
delay = spi_delay_to_ns(&spi->cs_setup, NULL);
|
||||
if (delay <= 0)
|
||||
return delay;
|
||||
|
||||
clk_rate = clk_get_rate(aq->pclk);
|
||||
if (!clk_rate)
|
||||
return -EINVAL;
|
||||
|
||||
cs_setup = DIV_ROUND_UP((delay * DIV_ROUND_UP(clk_rate, 1000000)),
|
||||
1000);
|
||||
|
||||
ret = pm_runtime_resume_and_get(ctrl->dev.parent);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
aq->scr |= QSPI_SCR_DLYBS(cs_setup);
|
||||
atmel_qspi_write(aq->scr, aq, QSPI_SCR);
|
||||
|
||||
pm_runtime_mark_last_busy(ctrl->dev.parent);
|
||||
pm_runtime_put_autosuspend(ctrl->dev.parent);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void atmel_qspi_init(struct atmel_qspi *aq)
|
||||
{
|
||||
/* Reset the QSPI controller */
|
||||
@@ -536,12 +613,17 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
|
||||
return IRQ_NONE;
|
||||
|
||||
aq->pending |= pending;
|
||||
if ((aq->pending & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED)
|
||||
if ((aq->pending & aq->irq_mask) == aq->irq_mask)
|
||||
complete(&aq->cmd_completion);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct atmel_qspi_ops atmel_qspi_ops = {
|
||||
.set_cfg = atmel_qspi_set_cfg,
|
||||
.transfer = atmel_qspi_transfer,
|
||||
};
|
||||
|
||||
static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct spi_controller *ctrl;
|
||||
@@ -555,6 +637,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
|
||||
ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD;
|
||||
ctrl->setup = atmel_qspi_setup;
|
||||
ctrl->set_cs_timing = atmel_qspi_set_cs_timing;
|
||||
ctrl->bus_num = -1;
|
||||
ctrl->mem_ops = &atmel_qspi_mem_ops;
|
||||
ctrl->num_chipselect = 1;
|
||||
@@ -565,6 +648,7 @@ static int atmel_qspi_probe(struct platform_device *pdev)
|
||||
|
||||
init_completion(&aq->cmd_completion);
|
||||
aq->pdev = pdev;
|
||||
aq->ops = &atmel_qspi_ops;
|
||||
|
||||
/* Map the registers */
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "qspi_base");
|
||||
|
||||
Reference in New Issue
Block a user