]>
Commit | Line | Data |
---|---|---|
e29c22f5 KP |
1 | /* |
2 | * Core registration and callback routines for MTD | |
3 | * drivers and users. | |
4 | * | |
5 | * This program is free software; you can redistribute it and/or modify | |
6 | * it under the terms of the GNU General Public License version 2 as | |
7 | * published by the Free Software Foundation. | |
8 | */ | |
9 | ||
10 | #include <linux/mtd/mtd.h> | |
7b15e2bb | 11 | #include <linux/compat.h> |
e29c22f5 KP |
12 | #include <ubi_uboot.h> |
13 | ||
14 | struct mtd_info *mtd_table[MAX_MTD_DEVICES]; | |
15 | ||
16 | int add_mtd_device(struct mtd_info *mtd) | |
17 | { | |
18 | int i; | |
19 | ||
20 | BUG_ON(mtd->writesize == 0); | |
21 | ||
22 | for (i = 0; i < MAX_MTD_DEVICES; i++) | |
23 | if (!mtd_table[i]) { | |
24 | mtd_table[i] = mtd; | |
25 | mtd->index = i; | |
26 | mtd->usecount = 0; | |
27 | ||
dfe64e2c SL |
28 | /* default value if not set by driver */ |
29 | if (mtd->bitflip_threshold == 0) | |
30 | mtd->bitflip_threshold = mtd->ecc_strength; | |
31 | ||
32 | ||
e29c22f5 KP |
33 | /* No need to get a refcount on the module containing |
34 | the notifier, since we hold the mtd_table_mutex */ | |
35 | ||
36 | /* We _know_ we aren't being removed, because | |
37 | our caller is still holding us here. So none | |
38 | of this try_ nonsense, and no bitching about it | |
39 | either. :) */ | |
40 | return 0; | |
41 | } | |
42 | ||
43 | return 1; | |
44 | } | |
45 | ||
46 | /** | |
47 | * del_mtd_device - unregister an MTD device | |
48 | * @mtd: pointer to MTD device info structure | |
49 | * | |
50 | * Remove a device from the list of MTD devices present in the system, | |
51 | * and notify each currently active MTD 'user' of its departure. | |
52 | * Returns zero on success or 1 on failure, which currently will happen | |
53 | * if the requested device does not appear to be present in the list. | |
54 | */ | |
55 | int del_mtd_device(struct mtd_info *mtd) | |
56 | { | |
57 | int ret; | |
58 | ||
59 | if (mtd_table[mtd->index] != mtd) { | |
60 | ret = -ENODEV; | |
61 | } else if (mtd->usecount) { | |
62 | printk(KERN_NOTICE "Removing MTD device #%d (%s)" | |
63 | " with use count %d\n", | |
64 | mtd->index, mtd->name, mtd->usecount); | |
65 | ret = -EBUSY; | |
66 | } else { | |
67 | /* No need to get a refcount on the module containing | |
68 | * the notifier, since we hold the mtd_table_mutex */ | |
69 | mtd_table[mtd->index] = NULL; | |
70 | ||
71 | ret = 0; | |
72 | } | |
73 | ||
74 | return ret; | |
75 | } | |
76 | ||
77 | /** | |
78 | * get_mtd_device - obtain a validated handle for an MTD device | |
79 | * @mtd: last known address of the required MTD device | |
80 | * @num: internal device number of the required MTD device | |
81 | * | |
82 | * Given a number and NULL address, return the num'th entry in the device | |
83 | * table, if any. Given an address and num == -1, search the device table | |
84 | * for a device with that address and return if it's still present. Given | |
85 | * both, return the num'th driver only if its address matches. Return | |
86 | * error code if not. | |
87 | */ | |
88 | struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) | |
89 | { | |
90 | struct mtd_info *ret = NULL; | |
91 | int i, err = -ENODEV; | |
92 | ||
93 | if (num == -1) { | |
94 | for (i = 0; i < MAX_MTD_DEVICES; i++) | |
95 | if (mtd_table[i] == mtd) | |
96 | ret = mtd_table[i]; | |
97 | } else if (num < MAX_MTD_DEVICES) { | |
98 | ret = mtd_table[num]; | |
99 | if (mtd && mtd != ret) | |
100 | ret = NULL; | |
101 | } | |
102 | ||
103 | if (!ret) | |
104 | goto out_unlock; | |
105 | ||
106 | ret->usecount++; | |
107 | return ret; | |
108 | ||
109 | out_unlock: | |
110 | return ERR_PTR(err); | |
111 | } | |
112 | ||
113 | /** | |
114 | * get_mtd_device_nm - obtain a validated handle for an MTD device by | |
115 | * device name | |
116 | * @name: MTD device name to open | |
117 | * | |
118 | * This function returns MTD device description structure in case of | |
119 | * success and an error code in case of failure. | |
120 | */ | |
121 | struct mtd_info *get_mtd_device_nm(const char *name) | |
122 | { | |
123 | int i, err = -ENODEV; | |
124 | struct mtd_info *mtd = NULL; | |
125 | ||
126 | for (i = 0; i < MAX_MTD_DEVICES; i++) { | |
127 | if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) { | |
128 | mtd = mtd_table[i]; | |
129 | break; | |
130 | } | |
131 | } | |
132 | ||
133 | if (!mtd) | |
134 | goto out_unlock; | |
135 | ||
136 | mtd->usecount++; | |
137 | return mtd; | |
138 | ||
139 | out_unlock: | |
140 | return ERR_PTR(err); | |
141 | } | |
142 | ||
143 | void put_mtd_device(struct mtd_info *mtd) | |
144 | { | |
145 | int c; | |
146 | ||
147 | c = --mtd->usecount; | |
148 | BUG_ON(c < 0); | |
149 | } | |
4ba692fb BG |
150 | |
151 | #if defined(CONFIG_CMD_MTDPARTS_SPREAD) | |
152 | /** | |
153 | * mtd_get_len_incl_bad | |
154 | * | |
155 | * Check if length including bad blocks fits into device. | |
156 | * | |
157 | * @param mtd an MTD device | |
158 | * @param offset offset in flash | |
159 | * @param length image length | |
160 | * @return image length including bad blocks in *len_incl_bad and whether or not | |
161 | * the length returned was truncated in *truncated | |
162 | */ | |
163 | void mtd_get_len_incl_bad(struct mtd_info *mtd, uint64_t offset, | |
164 | const uint64_t length, uint64_t *len_incl_bad, | |
165 | int *truncated) | |
166 | { | |
167 | *truncated = 0; | |
168 | *len_incl_bad = 0; | |
169 | ||
4ba692fb BG |
170 | if (!mtd->block_isbad) { |
171 | *len_incl_bad = length; | |
172 | return; | |
173 | } | |
174 | ||
175 | uint64_t len_excl_bad = 0; | |
176 | uint64_t block_len; | |
177 | ||
178 | while (len_excl_bad < length) { | |
36650ca9 SW |
179 | if (offset >= mtd->size) { |
180 | *truncated = 1; | |
181 | return; | |
182 | } | |
183 | ||
4ba692fb BG |
184 | block_len = mtd->erasesize - (offset & (mtd->erasesize - 1)); |
185 | ||
186 | if (!mtd->block_isbad(mtd, offset & ~(mtd->erasesize - 1))) | |
187 | len_excl_bad += block_len; | |
188 | ||
189 | *len_incl_bad += block_len; | |
190 | offset += block_len; | |
4ba692fb BG |
191 | } |
192 | } | |
193 | #endif /* defined(CONFIG_CMD_MTDPARTS_SPREAD) */ | |
dfe64e2c SL |
194 | |
195 | /* | |
196 | * Erase is an asynchronous operation. Device drivers are supposed | |
197 | * to call instr->callback() whenever the operation completes, even | |
198 | * if it completes with a failure. | |
199 | * Callers are supposed to pass a callback function and wait for it | |
200 | * to be called before writing to the block. | |
201 | */ | |
202 | int mtd_erase(struct mtd_info *mtd, struct erase_info *instr) | |
203 | { | |
204 | if (instr->addr > mtd->size || instr->len > mtd->size - instr->addr) | |
205 | return -EINVAL; | |
206 | if (!(mtd->flags & MTD_WRITEABLE)) | |
207 | return -EROFS; | |
208 | instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; | |
209 | if (!instr->len) { | |
210 | instr->state = MTD_ERASE_DONE; | |
211 | mtd_erase_callback(instr); | |
212 | return 0; | |
213 | } | |
214 | return mtd->_erase(mtd, instr); | |
215 | } | |
216 | ||
217 | int mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, | |
218 | u_char *buf) | |
219 | { | |
40462e54 | 220 | int ret_code; |
dfe64e2c SL |
221 | if (from < 0 || from > mtd->size || len > mtd->size - from) |
222 | return -EINVAL; | |
223 | if (!len) | |
224 | return 0; | |
40462e54 PB |
225 | |
226 | /* | |
227 | * In the absence of an error, drivers return a non-negative integer | |
228 | * representing the maximum number of bitflips that were corrected on | |
229 | * any one ecc region (if applicable; zero otherwise). | |
230 | */ | |
231 | ret_code = mtd->_read(mtd, from, len, retlen, buf); | |
232 | if (unlikely(ret_code < 0)) | |
233 | return ret_code; | |
234 | if (mtd->ecc_strength == 0) | |
235 | return 0; /* device lacks ecc */ | |
236 | return ret_code >= mtd->bitflip_threshold ? -EUCLEAN : 0; | |
dfe64e2c SL |
237 | } |
238 | ||
239 | int mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, | |
240 | const u_char *buf) | |
241 | { | |
242 | *retlen = 0; | |
243 | if (to < 0 || to > mtd->size || len > mtd->size - to) | |
244 | return -EINVAL; | |
245 | if (!mtd->_write || !(mtd->flags & MTD_WRITEABLE)) | |
246 | return -EROFS; | |
247 | if (!len) | |
248 | return 0; | |
249 | return mtd->_write(mtd, to, len, retlen, buf); | |
250 | } | |
251 | ||
252 | /* | |
253 | * In blackbox flight recorder like scenarios we want to make successful writes | |
254 | * in interrupt context. panic_write() is only intended to be called when its | |
255 | * known the kernel is about to panic and we need the write to succeed. Since | |
256 | * the kernel is not going to be running for much longer, this function can | |
257 | * break locks and delay to ensure the write succeeds (but not sleep). | |
258 | */ | |
259 | int mtd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, | |
260 | const u_char *buf) | |
261 | { | |
262 | *retlen = 0; | |
263 | if (!mtd->_panic_write) | |
264 | return -EOPNOTSUPP; | |
265 | if (to < 0 || to > mtd->size || len > mtd->size - to) | |
266 | return -EINVAL; | |
267 | if (!(mtd->flags & MTD_WRITEABLE)) | |
268 | return -EROFS; | |
269 | if (!len) | |
270 | return 0; | |
271 | return mtd->_panic_write(mtd, to, len, retlen, buf); | |
272 | } | |
273 | ||
274 | int mtd_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) | |
275 | { | |
276 | ops->retlen = ops->oobretlen = 0; | |
277 | if (!mtd->_read_oob) | |
278 | return -EOPNOTSUPP; | |
279 | return mtd->_read_oob(mtd, from, ops); | |
280 | } | |
281 | ||
282 | /* | |
283 | * Method to access the protection register area, present in some flash | |
284 | * devices. The user data is one time programmable but the factory data is read | |
285 | * only. | |
286 | */ | |
287 | int mtd_get_fact_prot_info(struct mtd_info *mtd, struct otp_info *buf, | |
288 | size_t len) | |
289 | { | |
290 | if (!mtd->_get_fact_prot_info) | |
291 | return -EOPNOTSUPP; | |
292 | if (!len) | |
293 | return 0; | |
294 | return mtd->_get_fact_prot_info(mtd, buf, len); | |
295 | } | |
296 | ||
297 | int mtd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, | |
298 | size_t *retlen, u_char *buf) | |
299 | { | |
300 | *retlen = 0; | |
301 | if (!mtd->_read_fact_prot_reg) | |
302 | return -EOPNOTSUPP; | |
303 | if (!len) | |
304 | return 0; | |
305 | return mtd->_read_fact_prot_reg(mtd, from, len, retlen, buf); | |
306 | } | |
307 | ||
308 | int mtd_get_user_prot_info(struct mtd_info *mtd, struct otp_info *buf, | |
309 | size_t len) | |
310 | { | |
311 | if (!mtd->_get_user_prot_info) | |
312 | return -EOPNOTSUPP; | |
313 | if (!len) | |
314 | return 0; | |
315 | return mtd->_get_user_prot_info(mtd, buf, len); | |
316 | } | |
317 | ||
318 | int mtd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len, | |
319 | size_t *retlen, u_char *buf) | |
320 | { | |
321 | *retlen = 0; | |
322 | if (!mtd->_read_user_prot_reg) | |
323 | return -EOPNOTSUPP; | |
324 | if (!len) | |
325 | return 0; | |
326 | return mtd->_read_user_prot_reg(mtd, from, len, retlen, buf); | |
327 | } | |
328 | ||
329 | int mtd_write_user_prot_reg(struct mtd_info *mtd, loff_t to, size_t len, | |
330 | size_t *retlen, u_char *buf) | |
331 | { | |
332 | *retlen = 0; | |
333 | if (!mtd->_write_user_prot_reg) | |
334 | return -EOPNOTSUPP; | |
335 | if (!len) | |
336 | return 0; | |
337 | return mtd->_write_user_prot_reg(mtd, to, len, retlen, buf); | |
338 | } | |
339 | ||
340 | int mtd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) | |
341 | { | |
342 | if (!mtd->_lock_user_prot_reg) | |
343 | return -EOPNOTSUPP; | |
344 | if (!len) | |
345 | return 0; | |
346 | return mtd->_lock_user_prot_reg(mtd, from, len); | |
347 | } | |
348 | ||
349 | /* Chip-supported device locking */ | |
350 | int mtd_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | |
351 | { | |
352 | if (!mtd->_lock) | |
353 | return -EOPNOTSUPP; | |
354 | if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs) | |
355 | return -EINVAL; | |
356 | if (!len) | |
357 | return 0; | |
358 | return mtd->_lock(mtd, ofs, len); | |
359 | } | |
360 | ||
361 | int mtd_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) | |
362 | { | |
363 | if (!mtd->_unlock) | |
364 | return -EOPNOTSUPP; | |
365 | if (ofs < 0 || ofs > mtd->size || len > mtd->size - ofs) | |
366 | return -EINVAL; | |
367 | if (!len) | |
368 | return 0; | |
369 | return mtd->_unlock(mtd, ofs, len); | |
370 | } | |
371 | ||
372 | int mtd_block_isbad(struct mtd_info *mtd, loff_t ofs) | |
373 | { | |
374 | if (!mtd->_block_isbad) | |
375 | return 0; | |
376 | if (ofs < 0 || ofs > mtd->size) | |
377 | return -EINVAL; | |
378 | return mtd->_block_isbad(mtd, ofs); | |
379 | } | |
380 | ||
381 | int mtd_block_markbad(struct mtd_info *mtd, loff_t ofs) | |
382 | { | |
383 | if (!mtd->_block_markbad) | |
384 | return -EOPNOTSUPP; | |
385 | if (ofs < 0 || ofs > mtd->size) | |
386 | return -EINVAL; | |
387 | if (!(mtd->flags & MTD_WRITEABLE)) | |
388 | return -EROFS; | |
389 | return mtd->_block_markbad(mtd, ofs); | |
390 | } |