diff options
Diffstat (limited to 'drivers/firmware/reboot_reason_sram.c')
-rw-r--r-- | drivers/firmware/reboot_reason_sram.c | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/drivers/firmware/reboot_reason_sram.c b/drivers/firmware/reboot_reason_sram.c new file mode 100644 index 000000000000..af87b6d34f77 --- /dev/null +++ b/drivers/firmware/reboot_reason_sram.c @@ -0,0 +1,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); |