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