summaryrefslogtreecommitdiff
path: root/drivers/power/reset
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-08-21 14:07:25 +0100
committerMark Brown <broonie@kernel.org>2026-08-21 14:07:25 +0100
commitb5b59fef531867ccf61c426ad8761ed033b8f2c8 (patch)
treede95ff16fb53f49717b059a3099235bc71eae9fe /drivers/power/reset
parent5415235ff1aa2c5a2a3792aabcb6fb070537f3e6 (diff)
parent2da28b059e0ddcd2e1956eeae383246207965573 (diff)
downloadlinux-next-b5b59fef531867ccf61c426ad8761ed033b8f2c8.tar.gz
linux-next-b5b59fef531867ccf61c426ad8761ed033b8f2c8.zip
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power-supply.git
Diffstat (limited to 'drivers/power/reset')
-rw-r--r--drivers/power/reset/Kconfig12
-rw-r--r--drivers/power/reset/Makefile1
-rw-r--r--drivers/power/reset/mcf-rcm-reset.c126
-rw-r--r--drivers/power/reset/piix4-poweroff.c1
-rw-r--r--drivers/power/reset/pwr-mlxbf.c2
-rw-r--r--drivers/power/reset/reboot-mode.c30
-rw-r--r--drivers/power/reset/syscon-reboot.c4
7 files changed, 161 insertions, 15 deletions
diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig
index 124afb99febe..bce996bbef28 100644
--- a/drivers/power/reset/Kconfig
+++ b/drivers/power/reset/Kconfig
@@ -128,6 +128,18 @@ config POWER_RESET_LINKSTATION
Say Y here if you have a Buffalo LinkStation LS421D/E.
+config POWER_RESET_MCF_RCM
+ tristate "Freescale ColdFire RCM power-on reason driver"
+ depends on M5441x || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ This driver exposes the cause of the last reset on Freescale
+ ColdFire 5441x SoCs through the standard power_on_reason sysfs
+ ABI. It reads the Reset Status Register (RSR) of the Reset
+ Controller Module once at probe time and reports a normalised
+ string (regular power-up, reset button action, software reset
+ or unknown reason).
+
config POWER_RESET_MACSMC
tristate "Apple SMC reset/power-off driver"
depends on MFD_MACSMC
diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile
index d7ae97241a83..e31cab4ba78e 100644
--- a/drivers/power/reset/Makefile
+++ b/drivers/power/reset/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o
obj-$(CONFIG_POWER_RESET_GPIO_RESTART) += gpio-restart.o
obj-$(CONFIG_POWER_RESET_HISI) += hisi-reboot.o
obj-$(CONFIG_POWER_RESET_LINKSTATION) += linkstation-poweroff.o
+obj-$(CONFIG_POWER_RESET_MCF_RCM) += mcf-rcm-reset.o
obj-$(CONFIG_POWER_RESET_MACSMC) += macsmc-reboot.o
obj-$(CONFIG_POWER_RESET_MSM) += msm-poweroff.o
obj-$(CONFIG_POWER_RESET_MT6323) += mt6323-poweroff.o
diff --git a/drivers/power/reset/mcf-rcm-reset.c b/drivers/power/reset/mcf-rcm-reset.c
new file mode 100644
index 000000000000..18f11c05a868
--- /dev/null
+++ b/drivers/power/reset/mcf-rcm-reset.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Freescale ColdFire MCF5441x RCM power-on reason driver
+ *
+ * Copyright (C) 2026 Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>
+ */
+
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/power/power_on_reason.h>
+
+/*
+ * Reset Status Register (RSR) layout, MCF54418 Reference Manual chapter
+ * 12.3.2. The register is 8-bit read-only and latches the cause (or
+ * causes) of the most recent reset until the next one occurs.
+ */
+#define MCF_RSR_SOFT 0x20 /* Last reset caused by software */
+#define MCF_RSR_LOC 0x10 /* Last reset caused by PLL loss of clock */
+#define MCF_RSR_POR 0x08 /* Last reset caused by power-on */
+#define MCF_RSR_EXT 0x04 /* Last reset caused by external pin */
+#define MCF_RSR_WDRCORE 0x02 /* Last reset caused by core watchdog */
+#define MCF_RSR_LOL 0x01 /* Last reset caused by PLL loss of lock */
+
+#define MCF_RSR_KNOWN_CAUSES (MCF_RSR_POR | MCF_RSR_EXT | MCF_RSR_WDRCORE | \
+ MCF_RSR_LOC | MCF_RSR_LOL | MCF_RSR_SOFT)
+
+struct mcf_rcm {
+ const char *reason;
+};
+
+/*
+ * Decode RSR into a power_on_reason string.
+ *
+ * The MCF5441x Reset Status Register can latch several cause bits at the
+ * same time (Reference Manual chapter 12.3.2: "one or more status bits
+ * may be set at the same time"). A power-on, for example, also resets
+ * the PLL and may co-flag LOC and LOL during the boot sequence. The
+ * power_on_reason ABI carries a single string, so this routine picks
+ * one cause; the chosen priority surfaces the most explanatory one for
+ * diagnostics:
+ *
+ * POR cold boot dominates any spurious co-flagged cause
+ * EXT operator action via the RESET pin
+ * WDRCORE core watchdog timeout, a fault to investigate
+ * LOC, LOL PLL clock or lock failure, hardware fault
+ * SOFT explicit software-requested reset
+ */
+static const char *mcf_rcm_decode(u8 rsr)
+{
+ if (rsr & MCF_RSR_POR)
+ return POWER_ON_REASON_REGULAR;
+ if (rsr & MCF_RSR_EXT)
+ return POWER_ON_REASON_RST_BTN;
+ if (rsr & MCF_RSR_WDRCORE)
+ return POWER_ON_REASON_WATCHDOG;
+ if (rsr & (MCF_RSR_LOC | MCF_RSR_LOL))
+ return POWER_ON_REASON_CPU_CLK_FAIL;
+ if (rsr & MCF_RSR_SOFT)
+ return POWER_ON_REASON_SOFTWARE;
+ return POWER_ON_REASON_UNKNOWN;
+}
+
+static ssize_t power_on_reason_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct mcf_rcm *rcm = platform_get_drvdata(to_platform_device(dev));
+
+ return sysfs_emit(buf, "%s\n", rcm->reason);
+}
+static DEVICE_ATTR_RO(power_on_reason);
+
+static struct attribute *mcf_rcm_attrs[] = {
+ &dev_attr_power_on_reason.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(mcf_rcm);
+
+static int mcf_rcm_probe(struct platform_device *pdev)
+{
+ struct mcf_rcm *rcm;
+ void __iomem *base;
+ u8 rsr;
+
+ rcm = devm_kzalloc(&pdev->dev, sizeof(*rcm), GFP_KERNEL);
+ if (!rcm)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ rsr = readb_relaxed(base);
+ rcm->reason = mcf_rcm_decode(rsr);
+
+ platform_set_drvdata(pdev, rcm);
+
+ if (!(rsr & MCF_RSR_KNOWN_CAUSES))
+ dev_warn(&pdev->dev, "Unknown reset cause (RSR=0x%02x)\n", rsr);
+ else
+ dev_info(&pdev->dev, "Starting after %s (RSR=0x%02x)\n",
+ rcm->reason, rsr);
+
+ return 0;
+}
+
+static const struct platform_device_id mcf_rcm_id[] = {
+ { "mcf-rcm-reset" },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, mcf_rcm_id);
+
+static struct platform_driver mcf_rcm_driver = {
+ .probe = mcf_rcm_probe,
+ .id_table = mcf_rcm_id,
+ .driver = {
+ .name = "mcf-rcm-reset",
+ .dev_groups = mcf_rcm_groups,
+ },
+};
+module_platform_driver(mcf_rcm_driver);
+
+MODULE_AUTHOR("Jean-Michel Hautbois <jeanmichel.hautbois@yoseli.org>");
+MODULE_DESCRIPTION("Freescale ColdFire RCM power-on reason driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/power/reset/piix4-poweroff.c b/drivers/power/reset/piix4-poweroff.c
index e6822c021000..1a6f3d22d297 100644
--- a/drivers/power/reset/piix4-poweroff.c
+++ b/drivers/power/reset/piix4-poweroff.c
@@ -96,6 +96,7 @@ static const struct pci_device_id piix4_poweroff_ids[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3) },
{ 0 },
};
+MODULE_DEVICE_TABLE(pci, piix4_poweroff_ids);
static struct pci_driver piix4_poweroff_driver = {
.name = "piix4-poweroff",
diff --git a/drivers/power/reset/pwr-mlxbf.c b/drivers/power/reset/pwr-mlxbf.c
index 15d92c9f64dd..459c2b7f3a21 100644
--- a/drivers/power/reset/pwr-mlxbf.c
+++ b/drivers/power/reset/pwr-mlxbf.c
@@ -68,8 +68,6 @@ static int pwr_mlxbf_probe(struct platform_device *pdev)
return err;
err = devm_request_irq(dev, irq, pwr_mlxbf_irq, 0, hid, priv);
- if (err)
- dev_err(dev, "Failed request of %s irq\n", priv->hid);
return err;
}
diff --git a/drivers/power/reset/reboot-mode.c b/drivers/power/reset/reboot-mode.c
index d20e44db0532..af00c00eceee 100644
--- a/drivers/power/reset/reboot-mode.c
+++ b/drivers/power/reset/reboot-mode.c
@@ -10,6 +10,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/property.h>
#include <linux/reboot.h>
#include <linux/reboot-mode.h>
#include <linux/slab.h>
@@ -168,10 +169,11 @@ error:
*/
int reboot_mode_register(struct reboot_mode_driver *reboot)
{
- struct mode_info *info;
+ struct mode_info *info = NULL;
struct property *prop;
struct device_node *np = reboot->dev->of_node;
size_t len = strlen(PREFIX);
+ u32 magic;
int ret;
INIT_LIST_HEAD(&reboot->head);
@@ -180,22 +182,22 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
if (strncmp(prop->name, PREFIX, len))
continue;
- info = devm_kzalloc(reboot->dev, sizeof(*info), GFP_KERNEL);
+ if (device_property_read_u32(reboot->dev, prop->name, &magic)) {
+ dev_dbg(reboot->dev, "reboot mode %s without magic number\n",
+ prop->name);
+ continue;
+ }
+
+ info = kzalloc_obj(*info, GFP_KERNEL);
if (!info) {
ret = -ENOMEM;
goto error;
}
- if (of_property_read_u32(np, prop->name, &info->magic)) {
- dev_err(reboot->dev, "reboot mode %s without magic number\n",
- info->mode);
- devm_kfree(reboot->dev, info);
- continue;
- }
-
+ info->magic = magic;
info->mode = kstrdup_const(prop->name + len, GFP_KERNEL);
if (!info->mode) {
- ret = -ENOMEM;
+ ret = -ENOMEM;
goto error;
} else if (info->mode[0] == '\0') {
kfree_const(info->mode);
@@ -206,6 +208,7 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
}
list_add_tail(&info->list, &reboot->head);
+ info = NULL;
}
reboot->reboot_notifier.notifier_call = reboot_mode_notify;
@@ -218,6 +221,7 @@ int reboot_mode_register(struct reboot_mode_driver *reboot)
return 0;
error:
+ kfree(info);
reboot_mode_unregister(reboot);
return ret;
}
@@ -261,12 +265,16 @@ static inline void reboot_mode_unregister_device(struct reboot_mode_driver *rebo
int reboot_mode_unregister(struct reboot_mode_driver *reboot)
{
struct mode_info *info;
+ struct mode_info *next;
unregister_reboot_notifier(&reboot->reboot_notifier);
reboot_mode_unregister_device(reboot);
- list_for_each_entry(info, &reboot->head, list)
+ list_for_each_entry_safe(info, next, &reboot->head, list) {
+ list_del(&info->list);
kfree_const(info->mode);
+ kfree(info);
+ }
return 0;
}
diff --git a/drivers/power/reset/syscon-reboot.c b/drivers/power/reset/syscon-reboot.c
index 2e2cf5f62d73..60e855cae68e 100644
--- a/drivers/power/reset/syscon-reboot.c
+++ b/drivers/power/reset/syscon-reboot.c
@@ -67,7 +67,7 @@ static int syscon_reboot_probe(struct platform_device *pdev)
{
struct syscon_reboot_context *ctx;
struct device *dev = &pdev->dev;
- int priority;
+ u32 priority;
int err;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
@@ -81,7 +81,7 @@ static int syscon_reboot_probe(struct platform_device *pdev)
return PTR_ERR(ctx->map);
}
- if (of_property_read_s32(pdev->dev.of_node, "priority", &priority))
+ if (of_property_read_u32(pdev->dev.of_node, "priority", &priority))
priority = 192;
ctx->rd = of_device_get_match_data(dev);