Merge remote-tracking branches 'spi/topic/mem' and 'spi/topic/mtd' into spi-next

This commit is contained in:
Mark Brown 2018-12-20 16:01:30 +00:00
commit 74ff666bd7
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
8 changed files with 488 additions and 413 deletions

View File

@ -39,15 +39,6 @@ config SPI_ASPEED_SMC
and support for the SPI flash memory controller (SPI) for and support for the SPI flash memory controller (SPI) for
the host firmware. The implementation only supports SPI NOR. the host firmware. The implementation only supports SPI NOR.
config SPI_ATMEL_QUADSPI
tristate "Atmel Quad SPI Controller"
depends on ARCH_AT91 || (ARM && COMPILE_TEST)
depends on OF && HAS_IOMEM
help
This enables support for the Quad SPI controller in master mode.
This driver does not support generic SPI. The implementation only
supports SPI NOR.
config SPI_CADENCE_QUADSPI config SPI_CADENCE_QUADSPI
tristate "Cadence Quad SPI controller" tristate "Cadence Quad SPI controller"
depends on OF && (ARM || ARM64 || COMPILE_TEST) depends on OF && (ARM || ARM64 || COMPILE_TEST)

View File

@ -1,7 +1,6 @@
# SPDX-License-Identifier: GPL-2.0 # SPDX-License-Identifier: GPL-2.0
obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o obj-$(CONFIG_MTD_SPI_NOR) += spi-nor.o
obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o obj-$(CONFIG_SPI_ASPEED_SMC) += aspeed-smc.o
obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o
obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o obj-$(CONFIG_SPI_CADENCE_QUADSPI) += cadence-quadspi.o
obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o obj-$(CONFIG_SPI_FSL_QUADSPI) += fsl-quadspi.o
obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o obj-$(CONFIG_SPI_HISI_SFC) += hisi-sfc.o

View File

@ -91,6 +91,15 @@ config SPI_AT91_USART
This selects a driver for the AT91 USART Controller as SPI Master, This selects a driver for the AT91 USART Controller as SPI Master,
present on AT91 and SAMA5 SoC series. present on AT91 and SAMA5 SoC series.
config SPI_ATMEL_QUADSPI
tristate "Atmel Quad SPI Controller"
depends on ARCH_AT91 || (ARM && COMPILE_TEST && !ARCH_EBSA110)
depends on OF && HAS_IOMEM
help
This enables support for the Quad SPI controller in master mode.
This driver does not support generic SPI. The implementation only
supports spi-mem interface.
config SPI_AU1550 config SPI_AU1550
tristate "Au1550/Au1200/Au1300 SPI Controller" tristate "Au1550/Au1200/Au1300 SPI Controller"
depends on MIPS_ALCHEMY depends on MIPS_ALCHEMY

View File

@ -16,6 +16,7 @@ obj-$(CONFIG_SPI_LOOPBACK_TEST) += spi-loopback-test.o
obj-$(CONFIG_SPI_ALTERA) += spi-altera.o obj-$(CONFIG_SPI_ALTERA) += spi-altera.o
obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o obj-$(CONFIG_SPI_ARMADA_3700) += spi-armada-3700.o
obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o obj-$(CONFIG_SPI_ATMEL) += spi-atmel.o
obj-$(CONFIG_SPI_ATMEL_QUADSPI) += atmel-quadspi.o
obj-$(CONFIG_SPI_AT91_USART) += spi-at91-usart.o obj-$(CONFIG_SPI_AT91_USART) += spi-at91-usart.o
obj-$(CONFIG_SPI_ATH79) += spi-ath79.o obj-$(CONFIG_SPI_ATH79) += spi-ath79.o
obj-$(CONFIG_SPI_AU1550) += spi-au1550.o obj-$(CONFIG_SPI_AU1550) += spi-au1550.o

View File

@ -2,8 +2,10 @@
* Driver for Atmel QSPI Controller * Driver for Atmel QSPI Controller
* *
* Copyright (C) 2015 Atmel Corporation * Copyright (C) 2015 Atmel Corporation
* Copyright (C) 2018 Cryptera A/S
* *
* Author: Cyrille Pitchen <cyrille.pitchen@atmel.com> * Author: Cyrille Pitchen <cyrille.pitchen@atmel.com>
* Author: Piotr Bugalski <bugalski.piotr@gmail.com>
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
@ -27,14 +29,10 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/interrupt.h> #include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/spi-nor.h>
#include <linux/platform_data/atmel.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/io.h> #include <linux/io.h>
#include <linux/gpio/consumer.h> #include <linux/spi/spi-mem.h>
/* QSPI register offsets */ /* QSPI register offsets */
#define QSPI_CR 0x0000 /* Control Register */ #define QSPI_CR 0x0000 /* Control Register */
@ -67,7 +65,7 @@
#define QSPI_CR_LASTXFER BIT(24) #define QSPI_CR_LASTXFER BIT(24)
/* Bitfields in QSPI_MR (Mode Register) */ /* Bitfields in QSPI_MR (Mode Register) */
#define QSPI_MR_SSM BIT(0) #define QSPI_MR_SMM BIT(0)
#define QSPI_MR_LLB BIT(1) #define QSPI_MR_LLB BIT(1)
#define QSPI_MR_WDRBT BIT(2) #define QSPI_MR_WDRBT BIT(2)
#define QSPI_MR_SMRM BIT(3) #define QSPI_MR_SMRM BIT(3)
@ -157,33 +155,24 @@ struct atmel_qspi {
struct clk *clk; struct clk *clk;
struct platform_device *pdev; struct platform_device *pdev;
u32 pending; u32 pending;
struct spi_nor nor;
u32 clk_rate;
struct completion cmd_completion; struct completion cmd_completion;
}; };
struct atmel_qspi_command { struct qspi_mode {
union { u8 cmd_buswidth;
struct { u8 addr_buswidth;
u32 instruction:1; u8 data_buswidth;
u32 address:3; u32 config;
u32 mode:1; };
u32 dummy:1;
u32 data:1;
u32 reserved:25;
} bits;
u32 word;
} enable;
u8 instruction;
u8 mode;
u8 num_mode_cycles;
u8 num_dummy_cycles;
u32 address;
size_t buf_len; static const struct qspi_mode sama5d2_qspi_modes[] = {
const void *tx_buf; { 1, 1, 1, QSPI_IFR_WIDTH_SINGLE_BIT_SPI },
void *rx_buf; { 1, 1, 2, QSPI_IFR_WIDTH_DUAL_OUTPUT },
{ 1, 1, 4, QSPI_IFR_WIDTH_QUAD_OUTPUT },
{ 1, 2, 2, QSPI_IFR_WIDTH_DUAL_IO },
{ 1, 4, 4, QSPI_IFR_WIDTH_QUAD_IO },
{ 2, 2, 2, QSPI_IFR_WIDTH_DUAL_CMD },
{ 4, 4, 4, QSPI_IFR_WIDTH_QUAD_CMD },
}; };
/* Register access functions */ /* Register access functions */
@ -197,246 +186,140 @@ static inline void qspi_writel(struct atmel_qspi *aq, u32 reg, u32 value)
writel_relaxed(value, aq->regs + reg); writel_relaxed(value, aq->regs + reg);
} }
static int atmel_qspi_run_transfer(struct atmel_qspi *aq, static inline bool is_compatible(const struct spi_mem_op *op,
const struct atmel_qspi_command *cmd) const struct qspi_mode *mode)
{ {
void __iomem *ahb_mem; if (op->cmd.buswidth != mode->cmd_buswidth)
return false;
/* Then fallback to a PIO transfer (memcpy() DOES NOT work!) */ if (op->addr.nbytes && op->addr.buswidth != mode->addr_buswidth)
ahb_mem = aq->mem; return false;
if (cmd->enable.bits.address)
ahb_mem += cmd->address;
if (cmd->tx_buf)
_memcpy_toio(ahb_mem, cmd->tx_buf, cmd->buf_len);
else
_memcpy_fromio(cmd->rx_buf, ahb_mem, cmd->buf_len);
return 0; if (op->data.nbytes && op->data.buswidth != mode->data_buswidth)
return false;
return true;
} }
#ifdef DEBUG static int find_mode(const struct spi_mem_op *op)
static void atmel_qspi_debug_command(struct atmel_qspi *aq,
const struct atmel_qspi_command *cmd,
u32 ifr)
{ {
u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE]; u32 i;
size_t len = 0;
int i;
if (cmd->enable.bits.instruction) for (i = 0; i < ARRAY_SIZE(sama5d2_qspi_modes); i++)
cmd_buf[len++] = cmd->instruction; if (is_compatible(op, &sama5d2_qspi_modes[i]))
return i;
for (i = cmd->enable.bits.address-1; i >= 0; --i) return -1;
cmd_buf[len++] = (cmd->address >> (i << 3)) & 0xff;
if (cmd->enable.bits.mode)
cmd_buf[len++] = cmd->mode;
if (cmd->enable.bits.dummy) {
int num = cmd->num_dummy_cycles;
switch (ifr & QSPI_IFR_WIDTH_MASK) {
case QSPI_IFR_WIDTH_SINGLE_BIT_SPI:
case QSPI_IFR_WIDTH_DUAL_OUTPUT:
case QSPI_IFR_WIDTH_QUAD_OUTPUT:
num >>= 3;
break;
case QSPI_IFR_WIDTH_DUAL_IO:
case QSPI_IFR_WIDTH_DUAL_CMD:
num >>= 2;
break;
case QSPI_IFR_WIDTH_QUAD_IO:
case QSPI_IFR_WIDTH_QUAD_CMD:
num >>= 1;
break;
default:
return;
}
for (i = 0; i < num; ++i)
cmd_buf[len++] = 0;
}
/* Dump the SPI command */
print_hex_dump(KERN_DEBUG, "qspi cmd: ", DUMP_PREFIX_NONE,
32, 1, cmd_buf, len, false);
#ifdef VERBOSE_DEBUG
/* If verbose debug is enabled, also dump the TX data */
if (cmd->enable.bits.data && cmd->tx_buf)
print_hex_dump(KERN_DEBUG, "qspi tx : ", DUMP_PREFIX_NONE,
32, 1, cmd->tx_buf, cmd->buf_len, false);
#endif
} }
#else
#define atmel_qspi_debug_command(aq, cmd, ifr)
#endif
static int atmel_qspi_run_command(struct atmel_qspi *aq, static bool atmel_qspi_supports_op(struct spi_mem *mem,
const struct atmel_qspi_command *cmd, const struct spi_mem_op *op)
u32 ifr_tfrtyp, enum spi_nor_protocol proto)
{ {
if (find_mode(op) < 0)
return false;
/* special case not supported by hardware */
if (op->addr.nbytes == 2 && op->cmd.buswidth != op->addr.buswidth &&
op->dummy.nbytes == 0)
return false;
return true;
}
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);
int mode;
u32 dummy_cycles = 0;
u32 iar, icr, ifr, sr; u32 iar, icr, ifr, sr;
int err = 0; int err = 0;
iar = 0; iar = 0;
icr = 0; icr = QSPI_ICR_INST(op->cmd.opcode);
ifr = ifr_tfrtyp; ifr = QSPI_IFR_INSTEN;
/* Set the SPI protocol */ qspi_writel(aq, QSPI_MR, QSPI_MR_SMM);
switch (proto) {
case SNOR_PROTO_1_1_1:
ifr |= QSPI_IFR_WIDTH_SINGLE_BIT_SPI;
break;
case SNOR_PROTO_1_1_2: mode = find_mode(op);
ifr |= QSPI_IFR_WIDTH_DUAL_OUTPUT; if (mode < 0)
break; return -ENOTSUPP;
case SNOR_PROTO_1_1_4: ifr |= sama5d2_qspi_modes[mode].config;
ifr |= QSPI_IFR_WIDTH_QUAD_OUTPUT;
break;
case SNOR_PROTO_1_2_2: if (op->dummy.buswidth && op->dummy.nbytes)
ifr |= QSPI_IFR_WIDTH_DUAL_IO; dummy_cycles = op->dummy.nbytes * 8 / op->dummy.buswidth;
break;
case SNOR_PROTO_1_4_4: if (op->addr.buswidth) {
ifr |= QSPI_IFR_WIDTH_QUAD_IO; switch (op->addr.nbytes) {
break; case 0:
case SNOR_PROTO_2_2_2:
ifr |= QSPI_IFR_WIDTH_DUAL_CMD;
break;
case SNOR_PROTO_4_4_4:
ifr |= QSPI_IFR_WIDTH_QUAD_CMD;
break;
default:
return -EINVAL;
}
/* Compute instruction parameters */
if (cmd->enable.bits.instruction) {
icr |= QSPI_ICR_INST(cmd->instruction);
ifr |= QSPI_IFR_INSTEN;
}
/* Compute address parameters */
switch (cmd->enable.bits.address) {
case 4:
ifr |= QSPI_IFR_ADDRL;
/* fall through to the 24bit (3 byte) address case. */
case 3:
iar = (cmd->enable.bits.data) ? 0 : cmd->address;
ifr |= QSPI_IFR_ADDREN;
break;
case 0:
break;
default:
return -EINVAL;
}
/* Compute option parameters */
if (cmd->enable.bits.mode && cmd->num_mode_cycles) {
u32 mode_cycle_bits, mode_bits;
icr |= QSPI_ICR_OPT(cmd->mode);
ifr |= QSPI_IFR_OPTEN;
switch (ifr & QSPI_IFR_WIDTH_MASK) {
case QSPI_IFR_WIDTH_SINGLE_BIT_SPI:
case QSPI_IFR_WIDTH_DUAL_OUTPUT:
case QSPI_IFR_WIDTH_QUAD_OUTPUT:
mode_cycle_bits = 1;
break; break;
case QSPI_IFR_WIDTH_DUAL_IO:
case QSPI_IFR_WIDTH_DUAL_CMD:
mode_cycle_bits = 2;
break;
case QSPI_IFR_WIDTH_QUAD_IO:
case QSPI_IFR_WIDTH_QUAD_CMD:
mode_cycle_bits = 4;
break;
default:
return -EINVAL;
}
mode_bits = cmd->num_mode_cycles * mode_cycle_bits;
switch (mode_bits) {
case 1: case 1:
ifr |= QSPI_IFR_OPTL_1BIT; ifr |= QSPI_IFR_OPTEN | QSPI_IFR_OPTL_8BIT;
icr |= QSPI_ICR_OPT(op->addr.val & 0xff);
break; break;
case 2: case 2:
ifr |= QSPI_IFR_OPTL_2BIT; if (dummy_cycles < 8 / op->addr.buswidth) {
ifr &= ~QSPI_IFR_INSTEN;
ifr |= QSPI_IFR_ADDREN;
iar = (op->cmd.opcode << 16) |
(op->addr.val & 0xffff);
} else {
ifr |= QSPI_IFR_ADDREN;
iar = (op->addr.val << 8) & 0xffffff;
dummy_cycles -= 8 / op->addr.buswidth;
}
break;
case 3:
ifr |= QSPI_IFR_ADDREN;
iar = op->addr.val & 0xffffff;
break; break;
case 4: case 4:
ifr |= QSPI_IFR_OPTL_4BIT; ifr |= QSPI_IFR_ADDREN | QSPI_IFR_ADDRL;
iar = op->addr.val & 0x7ffffff;
break; break;
case 8:
ifr |= QSPI_IFR_OPTL_8BIT;
break;
default: default:
return -EINVAL; return -ENOTSUPP;
} }
} }
/* Set number of dummy cycles */ /* Set number of dummy cycles */
if (cmd->enable.bits.dummy) if (dummy_cycles)
ifr |= QSPI_IFR_NBDUM(cmd->num_dummy_cycles); ifr |= QSPI_IFR_NBDUM(dummy_cycles);
/* Set data enable */ /* Set data enable */
if (cmd->enable.bits.data) { if (op->data.nbytes)
ifr |= QSPI_IFR_DATAEN; ifr |= QSPI_IFR_DATAEN;
/* Special case for Continuous Read Mode */ if (op->data.dir == SPI_MEM_DATA_IN && op->data.nbytes)
if (!cmd->tx_buf && !cmd->rx_buf) ifr |= QSPI_IFR_TFRTYP_TRSFR_READ;
ifr |= QSPI_IFR_CRM; else
} ifr |= QSPI_IFR_TFRTYP_TRSFR_WRITE;
/* Clear pending interrupts */ /* Clear pending interrupts */
(void)qspi_readl(aq, QSPI_SR); (void)qspi_readl(aq, QSPI_SR);
/* Set QSPI Instruction Frame registers */ /* Set QSPI Instruction Frame registers */
atmel_qspi_debug_command(aq, cmd, ifr);
qspi_writel(aq, QSPI_IAR, iar); qspi_writel(aq, QSPI_IAR, iar);
qspi_writel(aq, QSPI_ICR, icr); qspi_writel(aq, QSPI_ICR, icr);
qspi_writel(aq, QSPI_IFR, ifr); qspi_writel(aq, QSPI_IFR, ifr);
/* Skip to the final steps if there is no data */ /* Skip to the final steps if there is no data */
if (!cmd->enable.bits.data) if (op->data.nbytes) {
goto no_data; /* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */
(void)qspi_readl(aq, QSPI_IFR);
/* Dummy read of QSPI_IFR to synchronize APB and AHB accesses */ /* Send/Receive data */
(void)qspi_readl(aq, QSPI_IFR); if (op->data.dir == SPI_MEM_DATA_IN)
_memcpy_fromio(op->data.buf.in,
aq->mem + iar, op->data.nbytes);
else
_memcpy_toio(aq->mem + iar,
op->data.buf.out, op->data.nbytes);
/* Stop here for continuous read */ /* Release the chip-select */
if (!cmd->tx_buf && !cmd->rx_buf) qspi_writel(aq, QSPI_CR, QSPI_CR_LASTXFER);
return 0; }
/* Send/Receive data */
err = atmel_qspi_run_transfer(aq, cmd);
/* Release the chip-select */
qspi_writel(aq, QSPI_CR, QSPI_CR_LASTXFER);
if (err)
return err;
#if defined(DEBUG) && defined(VERBOSE_DEBUG)
/*
* If verbose debug is enabled, also dump the RX data in addition to
* the SPI command previously dumped by atmel_qspi_debug_command()
*/
if (cmd->rx_buf)
print_hex_dump(KERN_DEBUG, "qspi rx : ", DUMP_PREFIX_NONE,
32, 1, cmd->rx_buf, cmd->buf_len, false);
#endif
no_data:
/* Poll INSTRuction End status */ /* Poll INSTRuction End status */
sr = qspi_readl(aq, QSPI_SR); sr = qspi_readl(aq, QSPI_SR);
if ((sr & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED) if ((sr & QSPI_SR_CMD_COMPLETED) == QSPI_SR_CMD_COMPLETED)
@ -454,129 +337,50 @@ no_data:
return err; return err;
} }
static int atmel_qspi_read_reg(struct spi_nor *nor, u8 opcode, const char *atmel_qspi_get_name(struct spi_mem *spimem)
u8 *buf, int len)
{ {
struct atmel_qspi *aq = nor->priv; return dev_name(spimem->spi->dev.parent);
struct atmel_qspi_command cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.enable.bits.instruction = 1;
cmd.enable.bits.data = 1;
cmd.instruction = opcode;
cmd.rx_buf = buf;
cmd.buf_len = len;
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ,
nor->reg_proto);
} }
static int atmel_qspi_write_reg(struct spi_nor *nor, u8 opcode, static const struct spi_controller_mem_ops atmel_qspi_mem_ops = {
u8 *buf, int len) .supports_op = atmel_qspi_supports_op,
{ .exec_op = atmel_qspi_exec_op,
struct atmel_qspi *aq = nor->priv; .get_name = atmel_qspi_get_name
struct atmel_qspi_command cmd; };
memset(&cmd, 0, sizeof(cmd)); static int atmel_qspi_setup(struct spi_device *spi)
cmd.enable.bits.instruction = 1;
cmd.enable.bits.data = (buf != NULL && len > 0);
cmd.instruction = opcode;
cmd.tx_buf = buf;
cmd.buf_len = len;
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
nor->reg_proto);
}
static ssize_t atmel_qspi_write(struct spi_nor *nor, loff_t to, size_t len,
const u_char *write_buf)
{
struct atmel_qspi *aq = nor->priv;
struct atmel_qspi_command cmd;
ssize_t ret;
memset(&cmd, 0, sizeof(cmd));
cmd.enable.bits.instruction = 1;
cmd.enable.bits.address = nor->addr_width;
cmd.enable.bits.data = 1;
cmd.instruction = nor->program_opcode;
cmd.address = (u32)to;
cmd.tx_buf = write_buf;
cmd.buf_len = len;
ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE_MEM,
nor->write_proto);
return (ret < 0) ? ret : len;
}
static int atmel_qspi_erase(struct spi_nor *nor, loff_t offs)
{
struct atmel_qspi *aq = nor->priv;
struct atmel_qspi_command cmd;
memset(&cmd, 0, sizeof(cmd));
cmd.enable.bits.instruction = 1;
cmd.enable.bits.address = nor->addr_width;
cmd.instruction = nor->erase_opcode;
cmd.address = (u32)offs;
return atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_WRITE,
nor->reg_proto);
}
static ssize_t atmel_qspi_read(struct spi_nor *nor, loff_t from, size_t len,
u_char *read_buf)
{
struct atmel_qspi *aq = nor->priv;
struct atmel_qspi_command cmd;
u8 num_mode_cycles, num_dummy_cycles;
ssize_t ret;
if (nor->read_dummy >= 2) {
num_mode_cycles = 2;
num_dummy_cycles = nor->read_dummy - 2;
} else {
num_mode_cycles = nor->read_dummy;
num_dummy_cycles = 0;
}
memset(&cmd, 0, sizeof(cmd));
cmd.enable.bits.instruction = 1;
cmd.enable.bits.address = nor->addr_width;
cmd.enable.bits.mode = (num_mode_cycles > 0);
cmd.enable.bits.dummy = (num_dummy_cycles > 0);
cmd.enable.bits.data = 1;
cmd.instruction = nor->read_opcode;
cmd.address = (u32)from;
cmd.mode = 0xff; /* This value prevents from entering the 0-4-4 mode */
cmd.num_mode_cycles = num_mode_cycles;
cmd.num_dummy_cycles = num_dummy_cycles;
cmd.rx_buf = read_buf;
cmd.buf_len = len;
ret = atmel_qspi_run_command(aq, &cmd, QSPI_IFR_TFRTYP_TRSFR_READ_MEM,
nor->read_proto);
return (ret < 0) ? ret : len;
}
static int atmel_qspi_init(struct atmel_qspi *aq)
{ {
struct spi_controller *ctrl = spi->master;
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
unsigned long src_rate; unsigned long src_rate;
u32 mr, scr, scbr; u32 scr, scbr;
/* Reset the QSPI controller */ if (ctrl->busy)
qspi_writel(aq, QSPI_CR, QSPI_CR_SWRST); return -EBUSY;
/* Set the QSPI controller in Serial Memory Mode */ if (!spi->max_speed_hz)
mr = QSPI_MR_NBBITS(8) | QSPI_MR_SSM; return -EINVAL;
qspi_writel(aq, QSPI_MR, mr);
src_rate = clk_get_rate(aq->clk); src_rate = clk_get_rate(aq->clk);
if (!src_rate) if (!src_rate)
return -EINVAL; return -EINVAL;
/* Compute the QSPI baudrate */ /* Compute the QSPI baudrate */
scbr = DIV_ROUND_UP(src_rate, aq->clk_rate); scbr = DIV_ROUND_UP(src_rate, spi->max_speed_hz);
if (scbr > 0) if (scbr > 0)
scbr--; scbr--;
scr = QSPI_SCR_SCBR(scbr); scr = QSPI_SCR_SCBR(scbr);
qspi_writel(aq, QSPI_SCR, scr); qspi_writel(aq, QSPI_SCR, scr);
return 0;
}
static int atmel_qspi_init(struct atmel_qspi *aq)
{
/* Reset the QSPI controller */
qspi_writel(aq, QSPI_CR, QSPI_CR_SWRST);
/* Enable the QSPI controller */ /* Enable the QSPI controller */
qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIEN); qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIEN);
@ -604,38 +408,25 @@ static irqreturn_t atmel_qspi_interrupt(int irq, void *dev_id)
static int atmel_qspi_probe(struct platform_device *pdev) static int atmel_qspi_probe(struct platform_device *pdev)
{ {
const struct spi_nor_hwcaps hwcaps = { struct spi_controller *ctrl;
.mask = SNOR_HWCAPS_READ |
SNOR_HWCAPS_READ_FAST |
SNOR_HWCAPS_READ_1_1_2 |
SNOR_HWCAPS_READ_1_2_2 |
SNOR_HWCAPS_READ_2_2_2 |
SNOR_HWCAPS_READ_1_1_4 |
SNOR_HWCAPS_READ_1_4_4 |
SNOR_HWCAPS_READ_4_4_4 |
SNOR_HWCAPS_PP |
SNOR_HWCAPS_PP_1_1_4 |
SNOR_HWCAPS_PP_1_4_4 |
SNOR_HWCAPS_PP_4_4_4,
};
struct device_node *child, *np = pdev->dev.of_node;
struct atmel_qspi *aq; struct atmel_qspi *aq;
struct resource *res; struct resource *res;
struct spi_nor *nor;
struct mtd_info *mtd;
int irq, err = 0; int irq, err = 0;
if (of_get_child_count(np) != 1) ctrl = spi_alloc_master(&pdev->dev, sizeof(*aq));
return -ENODEV; if (!ctrl)
child = of_get_next_child(np, NULL); return -ENOMEM;
aq = devm_kzalloc(&pdev->dev, sizeof(*aq), GFP_KERNEL); ctrl->mode_bits = SPI_RX_DUAL | SPI_RX_QUAD | SPI_TX_DUAL | SPI_TX_QUAD;
if (!aq) { ctrl->setup = atmel_qspi_setup;
err = -ENOMEM; ctrl->bus_num = -1;
goto exit; ctrl->mem_ops = &atmel_qspi_mem_ops;
} ctrl->num_chipselect = 1;
ctrl->dev.of_node = pdev->dev.of_node;
platform_set_drvdata(pdev, ctrl);
aq = spi_controller_get_devdata(ctrl);
platform_set_drvdata(pdev, aq);
init_completion(&aq->cmd_completion); init_completion(&aq->cmd_completion);
aq->pdev = pdev; aq->pdev = pdev;
@ -684,54 +475,30 @@ static int atmel_qspi_probe(struct platform_device *pdev)
if (err) if (err)
goto disable_clk; goto disable_clk;
/* Setup the spi-nor */
nor = &aq->nor;
mtd = &nor->mtd;
nor->dev = &pdev->dev;
spi_nor_set_flash_node(nor, child);
nor->priv = aq;
mtd->priv = nor;
nor->read_reg = atmel_qspi_read_reg;
nor->write_reg = atmel_qspi_write_reg;
nor->read = atmel_qspi_read;
nor->write = atmel_qspi_write;
nor->erase = atmel_qspi_erase;
err = of_property_read_u32(child, "spi-max-frequency", &aq->clk_rate);
if (err < 0)
goto disable_clk;
err = atmel_qspi_init(aq); err = atmel_qspi_init(aq);
if (err) if (err)
goto disable_clk; goto disable_clk;
err = spi_nor_scan(nor, NULL, &hwcaps); err = spi_register_controller(ctrl);
if (err) if (err)
goto disable_clk; goto disable_clk;
err = mtd_device_register(mtd, NULL, 0);
if (err)
goto disable_clk;
of_node_put(child);
return 0; return 0;
disable_clk: disable_clk:
clk_disable_unprepare(aq->clk); clk_disable_unprepare(aq->clk);
exit: exit:
of_node_put(child); spi_controller_put(ctrl);
return err; return err;
} }
static int atmel_qspi_remove(struct platform_device *pdev) static int atmel_qspi_remove(struct platform_device *pdev)
{ {
struct atmel_qspi *aq = platform_get_drvdata(pdev); struct spi_controller *ctrl = platform_get_drvdata(pdev);
struct atmel_qspi *aq = spi_controller_get_devdata(ctrl);
mtd_device_unregister(&aq->nor.mtd); spi_unregister_controller(ctrl);
qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIDIS); qspi_writel(aq, QSPI_CR, QSPI_CR_QSPIDIS);
clk_disable_unprepare(aq->clk); clk_disable_unprepare(aq->clk);
return 0; return 0;
@ -777,5 +544,6 @@ static struct platform_driver atmel_qspi_driver = {
module_platform_driver(atmel_qspi_driver); module_platform_driver(atmel_qspi_driver);
MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>"); MODULE_AUTHOR("Cyrille Pitchen <cyrille.pitchen@atmel.com>");
MODULE_AUTHOR("Piotr Bugalski <bugalski.piotr@gmail.com");
MODULE_DESCRIPTION("Atmel QSPI Controller driver"); MODULE_DESCRIPTION("Atmel QSPI Controller driver");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");

View File

@ -149,7 +149,7 @@ static bool spi_mem_default_supports_op(struct spi_mem *mem,
spi_check_buswidth_req(mem, op->dummy.buswidth, true)) spi_check_buswidth_req(mem, op->dummy.buswidth, true))
return false; return false;
if (op->data.nbytes && if (op->data.dir != SPI_MEM_NO_DATA &&
spi_check_buswidth_req(mem, op->data.buswidth, spi_check_buswidth_req(mem, op->data.buswidth,
op->data.dir == SPI_MEM_DATA_OUT)) op->data.dir == SPI_MEM_DATA_OUT))
return false; return false;
@ -220,6 +220,44 @@ bool spi_mem_supports_op(struct spi_mem *mem, const struct spi_mem_op *op)
} }
EXPORT_SYMBOL_GPL(spi_mem_supports_op); EXPORT_SYMBOL_GPL(spi_mem_supports_op);
static int spi_mem_access_start(struct spi_mem *mem)
{
struct spi_controller *ctlr = mem->spi->controller;
/*
* Flush the message queue before executing our SPI memory
* operation to prevent preemption of regular SPI transfers.
*/
spi_flush_queue(ctlr);
if (ctlr->auto_runtime_pm) {
int ret;
ret = pm_runtime_get_sync(ctlr->dev.parent);
if (ret < 0) {
dev_err(&ctlr->dev, "Failed to power device: %d\n",
ret);
return ret;
}
}
mutex_lock(&ctlr->bus_lock_mutex);
mutex_lock(&ctlr->io_mutex);
return 0;
}
static void spi_mem_access_end(struct spi_mem *mem)
{
struct spi_controller *ctlr = mem->spi->controller;
mutex_unlock(&ctlr->io_mutex);
mutex_unlock(&ctlr->bus_lock_mutex);
if (ctlr->auto_runtime_pm)
pm_runtime_put(ctlr->dev.parent);
}
/** /**
* spi_mem_exec_op() - Execute a memory operation * spi_mem_exec_op() - Execute a memory operation
* @mem: the SPI memory * @mem: the SPI memory
@ -249,30 +287,13 @@ int spi_mem_exec_op(struct spi_mem *mem, const struct spi_mem_op *op)
return -ENOTSUPP; return -ENOTSUPP;
if (ctlr->mem_ops) { if (ctlr->mem_ops) {
/* ret = spi_mem_access_start(mem);
* Flush the message queue before executing our SPI memory if (ret)
* operation to prevent preemption of regular SPI transfers. return ret;
*/
spi_flush_queue(ctlr);
if (ctlr->auto_runtime_pm) {
ret = pm_runtime_get_sync(ctlr->dev.parent);
if (ret < 0) {
dev_err(&ctlr->dev,
"Failed to power device: %d\n",
ret);
return ret;
}
}
mutex_lock(&ctlr->bus_lock_mutex);
mutex_lock(&ctlr->io_mutex);
ret = ctlr->mem_ops->exec_op(mem, op); ret = ctlr->mem_ops->exec_op(mem, op);
mutex_unlock(&ctlr->io_mutex);
mutex_unlock(&ctlr->bus_lock_mutex);
if (ctlr->auto_runtime_pm) spi_mem_access_end(mem);
pm_runtime_put(ctlr->dev.parent);
/* /*
* Some controllers only optimize specific paths (typically the * Some controllers only optimize specific paths (typically the
@ -418,6 +439,210 @@ int spi_mem_adjust_op_size(struct spi_mem *mem, struct spi_mem_op *op)
} }
EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size); EXPORT_SYMBOL_GPL(spi_mem_adjust_op_size);
static ssize_t spi_mem_no_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf)
{
struct spi_mem_op op = desc->info.op_tmpl;
int ret;
op.addr.val = desc->info.offset + offs;
op.data.buf.in = buf;
op.data.nbytes = len;
ret = spi_mem_adjust_op_size(desc->mem, &op);
if (ret)
return ret;
ret = spi_mem_exec_op(desc->mem, &op);
if (ret)
return ret;
return op.data.nbytes;
}
static ssize_t spi_mem_no_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf)
{
struct spi_mem_op op = desc->info.op_tmpl;
int ret;
op.addr.val = desc->info.offset + offs;
op.data.buf.out = buf;
op.data.nbytes = len;
ret = spi_mem_adjust_op_size(desc->mem, &op);
if (ret)
return ret;
ret = spi_mem_exec_op(desc->mem, &op);
if (ret)
return ret;
return op.data.nbytes;
}
/**
* spi_mem_dirmap_create() - Create a direct mapping descriptor
* @mem: SPI mem device this direct mapping should be created for
* @info: direct mapping information
*
* This function is creating a direct mapping descriptor which can then be used
* to access the memory using spi_mem_dirmap_read() or spi_mem_dirmap_write().
* If the SPI controller driver does not support direct mapping, this function
* fallback to an implementation using spi_mem_exec_op(), so that the caller
* doesn't have to bother implementing a fallback on his own.
*
* Return: a valid pointer in case of success, and ERR_PTR() otherwise.
*/
struct spi_mem_dirmap_desc *
spi_mem_dirmap_create(struct spi_mem *mem,
const struct spi_mem_dirmap_info *info)
{
struct spi_controller *ctlr = mem->spi->controller;
struct spi_mem_dirmap_desc *desc;
int ret = -ENOTSUPP;
/* Make sure the number of address cycles is between 1 and 8 bytes. */
if (!info->op_tmpl.addr.nbytes || info->op_tmpl.addr.nbytes > 8)
return ERR_PTR(-EINVAL);
/* data.dir should either be SPI_MEM_DATA_IN or SPI_MEM_DATA_OUT. */
if (info->op_tmpl.data.dir == SPI_MEM_NO_DATA)
return ERR_PTR(-EINVAL);
desc = kzalloc(sizeof(*desc), GFP_KERNEL);
if (!desc)
return ERR_PTR(-ENOMEM);
desc->mem = mem;
desc->info = *info;
if (ctlr->mem_ops && ctlr->mem_ops->dirmap_create)
ret = ctlr->mem_ops->dirmap_create(desc);
if (ret) {
desc->nodirmap = true;
if (!spi_mem_supports_op(desc->mem, &desc->info.op_tmpl))
ret = -ENOTSUPP;
else
ret = 0;
}
if (ret) {
kfree(desc);
return ERR_PTR(ret);
}
return desc;
}
EXPORT_SYMBOL_GPL(spi_mem_dirmap_create);
/**
* spi_mem_dirmap_destroy() - Destroy a direct mapping descriptor
* @desc: the direct mapping descriptor to destroy
* @info: direct mapping information
*
* This function destroys a direct mapping descriptor previously created by
* spi_mem_dirmap_create().
*/
void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc)
{
struct spi_controller *ctlr = desc->mem->spi->controller;
if (!desc->nodirmap && ctlr->mem_ops && ctlr->mem_ops->dirmap_destroy)
ctlr->mem_ops->dirmap_destroy(desc);
}
EXPORT_SYMBOL_GPL(spi_mem_dirmap_destroy);
/**
* spi_mem_dirmap_dirmap_read() - Read data through a direct mapping
* @desc: direct mapping descriptor
* @offs: offset to start reading from. Note that this is not an absolute
* offset, but the offset within the direct mapping which already has
* its own offset
* @len: length in bytes
* @buf: destination buffer. This buffer must be DMA-able
*
* This function reads data from a memory device using a direct mapping
* previously instantiated with spi_mem_dirmap_create().
*
* Return: the amount of data read from the memory device or a negative error
* code. Note that the returned size might be smaller than @len, and the caller
* is responsible for calling spi_mem_dirmap_read() again when that happens.
*/
ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf)
{
struct spi_controller *ctlr = desc->mem->spi->controller;
ssize_t ret;
if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_IN)
return -EINVAL;
if (!len)
return 0;
if (desc->nodirmap) {
ret = spi_mem_no_dirmap_read(desc, offs, len, buf);
} else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_read) {
ret = spi_mem_access_start(desc->mem);
if (ret)
return ret;
ret = ctlr->mem_ops->dirmap_read(desc, offs, len, buf);
spi_mem_access_end(desc->mem);
} else {
ret = -ENOTSUPP;
}
return ret;
}
EXPORT_SYMBOL_GPL(spi_mem_dirmap_read);
/**
* spi_mem_dirmap_dirmap_write() - Write data through a direct mapping
* @desc: direct mapping descriptor
* @offs: offset to start writing from. Note that this is not an absolute
* offset, but the offset within the direct mapping which already has
* its own offset
* @len: length in bytes
* @buf: source buffer. This buffer must be DMA-able
*
* This function writes data to a memory device using a direct mapping
* previously instantiated with spi_mem_dirmap_create().
*
* Return: the amount of data written to the memory device or a negative error
* code. Note that the returned size might be smaller than @len, and the caller
* is responsible for calling spi_mem_dirmap_write() again when that happens.
*/
ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf)
{
struct spi_controller *ctlr = desc->mem->spi->controller;
ssize_t ret;
if (desc->info.op_tmpl.data.dir != SPI_MEM_DATA_OUT)
return -EINVAL;
if (!len)
return 0;
if (desc->nodirmap) {
ret = spi_mem_no_dirmap_write(desc, offs, len, buf);
} else if (ctlr->mem_ops && ctlr->mem_ops->dirmap_write) {
ret = spi_mem_access_start(desc->mem);
if (ret)
return ret;
ret = ctlr->mem_ops->dirmap_write(desc, offs, len, buf);
spi_mem_access_end(desc->mem);
} else {
ret = -ENOTSUPP;
}
return ret;
}
EXPORT_SYMBOL_GPL(spi_mem_dirmap_write);
static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv) static inline struct spi_mem_driver *to_spi_mem_drv(struct device_driver *drv)
{ {
return container_of(drv, struct spi_mem_driver, spidrv.driver); return container_of(drv, struct spi_mem_driver, spidrv.driver);

View File

@ -57,10 +57,12 @@
/** /**
* enum spi_mem_data_dir - describes the direction of a SPI memory data * enum spi_mem_data_dir - describes the direction of a SPI memory data
* transfer from the controller perspective * transfer from the controller perspective
* @SPI_MEM_NO_DATA: no data transferred
* @SPI_MEM_DATA_IN: data coming from the SPI memory * @SPI_MEM_DATA_IN: data coming from the SPI memory
* @SPI_MEM_DATA_OUT: data sent the SPI memory * @SPI_MEM_DATA_OUT: data sent to the SPI memory
*/ */
enum spi_mem_data_dir { enum spi_mem_data_dir {
SPI_MEM_NO_DATA,
SPI_MEM_DATA_IN, SPI_MEM_DATA_IN,
SPI_MEM_DATA_OUT, SPI_MEM_DATA_OUT,
}; };
@ -122,6 +124,49 @@ struct spi_mem_op {
.data = __data, \ .data = __data, \
} }
/**
* struct spi_mem_dirmap_info - Direct mapping information
* @op_tmpl: operation template that should be used by the direct mapping when
* the memory device is accessed
* @offset: absolute offset this direct mapping is pointing to
* @length: length in byte of this direct mapping
*
* These information are used by the controller specific implementation to know
* the portion of memory that is directly mapped and the spi_mem_op that should
* be used to access the device.
* A direct mapping is only valid for one direction (read or write) and this
* direction is directly encoded in the ->op_tmpl.data.dir field.
*/
struct spi_mem_dirmap_info {
struct spi_mem_op op_tmpl;
u64 offset;
u64 length;
};
/**
* struct spi_mem_dirmap_desc - Direct mapping descriptor
* @mem: the SPI memory device this direct mapping is attached to
* @info: information passed at direct mapping creation time
* @nodirmap: set to 1 if the SPI controller does not implement
* ->mem_ops->dirmap_create() or when this function returned an
* error. If @nodirmap is true, all spi_mem_dirmap_{read,write}()
* calls will use spi_mem_exec_op() to access the memory. This is a
* degraded mode that allows spi_mem drivers to use the same code
* no matter whether the controller supports direct mapping or not
* @priv: field pointing to controller specific data
*
* Common part of a direct mapping descriptor. This object is created by
* spi_mem_dirmap_create() and controller implementation of ->create_dirmap()
* can create/attach direct mapping resources to the descriptor in the ->priv
* field.
*/
struct spi_mem_dirmap_desc {
struct spi_mem *mem;
struct spi_mem_dirmap_info info;
unsigned int nodirmap;
void *priv;
};
/** /**
* struct spi_mem - describes a SPI memory device * struct spi_mem - describes a SPI memory device
* @spi: the underlying SPI device * @spi: the underlying SPI device
@ -177,10 +222,32 @@ static inline void *spi_mem_get_drvdata(struct spi_mem *mem)
* Note that if the implementation of this function allocates memory * Note that if the implementation of this function allocates memory
* dynamically, then it should do so with devm_xxx(), as we don't * dynamically, then it should do so with devm_xxx(), as we don't
* have a ->free_name() function. * have a ->free_name() function.
* @dirmap_create: create a direct mapping descriptor that can later be used to
* access the memory device. This method is optional
* @dirmap_destroy: destroy a memory descriptor previous created by
* ->dirmap_create()
* @dirmap_read: read data from the memory device using the direct mapping
* created by ->dirmap_create(). The function can return less
* data than requested (for example when the request is crossing
* the currently mapped area), and the caller of
* spi_mem_dirmap_read() is responsible for calling it again in
* this case.
* @dirmap_write: write data to the memory device using the direct mapping
* created by ->dirmap_create(). The function can return less
* data than requested (for example when the request is crossing
* the currently mapped area), and the caller of
* spi_mem_dirmap_write() is responsible for calling it again in
* this case.
* *
* This interface should be implemented by SPI controllers providing an * This interface should be implemented by SPI controllers providing an
* high-level interface to execute SPI memory operation, which is usually the * high-level interface to execute SPI memory operation, which is usually the
* case for QSPI controllers. * case for QSPI controllers.
*
* Note on ->dirmap_{read,write}(): drivers should avoid accessing the direct
* mapping from the CPU because doing that can stall the CPU waiting for the
* SPI mem transaction to finish, and this will make real-time maintainers
* unhappy and might make your system less reactive. Instead, drivers should
* use DMA to access this direct mapping.
*/ */
struct spi_controller_mem_ops { struct spi_controller_mem_ops {
int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op); int (*adjust_op_size)(struct spi_mem *mem, struct spi_mem_op *op);
@ -189,6 +256,12 @@ struct spi_controller_mem_ops {
int (*exec_op)(struct spi_mem *mem, int (*exec_op)(struct spi_mem *mem,
const struct spi_mem_op *op); const struct spi_mem_op *op);
const char *(*get_name)(struct spi_mem *mem); const char *(*get_name)(struct spi_mem *mem);
int (*dirmap_create)(struct spi_mem_dirmap_desc *desc);
void (*dirmap_destroy)(struct spi_mem_dirmap_desc *desc);
ssize_t (*dirmap_read)(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf);
ssize_t (*dirmap_write)(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf);
}; };
/** /**
@ -249,6 +322,15 @@ int spi_mem_exec_op(struct spi_mem *mem,
const char *spi_mem_get_name(struct spi_mem *mem); const char *spi_mem_get_name(struct spi_mem *mem);
struct spi_mem_dirmap_desc *
spi_mem_dirmap_create(struct spi_mem *mem,
const struct spi_mem_dirmap_info *info);
void spi_mem_dirmap_destroy(struct spi_mem_dirmap_desc *desc);
ssize_t spi_mem_dirmap_read(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, void *buf);
ssize_t spi_mem_dirmap_write(struct spi_mem_dirmap_desc *desc,
u64 offs, size_t len, const void *buf);
int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv, int spi_mem_driver_register_with_owner(struct spi_mem_driver *drv,
struct module *owner); struct module *owner);