138a0f36eSThomas Chou /* 238a0f36eSThomas Chou * Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw> 338a0f36eSThomas Chou * 438a0f36eSThomas Chou * SPDX-License-Identifier: GPL-2.0+ 538a0f36eSThomas Chou */ 638a0f36eSThomas Chou 738a0f36eSThomas Chou #include <common.h> 838a0f36eSThomas Chou #include <dm.h> 938a0f36eSThomas Chou #include <errno.h> 1038a0f36eSThomas Chou #include <fdt_support.h> 1138a0f36eSThomas Chou #include <flash.h> 1238a0f36eSThomas Chou #include <mtd.h> 1338a0f36eSThomas Chou #include <asm/io.h> 1438a0f36eSThomas Chou 1538a0f36eSThomas Chou DECLARE_GLOBAL_DATA_PTR; 1638a0f36eSThomas Chou 17*421f306fSThomas Chou /* The STATUS register */ 18*421f306fSThomas Chou #define QUADSPI_SR_BP0 BIT(2) 19*421f306fSThomas Chou #define QUADSPI_SR_BP1 BIT(3) 20*421f306fSThomas Chou #define QUADSPI_SR_BP2 BIT(4) 21*421f306fSThomas Chou #define QUADSPI_SR_BP2_0 GENMASK(4, 2) 22*421f306fSThomas Chou #define QUADSPI_SR_BP3 BIT(6) 23*421f306fSThomas Chou #define QUADSPI_SR_TB BIT(5) 24*421f306fSThomas Chou 2538a0f36eSThomas Chou /* 2638a0f36eSThomas Chou * The QUADSPI_MEM_OP register is used to do memory protect and erase operations 2738a0f36eSThomas Chou */ 2838a0f36eSThomas Chou #define QUADSPI_MEM_OP_BULK_ERASE 0x00000001 2938a0f36eSThomas Chou #define QUADSPI_MEM_OP_SECTOR_ERASE 0x00000002 3038a0f36eSThomas Chou #define QUADSPI_MEM_OP_SECTOR_PROTECT 0x00000003 3138a0f36eSThomas Chou 3238a0f36eSThomas Chou /* 3338a0f36eSThomas Chou * The QUADSPI_ISR register is used to determine whether an invalid write or 3438a0f36eSThomas Chou * erase operation trigerred an interrupt 3538a0f36eSThomas Chou */ 3638a0f36eSThomas Chou #define QUADSPI_ISR_ILLEGAL_ERASE BIT(0) 3738a0f36eSThomas Chou #define QUADSPI_ISR_ILLEGAL_WRITE BIT(1) 3838a0f36eSThomas Chou 3938a0f36eSThomas Chou struct altera_qspi_regs { 4038a0f36eSThomas Chou u32 rd_status; 4138a0f36eSThomas Chou u32 rd_sid; 4238a0f36eSThomas Chou u32 rd_rdid; 4338a0f36eSThomas Chou u32 mem_op; 4438a0f36eSThomas Chou u32 isr; 4538a0f36eSThomas Chou u32 imr; 4638a0f36eSThomas Chou u32 chip_select; 4738a0f36eSThomas Chou }; 4838a0f36eSThomas Chou 4938a0f36eSThomas Chou struct altera_qspi_platdata { 5038a0f36eSThomas Chou struct altera_qspi_regs *regs; 5138a0f36eSThomas Chou void *base; 5238a0f36eSThomas Chou unsigned long size; 5338a0f36eSThomas Chou }; 5438a0f36eSThomas Chou 5538a0f36eSThomas Chou flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* FLASH chips info */ 5638a0f36eSThomas Chou 57*421f306fSThomas Chou static void altera_qspi_get_locked_range(struct mtd_info *mtd, loff_t *ofs, 58*421f306fSThomas Chou uint64_t *len); 59*421f306fSThomas Chou 6038a0f36eSThomas Chou void flash_print_info(flash_info_t *info) 6138a0f36eSThomas Chou { 62*421f306fSThomas Chou struct mtd_info *mtd = info->mtd; 63*421f306fSThomas Chou loff_t ofs; 64*421f306fSThomas Chou u64 len; 65*421f306fSThomas Chou 6638a0f36eSThomas Chou printf("Altera QSPI flash Size: %ld MB in %d Sectors\n", 6738a0f36eSThomas Chou info->size >> 20, info->sector_count); 68*421f306fSThomas Chou altera_qspi_get_locked_range(mtd, &ofs, &len); 69*421f306fSThomas Chou printf(" %08lX +%lX", info->start[0], info->size); 70*421f306fSThomas Chou if (len) { 71*421f306fSThomas Chou printf(", protected %08llX +%llX", 72*421f306fSThomas Chou info->start[0] + ofs, len); 73*421f306fSThomas Chou } 74*421f306fSThomas Chou putc('\n'); 7538a0f36eSThomas Chou } 7638a0f36eSThomas Chou 7738a0f36eSThomas Chou int flash_erase(flash_info_t *info, int s_first, int s_last) 7838a0f36eSThomas Chou { 7938a0f36eSThomas Chou struct mtd_info *mtd = info->mtd; 8038a0f36eSThomas Chou struct erase_info instr; 8138a0f36eSThomas Chou int ret; 8238a0f36eSThomas Chou 8338a0f36eSThomas Chou memset(&instr, 0, sizeof(instr)); 8438a0f36eSThomas Chou instr.addr = mtd->erasesize * s_first; 8538a0f36eSThomas Chou instr.len = mtd->erasesize * (s_last + 1 - s_first); 8638a0f36eSThomas Chou ret = mtd_erase(mtd, &instr); 8738a0f36eSThomas Chou if (ret) 8838a0f36eSThomas Chou return ERR_NOT_ERASED; 8938a0f36eSThomas Chou 9038a0f36eSThomas Chou return 0; 9138a0f36eSThomas Chou } 9238a0f36eSThomas Chou 9338a0f36eSThomas Chou int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) 9438a0f36eSThomas Chou { 9538a0f36eSThomas Chou struct mtd_info *mtd = info->mtd; 9638a0f36eSThomas Chou struct udevice *dev = mtd->dev; 9738a0f36eSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 9838a0f36eSThomas Chou ulong base = (ulong)pdata->base; 9938a0f36eSThomas Chou loff_t to = addr - base; 10038a0f36eSThomas Chou size_t retlen; 10138a0f36eSThomas Chou int ret; 10238a0f36eSThomas Chou 10338a0f36eSThomas Chou ret = mtd_write(mtd, to, cnt, &retlen, src); 10438a0f36eSThomas Chou if (ret) 10538a0f36eSThomas Chou return ERR_NOT_ERASED; 10638a0f36eSThomas Chou 10738a0f36eSThomas Chou return 0; 10838a0f36eSThomas Chou } 10938a0f36eSThomas Chou 11038a0f36eSThomas Chou unsigned long flash_init(void) 11138a0f36eSThomas Chou { 11238a0f36eSThomas Chou struct udevice *dev; 11338a0f36eSThomas Chou 11438a0f36eSThomas Chou /* probe every MTD device */ 11538a0f36eSThomas Chou for (uclass_first_device(UCLASS_MTD, &dev); 11638a0f36eSThomas Chou dev; 11738a0f36eSThomas Chou uclass_next_device(&dev)) { 11838a0f36eSThomas Chou } 11938a0f36eSThomas Chou 12038a0f36eSThomas Chou return flash_info[0].size; 12138a0f36eSThomas Chou } 12238a0f36eSThomas Chou 12338a0f36eSThomas Chou static int altera_qspi_erase(struct mtd_info *mtd, struct erase_info *instr) 12438a0f36eSThomas Chou { 12538a0f36eSThomas Chou struct udevice *dev = mtd->dev; 12638a0f36eSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 12738a0f36eSThomas Chou struct altera_qspi_regs *regs = pdata->regs; 12838a0f36eSThomas Chou size_t addr = instr->addr; 12938a0f36eSThomas Chou size_t len = instr->len; 13038a0f36eSThomas Chou size_t end = addr + len; 13138a0f36eSThomas Chou u32 sect; 13238a0f36eSThomas Chou u32 stat; 13338a0f36eSThomas Chou 13438a0f36eSThomas Chou instr->state = MTD_ERASING; 13538a0f36eSThomas Chou addr &= ~(mtd->erasesize - 1); /* get lower aligned address */ 13638a0f36eSThomas Chou while (addr < end) { 13738a0f36eSThomas Chou sect = addr / mtd->erasesize; 13838a0f36eSThomas Chou sect <<= 8; 13938a0f36eSThomas Chou sect |= QUADSPI_MEM_OP_SECTOR_ERASE; 14038a0f36eSThomas Chou debug("erase %08x\n", sect); 14138a0f36eSThomas Chou writel(sect, ®s->mem_op); 14238a0f36eSThomas Chou stat = readl(®s->isr); 14338a0f36eSThomas Chou if (stat & QUADSPI_ISR_ILLEGAL_ERASE) { 14438a0f36eSThomas Chou /* erase failed, sector might be protected */ 14538a0f36eSThomas Chou debug("erase %08x fail %x\n", sect, stat); 14638a0f36eSThomas Chou writel(stat, ®s->isr); /* clear isr */ 14738a0f36eSThomas Chou instr->state = MTD_ERASE_FAILED; 14838a0f36eSThomas Chou return -EIO; 14938a0f36eSThomas Chou } 15038a0f36eSThomas Chou addr += mtd->erasesize; 15138a0f36eSThomas Chou } 15238a0f36eSThomas Chou instr->state = MTD_ERASE_DONE; 15338a0f36eSThomas Chou mtd_erase_callback(instr); 15438a0f36eSThomas Chou 15538a0f36eSThomas Chou return 0; 15638a0f36eSThomas Chou } 15738a0f36eSThomas Chou 15838a0f36eSThomas Chou static int altera_qspi_read(struct mtd_info *mtd, loff_t from, size_t len, 15938a0f36eSThomas Chou size_t *retlen, u_char *buf) 16038a0f36eSThomas Chou { 16138a0f36eSThomas Chou struct udevice *dev = mtd->dev; 16238a0f36eSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 16338a0f36eSThomas Chou 16438a0f36eSThomas Chou memcpy_fromio(buf, pdata->base + from, len); 16538a0f36eSThomas Chou *retlen = len; 16638a0f36eSThomas Chou 16738a0f36eSThomas Chou return 0; 16838a0f36eSThomas Chou } 16938a0f36eSThomas Chou 17038a0f36eSThomas Chou static int altera_qspi_write(struct mtd_info *mtd, loff_t to, size_t len, 17138a0f36eSThomas Chou size_t *retlen, const u_char *buf) 17238a0f36eSThomas Chou { 17338a0f36eSThomas Chou struct udevice *dev = mtd->dev; 17438a0f36eSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 17538a0f36eSThomas Chou struct altera_qspi_regs *regs = pdata->regs; 17638a0f36eSThomas Chou u32 stat; 17738a0f36eSThomas Chou 17838a0f36eSThomas Chou memcpy_toio(pdata->base + to, buf, len); 17938a0f36eSThomas Chou /* check whether write triggered a illegal write interrupt */ 18038a0f36eSThomas Chou stat = readl(®s->isr); 18138a0f36eSThomas Chou if (stat & QUADSPI_ISR_ILLEGAL_WRITE) { 18238a0f36eSThomas Chou /* write failed, sector might be protected */ 18338a0f36eSThomas Chou debug("write fail %x\n", stat); 18438a0f36eSThomas Chou writel(stat, ®s->isr); /* clear isr */ 18538a0f36eSThomas Chou return -EIO; 18638a0f36eSThomas Chou } 18738a0f36eSThomas Chou *retlen = len; 18838a0f36eSThomas Chou 18938a0f36eSThomas Chou return 0; 19038a0f36eSThomas Chou } 19138a0f36eSThomas Chou 19238a0f36eSThomas Chou static void altera_qspi_sync(struct mtd_info *mtd) 19338a0f36eSThomas Chou { 19438a0f36eSThomas Chou } 19538a0f36eSThomas Chou 196*421f306fSThomas Chou static void altera_qspi_get_locked_range(struct mtd_info *mtd, loff_t *ofs, 197*421f306fSThomas Chou uint64_t *len) 198*421f306fSThomas Chou { 199*421f306fSThomas Chou struct udevice *dev = mtd->dev; 200*421f306fSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 201*421f306fSThomas Chou struct altera_qspi_regs *regs = pdata->regs; 202*421f306fSThomas Chou int shift0 = ffs(QUADSPI_SR_BP2_0) - 1; 203*421f306fSThomas Chou int shift3 = ffs(QUADSPI_SR_BP3) - 1 - 3; 204*421f306fSThomas Chou u32 stat = readl(®s->rd_status); 205*421f306fSThomas Chou unsigned pow = ((stat & QUADSPI_SR_BP2_0) >> shift0) | 206*421f306fSThomas Chou ((stat & QUADSPI_SR_BP3) >> shift3); 207*421f306fSThomas Chou 208*421f306fSThomas Chou *ofs = 0; 209*421f306fSThomas Chou *len = 0; 210*421f306fSThomas Chou if (pow) { 211*421f306fSThomas Chou *len = mtd->erasesize << (pow - 1); 212*421f306fSThomas Chou if (*len > mtd->size) 213*421f306fSThomas Chou *len = mtd->size; 214*421f306fSThomas Chou if (!(stat & QUADSPI_SR_TB)) 215*421f306fSThomas Chou *ofs = mtd->size - *len; 216*421f306fSThomas Chou } 217*421f306fSThomas Chou } 218*421f306fSThomas Chou 219*421f306fSThomas Chou static int altera_qspi_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 220*421f306fSThomas Chou { 221*421f306fSThomas Chou struct udevice *dev = mtd->dev; 222*421f306fSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 223*421f306fSThomas Chou struct altera_qspi_regs *regs = pdata->regs; 224*421f306fSThomas Chou u32 sector_start, sector_end; 225*421f306fSThomas Chou u32 num_sectors; 226*421f306fSThomas Chou u32 mem_op; 227*421f306fSThomas Chou u32 sr_bp; 228*421f306fSThomas Chou u32 sr_tb; 229*421f306fSThomas Chou 230*421f306fSThomas Chou num_sectors = mtd->size / mtd->erasesize; 231*421f306fSThomas Chou sector_start = ofs / mtd->erasesize; 232*421f306fSThomas Chou sector_end = (ofs + len) / mtd->erasesize; 233*421f306fSThomas Chou 234*421f306fSThomas Chou if (sector_start >= num_sectors / 2) { 235*421f306fSThomas Chou sr_bp = fls(num_sectors - 1 - sector_start) + 1; 236*421f306fSThomas Chou sr_tb = 0; 237*421f306fSThomas Chou } else if (sector_end < num_sectors / 2) { 238*421f306fSThomas Chou sr_bp = fls(sector_end) + 1; 239*421f306fSThomas Chou sr_tb = 1; 240*421f306fSThomas Chou } else { 241*421f306fSThomas Chou sr_bp = 15; 242*421f306fSThomas Chou sr_tb = 0; 243*421f306fSThomas Chou } 244*421f306fSThomas Chou 245*421f306fSThomas Chou mem_op = (sr_tb << 12) | (sr_bp << 8); 246*421f306fSThomas Chou mem_op |= QUADSPI_MEM_OP_SECTOR_PROTECT; 247*421f306fSThomas Chou debug("lock %08x\n", mem_op); 248*421f306fSThomas Chou writel(mem_op, ®s->mem_op); 249*421f306fSThomas Chou 250*421f306fSThomas Chou return 0; 251*421f306fSThomas Chou } 252*421f306fSThomas Chou 253*421f306fSThomas Chou static int altera_qspi_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) 254*421f306fSThomas Chou { 255*421f306fSThomas Chou struct udevice *dev = mtd->dev; 256*421f306fSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 257*421f306fSThomas Chou struct altera_qspi_regs *regs = pdata->regs; 258*421f306fSThomas Chou u32 mem_op; 259*421f306fSThomas Chou 260*421f306fSThomas Chou mem_op = QUADSPI_MEM_OP_SECTOR_PROTECT; 261*421f306fSThomas Chou debug("unlock %08x\n", mem_op); 262*421f306fSThomas Chou writel(mem_op, ®s->mem_op); 263*421f306fSThomas Chou 264*421f306fSThomas Chou return 0; 265*421f306fSThomas Chou } 266*421f306fSThomas Chou 26738a0f36eSThomas Chou static int altera_qspi_probe(struct udevice *dev) 26838a0f36eSThomas Chou { 26938a0f36eSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 27038a0f36eSThomas Chou struct altera_qspi_regs *regs = pdata->regs; 27138a0f36eSThomas Chou unsigned long base = (unsigned long)pdata->base; 27238a0f36eSThomas Chou struct mtd_info *mtd; 27338a0f36eSThomas Chou flash_info_t *flash = &flash_info[0]; 27438a0f36eSThomas Chou u32 rdid; 27538a0f36eSThomas Chou int i; 27638a0f36eSThomas Chou 27738a0f36eSThomas Chou rdid = readl(®s->rd_rdid); 27838a0f36eSThomas Chou debug("rdid %x\n", rdid); 27938a0f36eSThomas Chou 28038a0f36eSThomas Chou mtd = dev_get_uclass_priv(dev); 28138a0f36eSThomas Chou mtd->dev = dev; 28238a0f36eSThomas Chou mtd->name = "nor0"; 28338a0f36eSThomas Chou mtd->type = MTD_NORFLASH; 28438a0f36eSThomas Chou mtd->flags = MTD_CAP_NORFLASH; 28538a0f36eSThomas Chou mtd->size = 1 << ((rdid & 0xff) - 6); 28638a0f36eSThomas Chou mtd->writesize = 1; 28738a0f36eSThomas Chou mtd->writebufsize = mtd->writesize; 28838a0f36eSThomas Chou mtd->_erase = altera_qspi_erase; 28938a0f36eSThomas Chou mtd->_read = altera_qspi_read; 29038a0f36eSThomas Chou mtd->_write = altera_qspi_write; 29138a0f36eSThomas Chou mtd->_sync = altera_qspi_sync; 292*421f306fSThomas Chou mtd->_lock = altera_qspi_lock; 293*421f306fSThomas Chou mtd->_unlock = altera_qspi_unlock; 29438a0f36eSThomas Chou mtd->numeraseregions = 0; 29538a0f36eSThomas Chou mtd->erasesize = 0x10000; 29638a0f36eSThomas Chou if (add_mtd_device(mtd)) 29738a0f36eSThomas Chou return -ENOMEM; 29838a0f36eSThomas Chou 29938a0f36eSThomas Chou flash->mtd = mtd; 30038a0f36eSThomas Chou flash->size = mtd->size; 30138a0f36eSThomas Chou flash->sector_count = mtd->size / mtd->erasesize; 30238a0f36eSThomas Chou flash->flash_id = rdid; 30338a0f36eSThomas Chou flash->start[0] = base; 30438a0f36eSThomas Chou for (i = 1; i < flash->sector_count; i++) 30538a0f36eSThomas Chou flash->start[i] = flash->start[i - 1] + mtd->erasesize; 30638a0f36eSThomas Chou gd->bd->bi_flashstart = base; 30738a0f36eSThomas Chou 30838a0f36eSThomas Chou return 0; 30938a0f36eSThomas Chou } 31038a0f36eSThomas Chou 31138a0f36eSThomas Chou static int altera_qspi_ofdata_to_platdata(struct udevice *dev) 31238a0f36eSThomas Chou { 31338a0f36eSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 31438a0f36eSThomas Chou void *blob = (void *)gd->fdt_blob; 31538a0f36eSThomas Chou int node = dev->of_offset; 31638a0f36eSThomas Chou const char *list, *end; 31738a0f36eSThomas Chou const fdt32_t *cell; 31838a0f36eSThomas Chou void *base; 31938a0f36eSThomas Chou unsigned long addr, size; 32038a0f36eSThomas Chou int parent, addrc, sizec; 32138a0f36eSThomas Chou int len, idx; 32238a0f36eSThomas Chou 32338a0f36eSThomas Chou /* 32438a0f36eSThomas Chou * decode regs. there are multiple reg tuples, and they need to 32538a0f36eSThomas Chou * match with reg-names. 32638a0f36eSThomas Chou */ 32738a0f36eSThomas Chou parent = fdt_parent_offset(blob, node); 32838a0f36eSThomas Chou of_bus_default_count_cells(blob, parent, &addrc, &sizec); 32938a0f36eSThomas Chou list = fdt_getprop(blob, node, "reg-names", &len); 33038a0f36eSThomas Chou if (!list) 33138a0f36eSThomas Chou return -ENOENT; 33238a0f36eSThomas Chou end = list + len; 33338a0f36eSThomas Chou cell = fdt_getprop(blob, node, "reg", &len); 33438a0f36eSThomas Chou if (!cell) 33538a0f36eSThomas Chou return -ENOENT; 33638a0f36eSThomas Chou idx = 0; 33738a0f36eSThomas Chou while (list < end) { 33838a0f36eSThomas Chou addr = fdt_translate_address((void *)blob, 33938a0f36eSThomas Chou node, cell + idx); 34038a0f36eSThomas Chou size = fdt_addr_to_cpu(cell[idx + addrc]); 3418ed38fa5SThomas Chou base = map_physmem(addr, size, MAP_NOCACHE); 34238a0f36eSThomas Chou len = strlen(list); 34338a0f36eSThomas Chou if (strcmp(list, "avl_csr") == 0) { 34438a0f36eSThomas Chou pdata->regs = base; 34538a0f36eSThomas Chou } else if (strcmp(list, "avl_mem") == 0) { 34638a0f36eSThomas Chou pdata->base = base; 34738a0f36eSThomas Chou pdata->size = size; 34838a0f36eSThomas Chou } 34938a0f36eSThomas Chou idx += addrc + sizec; 35038a0f36eSThomas Chou list += (len + 1); 35138a0f36eSThomas Chou } 35238a0f36eSThomas Chou 35338a0f36eSThomas Chou return 0; 35438a0f36eSThomas Chou } 35538a0f36eSThomas Chou 35638a0f36eSThomas Chou static const struct udevice_id altera_qspi_ids[] = { 35738a0f36eSThomas Chou { .compatible = "altr,quadspi-1.0" }, 35838a0f36eSThomas Chou {} 35938a0f36eSThomas Chou }; 36038a0f36eSThomas Chou 36138a0f36eSThomas Chou U_BOOT_DRIVER(altera_qspi) = { 36238a0f36eSThomas Chou .name = "altera_qspi", 36338a0f36eSThomas Chou .id = UCLASS_MTD, 36438a0f36eSThomas Chou .of_match = altera_qspi_ids, 36538a0f36eSThomas Chou .ofdata_to_platdata = altera_qspi_ofdata_to_platdata, 36638a0f36eSThomas Chou .platdata_auto_alloc_size = sizeof(struct altera_qspi_platdata), 36738a0f36eSThomas Chou .probe = altera_qspi_probe, 36838a0f36eSThomas Chou }; 369