diff options
| author | Mark Brown <broonie@kernel.org> | 2026-08-21 14:07:25 +0100 |
|---|---|---|
| committer | Mark Brown <broonie@kernel.org> | 2026-08-21 14:07:25 +0100 |
| commit | b5b59fef531867ccf61c426ad8761ed033b8f2c8 (patch) | |
| tree | de95ff16fb53f49717b059a3099235bc71eae9fe /drivers/power/reset | |
| parent | 5415235ff1aa2c5a2a3792aabcb6fb070537f3e6 (diff) | |
| parent | 2da28b059e0ddcd2e1956eeae383246207965573 (diff) | |
| download | linux-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/Kconfig | 12 | ||||
| -rw-r--r-- | drivers/power/reset/Makefile | 1 | ||||
| -rw-r--r-- | drivers/power/reset/mcf-rcm-reset.c | 126 | ||||
| -rw-r--r-- | drivers/power/reset/piix4-poweroff.c | 1 | ||||
| -rw-r--r-- | drivers/power/reset/pwr-mlxbf.c | 2 | ||||
| -rw-r--r-- | drivers/power/reset/reboot-mode.c | 30 | ||||
| -rw-r--r-- | drivers/power/reset/syscon-reboot.c | 4 |
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); |
