uboot/drivers/mtd/nand/raw/denali_spl.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2014       Panasonic Corporation
   4 * Copyright (C) 2014-2015  Masahiro Yamada <yamada.masahiro@socionext.com>
   5 */
   6
   7#include <common.h>
   8#include <asm/io.h>
   9#include <asm/unaligned.h>
  10#include <linux/mtd/rawnand.h>
  11#include "denali.h"
  12
  13#define DENALI_MAP01            (1 << 26)       /* read/write pages in PIO */
  14#define DENALI_MAP10            (2 << 26)       /* high-level control plane */
  15
  16#define INDEX_CTRL_REG          0x0
  17#define INDEX_DATA_REG          0x10
  18
  19#define SPARE_ACCESS            0x41
  20#define MAIN_ACCESS             0x42
  21#define PIPELINE_ACCESS         0x2000
  22
  23#define BANK(x) ((x) << 24)
  24
  25static void __iomem *denali_flash_mem =
  26                        (void __iomem *)CONFIG_SYS_NAND_DATA_BASE;
  27static void __iomem *denali_flash_reg =
  28                        (void __iomem *)CONFIG_SYS_NAND_REGS_BASE;
  29
  30static const int flash_bank;
  31static int page_size, oob_size, pages_per_block;
  32
  33static void index_addr(uint32_t address, uint32_t data)
  34{
  35        writel(address, denali_flash_mem + INDEX_CTRL_REG);
  36        writel(data, denali_flash_mem + INDEX_DATA_REG);
  37}
  38
  39static int wait_for_irq(uint32_t irq_mask)
  40{
  41        unsigned long timeout = 1000000;
  42        uint32_t intr_status;
  43
  44        do {
  45                intr_status = readl(denali_flash_reg + INTR_STATUS(flash_bank));
  46
  47                if (intr_status & INTR__ECC_UNCOR_ERR) {
  48                        debug("Uncorrected ECC detected\n");
  49                        return -EBADMSG;
  50                }
  51
  52                if (intr_status & irq_mask)
  53                        break;
  54
  55                udelay(1);
  56                timeout--;
  57        } while (timeout);
  58
  59        if (!timeout) {
  60                debug("Timeout with interrupt status %08x\n", intr_status);
  61                return -EIO;
  62        }
  63
  64        return 0;
  65}
  66
  67static void read_data_from_flash_mem(uint8_t *buf, int len)
  68{
  69        int i;
  70        uint32_t *buf32;
  71
  72        /* transfer the data from the flash */
  73        buf32 = (uint32_t *)buf;
  74
  75        /*
  76         * Let's take care of unaligned access although it rarely happens.
  77         * Avoid put_unaligned() for the normal use cases since it leads to
  78         * a bit performance regression.
  79         */
  80        if ((unsigned long)buf32 % 4) {
  81                for (i = 0; i < len / 4; i++)
  82                        put_unaligned(readl(denali_flash_mem + INDEX_DATA_REG),
  83                                      buf32++);
  84        } else {
  85                for (i = 0; i < len / 4; i++)
  86                        *buf32++ = readl(denali_flash_mem + INDEX_DATA_REG);
  87        }
  88
  89        if (len % 4) {
  90                u32 tmp;
  91
  92                tmp = cpu_to_le32(readl(denali_flash_mem + INDEX_DATA_REG));
  93                buf = (uint8_t *)buf32;
  94                for (i = 0; i < len % 4; i++) {
  95                        *buf++ = tmp;
  96                        tmp >>= 8;
  97                }
  98        }
  99}
 100
 101int denali_send_pipeline_cmd(int page, int ecc_en, int access_type)
 102{
 103        uint32_t addr, cmd;
 104        static uint32_t page_count = 1;
 105
 106        writel(ecc_en, denali_flash_reg + ECC_ENABLE);
 107
 108        /* clear all bits of intr_status. */
 109        writel(0xffff, denali_flash_reg + INTR_STATUS(flash_bank));
 110
 111        addr = BANK(flash_bank) | page;
 112
 113        /* setup the acccess type */
 114        cmd = DENALI_MAP10 | addr;
 115        index_addr(cmd, access_type);
 116
 117        /* setup the pipeline command */
 118        index_addr(cmd, PIPELINE_ACCESS | page_count);
 119
 120        cmd = DENALI_MAP01 | addr;
 121        writel(cmd, denali_flash_mem + INDEX_CTRL_REG);
 122
 123        return wait_for_irq(INTR__LOAD_COMP);
 124}
 125
 126static int nand_read_oob(void *buf, int page)
 127{
 128        int ret;
 129
 130        ret = denali_send_pipeline_cmd(page, 0, SPARE_ACCESS);
 131        if (ret < 0)
 132                return ret;
 133
 134        read_data_from_flash_mem(buf, oob_size);
 135
 136        return 0;
 137}
 138
 139static int nand_read_page(void *buf, int page)
 140{
 141        int ret;
 142
 143        ret = denali_send_pipeline_cmd(page, 1, MAIN_ACCESS);
 144        if (ret < 0)
 145                return ret;
 146
 147        read_data_from_flash_mem(buf, page_size);
 148
 149        return 0;
 150}
 151
 152static int nand_block_isbad(void *buf, int block)
 153{
 154        int ret;
 155
 156        ret = nand_read_oob(buf, block * pages_per_block);
 157        if (ret < 0)
 158                return ret;
 159
 160        return *((uint8_t *)buf + CONFIG_SYS_NAND_BAD_BLOCK_POS) != 0xff;
 161}
 162
 163/* nand_init() - initialize data to make nand usable by SPL */
 164void nand_init(void)
 165{
 166        /* access to main area */
 167        writel(0, denali_flash_reg + TRANSFER_SPARE_REG);
 168
 169        /*
 170         * These registers are expected to be already set by the hardware
 171         * or earlier boot code.  So we read these values out.
 172         */
 173        page_size = readl(denali_flash_reg + DEVICE_MAIN_AREA_SIZE);
 174        oob_size = readl(denali_flash_reg + DEVICE_SPARE_AREA_SIZE);
 175        pages_per_block = readl(denali_flash_reg + PAGES_PER_BLOCK);
 176}
 177
 178int nand_spl_load_image(uint32_t offs, unsigned int size, void *dst)
 179{
 180        int block, page, column, readlen;
 181        int ret;
 182        int force_bad_block_check = 1;
 183
 184        page = offs / page_size;
 185        column = offs % page_size;
 186
 187        block = page / pages_per_block;
 188        page = page % pages_per_block;
 189
 190        while (size) {
 191                if (force_bad_block_check || page == 0) {
 192                        ret = nand_block_isbad(dst, block);
 193                        if (ret < 0)
 194                                return ret;
 195
 196                        if (ret) {
 197                                block++;
 198                                continue;
 199                        }
 200                }
 201
 202                force_bad_block_check = 0;
 203
 204                ret = nand_read_page(dst, block * pages_per_block + page);
 205                if (ret < 0)
 206                        return ret;
 207
 208                readlen = min(page_size - column, (int)size);
 209
 210                if (unlikely(column)) {
 211                        /* Partial page read */
 212                        memmove(dst, dst + column, readlen);
 213                        column = 0;
 214                }
 215
 216                size -= readlen;
 217                dst += readlen;
 218                page++;
 219                if (page == pages_per_block) {
 220                        block++;
 221                        page = 0;
 222                }
 223        }
 224
 225        return 0;
 226}
 227
 228void nand_deselect(void) {}
 229