[U-Boot] [PATCH] mtd: add altera quadspi driver

Marek Vasut marex at denx.de
Wed Nov 4 17:18:35 CET 2015


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, &regs->mem_op);
> > +		stat = readl(&regs->isr);
> > +		if (stat & QUADSPI_ISR_ILLEGAL_ERASE) {
> > +			/* erase failed, sector might be protected */
> > +			debug("erase %08x fail %x\n", sect, stat);
> > +			writel(stat, &regs->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(&regs->isr);
> > +	if (stat & QUADSPI_ISR_ILLEGAL_WRITE) {
> > +		/* write failed, sector might be protected */
> > +		debug("write %08x fail %x\n", pos, stat);
> > +		writel(stat, &regs->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.


More information about the U-Boot mailing list