Import xiaomi mtd changes from mayfly-s-oss

This commit is contained in:
Arian 2022-12-22 00:18:49 +01:00 committed by Jens Reidel
parent be89c2c54e
commit ced9995b90
No known key found for this signature in database
GPG Key ID: 23C1E5F512C12303
3 changed files with 450 additions and 21 deletions

View File

@ -18,6 +18,8 @@
#include <linux/platform_device.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/nmi.h>
#include <linux/sched/debug.h>
#define PON_REV2 0x01
@ -51,6 +53,11 @@
#define PON_DBC_CTL 0x71
#define PON_DBC_DELAY_MASK 0x7
#if IS_ENABLED(CONFIG_MTD_OOPS)
extern int g_long_press_reason;
extern void mtdoops_do_dump_if(int reason);
#endif
struct pm8941_data {
unsigned int pull_up_bit;
unsigned int status_bit;
@ -141,6 +148,35 @@ static int pm8941_reboot_notify(struct notifier_block *nb,
return NOTIFY_DONE;
}
void show_state_filter_single(unsigned long state_filter)
{
struct task_struct *g, *p;
#if BITS_PER_LONG == 32
printk(KERN_INFO
" task PC stack pid father\n");
#else
printk(KERN_INFO
" task PC stack pid father\n");
#endif
rcu_read_lock();
for_each_process_thread(g, p) {
/*
* reset the NMI-timeout, listing all files on a slow
* console might take a lot of time:
* Also, reset softlockup watchdogs on all CPUs, because
* another CPU might be blocked waiting for us to process
* an IPI.
*/
touch_nmi_watchdog();
//touch_all_softlockup_watchdogs();
if (p->state == state_filter)
sched_show_task(p);
}
rcu_read_unlock();
}
static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
{
struct pm8941_pwrkey *pwrkey = _data;
@ -148,6 +184,42 @@ static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
int error;
u64 elapsed_us;
if (!strcmp(pwrkey->data->name,"pmic_pwrkey_bark")){
#if IS_ENABLED(CONFIG_MTD_OOPS)
error = regmap_read(pwrkey->regmap,
pwrkey->baseaddr + PON_RT_STS, &sts);
if (error)
return IRQ_HANDLED;
sts &= pwrkey->data->status_bit;
if(sts){
dev_err(pwrkey->dev, "pwrkey_bark_irq trigger, start dump mtdoops");
mtdoops_do_dump_if(g_long_press_reason);
}
#endif
return IRQ_HANDLED;
}
else if (!strcmp(pwrkey->data->name,"pmic_pwrkey_resin_bark")){
error = regmap_read(pwrkey->regmap,
pwrkey->baseaddr + PON_RT_STS, &sts);
if (error)
return IRQ_HANDLED;
sts &= pwrkey->data->status_bit;
if(sts){
int tmp_console = console_loglevel;
dev_err(pwrkey->dev, "pwrkey_resin_bark_irq trigger, start D&R task info");
console_verbose();
pr_info("------ collect D&R-state processes info before long comb key ------\n");
show_state_filter_single(TASK_UNINTERRUPTIBLE);
show_state_filter_single(TASK_RUNNING);
pr_info("------ end collecting D&R-state processes info ------\n");
console_loglevel = tmp_console;
}
else{
}
}
if (pwrkey->sw_debounce_time_us) {
elapsed_us = ktime_us_delta(ktime_get(),
pwrkey->last_release_time);
@ -471,11 +543,33 @@ static const struct pm8941_data pon_gen3_resin_data = {
.has_pon_pbs = true,
};
static const struct pm8941_data pon_gen3_pwrkey_bark_data = {
.status_bit = PON_GEN3_KPDPWR_N_SET,
.name = "pmic_pwrkey_bark",
.phys = "pmic_pwrkey_bark/input0",
.supports_ps_hold_poff_config = false,
.supports_debounce_config = false,
.needs_sw_debounce = true,
.has_pon_pbs = true,
};
static const struct pm8941_data pon_gen3_pwrkey_resin_bark_data = {
.status_bit = PON_GEN3_KPDPWR_N_SET,
.name = "pmic_pwrkey_resin_bark",
.phys = "pmic_pwrkey_resin_bark/input0",
.supports_ps_hold_poff_config = false,
.supports_debounce_config = false,
.needs_sw_debounce = true,
.has_pon_pbs = true,
};
static const struct of_device_id pm8941_pwr_key_id_table[] = {
{ .compatible = "qcom,pm8941-pwrkey", .data = &pwrkey_data },
{ .compatible = "qcom,pm8941-resin", .data = &resin_data },
{ .compatible = "qcom,pmk8350-pwrkey", .data = &pon_gen3_pwrkey_data },
{ .compatible = "qcom,pmk8350-resin", .data = &pon_gen3_resin_data },
{ .compatible = "qcom,pmk8350-pwrkey-bark", .data = &pon_gen3_pwrkey_bark_data },
{ .compatible = "qcom,pmk8350-pwrkey-resin-bark", .data = &pon_gen3_pwrkey_resin_bark_data },
{ }
};
MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table);

View File

@ -141,6 +141,7 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
int offset = to & ~PAGE_MASK; // page offset
int cpylen;
printk(KERN_ERR "_block2mtd_write: start len = %d, mapping_index= %d \n",len,index);
while (len) {
if ((offset+len) > PAGE_SIZE)
cpylen = PAGE_SIZE - offset; // multiple pages
@ -168,6 +169,8 @@ static int _block2mtd_write(struct block2mtd_dev *dev, const u_char *buf,
offset = 0;
index++;
}
printk(KERN_ERR "_block2mtd_write: finish all \n");
return 0;
}
@ -183,6 +186,7 @@ static int block2mtd_write(struct mtd_info *mtd, loff_t to, size_t len,
mutex_unlock(&dev->write_mutex);
if (err > 0)
err = 0;
dev->mtd._sync(mtd);
return err;
}
@ -213,16 +217,34 @@ static void block2mtd_free_device(struct block2mtd_dev *dev)
}
int delSubStr(char * src, char * sub,char * result)
{
int i,find = 0;
int len_src = strlen(src);
int len_sub = strlen(sub);
for( i = 0 ; i <= len_src - len_sub; i++){
if(strncmp(src+i, sub, len_sub)==0){
strncpy(result,src,i);
strncpy(result+i,src+i+len_sub,len_src-i-len_sub);
find =1;
break;
}
}
return find;
}
static struct block2mtd_dev *add_device(char *devname, int erase_size,
int timeout)
{
#ifndef MODULE
//#ifndef MODULE
int i;
#endif
//#endif
const fmode_t mode = FMODE_READ | FMODE_WRITE | FMODE_EXCL;
struct block_device *bdev;
struct block2mtd_dev *dev;
char *name;
char name_for_find_devt[80]={0};
if (!devname)
return NULL;
@ -234,11 +256,14 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size,
/* Get a handle on the device */
bdev = blkdev_get_by_path(devname, mode, dev);
#ifndef MODULE
//#ifndef MODULE
/*
* We might not have the root device mounted at this point.
* Try to resolve the device name by other means.
*/
if(!delSubStr(devname, "block/",name_for_find_devt))
return NULL;
for (i = 0; IS_ERR(bdev) && i <= timeout; i++) {
dev_t devt;
@ -251,12 +276,12 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size,
msleep(1000);
wait_for_device_probe();
devt = name_to_dev_t(devname);
devt = name_to_dev_t(name_for_find_devt);
if (!devt)
continue;
bdev = blkdev_get_by_dev(devt, mode, dev);
}
#endif
//#endif
if (IS_ERR(bdev)) {
pr_err("error: cannot open device %s\n", devname);
@ -307,6 +332,8 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size,
dev->mtd.index,
dev->mtd.name + strlen("block2mtd: "),
dev->mtd.erasesize >> 10, dev->mtd.erasesize);
pr_err("block2mtd: add device sucess \n");
return dev;
err_destroy_mutex:

View File

@ -11,6 +11,7 @@
#include <linux/module.h>
#include <linux/console.h>
#include <linux/vmalloc.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/wait.h>
@ -18,14 +19,77 @@
#include <linux/interrupt.h>
#include <linux/mtd/mtd.h>
#include <linux/kmsg_dump.h>
#include <linux/version.h>
#include <linux/reboot.h>
#include <linux/platform_device.h>
#include <generated/utsrelease.h>
struct pmsg_buffer_hdr {
uint32_t sig;
atomic_t start;
atomic_t size;
uint8_t data[0];
};
/* Maximum MTD partition size */
#define MTDOOPS_MAX_MTD_SIZE (8 * 1024 * 1024)
#define MTDOOPS_MAX_MTD_SIZE (16 * 1024 * 1024)
#define MTDOOPS_KERNMSG_MAGIC 0x5d005d00
#define MTDOOPS_HEADER_SIZE 8
/* Expand kmsg_dump_reason
enum kmsg_dump_reason {
KMSG_DUMP_UNDEF,
KMSG_DUMP_PANIC,
KMSG_DUMP_OOPS,
KMSG_DUMP_EMERG,
KMSG_DUMP_SHUTDOWN,
KMSG_DUMP_MAX
};
*/
enum mtd_dump_reason {
MTD_DUMP_UNDEF,
MTD_DUMP_PANIC,
MTD_DUMP_OOPS,
MTD_DUMP_EMERG,
MTD_DUMP_SHUTDOWN,
MTD_DUMP_RESTART,
MTD_DUMP_POWEROFF,
MTD_DUMP_LONG_PRESS,
MTD_DUMP_MAX
};
static char *kdump_reason[8] = {
"Unknown",
"Kernel Panic",
"Oops!",
"Emerg",
"Shut Down",
"Restart",
"PowerOff",
"Long Press"
};
enum mtdoops_log_type {
MTDOOPS_TYPE_UNDEF,
MTDOOPS_TYPE_DMESG,
MTDOOPS_TYPE_PMSG,
};
static char *log_type[4] = {
"Unknown",
"LAST KMSG",
"LAST LOGCAT"
};
int g_long_press_reason = MTD_DUMP_LONG_PRESS;
EXPORT_SYMBOL_GPL(g_long_press_reason);
static unsigned long record_size = 4096;
static unsigned long lkmsg_record_size = 512 * 1024;
module_param(record_size, ulong, 0400);
MODULE_PARM_DESC(record_size,
"record size for MTD OOPS pages in bytes (default 4096)");
@ -35,13 +99,27 @@ module_param_string(mtddev, mtddev, 80, 0400);
MODULE_PARM_DESC(mtddev,
"name or index number of the MTD device to use");
static int dump_oops = 1;
static int dump_oops = 0;
module_param(dump_oops, int, 0600);
MODULE_PARM_DESC(dump_oops,
"set to 1 to dump oopses, 0 to only dump panics (default 1)");
#define MAX_CMDLINE_PARAM_LEN 64
static char build_fingerprint[MAX_CMDLINE_PARAM_LEN] = {0};
module_param_string(fingerprint, build_fingerprint, MAX_CMDLINE_PARAM_LEN,0644);
struct pmsg_platform_data {
unsigned long mem_size;
phys_addr_t mem_address;
unsigned long console_size;
unsigned long pmsg_size;
};
static struct mtdoops_context {
struct kmsg_dumper dump;
struct notifier_block reboot_nb;
struct pmsg_platform_data pmsg_data;
int mtd_index;
struct work_struct work_erase;
@ -107,13 +185,14 @@ static void mtdoops_inc_counter(struct mtdoops_context *cxt)
if (cxt->nextcount == 0xffffffff)
cxt->nextcount = 0;
printk(KERN_ERR "mtdoops: ready nextpage= %d, nextcount= %d (no erase), oops_page_used_flag = %lx",
cxt->nextpage, cxt->nextcount, *cxt->oops_page_used );
if (page_is_used(cxt, cxt->nextpage)) {
schedule_work(&cxt->work_erase);
return;
}
printk(KERN_DEBUG "mtdoops: ready %d, %d (no erase)\n",
cxt->nextpage, cxt->nextcount);
}
/* Scheduled work - when we can't proceed without erasing a block */
@ -200,9 +279,10 @@ static void mtdoops_write(struct mtdoops_context *cxt, int panic)
printk(KERN_ERR "mtdoops: write failure at %ld (%td of %ld written), error %d\n",
cxt->nextpage * record_size, retlen, record_size, ret);
mark_page_used(cxt, cxt->nextpage);
memset(cxt->oops_buf, 0xff, record_size);
mtdoops_inc_counter(cxt);
// device will reboot ,so do not need find next and erase
// if (!panic)
// mtdoops_inc_counter(cxt);
}
static void mtdoops_workfunc_write(struct work_struct *work)
@ -266,28 +346,160 @@ static void find_next_position(struct mtdoops_context *cxt)
mtdoops_inc_counter(cxt);
}
static void mtdoops_do_dump(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason)
static void mtdoops_add_reason(char *oops_buf, enum mtd_dump_reason reason, enum mtdoops_log_type type, int index, int nextpage)
{
char str_buf[200] = {0};
int ret_len = 0;
struct timespec64 now;
struct tm ts;
ktime_get_coarse_real_ts64(&now);
time64_to_tm(now.tv_sec, 0, &ts);
if (nextpage > 1)
ret_len = snprintf(str_buf, 200,
"\n```\n## Index: %d\t\n### Build: %s\t\n## REASON: %s\n#### LOG TYPE:%s\n#####%04ld-%02d-%02d %02d:%02d:%02d\t\n```c\t\n",
index, build_fingerprint, kdump_reason[reason], log_type[type], ts.tm_year+1900, ts.tm_mon+1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec);
else
ret_len = snprintf(str_buf, 200,
"\n\n## Index: %d\t\n### Build: %s\t\n## REASON: %s\n#### LOG TYPE: %s\n#####%04ld-%02d-%02d %02d:%02d:%02d\t\n```c\t\n",
index, build_fingerprint, kdump_reason[reason], log_type[type], ts.tm_year+1900, ts.tm_mon+1, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec);
memcpy(oops_buf, str_buf, ret_len);
}
static void mtdoops_add_pmsg_head(char *oops_buf, enum mtdoops_log_type type)
{
char str_buf[80] = {0};
int ret_len = 0;
struct timespec64 now;
struct tm ts;
ktime_get_coarse_real_ts64(&now);
time64_to_tm(now.tv_sec, 0, &ts);
ret_len = snprintf(str_buf, 80,
"\n```\n#### LOG TYPE:%s\n#####%04ld-%02d-%02d %02d:%02d:%02d\t\n```c\t\n",
log_type[type], ts.tm_year+1900, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec);
memcpy(oops_buf, str_buf, ret_len);
}
static void mtdoops_do_dump(struct kmsg_dumper *dumper,
enum mtd_dump_reason reason)
{
static int do_dump_count = 0;
struct mtdoops_context *cxt = container_of(dumper,
struct mtdoops_context, dump);
size_t ret_len = 0;
char *pmsg_buffer_start = NULL;
struct pmsg_buffer_hdr *p_hdr = NULL;
int j, ret;
if(cxt->mtd == NULL) {
printk(KERN_ERR "mtdoops: init is not finish. Cannot write mtd logs \n");
return;
}
do_dump_count++;
printk(KERN_ERR "mtdoops: %s start , count = %d , page = %d, reason = %d, dump_count = %d\n",__func__,cxt->nextcount, cxt->nextpage,reason,do_dump_count);
if(do_dump_count>1)
{
for (j = 0, ret = -1; (j < 3) && (ret < 0); j++)
ret = mtdoops_erase_block(cxt, cxt->nextpage * record_size);
}
/* Only dump oopses if dump_oops is set */
if (reason == KMSG_DUMP_OOPS && !dump_oops)
return;
kmsg_dump_get_buffer(dumper, true, cxt->oops_buf + MTDOOPS_HEADER_SIZE,
record_size - MTDOOPS_HEADER_SIZE, NULL);
lkmsg_record_size - MTDOOPS_HEADER_SIZE, &ret_len);
if (reason != KMSG_DUMP_OOPS) {
/* Panics must be written immediately */
mtdoops_add_reason(cxt->oops_buf + MTDOOPS_HEADER_SIZE, reason, MTDOOPS_TYPE_DMESG, cxt->nextcount, cxt->nextpage);
pmsg_buffer_start = phys_to_virt((cxt->pmsg_data.mem_address + cxt->pmsg_data.mem_size) - cxt->pmsg_data.pmsg_size);
p_hdr = (struct pmsg_buffer_hdr *)pmsg_buffer_start;
pr_err("mtdoops: mtdoops_do_dump pmsg paddr = 0x%lx \n",pmsg_buffer_start);
if(p_hdr->sig == 0x43474244)
{
void *oopsbuf = cxt->oops_buf + (MTDOOPS_HEADER_SIZE + ret_len);
uint8_t *p_buff_end = p_hdr->data + p_hdr->size.counter;
int pmsg_cp_size = 0;
int pstart = p_hdr->start.counter;
int psize = p_hdr->size.counter;
pmsg_cp_size = (record_size - (ret_len + MTDOOPS_HEADER_SIZE));
if (psize <= pmsg_cp_size)
pmsg_cp_size = psize;
if (pstart >= pmsg_cp_size)
memcpy(oopsbuf, p_hdr->data, pmsg_cp_size);
else{
memcpy(oopsbuf, p_buff_end - (pmsg_cp_size - pstart), pmsg_cp_size - pstart);
memcpy(oopsbuf + (pmsg_cp_size - pstart), p_hdr->data, pstart);
}
mtdoops_add_pmsg_head(cxt->oops_buf + (MTDOOPS_HEADER_SIZE + ret_len), MTDOOPS_TYPE_PMSG);
}
else
printk(KERN_ERR "mtdoops: read pmsg failed sig = 0x%x \n", p_hdr->sig);
/* Panics must be written immediately */
if (reason == KMSG_DUMP_OOPS || reason == KMSG_DUMP_PANIC) {
mtdoops_write(cxt, 1);
} else {
/* For other cases, schedule work to write it "nicely" */
schedule_work(&cxt->work_write);
//schedule_work(&cxt->work_write);
// we should write log immediately , if use work to write, ufs will shutdown before write log finish
mtdoops_write(cxt, 0);
}
printk(KERN_ERR "mtdoops: mtdoops_do_dump() finish \n");
}
static void mtdoops_do_dump_kmsgdump(struct kmsg_dumper *dumper,
enum kmsg_dump_reason reason)
{
//mtdoops_do_dump(dumper, (enum mtd_dump_reason)reason);
}
void mtdoops_do_dump_if(int reason)
{
struct mtdoops_context *cxt = &oops_cxt;
cxt->dump.active = true;
kmsg_dump_rewind(&cxt->dump);
mtdoops_do_dump(&cxt->dump,(enum mtd_dump_reason)reason);
cxt->dump.active = false;
}
EXPORT_SYMBOL_GPL(mtdoops_do_dump_if);
static int mtdoops_reboot_nb_handle(struct notifier_block *this, unsigned long event,
void *ptr)
{
//char *cmd = ptr;
enum mtd_dump_reason reason;
if (event == SYS_RESTART)
reason = MTD_DUMP_RESTART;
else if(event == SYS_POWER_OFF)
reason = MTD_DUMP_POWEROFF;
else
return NOTIFY_OK;
mtdoops_do_dump_if(reason);
return NOTIFY_OK;
}
static void mtdoops_notify_add(struct mtd_info *mtd)
{
struct mtdoops_context *cxt = &oops_cxt;
@ -326,8 +538,9 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
return;
}
cxt->dump.max_reason = KMSG_DUMP_OOPS;
cxt->dump.dump = mtdoops_do_dump;
// for panic
cxt->dump.max_reason = KMSG_DUMP_MAX;
cxt->dump.dump = mtdoops_do_dump_kmsgdump;
err = kmsg_dump_register(&cxt->dump);
if (err) {
printk(KERN_ERR "mtdoops: registering kmsg dumper failed, error %d\n", err);
@ -336,6 +549,12 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
return;
}
// for restart and power off
cxt->reboot_nb.notifier_call = mtdoops_reboot_nb_handle;
cxt->reboot_nb.priority = 255;
register_reboot_notifier(&cxt->reboot_nb);
cxt->mtd = mtd;
cxt->oops_pages = (int)mtd->size / record_size;
find_next_position(cxt);
@ -352,6 +571,8 @@ static void mtdoops_notify_remove(struct mtd_info *mtd)
if (kmsg_dump_unregister(&cxt->dump) < 0)
printk(KERN_WARNING "mtdoops: could not unregister kmsg_dumper\n");
unregister_reboot_notifier(&cxt->reboot_nb);
cxt->mtd = NULL;
flush_work(&cxt->work_erase);
flush_work(&cxt->work_write);
@ -363,12 +584,94 @@ static struct mtd_notifier mtdoops_notifier = {
.remove = mtdoops_notify_remove,
};
static int mtdoops_parse_dt_u32(struct platform_device *pdev,
const char *propname,
u32 default_value, u32 *value)
{
u32 val32 = 0;
int ret;
ret = of_property_read_u32(pdev->dev.of_node, propname, &val32);
if (ret == -EINVAL) {
/* field is missing, use default value. */
val32 = default_value;
} else if (ret < 0) {
dev_err(&pdev->dev, "failed to parse property %s: %d\n",
propname, ret);
return ret;
}
/* Sanity check our results. */
if (val32 > INT_MAX) {
dev_err(&pdev->dev, "%s %u > INT_MAX\n", propname, val32);
return -EOVERFLOW;
}
*value = val32;
return 0;
}
static int mtdoops_pmsg_probe(struct platform_device *pdev)
{
struct mtdoops_context *cxt = &oops_cxt;
struct resource *res;
u32 value;
int ret;
dev_dbg(&pdev->dev, "using Device Tree\n");
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(&pdev->dev,
"failed to locate DT /reserved-memory resource\n");
return -EINVAL;
}
cxt->pmsg_data.mem_size = resource_size(res);
cxt->pmsg_data.mem_address = res->start;
#define parse_u32(name, field, default_value) { \
ret = mtdoops_parse_dt_u32(pdev, name, default_value, \
&value); \
if (ret < 0) \
return ret; \
field = value; \
}
parse_u32("console-size", cxt->pmsg_data.console_size, 0);
parse_u32("pmsg-size", cxt->pmsg_data.pmsg_size, 0);
#undef parse_u32
printk(KERN_ERR "mtdoops: pares mtd_dt, mem_address =0x%x, mem_size =0x%x \n", cxt->pmsg_data.mem_address, cxt->pmsg_data.mem_size);
printk(KERN_ERR "mtdoops: pares mtd_dt, pmsg_size =0x%x \n", cxt->pmsg_data.pmsg_size);
return 0;
}
static const struct of_device_id dt_match[] = {
{ .compatible = "mtdoops_pmsg" },
{}
};
static struct platform_driver mtdoops_pmsg_driver = {
.probe = mtdoops_pmsg_probe,
.driver = {
.name = "mtdoops_pmsg",
.of_match_table = dt_match,
},
};
static int __init mtdoops_init(void)
{
struct mtdoops_context *cxt = &oops_cxt;
int mtd_index;
char *endp;
printk(KERN_ERR "mtdoops: %s \n",__func__);
if (strlen(mtddev) == 0) {
printk(KERN_ERR "mtdoops: mtd device (mtddev=name/number) must be supplied\n");
return -EINVAL;
@ -388,7 +691,8 @@ static int __init mtdoops_init(void)
if (*endp == '\0')
cxt->mtd_index = mtd_index;
cxt->oops_buf = vmalloc(record_size);
//cxt->oops_buf = vmalloc(record_size);
cxt->oops_buf = kmalloc(record_size, GFP_KERNEL);
if (!cxt->oops_buf) {
printk(KERN_ERR "mtdoops: failed to allocate buffer workspace\n");
return -ENOMEM;
@ -396,8 +700,12 @@ static int __init mtdoops_init(void)
memset(cxt->oops_buf, 0xff, record_size);
INIT_WORK(&cxt->work_erase, mtdoops_workfunc_erase);
// we should write log immediately , if use work to write, ufs will shutdown before write log finish
INIT_WORK(&cxt->work_write, mtdoops_workfunc_write);
platform_driver_register(&mtdoops_pmsg_driver);
register_mtd_user(&mtdoops_notifier);
return 0;
}
@ -407,7 +715,7 @@ static void __exit mtdoops_exit(void)
struct mtdoops_context *cxt = &oops_cxt;
unregister_mtd_user(&mtdoops_notifier);
vfree(cxt->oops_buf);
kfree(cxt->oops_buf);
vfree(cxt->oops_page_used);
}