uboot/drivers/mtd/onenand/onenand_spl.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
   3 *
   4 * Based on code:
   5 *      Copyright (C) 2005-2009 Samsung Electronics
   6 *      Kyungmin Park <kyungmin.park@samsung.com>
   7 *
   8 * SPDX-License-Identifier:     GPL-2.0+
   9 */
  10
  11#include <common.h>
  12#include <asm/io.h>
  13#include <linux/mtd/onenand_regs.h>
  14#include <onenand_uboot.h>
  15
  16/*
  17 * Device geometry:
  18 * - 2048b page, 128k erase block.
  19 * - 4096b page, 256k erase block.
  20 */
  21enum onenand_spl_pagesize {
  22        PAGE_2K = 2048,
  23        PAGE_4K = 4096,
  24};
  25
  26static unsigned int density_mask;
  27
  28#define ONENAND_PAGES_PER_BLOCK                 64
  29#define onenand_sector_address(page)            (page << 2)
  30#define onenand_buffer_address()                ((1 << 3) << 8)
  31
  32static inline int onenand_block_address(int block)
  33{
  34        /* Device Flash Core select, NAND Flash Block Address */
  35        if (block & density_mask)
  36                return ONENAND_DDP_CHIP1 | (block ^ density_mask);
  37
  38        return block;
  39}
  40
  41static inline int onenand_bufferram_address(int block)
  42{
  43        /* Device BufferRAM Select */
  44        if (block & density_mask)
  45                return ONENAND_DDP_CHIP1;
  46
  47        return ONENAND_DDP_CHIP0;
  48}
  49
  50static inline uint16_t onenand_readw(uint32_t addr)
  51{
  52        return readw(CONFIG_SYS_ONENAND_BASE + addr);
  53}
  54
  55static inline void onenand_writew(uint16_t value, uint32_t addr)
  56{
  57        writew(value, CONFIG_SYS_ONENAND_BASE + addr);
  58}
  59
  60static enum onenand_spl_pagesize onenand_spl_get_geometry(void)
  61{
  62        unsigned int dev_id, density, size;
  63
  64        if (!onenand_readw(ONENAND_REG_TECHNOLOGY)) {
  65                dev_id = onenand_readw(ONENAND_REG_DEVICE_ID);
  66                density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
  67                density &= ONENAND_DEVICE_DENSITY_MASK;
  68
  69                if (density < ONENAND_DEVICE_DENSITY_4Gb)
  70                        return PAGE_2K;
  71
  72                if (dev_id & ONENAND_DEVICE_IS_DDP) {
  73                        size = onenand_readw(ONENAND_REG_DATA_BUFFER_SIZE);
  74                        density_mask = 1 << (18 + density - ffs(size));
  75                        return PAGE_2K;
  76                }
  77        }
  78
  79        return PAGE_4K;
  80}
  81
  82static int onenand_spl_read_page(uint32_t block, uint32_t page, uint32_t *buf,
  83                                        enum onenand_spl_pagesize pagesize)
  84{
  85        const uint32_t addr = CONFIG_SYS_ONENAND_BASE + ONENAND_DATARAM;
  86        uint32_t offset;
  87
  88        onenand_writew(onenand_block_address(block),
  89                        ONENAND_REG_START_ADDRESS1);
  90
  91        onenand_writew(onenand_bufferram_address(block),
  92                        ONENAND_REG_START_ADDRESS2);
  93
  94        onenand_writew(onenand_sector_address(page),
  95                        ONENAND_REG_START_ADDRESS8);
  96
  97        onenand_writew(onenand_buffer_address(),
  98                        ONENAND_REG_START_BUFFER);
  99
 100        onenand_writew(ONENAND_INT_CLEAR, ONENAND_REG_INTERRUPT);
 101
 102        onenand_writew(ONENAND_CMD_READ, ONENAND_REG_COMMAND);
 103
 104        while (!(onenand_readw(ONENAND_REG_INTERRUPT) & ONENAND_INT_READ))
 105                continue;
 106
 107        /* Check for invalid block mark */
 108        if (page < 2 && (onenand_readw(ONENAND_SPARERAM) != 0xffff))
 109                return 1;
 110
 111        for (offset = 0; offset < pagesize; offset += 4)
 112                buf[offset / 4] = readl(addr + offset);
 113
 114        return 0;
 115}
 116
 117#ifdef CONFIG_SPL_UBI
 118/* Temporary storage for non page aligned and non page sized reads. */
 119static u8 scratch_buf[PAGE_4K];
 120
 121/**
 122 * onenand_spl_read_block - Read data from physical eraseblock into a buffer
 123 * @block:      Number of the physical eraseblock
 124 * @offset:     Data offset from the start of @peb
 125 * @len:        Data size to read
 126 * @dst:        Address of the destination buffer
 127 *
 128 * Notes:
 129 *      @offset + @len are not allowed to be larger than a physical
 130 *      erase block. No sanity check done for simplicity reasons.
 131 */
 132int onenand_spl_read_block(int block, int offset, int len, void *dst)
 133{
 134        int page, read;
 135        static int psize;
 136
 137        if (!psize)
 138                psize = onenand_spl_get_geometry();
 139
 140        /* Calculate the page number */
 141        page = offset / psize;
 142        /* Offset to the start of a flash page */
 143        offset = offset % psize;
 144
 145        while (len) {
 146                /*
 147                 * Non page aligned reads go to the scratch buffer.
 148                 * Page aligned reads go directly to the destination.
 149                 */
 150                if (offset || len < psize) {
 151                        onenand_spl_read_page(block, page,
 152                                              (uint32_t *)scratch_buf, psize);
 153                        read = min(len, psize - offset);
 154                        memcpy(dst, scratch_buf + offset, read);
 155                        offset = 0;
 156                } else {
 157                        onenand_spl_read_page(block, page, dst, psize);
 158                        read = psize;
 159                }
 160                page++;
 161                len -= read;
 162                dst += read;
 163        }
 164        return 0;
 165}
 166#endif
 167
 168void onenand_spl_load_image(uint32_t offs, uint32_t size, void *dst)
 169{
 170        uint32_t *addr = (uint32_t *)dst;
 171        uint32_t to_page;
 172        uint32_t block;
 173        uint32_t page, rpage;
 174        enum onenand_spl_pagesize pagesize;
 175        int ret;
 176
 177        pagesize = onenand_spl_get_geometry();
 178
 179        /*
 180         * The page can be either 2k or 4k, avoid using DIV_ROUND_UP to avoid
 181         * pulling further unwanted functions into the SPL.
 182         */
 183        if (pagesize == 2048) {
 184                page = offs / 2048;
 185                to_page = page + DIV_ROUND_UP(size, 2048);
 186        } else {
 187                page = offs / 4096;
 188                to_page = page + DIV_ROUND_UP(size, 4096);
 189        }
 190
 191        for (; page <= to_page; page++) {
 192                block = page / ONENAND_PAGES_PER_BLOCK;
 193                rpage = page & (ONENAND_PAGES_PER_BLOCK - 1);
 194                ret = onenand_spl_read_page(block, rpage, addr, pagesize);
 195                if (ret)
 196                        page += ONENAND_PAGES_PER_BLOCK - 1;
 197                else
 198                        addr += pagesize / 4;
 199        }
 200}
 201