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