From: Freddy Hsin <freddy.hsin@xxxxxxxxxxxx> This driver parses the reboot commands like "reboot bootloader" and "reboot recovery" to get a boot mode described in the device tree , then call the write interfae to store the boot mode in mtk RGU (reset generation unit) non-volatile register, which can be read by the bootloader after system reboot, then the bootloader can take different action according to the mode stored. Signed-off-by: Freddy Hsin <freddy.hsin@xxxxxxxxxxxx> --- drivers/power/reset/Kconfig | 11 ++++ drivers/power/reset/Makefile | 1 + drivers/power/reset/mtk-reboot.c | 116 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 128 insertions(+) create mode 100644 drivers/power/reset/mtk-reboot.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index a564237..31fedb8 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -256,5 +256,16 @@ config NVMEM_REBOOT_MODE then the bootloader can read it and take different action according to the mode. +config MTK_REBOOT_MODE + tristate "Mediatek SoCs reset driver" + depends on OF + depends on REGMAP + select REBOOT_MODE + help + Say y here will enable reboot mode driver. This will + get reboot mode arguments and store it in RGU mapped + register, then the bootloader can read it to take different + action according to the mode. + endif diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 85da3198..2c64104 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -30,3 +30,4 @@ obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o obj-$(CONFIG_POWER_RESET_SC27XX) += sc27xx-poweroff.o obj-$(CONFIG_NVMEM_REBOOT_MODE) += nvmem-reboot-mode.o +obj-$(CONFIG_MTK_REBOOT_MODE) += mtk-reboot.o diff --git a/drivers/power/reset/mtk-reboot.c b/drivers/power/reset/mtk-reboot.c new file mode 100644 index 0000000..545c427 --- /dev/null +++ b/drivers/power/reset/mtk-reboot.c @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 MediaTek Inc. + * Author Freddy Hsin <freddy.hsin@xxxxxxxxxxxx> + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/reboot.h> +#include <linux/regmap.h> +#include <linux/of_address.h> +#include <linux/reboot-mode.h> + +static const struct regmap_config mtk_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; + +struct mtk_reboot_mode { + struct regmap *map; + struct reboot_mode_driver reboot; + u32 offset; + u32 mask; +}; + +static int mtk_reboot_mode_write(struct reboot_mode_driver *reboot, + unsigned int magic) +{ + struct mtk_reboot_mode *mtk_rbm; + int ret; + + mtk_rbm = container_of(reboot, struct mtk_reboot_mode, reboot); + + ret = regmap_update_bits(mtk_rbm->map, mtk_rbm->offset, + mtk_rbm->mask, magic); + if (ret < 0) + dev_info(reboot->dev, "update reboot mode bits failed\n"); + + return ret; +} + +static int mtk_regmap_lookup_by_phandle(struct device *dev, + struct mtk_reboot_mode *mtk_rbm) +{ + struct device_node *toprgu_np; + struct device_node *np = dev->of_node; + void __iomem *base; + + toprgu_np = of_parse_phandle(np, "regmap", 0); + + if (!of_device_is_compatible(toprgu_np, "mediatek,toprgu")) + return -EINVAL; + + base = of_iomap(toprgu_np, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + mtk_rbm->map = devm_regmap_init_mmio(dev, base, + &mtk_regmap_config); + return PTR_ERR_OR_ZERO(mtk_rbm->map); +} + +static int mtk_reboot_mode_probe(struct platform_device *pdev) +{ + int ret; + struct mtk_reboot_mode *mtk_rbm; + + mtk_rbm = devm_kzalloc(&pdev->dev, sizeof(*mtk_rbm), GFP_KERNEL); + if (!mtk_rbm) + return -ENOMEM; + + mtk_rbm->reboot.dev = &pdev->dev; + mtk_rbm->reboot.write = mtk_reboot_mode_write; + mtk_rbm->mask = 0xf; + + ret = mtk_regmap_lookup_by_phandle(&pdev->dev, mtk_rbm); + if (ret) { + dev_info(&pdev->dev, "Couldn't create the toprgu regmap\n"); + return -EINVAL; + } + + if (of_property_read_u32(pdev->dev.of_node, "offset", + &mtk_rbm->offset)) + return -EINVAL; + + of_property_read_u32(pdev->dev.of_node, "mask", &mtk_rbm->mask); + + ret = devm_reboot_mode_register(&pdev->dev, &mtk_rbm->reboot); + if (ret) + dev_info(&pdev->dev, "can't register reboot mode\n"); + + return ret; +} + +static const struct of_device_id mtk_reboot_mode_of_match[] = { + { .compatible = "toprgu-reboot-mode" }, + {} +}; +MODULE_DEVICE_TABLE(of, mtk_reboot_mode_of_match); + +static struct platform_driver mtk_reboot_mode_driver = { + .probe = mtk_reboot_mode_probe, + .driver = { + .name = "toprgu-reboot-mode", + .of_match_table = mtk_reboot_mode_of_match, + }, +}; +module_platform_driver(mtk_reboot_mode_driver); + +MODULE_AUTHOR("Freddy Hsin <freddy.hsin@xxxxxxxxxxxx>"); +MODULE_DESCRIPTION("Mediatek reboot mode driver"); +MODULE_LICENSE("GPL v2"); -- 1.7.9.5