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