uboot/drivers/mtd/altera_qspi.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2015 Thomas Chou <thomas@wytron.com.tw>
   3 *
   4 * SPDX-License-Identifier:     GPL-2.0+
   5 */
   6
   7#include <common.h>
   8#include <console.h>
   9#include <dm.h>
  10#include <errno.h>
  11#include <fdt_support.h>
  12#include <flash.h>
  13#include <mtd.h>
  14#include <asm/io.h>
  15
  16DECLARE_GLOBAL_DATA_PTR;
  17
  18/* The STATUS register */
  19#define QUADSPI_SR_BP0                          BIT(2)
  20#define QUADSPI_SR_BP1                          BIT(3)
  21#define QUADSPI_SR_BP2                          BIT(4)
  22#define QUADSPI_SR_BP2_0                        GENMASK(4, 2)
  23#define QUADSPI_SR_BP3                          BIT(6)
  24#define QUADSPI_SR_TB                           BIT(5)
  25
  26/*
  27 * The QUADSPI_MEM_OP register is used to do memory protect and erase operations
  28 */
  29#define QUADSPI_MEM_OP_BULK_ERASE               0x00000001
  30#define QUADSPI_MEM_OP_SECTOR_ERASE             0x00000002
  31#define QUADSPI_MEM_OP_SECTOR_PROTECT           0x00000003
  32
  33/*
  34 * The QUADSPI_ISR register is used to determine whether an invalid write or
  35 * erase operation trigerred an interrupt
  36 */
  37#define QUADSPI_ISR_ILLEGAL_ERASE               BIT(0)
  38#define QUADSPI_ISR_ILLEGAL_WRITE               BIT(1)
  39
  40struct altera_qspi_regs {
  41        u32     rd_status;
  42        u32     rd_sid;
  43        u32     rd_rdid;
  44        u32     mem_op;
  45        u32     isr;
  46        u32     imr;
  47        u32     chip_select;
  48};
  49
  50struct altera_qspi_platdata {
  51        struct altera_qspi_regs *regs;
  52        void *base;
  53        unsigned long size;
  54};
  55
  56static uint flash_verbose;
  57flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];    /* FLASH chips info */
  58
  59static void altera_qspi_get_locked_range(struct mtd_info *mtd, loff_t *ofs,
  60                                         uint64_t *len);
  61
  62void flash_print_info(flash_info_t *info)
  63{
  64        struct mtd_info *mtd = info->mtd;
  65        loff_t ofs;
  66        u64 len;
  67
  68        printf("Altera QSPI flash  Size: %ld MB in %d Sectors\n",
  69               info->size >> 20, info->sector_count);
  70        altera_qspi_get_locked_range(mtd, &ofs, &len);
  71        printf("  %08lX +%lX", info->start[0], info->size);
  72        if (len) {
  73                printf(", protected %08llX +%llX",
  74                       info->start[0] + ofs, len);
  75        }
  76        putc('\n');
  77}
  78
  79void flash_set_verbose(uint v)
  80{
  81        flash_verbose = v;
  82}
  83
  84int flash_erase(flash_info_t *info, int s_first, int s_last)
  85{
  86        struct mtd_info *mtd = info->mtd;
  87        struct erase_info instr;
  88        int ret;
  89
  90        memset(&instr, 0, sizeof(instr));
  91        instr.mtd = mtd;
  92        instr.addr = mtd->erasesize * s_first;
  93        instr.len = mtd->erasesize * (s_last + 1 - s_first);
  94        flash_set_verbose(1);
  95        ret = mtd_erase(mtd, &instr);
  96        flash_set_verbose(0);
  97        if (ret)
  98                return ERR_PROTECTED;
  99
 100        puts(" done\n");
 101        return 0;
 102}
 103
 104int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt)
 105{
 106        struct mtd_info *mtd = info->mtd;
 107        struct udevice *dev = mtd->dev;
 108        struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
 109        ulong base = (ulong)pdata->base;
 110        loff_t to = addr - base;
 111        size_t retlen;
 112        int ret;
 113
 114        ret = mtd_write(mtd, to, cnt, &retlen, src);
 115        if (ret)
 116                return ERR_PROTECTED;
 117
 118        return 0;
 119}
 120
 121unsigned long flash_init(void)
 122{
 123        struct udevice *dev;
 124
 125        /* probe every MTD device */
 126        for (uclass_first_device(UCLASS_MTD, &dev);
 127             dev;
 128             uclass_next_device(&dev)) {
 129        }
 130
 131        return flash_info[0].size;
 132}
 133
 134static int altera_qspi_erase(struct mtd_info *mtd, struct erase_info *instr)
 135{
 136        struct udevice *dev = mtd->dev;
 137        struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
 138        struct altera_qspi_regs *regs = pdata->regs;
 139        size_t addr = instr->addr;
 140        size_t len = instr->len;
 141        size_t end = addr + len;
 142        u32 sect;
 143        u32 stat;
 144        u32 *flash, *last;
 145
 146        instr->state = MTD_ERASING;
 147        addr &= ~(mtd->erasesize - 1); /* get lower aligned address */
 148        while (addr < end) {
 149                if (ctrlc()) {
 150                        if (flash_verbose)
 151                                putc('\n');
 152                        instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
 153                        instr->state = MTD_ERASE_FAILED;
 154                        mtd_erase_callback(instr);
 155                        return -EIO;
 156                }
 157                flash = pdata->base + addr;
 158                last = pdata->base + addr + mtd->erasesize;
 159                /* skip erase if sector is blank */
 160                while (flash < last) {
 161                        if (readl(flash) != 0xffffffff)
 162                                break;
 163                        flash++;
 164                }
 165                if (flash < last) {
 166                        sect = addr / mtd->erasesize;
 167                        sect <<= 8;
 168                        sect |= QUADSPI_MEM_OP_SECTOR_ERASE;
 169                        debug("erase %08x\n", sect);
 170                        writel(sect, &regs->mem_op);
 171                        stat = readl(&regs->isr);
 172                        if (stat & QUADSPI_ISR_ILLEGAL_ERASE) {
 173                                /* erase failed, sector might be protected */
 174                                debug("erase %08x fail %x\n", sect, stat);
 175                                writel(stat, &regs->isr); /* clear isr */
 176                                instr->fail_addr = addr;
 177                                instr->state = MTD_ERASE_FAILED;
 178                                mtd_erase_callback(instr);
 179                                return -EIO;
 180                        }
 181                        if (flash_verbose)
 182                                putc('.');
 183                } else {
 184                        if (flash_verbose)
 185                                putc(',');
 186                }
 187                addr += mtd->erasesize;
 188        }
 189        instr->state = MTD_ERASE_DONE;
 190        mtd_erase_callback(instr);
 191
 192        return 0;
 193}
 194
 195static int altera_qspi_read(struct mtd_info *mtd, loff_t from, size_t len,
 196                            size_t *retlen, u_char *buf)
 197{
 198        struct udevice *dev = mtd->dev;
 199        struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
 200
 201        memcpy_fromio(buf, pdata->base + from, len);
 202        *retlen = len;
 203
 204        return 0;
 205}
 206
 207static int altera_qspi_write(struct mtd_info *mtd, loff_t to, size_t len,
 208                             size_t *retlen, const u_char *buf)
 209{
 210        struct udevice *dev = mtd->dev;
 211        struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
 212        struct altera_qspi_regs *regs = pdata->regs;
 213        u32 stat;
 214
 215        memcpy_toio(pdata->base + to, buf, len);
 216        /* check whether write triggered a illegal write interrupt */
 217        stat = readl(&regs->isr);
 218        if (stat & QUADSPI_ISR_ILLEGAL_WRITE) {
 219                /* write failed, sector might be protected */
 220                debug("write fail %x\n", stat);
 221                writel(stat, &regs->isr); /* clear isr */
 222                return -EIO;
 223        }
 224        *retlen = len;
 225
 226        return 0;
 227}
 228
 229static void altera_qspi_sync(struct mtd_info *mtd)
 230{
 231}
 232
 233static void altera_qspi_get_locked_range(struct mtd_info *mtd, loff_t *ofs,
 234                                         uint64_t *len)
 235{
 236        struct udevice *dev = mtd->dev;
 237        struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
 238        struct altera_qspi_regs *regs = pdata->regs;
 239        int shift0 = ffs(QUADSPI_SR_BP2_0) - 1;
 240        int shift3 = ffs(QUADSPI_SR_BP3) - 1 - 3;
 241        u32 stat = readl(&regs->rd_status);
 242        unsigned pow = ((stat & QUADSPI_SR_BP2_0) >> shift0) |
 243                ((stat & QUADSPI_SR_BP3) >> shift3);
 244
 245        *ofs = 0;
 246        *len = 0;
 247        if (pow) {
 248                *len = mtd->erasesize << (pow - 1);
 249                if (*len > mtd->size)
 250                        *len = mtd->size;
 251                if (!(stat & QUADSPI_SR_TB))
 252                        *ofs = mtd->size - *len;
 253        }
 254}
 255
 256static int altera_qspi_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 257{
 258        struct udevice *dev = mtd->dev;
 259        struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
 260        struct altera_qspi_regs *regs = pdata->regs;
 261        u32 sector_start, sector_end;
 262        u32 num_sectors;
 263        u32 mem_op;
 264        u32 sr_bp;
 265        u32 sr_tb;
 266
 267        num_sectors = mtd->size / mtd->erasesize;
 268        sector_start = ofs / mtd->erasesize;
 269        sector_end = (ofs + len) / mtd->erasesize;
 270
 271        if (sector_start >= num_sectors / 2) {
 272                sr_bp = fls(num_sectors - 1 - sector_start) + 1;
 273                sr_tb = 0;
 274        } else if (sector_end < num_sectors / 2) {
 275                sr_bp = fls(sector_end) + 1;
 276                sr_tb = 1;
 277        } else {
 278                sr_bp = 15;
 279                sr_tb = 0;
 280        }
 281
 282        mem_op = (sr_tb << 12) | (sr_bp << 8);
 283        mem_op |= QUADSPI_MEM_OP_SECTOR_PROTECT;
 284        debug("lock %08x\n", mem_op);
 285        writel(mem_op, &regs->mem_op);
 286
 287        return 0;
 288}
 289
 290static int altera_qspi_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 291{
 292        struct udevice *dev = mtd->dev;
 293        struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
 294        struct altera_qspi_regs *regs = pdata->regs;
 295        u32 mem_op;
 296
 297        mem_op = QUADSPI_MEM_OP_SECTOR_PROTECT;
 298        debug("unlock %08x\n", mem_op);
 299        writel(mem_op, &regs->mem_op);
 300
 301        return 0;
 302}
 303
 304static int altera_qspi_probe(struct udevice *dev)
 305{
 306        struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
 307        struct altera_qspi_regs *regs = pdata->regs;
 308        unsigned long base = (unsigned long)pdata->base;
 309        struct mtd_info *mtd;
 310        flash_info_t *flash = &flash_info[0];
 311        u32 rdid;
 312        int i;
 313
 314        rdid = readl(&regs->rd_rdid);
 315        debug("rdid %x\n", rdid);
 316
 317        mtd = dev_get_uclass_priv(dev);
 318        mtd->dev = dev;
 319        mtd->name               = "nor0";
 320        mtd->type               = MTD_NORFLASH;
 321        mtd->flags              = MTD_CAP_NORFLASH;
 322        mtd->size               = 1 << ((rdid & 0xff) - 6);
 323        mtd->writesize          = 1;
 324        mtd->writebufsize       = mtd->writesize;
 325        mtd->_erase             = altera_qspi_erase;
 326        mtd->_read              = altera_qspi_read;
 327        mtd->_write             = altera_qspi_write;
 328        mtd->_sync              = altera_qspi_sync;
 329        mtd->_lock              = altera_qspi_lock;
 330        mtd->_unlock            = altera_qspi_unlock;
 331        mtd->numeraseregions = 0;
 332        mtd->erasesize = 0x10000;
 333        if (add_mtd_device(mtd))
 334                return -ENOMEM;
 335
 336        flash->mtd = mtd;
 337        flash->size = mtd->size;
 338        flash->sector_count = mtd->size / mtd->erasesize;
 339        flash->flash_id = rdid;
 340        flash->start[0] = base;
 341        for (i = 1; i < flash->sector_count; i++)
 342                flash->start[i] = flash->start[i - 1] + mtd->erasesize;
 343        gd->bd->bi_flashstart = base;
 344
 345        return 0;
 346}
 347
 348static int altera_qspi_ofdata_to_platdata(struct udevice *dev)
 349{
 350        struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
 351        void *blob = (void *)gd->fdt_blob;
 352        int node = dev->of_offset;
 353        const char *list, *end;
 354        const fdt32_t *cell;
 355        void *base;
 356        unsigned long addr, size;
 357        int parent, addrc, sizec;
 358        int len, idx;
 359
 360        /*
 361         * decode regs. there are multiple reg tuples, and they need to
 362         * match with reg-names.
 363         */
 364        parent = fdt_parent_offset(blob, node);
 365        of_bus_default_count_cells(blob, parent, &addrc, &sizec);
 366        list = fdt_getprop(blob, node, "reg-names", &len);
 367        if (!list)
 368                return -ENOENT;
 369        end = list + len;
 370        cell = fdt_getprop(blob, node, "reg", &len);
 371        if (!cell)
 372                return -ENOENT;
 373        idx = 0;
 374        while (list < end) {
 375                addr = fdt_translate_address((void *)blob,
 376                                             node, cell + idx);
 377                size = fdt_addr_to_cpu(cell[idx + addrc]);
 378                base = map_physmem(addr, size, MAP_NOCACHE);
 379                len = strlen(list);
 380                if (strcmp(list, "avl_csr") == 0) {
 381                        pdata->regs = base;
 382                } else if (strcmp(list, "avl_mem") == 0) {
 383                        pdata->base = base;
 384                        pdata->size = size;
 385                }
 386                idx += addrc + sizec;
 387                list += (len + 1);
 388        }
 389
 390        return 0;
 391}
 392
 393static const struct udevice_id altera_qspi_ids[] = {
 394        { .compatible = "altr,quadspi-1.0" },
 395        {}
 396};
 397
 398U_BOOT_DRIVER(altera_qspi) = {
 399        .name   = "altera_qspi",
 400        .id     = UCLASS_MTD,
 401        .of_match = altera_qspi_ids,
 402        .ofdata_to_platdata = altera_qspi_ofdata_to_platdata,
 403        .platdata_auto_alloc_size = sizeof(struct altera_qspi_platdata),
 404        .probe  = altera_qspi_probe,
 405};
 406