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                        mtd_erase_callback(instr);
 157                        return -EIO;
 158                }
 159                flash = pdata->base + addr;
 160                last = pdata->base + addr + mtd->erasesize;
 161                /* skip erase if sector is blank */
 162                while (flash < last) {
 163                        if (readl(flash) != 0xffffffff)
 164                                break;
 165                        flash++;
 166                }
 167                if (flash < last) {
 168                        sect = addr / mtd->erasesize;
 169                        sect <<= 8;
 170                        sect |= QUADSPI_MEM_OP_SECTOR_ERASE;
 171                        debug("erase %08x\n", sect);
 172                        writel(sect, &regs->mem_op);
 173                        stat = readl(&regs->isr);
 174                        if (stat & QUADSPI_ISR_ILLEGAL_ERASE) {
 175                                /* erase failed, sector might be protected */
 176                                debug("erase %08x fail %x\n", sect, stat);
 177                                writel(stat, &regs->isr); /* clear isr */
 178                                instr->fail_addr = addr;
 179                                instr->state = MTD_ERASE_FAILED;
 180                                mtd_erase_callback(instr);
 181                                return -EIO;
 182                        }
 183                        if (flash_verbose)
 184                                putc('.');
 185                } else {
 186                        if (flash_verbose)
 187                                putc(',');
 188                }
 189                addr += mtd->erasesize;
 190        }
 191        instr->state = MTD_ERASE_DONE;
 192        mtd_erase_callback(instr);
 193
 194        return 0;
 195}
 196
 197static int altera_qspi_read(struct mtd_info *mtd, loff_t from, size_t len,
 198                            size_t *retlen, u_char *buf)
 199{
 200        struct udevice *dev = mtd->dev;
 201        struct altera_qspi_plat *pdata = dev_get_plat(dev);
 202
 203        memcpy_fromio(buf, pdata->base + from, len);
 204        *retlen = len;
 205
 206        return 0;
 207}
 208
 209static int altera_qspi_write(struct mtd_info *mtd, loff_t to, size_t len,
 210                             size_t *retlen, const u_char *buf)
 211{
 212        struct udevice *dev = mtd->dev;
 213        struct altera_qspi_plat *pdata = dev_get_plat(dev);
 214        struct altera_qspi_regs *regs = pdata->regs;
 215        u32 stat;
 216
 217        memcpy_toio(pdata->base + to, buf, len);
 218        /* check whether write triggered a illegal write interrupt */
 219        stat = readl(&regs->isr);
 220        if (stat & QUADSPI_ISR_ILLEGAL_WRITE) {
 221                /* write failed, sector might be protected */
 222                debug("write fail %x\n", stat);
 223                writel(stat, &regs->isr); /* clear isr */
 224                return -EIO;
 225        }
 226        *retlen = len;
 227
 228        return 0;
 229}
 230
 231static void altera_qspi_sync(struct mtd_info *mtd)
 232{
 233}
 234
 235static void altera_qspi_get_locked_range(struct mtd_info *mtd, loff_t *ofs,
 236                                         uint64_t *len)
 237{
 238        struct udevice *dev = mtd->dev;
 239        struct altera_qspi_plat *pdata = dev_get_plat(dev);
 240        struct altera_qspi_regs *regs = pdata->regs;
 241        int shift0 = ffs(QUADSPI_SR_BP2_0) - 1;
 242        int shift3 = ffs(QUADSPI_SR_BP3) - 1 - 3;
 243        u32 stat = readl(&regs->rd_status);
 244        unsigned pow = ((stat & QUADSPI_SR_BP2_0) >> shift0) |
 245                ((stat & QUADSPI_SR_BP3) >> shift3);
 246
 247        *ofs = 0;
 248        *len = 0;
 249        if (pow) {
 250                *len = mtd->erasesize << (pow - 1);
 251                if (*len > mtd->size)
 252                        *len = mtd->size;
 253                if (!(stat & QUADSPI_SR_TB))
 254                        *ofs = mtd->size - *len;
 255        }
 256}
 257
 258static int altera_qspi_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 259{
 260        struct udevice *dev = mtd->dev;
 261        struct altera_qspi_plat *pdata = dev_get_plat(dev);
 262        struct altera_qspi_regs *regs = pdata->regs;
 263        u32 sector_start, sector_end;
 264        u32 num_sectors;
 265        u32 mem_op;
 266        u32 sr_bp;
 267        u32 sr_tb;
 268
 269        num_sectors = mtd->size / mtd->erasesize;
 270        sector_start = ofs / mtd->erasesize;
 271        sector_end = (ofs + len) / mtd->erasesize;
 272
 273        if (sector_start >= num_sectors / 2) {
 274                sr_bp = fls(num_sectors - 1 - sector_start) + 1;
 275                sr_tb = 0;
 276        } else if (sector_end < num_sectors / 2) {
 277                sr_bp = fls(sector_end) + 1;
 278                sr_tb = 1;
 279        } else {
 280                sr_bp = 15;
 281                sr_tb = 0;
 282        }
 283
 284        mem_op = (sr_tb << 12) | (sr_bp << 8);
 285        mem_op |= QUADSPI_MEM_OP_SECTOR_PROTECT;
 286        debug("lock %08x\n", mem_op);
 287        writel(mem_op, &regs->mem_op);
 288
 289        return 0;
 290}
 291
 292static int altera_qspi_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len)
 293{
 294        struct udevice *dev = mtd->dev;
 295        struct altera_qspi_plat *pdata = dev_get_plat(dev);
 296        struct altera_qspi_regs *regs = pdata->regs;
 297        u32 mem_op;
 298
 299        mem_op = QUADSPI_MEM_OP_SECTOR_PROTECT;
 300        debug("unlock %08x\n", mem_op);
 301        writel(mem_op, &regs->mem_op);
 302
 303        return 0;
 304}
 305
 306static int altera_qspi_probe(struct udevice *dev)
 307{
 308        struct altera_qspi_plat *pdata = dev_get_plat(dev);
 309        struct altera_qspi_regs *regs = pdata->regs;
 310        unsigned long base = (unsigned long)pdata->base;
 311        struct mtd_info *mtd;
 312        flash_info_t *flash = &flash_info[0];
 313        u32 rdid;
 314        int i;
 315
 316        rdid = readl(&regs->rd_rdid);
 317        debug("rdid %x\n", rdid);
 318
 319        mtd = dev_get_uclass_priv(dev);
 320        mtd->dev = dev;
 321        mtd->name               = "nor0";
 322        mtd->type               = MTD_NORFLASH;
 323        mtd->flags              = MTD_CAP_NORFLASH;
 324        mtd->size               = 1 << ((rdid & 0xff) - 6);
 325        mtd->writesize          = 1;
 326        mtd->writebufsize       = mtd->writesize;
 327        mtd->_erase             = altera_qspi_erase;
 328        mtd->_read              = altera_qspi_read;
 329        mtd->_write             = altera_qspi_write;
 330        mtd->_sync              = altera_qspi_sync;
 331        mtd->_lock              = altera_qspi_lock;
 332        mtd->_unlock            = altera_qspi_unlock;
 333        mtd->numeraseregions = 0;
 334        mtd->erasesize = 0x10000;
 335        if (add_mtd_device(mtd))
 336                return -ENOMEM;
 337
 338        flash->mtd = mtd;
 339        flash->size = mtd->size;
 340        flash->sector_count = mtd->size / mtd->erasesize;
 341        flash->flash_id = rdid;
 342        flash->start[0] = base;
 343        for (i = 1; i < flash->sector_count; i++)
 344                flash->start[i] = flash->start[i - 1] + mtd->erasesize;
 345        gd->bd->bi_flashstart = base;
 346
 347        return 0;
 348}
 349
 350static int altera_qspi_of_to_plat(struct udevice *dev)
 351{
 352        struct altera_qspi_plat *pdata = dev_get_plat(dev);
 353        void *blob = (void *)gd->fdt_blob;
 354        int node = dev_of_offset(dev);
 355        const char *list, *end;
 356        const fdt32_t *cell;
 357        void *base;
 358        unsigned long addr, size;
 359        int parent, addrc, sizec;
 360        int len, idx;
 361
 362        /*
 363         * decode regs. there are multiple reg tuples, and they need to
 364         * match with reg-names.
 365         */
 366        parent = fdt_parent_offset(blob, node);
 367        fdt_support_default_count_cells(blob, parent, &addrc, &sizec);
 368        list = fdt_getprop(blob, node, "reg-names", &len);
 369        if (!list)
 370                return -ENOENT;
 371        end = list + len;
 372        cell = fdt_getprop(blob, node, "reg", &len);
 373        if (!cell)
 374                return -ENOENT;
 375        idx = 0;
 376        while (list < end) {
 377                addr = fdt_translate_address((void *)blob,
 378                                             node, cell + idx);
 379                size = fdt_addr_to_cpu(cell[idx + addrc]);
 380                base = map_physmem(addr, size, MAP_NOCACHE);
 381                len = strlen(list);
 382                if (strcmp(list, "avl_csr") == 0) {
 383                        pdata->regs = base;
 384                } else if (strcmp(list, "avl_mem") == 0) {
 385                        pdata->base = base;
 386                        pdata->size = size;
 387                }
 388                idx += addrc + sizec;
 389                list += (len + 1);
 390        }
 391
 392        return 0;
 393}
 394
 395static const struct udevice_id altera_qspi_ids[] = {
 396        { .compatible = "altr,quadspi-1.0" },
 397        {}
 398};
 399
 400U_BOOT_DRIVER(altera_qspi) = {
 401        .name   = "altera_qspi",
 402        .id     = UCLASS_MTD,
 403        .of_match = altera_qspi_ids,
 404        .of_to_plat = altera_qspi_of_to_plat,
 405        .plat_auto      = sizeof(struct altera_qspi_plat),
 406        .probe  = altera_qspi_probe,
 407};
 408