14b23aff0SRichard Purdie /* 24b23aff0SRichard Purdie * MTD Oops/Panic logger 34b23aff0SRichard Purdie * 44b23aff0SRichard Purdie * Copyright (C) 2007 Nokia Corporation. All rights reserved. 54b23aff0SRichard Purdie * 64b23aff0SRichard Purdie * Author: Richard Purdie <rpurdie@openedhand.com> 74b23aff0SRichard Purdie * 84b23aff0SRichard Purdie * This program is free software; you can redistribute it and/or 94b23aff0SRichard Purdie * modify it under the terms of the GNU General Public License 104b23aff0SRichard Purdie * version 2 as published by the Free Software Foundation. 114b23aff0SRichard Purdie * 124b23aff0SRichard Purdie * This program is distributed in the hope that it will be useful, but 134b23aff0SRichard Purdie * WITHOUT ANY WARRANTY; without even the implied warranty of 144b23aff0SRichard Purdie * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 154b23aff0SRichard Purdie * General Public License for more details. 164b23aff0SRichard Purdie * 174b23aff0SRichard Purdie * You should have received a copy of the GNU General Public License 184b23aff0SRichard Purdie * along with this program; if not, write to the Free Software 194b23aff0SRichard Purdie * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 204b23aff0SRichard Purdie * 02110-1301 USA 214b23aff0SRichard Purdie * 224b23aff0SRichard Purdie */ 234b23aff0SRichard Purdie 244b23aff0SRichard Purdie #include <linux/kernel.h> 254b23aff0SRichard Purdie #include <linux/module.h> 264b23aff0SRichard Purdie #include <linux/console.h> 274b23aff0SRichard Purdie #include <linux/vmalloc.h> 284b23aff0SRichard Purdie #include <linux/workqueue.h> 294b23aff0SRichard Purdie #include <linux/sched.h> 304b23aff0SRichard Purdie #include <linux/wait.h> 31621e4f8eSRichard Purdie #include <linux/delay.h> 3247c152b8SRichard Purdie #include <linux/spinlock.h> 33f9f7dd22SDavid Woodhouse #include <linux/interrupt.h> 344b23aff0SRichard Purdie #include <linux/mtd/mtd.h> 354b23aff0SRichard Purdie 364b23aff0SRichard Purdie #define OOPS_PAGE_SIZE 4096 374b23aff0SRichard Purdie 387903cbabSAdrian Bunk static struct mtdoops_context { 394b23aff0SRichard Purdie int mtd_index; 406ce0a856SRichard Purdie struct work_struct work_erase; 416ce0a856SRichard Purdie struct work_struct work_write; 424b23aff0SRichard Purdie struct mtd_info *mtd; 434b23aff0SRichard Purdie int oops_pages; 444b23aff0SRichard Purdie int nextpage; 454b23aff0SRichard Purdie int nextcount; 464b23aff0SRichard Purdie 474b23aff0SRichard Purdie void *oops_buf; 4847c152b8SRichard Purdie 4947c152b8SRichard Purdie /* writecount and disabling ready are spin lock protected */ 5047c152b8SRichard Purdie spinlock_t writecount_lock; 514b23aff0SRichard Purdie int ready; 524b23aff0SRichard Purdie int writecount; 534b23aff0SRichard Purdie } oops_cxt; 544b23aff0SRichard Purdie 554b23aff0SRichard Purdie static void mtdoops_erase_callback(struct erase_info *done) 564b23aff0SRichard Purdie { 574b23aff0SRichard Purdie wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; 584b23aff0SRichard Purdie wake_up(wait_q); 594b23aff0SRichard Purdie } 604b23aff0SRichard Purdie 614b23aff0SRichard Purdie static int mtdoops_erase_block(struct mtd_info *mtd, int offset) 624b23aff0SRichard Purdie { 634b23aff0SRichard Purdie struct erase_info erase; 644b23aff0SRichard Purdie DECLARE_WAITQUEUE(wait, current); 654b23aff0SRichard Purdie wait_queue_head_t wait_q; 664b23aff0SRichard Purdie int ret; 674b23aff0SRichard Purdie 684b23aff0SRichard Purdie init_waitqueue_head(&wait_q); 694b23aff0SRichard Purdie erase.mtd = mtd; 704b23aff0SRichard Purdie erase.callback = mtdoops_erase_callback; 714b23aff0SRichard Purdie erase.addr = offset; 724b23aff0SRichard Purdie erase.len = mtd->erasesize; 734b23aff0SRichard Purdie erase.priv = (u_long)&wait_q; 744b23aff0SRichard Purdie 754b23aff0SRichard Purdie set_current_state(TASK_INTERRUPTIBLE); 764b23aff0SRichard Purdie add_wait_queue(&wait_q, &wait); 774b23aff0SRichard Purdie 784b23aff0SRichard Purdie ret = mtd->erase(mtd, &erase); 794b23aff0SRichard Purdie if (ret) { 804b23aff0SRichard Purdie set_current_state(TASK_RUNNING); 814b23aff0SRichard Purdie remove_wait_queue(&wait_q, &wait); 824b23aff0SRichard Purdie printk (KERN_WARNING "mtdoops: erase of region [0x%x, 0x%x] " 834b23aff0SRichard Purdie "on \"%s\" failed\n", 844b23aff0SRichard Purdie erase.addr, erase.len, mtd->name); 854b23aff0SRichard Purdie return ret; 864b23aff0SRichard Purdie } 874b23aff0SRichard Purdie 884b23aff0SRichard Purdie schedule(); /* Wait for erase to finish. */ 894b23aff0SRichard Purdie remove_wait_queue(&wait_q, &wait); 904b23aff0SRichard Purdie 914b23aff0SRichard Purdie return 0; 924b23aff0SRichard Purdie } 934b23aff0SRichard Purdie 946ce0a856SRichard Purdie static void mtdoops_inc_counter(struct mtdoops_context *cxt) 954b23aff0SRichard Purdie { 964b23aff0SRichard Purdie struct mtd_info *mtd = cxt->mtd; 974b23aff0SRichard Purdie size_t retlen; 984b23aff0SRichard Purdie u32 count; 994b23aff0SRichard Purdie int ret; 1004b23aff0SRichard Purdie 1014b23aff0SRichard Purdie cxt->nextpage++; 1024b23aff0SRichard Purdie if (cxt->nextpage > cxt->oops_pages) 1034b23aff0SRichard Purdie cxt->nextpage = 0; 1044b23aff0SRichard Purdie cxt->nextcount++; 1054b23aff0SRichard Purdie if (cxt->nextcount == 0xffffffff) 1064b23aff0SRichard Purdie cxt->nextcount = 0; 1074b23aff0SRichard Purdie 1084b23aff0SRichard Purdie ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4, 1094b23aff0SRichard Purdie &retlen, (u_char *) &count); 1102986bd2aSRichard Purdie if ((retlen != 4) || ((ret < 0) && (ret != -EUCLEAN))) { 11168d09b1bSAndrew Morton printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" 1124b23aff0SRichard Purdie ", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE, 1134b23aff0SRichard Purdie retlen, ret); 1146ce0a856SRichard Purdie schedule_work(&cxt->work_erase); 1156ce0a856SRichard Purdie return; 1164b23aff0SRichard Purdie } 1174b23aff0SRichard Purdie 1184b23aff0SRichard Purdie /* See if we need to erase the next block */ 1196ce0a856SRichard Purdie if (count != 0xffffffff) { 1206ce0a856SRichard Purdie schedule_work(&cxt->work_erase); 1216ce0a856SRichard Purdie return; 1226ce0a856SRichard Purdie } 1234b23aff0SRichard Purdie 1244b23aff0SRichard Purdie printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n", 1254b23aff0SRichard Purdie cxt->nextpage, cxt->nextcount); 1264b23aff0SRichard Purdie cxt->ready = 1; 1274b23aff0SRichard Purdie } 1284b23aff0SRichard Purdie 1296ce0a856SRichard Purdie /* Scheduled work - when we can't proceed without erasing a block */ 1306ce0a856SRichard Purdie static void mtdoops_workfunc_erase(struct work_struct *work) 1314b23aff0SRichard Purdie { 1326ce0a856SRichard Purdie struct mtdoops_context *cxt = 1336ce0a856SRichard Purdie container_of(work, struct mtdoops_context, work_erase); 1344b23aff0SRichard Purdie struct mtd_info *mtd = cxt->mtd; 1354b23aff0SRichard Purdie int i = 0, j, ret, mod; 1364b23aff0SRichard Purdie 1374b23aff0SRichard Purdie /* We were unregistered */ 1384b23aff0SRichard Purdie if (!mtd) 1394b23aff0SRichard Purdie return; 1404b23aff0SRichard Purdie 1414b23aff0SRichard Purdie mod = (cxt->nextpage * OOPS_PAGE_SIZE) % mtd->erasesize; 1424b23aff0SRichard Purdie if (mod != 0) { 1434b23aff0SRichard Purdie cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / OOPS_PAGE_SIZE); 1444b23aff0SRichard Purdie if (cxt->nextpage > cxt->oops_pages) 1454b23aff0SRichard Purdie cxt->nextpage = 0; 1464b23aff0SRichard Purdie } 1474b23aff0SRichard Purdie 1482986bd2aSRichard Purdie while (mtd->block_isbad) { 1492986bd2aSRichard Purdie ret = mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); 1502986bd2aSRichard Purdie if (!ret) 1512986bd2aSRichard Purdie break; 1522986bd2aSRichard Purdie if (ret < 0) { 1532986bd2aSRichard Purdie printk(KERN_ERR "mtdoops: block_isbad failed, aborting.\n"); 1542986bd2aSRichard Purdie return; 1552986bd2aSRichard Purdie } 1564b23aff0SRichard Purdie badblock: 1574b23aff0SRichard Purdie printk(KERN_WARNING "mtdoops: Bad block at %08x\n", 1584b23aff0SRichard Purdie cxt->nextpage * OOPS_PAGE_SIZE); 1594b23aff0SRichard Purdie i++; 1604b23aff0SRichard Purdie cxt->nextpage = cxt->nextpage + (mtd->erasesize / OOPS_PAGE_SIZE); 1614b23aff0SRichard Purdie if (cxt->nextpage > cxt->oops_pages) 1624b23aff0SRichard Purdie cxt->nextpage = 0; 1634b23aff0SRichard Purdie if (i == (cxt->oops_pages / (mtd->erasesize / OOPS_PAGE_SIZE))) { 1644b23aff0SRichard Purdie printk(KERN_ERR "mtdoops: All blocks bad!\n"); 1654b23aff0SRichard Purdie return; 1664b23aff0SRichard Purdie } 1674b23aff0SRichard Purdie } 1684b23aff0SRichard Purdie 1694b23aff0SRichard Purdie for (j = 0, ret = -1; (j < 3) && (ret < 0); j++) 1704b23aff0SRichard Purdie ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE); 1714b23aff0SRichard Purdie 1722986bd2aSRichard Purdie if (ret >= 0) { 1732986bd2aSRichard Purdie printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount); 1742986bd2aSRichard Purdie cxt->ready = 1; 1752986bd2aSRichard Purdie return; 1764b23aff0SRichard Purdie } 1774b23aff0SRichard Purdie 1782986bd2aSRichard Purdie if (mtd->block_markbad && (ret == -EIO)) { 1792986bd2aSRichard Purdie ret = mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); 1802986bd2aSRichard Purdie if (ret < 0) { 1812986bd2aSRichard Purdie printk(KERN_ERR "mtdoops: block_markbad failed, aborting.\n"); 1822986bd2aSRichard Purdie return; 1832986bd2aSRichard Purdie } 1842986bd2aSRichard Purdie } 1852986bd2aSRichard Purdie goto badblock; 1864b23aff0SRichard Purdie } 1874b23aff0SRichard Purdie 188621e4f8eSRichard Purdie static void mtdoops_write(struct mtdoops_context *cxt, int panic) 1894b23aff0SRichard Purdie { 1906ce0a856SRichard Purdie struct mtd_info *mtd = cxt->mtd; 1916ce0a856SRichard Purdie size_t retlen; 1926ce0a856SRichard Purdie int ret; 1934b23aff0SRichard Purdie 1946ce0a856SRichard Purdie if (cxt->writecount < OOPS_PAGE_SIZE) 1956ce0a856SRichard Purdie memset(cxt->oops_buf + cxt->writecount, 0xff, 1966ce0a856SRichard Purdie OOPS_PAGE_SIZE - cxt->writecount); 1976ce0a856SRichard Purdie 198621e4f8eSRichard Purdie if (panic) 199621e4f8eSRichard Purdie ret = mtd->panic_write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 200621e4f8eSRichard Purdie OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); 201621e4f8eSRichard Purdie else 2026ce0a856SRichard Purdie ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 2036ce0a856SRichard Purdie OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); 2046ce0a856SRichard Purdie 2056ce0a856SRichard Purdie cxt->writecount = 0; 2066ce0a856SRichard Purdie 2076ce0a856SRichard Purdie if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) 2086ce0a856SRichard Purdie printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n", 2096ce0a856SRichard Purdie cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); 2106ce0a856SRichard Purdie 2116ce0a856SRichard Purdie mtdoops_inc_counter(cxt); 2124b23aff0SRichard Purdie } 2134b23aff0SRichard Purdie 214621e4f8eSRichard Purdie 215621e4f8eSRichard Purdie static void mtdoops_workfunc_write(struct work_struct *work) 216621e4f8eSRichard Purdie { 217621e4f8eSRichard Purdie struct mtdoops_context *cxt = 218621e4f8eSRichard Purdie container_of(work, struct mtdoops_context, work_write); 219621e4f8eSRichard Purdie 220621e4f8eSRichard Purdie mtdoops_write(cxt, 0); 221621e4f8eSRichard Purdie } 222621e4f8eSRichard Purdie 2236ce0a856SRichard Purdie static void find_next_position(struct mtdoops_context *cxt) 2244b23aff0SRichard Purdie { 2254b23aff0SRichard Purdie struct mtd_info *mtd = cxt->mtd; 2262986bd2aSRichard Purdie int ret, page, maxpos = 0; 2274b23aff0SRichard Purdie u32 count, maxcount = 0xffffffff; 2284b23aff0SRichard Purdie size_t retlen; 2294b23aff0SRichard Purdie 2304b23aff0SRichard Purdie for (page = 0; page < cxt->oops_pages; page++) { 2312986bd2aSRichard Purdie ret = mtd->read(mtd, page * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count); 2322986bd2aSRichard Purdie if ((retlen != 4) || ((ret < 0) && (ret != -EUCLEAN))) { 2332986bd2aSRichard Purdie printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" 2342986bd2aSRichard Purdie ", err %d.\n", page * OOPS_PAGE_SIZE, retlen, ret); 2352986bd2aSRichard Purdie continue; 2362986bd2aSRichard Purdie } 2372986bd2aSRichard Purdie 2384b23aff0SRichard Purdie if (count == 0xffffffff) 2394b23aff0SRichard Purdie continue; 2404b23aff0SRichard Purdie if (maxcount == 0xffffffff) { 2414b23aff0SRichard Purdie maxcount = count; 2424b23aff0SRichard Purdie maxpos = page; 2434b23aff0SRichard Purdie } else if ((count < 0x40000000) && (maxcount > 0xc0000000)) { 2444b23aff0SRichard Purdie maxcount = count; 2454b23aff0SRichard Purdie maxpos = page; 2464b23aff0SRichard Purdie } else if ((count > maxcount) && (count < 0xc0000000)) { 2474b23aff0SRichard Purdie maxcount = count; 2484b23aff0SRichard Purdie maxpos = page; 2494b23aff0SRichard Purdie } else if ((count > maxcount) && (count > 0xc0000000) 2504b23aff0SRichard Purdie && (maxcount > 0x80000000)) { 2514b23aff0SRichard Purdie maxcount = count; 2524b23aff0SRichard Purdie maxpos = page; 2534b23aff0SRichard Purdie } 2544b23aff0SRichard Purdie } 2554b23aff0SRichard Purdie if (maxcount == 0xffffffff) { 2564b23aff0SRichard Purdie cxt->nextpage = 0; 2574b23aff0SRichard Purdie cxt->nextcount = 1; 2584b23aff0SRichard Purdie cxt->ready = 1; 2594b23aff0SRichard Purdie printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n", 2604b23aff0SRichard Purdie cxt->nextpage, cxt->nextcount); 2616ce0a856SRichard Purdie return; 2624b23aff0SRichard Purdie } 2634b23aff0SRichard Purdie 2644b23aff0SRichard Purdie cxt->nextpage = maxpos; 2654b23aff0SRichard Purdie cxt->nextcount = maxcount; 2664b23aff0SRichard Purdie 2676ce0a856SRichard Purdie mtdoops_inc_counter(cxt); 2684b23aff0SRichard Purdie } 2694b23aff0SRichard Purdie 2704b23aff0SRichard Purdie 2714b23aff0SRichard Purdie static void mtdoops_notify_add(struct mtd_info *mtd) 2724b23aff0SRichard Purdie { 2734b23aff0SRichard Purdie struct mtdoops_context *cxt = &oops_cxt; 2744b23aff0SRichard Purdie 2754b23aff0SRichard Purdie if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) 2764b23aff0SRichard Purdie return; 2774b23aff0SRichard Purdie 2784b23aff0SRichard Purdie if (mtd->size < (mtd->erasesize * 2)) { 2794b23aff0SRichard Purdie printk(KERN_ERR "MTD partition %d not big enough for mtdoops\n", 2804b23aff0SRichard Purdie mtd->index); 2814b23aff0SRichard Purdie return; 2824b23aff0SRichard Purdie } 2834b23aff0SRichard Purdie 28479dcd8e9SRichard Purdie if (mtd->erasesize < OOPS_PAGE_SIZE) { 28579dcd8e9SRichard Purdie printk(KERN_ERR "Eraseblock size of MTD partition %d too small\n", 28679dcd8e9SRichard Purdie mtd->index); 28779dcd8e9SRichard Purdie return; 28879dcd8e9SRichard Purdie } 28979dcd8e9SRichard Purdie 2904b23aff0SRichard Purdie cxt->mtd = mtd; 2914b23aff0SRichard Purdie cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE; 2924b23aff0SRichard Purdie 2936ce0a856SRichard Purdie find_next_position(cxt); 2944b23aff0SRichard Purdie 29579dcd8e9SRichard Purdie printk(KERN_INFO "mtdoops: Attached to MTD device %d\n", mtd->index); 2964b23aff0SRichard Purdie } 2974b23aff0SRichard Purdie 2984b23aff0SRichard Purdie static void mtdoops_notify_remove(struct mtd_info *mtd) 2994b23aff0SRichard Purdie { 3004b23aff0SRichard Purdie struct mtdoops_context *cxt = &oops_cxt; 3014b23aff0SRichard Purdie 3024b23aff0SRichard Purdie if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) 3034b23aff0SRichard Purdie return; 3044b23aff0SRichard Purdie 3054b23aff0SRichard Purdie cxt->mtd = NULL; 3064b23aff0SRichard Purdie flush_scheduled_work(); 3074b23aff0SRichard Purdie } 3084b23aff0SRichard Purdie 3098691a729SRichard Purdie static void mtdoops_console_sync(void) 3104b23aff0SRichard Purdie { 3118691a729SRichard Purdie struct mtdoops_context *cxt = &oops_cxt; 3124b23aff0SRichard Purdie struct mtd_info *mtd = cxt->mtd; 31347c152b8SRichard Purdie unsigned long flags; 3144b23aff0SRichard Purdie 3156ce0a856SRichard Purdie if (!cxt->ready || !mtd || cxt->writecount == 0) 3164b23aff0SRichard Purdie return; 3174b23aff0SRichard Purdie 31847c152b8SRichard Purdie /* 31947c152b8SRichard Purdie * Once ready is 0 and we've held the lock no further writes to the 32047c152b8SRichard Purdie * buffer will happen 32147c152b8SRichard Purdie */ 32247c152b8SRichard Purdie spin_lock_irqsave(&cxt->writecount_lock, flags); 32347c152b8SRichard Purdie if (!cxt->ready) { 32447c152b8SRichard Purdie spin_unlock_irqrestore(&cxt->writecount_lock, flags); 32547c152b8SRichard Purdie return; 32647c152b8SRichard Purdie } 3274b23aff0SRichard Purdie cxt->ready = 0; 32847c152b8SRichard Purdie spin_unlock_irqrestore(&cxt->writecount_lock, flags); 3294b23aff0SRichard Purdie 330621e4f8eSRichard Purdie if (mtd->panic_write && in_interrupt()) 331621e4f8eSRichard Purdie /* Interrupt context, we're going to panic so try and log */ 332621e4f8eSRichard Purdie mtdoops_write(cxt, 1); 333621e4f8eSRichard Purdie else 3346ce0a856SRichard Purdie schedule_work(&cxt->work_write); 3354b23aff0SRichard Purdie } 3364b23aff0SRichard Purdie 3378691a729SRichard Purdie static void 3388691a729SRichard Purdie mtdoops_console_write(struct console *co, const char *s, unsigned int count) 3398691a729SRichard Purdie { 3408691a729SRichard Purdie struct mtdoops_context *cxt = co->data; 3418691a729SRichard Purdie struct mtd_info *mtd = cxt->mtd; 34247c152b8SRichard Purdie unsigned long flags; 3438691a729SRichard Purdie 3448691a729SRichard Purdie if (!oops_in_progress) { 3458691a729SRichard Purdie mtdoops_console_sync(); 3468691a729SRichard Purdie return; 3478691a729SRichard Purdie } 3488691a729SRichard Purdie 3498691a729SRichard Purdie if (!cxt->ready || !mtd) 3504b23aff0SRichard Purdie return; 3514b23aff0SRichard Purdie 35247c152b8SRichard Purdie /* Locking on writecount ensures sequential writes to the buffer */ 35347c152b8SRichard Purdie spin_lock_irqsave(&cxt->writecount_lock, flags); 35447c152b8SRichard Purdie 35547c152b8SRichard Purdie /* Check ready status didn't change whilst waiting for the lock */ 35647c152b8SRichard Purdie if (!cxt->ready) 35747c152b8SRichard Purdie return; 35847c152b8SRichard Purdie 3594b23aff0SRichard Purdie if (cxt->writecount == 0) { 3604b23aff0SRichard Purdie u32 *stamp = cxt->oops_buf; 3614b23aff0SRichard Purdie *stamp = cxt->nextcount; 3624b23aff0SRichard Purdie cxt->writecount = 4; 3634b23aff0SRichard Purdie } 3644b23aff0SRichard Purdie 3654b23aff0SRichard Purdie if ((count + cxt->writecount) > OOPS_PAGE_SIZE) 3664b23aff0SRichard Purdie count = OOPS_PAGE_SIZE - cxt->writecount; 3674b23aff0SRichard Purdie 368235d6200SPeter Korsgaard memcpy(cxt->oops_buf + cxt->writecount, s, count); 369235d6200SPeter Korsgaard cxt->writecount += count; 37047c152b8SRichard Purdie 37147c152b8SRichard Purdie spin_unlock_irqrestore(&cxt->writecount_lock, flags); 37247c152b8SRichard Purdie 37347c152b8SRichard Purdie if (cxt->writecount == OOPS_PAGE_SIZE) 37447c152b8SRichard Purdie mtdoops_console_sync(); 3754b23aff0SRichard Purdie } 3764b23aff0SRichard Purdie 3774b23aff0SRichard Purdie static int __init mtdoops_console_setup(struct console *co, char *options) 3784b23aff0SRichard Purdie { 3794b23aff0SRichard Purdie struct mtdoops_context *cxt = co->data; 3804b23aff0SRichard Purdie 3814b23aff0SRichard Purdie if (cxt->mtd_index != -1) 3824b23aff0SRichard Purdie return -EBUSY; 3834b23aff0SRichard Purdie if (co->index == -1) 3844b23aff0SRichard Purdie return -EINVAL; 3854b23aff0SRichard Purdie 3864b23aff0SRichard Purdie cxt->mtd_index = co->index; 3874b23aff0SRichard Purdie return 0; 3884b23aff0SRichard Purdie } 3894b23aff0SRichard Purdie 3904b23aff0SRichard Purdie static struct mtd_notifier mtdoops_notifier = { 3914b23aff0SRichard Purdie .add = mtdoops_notify_add, 3924b23aff0SRichard Purdie .remove = mtdoops_notify_remove, 3934b23aff0SRichard Purdie }; 3944b23aff0SRichard Purdie 3954b23aff0SRichard Purdie static struct console mtdoops_console = { 3964b23aff0SRichard Purdie .name = "ttyMTD", 3974b23aff0SRichard Purdie .write = mtdoops_console_write, 3984b23aff0SRichard Purdie .setup = mtdoops_console_setup, 3998691a729SRichard Purdie .unblank = mtdoops_console_sync, 4004b23aff0SRichard Purdie .index = -1, 4014b23aff0SRichard Purdie .data = &oops_cxt, 4024b23aff0SRichard Purdie }; 4034b23aff0SRichard Purdie 4044b23aff0SRichard Purdie static int __init mtdoops_console_init(void) 4054b23aff0SRichard Purdie { 4064b23aff0SRichard Purdie struct mtdoops_context *cxt = &oops_cxt; 4074b23aff0SRichard Purdie 4084b23aff0SRichard Purdie cxt->mtd_index = -1; 4094b23aff0SRichard Purdie cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE); 4104b23aff0SRichard Purdie 4114b23aff0SRichard Purdie if (!cxt->oops_buf) { 41279dcd8e9SRichard Purdie printk(KERN_ERR "Failed to allocate mtdoops buffer workspace\n"); 4134b23aff0SRichard Purdie return -ENOMEM; 4144b23aff0SRichard Purdie } 4154b23aff0SRichard Purdie 4166ce0a856SRichard Purdie INIT_WORK(&cxt->work_erase, mtdoops_workfunc_erase); 4176ce0a856SRichard Purdie INIT_WORK(&cxt->work_write, mtdoops_workfunc_write); 4184b23aff0SRichard Purdie 4194b23aff0SRichard Purdie register_console(&mtdoops_console); 4204b23aff0SRichard Purdie register_mtd_user(&mtdoops_notifier); 4214b23aff0SRichard Purdie return 0; 4224b23aff0SRichard Purdie } 4234b23aff0SRichard Purdie 4244b23aff0SRichard Purdie static void __exit mtdoops_console_exit(void) 4254b23aff0SRichard Purdie { 4264b23aff0SRichard Purdie struct mtdoops_context *cxt = &oops_cxt; 4274b23aff0SRichard Purdie 4284b23aff0SRichard Purdie unregister_mtd_user(&mtdoops_notifier); 4294b23aff0SRichard Purdie unregister_console(&mtdoops_console); 4304b23aff0SRichard Purdie vfree(cxt->oops_buf); 4314b23aff0SRichard Purdie } 4324b23aff0SRichard Purdie 4334b23aff0SRichard Purdie 4344b23aff0SRichard Purdie subsys_initcall(mtdoops_console_init); 4354b23aff0SRichard Purdie module_exit(mtdoops_console_exit); 4364b23aff0SRichard Purdie 4374b23aff0SRichard Purdie MODULE_LICENSE("GPL"); 4384b23aff0SRichard Purdie MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>"); 4394b23aff0SRichard Purdie MODULE_DESCRIPTION("MTD Oops/Panic console logger/driver"); 440