]> git.sur5r.net Git - u-boot/blobdiff - drivers/mtd/onenand/onenand_spl.c
SPDX: Convert all of our single license tags to Linux Kernel style
[u-boot] / drivers / mtd / onenand / onenand_spl.c
index 50eaa71882cf19accbb1d60024350879e01b568b..c4983d15d5117ec5772d2b0221674569629cbdda 100644 (file)
@@ -1,27 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
  *
  * Based on code:
  *     Copyright (C) 2005-2009 Samsung Electronics
  *     Kyungmin Park <kyungmin.park@samsung.com>
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
  */
 
 #include <common.h>
@@ -39,11 +22,29 @@ enum onenand_spl_pagesize {
        PAGE_4K = 4096,
 };
 
+static unsigned int density_mask;
+
 #define ONENAND_PAGES_PER_BLOCK                        64
-#define onenand_block_address(block)           (block)
 #define onenand_sector_address(page)           (page << 2)
 #define onenand_buffer_address()               ((1 << 3) << 8)
-#define onenand_bufferram_address(block)       (0)
+
+static inline int onenand_block_address(int block)
+{
+       /* Device Flash Core select, NAND Flash Block Address */
+       if (block & density_mask)
+               return ONENAND_DDP_CHIP1 | (block ^ density_mask);
+
+       return block;
+}
+
+static inline int onenand_bufferram_address(int block)
+{
+       /* Device BufferRAM Select */
+       if (block & density_mask)
+               return ONENAND_DDP_CHIP1;
+
+       return ONENAND_DDP_CHIP0;
+}
 
 static inline uint16_t onenand_readw(uint32_t addr)
 {
@@ -57,7 +58,7 @@ static inline void onenand_writew(uint16_t value, uint32_t addr)
 
 static enum onenand_spl_pagesize onenand_spl_get_geometry(void)
 {
-       uint32_t dev_id, density;
+       unsigned int dev_id, density, size;
 
        if (!onenand_readw(ONENAND_REG_TECHNOLOGY)) {
                dev_id = onenand_readw(ONENAND_REG_DEVICE_ID);
@@ -67,8 +68,11 @@ static enum onenand_spl_pagesize onenand_spl_get_geometry(void)
                if (density < ONENAND_DEVICE_DENSITY_4Gb)
                        return PAGE_2K;
 
-               if (dev_id & ONENAND_DEVICE_IS_DDP)
+               if (dev_id & ONENAND_DEVICE_IS_DDP) {
+                       size = onenand_readw(ONENAND_REG_DATA_BUFFER_SIZE);
+                       density_mask = 1 << (18 + density - ffs(size));
                        return PAGE_2K;
+               }
        }
 
        return PAGE_4K;
@@ -109,10 +113,61 @@ static int onenand_spl_read_page(uint32_t block, uint32_t page, uint32_t *buf,
        return 0;
 }
 
+#ifdef CONFIG_SPL_UBI
+/* Temporary storage for non page aligned and non page sized reads. */
+static u8 scratch_buf[PAGE_4K];
+
+/**
+ * onenand_spl_read_block - Read data from physical eraseblock into a buffer
+ * @block:     Number of the physical eraseblock
+ * @offset:    Data offset from the start of @peb
+ * @len:       Data size to read
+ * @dst:       Address of the destination buffer
+ *
+ * Notes:
+ *     @offset + @len are not allowed to be larger than a physical
+ *     erase block. No sanity check done for simplicity reasons.
+ */
+int onenand_spl_read_block(int block, int offset, int len, void *dst)
+{
+       int page, read;
+       static int psize;
+
+       if (!psize)
+               psize = onenand_spl_get_geometry();
+
+       /* Calculate the page number */
+       page = offset / psize;
+       /* Offset to the start of a flash page */
+       offset = offset % psize;
+
+       while (len) {
+               /*
+                * Non page aligned reads go to the scratch buffer.
+                * Page aligned reads go directly to the destination.
+                */
+               if (offset || len < psize) {
+                       onenand_spl_read_page(block, page,
+                                             (uint32_t *)scratch_buf, psize);
+                       read = min(len, psize - offset);
+                       memcpy(dst, scratch_buf + offset, read);
+                       offset = 0;
+               } else {
+                       onenand_spl_read_page(block, page, dst, psize);
+                       read = psize;
+               }
+               page++;
+               len -= read;
+               dst += read;
+       }
+       return 0;
+}
+#endif
+
 void onenand_spl_load_image(uint32_t offs, uint32_t size, void *dst)
 {
        uint32_t *addr = (uint32_t *)dst;
-       uint32_t total_pages;
+       uint32_t to_page;
        uint32_t block;
        uint32_t page, rpage;
        enum onenand_spl_pagesize pagesize;
@@ -125,22 +180,20 @@ void onenand_spl_load_image(uint32_t offs, uint32_t size, void *dst)
         * pulling further unwanted functions into the SPL.
         */
        if (pagesize == 2048) {
-               total_pages = DIV_ROUND_UP(size, 2048);
                page = offs / 2048;
+               to_page = page + DIV_ROUND_UP(size, 2048);
        } else {
-               total_pages = DIV_ROUND_UP(size, 4096);
                page = offs / 4096;
+               to_page = page + DIV_ROUND_UP(size, 4096);
        }
 
-       for (; page <= total_pages; page++) {
+       for (; page <= to_page; page++) {
                block = page / ONENAND_PAGES_PER_BLOCK;
                rpage = page & (ONENAND_PAGES_PER_BLOCK - 1);
                ret = onenand_spl_read_page(block, rpage, addr, pagesize);
-               if (ret) {
-                       total_pages += ONENAND_PAGES_PER_BLOCK;
+               if (ret)
                        page += ONENAND_PAGES_PER_BLOCK - 1;
-               } else {
+               else
                        addr += pagesize / 4;
-               }
        }
 }