[PATCH v2 5/5] mtd: atmel-quadspi: add driver for Atmel QSPI controller
Marek Vasut
marex at denx.de
Mon Jul 27 03:53:52 PDT 2015
On Monday, July 27, 2015 at 10:41:37 AM, Cyrille Pitchen wrote:
> Hi Marek,
>
> Le 22/07/2015 15:50, Marek Vasut a écrit :
> > On Wednesday, July 22, 2015 at 03:17:10 PM, Cyrille Pitchen wrote:
> >> This driver add support to the new Atmel QSPI controller embedded into
> >> sama5d2x SoCs. It expects a NOR memory to be connected to the QSPI
> >> controller.
> >>
> >> Signed-off-by: Cyrille Pitchen <cyrille.pitchen at atmel.com>
> >> ---
> >
> > [...]
> >
> >> +/* QSPI register offsets */
> >> +#define QSPI_CR 0x0000 /* Control Register */
> >> +#define QSPI_MR 0x0004 /* Mode Register */
> >> +#define QSPI_RD 0x0008 /* Receive Data Register */
> >> +#define QSPI_TD 0x000c /* Transmit Data Register */
> >> +#define QSPI_SR 0x0010 /* Status Register */
> >> +#define QSPI_IER 0x0014 /* Interrupt Enable Register */
> >> +#define QSPI_IDR 0x0018 /* Interrupt Disable Register */
> >> +#define QSPI_IMR 0x001c /* Interrupt Mask Register */
> >> +#define QSPI_SCR 0x0020 /* Serial Clock Register */
> >> +
> >> +#define QSPI_IAR 0x0030 /* Instruction Address Register */
> >> +#define QSPI_ICR 0x0034 /* Instruction Code Register */
> >> +#define QSPI_IFR 0x0038 /* Instruction Frame Register */
> >> +
> >> +#define QSPI_SMR 0x0040 /* Scrambling Mode Register */
> >> +#define QSPI_SKR 0x0044 /* Scrambling Key Register */
> >> +
> >> +#define QSPI_WPMR 0x00E4 /* Write Protection Mode Register */
> >> +#define QSPI_WPSR 0x00E8 /* Write Protection Status Register */
> >> +
> >> +#define QSPI_VERSION 0x00FC /* Version Register */
> >> +
> >> +/* Bitfields in QSPI_CR (Control Register) */
> >> +#define QSPI_CR_QSPIEN_OFFSET 0
> >> +#define QSPI_CR_QSPIEN_SIZE 1
> >> +#define QSPI_CR_QSPIDIS_OFFSET 1
> >> +#define QSPI_CR_QSPIDIS_SIZE 1
> >> +#define QSPI_CR_SWRST_OFFSET 7
> >> +#define QSPI_CR_SWRST_SIZE 1
> >> +#define QSPI_CR_LASTXFER_OFFSET 24
> >> +#define QSPI_CR_LASTXFER_SIZE 1
> >> +
> >> +/* Bitfields in QSPI_MR (Mode Register) */
> >> +#define QSPI_MR_SSM_OFFSET 0
> >> +#define QSPI_MR_SSM_SIZE 1
> >> +#define QSPI_MR_LLB_OFFSET 1
> >> +#define QSPI_MR_LLB_SIZE 1
> >> +#define QSPI_MR_WDRBT_OFFSET 2
> >> +#define QSPI_MR_WDRBT_SIZE 1
> >> +#define QPSI_MR_SMRM_OFFSET 3
> >> +#define QSPI_MR_SMRM_SIZE 1
> >> +#define QSPI_MR_CSMODE_OFFSET 4
> >> +#define QSPI_MR_CSMODE_SIZE 2
> >> +#define QSPI_MR_NBBITS_OFFSET 8
> >> +#define QSPI_MR_NBBITS_SIZE 4
> >> +#define QSPI_MR_NBBITS_8_BIT 0
> >> +#define QSPI_MR_NBBITS_9_BIT 1
> >> +#define QSPI_MR_NBBITS_10_BIT 2
> >> +#define QSPI_MR_NBBITS_11_BIT 3
> >> +#define QSPI_MR_NBBITS_12_BIT 4
> >> +#define QSPI_MR_NBBITS_13_BIT 5
> >> +#define QSPI_MR_NBBITS_14_BIT 6
> >> +#define QSPI_MR_NBBITS_15_BIT 7
> >> +#define QSPI_MR_NBBITS_16_BIT 8
> >
> > You might want to turn this into something like:
> >
> > #define QSPI_NR_NBBITS(n) ((n) - 8)
>
> done in the next series
>
> >> +#define QSPI_MR_DLYBCT_OFFSET 16
> >> +#define QSPI_MR_DLYBCT_SIZE 8
> >> +#define QSPI_MR_DLYCS_OFFSET 24
> >> +#define QSPI_MR_DLYCS_SIZE 8
> >
> > [...]
> >
> >> +/* Bitfields in QSPI_IFR (Instruction Frame Register) */
> >> +#define QSPI_IFR_WIDTH_OFFSET 0
> >> +#define QSPI_IFR_WIDTH_SIZE 3
> >> +#define QSPI_IFR_WIDTH_SINGLE_BIT_SPI 0
> >> +#define QSPI_IFR_WIDTH_DUAL_OUTPUT 1
> >> +#define QSPI_IFR_WIDTH_QUAD_OUTPUT 2
> >> +#define QSPI_IFR_WIDTH_DUAL_IO 3
> >> +#define QSPI_IFR_WIDTH_QUAD_IO 4
> >> +#define QSPI_IFR_WIDTH_DUAL_CMD 5
> >> +#define QSPI_IFR_WIDTH_QUAD_CMD 6
> >
> > Please use #define[SPACE] instead of inconsistent #define[TAB]
>
> done in the next series. I also use BIT and GENMASK macros as much as
> possible to define the register bit fields.
>
> > [...]
> >
> >> +/* Bit manipulation macros */
> >> +#define QSPI_BIT(name) \
> >> + (1 << QSPI_##name##_OFFSET)
> >> +#define QSPI_BF(name, value) \
> >> + (((value) & ((1 << QSPI_##name##_SIZE) - 1)) << QSPI_##name##_OFFSET)
> >> +#define QSPI_BFEXT(name, value) \
> >> + (((value) >> QSPI_##name##_OFFSET) & ((1 << QSPI_##name##_SIZE) - 1))
> >> +#define QSPI_BFINS(name, value, old) \
> >> + (((old) & ~(((1 << QSPI_##name##_SIZE) - 1) << QSPI_##name##_OFFSET))
> >> \ + | QSPI_BF(name, value))
> >> +
> >> +/* Register access macros */
> >> +#define qspi_readl(port, reg) \
> >> + readl_relaxed((port)->regs + QSPI_##reg)
> >> +#define qspi_writel(port, reg, value) \
> >> + writel_relaxed((value), (port)->regs + QSPI_##reg)
> >> +
> >> +#define qspi_readw(port, reg) \
> >> + readw_relaxed((port)->regs + QSPI_##reg)
> >> +#define qspi_writew(port, reg, value) \
> >> + writew_relaxed((value), (port)->regs + QSPI_##reg)
> >> +
> >> +#define qspi_readb(port, reg) \
> >> + readb_relaxed((port)->regs + QSPI_##reg)
> >> +#define qspi_writeb(port, reg, value) \
> >> + writeb_relaxed((value), (port)->regs + QSPI_##reg)
> >
> > I cannot say I'd be very fond of those preprocessor concatenations. Can't
> > you do something about them? It's really hard to look for symbols which
> > are in fact result of this horrible macro voodoo .
>
> I agree with you. I did it this way at first to make it consistent with
> other Atmel drivers which use such macros but we tend to remove them
> progressively as they prevent from using ctags for instance.
>
> >> +struct atmel_qspi {
> >> + void __iomem *regs;
> >> + void __iomem *mem;
> >> + dma_addr_t phys_addr;
> >> + struct dma_chan *chan;
> >> + struct clk *clk;
> >> + struct platform_device *pdev;
> >> + u32 ifr_width;
> >> + u32 pending;
> >> +
> >> + struct mtd_info mtd;
> >> + struct spi_nor nor;
> >> + u32 clk_rate;
> >> + struct completion completion;
> >> +
> >> +#ifdef DEBUG
> >> + u8 last_instruction;
> >> +#endif
> >> +};
> >
> > [...]
> >
> >> +#ifdef DEBUG
> >> +static void atmel_qspi_debug_command(struct atmel_qspi *aq,
> >> + const struct atmel_qspi_command *cmd)
> >> +{
> >> + u8 cmd_buf[SPI_NOR_MAX_CMD_SIZE];
> >> + size_t len = 0;
> >> + int i;
> >> +
> >> + if (cmd->enable.bits.instruction) {
> >> + if (aq->last_instruction == cmd->instruction)
> >> + return;
> >> + aq->last_instruction = cmd->instruction;
> >> + }
> >> +
> >> + if (cmd->enable.bits.instruction)
> >> + cmd_buf[len++] = cmd->instruction;
> >> +
> >> + for (i = cmd->enable.bits.address-1; i >= 0; --i)
> >> + 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 (aq->ifr_width) {
> >> + 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;
> >> + }
> >> +
> >> + print_hex_dump(KERN_DEBUG, "qspi cmd: ", DUMP_PREFIX_NONE,
> >> + 32, 1, cmd_buf, len, false);
> >> +
> >> +#ifdef VERBOSE_DEBUG
> >
> > The print_hex_dump() is already KERN_DEBUG, so I don't think there's any
> > need to introduce yet another preprocessor check here.
>
> Indeed, there is only one debug level for printk() and print_hex_dump():
> KERN_DEBUG. However I've used the DEBUG and VERBOSE_DEBUG macros to have
> two levels of debug output. When the DEBUG macro is defined the driver
> prints the QSPI command. When both the DEBUG and VERBOSE_DEBUG macro are
> defined, the driver also prints the RX or TX data following the commands.
> Depending on the command to debug, data can be usefull, as for the READ ID
> command, or really annoying as for big Fast Read commands.
>
> If it's fine with you, I'd rather keep these two debug levels.
I'm not the one to decide, but it does indeed make sense.
Thanks a lot for doing the cleanups !
More information about the linux-mtd
mailing list