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 #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 32 static inline uint16_t onenand_readw(uint32_t addr) 33 { 34 return readw(CONFIG_SYS_ONENAND_BASE + addr); 35 } 36 37 static inline void onenand_writew(uint16_t value, uint32_t addr) 38 { 39 writew(value, CONFIG_SYS_ONENAND_BASE + addr); 40 } 41 42 static 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 61 static 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 96 #ifdef CONFIG_SPL_UBI 97 /* Temporary storage for non page aligned and non page sized reads. */ 98 static u8 scratch_buf[PAGE_4K]; 99 100 /** 101 * onenand_spl_read_block - Read data from physical eraseblock into a buffer 102 * @block: Number of the physical eraseblock 103 * @offset: Data offset from the start of @peb 104 * @len: Data size to read 105 * @dst: Address of the destination buffer 106 * 107 * Notes: 108 * @offset + @len are not allowed to be larger than a physical 109 * erase block. No sanity check done for simplicity reasons. 110 */ 111 int onenand_spl_read_block(int block, int offset, int len, void *dst) 112 { 113 int page, read, psize; 114 115 psize = onenand_spl_get_geometry(); 116 /* Calculate the page number */ 117 page = offset / psize; 118 /* Offset to the start of a flash page */ 119 offset = offset % psize; 120 121 while (len) { 122 /* 123 * Non page aligned reads go to the scratch buffer. 124 * Page aligned reads go directly to the destination. 125 */ 126 if (offset || len < psize) { 127 onenand_spl_read_page(block, page, 128 (uint32_t *)scratch_buf, psize); 129 read = min(len, psize - offset); 130 memcpy(dst, scratch_buf + offset, read); 131 offset = 0; 132 } else { 133 onenand_spl_read_page(block, page, dst, psize); 134 read = psize; 135 } 136 page++; 137 len -= read; 138 dst += read; 139 } 140 return 0; 141 } 142 #endif 143 144 void onenand_spl_load_image(uint32_t offs, uint32_t size, void *dst) 145 { 146 uint32_t *addr = (uint32_t *)dst; 147 uint32_t to_page; 148 uint32_t block; 149 uint32_t page, rpage; 150 enum onenand_spl_pagesize pagesize; 151 int ret; 152 153 pagesize = onenand_spl_get_geometry(); 154 155 /* 156 * The page can be either 2k or 4k, avoid using DIV_ROUND_UP to avoid 157 * pulling further unwanted functions into the SPL. 158 */ 159 if (pagesize == 2048) { 160 page = offs / 2048; 161 to_page = page + DIV_ROUND_UP(size, 2048); 162 } else { 163 page = offs / 4096; 164 to_page = page + DIV_ROUND_UP(size, 4096); 165 } 166 167 for (; page <= to_page; page++) { 168 block = page / ONENAND_PAGES_PER_BLOCK; 169 rpage = page & (ONENAND_PAGES_PER_BLOCK - 1); 170 ret = onenand_spl_read_page(block, rpage, addr, pagesize); 171 if (ret) 172 page += ONENAND_PAGES_PER_BLOCK - 1; 173 else 174 addr += pagesize / 4; 175 } 176 } 177