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 void onenand_spl_load_image(uint32_t offs, uint32_t size, void *dst) 97 { 98 uint32_t *addr = (uint32_t *)dst; 99 uint32_t to_page; 100 uint32_t block; 101 uint32_t page, rpage; 102 enum onenand_spl_pagesize pagesize; 103 int ret; 104 105 pagesize = onenand_spl_get_geometry(); 106 107 /* 108 * The page can be either 2k or 4k, avoid using DIV_ROUND_UP to avoid 109 * pulling further unwanted functions into the SPL. 110 */ 111 if (pagesize == 2048) { 112 page = offs / 2048; 113 to_page = page + DIV_ROUND_UP(size, 2048); 114 } else { 115 page = offs / 4096; 116 to_page = page + DIV_ROUND_UP(size, 4096); 117 } 118 119 for (; page <= to_page; page++) { 120 block = page / ONENAND_PAGES_PER_BLOCK; 121 rpage = page & (ONENAND_PAGES_PER_BLOCK - 1); 122 ret = onenand_spl_read_page(block, rpage, addr, pagesize); 123 if (ret) 124 page += ONENAND_PAGES_PER_BLOCK - 1; 125 else 126 addr += pagesize / 4; 127 } 128 } 129