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
  26#define ONENAND_PAGES_PER_BLOCK                 64
  27#define onenand_block_address(block)            (block)
  28#define onenand_sector_address(page)            (page << 2)
  29#define onenand_buffer_address()                ((1 << 3) << 8)
  30#define onenand_bufferram_address(block)        (0)
  31
  32static inline uint16_t onenand_readw(uint32_t addr)
  33{
  34        return readw(CONFIG_SYS_ONENAND_BASE + addr);
  35}
  36
  37static inline void onenand_writew(uint16_t value, uint32_t addr)
  38{
  39        writew(value, CONFIG_SYS_ONENAND_BASE + addr);
  40}
  41
  42static enum onenand_spl_pagesize onenand_spl_get_geometry(void)
  43{
  44        uint32_t dev_id, density;
  45
  46        if (!onenand_readw(ONENAND_REG_TECHNOLOGY)) {
  47                dev_id = onenand_readw(ONENAND_REG_DEVICE_ID);
  48                density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
  49                density &= ONENAND_DEVICE_DENSITY_MASK;
  50
  51                if (density < ONENAND_DEVICE_DENSITY_4Gb)
  52                        return PAGE_2K;
  53
  54                if (dev_id & ONENAND_DEVICE_IS_DDP)
  55                        return PAGE_2K;
  56        }
  57
  58        return PAGE_4K;
  59}
  60
  61static int onenand_spl_read_page(uint32_t block, uint32_t page, uint32_t *buf,
  62                                        enum onenand_spl_pagesize pagesize)
  63{
  64        const uint32_t addr = CONFIG_SYS_ONENAND_BASE + ONENAND_DATARAM;
  65        uint32_t offset;
  66
  67        onenand_writew(onenand_block_address(block),
  68                        ONENAND_REG_START_ADDRESS1);
  69
  70        onenand_writew(onenand_bufferram_address(block),
  71                        ONENAND_REG_START_ADDRESS2);
  72
  73        onenand_writew(onenand_sector_address(page),
  74                        ONENAND_REG_START_ADDRESS8);
  75
  76        onenand_writew(onenand_buffer_address(),
  77                        ONENAND_REG_START_BUFFER);
  78
  79        onenand_writew(ONENAND_INT_CLEAR, ONENAND_REG_INTERRUPT);
  80
  81        onenand_writew(ONENAND_CMD_READ, ONENAND_REG_COMMAND);
  82
  83        while (!(onenand_readw(ONENAND_REG_INTERRUPT) & ONENAND_INT_READ))
  84                continue;
  85
  86        /* Check for invalid block mark */
  87        if (page < 2 && (onenand_readw(ONENAND_SPARERAM) != 0xffff))
  88                return 1;
  89
  90        for (offset = 0; offset < pagesize; offset += 4)
  91                buf[offset / 4] = readl(addr + offset);
  92
  93        return 0;
  94}
  95
  96void onenand_spl_load_image(uint32_t offs, uint32_t size, void *dst)
  97{
  98        uint32_t *addr = (uint32_t *)dst;
  99        uint32_t to_page;
 100        uint32_t block;
 101        uint32_t page, rpage;
 102        enum onenand_spl_pagesize pagesize;
 103        int ret;
 104
 105        pagesize = onenand_spl_get_geometry();
 106
 107        /*
 108         * The page can be either 2k or 4k, avoid using DIV_ROUND_UP to avoid
 109         * pulling further unwanted functions into the SPL.
 110         */
 111        if (pagesize == 2048) {
 112                page = offs / 2048;
 113                to_page = page + DIV_ROUND_UP(size, 2048);
 114        } else {
 115                page = offs / 4096;
 116                to_page = page + DIV_ROUND_UP(size, 4096);
 117        }
 118
 119        for (; page <= to_page; page++) {
 120                block = page / ONENAND_PAGES_PER_BLOCK;
 121                rpage = page & (ONENAND_PAGES_PER_BLOCK - 1);
 122                ret = onenand_spl_read_page(block, rpage, addr, pagesize);
 123                if (ret)
 124                        page += ONENAND_PAGES_PER_BLOCK - 1;
 125                else
 126                        addr += pagesize / 4;
 127        }
 128}
 129