forked from torvalds/linux
Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
drivers, misc: add U-Boot bootcount driver
This driver implements the Linux kernel half of the boot count feature - the boot counter can only be reset after it is clear that the application has been started and is running correctly, which usually can only be determined by the application code itself. Thus the reset of the boot counter must be done by application code, which thus needs an appropriate driver. Required feature by the Carrier Grade Linux Requirements Definition; see for example document "Carrier Grade Linux Requirements Definition Overview V3.0" at http://www.linuxfoundation.org/collaborate/workgroups/cgl/requirements#SMM.6.0_Boot_Cycle_Detection Description: OSDL CGL specifies that carrier grade Linux shall provide support for detecting a repeating reboot cycle due to recurring failures. This detection should happen in user space before system services are started. This driver provides read/write access to the U-Boot bootcounter through sysFS file. Currently memory type only supported. The bootcountregister gets configured via DTS. for example on the enbw_cmc board: bootcount@0x23060 { compatible = "uboot,bootcount"; reg = <0x23060 0x1>; }; original post from: http://lkml.indiana.edu/hypermail/linux/kernel/1112.0/01142.html Change-Id: Icc050a10743fd7364f3c64546ca3c5a39512ca9f Signed-off-by: Heiko Schocher <hs@denx.de> Cc: Wolfgang Denk <wd@denx.de> Cc: Vitaly Bordug <vbordug@ru.mvista.com> Cc: devicetree-discuss@lists.ozlabs.org Cc: linux-kernel@vger.kernel.org Cc: Steffen Rumler <steffen.rumler@siemens.com> Cc: Matthias Kaehlcke <matthias@kaehlcke.net> Cc: Paul Bolle <pebolle@tiscali.nl> Cc: Wolfram Sang <w.sang@pengutronix.de> Cc: Ryan Mallon <rmallon@gmail.com> Signed-off-by: Heiko Schocher <hs@denx.de> Signed-off-by: Tom Rini <trini@ti.com> Signed-off-by: Eric Nelson <eric@nelint.com> Signed-off-by: Otavio Salvador <otavio@ossystems.com.br>
- Loading branch information
Showing
5 changed files
with
232 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| What: /sys/class/misc/uboot-bootcount/uboot-bootcount | ||
| Date: Dezember 2011 | ||
| Contact: Heiko Schocher <hs@denx.de> | ||
| Description: | ||
| This driver implements the Linux kernel half of the | ||
| U-Boot bootcount feature - the boot counter can only | ||
| be reset after it is clear that the application has | ||
| been started and is running correctly, which usually | ||
| can only be determined by the application code itself. | ||
| Thus the reset of the boot counter must be done by | ||
| application code, which thus needs an appropriate driver. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| U-Boot bootcount driver | ||
|
|
||
| This driver implements the Linux kernel half of the boot count feature - | ||
| the boot counter can only be reset after it is clear that the | ||
| application has been started and is running correctly, which usually | ||
| can only be determined by the application code itself. Thus the reset | ||
| of the boot counter must be done by application code, which thus needs | ||
| an appropriate driver. | ||
|
|
||
| The length property here determines if a single address is being used for | ||
| both the magic value and count, or if they are in adjacent locations. | ||
| Within U-Boot either is supported, so this information must be passed. | ||
|
|
||
| Required feature by the Carrier Grade Linux Requirements Definition; | ||
| see for example document "Carrier Grade Linux Requirements Definition | ||
| Overview V3.0" at | ||
|
|
||
| http://www.linuxfoundation.org/collaborate/workgroups/cgl/requirements#SMM.6.0_Boot_Cycle_Detection | ||
|
|
||
| Description: OSDL CGL specifies that carrier grade Linux | ||
| shall provide support for detecting a repeating reboot cycle | ||
| due to recurring failures. This detection should happen in | ||
| user space before system services are started. | ||
|
|
||
| This driver provides read/write access to the U-Boot bootcounter | ||
| through sysFS file. | ||
|
|
||
| Currently memory type only supported. | ||
|
|
||
| Required properties: | ||
|
|
||
| - compatible : should be "uboot,bootcount" | ||
| - reg: the address of the bootcounter | ||
|
|
||
| Example: | ||
|
|
||
| bootcount@1c23000 { | ||
| compatible = "uboot,bootcount"; | ||
| reg = <0x23060 0x2>; | ||
| }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,173 @@ | ||
| /* | ||
| * This driver gives access(read/write) to the bootcounter used by u-boot. | ||
| * Access is supported via sysfs. | ||
| * | ||
| * Copyright 2008 DENX Software Engineering GmbH | ||
| * Author: Heiko Schocher <hs@denx.de> | ||
| * Based on work from: Steffen Rumler <Steffen.Rumler@siemens.com> | ||
| * | ||
| * This program is free software; you can redistribute it and/or modify it | ||
| * under the terms of the GNU General Public License as published by the | ||
| * Free Software Foundation; either version 2 of the License, or (at your | ||
| * option) any later version. | ||
| */ | ||
|
|
||
| #include <linux/fs.h> | ||
| #include <linux/io.h> | ||
| #include <linux/miscdevice.h> | ||
| #include <linux/module.h> | ||
| #include <linux/of.h> | ||
| #include <linux/of_address.h> | ||
| #include <linux/of_platform.h> | ||
|
|
||
| #define UBOOT_BOOTCOUNT_NAME "bootcount" | ||
|
|
||
| #define UBOOT_BOOTCOUNT_MAGIC_OFFSET 0x04 /* offset of magic number */ | ||
| #define UBOOT_BOOTCOUNT_MAGIC 0xB001C041 /* magic number value */ | ||
| #define UBOOT_BOOTCOUNT_MAGIC_MASK 0xFFFF0000 /* magic, when combined */ | ||
| #define UBOOT_BOOTCOUNT_COUNT_MASK 0x0000FFFF /* value, when combined */ | ||
|
|
||
| static void __iomem *mem; | ||
| /* Default to magic number being adjacent. */ | ||
| static bool single_register = false; | ||
|
|
||
| /* Helper for the sysfs */ | ||
| static int show_str_bootcount(struct device *device, | ||
| struct device_attribute *attr, | ||
| char *buf) | ||
| { | ||
| unsigned long counter; | ||
|
|
||
| counter = be32_to_cpu(readl(mem)); | ||
|
|
||
| if (single_register) | ||
| counter &= UBOOT_BOOTCOUNT_COUNT_MASK; | ||
|
|
||
| return sprintf(buf, "%lu\n", counter); | ||
| } | ||
|
|
||
| static int store_str_bootcount(struct device *dev, | ||
| struct device_attribute *attr, | ||
| const char *buf, | ||
| size_t count) | ||
| { | ||
| int r; | ||
| u32 value; | ||
|
|
||
| r = kstrtou32(buf, 0, &value); | ||
| if (r < 0) | ||
| return -EINVAL; | ||
|
|
||
| if (single_register) | ||
| value = (UBOOT_BOOTCOUNT_MAGIC & UBOOT_BOOTCOUNT_MAGIC_MASK) | | ||
| (value & UBOOT_BOOTCOUNT_COUNT_MASK); | ||
|
|
||
| writel(cpu_to_be32(value), mem); | ||
|
|
||
| return count; | ||
| } | ||
|
|
||
| static DEVICE_ATTR(bootcount, S_IWUSR | S_IRUGO, show_str_bootcount, | ||
| store_str_bootcount); | ||
|
|
||
| static const struct file_operations bootcount_fops = { | ||
| .owner = THIS_MODULE, | ||
| }; | ||
|
|
||
| static struct miscdevice bootcount_miscdev = { | ||
| MISC_DYNAMIC_MINOR, | ||
| UBOOT_BOOTCOUNT_NAME, | ||
| &bootcount_fops | ||
| }; | ||
|
|
||
|
|
||
| static int bootcount_probe(struct platform_device *ofdev) | ||
| { | ||
| struct device_node *np = of_node_get(ofdev->dev.of_node); | ||
| unsigned long magic; | ||
|
|
||
| if (resource_size(ofdev->resource) == 1) | ||
| single_register = true; | ||
|
|
||
| mem = of_iomap(np, 0); | ||
| if (mem == NULL) { | ||
| dev_err(&ofdev->dev, "couldnt map register.\n"); | ||
| return -ENODEV; | ||
| } | ||
|
|
||
| if (single_register) { | ||
| magic = be32_to_cpu(readl(mem)); | ||
| if ((magic & UBOOT_BOOTCOUNT_MAGIC_MASK) != | ||
| (UBOOT_BOOTCOUNT_MAGIC & UBOOT_BOOTCOUNT_MAGIC_MASK)) { | ||
| dev_err(&ofdev->dev, "bad magic.\n"); | ||
| goto no_magic; | ||
| } | ||
| } else { | ||
| magic = be32_to_cpu(readl(mem + UBOOT_BOOTCOUNT_MAGIC_OFFSET)); | ||
| if (magic != UBOOT_BOOTCOUNT_MAGIC) { | ||
| dev_err(&ofdev->dev, "bad magic.\n"); | ||
| goto no_magic; | ||
| } | ||
| } | ||
|
|
||
| if (misc_register(&bootcount_miscdev)) { | ||
| dev_err(&ofdev->dev, "failed to register device\n"); | ||
| goto misc_register_fail; | ||
| } | ||
|
|
||
| if (device_create_file(bootcount_miscdev.this_device, | ||
| &dev_attr_bootcount)) { | ||
| dev_warn(&ofdev->dev, "couldn't register sysfs entry.\n"); | ||
| goto register_sysfs_fail; | ||
| } | ||
|
|
||
| return 0; | ||
| register_sysfs_fail: | ||
| misc_deregister(&bootcount_miscdev); | ||
| misc_register_fail: | ||
| no_magic: | ||
| iounmap(mem); | ||
| return -ENODEV; | ||
| } | ||
|
|
||
| static int bootcount_remove(struct platform_device *ofdev) | ||
| { | ||
| misc_deregister(&bootcount_miscdev); | ||
| iounmap(mem); | ||
| return 0; | ||
| } | ||
|
|
||
| static const struct of_device_id bootcount_match[] = { | ||
| { | ||
| .compatible = "uboot,bootcount", | ||
| }, | ||
| {}, | ||
| }; | ||
| MODULE_DEVICE_TABLE(of, bootcount_match); | ||
|
|
||
| static struct platform_driver bootcount_driver = { | ||
| .driver = { | ||
| .name = UBOOT_BOOTCOUNT_NAME, | ||
| .of_match_table = bootcount_match, | ||
| .owner = THIS_MODULE, | ||
| }, | ||
| .probe = bootcount_probe, | ||
| .remove = bootcount_remove, | ||
| }; | ||
|
|
||
| static int __init uboot_bootcount_init(void) | ||
| { | ||
| return platform_driver_register(&bootcount_driver); | ||
| } | ||
|
|
||
| static void __exit uboot_bootcount_cleanup(void) | ||
| { | ||
| platform_driver_unregister(&bootcount_driver); | ||
| } | ||
|
|
||
| late_initcall(uboot_bootcount_init); | ||
| module_exit(uboot_bootcount_cleanup); | ||
|
|
||
| MODULE_LICENSE("GPL"); | ||
| MODULE_AUTHOR("Steffen Rumler <steffen.rumler@siemens.com>"); | ||
| MODULE_DESCRIPTION("Provide (read/write) access to the U-Boot bootcounter via sysfs"); |