1*38a0f36eSThomas Chou /* 2*38a0f36eSThomas Chou * Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw> 3*38a0f36eSThomas Chou * 4*38a0f36eSThomas Chou * SPDX-License-Identifier: GPL-2.0+ 5*38a0f36eSThomas Chou */ 6*38a0f36eSThomas Chou 7*38a0f36eSThomas Chou #include <common.h> 8*38a0f36eSThomas Chou #include <dm.h> 9*38a0f36eSThomas Chou #include <errno.h> 10*38a0f36eSThomas Chou #include <fdt_support.h> 11*38a0f36eSThomas Chou #include <flash.h> 12*38a0f36eSThomas Chou #include <mtd.h> 13*38a0f36eSThomas Chou #include <asm/io.h> 14*38a0f36eSThomas Chou 15*38a0f36eSThomas Chou DECLARE_GLOBAL_DATA_PTR; 16*38a0f36eSThomas Chou 17*38a0f36eSThomas Chou /* 18*38a0f36eSThomas Chou * The QUADSPI_MEM_OP register is used to do memory protect and erase operations 19*38a0f36eSThomas Chou */ 20*38a0f36eSThomas Chou #define QUADSPI_MEM_OP_BULK_ERASE 0x00000001 21*38a0f36eSThomas Chou #define QUADSPI_MEM_OP_SECTOR_ERASE 0x00000002 22*38a0f36eSThomas Chou #define QUADSPI_MEM_OP_SECTOR_PROTECT 0x00000003 23*38a0f36eSThomas Chou 24*38a0f36eSThomas Chou /* 25*38a0f36eSThomas Chou * The QUADSPI_ISR register is used to determine whether an invalid write or 26*38a0f36eSThomas Chou * erase operation trigerred an interrupt 27*38a0f36eSThomas Chou */ 28*38a0f36eSThomas Chou #define QUADSPI_ISR_ILLEGAL_ERASE BIT(0) 29*38a0f36eSThomas Chou #define QUADSPI_ISR_ILLEGAL_WRITE BIT(1) 30*38a0f36eSThomas Chou 31*38a0f36eSThomas Chou struct altera_qspi_regs { 32*38a0f36eSThomas Chou u32 rd_status; 33*38a0f36eSThomas Chou u32 rd_sid; 34*38a0f36eSThomas Chou u32 rd_rdid; 35*38a0f36eSThomas Chou u32 mem_op; 36*38a0f36eSThomas Chou u32 isr; 37*38a0f36eSThomas Chou u32 imr; 38*38a0f36eSThomas Chou u32 chip_select; 39*38a0f36eSThomas Chou }; 40*38a0f36eSThomas Chou 41*38a0f36eSThomas Chou struct altera_qspi_platdata { 42*38a0f36eSThomas Chou struct altera_qspi_regs *regs; 43*38a0f36eSThomas Chou void *base; 44*38a0f36eSThomas Chou unsigned long size; 45*38a0f36eSThomas Chou }; 46*38a0f36eSThomas Chou 47*38a0f36eSThomas Chou flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; /* FLASH chips info */ 48*38a0f36eSThomas Chou 49*38a0f36eSThomas Chou void flash_print_info(flash_info_t *info) 50*38a0f36eSThomas Chou { 51*38a0f36eSThomas Chou printf("Altera QSPI flash Size: %ld MB in %d Sectors\n", 52*38a0f36eSThomas Chou info->size >> 20, info->sector_count); 53*38a0f36eSThomas Chou } 54*38a0f36eSThomas Chou 55*38a0f36eSThomas Chou int flash_erase(flash_info_t *info, int s_first, int s_last) 56*38a0f36eSThomas Chou { 57*38a0f36eSThomas Chou struct mtd_info *mtd = info->mtd; 58*38a0f36eSThomas Chou struct erase_info instr; 59*38a0f36eSThomas Chou int ret; 60*38a0f36eSThomas Chou 61*38a0f36eSThomas Chou memset(&instr, 0, sizeof(instr)); 62*38a0f36eSThomas Chou instr.addr = mtd->erasesize * s_first; 63*38a0f36eSThomas Chou instr.len = mtd->erasesize * (s_last + 1 - s_first); 64*38a0f36eSThomas Chou ret = mtd_erase(mtd, &instr); 65*38a0f36eSThomas Chou if (ret) 66*38a0f36eSThomas Chou return ERR_NOT_ERASED; 67*38a0f36eSThomas Chou 68*38a0f36eSThomas Chou return 0; 69*38a0f36eSThomas Chou } 70*38a0f36eSThomas Chou 71*38a0f36eSThomas Chou int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt) 72*38a0f36eSThomas Chou { 73*38a0f36eSThomas Chou struct mtd_info *mtd = info->mtd; 74*38a0f36eSThomas Chou struct udevice *dev = mtd->dev; 75*38a0f36eSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 76*38a0f36eSThomas Chou ulong base = (ulong)pdata->base; 77*38a0f36eSThomas Chou loff_t to = addr - base; 78*38a0f36eSThomas Chou size_t retlen; 79*38a0f36eSThomas Chou int ret; 80*38a0f36eSThomas Chou 81*38a0f36eSThomas Chou ret = mtd_write(mtd, to, cnt, &retlen, src); 82*38a0f36eSThomas Chou if (ret) 83*38a0f36eSThomas Chou return ERR_NOT_ERASED; 84*38a0f36eSThomas Chou 85*38a0f36eSThomas Chou return 0; 86*38a0f36eSThomas Chou } 87*38a0f36eSThomas Chou 88*38a0f36eSThomas Chou unsigned long flash_init(void) 89*38a0f36eSThomas Chou { 90*38a0f36eSThomas Chou struct udevice *dev; 91*38a0f36eSThomas Chou 92*38a0f36eSThomas Chou /* probe every MTD device */ 93*38a0f36eSThomas Chou for (uclass_first_device(UCLASS_MTD, &dev); 94*38a0f36eSThomas Chou dev; 95*38a0f36eSThomas Chou uclass_next_device(&dev)) { 96*38a0f36eSThomas Chou } 97*38a0f36eSThomas Chou 98*38a0f36eSThomas Chou return flash_info[0].size; 99*38a0f36eSThomas Chou } 100*38a0f36eSThomas Chou 101*38a0f36eSThomas Chou static int altera_qspi_erase(struct mtd_info *mtd, struct erase_info *instr) 102*38a0f36eSThomas Chou { 103*38a0f36eSThomas Chou struct udevice *dev = mtd->dev; 104*38a0f36eSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 105*38a0f36eSThomas Chou struct altera_qspi_regs *regs = pdata->regs; 106*38a0f36eSThomas Chou size_t addr = instr->addr; 107*38a0f36eSThomas Chou size_t len = instr->len; 108*38a0f36eSThomas Chou size_t end = addr + len; 109*38a0f36eSThomas Chou u32 sect; 110*38a0f36eSThomas Chou u32 stat; 111*38a0f36eSThomas Chou 112*38a0f36eSThomas Chou instr->state = MTD_ERASING; 113*38a0f36eSThomas Chou addr &= ~(mtd->erasesize - 1); /* get lower aligned address */ 114*38a0f36eSThomas Chou while (addr < end) { 115*38a0f36eSThomas Chou sect = addr / mtd->erasesize; 116*38a0f36eSThomas Chou sect <<= 8; 117*38a0f36eSThomas Chou sect |= QUADSPI_MEM_OP_SECTOR_ERASE; 118*38a0f36eSThomas Chou debug("erase %08x\n", sect); 119*38a0f36eSThomas Chou writel(sect, ®s->mem_op); 120*38a0f36eSThomas Chou stat = readl(®s->isr); 121*38a0f36eSThomas Chou if (stat & QUADSPI_ISR_ILLEGAL_ERASE) { 122*38a0f36eSThomas Chou /* erase failed, sector might be protected */ 123*38a0f36eSThomas Chou debug("erase %08x fail %x\n", sect, stat); 124*38a0f36eSThomas Chou writel(stat, ®s->isr); /* clear isr */ 125*38a0f36eSThomas Chou instr->state = MTD_ERASE_FAILED; 126*38a0f36eSThomas Chou return -EIO; 127*38a0f36eSThomas Chou } 128*38a0f36eSThomas Chou addr += mtd->erasesize; 129*38a0f36eSThomas Chou } 130*38a0f36eSThomas Chou instr->state = MTD_ERASE_DONE; 131*38a0f36eSThomas Chou mtd_erase_callback(instr); 132*38a0f36eSThomas Chou 133*38a0f36eSThomas Chou return 0; 134*38a0f36eSThomas Chou } 135*38a0f36eSThomas Chou 136*38a0f36eSThomas Chou static int altera_qspi_read(struct mtd_info *mtd, loff_t from, size_t len, 137*38a0f36eSThomas Chou size_t *retlen, u_char *buf) 138*38a0f36eSThomas Chou { 139*38a0f36eSThomas Chou struct udevice *dev = mtd->dev; 140*38a0f36eSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 141*38a0f36eSThomas Chou 142*38a0f36eSThomas Chou memcpy_fromio(buf, pdata->base + from, len); 143*38a0f36eSThomas Chou *retlen = len; 144*38a0f36eSThomas Chou 145*38a0f36eSThomas Chou return 0; 146*38a0f36eSThomas Chou } 147*38a0f36eSThomas Chou 148*38a0f36eSThomas Chou static int altera_qspi_write(struct mtd_info *mtd, loff_t to, size_t len, 149*38a0f36eSThomas Chou size_t *retlen, const u_char *buf) 150*38a0f36eSThomas Chou { 151*38a0f36eSThomas Chou struct udevice *dev = mtd->dev; 152*38a0f36eSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 153*38a0f36eSThomas Chou struct altera_qspi_regs *regs = pdata->regs; 154*38a0f36eSThomas Chou u32 stat; 155*38a0f36eSThomas Chou 156*38a0f36eSThomas Chou memcpy_toio(pdata->base + to, buf, len); 157*38a0f36eSThomas Chou /* check whether write triggered a illegal write interrupt */ 158*38a0f36eSThomas Chou stat = readl(®s->isr); 159*38a0f36eSThomas Chou if (stat & QUADSPI_ISR_ILLEGAL_WRITE) { 160*38a0f36eSThomas Chou /* write failed, sector might be protected */ 161*38a0f36eSThomas Chou debug("write fail %x\n", stat); 162*38a0f36eSThomas Chou writel(stat, ®s->isr); /* clear isr */ 163*38a0f36eSThomas Chou return -EIO; 164*38a0f36eSThomas Chou } 165*38a0f36eSThomas Chou *retlen = len; 166*38a0f36eSThomas Chou 167*38a0f36eSThomas Chou return 0; 168*38a0f36eSThomas Chou } 169*38a0f36eSThomas Chou 170*38a0f36eSThomas Chou static void altera_qspi_sync(struct mtd_info *mtd) 171*38a0f36eSThomas Chou { 172*38a0f36eSThomas Chou } 173*38a0f36eSThomas Chou 174*38a0f36eSThomas Chou static int altera_qspi_probe(struct udevice *dev) 175*38a0f36eSThomas Chou { 176*38a0f36eSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 177*38a0f36eSThomas Chou struct altera_qspi_regs *regs = pdata->regs; 178*38a0f36eSThomas Chou unsigned long base = (unsigned long)pdata->base; 179*38a0f36eSThomas Chou struct mtd_info *mtd; 180*38a0f36eSThomas Chou flash_info_t *flash = &flash_info[0]; 181*38a0f36eSThomas Chou u32 rdid; 182*38a0f36eSThomas Chou int i; 183*38a0f36eSThomas Chou 184*38a0f36eSThomas Chou rdid = readl(®s->rd_rdid); 185*38a0f36eSThomas Chou debug("rdid %x\n", rdid); 186*38a0f36eSThomas Chou 187*38a0f36eSThomas Chou mtd = dev_get_uclass_priv(dev); 188*38a0f36eSThomas Chou mtd->dev = dev; 189*38a0f36eSThomas Chou mtd->name = "nor0"; 190*38a0f36eSThomas Chou mtd->type = MTD_NORFLASH; 191*38a0f36eSThomas Chou mtd->flags = MTD_CAP_NORFLASH; 192*38a0f36eSThomas Chou mtd->size = 1 << ((rdid & 0xff) - 6); 193*38a0f36eSThomas Chou mtd->writesize = 1; 194*38a0f36eSThomas Chou mtd->writebufsize = mtd->writesize; 195*38a0f36eSThomas Chou mtd->_erase = altera_qspi_erase; 196*38a0f36eSThomas Chou mtd->_read = altera_qspi_read; 197*38a0f36eSThomas Chou mtd->_write = altera_qspi_write; 198*38a0f36eSThomas Chou mtd->_sync = altera_qspi_sync; 199*38a0f36eSThomas Chou mtd->numeraseregions = 0; 200*38a0f36eSThomas Chou mtd->erasesize = 0x10000; 201*38a0f36eSThomas Chou if (add_mtd_device(mtd)) 202*38a0f36eSThomas Chou return -ENOMEM; 203*38a0f36eSThomas Chou 204*38a0f36eSThomas Chou flash->mtd = mtd; 205*38a0f36eSThomas Chou flash->size = mtd->size; 206*38a0f36eSThomas Chou flash->sector_count = mtd->size / mtd->erasesize; 207*38a0f36eSThomas Chou flash->flash_id = rdid; 208*38a0f36eSThomas Chou flash->start[0] = base; 209*38a0f36eSThomas Chou for (i = 1; i < flash->sector_count; i++) 210*38a0f36eSThomas Chou flash->start[i] = flash->start[i - 1] + mtd->erasesize; 211*38a0f36eSThomas Chou gd->bd->bi_flashstart = base; 212*38a0f36eSThomas Chou 213*38a0f36eSThomas Chou return 0; 214*38a0f36eSThomas Chou } 215*38a0f36eSThomas Chou 216*38a0f36eSThomas Chou static int altera_qspi_ofdata_to_platdata(struct udevice *dev) 217*38a0f36eSThomas Chou { 218*38a0f36eSThomas Chou struct altera_qspi_platdata *pdata = dev_get_platdata(dev); 219*38a0f36eSThomas Chou void *blob = (void *)gd->fdt_blob; 220*38a0f36eSThomas Chou int node = dev->of_offset; 221*38a0f36eSThomas Chou const char *list, *end; 222*38a0f36eSThomas Chou const fdt32_t *cell; 223*38a0f36eSThomas Chou void *base; 224*38a0f36eSThomas Chou unsigned long addr, size; 225*38a0f36eSThomas Chou int parent, addrc, sizec; 226*38a0f36eSThomas Chou int len, idx; 227*38a0f36eSThomas Chou 228*38a0f36eSThomas Chou /* 229*38a0f36eSThomas Chou * decode regs. there are multiple reg tuples, and they need to 230*38a0f36eSThomas Chou * match with reg-names. 231*38a0f36eSThomas Chou */ 232*38a0f36eSThomas Chou parent = fdt_parent_offset(blob, node); 233*38a0f36eSThomas Chou of_bus_default_count_cells(blob, parent, &addrc, &sizec); 234*38a0f36eSThomas Chou list = fdt_getprop(blob, node, "reg-names", &len); 235*38a0f36eSThomas Chou if (!list) 236*38a0f36eSThomas Chou return -ENOENT; 237*38a0f36eSThomas Chou end = list + len; 238*38a0f36eSThomas Chou cell = fdt_getprop(blob, node, "reg", &len); 239*38a0f36eSThomas Chou if (!cell) 240*38a0f36eSThomas Chou return -ENOENT; 241*38a0f36eSThomas Chou idx = 0; 242*38a0f36eSThomas Chou while (list < end) { 243*38a0f36eSThomas Chou addr = fdt_translate_address((void *)blob, 244*38a0f36eSThomas Chou node, cell + idx); 245*38a0f36eSThomas Chou size = fdt_addr_to_cpu(cell[idx + addrc]); 246*38a0f36eSThomas Chou base = ioremap(addr, size); 247*38a0f36eSThomas Chou len = strlen(list); 248*38a0f36eSThomas Chou if (strcmp(list, "avl_csr") == 0) { 249*38a0f36eSThomas Chou pdata->regs = base; 250*38a0f36eSThomas Chou } else if (strcmp(list, "avl_mem") == 0) { 251*38a0f36eSThomas Chou pdata->base = base; 252*38a0f36eSThomas Chou pdata->size = size; 253*38a0f36eSThomas Chou } 254*38a0f36eSThomas Chou idx += addrc + sizec; 255*38a0f36eSThomas Chou list += (len + 1); 256*38a0f36eSThomas Chou } 257*38a0f36eSThomas Chou 258*38a0f36eSThomas Chou return 0; 259*38a0f36eSThomas Chou } 260*38a0f36eSThomas Chou 261*38a0f36eSThomas Chou static const struct udevice_id altera_qspi_ids[] = { 262*38a0f36eSThomas Chou { .compatible = "altr,quadspi-1.0" }, 263*38a0f36eSThomas Chou {} 264*38a0f36eSThomas Chou }; 265*38a0f36eSThomas Chou 266*38a0f36eSThomas Chou U_BOOT_DRIVER(altera_qspi) = { 267*38a0f36eSThomas Chou .name = "altera_qspi", 268*38a0f36eSThomas Chou .id = UCLASS_MTD, 269*38a0f36eSThomas Chou .of_match = altera_qspi_ids, 270*38a0f36eSThomas Chou .ofdata_to_platdata = altera_qspi_ofdata_to_platdata, 271*38a0f36eSThomas Chou .platdata_auto_alloc_size = sizeof(struct altera_qspi_platdata), 272*38a0f36eSThomas Chou .probe = altera_qspi_probe, 273*38a0f36eSThomas Chou }; 274