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 */ 21 enum onenand_spl_pagesize { 22 PAGE_2K = 2048, 23 PAGE_4K = 4096, 24 }; 25 26 static 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 32 static 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 41 static 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 50 static inline uint16_t onenand_readw(uint32_t addr) 51 { 52 return readw(CONFIG_SYS_ONENAND_BASE + addr); 53 } 54 55 static inline void onenand_writew(uint16_t value, uint32_t addr) 56 { 57 writew(value, CONFIG_SYS_ONENAND_BASE + addr); 58 } 59 60 static 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 82 static 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. */ 119 static 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 */ 132 int 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 168 void 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