[U-Boot] [PATCH] mtd: add altera quadspi driver
Marek Vasut
marex at denx.de
Thu Nov 5 03:53:09 CET 2015
On Thursday, November 05, 2015 at 03:49:18 AM, Chin Liang See wrote:
> Hi Marek,
>
> On Wed, 2015-11-04 at 10:18 +0000, marex at denx.de wrote:
> > On Wednesday, November 04, 2015 at 04:56:10 PM, Chin Liang See wrote:
> > > On Tue, 2015-11-03 at 21:22 +0800, thomas at wytron.com.tw wrote:
> > > > Add Altera Generic Quad SPI Controller support. The controller
> > > > converts SPI NOR flash to parallel flash interface. So it is
> > > > not like other SPI flash, but rather like CFI flash.
> > > >
> > > > Signed-off-by: Thomas Chou <thomas at wytron.com.tw>
> > > > ---
> > > >
> > > > doc/device-tree-bindings/mtd/altera_qspi.txt | 35 +++
> > > > drivers/mtd/Kconfig | 9 +
> > > > drivers/mtd/Makefile | 1 +
> > > > drivers/mtd/altera_qspi.c | 312
> > > > +++++++++++++++++++++++++++ 4 files changed, 357 insertions(+)
> > > > create mode 100644 doc/device-tree-bindings/mtd/altera_qspi.txt
> > > > create mode 100644 drivers/mtd/altera_qspi.c
> > > > ...
> > > >
> > > > diff --git a/drivers/mtd/altera_qspi.c b/drivers/mtd/altera_qspi.c
> > > > new file mode 100644
> > > > index 0000000..06bc53e
> > > > --- /dev/null
> > > > +++ b/drivers/mtd/altera_qspi.c
> > > > @@ -0,0 +1,312 @@
> > > > +/*
> > > > + * Copyright (C) 2015 Thomas Chou <thomas at wytron.com.tw>
> > > > + *
> > > > + * SPDX-License-Identifier: GPL-2.0+
> > > > + */
> > > > +
> > > > +#include <common.h>
> > > > +#include <dm.h>
> > > > +#include <errno.h>
> > > > +#include <fdt_support.h>
> > > > +#include <flash.h>
> > > > +#include <mtd.h>
> > > > +#include <asm/io.h>
> > > > +
> > > > +DECLARE_GLOBAL_DATA_PTR;
> > > > +
> > > > +/*
> > > > + * The QUADSPI_MEM_OP register is used to do memory protect and
> > > > erase operations + */
> > > > +#define QUADSPI_MEM_OP_BULK_ERASE 0x00000001
> > > > +#define QUADSPI_MEM_OP_SECTOR_ERASE 0x00000002
> > > > +#define QUADSPI_MEM_OP_SECTOR_PROTECT 0x00000003
> > > > +
> > > > +/*
> > > > + * The QUADSPI_ISR register is used to determine whether an invalid
> > > > write or + * erase operation trigerred an interrupt
> > > > + */
> > > > +#define QUADSPI_ISR_ILLEGAL_ERASE BIT(0)
> > > > +#define QUADSPI_ISR_ILLEGAL_WRITE BIT(1)
> > > > +
> > > > +struct altera_qspi_regs {
> > > > + u32 rd_status;
> > > > + u32 rd_sid;
> > > > + u32 rd_rdid;
> > > > + u32 mem_op;
> > > > + u32 isr;
> > > > + u32 imr;
> > > > + u32 chip_select;
> > > > +};
> > > > +
> > > > +struct altera_qspi_platdata {
> > > > + struct altera_qspi_regs *regs;
> > > > + void *base;
> > > > + unsigned long size;
> > > > +};
> > > > +
> > > > +flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* FLASH chips
> >
> > info
> >
> > > > */ +
> > > > +void flash_print_info(flash_info_t *info)
> > > > +{
> > > > + printf("Altera QSPI flash Size: %ld MB in %d Sectors\n",
> > > > + info->size >> 20, info->sector_count);
> > > > +}
> > > > +
> > > > +int flash_erase(flash_info_t *info, int s_first, int s_last)
> > > > +{
> > > > + struct mtd_info *mtd = info->mtd;
> > > > + struct erase_info instr;
> > > > + int ret;
> > > > +
> > > > + memset(&instr, 0, sizeof(instr));
> > > > + instr.addr = mtd->erasesize * s_first;
> > > > + instr.len = mtd->erasesize * (s_last + 1 - s_first);
> > > > + ret = mtd_erase(mtd, &instr);
> > > > + if (ret)
> > > > + return ERR_NOT_ERASED;
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong
> > > > cnt) +{
> > > > + struct mtd_info *mtd = info->mtd;
> > > > + struct udevice *dev = mtd->dev;
> > > > + struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
> > > > + ulong base = (ulong)pdata->base;
> > > > + loff_t to = addr - base;
> > > > + size_t retlen;
> > > > + int ret;
> > > > +
> > > > + ret = mtd_write(mtd, to, cnt, &retlen, src);
> > > > + if (ret)
> > > > + return ERR_NOT_ERASED;
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +unsigned long flash_init(void)
> > > > +{
> > > > + struct udevice *dev;
> > > > +
> > > > + /* probe every MTD device */
> > > > + for (uclass_first_device(UCLASS_MTD, &dev);
> > > > + dev;
> > > > + uclass_next_device(&dev)) {
> > > > + }
> > > > +
> > > > + return flash_info[0].size;
> > > > +}
> > > > +
> > > > +static int altera_qspi_erase(struct mtd_info *mtd, struct erase_info
> > > > *instr) +{
> > > > + struct udevice *dev = mtd->dev;
> > > > + struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
> > > > + struct altera_qspi_regs *regs = pdata->regs;
> > > > + size_t addr = instr->addr;
> > > > + size_t len = instr->len;
> > > > + size_t end = addr + len;
> > > > + u32 sect;
> > > > + u32 stat;
> > > > +
> > > > + instr->state = MTD_ERASING;
> > > > + addr &= ~(mtd->erasesize - 1); /* get lower aligned address */
> > > > + while (addr < end) {
> > > > + sect = addr / mtd->erasesize;
> > > > + sect <<= 8;
> > > > + sect |= QUADSPI_MEM_OP_SECTOR_ERASE;
> > > > + debug("erase %08x\n", sect);
> > > > + writel(sect, ®s->mem_op);
> > > > + stat = readl(®s->isr);
> > > > + if (stat & QUADSPI_ISR_ILLEGAL_ERASE) {
> > > > + /* erase failed, sector might be protected */
> > > > + debug("erase %08x fail %x\n", sect, stat);
> > > > + writel(stat, ®s->isr); /* clear isr */
> > > > + instr->state = MTD_ERASE_FAILED;
> > > > + return -EIO;
> > > > + }
> > > > + addr += mtd->erasesize;
> > > > + }
> > > > + instr->state = MTD_ERASE_DONE;
> > > > + mtd_erase_callback(instr);
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int altera_qspi_read(struct mtd_info *mtd, loff_t from,
> > > > size_t len, + size_t *retlen, u_char *buf)
> > > > +{
> > > > + struct udevice *dev = mtd->dev;
> > > > + struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
> > > > +
> > > > + memcpy(buf, pdata->base + from, len);
> > > > + *retlen = len;
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static inline u32 add_byte(u32 data, u8 byte, int shift)
> > > > +{
> > > > + data &= ~(0xff << shift);
> > > > + data |= byte << shift;
> > > > + return data;
> > > > +}
> > > > +
> > > > +static int altera_qspi_write_word(struct mtd_info *mtd, loff_t to,
> > > > + u32 data)
> > > > +{
> > > > + struct udevice *dev = mtd->dev;
> > > > + struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
> > > > + struct altera_qspi_regs *regs = pdata->regs;
> > > > + u32 pos = (u32)to;
> > > > + u32 stat;
> > > > +
> > > > + /* write to flash 32 bits at a time */
> > > > + writel(data, pdata->base + pos);
> > > > + /* check whether write triggered a illegal write interrupt */
> > > > + stat = readl(®s->isr);
> > > > + if (stat & QUADSPI_ISR_ILLEGAL_WRITE) {
> > > > + /* write failed, sector might be protected */
> > > > + debug("write %08x fail %x\n", pos, stat);
> > > > + writel(stat, ®s->isr); /* clear isr */
> > > > + return -EIO;
> > > > + }
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > > > +static int altera_qspi_write(struct mtd_info *mtd, loff_t to, size_t
> > > > len, + size_t *retlen, const u_char *buf)
> > > > +{
> > > > + const u_char *end = buf + len;
> > > > + unsigned shift;
> > > > + u32 data;
> > > > + int ret;
> > > > +
> > > > + shift = (to & (sizeof(u32) - 1)) * 8; /* first shift to add byte
*/
> > > > + to &= ~(sizeof(u32) - 1); /* get lower aligned address */
> > > > + while (buf < end) {
> > > > + data = 0xffffffff; /* pad data */
> > > > + while (buf < end && shift < 32) {
> > > > + /* add byte from buf */
> > > > + data = add_byte(data, *buf++, shift);
> > > > + shift += 8;
> > > > + }
> > > > + ret = altera_qspi_write_word(mtd, to, data);
> > > > + if (ret)
> > > > + return ret;
> > > > + to += sizeof(u32);
> > > > + shift = 0;
> > > > + }
> > > > + *retlen = len;
> > > > +
> > > > + return 0;
> > > > +}
> > > > +
> > >
> > > Hi Thomas,
> > >
> > > Thanks for the patch.
> > >
> > > I notice you are writing in word style which might have concern in
> > > performance. As the burst count can go up to 64, we can write larger
> > > data through memcpy. This will avoid redundancy of data header (opcode
> > > + address + dummy).
> >
> > You cannot do that, memcpy works on memory while write*() operators work
> > on I/O. You should use readsl() and friends then.
>
> Actually I am thinking to take advantage the cache fill and dump. But
> after rethinking, this might limit some of the use case as we want the
> driver to support NIOS II without cache. With that, just ignore this
> comment for now.
I'm not sure I want to ask for details here. I think we're reading data from
some sort of IO device, so we should just use readl() or readsl() to read
them out (and write*() for the other direction). I don't think cache operations
can be involved in any way. Correct me if I'm wrong please.
> But your comment lead to the fact that the read part is now using
> memcpy. Thomas needs to fix that to use the readl :)
Uhm, I don't think I understand this remark, sorry. I never suggested to use
memcpy() in this entire thread, did I ?
More information about the U-Boot
mailing list