ipanic_mtd.c 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. #include <linux/slab.h>
  2. #include <linux/kernel.h>
  3. #include <linux/hardirq.h>
  4. #include <linux/module.h>
  5. #include <linux/workqueue.h>
  6. #include <linux/mtd/mtd.h>
  7. #include "ipanic.h"
  8. struct ipanic_mtd_data {
  9. struct mtd_info *mtd;
  10. u32 blk_nr;
  11. int bufsize;
  12. u64 buf;
  13. u32 blk_offset[1024];
  14. };
  15. static struct ipanic_mtd_data *ipanic_mtd_ctx;
  16. static int ipanic_mtd_block_read_single(struct ipanic_mtd_data *ctx, loff_t offset, void *buf)
  17. {
  18. int rc;
  19. size_t len;
  20. int index = offset >> ctx->mtd->writesize_shift;
  21. if ((index < 0) || (index >= ctx->blk_nr))
  22. return -EINVAL;
  23. rc = ctx->mtd->_read(ctx->mtd, ctx->blk_offset[index], ctx->mtd->writesize, &len, buf);
  24. #if 0
  25. if (rc == -EBADMSG) {
  26. LOGW("Check sum error (ignore)\n");
  27. rc = 0;
  28. }
  29. #endif
  30. if (rc == -EUCLEAN) {
  31. LOGW("ECC Check sum error corrected %lld\n", offset);
  32. rc = 0;
  33. }
  34. if ((rc == 0) && (len != ctx->mtd->writesize)) {
  35. LOGE("aee-ipanic: read size mismatch %zu\n", len);
  36. return -EINVAL;
  37. }
  38. return rc;
  39. }
  40. static int ipanic_mtd_block_read(struct ipanic_mtd_data *ctx, off_t file_offset,
  41. int file_length, void *buf)
  42. {
  43. #if 0
  44. LOGI("read: file_offset:%d file_length:%lu\n", file_offset, file_length);
  45. #endif
  46. while (file_length > 0) {
  47. unsigned int page_no;
  48. off_t page_offset;
  49. int rc;
  50. size_t count = file_length;
  51. /* We only support reading a maximum of a flash page */
  52. if (count > ctx->mtd->writesize)
  53. count = ctx->mtd->writesize;
  54. page_no = file_offset / ctx->mtd->writesize;
  55. page_offset = file_offset % ctx->mtd->writesize;
  56. rc = ipanic_mtd_block_read_single(ctx, page_no * ctx->mtd->writesize, buf);
  57. if (rc < 0) {
  58. LOGE("mtd read error page_no(%d) error(%d)\n", page_no, rc);
  59. return -1;
  60. }
  61. if (page_offset)
  62. count -= page_offset;
  63. file_length -= count;
  64. buf += count;
  65. file_offset += count;
  66. }
  67. return 0;
  68. }
  69. static int ipanic_mtd_block_write(struct ipanic_mtd_data *ctx, loff_t to, int bounce_len, void *buf)
  70. {
  71. int rc;
  72. size_t wlen;
  73. /*int panic = in_interrupt() | in_atomic();*/
  74. int panic = in_interrupt(); /*remove in_atomic() since checkpatch: do not use in_atomic in drivers*/
  75. int index = to >> ctx->mtd->writesize_shift;
  76. if ((index < 0) || (index >= ctx->blk_nr))
  77. return -EINVAL;
  78. if (bounce_len > ctx->mtd->writesize) {
  79. LOGE("%s: len too large[%x]\n", __func__, bounce_len);
  80. return -EINVAL;
  81. }
  82. if (panic && !ctx->mtd->_panic_write) {
  83. LOGE("%s: No panic_write available\n", __func__);
  84. return 0;
  85. } else if (!panic && !ctx->mtd->_write) {
  86. LOGE("%s: No write available\n", __func__);
  87. return 0;
  88. }
  89. if (bounce_len < ctx->mtd->writesize)
  90. memset(buf + bounce_len, 0, ctx->mtd->writesize - bounce_len);
  91. if (panic)
  92. rc = ctx->mtd->_panic_write(ctx->mtd, ctx->blk_offset[index], ctx->mtd->writesize,
  93. &wlen, buf);
  94. else
  95. rc = ctx->mtd->_write(ctx->mtd, ctx->blk_offset[index], ctx->mtd->writesize, &wlen,
  96. buf);
  97. if (rc) {
  98. LOGE("%s: Error writing data to flash (%d)\n", __func__, rc);
  99. return rc;
  100. }
  101. return wlen;
  102. }
  103. static int ipanic_mtd_block_scan(struct ipanic_mtd_data *ctx)
  104. {
  105. int index = 0, offset;
  106. /* calcuate total number of non-bad blocks on NAND device, */
  107. /* and record it's offset */
  108. for (offset = 0; offset < ctx->mtd->size; offset += ctx->mtd->writesize) {
  109. if (!ctx->mtd->_block_isbad(ctx->mtd, offset)) {
  110. /*index can't surpass array size */
  111. if (index >= (sizeof(ctx->blk_offset) / sizeof(u32)))
  112. break;
  113. ctx->blk_offset[index++] = offset;
  114. }
  115. }
  116. ctx->blk_nr = index;
  117. #if 0
  118. LOGI("blocks: ");
  119. for (index = 0; index < ctx->blk_nr; index++)
  120. LOGI("%x,", ctx->blk_offset[index]);
  121. LOGI("\n");
  122. #endif
  123. return 1;
  124. }
  125. static void ipanic_mtd_block_erase_callback(struct erase_info *done)
  126. {
  127. wait_queue_head_t *wait_q = (wait_queue_head_t *) done->priv;
  128. wake_up(wait_q);
  129. }
  130. static void ipanic_mtd_block_erase(void)
  131. {
  132. struct ipanic_mtd_data *ctx = ipanic_mtd_ctx;
  133. struct erase_info erase;
  134. DECLARE_WAITQUEUE(wait, current);
  135. wait_queue_head_t wait_q;
  136. int rc, i;
  137. init_waitqueue_head(&wait_q);
  138. erase.mtd = ctx->mtd;
  139. erase.callback = ipanic_mtd_block_erase_callback;
  140. erase.len = ctx->mtd->erasesize;
  141. erase.priv = (u_long) & wait_q;
  142. for (i = 0; i < ctx->mtd->size; i += ctx->mtd->erasesize) {
  143. erase.addr = i;
  144. set_current_state(TASK_INTERRUPTIBLE);
  145. add_wait_queue(&wait_q, &wait);
  146. rc = ctx->mtd->_block_isbad(ctx->mtd, erase.addr);
  147. if (rc < 0) {
  148. LOGE("Bad block check failed (%d)\n", rc);
  149. goto out;
  150. }
  151. if (rc) {
  152. LOGW("Skipping erase of bad block @%llx\n", erase.addr);
  153. set_current_state(TASK_RUNNING);
  154. remove_wait_queue(&wait_q, &wait);
  155. continue;
  156. }
  157. rc = ctx->mtd->_erase(ctx->mtd, &erase);
  158. if (rc) {
  159. set_current_state(TASK_RUNNING);
  160. remove_wait_queue(&wait_q, &wait);
  161. LOGE("Erase of 0x%llx, 0x%llx failed\n",
  162. (unsigned long long)erase.addr, (unsigned long long)erase.len);
  163. if (rc == -EIO) {
  164. if (ctx->mtd->_block_markbad(ctx->mtd, erase.addr, NULL)) {
  165. LOGE("aee-ipanic: Err marking blk bad\n");
  166. goto out;
  167. }
  168. LOGI("Marked a bad block @%llx\n", erase.addr);
  169. continue;
  170. }
  171. goto out;
  172. }
  173. schedule();
  174. remove_wait_queue(&wait_q, &wait);
  175. }
  176. LOGD("%s partition erased\n", AEE_IPANIC_PLABEL);
  177. out:
  178. return;
  179. }
  180. static void ipanic_mtd_notify_add(struct mtd_info *mtd)
  181. {
  182. static struct ipanic_mtd_data mtd_drv_ctx;
  183. struct ipanic_mtd_data *ctx = &mtd_drv_ctx;
  184. if (strcmp(mtd->name, AEE_IPANIC_PLABEL))
  185. return;
  186. ctx->mtd = mtd;
  187. if (!ipanic_mtd_block_scan(ctx))
  188. goto out_err;
  189. LOGI("Bound to '%s', write size(%d), write size shift(%d)\n",
  190. mtd->name, mtd->writesize, mtd->writesize_shift);
  191. ctx->bufsize = ALIGN(PAGE_SIZE, mtd->writesize);
  192. ctx->buf = (u64) (unsigned long)kmalloc(ctx->bufsize, GFP_KERNEL);
  193. ipanic_mtd_ctx = ctx;
  194. return;
  195. out_err:
  196. ctx->mtd = NULL;
  197. }
  198. static void ipanic_mtd_notify_remove(struct mtd_info *mtd)
  199. {
  200. struct ipanic_mtd_data *ctx = ipanic_mtd_ctx;
  201. if (mtd == ctx->mtd) {
  202. ctx->mtd = NULL;
  203. LOGI("aee-ipanic: Unbound from %s\n", mtd->name);
  204. }
  205. }
  206. static struct mtd_notifier ipanic_mtd_notifier = {
  207. .add = ipanic_mtd_notify_add,
  208. .remove = ipanic_mtd_notify_remove,
  209. };
  210. void ipanic_msdc_init(void)
  211. {
  212. register_mtd_user(&ipanic_mtd_notifier);
  213. }
  214. EXPORT_SYMBOL(ipanic_msdc_init);
  215. int ipanic_msdc_info(struct ipanic_header *iheader)
  216. {
  217. if (ipanic_mtd_ctx == NULL || ipanic_mtd_ctx->mtd == NULL) {
  218. LOGE("%s: mtd is not ready.", __func__);
  219. return -1;
  220. }
  221. iheader->blksize = ipanic_mtd_ctx->mtd->writesize;
  222. iheader->partsize = ipanic_mtd_ctx->blk_nr * iheader->blksize;
  223. iheader->buf = ipanic_mtd_ctx->buf;
  224. iheader->bufsize = ipanic_mtd_ctx->bufsize;
  225. if (iheader->buf == 0) {
  226. LOGE("kmalloc fail[%x]\n", iheader->bufsize);
  227. return -1;
  228. }
  229. return 0;
  230. }
  231. EXPORT_SYMBOL(ipanic_msdc_info);
  232. char *ipanic_read_size(int off, int len)
  233. {
  234. int size;
  235. char *buff = NULL;
  236. if (ipanic_mtd_ctx == NULL || ipanic_mtd_ctx->mtd == NULL) {
  237. LOGE("%s: mtd is not ready.", __func__);
  238. return NULL;
  239. }
  240. if (len == 0)
  241. return NULL;
  242. size = ALIGN(len, ipanic_mtd_ctx->mtd->writesize);
  243. buff = kzalloc(size, GFP_KERNEL);
  244. if (buff == NULL) {
  245. LOGE("%s: cannot allocate buffer(len:%d)\n", __func__, len);
  246. return NULL;
  247. }
  248. if (ipanic_mtd_block_read(ipanic_mtd_ctx, off, size, buff) != 0) {
  249. LOGE("%s: read failed(offset:%d,size:%d)\n", __func__, off, size);
  250. kfree(buff);
  251. return NULL;
  252. }
  253. return buff;
  254. }
  255. EXPORT_SYMBOL(ipanic_read_size);
  256. int ipanic_write_size(void *buf, int off, int len)
  257. {
  258. int wlen = 0;
  259. int start = off;
  260. if (ipanic_mtd_ctx == NULL || ipanic_mtd_ctx->mtd == NULL) {
  261. LOGE("%s: mtd is not ready.", __func__);
  262. return -1;
  263. }
  264. if (len & (ipanic_mtd_ctx->mtd->writesize - 1))
  265. return -2;
  266. while (len > 0) {
  267. wlen = len > ipanic_mtd_ctx->mtd->writesize ? ipanic_mtd_ctx->mtd->writesize : len;
  268. wlen = ipanic_mtd_block_write(ipanic_mtd_ctx, off, wlen, buf);
  269. if (wlen < 0) {
  270. LOGE("%s: failed(%d)", __func__, wlen);
  271. return -1;
  272. }
  273. off += wlen;
  274. buf += wlen;
  275. len -= wlen;
  276. }
  277. return (off - start);
  278. }
  279. EXPORT_SYMBOL(ipanic_write_size);
  280. void ipanic_erase(void)
  281. {
  282. ipanic_mtd_block_erase();
  283. }
  284. EXPORT_SYMBOL(ipanic_erase);