diff options
Diffstat (limited to 'firmware/target/mips/ingenic_x1000/nand-x1000.c')
-rw-r--r-- | firmware/target/mips/ingenic_x1000/nand-x1000.c | 686 |
1 files changed, 300 insertions, 386 deletions
diff --git a/firmware/target/mips/ingenic_x1000/nand-x1000.c b/firmware/target/mips/ingenic_x1000/nand-x1000.c index 5a21b1f8c2..b76efe65e5 100644 --- a/firmware/target/mips/ingenic_x1000/nand-x1000.c +++ b/firmware/target/mips/ingenic_x1000/nand-x1000.c | |||
@@ -22,480 +22,394 @@ | |||
22 | #include "nand-x1000.h" | 22 | #include "nand-x1000.h" |
23 | #include "sfc-x1000.h" | 23 | #include "sfc-x1000.h" |
24 | #include "system.h" | 24 | #include "system.h" |
25 | #include <stddef.h> | 25 | #include <string.h> |
26 | 26 | ||
27 | /* NAND command numbers */ | 27 | /* cmd mode a d phase format has data */ |
28 | #define NAND_CMD_READ_ID 0x9f | 28 | #define NANDCMD_RESET SFC_CMD(0xff, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 0) |
29 | #define NAND_CMD_WRITE_ENABLE 0x06 | 29 | #define NANDCMD_READID(x,y) SFC_CMD(0x9f, SFC_TMODE_1_1_1, x, y, SFC_PFMT_ADDR_FIRST, 1) |
30 | #define NAND_CMD_GET_FEATURE 0x0f | 30 | #define NANDCMD_WR_EN SFC_CMD(0x06, SFC_TMODE_1_1_1, 0, 0, SFC_PFMT_ADDR_FIRST, 0) |
31 | #define NAND_CMD_SET_FEATURE 0x1f | 31 | #define NANDCMD_GET_FEATURE SFC_CMD(0x0f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1) |
32 | #define NAND_CMD_PAGE_READ_TO_CACHE 0x13 | 32 | #define NANDCMD_SET_FEATURE SFC_CMD(0x1f, SFC_TMODE_1_1_1, 1, 0, SFC_PFMT_ADDR_FIRST, 1) |
33 | #define NAND_CMD_READ_FROM_CACHE 0x0b | 33 | #define NANDCMD_PAGE_READ(x) SFC_CMD(0x13, SFC_TMODE_1_1_1, x, 0, SFC_PFMT_ADDR_FIRST, 0) |
34 | #define NAND_CMD_READ_FROM_CACHEx4 0x6b | 34 | #define NANDCMD_READ_CACHE(x) SFC_CMD(0x0b, SFC_TMODE_1_1_1, x, 8, SFC_PFMT_ADDR_FIRST, 1) |
35 | #define NAND_CMD_PROGRAM_LOAD 0x02 | 35 | #define NANDCMD_READ_CACHE_x4(x) SFC_CMD(0x6b, SFC_TMODE_1_1_4, x, 8, SFC_PFMT_ADDR_FIRST, 1) |
36 | #define NAND_CMD_PROGRAM_LOADx4 0x32 | 36 | #define NANDCMD_PROGRAM_LOAD(x) SFC_CMD(0x02, SFC_TMODE_1_1_1, x, 0, SFC_PFMT_ADDR_FIRST, 1) |
37 | #define NAND_CMD_PROGRAM_EXECUTE 0x10 | 37 | #define NANDCMD_PROGRAM_LOAD_x4(x) SFC_CMD(0x32, SFC_TMODE_1_1_4, x, 0, SFC_PFMT_ADDR_FIRST, 1) |
38 | #define NAND_CMD_BLOCK_ERASE 0xd8 | 38 | #define NANDCMD_PROGRAM_EXECUTE(x) SFC_CMD(0x10, SFC_TMODE_1_1_1, x, 0, SFC_PFMT_ADDR_FIRST, 0) |
39 | 39 | #define NANDCMD_BLOCK_ERASE(x) SFC_CMD(0xd8, SFC_TMODE_1_1_1, x, 0, SFC_PFMT_ADDR_FIRST, 0) | |
40 | /* NAND device register addresses for GET_FEATURE / SET_FEATURE */ | 40 | |
41 | #define NAND_FREG_PROTECTION 0xa0 | 41 | /* Feature registers are found in linux/mtd/spinand.h, |
42 | #define NAND_FREG_FEATURE 0xb0 | 42 | * apparently these are pretty standardized */ |
43 | #define NAND_FREG_STATUS 0xc0 | 43 | #define FREG_PROT 0xa0 |
44 | 44 | #define FREG_PROT_UNLOCK 0x00 | |
45 | /* Protection register bits */ | 45 | |
46 | #define NAND_FREG_PROTECTION_BRWD 0x80 | 46 | #define FREG_CFG 0xb0 |
47 | #define NAND_FREG_PROTECTION_BP2 0x20 | 47 | #define FREG_CFG_OTP_ENABLE (1 << 6) |
48 | #define NAND_FREG_PROTECTION_BP1 0x10 | 48 | #define FREG_CFG_ECC_ENABLE (1 << 4) |
49 | #define NAND_FREG_PROTECTION_BP0 0x08 | 49 | #define FREG_CFG_QUAD_ENABLE (1 << 0) |
50 | /* Mask of BP bits 0-2 */ | 50 | |
51 | #define NAND_FREG_PROTECTION_ALLBP 0x38 | 51 | #define FREG_STATUS 0xc0 |
52 | 52 | #define FREG_STATUS_BUSY (1 << 0) | |
53 | /* Feature register bits */ | 53 | #define FREG_STATUS_EFAIL (1 << 2) |
54 | #define NAND_FREG_FEATURE_QE 0x01 | 54 | #define FREG_STATUS_PFAIL (1 << 3) |
55 | 55 | #define FREG_STATUS_ECC_MASK (3 << 4) | |
56 | /* Status register bits */ | 56 | #define FREG_STATUS_ECC_NO_FLIPS (0 << 4) |
57 | #define NAND_FREG_STATUS_OIP 0x01 | 57 | #define FREG_STATUS_ECC_HAS_FLIPS (1 << 4) |
58 | #define NAND_FREG_STATUS_WEL 0x02 | 58 | #define FREG_STATUS_ECC_UNCOR_ERR (2 << 4) |
59 | #define NAND_FREG_STATUS_E_FAIL 0x04 | 59 | |
60 | #define NAND_FREG_STATUS_P_FAIL 0x08 | 60 | const nand_chip supported_nand_chips[] = { |
61 | 61 | #if defined(FIIO_M3K) | |
62 | /* NAND chip config */ | ||
63 | const nand_chip_data target_nand_chip_data[] = { | ||
64 | #ifdef FIIO_M3K | ||
65 | { | 62 | { |
66 | /* ATO25D1GA */ | 63 | /* ATO25D1GA */ |
67 | .mf_id = 0x9b, | 64 | .mf_id = 0x9b, |
68 | .dev_id = 0x12, | 65 | .dev_id = 0x12, |
69 | .dev_conf = jz_orf(SFC_DEV_CONF, CE_DL(1), HOLD_DL(1), WP_DL(1), | 66 | .row_cycles = 3, |
70 | CPHA(0), CPOL(0), TSH(7), TSETUP(0), THOLD(0), | 67 | .col_cycles = 2, |
71 | STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), SMP_DELAY(1)), | 68 | .log2_ppb = 6, /* 64 pages */ |
69 | .page_size = 2048, | ||
70 | .oob_size = 64, | ||
71 | .nr_blocks = 1024, | ||
72 | .clock_freq = 150000000, | 72 | .clock_freq = 150000000, |
73 | .log2_page_size = 11, /* = 2048 bytes */ | 73 | .dev_conf = jz_orf(SFC_DEV_CONF, |
74 | .log2_block_size = 6, /* = 64 pages */ | 74 | CE_DL(1), HOLD_DL(1), WP_DL(1), |
75 | .rowaddr_width = 3, | 75 | CPHA(0), CPOL(0), |
76 | .coladdr_width = 2, | 76 | TSH(7), TSETUP(0), THOLD(0), |
77 | .flags = NANDCHIP_FLAG_QUAD, | 77 | STA_TYPE_V(1BYTE), CMD_TYPE_V(8BITS), |
78 | } | 78 | SMP_DELAY(1)), |
79 | .flags = NAND_CHIPFLAG_QUAD | NAND_CHIPFLAG_HAS_QE_BIT, | ||
80 | }, | ||
79 | #else | 81 | #else |
80 | /* Nobody will use this anyway if the device has no NAND flash */ | ||
81 | { 0 }, | 82 | { 0 }, |
82 | #endif | 83 | #endif |
83 | }; | 84 | }; |
84 | 85 | ||
85 | const size_t target_nand_chip_count = | 86 | const size_t nr_supported_nand_chips = |
86 | sizeof(target_nand_chip_data) / sizeof(nand_chip_data); | 87 | sizeof(supported_nand_chips) / sizeof(nand_chip); |
87 | |||
88 | /* NAND ops -- high level primitives used by the driver */ | ||
89 | static int nandop_wait_status(int errbit); | ||
90 | static int nandop_read_page(uint32_t row_addr, uint8_t* buf); | ||
91 | static int nandop_write_page(uint32_t row_addr, const uint8_t* buf); | ||
92 | static int nandop_erase_block(uint32_t block_addr); | ||
93 | static int nandop_set_write_protect(bool en); | ||
94 | |||
95 | /* NAND commands -- 1-to-1 mapping between chip commands and functions */ | ||
96 | static int nandcmd_read_id(int* mf_id, int* dev_id); | ||
97 | static int nandcmd_write_enable(void); | ||
98 | static int nandcmd_get_feature(uint8_t reg); | ||
99 | static int nandcmd_set_feature(uint8_t reg, uint8_t val); | ||
100 | static int nandcmd_page_read_to_cache(uint32_t row_addr); | ||
101 | static int nandcmd_read_from_cache(uint8_t* buf); | ||
102 | static int nandcmd_program_load(const uint8_t* buf); | ||
103 | static int nandcmd_program_execute(uint32_t row_addr); | ||
104 | static int nandcmd_block_erase(uint32_t block_addr); | ||
105 | |||
106 | struct nand_drv { | ||
107 | const nand_chip_data* chip_data; | ||
108 | bool write_enabled; | ||
109 | }; | ||
110 | 88 | ||
111 | static struct nand_drv nand_drv; | 89 | static nand_drv static_nand_drv; |
112 | static uint8_t nand_auxbuf[32] CACHEALIGN_ATTR; | 90 | static uint8_t static_scratch_buf[NAND_DRV_SCRATCHSIZE] CACHEALIGN_ATTR; |
91 | static uint8_t static_page_buf[NAND_DRV_MAXPAGESIZE] CACHEALIGN_ATTR; | ||
113 | 92 | ||
114 | static void nand_drv_reset(void) | 93 | nand_drv* nand_init(void) |
115 | { | 94 | { |
116 | nand_drv.chip_data = NULL; | 95 | static bool inited = false; |
117 | nand_drv.write_enabled = false; | 96 | if(!inited) { |
97 | mutex_init(&static_nand_drv.mutex); | ||
98 | static_nand_drv.scratch_buf = static_scratch_buf; | ||
99 | static_nand_drv.page_buf = static_page_buf; | ||
100 | static_nand_drv.refcount = 0; | ||
101 | } | ||
102 | |||
103 | return &static_nand_drv; | ||
118 | } | 104 | } |
119 | 105 | ||
120 | int nand_open(void) | 106 | static uint8_t nand_get_reg(nand_drv* drv, uint8_t reg) |
121 | { | 107 | { |
122 | sfc_init(); | 108 | sfc_exec(NANDCMD_GET_FEATURE, reg, drv->scratch_buf, 1|SFC_READ); |
123 | sfc_lock(); | 109 | return drv->scratch_buf[0]; |
124 | |||
125 | nand_drv_reset(); | ||
126 | sfc_open(); | ||
127 | |||
128 | const nand_chip_data* chip_data = &target_nand_chip_data[0]; | ||
129 | sfc_set_dev_conf(chip_data->dev_conf); | ||
130 | sfc_set_clock(chip_data->clock_freq); | ||
131 | |||
132 | sfc_unlock(); | ||
133 | return NAND_SUCCESS; | ||
134 | } | 110 | } |
135 | 111 | ||
136 | void nand_close(void) | 112 | static void nand_set_reg(nand_drv* drv, uint8_t reg, uint8_t val) |
137 | { | 113 | { |
138 | sfc_lock(); | 114 | drv->scratch_buf[0] = val; |
139 | sfc_close(); | 115 | sfc_exec(NANDCMD_SET_FEATURE, reg, drv->scratch_buf, 1|SFC_WRITE); |
140 | nand_drv_reset(); | ||
141 | sfc_unlock(); | ||
142 | } | 116 | } |
143 | 117 | ||
144 | int nand_identify(int* mf_id, int* dev_id) | 118 | static void nand_upd_reg(nand_drv* drv, uint8_t reg, uint8_t msk, uint8_t val) |
145 | { | 119 | { |
146 | sfc_lock(); | 120 | uint8_t x = nand_get_reg(drv, reg); |
147 | 121 | x &= ~msk; | |
148 | int status = nandcmd_read_id(mf_id, dev_id); | 122 | x |= val; |
149 | if(status < 0) | 123 | nand_set_reg(drv, reg, x); |
150 | goto error; | 124 | } |
151 | 125 | ||
152 | for(size_t i = 0; i < target_nand_chip_count; ++i) { | 126 | static bool identify_chip(nand_drv* drv) |
153 | const nand_chip_data* data = &target_nand_chip_data[i]; | 127 | { |
154 | if(data->mf_id == *mf_id && data->dev_id == *dev_id) { | 128 | /* Read ID command has some variations; Linux handles these 3: |
155 | nand_drv.chip_data = data; | 129 | * - no address or dummy bytes |
156 | break; | 130 | * - 1 byte address, no dummy byte |
131 | * - no address byte, 1 byte dummy | ||
132 | * | ||
133 | * Right now there is only a need for the 2nd variation, as that is | ||
134 | * the method used by the ATO25D1GA. | ||
135 | * | ||
136 | * Some chips also output more than 2 ID bytes. | ||
137 | */ | ||
138 | sfc_exec(NANDCMD_READID(1, 0), 0, drv->scratch_buf, 2|SFC_READ); | ||
139 | drv->mf_id = drv->scratch_buf[0]; | ||
140 | drv->dev_id = drv->scratch_buf[1]; | ||
141 | |||
142 | for(size_t i = 0; i < nr_supported_nand_chips; ++i) { | ||
143 | const nand_chip* chip = &supported_nand_chips[i]; | ||
144 | if(chip->mf_id == drv->mf_id && chip->dev_id == drv->dev_id) { | ||
145 | drv->chip = chip; | ||
146 | return true; | ||
157 | } | 147 | } |
158 | } | 148 | } |
159 | 149 | ||
160 | if(!nand_drv.chip_data) { | 150 | return false; |
161 | status = NAND_ERR_UNKNOWN_CHIP; | 151 | } |
162 | goto error; | ||
163 | } | ||
164 | |||
165 | /* Set parameters according to new chip data */ | ||
166 | sfc_set_dev_conf(nand_drv.chip_data->dev_conf); | ||
167 | sfc_set_clock(nand_drv.chip_data->clock_freq); | ||
168 | status = NAND_SUCCESS; | ||
169 | 152 | ||
170 | error: | 153 | static void setup_chip_data(nand_drv* drv) |
171 | sfc_unlock(); | 154 | { |
172 | return status; | 155 | drv->ppb = 1 << drv->chip->log2_ppb; |
156 | drv->fpage_size = drv->chip->page_size + drv->chip->oob_size; | ||
173 | } | 157 | } |
174 | 158 | ||
175 | const nand_chip_data* nand_get_chip_data(void) | 159 | static void setup_chip_commands(nand_drv* drv) |
176 | { | 160 | { |
177 | return nand_drv.chip_data; | 161 | /* Select commands appropriate for the chip */ |
162 | drv->cmd_page_read = NANDCMD_PAGE_READ(drv->chip->row_cycles); | ||
163 | drv->cmd_program_execute = NANDCMD_PROGRAM_EXECUTE(drv->chip->row_cycles); | ||
164 | drv->cmd_block_erase = NANDCMD_BLOCK_ERASE(drv->chip->row_cycles); | ||
165 | |||
166 | if(drv->chip->flags & NAND_CHIPFLAG_QUAD) { | ||
167 | drv->cmd_read_cache = NANDCMD_READ_CACHE_x4(drv->chip->col_cycles); | ||
168 | drv->cmd_program_load = NANDCMD_PROGRAM_LOAD_x4(drv->chip->col_cycles); | ||
169 | } else { | ||
170 | drv->cmd_read_cache = NANDCMD_READ_CACHE(drv->chip->col_cycles); | ||
171 | drv->cmd_program_load = NANDCMD_PROGRAM_LOAD(drv->chip->col_cycles); | ||
172 | } | ||
178 | } | 173 | } |
179 | 174 | ||
180 | extern int nand_enable_writes(bool en) | 175 | static void setup_chip_registers(nand_drv* drv) |
181 | { | 176 | { |
182 | if(en == nand_drv.write_enabled) | 177 | /* Set chip registers to enter normal operation */ |
183 | return NAND_SUCCESS; | 178 | if(drv->chip->flags & NAND_CHIPFLAG_HAS_QE_BIT) { |
179 | bool en = (drv->chip->flags & NAND_CHIPFLAG_QUAD) != 0; | ||
180 | nand_upd_reg(drv, FREG_CFG, FREG_CFG_QUAD_ENABLE, | ||
181 | en ? FREG_CFG_QUAD_ENABLE : 0); | ||
182 | } | ||
184 | 183 | ||
185 | int rc = nandop_set_write_protect(!en); | 184 | /* Clear OTP bit to access the main data array */ |
186 | if(rc == NAND_SUCCESS) | 185 | nand_upd_reg(drv, FREG_CFG, FREG_CFG_OTP_ENABLE, 0); |
187 | nand_drv.write_enabled = en; | ||
188 | 186 | ||
189 | return rc; | 187 | /* Clear write protection bits */ |
188 | nand_set_reg(drv, FREG_PROT, FREG_PROT_UNLOCK); | ||
190 | } | 189 | } |
191 | 190 | ||
192 | static int nand_rdwr(bool write, uint32_t addr, uint32_t size, uint8_t* buf) | 191 | int nand_open(nand_drv* drv) |
193 | { | 192 | { |
194 | const uint32_t page_size = (1 << nand_drv.chip_data->log2_page_size); | 193 | if(drv->refcount > 0) |
195 | |||
196 | if(addr & (page_size - 1)) | ||
197 | return NAND_ERR_UNALIGNED; | ||
198 | if(size & (page_size - 1)) | ||
199 | return NAND_ERR_UNALIGNED; | ||
200 | if(size <= 0) | ||
201 | return NAND_SUCCESS; | 194 | return NAND_SUCCESS; |
202 | if(write && !nand_drv.write_enabled) | ||
203 | return NAND_ERR_WRITE_PROTECT; | ||
204 | if((uint32_t)buf & (CACHEALIGN_SIZE - 1)) | ||
205 | return NAND_ERR_UNALIGNED; | ||
206 | 195 | ||
207 | addr >>= nand_drv.chip_data->log2_page_size; | 196 | /* Initialize the controller */ |
208 | size >>= nand_drv.chip_data->log2_page_size; | 197 | sfc_open(); |
198 | sfc_set_dev_conf(supported_nand_chips[0].dev_conf); | ||
199 | sfc_set_clock(supported_nand_chips[0].clock_freq); | ||
209 | 200 | ||
210 | int rc = NAND_SUCCESS; | 201 | /* Send the software reset command */ |
211 | sfc_lock(); | 202 | sfc_exec(NANDCMD_RESET, 0, NULL, 0); |
203 | mdelay(10); | ||
212 | 204 | ||
213 | for(; size > 0; --size, ++addr, buf += page_size) { | 205 | /* Chip identification and setup */ |
214 | if(write) | 206 | if(!identify_chip(drv)) |
215 | rc = nandop_write_page(addr, buf); | 207 | return NAND_ERR_UNKNOWN_CHIP; |
216 | else | ||
217 | rc = nandop_read_page(addr, buf); | ||
218 | 208 | ||
219 | if(rc) | 209 | setup_chip_data(drv); |
220 | break; | 210 | setup_chip_commands(drv); |
221 | } | ||
222 | 211 | ||
223 | sfc_unlock(); | 212 | /* Set new SFC parameters */ |
224 | return rc; | 213 | sfc_set_dev_conf(drv->chip->dev_conf); |
225 | } | 214 | sfc_set_clock(drv->chip->clock_freq); |
226 | 215 | ||
227 | int nand_read(uint32_t addr, uint32_t size, uint8_t* buf) | 216 | /* Enter normal operating mode */ |
228 | { | 217 | setup_chip_registers(drv); |
229 | return nand_rdwr(false, addr, size, buf); | ||
230 | } | ||
231 | 218 | ||
232 | int nand_write(uint32_t addr, uint32_t size, const uint8_t* buf) | 219 | drv->refcount++; |
233 | { | 220 | return NAND_SUCCESS; |
234 | return nand_rdwr(true, addr, size, (uint8_t*)buf); | ||
235 | } | 221 | } |
236 | 222 | ||
237 | int nand_erase(uint32_t addr, uint32_t size) | 223 | void nand_close(nand_drv* drv) |
238 | { | 224 | { |
239 | const uint32_t page_size = 1 << nand_drv.chip_data->log2_page_size; | 225 | if(drv->refcount == 0) |
240 | const uint32_t block_size = page_size << nand_drv.chip_data->log2_block_size; | 226 | return; |
241 | const uint32_t pages_per_block = 1 << nand_drv.chip_data->log2_block_size; | ||
242 | 227 | ||
243 | if(addr & (block_size - 1)) | 228 | /* Let's reset the chip... the idea is to restore the registers |
244 | return NAND_ERR_UNALIGNED; | 229 | * to whatever they should "normally" be */ |
245 | if(size & (block_size - 1)) | 230 | sfc_exec(NANDCMD_RESET, 0, NULL, 0); |
246 | return NAND_ERR_UNALIGNED; | 231 | mdelay(10); |
247 | if(size <= 0) | ||
248 | return NAND_SUCCESS; | ||
249 | if(!nand_drv.write_enabled) | ||
250 | return NAND_ERR_WRITE_PROTECT; | ||
251 | |||
252 | addr >>= nand_drv.chip_data->log2_page_size; | ||
253 | size >>= nand_drv.chip_data->log2_page_size; | ||
254 | size >>= nand_drv.chip_data->log2_block_size; | ||
255 | 232 | ||
256 | int rc = NAND_SUCCESS; | 233 | sfc_close(); |
257 | sfc_lock(); | 234 | drv->refcount--; |
258 | |||
259 | for(; size > 0; --size, addr += pages_per_block) | ||
260 | if((rc = nandop_erase_block(addr))) | ||
261 | break; | ||
262 | |||
263 | sfc_unlock(); | ||
264 | return rc; | ||
265 | } | 235 | } |
266 | 236 | ||
267 | /* | 237 | static uint8_t nand_wait_busy(nand_drv* drv) |
268 | * NAND ops | ||
269 | */ | ||
270 | |||
271 | static int nandop_wait_status(int errbit) | ||
272 | { | 238 | { |
273 | int reg; | 239 | uint8_t reg; |
274 | do { | 240 | do { |
275 | reg = nandcmd_get_feature(NAND_FREG_STATUS); | 241 | reg = nand_get_reg(drv, FREG_STATUS); |
276 | if(reg < 0) | 242 | } while(reg & FREG_STATUS_BUSY); |
277 | return reg; | ||
278 | } while(reg & NAND_FREG_STATUS_OIP); | ||
279 | |||
280 | if(reg & errbit) | ||
281 | return NAND_ERR_COMMAND; | ||
282 | |||
283 | return reg; | 243 | return reg; |
284 | } | 244 | } |
285 | 245 | ||
286 | static int nandop_read_page(uint32_t row_addr, uint8_t* buf) | 246 | int nand_block_erase(nand_drv* drv, nand_block_t block) |
287 | { | 247 | { |
288 | int status; | 248 | sfc_exec(NANDCMD_WR_EN, 0, NULL, 0); |
289 | 249 | sfc_exec(drv->cmd_block_erase, block, NULL, 0); | |
290 | if((status = nandcmd_page_read_to_cache(row_addr)) < 0) | ||
291 | return status; | ||
292 | if((status = nandop_wait_status(0)) < 0) | ||
293 | return status; | ||
294 | if((status = nandcmd_read_from_cache(buf)) < 0) | ||
295 | return status; | ||
296 | 250 | ||
297 | return NAND_SUCCESS; | 251 | uint8_t status = nand_wait_busy(drv); |
252 | if(status & FREG_STATUS_EFAIL) | ||
253 | return NAND_ERR_ERASE_FAIL; | ||
254 | else | ||
255 | return NAND_SUCCESS; | ||
298 | } | 256 | } |
299 | 257 | ||
300 | static int nandop_write_page(uint32_t row_addr, const uint8_t* buf) | 258 | int nand_page_program(nand_drv* drv, nand_page_t page, const void* buffer) |
301 | { | 259 | { |
302 | int status; | 260 | sfc_exec(NANDCMD_WR_EN, 0, NULL, 0); |
303 | 261 | sfc_exec(drv->cmd_program_load, 0, (void*)buffer, drv->fpage_size|SFC_WRITE); | |
304 | if((status = nandcmd_write_enable()) < 0) | 262 | sfc_exec(drv->cmd_program_execute, page, NULL, 0); |
305 | return status; | 263 | |
306 | if((status = nandcmd_program_load(buf)) < 0) | 264 | uint8_t status = nand_wait_busy(drv); |
307 | return status; | 265 | if(status & FREG_STATUS_PFAIL) |
308 | if((status = nandcmd_program_execute(row_addr)) < 0) | 266 | return NAND_ERR_PROGRAM_FAIL; |
309 | return status; | 267 | else |
310 | if((status = nandop_wait_status(NAND_FREG_STATUS_P_FAIL)) < 0) | 268 | return NAND_SUCCESS; |
311 | return status; | ||
312 | |||
313 | return NAND_SUCCESS; | ||
314 | } | 269 | } |
315 | 270 | ||
316 | static int nandop_erase_block(uint32_t block_addr) | 271 | int nand_page_read(nand_drv* drv, nand_page_t page, void* buffer) |
317 | { | 272 | { |
318 | int status; | 273 | sfc_exec(drv->cmd_page_read, page, NULL, 0); |
319 | 274 | nand_wait_busy(drv); | |
320 | if((status = nandcmd_write_enable()) < 0) | 275 | sfc_exec(drv->cmd_read_cache, 0, buffer, drv->fpage_size|SFC_READ); |
321 | return status; | ||
322 | if((status = nandcmd_block_erase(block_addr)) < 0) | ||
323 | return status; | ||
324 | if((status = nandop_wait_status(NAND_FREG_STATUS_E_FAIL)) < 0) | ||
325 | return status; | ||
326 | |||
327 | return NAND_SUCCESS; | 276 | return NAND_SUCCESS; |
328 | } | 277 | } |
329 | 278 | ||
330 | static int nandop_set_write_protect(bool en) | 279 | int nand_read_bytes(nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, void* buffer) |
331 | { | 280 | { |
332 | int val = nandcmd_get_feature(NAND_FREG_PROTECTION); | 281 | if(byte_len == 0) |
333 | if(val < 0) | 282 | return NAND_SUCCESS; |
334 | return val; | ||
335 | |||
336 | if(en) { | ||
337 | val |= NAND_FREG_PROTECTION_ALLBP; | ||
338 | if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD) | ||
339 | val |= NAND_FREG_PROTECTION_BRWD; | ||
340 | } else { | ||
341 | val &= ~NAND_FREG_PROTECTION_ALLBP; | ||
342 | if(nand_drv.chip_data->flags & NANDCHIP_FLAG_USE_BRWD) | ||
343 | val &= ~NAND_FREG_PROTECTION_BRWD; | ||
344 | } | ||
345 | |||
346 | /* NOTE: The WP pin typically only protects changes to the protection | ||
347 | * register -- it doesn't actually prevent writing to the chip. That's | ||
348 | * why it should be re-enabled after setting the new protection status. | ||
349 | */ | ||
350 | sfc_set_wp_enable(false); | ||
351 | int status = nandcmd_set_feature(NAND_FREG_PROTECTION, val); | ||
352 | sfc_set_wp_enable(true); | ||
353 | |||
354 | if(status < 0) | ||
355 | return status; | ||
356 | 283 | ||
357 | return NAND_SUCCESS; | 284 | int rc; |
358 | } | 285 | unsigned pg_size = drv->chip->page_size; |
286 | nand_page_t page = byte_addr / pg_size; | ||
287 | unsigned offset = byte_addr % pg_size; | ||
288 | while(1) { | ||
289 | rc = nand_page_read(drv, page, drv->page_buf); | ||
290 | if(rc < 0) | ||
291 | return rc; | ||
359 | 292 | ||
360 | /* | 293 | memcpy(buffer, &drv->page_buf[offset], MIN(pg_size, byte_len)); |
361 | * Low-level NAND commands | ||
362 | */ | ||
363 | 294 | ||
364 | static int nandcmd_read_id(int* mf_id, int* dev_id) | 295 | if(byte_len <= pg_size) |
365 | { | 296 | break; |
366 | sfc_op op = {0}; | ||
367 | op.command = NAND_CMD_READ_ID; | ||
368 | op.flags = SFC_FLAG_READ; | ||
369 | op.addr_bytes = 1; | ||
370 | op.addr_lo = 0; | ||
371 | op.data_bytes = 2; | ||
372 | op.buffer = nand_auxbuf; | ||
373 | if(sfc_exec(&op)) | ||
374 | return NAND_ERR_CONTROLLER; | ||
375 | |||
376 | *mf_id = nand_auxbuf[0]; | ||
377 | *dev_id = nand_auxbuf[1]; | ||
378 | return NAND_SUCCESS; | ||
379 | } | ||
380 | 297 | ||
381 | static int nandcmd_write_enable(void) | 298 | offset = 0; |
382 | { | 299 | byte_len -= pg_size; |
383 | sfc_op op = {0}; | 300 | buffer += pg_size; |
384 | op.command = NAND_CMD_WRITE_ENABLE; | 301 | page++; |
385 | if(sfc_exec(&op)) | 302 | } |
386 | return NAND_ERR_CONTROLLER; | ||
387 | 303 | ||
388 | return NAND_SUCCESS; | 304 | return NAND_SUCCESS; |
389 | } | 305 | } |
390 | 306 | ||
391 | static int nandcmd_get_feature(uint8_t reg) | 307 | int nand_write_bytes(nand_drv* drv, uint32_t byte_addr, uint32_t byte_len, const void* buffer) |
392 | { | 308 | { |
393 | sfc_op op = {0}; | 309 | if(byte_len == 0) |
394 | op.command = NAND_CMD_GET_FEATURE; | 310 | return NAND_SUCCESS; |
395 | op.flags = SFC_FLAG_READ; | ||
396 | op.addr_bytes = 1; | ||
397 | op.addr_lo = reg; | ||
398 | op.data_bytes = 1; | ||
399 | op.buffer = nand_auxbuf; | ||
400 | if(sfc_exec(&op)) | ||
401 | return NAND_ERR_CONTROLLER; | ||
402 | |||
403 | return nand_auxbuf[0]; | ||
404 | } | ||
405 | |||
406 | static int nandcmd_set_feature(uint8_t reg, uint8_t val) | ||
407 | { | ||
408 | sfc_op op = {0}; | ||
409 | op.command = NAND_CMD_SET_FEATURE; | ||
410 | op.flags = SFC_FLAG_WRITE; | ||
411 | op.addr_bytes = 1; | ||
412 | op.addr_lo = reg; | ||
413 | op.data_bytes = 1; | ||
414 | op.buffer = nand_auxbuf; | ||
415 | nand_auxbuf[0] = val; | ||
416 | if(sfc_exec(&op)) | ||
417 | return NAND_ERR_CONTROLLER; | ||
418 | 311 | ||
419 | return NAND_SUCCESS; | 312 | int rc; |
420 | } | 313 | unsigned pg_size = drv->chip->page_size; |
314 | unsigned blk_size = pg_size << drv->chip->log2_ppb; | ||
421 | 315 | ||
422 | static int nandcmd_page_read_to_cache(uint32_t row_addr) | 316 | if(byte_addr % blk_size != 0) |
423 | { | 317 | return NAND_ERR_UNALIGNED; |
424 | sfc_op op = {0}; | 318 | if(byte_len % blk_size != 0) |
425 | op.command = NAND_CMD_PAGE_READ_TO_CACHE; | 319 | return NAND_ERR_UNALIGNED; |
426 | op.addr_bytes = nand_drv.chip_data->rowaddr_width; | ||
427 | op.addr_lo = row_addr; | ||
428 | if(sfc_exec(&op)) | ||
429 | return NAND_ERR_CONTROLLER; | ||
430 | 320 | ||
431 | return NAND_SUCCESS; | 321 | nand_page_t page = byte_addr / pg_size; |
432 | } | 322 | nand_page_t end_page = page + (byte_len / pg_size); |
433 | 323 | ||
434 | static int nandcmd_read_from_cache(uint8_t* buf) | 324 | for(nand_block_t blk = page; blk < end_page; blk += drv->ppb) { |
435 | { | 325 | rc = nand_block_erase(drv, blk); |
436 | sfc_op op = {0}; | 326 | if(rc < 0) |
437 | if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) { | 327 | return rc; |
438 | op.command = NAND_CMD_READ_FROM_CACHEx4; | ||
439 | op.mode = SFC_MODE_QUAD_IO; | ||
440 | } else { | ||
441 | op.command = NAND_CMD_READ_FROM_CACHE; | ||
442 | op.mode = SFC_MODE_STANDARD; | ||
443 | } | 328 | } |
444 | 329 | ||
445 | op.flags = SFC_FLAG_READ; | 330 | for(; page != end_page; ++page) { |
446 | op.addr_bytes = nand_drv.chip_data->coladdr_width; | 331 | memcpy(drv->page_buf, buffer, pg_size); |
447 | op.addr_lo = 0; | 332 | memset(&drv->page_buf[pg_size], 0xff, drv->chip->oob_size); |
448 | op.dummy_bits = 8; // NOTE: this may need a chip_data parameter | 333 | buffer += pg_size; |
449 | op.data_bytes = (1 << nand_drv.chip_data->log2_page_size); | ||
450 | op.buffer = buf; | ||
451 | if(sfc_exec(&op)) | ||
452 | return NAND_ERR_CONTROLLER; | ||
453 | |||
454 | return NAND_SUCCESS; | ||
455 | } | ||
456 | 334 | ||
457 | static int nandcmd_program_load(const uint8_t* buf) | 335 | rc = nand_page_program(drv, page, drv->page_buf); |
458 | { | 336 | if(rc < 0) |
459 | sfc_op op = {0}; | 337 | return rc; |
460 | if(nand_drv.chip_data->flags & NANDCHIP_FLAG_QUAD) { | ||
461 | op.command = NAND_CMD_PROGRAM_LOADx4; | ||
462 | op.mode = SFC_MODE_QUAD_IO; | ||
463 | } else { | ||
464 | op.command = NAND_CMD_PROGRAM_LOAD; | ||
465 | op.mode = SFC_MODE_STANDARD; | ||
466 | } | 338 | } |
467 | 339 | ||
468 | op.flags = SFC_FLAG_WRITE; | ||
469 | op.addr_bytes = nand_drv.chip_data->coladdr_width; | ||
470 | op.addr_lo = 0; | ||
471 | op.data_bytes = (1 << nand_drv.chip_data->log2_page_size); | ||
472 | op.buffer = (void*)buf; | ||
473 | if(sfc_exec(&op)) | ||
474 | return NAND_ERR_CONTROLLER; | ||
475 | |||
476 | return NAND_SUCCESS; | ||
477 | } | ||
478 | |||
479 | static int nandcmd_program_execute(uint32_t row_addr) | ||
480 | { | ||
481 | sfc_op op = {0}; | ||
482 | op.command = NAND_CMD_PROGRAM_EXECUTE; | ||
483 | op.addr_bytes = nand_drv.chip_data->rowaddr_width; | ||
484 | op.addr_lo = row_addr; | ||
485 | if(sfc_exec(&op)) | ||
486 | return NAND_ERR_CONTROLLER; | ||
487 | |||
488 | return NAND_SUCCESS; | 340 | return NAND_SUCCESS; |
489 | } | 341 | } |
490 | 342 | ||
491 | static int nandcmd_block_erase(uint32_t block_addr) | 343 | /* TODO - NAND driver future improvements |
492 | { | 344 | * |
493 | sfc_op op = {0}; | 345 | * 1. Support sofware or on-die ECC transparently. Support debug ECC bypass. |
494 | op.command = NAND_CMD_BLOCK_ERASE; | 346 | * |
495 | op.addr_bytes = nand_drv.chip_data->rowaddr_width; | 347 | * It's probably best to add an API call to turn ECC on or off. Software |
496 | op.addr_lo = block_addr; | 348 | * ECC and most or all on-die ECC implementations require some OOB bytes |
497 | if(sfc_exec(&op)) | 349 | * to function; which leads us to the next problem... |
498 | return NAND_ERR_CONTROLLER; | 350 | * |
499 | 351 | * 2. Allow safe access to OOB areas | |
500 | return NAND_SUCCESS; | 352 | * |
501 | } | 353 | * The OOB data area is not fully available to users; it is also occupied |
354 | * by ECC data and bad block markings. The NAND driver needs to provide a | ||
355 | * mapping which allows OOB data users to map around those reserved areas, | ||
356 | * otherwise it's not really possible to use OOB data. | ||
357 | * | ||
358 | * 3. Support partial page programming. | ||
359 | * | ||
360 | * This might already work. My understanding of NAND flash is that bits are | ||
361 | * represented by charge deposited on flash cells. In the case of SLC flash, | ||
362 | * cells are one bit. For MLC flash, cells can store more than one bit; but | ||
363 | * MLC flash is much less reliable than SLC. We probably don't have to be | ||
364 | * concerned about MLC flash, and its does not support partial programming | ||
365 | * anyway due to the cell characteristics, so I will only consider SLC here. | ||
366 | * | ||
367 | * For SLC there are two cell states -- an uncharged cell represents a "1" | ||
368 | * and a charged cell represents "0". Programming can only deposit charge | ||
369 | * on a cell and erasing can only remove charge. Therefore, "programming" a | ||
370 | * cell to 1 is actually a no-op. | ||
371 | * | ||
372 | * So, there's no datasheet which spells this out, but I suspect you just | ||
373 | * set the areas you're not interested in programming to 0xff. Programming | ||
374 | * can never change a written 0 back to a 1, so programming a 1 bit works | ||
375 | * more like a "don't care" (= keep whatever value is already there). | ||
376 | * | ||
377 | * What _is_ given by the datasheets is limits on how many times you can | ||
378 | * reprogram the same page without erasing it. This is an overall limit | ||
379 | * called NOP (number of programs) in many datasheets. In addition to this, | ||
380 | * sub-regions of the page have further limits: it's common for a 2048+64 | ||
381 | * byte page to be split into 8 regions, with four 512-byte main areas and | ||
382 | * four 16-byte OOB areas. Usually, each subregion can only be programmed | ||
383 | * once. However, you can write multiple subregions with a single program. | ||
384 | * | ||
385 | * Violating programming constraints could cause data loss, so we need to | ||
386 | * communicate to upper layers what the limitations are here if they want | ||
387 | * to use partial programming safely. | ||
388 | * | ||
389 | * Programming the same page more than once increases the overall stress | ||
390 | * on the flash cells and can cause bitflips. For this reason, it's best | ||
391 | * to keep the number of programs as low as possible. Some sources suggest | ||
392 | * that programming the pages in a block in linear order is also better to | ||
393 | * reduce stress, although I don't know why this would be. | ||
394 | * | ||
395 | * These program/read stresses can flip bits, but it's only due to residual | ||
396 | * charge building up on uncharged cells; cells are not permanently damaged | ||
397 | * by these kind of stresses. Erasing the block will remove the charge and | ||
398 | * restore all the cells to a clean state. | ||
399 | * | ||
400 | * These slides are fairly informative on this subject: | ||
401 | * - https://cushychicken.github.io/assets/cooke_inconvenient_truths.pdf | ||
402 | * | ||
403 | * 4. Bad block management | ||
404 | * | ||
405 | * This probably doesn't belong in the NAND layer but it seems wise to keep | ||
406 | * at least a bad block table at the level of the NAND driver. Factory bad | ||
407 | * block marks are usually some non-0xFF byte in the OOB area, but bad blocks | ||
408 | * which develop over the device lifetime usually won't be marked; after all | ||
409 | * they are unreliable, so we can't program a marking on them and expect it | ||
410 | * to stick. So, most FTL systems keep a bad block table somewhere in flash | ||
411 | * and update it whenever a block goes bad. | ||
412 | * | ||
413 | * So, in addition to a bad block marker scan, we should try to gather bad | ||
414 | * block information from such tables. | ||
415 | */ | ||