]>
Commit | Line | Data |
---|---|---|
54cd51bf MV |
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 | * | |
1a459660 | 8 | * SPDX-License-Identifier: GPL-2.0+ |
54cd51bf MV |
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; | |
f9961378 | 99 | uint32_t to_page; |
54cd51bf MV |
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) { | |
54cd51bf | 112 | page = offs / 2048; |
f9961378 | 113 | to_page = page + DIV_ROUND_UP(size, 2048); |
54cd51bf | 114 | } else { |
54cd51bf | 115 | page = offs / 4096; |
f9961378 | 116 | to_page = page + DIV_ROUND_UP(size, 4096); |
54cd51bf MV |
117 | } |
118 | ||
f9961378 | 119 | for (; page <= to_page; page++) { |
54cd51bf MV |
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); | |
f9961378 | 123 | if (ret) |
54cd51bf | 124 | page += ONENAND_PAGES_PER_BLOCK - 1; |
f9961378 | 125 | else |
54cd51bf | 126 | addr += pagesize / 4; |
54cd51bf MV |
127 | } |
128 | } |