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> 3147c152b8SRichard Purdie #include <linux/spinlock.h> 324b23aff0SRichard Purdie #include <linux/mtd/mtd.h> 334b23aff0SRichard Purdie 344b23aff0SRichard Purdie #define OOPS_PAGE_SIZE 4096 354b23aff0SRichard Purdie 366ce0a856SRichard Purdie struct mtdoops_context { 374b23aff0SRichard Purdie int mtd_index; 386ce0a856SRichard Purdie struct work_struct work_erase; 396ce0a856SRichard Purdie struct work_struct work_write; 404b23aff0SRichard Purdie struct mtd_info *mtd; 414b23aff0SRichard Purdie int oops_pages; 424b23aff0SRichard Purdie int nextpage; 434b23aff0SRichard Purdie int nextcount; 444b23aff0SRichard Purdie 454b23aff0SRichard Purdie void *oops_buf; 4647c152b8SRichard Purdie 4747c152b8SRichard Purdie /* writecount and disabling ready are spin lock protected */ 4847c152b8SRichard Purdie spinlock_t writecount_lock; 494b23aff0SRichard Purdie int ready; 504b23aff0SRichard Purdie int writecount; 514b23aff0SRichard Purdie } oops_cxt; 524b23aff0SRichard Purdie 534b23aff0SRichard Purdie static void mtdoops_erase_callback(struct erase_info *done) 544b23aff0SRichard Purdie { 554b23aff0SRichard Purdie wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; 564b23aff0SRichard Purdie wake_up(wait_q); 574b23aff0SRichard Purdie } 584b23aff0SRichard Purdie 594b23aff0SRichard Purdie static int mtdoops_erase_block(struct mtd_info *mtd, int offset) 604b23aff0SRichard Purdie { 614b23aff0SRichard Purdie struct erase_info erase; 624b23aff0SRichard Purdie DECLARE_WAITQUEUE(wait, current); 634b23aff0SRichard Purdie wait_queue_head_t wait_q; 644b23aff0SRichard Purdie int ret; 654b23aff0SRichard Purdie 664b23aff0SRichard Purdie init_waitqueue_head(&wait_q); 674b23aff0SRichard Purdie erase.mtd = mtd; 684b23aff0SRichard Purdie erase.callback = mtdoops_erase_callback; 694b23aff0SRichard Purdie erase.addr = offset; 704b23aff0SRichard Purdie erase.len = mtd->erasesize; 714b23aff0SRichard Purdie erase.priv = (u_long)&wait_q; 724b23aff0SRichard Purdie 734b23aff0SRichard Purdie set_current_state(TASK_INTERRUPTIBLE); 744b23aff0SRichard Purdie add_wait_queue(&wait_q, &wait); 754b23aff0SRichard Purdie 764b23aff0SRichard Purdie ret = mtd->erase(mtd, &erase); 774b23aff0SRichard Purdie if (ret) { 784b23aff0SRichard Purdie set_current_state(TASK_RUNNING); 794b23aff0SRichard Purdie remove_wait_queue(&wait_q, &wait); 804b23aff0SRichard Purdie printk (KERN_WARNING "mtdoops: erase of region [0x%x, 0x%x] " 814b23aff0SRichard Purdie "on \"%s\" failed\n", 824b23aff0SRichard Purdie erase.addr, erase.len, mtd->name); 834b23aff0SRichard Purdie return ret; 844b23aff0SRichard Purdie } 854b23aff0SRichard Purdie 864b23aff0SRichard Purdie schedule(); /* Wait for erase to finish. */ 874b23aff0SRichard Purdie remove_wait_queue(&wait_q, &wait); 884b23aff0SRichard Purdie 894b23aff0SRichard Purdie return 0; 904b23aff0SRichard Purdie } 914b23aff0SRichard Purdie 926ce0a856SRichard Purdie static void mtdoops_inc_counter(struct mtdoops_context *cxt) 934b23aff0SRichard Purdie { 944b23aff0SRichard Purdie struct mtd_info *mtd = cxt->mtd; 954b23aff0SRichard Purdie size_t retlen; 964b23aff0SRichard Purdie u32 count; 974b23aff0SRichard Purdie int ret; 984b23aff0SRichard Purdie 994b23aff0SRichard Purdie cxt->nextpage++; 1004b23aff0SRichard Purdie if (cxt->nextpage > cxt->oops_pages) 1014b23aff0SRichard Purdie cxt->nextpage = 0; 1024b23aff0SRichard Purdie cxt->nextcount++; 1034b23aff0SRichard Purdie if (cxt->nextcount == 0xffffffff) 1044b23aff0SRichard Purdie cxt->nextcount = 0; 1054b23aff0SRichard Purdie 1064b23aff0SRichard Purdie ret = mtd->read(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 4, 1074b23aff0SRichard Purdie &retlen, (u_char *) &count); 1082986bd2aSRichard Purdie if ((retlen != 4) || ((ret < 0) && (ret != -EUCLEAN))) { 10968d09b1bSAndrew Morton printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" 1104b23aff0SRichard Purdie ", err %d.\n", cxt->nextpage * OOPS_PAGE_SIZE, 1114b23aff0SRichard Purdie retlen, ret); 1126ce0a856SRichard Purdie schedule_work(&cxt->work_erase); 1136ce0a856SRichard Purdie return; 1144b23aff0SRichard Purdie } 1154b23aff0SRichard Purdie 1164b23aff0SRichard Purdie /* See if we need to erase the next block */ 1176ce0a856SRichard Purdie if (count != 0xffffffff) { 1186ce0a856SRichard Purdie schedule_work(&cxt->work_erase); 1196ce0a856SRichard Purdie return; 1206ce0a856SRichard Purdie } 1214b23aff0SRichard Purdie 1224b23aff0SRichard Purdie printk(KERN_DEBUG "mtdoops: Ready %d, %d (no erase)\n", 1234b23aff0SRichard Purdie cxt->nextpage, cxt->nextcount); 1244b23aff0SRichard Purdie cxt->ready = 1; 1254b23aff0SRichard Purdie } 1264b23aff0SRichard Purdie 1276ce0a856SRichard Purdie /* Scheduled work - when we can't proceed without erasing a block */ 1286ce0a856SRichard Purdie static void mtdoops_workfunc_erase(struct work_struct *work) 1294b23aff0SRichard Purdie { 1306ce0a856SRichard Purdie struct mtdoops_context *cxt = 1316ce0a856SRichard Purdie container_of(work, struct mtdoops_context, work_erase); 1324b23aff0SRichard Purdie struct mtd_info *mtd = cxt->mtd; 1334b23aff0SRichard Purdie int i = 0, j, ret, mod; 1344b23aff0SRichard Purdie 1354b23aff0SRichard Purdie /* We were unregistered */ 1364b23aff0SRichard Purdie if (!mtd) 1374b23aff0SRichard Purdie return; 1384b23aff0SRichard Purdie 1394b23aff0SRichard Purdie mod = (cxt->nextpage * OOPS_PAGE_SIZE) % mtd->erasesize; 1404b23aff0SRichard Purdie if (mod != 0) { 1414b23aff0SRichard Purdie cxt->nextpage = cxt->nextpage + ((mtd->erasesize - mod) / OOPS_PAGE_SIZE); 1424b23aff0SRichard Purdie if (cxt->nextpage > cxt->oops_pages) 1434b23aff0SRichard Purdie cxt->nextpage = 0; 1444b23aff0SRichard Purdie } 1454b23aff0SRichard Purdie 1462986bd2aSRichard Purdie while (mtd->block_isbad) { 1472986bd2aSRichard Purdie ret = mtd->block_isbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); 1482986bd2aSRichard Purdie if (!ret) 1492986bd2aSRichard Purdie break; 1502986bd2aSRichard Purdie if (ret < 0) { 1512986bd2aSRichard Purdie printk(KERN_ERR "mtdoops: block_isbad failed, aborting.\n"); 1522986bd2aSRichard Purdie return; 1532986bd2aSRichard Purdie } 1544b23aff0SRichard Purdie badblock: 1554b23aff0SRichard Purdie printk(KERN_WARNING "mtdoops: Bad block at %08x\n", 1564b23aff0SRichard Purdie cxt->nextpage * OOPS_PAGE_SIZE); 1574b23aff0SRichard Purdie i++; 1584b23aff0SRichard Purdie cxt->nextpage = cxt->nextpage + (mtd->erasesize / OOPS_PAGE_SIZE); 1594b23aff0SRichard Purdie if (cxt->nextpage > cxt->oops_pages) 1604b23aff0SRichard Purdie cxt->nextpage = 0; 1614b23aff0SRichard Purdie if (i == (cxt->oops_pages / (mtd->erasesize / OOPS_PAGE_SIZE))) { 1624b23aff0SRichard Purdie printk(KERN_ERR "mtdoops: All blocks bad!\n"); 1634b23aff0SRichard Purdie return; 1644b23aff0SRichard Purdie } 1654b23aff0SRichard Purdie } 1664b23aff0SRichard Purdie 1674b23aff0SRichard Purdie for (j = 0, ret = -1; (j < 3) && (ret < 0); j++) 1684b23aff0SRichard Purdie ret = mtdoops_erase_block(mtd, cxt->nextpage * OOPS_PAGE_SIZE); 1694b23aff0SRichard Purdie 1702986bd2aSRichard Purdie if (ret >= 0) { 1712986bd2aSRichard Purdie printk(KERN_DEBUG "mtdoops: Ready %d, %d \n", cxt->nextpage, cxt->nextcount); 1722986bd2aSRichard Purdie cxt->ready = 1; 1732986bd2aSRichard Purdie return; 1744b23aff0SRichard Purdie } 1754b23aff0SRichard Purdie 1762986bd2aSRichard Purdie if (mtd->block_markbad && (ret == -EIO)) { 1772986bd2aSRichard Purdie ret = mtd->block_markbad(mtd, cxt->nextpage * OOPS_PAGE_SIZE); 1782986bd2aSRichard Purdie if (ret < 0) { 1792986bd2aSRichard Purdie printk(KERN_ERR "mtdoops: block_markbad failed, aborting.\n"); 1802986bd2aSRichard Purdie return; 1812986bd2aSRichard Purdie } 1822986bd2aSRichard Purdie } 1832986bd2aSRichard Purdie goto badblock; 1844b23aff0SRichard Purdie } 1854b23aff0SRichard Purdie 1866ce0a856SRichard Purdie static void mtdoops_workfunc_write(struct work_struct *work) 1874b23aff0SRichard Purdie { 1884b23aff0SRichard Purdie struct mtdoops_context *cxt = 1896ce0a856SRichard Purdie container_of(work, struct mtdoops_context, work_write); 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 1986ce0a856SRichard Purdie ret = mtd->write(mtd, cxt->nextpage * OOPS_PAGE_SIZE, 1996ce0a856SRichard Purdie OOPS_PAGE_SIZE, &retlen, cxt->oops_buf); 2006ce0a856SRichard Purdie 2016ce0a856SRichard Purdie cxt->writecount = 0; 2026ce0a856SRichard Purdie 2036ce0a856SRichard Purdie if ((retlen != OOPS_PAGE_SIZE) || (ret < 0)) 2046ce0a856SRichard Purdie printk(KERN_ERR "mtdoops: Write failure at %d (%td of %d written), err %d.\n", 2056ce0a856SRichard Purdie cxt->nextpage * OOPS_PAGE_SIZE, retlen, OOPS_PAGE_SIZE, ret); 2066ce0a856SRichard Purdie 2076ce0a856SRichard Purdie mtdoops_inc_counter(cxt); 2084b23aff0SRichard Purdie } 2094b23aff0SRichard Purdie 2106ce0a856SRichard Purdie static void find_next_position(struct mtdoops_context *cxt) 2114b23aff0SRichard Purdie { 2124b23aff0SRichard Purdie struct mtd_info *mtd = cxt->mtd; 2132986bd2aSRichard Purdie int ret, page, maxpos = 0; 2144b23aff0SRichard Purdie u32 count, maxcount = 0xffffffff; 2154b23aff0SRichard Purdie size_t retlen; 2164b23aff0SRichard Purdie 2174b23aff0SRichard Purdie for (page = 0; page < cxt->oops_pages; page++) { 2182986bd2aSRichard Purdie ret = mtd->read(mtd, page * OOPS_PAGE_SIZE, 4, &retlen, (u_char *) &count); 2192986bd2aSRichard Purdie if ((retlen != 4) || ((ret < 0) && (ret != -EUCLEAN))) { 2202986bd2aSRichard Purdie printk(KERN_ERR "mtdoops: Read failure at %d (%td of 4 read)" 2212986bd2aSRichard Purdie ", err %d.\n", page * OOPS_PAGE_SIZE, retlen, ret); 2222986bd2aSRichard Purdie continue; 2232986bd2aSRichard Purdie } 2242986bd2aSRichard Purdie 2254b23aff0SRichard Purdie if (count == 0xffffffff) 2264b23aff0SRichard Purdie continue; 2274b23aff0SRichard Purdie if (maxcount == 0xffffffff) { 2284b23aff0SRichard Purdie maxcount = count; 2294b23aff0SRichard Purdie maxpos = page; 2304b23aff0SRichard Purdie } else if ((count < 0x40000000) && (maxcount > 0xc0000000)) { 2314b23aff0SRichard Purdie maxcount = count; 2324b23aff0SRichard Purdie maxpos = page; 2334b23aff0SRichard Purdie } else if ((count > maxcount) && (count < 0xc0000000)) { 2344b23aff0SRichard Purdie maxcount = count; 2354b23aff0SRichard Purdie maxpos = page; 2364b23aff0SRichard Purdie } else if ((count > maxcount) && (count > 0xc0000000) 2374b23aff0SRichard Purdie && (maxcount > 0x80000000)) { 2384b23aff0SRichard Purdie maxcount = count; 2394b23aff0SRichard Purdie maxpos = page; 2404b23aff0SRichard Purdie } 2414b23aff0SRichard Purdie } 2424b23aff0SRichard Purdie if (maxcount == 0xffffffff) { 2434b23aff0SRichard Purdie cxt->nextpage = 0; 2444b23aff0SRichard Purdie cxt->nextcount = 1; 2454b23aff0SRichard Purdie cxt->ready = 1; 2464b23aff0SRichard Purdie printk(KERN_DEBUG "mtdoops: Ready %d, %d (first init)\n", 2474b23aff0SRichard Purdie cxt->nextpage, cxt->nextcount); 2486ce0a856SRichard Purdie return; 2494b23aff0SRichard Purdie } 2504b23aff0SRichard Purdie 2514b23aff0SRichard Purdie cxt->nextpage = maxpos; 2524b23aff0SRichard Purdie cxt->nextcount = maxcount; 2534b23aff0SRichard Purdie 2546ce0a856SRichard Purdie mtdoops_inc_counter(cxt); 2554b23aff0SRichard Purdie } 2564b23aff0SRichard Purdie 2574b23aff0SRichard Purdie 2584b23aff0SRichard Purdie static void mtdoops_notify_add(struct mtd_info *mtd) 2594b23aff0SRichard Purdie { 2604b23aff0SRichard Purdie struct mtdoops_context *cxt = &oops_cxt; 2614b23aff0SRichard Purdie 2624b23aff0SRichard Purdie if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) 2634b23aff0SRichard Purdie return; 2644b23aff0SRichard Purdie 2654b23aff0SRichard Purdie if (mtd->size < (mtd->erasesize * 2)) { 2664b23aff0SRichard Purdie printk(KERN_ERR "MTD partition %d not big enough for mtdoops\n", 2674b23aff0SRichard Purdie mtd->index); 2684b23aff0SRichard Purdie return; 2694b23aff0SRichard Purdie } 2704b23aff0SRichard Purdie 271*79dcd8e9SRichard Purdie if (mtd->erasesize < OOPS_PAGE_SIZE) { 272*79dcd8e9SRichard Purdie printk(KERN_ERR "Eraseblock size of MTD partition %d too small\n", 273*79dcd8e9SRichard Purdie mtd->index); 274*79dcd8e9SRichard Purdie return; 275*79dcd8e9SRichard Purdie } 276*79dcd8e9SRichard Purdie 2774b23aff0SRichard Purdie cxt->mtd = mtd; 2784b23aff0SRichard Purdie cxt->oops_pages = mtd->size / OOPS_PAGE_SIZE; 2794b23aff0SRichard Purdie 2806ce0a856SRichard Purdie find_next_position(cxt); 2814b23aff0SRichard Purdie 282*79dcd8e9SRichard Purdie printk(KERN_INFO "mtdoops: Attached to MTD device %d\n", mtd->index); 2834b23aff0SRichard Purdie } 2844b23aff0SRichard Purdie 2854b23aff0SRichard Purdie static void mtdoops_notify_remove(struct mtd_info *mtd) 2864b23aff0SRichard Purdie { 2874b23aff0SRichard Purdie struct mtdoops_context *cxt = &oops_cxt; 2884b23aff0SRichard Purdie 2894b23aff0SRichard Purdie if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0) 2904b23aff0SRichard Purdie return; 2914b23aff0SRichard Purdie 2924b23aff0SRichard Purdie cxt->mtd = NULL; 2934b23aff0SRichard Purdie flush_scheduled_work(); 2944b23aff0SRichard Purdie } 2954b23aff0SRichard Purdie 2968691a729SRichard Purdie static void mtdoops_console_sync(void) 2974b23aff0SRichard Purdie { 2988691a729SRichard Purdie struct mtdoops_context *cxt = &oops_cxt; 2994b23aff0SRichard Purdie struct mtd_info *mtd = cxt->mtd; 30047c152b8SRichard Purdie unsigned long flags; 3014b23aff0SRichard Purdie 3026ce0a856SRichard Purdie if (!cxt->ready || !mtd || cxt->writecount == 0) 3034b23aff0SRichard Purdie return; 3044b23aff0SRichard Purdie 30547c152b8SRichard Purdie /* 30647c152b8SRichard Purdie * Once ready is 0 and we've held the lock no further writes to the 30747c152b8SRichard Purdie * buffer will happen 30847c152b8SRichard Purdie */ 30947c152b8SRichard Purdie spin_lock_irqsave(&cxt->writecount_lock, flags); 31047c152b8SRichard Purdie if (!cxt->ready) { 31147c152b8SRichard Purdie spin_unlock_irqrestore(&cxt->writecount_lock, flags); 31247c152b8SRichard Purdie return; 31347c152b8SRichard Purdie } 3144b23aff0SRichard Purdie cxt->ready = 0; 31547c152b8SRichard Purdie spin_unlock_irqrestore(&cxt->writecount_lock, flags); 3164b23aff0SRichard Purdie 3176ce0a856SRichard Purdie schedule_work(&cxt->work_write); 3184b23aff0SRichard Purdie } 3194b23aff0SRichard Purdie 3208691a729SRichard Purdie static void 3218691a729SRichard Purdie mtdoops_console_write(struct console *co, const char *s, unsigned int count) 3228691a729SRichard Purdie { 3238691a729SRichard Purdie struct mtdoops_context *cxt = co->data; 3248691a729SRichard Purdie struct mtd_info *mtd = cxt->mtd; 32547c152b8SRichard Purdie unsigned long flags; 3268691a729SRichard Purdie 3278691a729SRichard Purdie if (!oops_in_progress) { 3288691a729SRichard Purdie mtdoops_console_sync(); 3298691a729SRichard Purdie return; 3308691a729SRichard Purdie } 3318691a729SRichard Purdie 3328691a729SRichard Purdie if (!cxt->ready || !mtd) 3334b23aff0SRichard Purdie return; 3344b23aff0SRichard Purdie 33547c152b8SRichard Purdie /* Locking on writecount ensures sequential writes to the buffer */ 33647c152b8SRichard Purdie spin_lock_irqsave(&cxt->writecount_lock, flags); 33747c152b8SRichard Purdie 33847c152b8SRichard Purdie /* Check ready status didn't change whilst waiting for the lock */ 33947c152b8SRichard Purdie if (!cxt->ready) 34047c152b8SRichard Purdie return; 34147c152b8SRichard Purdie 3424b23aff0SRichard Purdie if (cxt->writecount == 0) { 3434b23aff0SRichard Purdie u32 *stamp = cxt->oops_buf; 3444b23aff0SRichard Purdie *stamp = cxt->nextcount; 3454b23aff0SRichard Purdie cxt->writecount = 4; 3464b23aff0SRichard Purdie } 3474b23aff0SRichard Purdie 3484b23aff0SRichard Purdie if ((count + cxt->writecount) > OOPS_PAGE_SIZE) 3494b23aff0SRichard Purdie count = OOPS_PAGE_SIZE - cxt->writecount; 3504b23aff0SRichard Purdie 351235d6200SPeter Korsgaard memcpy(cxt->oops_buf + cxt->writecount, s, count); 352235d6200SPeter Korsgaard cxt->writecount += count; 35347c152b8SRichard Purdie 35447c152b8SRichard Purdie spin_unlock_irqrestore(&cxt->writecount_lock, flags); 35547c152b8SRichard Purdie 35647c152b8SRichard Purdie if (cxt->writecount == OOPS_PAGE_SIZE) 35747c152b8SRichard Purdie mtdoops_console_sync(); 3584b23aff0SRichard Purdie } 3594b23aff0SRichard Purdie 3604b23aff0SRichard Purdie static int __init mtdoops_console_setup(struct console *co, char *options) 3614b23aff0SRichard Purdie { 3624b23aff0SRichard Purdie struct mtdoops_context *cxt = co->data; 3634b23aff0SRichard Purdie 3644b23aff0SRichard Purdie if (cxt->mtd_index != -1) 3654b23aff0SRichard Purdie return -EBUSY; 3664b23aff0SRichard Purdie if (co->index == -1) 3674b23aff0SRichard Purdie return -EINVAL; 3684b23aff0SRichard Purdie 3694b23aff0SRichard Purdie cxt->mtd_index = co->index; 3704b23aff0SRichard Purdie return 0; 3714b23aff0SRichard Purdie } 3724b23aff0SRichard Purdie 3734b23aff0SRichard Purdie static struct mtd_notifier mtdoops_notifier = { 3744b23aff0SRichard Purdie .add = mtdoops_notify_add, 3754b23aff0SRichard Purdie .remove = mtdoops_notify_remove, 3764b23aff0SRichard Purdie }; 3774b23aff0SRichard Purdie 3784b23aff0SRichard Purdie static struct console mtdoops_console = { 3794b23aff0SRichard Purdie .name = "ttyMTD", 3804b23aff0SRichard Purdie .write = mtdoops_console_write, 3814b23aff0SRichard Purdie .setup = mtdoops_console_setup, 3828691a729SRichard Purdie .unblank = mtdoops_console_sync, 3834b23aff0SRichard Purdie .index = -1, 3844b23aff0SRichard Purdie .data = &oops_cxt, 3854b23aff0SRichard Purdie }; 3864b23aff0SRichard Purdie 3874b23aff0SRichard Purdie static int __init mtdoops_console_init(void) 3884b23aff0SRichard Purdie { 3894b23aff0SRichard Purdie struct mtdoops_context *cxt = &oops_cxt; 3904b23aff0SRichard Purdie 3914b23aff0SRichard Purdie cxt->mtd_index = -1; 3924b23aff0SRichard Purdie cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE); 3934b23aff0SRichard Purdie 3944b23aff0SRichard Purdie if (!cxt->oops_buf) { 395*79dcd8e9SRichard Purdie printk(KERN_ERR "Failed to allocate mtdoops buffer workspace\n"); 3964b23aff0SRichard Purdie return -ENOMEM; 3974b23aff0SRichard Purdie } 3984b23aff0SRichard Purdie 3996ce0a856SRichard Purdie INIT_WORK(&cxt->work_erase, mtdoops_workfunc_erase); 4006ce0a856SRichard Purdie INIT_WORK(&cxt->work_write, mtdoops_workfunc_write); 4014b23aff0SRichard Purdie 4024b23aff0SRichard Purdie register_console(&mtdoops_console); 4034b23aff0SRichard Purdie register_mtd_user(&mtdoops_notifier); 4044b23aff0SRichard Purdie return 0; 4054b23aff0SRichard Purdie } 4064b23aff0SRichard Purdie 4074b23aff0SRichard Purdie static void __exit mtdoops_console_exit(void) 4084b23aff0SRichard Purdie { 4094b23aff0SRichard Purdie struct mtdoops_context *cxt = &oops_cxt; 4104b23aff0SRichard Purdie 4114b23aff0SRichard Purdie unregister_mtd_user(&mtdoops_notifier); 4124b23aff0SRichard Purdie unregister_console(&mtdoops_console); 4134b23aff0SRichard Purdie vfree(cxt->oops_buf); 4144b23aff0SRichard Purdie } 4154b23aff0SRichard Purdie 4164b23aff0SRichard Purdie 4174b23aff0SRichard Purdie subsys_initcall(mtdoops_console_init); 4184b23aff0SRichard Purdie module_exit(mtdoops_console_exit); 4194b23aff0SRichard Purdie 4204b23aff0SRichard Purdie MODULE_LICENSE("GPL"); 4214b23aff0SRichard Purdie MODULE_AUTHOR("Richard Purdie <rpurdie@openedhand.com>"); 4224b23aff0SRichard Purdie MODULE_DESCRIPTION("MTD Oops/Panic console logger/driver"); 423