summaryrefslogtreecommitdiff
path: root/drivers/firmware/reboot_reason_sram.c
blob: af87b6d34f770f69861af1c1dbc4ca52931c4fa5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
#include <linux/err.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/reboot.h>

/* Types of reasons */
enum {
	NONE,
	BOOTLOADER,
	RECOVERY,
	OEM,
	MAX_REASONS
};

static u32			reasons[MAX_REASONS];
static void __iomem		*reboot_reason_val_addr;
static struct notifier_block	reboot_nb;

static int reboot_reason(struct notifier_block *nb, unsigned long action,
								void *data)
{
	char *cmd = (char *)data;
	u32 reason = reasons[NONE];

	if (!reboot_reason_val_addr)
		return NOTIFY_DONE;

	if (cmd != NULL) {
		if (!strncmp(cmd, "bootloader", 10))
			reason = reasons[BOOTLOADER];
		else if (!strncmp(cmd, "recovery", 8))
			reason = reasons[RECOVERY];
		else if (!strncmp(cmd, "oem-", 4)) {
			unsigned long code;

			if (!kstrtoul(cmd+4, 0, &code))
				reason = reasons[OEM] | (code & 0xff);
		}
	}

	if (reason != -1)
		writel(reason, reboot_reason_val_addr);
	return NOTIFY_DONE;
}

static int reboot_reason_probe(struct platform_device *pdev)
{
	struct resource *res;
	u32 val;
	int i;

	/* initialize the reasons */
	for (i = 0; i < MAX_REASONS; i++)
		reasons[i] = -1;

	/* Try to grab the reason io address */
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	reboot_reason_val_addr = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(reboot_reason_val_addr))
		return PTR_ERR(reboot_reason_val_addr);

	/* initialize specified reasons from DT */
	if (!of_property_read_u32(pdev->dev.of_node, "reason,none", &val))
		reasons[NONE] = val;
	if (!of_property_read_u32(pdev->dev.of_node, "reason,bootloader", &val))
		reasons[BOOTLOADER] = val;
	if (!of_property_read_u32(pdev->dev.of_node, "reason,recovery", &val))
		reasons[RECOVERY] = val;
	if (!of_property_read_u32(pdev->dev.of_node, "reason,oem", &val))
		reasons[OEM] = val;

	/* Install the notifier */
	reboot_nb.notifier_call = reboot_reason;
	reboot_nb.priority = 256;
	if (register_reboot_notifier(&reboot_nb)) {
		dev_err(&pdev->dev,
			"failed to setup restart handler.\n");
	}
	return 0;
}

int reboot_reason_remove(struct platform_device *pdev)
{
	unregister_reboot_notifier(&reboot_nb);
	return 0;
}

static const struct of_device_id reboot_reason_of_match[] = {
	{ .compatible = "linux,reboot-reason-sram", },
	{ },
};
MODULE_DEVICE_TABLE(of, reboot_reason_of_match);

static struct platform_driver reboot_reason_driver = {
	.driver = {
		.name = "reboot-reason-sram",
		.of_match_table = reboot_reason_of_match,
	},
	.probe = reboot_reason_probe,
	.remove = reboot_reason_remove,
};
module_platform_driver(reboot_reason_driver);