diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index a1ee475d180da..e7242f29781ba 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -37,6 +37,7 @@ config ARM_PSCI_CPUIDLE_DOMAIN depends on ARM_PSCI_CPUIDLE depends on PM_GENERIC_DOMAINS_OF select DT_IDLE_GENPD + select MFD_PSCI default y help Select this to enable the PSCI based CPUidle driver to use PM domains, diff --git a/drivers/cpuidle/cpuidle-psci-domain.c b/drivers/cpuidle/cpuidle-psci-domain.c index b9e4ad7d43a33..916318bc22665 100644 --- a/drivers/cpuidle/cpuidle-psci-domain.c +++ b/drivers/cpuidle/cpuidle-psci-domain.c @@ -129,7 +129,7 @@ static const struct of_device_id psci_of_match[] = { static int psci_cpuidle_domain_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; + struct device_node *np = pdev->dev.parent->of_node; bool use_osi = psci_has_osi_support(); int ret = 0, pd_count = 0; diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 7192c9d1d268e..733de4a55ba77 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -2355,6 +2355,15 @@ config MFD_ATC260X_I2C and ATC2609A chip variants, additional drivers must be enabled in order to use the functionality of the device. +config MFD_PSCI + bool "PSCI MFD for psci child cells" + depends on ARM_PSCI_FW + help + PSCI MFD registers PSCI child cells and exposes them as + platform devices. Child drivers are probed only if enabled in the + kernel configuration. Select this option whenever a supported PSCI + child driver is selected. + config MFD_KHADAS_MCU tristate "Support for Khadas System control Microcontroller" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e75e8045c28af..36e872b11b995 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -24,6 +24,8 @@ obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o obj-$(CONFIG_MFD_GATEWORKS_GSC) += gateworks-gsc.o obj-$(CONFIG_MFD_MACSMC) += macsmc.o +obj-$(CONFIG_MFD_PSCI) += psci-mfd.o + obj-$(CONFIG_MFD_TI_LP873X) += lp873x.o obj-$(CONFIG_MFD_TI_LP87565) += lp87565.o obj-$(CONFIG_MFD_TI_AM335X_TSCADC) += ti_am335x_tscadc.o diff --git a/drivers/mfd/psci-mfd.c b/drivers/mfd/psci-mfd.c new file mode 100644 index 0000000000000..c997498ee5d19 --- /dev/null +++ b/drivers/mfd/psci-mfd.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include + +static const struct mfd_cell psci_cells[] = { + { + .name = "psci-cpuidle-domain", + }, + { + .name = "psci-reboot-mode", + }, +}; + +static int psci_mfd_match_pdev_name(struct device *dev, const void *data) +{ + struct platform_device *pdev; + + if (dev->bus != &platform_bus_type) + return 0; + + pdev = to_platform_device(dev); + + return !strcmp(pdev->name, data); +} + +static void psci_mfd_bind_reboot_mode_node(struct platform_device *pdev) +{ + struct device_node *np; + struct device *child; + + if (!pdev->dev.of_node) + return; + + np = of_get_child_by_name(pdev->dev.of_node, "reboot-mode"); + if (!np) + return; + + child = device_find_child(&pdev->dev, "psci-reboot-mode", + psci_mfd_match_pdev_name); + if (!child) { + dev_dbg(&pdev->dev, "psci-reboot-mode child not found\n"); + of_node_put(np); + return; + } + + device_set_node(child, of_fwnode_handle(np)); + put_device(child); + of_node_put(np); +} + +static int psci_mfd_probe(struct platform_device *pdev) +{ + int ret; + + ret = devm_mfd_add_devices(&pdev->dev, PLATFORM_DEVID_AUTO, psci_cells, + ARRAY_SIZE(psci_cells), NULL, 0, NULL); + if (ret) + goto out; + + psci_mfd_bind_reboot_mode_node(pdev); + +out: + return ret; +} + +static const struct of_device_id psci_mfd_of_match[] = { + { .compatible = "arm,psci-1.0" }, + { } +}; +MODULE_DEVICE_TABLE(of, psci_mfd_of_match); + +static struct platform_driver psci_mfd_driver = { + .probe = psci_mfd_probe, + .driver = { + .name = "psci-mfd", + .of_match_table = psci_mfd_of_match, + }, +}; + +static int __init psci_mfd_init(void) +{ + return platform_driver_register(&psci_mfd_driver); +} + +core_initcall(psci_mfd_init); diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index f6c1bcbb57def..529d6c7d35556 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -348,6 +348,16 @@ config NVMEM_REBOOT_MODE then the bootloader can read it and take different action according to the mode. +config PSCI_REBOOT_MODE + bool "PSCI reboot mode driver" + depends on OF && ARM_PSCI_FW + select REBOOT_MODE + help + Say y here will enable PSCI reboot mode driver. This gets + the PSCI reboot mode arguments and passes them to psci + driver. psci driver uses these arguments for issuing + device reset into different boot states. + config POWER_MLXBF tristate "Mellanox BlueField power handling driver" depends on (GPIO_MLXBF2 || GPIO_MLXBF3) && ACPI diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index 0e4ae6f6b5c55..49774b42cdf61 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -40,4 +40,5 @@ 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_PSCI_REBOOT_MODE) += psci-reboot-mode.o obj-$(CONFIG_POWER_MLXBF) += pwr-mlxbf.o diff --git a/drivers/power/reset/psci-reboot-mode.c b/drivers/power/reset/psci-reboot-mode.c new file mode 100644 index 0000000000000..1c92db5d0f69a --- /dev/null +++ b/drivers/power/reset/psci-reboot-mode.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Predefined reboot-modes are defined as per the values + * of enum reboot_mode defined in the kernel: reboot.c. + */ +static struct mode_info psci_resets[] = { + { .mode = "warm", .magic = REBOOT_WARM}, + { .mode = "soft", .magic = REBOOT_SOFT}, + { .mode = "cold", .magic = REBOOT_COLD}, +}; + +static void psci_reboot_mode_set_predefined_modes(struct reboot_mode_driver *reboot) +{ + INIT_LIST_HEAD(&reboot->predefined_modes); + for (u32 i = 0; i < ARRAY_SIZE(psci_resets); i++) { + /* Prepare the magic with arg1 as 0 and arg2 as per pre-defined mode */ + psci_resets[i].magic = REBOOT_MODE_MAGIC(0, psci_resets[i].magic); + INIT_LIST_HEAD(&psci_resets[i].list); + list_add_tail(&psci_resets[i].list, &reboot->predefined_modes); + } +} + +/* + * arg1 is reset_type(Low 32 bit of magic). + * arg2 is cookie(High 32 bit of magic). + * If reset_type is 0, cookie will be used to decide the reset command. + */ +static int psci_reboot_mode_write(struct reboot_mode_driver *reboot, u64 magic) +{ + u32 reset_type = REBOOT_MODE_ARG1(magic); + u32 cookie = REBOOT_MODE_ARG2(magic); + + pr_err("DEBUG: PSCI write called"); + pr_err("DEBUG: PSCI write called"); + pr_err("DEBUG: PSCI write called"); + if (reset_type == 0) { + if (cookie == REBOOT_WARM || cookie == REBOOT_SOFT) + psci_set_reset_cmd(true, 0, 0); + else + psci_set_reset_cmd(false, 0, 0); + } else { + psci_set_reset_cmd(true, reset_type, cookie); + } + + return NOTIFY_DONE; +} + +static int psci_reboot_mode_probe(struct platform_device *pdev) +{ + struct reboot_mode_driver *reboot; + int ret; + + reboot = devm_kzalloc(&pdev->dev, sizeof(*reboot), GFP_KERNEL); + if (!reboot) + return -ENOMEM; + + psci_reboot_mode_set_predefined_modes(reboot); + reboot->write = psci_reboot_mode_write; + reboot->dev = &pdev->dev; + + ret = devm_reboot_mode_register(&pdev->dev, reboot); + if (ret) { + dev_err_probe(&pdev->dev, ret, "devm_reboot_mode_register failed %d\n", ret); + return ret; + } + + return 0; +} + +static struct platform_driver psci_reboot_mode_driver = { + .probe = psci_reboot_mode_probe, + .driver = { + .name = "psci-reboot-mode", + }, +}; + +module_platform_driver(psci_reboot_mode_driver);