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 <mtd.h>
  13#include <asm/io.h>
  14
  15DECLARE_GLOBAL_DATA_PTR;
  16
  17/* The STATUS register */
  18#define QUADSPI_SR_BP0                          BIT(2)
  19#define QUADSPI_SR_BP1                          BIT(3)
  20#define QUADSPI_SR_BP2                          BIT(4)
  21#define QUADSPI_SR_BP2_0                        GENMASK(4, 2)
  22#define QUADSPI_SR_BP3                          BIT(6)
  23#define QUADSPI_SR_TB                           BIT(5)
  24
  25/*
  26 * The QUADSPI_MEM_OP register is used to do memory protect and erase operations
  27 */
  28#define QUADSPI_MEM_OP_BULK_ERASE               0x00000001
  29#define QUADSPI_MEM_OP_SECTOR_ERASE             0x00000002
  30#define QUADSPI_MEM_OP_SECTOR_PROTECT           0x00000003
  31
  32/*
  33 * The QUADSPI_ISR register is used to determine whether an invalid write or
  34 * erase operation trigerred an interrupt
  35 */
  36#define QUADSPI_ISR_ILLEGAL_ERASE               BIT(0)
  37#define QUADSPI_ISR_ILLEGAL_WRITE               BIT(1)
  38
  39struct altera_qspi_regs {
  40        u32     rd_status;
  41        u32     rd_sid;
  42        u32     rd_rdid;
  43        u32     mem_op;
  44        u32     isr;
  45        u32     imr;
  46        u32     chip_select;
  47};
  48
  49struct altera_qspi_platdata {
  50        struct altera_qspi_regs *regs;
  51        void *base;
  52        unsigned long size;
  53};
  54
  55static uint flash_verbose;
  56flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];    /* FLASH chips info */
  57
  58static void altera_qspi_get_locked_range(struct mtd_info *mtd, loff_t *ofs,
  59                                         uint64_t *len);
  60
  61void flash_print_info(flash_info_t *info)
  62{
  63        struct mtd_info *mtd = info->mtd;
  64        loff_t ofs;
  65        u64 len;
  66
  67        printf("Altera QSPI flash  Size: %ld MB in %d Sectors\n",
  68               info->size >> 20, info->sector_count);
  69        altera_qspi_get_locked_range(mtd, &ofs, &len);
  70        printf("  %08lX +%lX", info->start[0], info->size);
  71        if (len) {
  72                printf(", protected %08llX +%llX",
  73                       info->start[0] + ofs, len);
  74        }
  75        putc('\n');
  76}
  77
  78void flash_set_verbose(uint v)
  79{
  80        flash_verbose = v;
  81}
  82
  83int flash_erase(flash_info_t *info, int s_first, int s_last)
  84{
  85        struct mtd_info *mtd = info->mtd;
  86        struct erase_info instr;
  87        int ret;
  88
  89        memset(&instr, 0, sizeof(instr));
  90        instr.mtd = mtd;
  91        instr.addr = mtd->erasesize * s_first;
  92        instr.len = mtd->erasesize * (s_last + 1 - s_first);
  93        flash_set_verbose(1);
  94        ret = mtd_erase(mtd, &instr);
  95        flash_set_verbose(0);
  96        if (ret)
  97                return ERR_PROTECTED;
  98
  99        puts(" done\n");
 100        return 0;
 101}
 102
 103int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt)
 104{
 105        struct mtd_info *mtd = info->mtd;
 106        struct udevice *dev = mtd->dev;
 107        struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
 108        ulong base = (ulong)pdata->base;
 109        loff_t to = addr - base;
 110        size_t retlen;
 111        int ret;
 112
 113        ret = mtd_write(mtd, to, cnt, &retlen, src);
 114        if (ret)
 115                return ERR_PROTECTED;
 116
 117        return 0;
 118}
 119
 120unsigned long flash_init(void)
 121{
 122        struct udevice *dev;
 123
 124        /* probe every MTD device */
 125        for (uclass_first_device(UCLASS_MTD, &dev);
 126             dev;
 127             uclass_next_device(&dev)) {
 128        }
 129
 130        return flash_info[0].size;
 131}
 132
 133static int altera_qspi_erase(struct mtd_info *mtd, struct erase_info *instr)
 134{
 135        struct udevice *dev = mtd->dev;
 136        struct altera_qspi_platdata *pdata = dev_get_platdata(dev);
 137        struct altera_qspi_regs *regs = pdata->regs;
 138        size_t addr = instr->addr;
 139        size_t len = instr->len;
 140        size_t end = addr + len;
 141        u32 sect;
 142        u32 stat;
 143        u32 *flash, *last;
 144
 145        instr->state = MTD_ERASING;
 146        addr &= ~(mtd->erasesize - 1); /* get lower aligned address */
 147        while (addr < end) {
 148                if (ctrlc()) {
 149                        if (flash_verbose)
 150                                putc('\n');
 151                        instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN;
 152                        instr->state = MTD_ERASE_FAILED;
 153                        mtd_erase_callback(instr);
 154                        return -EIO;
 155                }
 156                flash = pdata->base + addr;
 157                last = pdata->base + addr + mtd->erasesize;
 158                /* skip erase if sector is blank */
 159                while (flash < last) {
 160                        if (readl(flash) != 0xffffffff)
 161                                break;
 162                        flash++;
 163                }
 164                if (flash < last) {
 165                        sect = addr / mtd->erasesize;
 166                        sect <<= 8;
 167                        sect |= QUADSPI_MEM_OP_SECTOR_ERASE;
 168                        debug("erase %08x\n", sect);
 169                        writel(sect, &regs->mem_op);
 170                        stat = readl(&regs->isr);
 171                        if (stat & QUADSPI_ISR_ILLEGAL_ERASE) {
 172                                /* erase failed, sector might be protected */
 173                                debug("erase %08x fail %x\n", sect, stat);
 174                                writel(stat, &regs->isr); /* clear isr */
 175                                instr->fail_addr = addr;
 176                                instr->state = MTD_ERASE_FAILED;
 177                                mtd_erase_callback(instr);
 178                                return -EIO;
 179                        }
 180                        if (flash_verbose)
 181                                putc('.');
 182                } else {
 183                        if (flash_verbose)
 184                                putc(',');
 185                }
 186                addr += mtd->erasesize;
 187        }
 188        instr->state = MTD_ERASE_DONE;
 189        mtd_erase_callback(instr);
 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_platdata *pdata = dev_get_platdata(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_platdata *pdata = dev_get_platdata(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_platdata *pdata = dev_get_platdata(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_platdata *pdata = dev_get_platdata(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_platdata *pdata = dev_get_platdata(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_platdata *pdata = dev_get_platdata(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_ofdata_to_platdata(struct udevice *dev)
 348{
 349        struct altera_qspi_platdata *pdata = dev_get_platdata(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        .ofdata_to_platdata = altera_qspi_ofdata_to_platdata,
 402        .platdata_auto_alloc_size = sizeof(struct altera_qspi_platdata),
 403        .probe  = altera_qspi_probe,
 404};
 405