156d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli/*
256d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli * RAM Oops/Panic logger
356d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli *
456d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli * Copyright (C) 2010 Marco Stornelli <marco.stornelli@gmail.com>
556d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli *
656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli * This program is free software; you can redistribute it and/or
756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli * modify it under the terms of the GNU General Public License
856d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli * version 2 as published by the Free Software Foundation.
956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli *
1056d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli * This program is distributed in the hope that it will be useful, but
1156d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli * WITHOUT ANY WARRANTY; without even the implied warranty of
1256d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
1356d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli * General Public License for more details.
1456d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli *
1556d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli * You should have received a copy of the GNU General Public License
1656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli * along with this program; if not, write to the Free Software
1756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
1856d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli * 02110-1301 USA
1956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli *
2056d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli */
2156d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
220169256e4bbf29e507cdd1df5812c093d610f1d5Marco Stornelli#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
230169256e4bbf29e507cdd1df5812c093d610f1d5Marco Stornelli
2456d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli#include <linux/kernel.h>
2583c1b31794a9e3cb30edabef7e57fbdbe129c5ceJames Bottomley#include <linux/err.h>
2656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli#include <linux/module.h>
2756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli#include <linux/kmsg_dump.h>
2856d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli#include <linux/time.h>
2956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli#include <linux/io.h>
3056d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli#include <linux/ioport.h>
31c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park#include <linux/platform_device.h>
3213aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli#include <linux/slab.h>
33c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park#include <linux/ramoops.h>
3456d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
3556d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli#define RAMOOPS_KERNMSG_HDR "===="
363e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache#define MIN_MEM_SIZE 4096UL
3756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
383e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordachestatic ulong record_size = MIN_MEM_SIZE;
393e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordachemodule_param(record_size, ulong, 0400);
403e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu IordacheMODULE_PARM_DESC(record_size,
413e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache		"size of each dump done on oops/panic");
4256d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
4356d611a04fb2db77334e06274de4daed92e2c626Marco Stornellistatic ulong mem_address;
4456d611a04fb2db77334e06274de4daed92e2c626Marco Stornellimodule_param(mem_address, ulong, 0400);
4556d611a04fb2db77334e06274de4daed92e2c626Marco StornelliMODULE_PARM_DESC(mem_address,
4656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli		"start of reserved RAM used to store oops/panic logs");
4756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
4856d611a04fb2db77334e06274de4daed92e2c626Marco Stornellistatic ulong mem_size;
4956d611a04fb2db77334e06274de4daed92e2c626Marco Stornellimodule_param(mem_size, ulong, 0400);
5056d611a04fb2db77334e06274de4daed92e2c626Marco StornelliMODULE_PARM_DESC(mem_size,
5156d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli		"size of reserved RAM used to store oops/panic logs");
5256d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
5356d611a04fb2db77334e06274de4daed92e2c626Marco Stornellistatic int dump_oops = 1;
5456d611a04fb2db77334e06274de4daed92e2c626Marco Stornellimodule_param(dump_oops, int, 0600);
5556d611a04fb2db77334e06274de4daed92e2c626Marco StornelliMODULE_PARM_DESC(dump_oops,
5656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli		"set to 1 to dump oopses, 0 to only dump panics (default 1)");
5756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
5856d611a04fb2db77334e06274de4daed92e2c626Marco Stornellistatic struct ramoops_context {
5956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	struct kmsg_dumper dump;
6056d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	void *virt_addr;
6156d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	phys_addr_t phys_addr;
6256d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	unsigned long size;
633e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache	unsigned long record_size;
646b4d2a2733b9a17112f746d498c9f9a0427dcdd8Sergiu Iordache	int dump_oops;
6556d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	int count;
6656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	int max_count;
6756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli} oops_cxt;
6856d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
6913aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornellistatic struct platform_device *dummy;
7013aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornellistatic struct ramoops_platform_data *dummy_data;
7113aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli
7256d611a04fb2db77334e06274de4daed92e2c626Marco Stornellistatic void ramoops_do_dump(struct kmsg_dumper *dumper,
7356d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli		enum kmsg_dump_reason reason, const char *s1, unsigned long l1,
7456d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli		const char *s2, unsigned long l2)
7556d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli{
7656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	struct ramoops_context *cxt = container_of(dumper,
7756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli			struct ramoops_context, dump);
7856d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	unsigned long s1_start, s2_start;
7956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	unsigned long l1_cpy, l2_cpy;
801873bb8115e678ad9fd0aac9dbbc68383bc36e06Ahmed S. Darwish	int res, hdr_size;
811873bb8115e678ad9fd0aac9dbbc68383bc36e06Ahmed S. Darwish	char *buf, *buf_orig;
8256d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	struct timeval timestamp;
8356d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
84fc2d557c74dc58294b9acc7231a2113ae59af97cSeiji Aguchi	if (reason != KMSG_DUMP_OOPS &&
85a3dd3323058d281abd584b15ad4c5b65064d7a61WANG Cong	    reason != KMSG_DUMP_PANIC)
86fc2d557c74dc58294b9acc7231a2113ae59af97cSeiji Aguchi		return;
87fc2d557c74dc58294b9acc7231a2113ae59af97cSeiji Aguchi
8856d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	/* Only dump oopses if dump_oops is set */
896b4d2a2733b9a17112f746d498c9f9a0427dcdd8Sergiu Iordache	if (reason == KMSG_DUMP_OOPS && !cxt->dump_oops)
9056d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli		return;
9156d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
923e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache	buf = cxt->virt_addr + (cxt->count * cxt->record_size);
931873bb8115e678ad9fd0aac9dbbc68383bc36e06Ahmed S. Darwish	buf_orig = buf;
941873bb8115e678ad9fd0aac9dbbc68383bc36e06Ahmed S. Darwish
953e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache	memset(buf, '\0', cxt->record_size);
9656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	res = sprintf(buf, "%s", RAMOOPS_KERNMSG_HDR);
9756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	buf += res;
9856d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	do_gettimeofday(&timestamp);
9956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	res = sprintf(buf, "%lu.%lu\n", (long)timestamp.tv_sec, (long)timestamp.tv_usec);
10056d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	buf += res;
10156d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
1021873bb8115e678ad9fd0aac9dbbc68383bc36e06Ahmed S. Darwish	hdr_size = buf - buf_orig;
1033e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache	l2_cpy = min(l2, cxt->record_size - hdr_size);
1043e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache	l1_cpy = min(l1, cxt->record_size - hdr_size - l2_cpy);
10556d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
10656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	s2_start = l2 - l2_cpy;
10756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	s1_start = l1 - l1_cpy;
10856d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
10956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	memcpy(buf, s1 + s1_start, l1_cpy);
11056d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	memcpy(buf + l1_cpy, s2 + s2_start, l2_cpy);
11156d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
11256d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	cxt->count = (cxt->count + 1) % cxt->max_count;
11356d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli}
11456d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
115c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Parkstatic int __init ramoops_probe(struct platform_device *pdev)
11656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli{
117c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park	struct ramoops_platform_data *pdata = pdev->dev.platform_data;
11856d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	struct ramoops_context *cxt = &oops_cxt;
11956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	int err = -EINVAL;
12056d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
1213e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache	if (!pdata->mem_size || !pdata->record_size) {
1223e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache		pr_err("The memory size and the record size must be "
1233e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache			"non-zero\n");
12456d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli		goto fail3;
12556d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	}
12656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
127fdb5950754eb3dedb9fea7c8828d3e51d9dbc3f7Marco Stornelli	pdata->mem_size = rounddown_pow_of_two(pdata->mem_size);
128fdb5950754eb3dedb9fea7c8828d3e51d9dbc3f7Marco Stornelli	pdata->record_size = rounddown_pow_of_two(pdata->record_size);
12956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
1303e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache	/* Check for the minimum memory size */
1313e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache	if (pdata->mem_size < MIN_MEM_SIZE &&
1323e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache			pdata->record_size < MIN_MEM_SIZE) {
1333e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache		pr_err("memory size too small, minium is %lu\n", MIN_MEM_SIZE);
13456d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli		goto fail3;
13556d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	}
13656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
1373e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache	if (pdata->mem_size < pdata->record_size) {
1383e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache		pr_err("The memory size must be larger than the "
1393e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache			"records size\n");
1403e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache		goto fail3;
1413e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache	}
1423e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache
1433e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache	cxt->max_count = pdata->mem_size / pdata->record_size;
14456d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	cxt->count = 0;
14513aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli	cxt->size = pdata->mem_size;
14613aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli	cxt->phys_addr = pdata->mem_address;
1473e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache	cxt->record_size = pdata->record_size;
1486b4d2a2733b9a17112f746d498c9f9a0427dcdd8Sergiu Iordache	cxt->dump_oops = pdata->dump_oops;
14956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
15056d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	if (!request_mem_region(cxt->phys_addr, cxt->size, "ramoops")) {
1510169256e4bbf29e507cdd1df5812c093d610f1d5Marco Stornelli		pr_err("request mem region failed\n");
15256d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli		err = -EINVAL;
15356d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli		goto fail3;
15456d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	}
15556d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
15656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	cxt->virt_addr = ioremap(cxt->phys_addr,  cxt->size);
15756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	if (!cxt->virt_addr) {
1580169256e4bbf29e507cdd1df5812c093d610f1d5Marco Stornelli		pr_err("ioremap failed\n");
15956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli		goto fail2;
16056d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	}
16156d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
16256d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	cxt->dump.dump = ramoops_do_dump;
16356d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	err = kmsg_dump_register(&cxt->dump);
16456d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	if (err) {
1650169256e4bbf29e507cdd1df5812c093d610f1d5Marco Stornelli		pr_err("registering kmsg dumper failed\n");
16656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli		goto fail1;
16756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	}
16856d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
169c755201eb5c1e09f3477d0e83ae1e3aadac0e8d1Kees Cook	/*
170c755201eb5c1e09f3477d0e83ae1e3aadac0e8d1Kees Cook	 * Update the module parameter variables as well so they are visible
171c755201eb5c1e09f3477d0e83ae1e3aadac0e8d1Kees Cook	 * through /sys/module/ramoops/parameters/
172c755201eb5c1e09f3477d0e83ae1e3aadac0e8d1Kees Cook	 */
173c755201eb5c1e09f3477d0e83ae1e3aadac0e8d1Kees Cook	mem_size = pdata->mem_size;
174c755201eb5c1e09f3477d0e83ae1e3aadac0e8d1Kees Cook	mem_address = pdata->mem_address;
175c755201eb5c1e09f3477d0e83ae1e3aadac0e8d1Kees Cook	record_size = pdata->record_size;
176c755201eb5c1e09f3477d0e83ae1e3aadac0e8d1Kees Cook	dump_oops = pdata->dump_oops;
177c755201eb5c1e09f3477d0e83ae1e3aadac0e8d1Kees Cook
17856d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	return 0;
17956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
18056d611a04fb2db77334e06274de4daed92e2c626Marco Stornellifail1:
18156d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	iounmap(cxt->virt_addr);
18256d611a04fb2db77334e06274de4daed92e2c626Marco Stornellifail2:
18356d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	release_mem_region(cxt->phys_addr, cxt->size);
18456d611a04fb2db77334e06274de4daed92e2c626Marco Stornellifail3:
18556d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	return err;
18656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli}
18756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
188c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Parkstatic int __exit ramoops_remove(struct platform_device *pdev)
18956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli{
19056d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	struct ramoops_context *cxt = &oops_cxt;
19156d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
19256d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	if (kmsg_dump_unregister(&cxt->dump) < 0)
1930169256e4bbf29e507cdd1df5812c093d610f1d5Marco Stornelli		pr_warn("could not unregister kmsg_dumper\n");
19456d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
19556d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	iounmap(cxt->virt_addr);
19656d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli	release_mem_region(cxt->phys_addr, cxt->size);
197c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park	return 0;
19856d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli}
19956d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
200c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Parkstatic struct platform_driver ramoops_driver = {
201c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park	.remove		= __exit_p(ramoops_remove),
202c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park	.driver		= {
203c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park		.name	= "ramoops",
204c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park		.owner	= THIS_MODULE,
205c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park	},
206c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park};
207c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park
208c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Parkstatic int __init ramoops_init(void)
209c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park{
21013aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli	int ret;
21113aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli	ret = platform_driver_probe(&ramoops_driver, ramoops_probe);
21213aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli	if (ret == -ENODEV) {
21313aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli		/*
21413aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli		 * If we didn't find a platform device, we use module parameters
21513aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli		 * building platform data on the fly.
21613aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli		 */
2170169256e4bbf29e507cdd1df5812c093d610f1d5Marco Stornelli		pr_info("platform device not found, using module parameters\n");
21813aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli		dummy_data = kzalloc(sizeof(struct ramoops_platform_data),
21913aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli				     GFP_KERNEL);
22013aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli		if (!dummy_data)
22113aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli			return -ENOMEM;
22213aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli		dummy_data->mem_size = mem_size;
22313aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli		dummy_data->mem_address = mem_address;
2243e5c4fadb9943c7539364d0c8425db071a2020e4Sergiu Iordache		dummy_data->record_size = record_size;
2256b4d2a2733b9a17112f746d498c9f9a0427dcdd8Sergiu Iordache		dummy_data->dump_oops = dump_oops;
22613aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli		dummy = platform_create_bundle(&ramoops_driver, ramoops_probe,
22713aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli			NULL, 0, dummy_data,
22813aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli			sizeof(struct ramoops_platform_data));
22913aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli
23013aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli		if (IS_ERR(dummy))
23113aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli			ret = PTR_ERR(dummy);
23213aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli		else
23313aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli			ret = 0;
23413aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli	}
23513aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli
23613aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli	return ret;
237c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park}
238c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park
239c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Parkstatic void __exit ramoops_exit(void)
240c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park{
241c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park	platform_driver_unregister(&ramoops_driver);
24213aefd7293e7a697bbf452fca65e69cc1fa8a31cMarco Stornelli	kfree(dummy_data);
243c3b92ce9e75f6353104fc7f8e32fb9fdb2550ad0Kyungmin Park}
24456d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
24556d611a04fb2db77334e06274de4daed92e2c626Marco Stornellimodule_init(ramoops_init);
24656d611a04fb2db77334e06274de4daed92e2c626Marco Stornellimodule_exit(ramoops_exit);
24756d611a04fb2db77334e06274de4daed92e2c626Marco Stornelli
24856d611a04fb2db77334e06274de4daed92e2c626Marco StornelliMODULE_LICENSE("GPL");
24956d611a04fb2db77334e06274de4daed92e2c626Marco StornelliMODULE_AUTHOR("Marco Stornelli <marco.stornelli@gmail.com>");
25056d611a04fb2db77334e06274de4daed92e2c626Marco StornelliMODULE_DESCRIPTION("RAM Oops/Panic logger/driver");
251