]> 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 fe6b7d923ccc56bf75d9c07d116e50240f5c98be..c4983d15d5117ec5772d2b0221674569629cbdda 100644 (file)
@@ -1,11 +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>
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
@@ -23,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)
 {
@@ -41,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);
@@ -51,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;
@@ -93,6 +113,57 @@ 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;