On Wednesday, November 04, 2015 at 04:56:10 PM, Chin Liang See wrote: > On Tue, 2015-11-03 at 21:22 +0800, tho...@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 <tho...@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 <tho...@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. _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot