diff options
Diffstat (limited to 'drivers/firmware')
103 files changed, 4850 insertions, 1138 deletions
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index f028218815c6..ba8d3d0ef32c 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -1,24 +1,11 @@ +# SPDX-License-Identifier: GPL-2.0-only # # For a description of the syntax of this configuration file, -# see Documentation/kbuild/kconfig-language.txt. +# see Documentation/kbuild/kconfig-language.rst. # menu "Firmware Drivers" -config ARM_PSCI_FW - bool - -config ARM_PSCI_CHECKER - bool "ARM PSCI checker" - depends on ARM_PSCI_FW && HOTPLUG_CPU && CPU_IDLE && !TORTURE_TEST - help - Run the PSCI checker during startup. This checks that hotplug and - suspend operations work correctly when using PSCI. - - The torture tests may interfere with the PSCI checker by turning CPUs - on and off through hotplug, so for now torture tests and PSCI checker - are mutually exclusive. - config ARM_SCMI_PROTOCOL bool "ARM System Control and Management Interface (SCMI) Message Protocol" depends on ARM || ARM64 || COMPILE_TEST @@ -219,7 +206,7 @@ config FW_CFG_SYSFS_CMDLINE config INTEL_STRATIX10_SERVICE tristate "Intel Stratix10 Service Layer" - depends on HAVE_ARM_SMCCC + depends on ARCH_STRATIX10 && HAVE_ARM_SMCCC default n help Intel Stratix10 service layer runs at privileged exception level, @@ -268,9 +255,26 @@ config TI_SCI_PROTOCOL This protocol library is used by client drivers to use the features provided by the system controller. +config TRUSTED_FOUNDATIONS + bool "Trusted Foundations secure monitor support" + depends on ARM && CPU_V7 + help + Some devices (including most early Tegra-based consumer devices on + the market) are booted with the Trusted Foundations secure monitor + active, requiring some core operations to be performed by the secure + monitor instead of the kernel. + + This option allows the kernel to invoke the secure monitor whenever + required on devices using Trusted Foundations. See the functions and + comments in linux/firmware/trusted_foundations.h or the device tree + bindings for "tlm,trusted-foundations" for details on how to use it. + + Choose N if you don't know what this is about. + config HAVE_ARM_SMCCC bool +source "drivers/firmware/psci/Kconfig" source "drivers/firmware/broadcom/Kconfig" source "drivers/firmware/google/Kconfig" source "drivers/firmware/efi/Kconfig" diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 80feb635120f..3fa0b34eb72f 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -2,8 +2,6 @@ # # Makefile for the linux kernel. # -obj-$(CONFIG_ARM_PSCI_FW) += psci.o -obj-$(CONFIG_ARM_PSCI_CHECKER) += psci_checker.o obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o obj-$(CONFIG_ARM_SCPI_POWER_DOMAIN) += scpi_pm_domain.o obj-$(CONFIG_ARM_SDE_INTERFACE) += arm_sdei.o @@ -23,8 +21,10 @@ obj-$(CONFIG_QCOM_SCM_64) += qcom_scm-64.o obj-$(CONFIG_QCOM_SCM_32) += qcom_scm-32.o CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension sec,-DREQUIRES_SEC=1) -march=armv7-a obj-$(CONFIG_TI_SCI_PROTOCOL) += ti_sci.o +obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o obj-$(CONFIG_ARM_SCMI_PROTOCOL) += arm_scmi/ +obj-y += psci/ obj-y += broadcom/ obj-y += meson/ obj-$(CONFIG_GOOGLE_FIRMWARE) += google/ diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile index 99e36c580fbc..c47d28d556b6 100644 --- a/drivers/firmware/arm_scmi/Makefile +++ b/drivers/firmware/arm_scmi/Makefile @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-y = scmi-bus.o scmi-driver.o scmi-protocols.o scmi-bus-y = bus.o scmi-driver-y = driver.o diff --git a/drivers/firmware/arm_scmi/clock.c b/drivers/firmware/arm_scmi/clock.c index 30fc04e28431..0a194af92438 100644 --- a/drivers/firmware/arm_scmi/clock.c +++ b/drivers/firmware/arm_scmi/clock.c @@ -185,6 +185,8 @@ scmi_clock_describe_rates_get(const struct scmi_handle *handle, u32 clk_id, if (rate_discrete) clk->list.num_rates = tot_rate_cnt; + clk->rate_discrete = rate_discrete; + err: scmi_xfer_put(handle, t); return ret; diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index 937a930ce87d..44fd4f9404a9 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: GPL-2.0 +/* SPDX-License-Identifier: GPL-2.0 */ /* * System Control and Management Interface (SCMI) Message Protocol * driver common header file containing some definitions, structures diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 8f952f2f1a29..b5bc4c7a8fab 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -654,9 +654,7 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo) static int scmi_mailbox_check(struct device_node *np) { - struct of_phandle_args arg; - - return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, &arg); + return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, NULL); } static int scmi_mbox_free_channel(int id, void *p, void *data) @@ -798,7 +796,9 @@ static int scmi_probe(struct platform_device *pdev) return -EINVAL; } - desc = of_match_device(scmi_of_match, dev)->data; + desc = of_device_get_match_data(dev); + if (!desc) + return -EINVAL; info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); if (!info) diff --git a/drivers/firmware/arm_scmi/sensors.c b/drivers/firmware/arm_scmi/sensors.c index b53d5cc9c9f6..0e94ab56f679 100644 --- a/drivers/firmware/arm_scmi/sensors.c +++ b/drivers/firmware/arm_scmi/sensors.c @@ -30,10 +30,12 @@ struct scmi_msg_resp_sensor_description { __le32 id; __le32 attributes_low; #define SUPPORTS_ASYNC_READ(x) ((x) & BIT(31)) -#define NUM_TRIP_POINTS(x) (((x) >> 4) & 0xff) +#define NUM_TRIP_POINTS(x) ((x) & 0xff) __le32 attributes_high; #define SENSOR_TYPE(x) ((x) & 0xff) -#define SENSOR_SCALE(x) (((x) >> 11) & 0x3f) +#define SENSOR_SCALE(x) (((x) >> 11) & 0x1f) +#define SENSOR_SCALE_SIGN BIT(4) +#define SENSOR_SCALE_EXTEND GENMASK(7, 5) #define SENSOR_UPDATE_SCALE(x) (((x) >> 22) & 0x1f) #define SENSOR_UPDATE_BASE(x) (((x) >> 27) & 0x1f) u8 name[SCMI_MAX_STR_SIZE]; @@ -140,6 +142,10 @@ static int scmi_sensor_description_get(const struct scmi_handle *handle, s = &si->sensors[desc_index + cnt]; s->id = le32_to_cpu(buf->desc[cnt].id); s->type = SENSOR_TYPE(attrh); + s->scale = SENSOR_SCALE(attrh); + /* Sign extend to a full s8 */ + if (s->scale & SENSOR_SCALE_SIGN) + s->scale |= SENSOR_SCALE_EXTEND; strlcpy(s->name, buf->desc[cnt].name, SCMI_MAX_STR_SIZE); } diff --git a/drivers/firmware/arm_scpi.c b/drivers/firmware/arm_scpi.c index c7d06a36b23a..725164b83242 100644 --- a/drivers/firmware/arm_scpi.c +++ b/drivers/firmware/arm_scpi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * System Control and Power Interface (SCPI) Message Protocol driver * @@ -11,18 +12,6 @@ * clocks configuration, thermal sensors and many others. * * Copyright (C) 2015 ARM Ltd. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c index c64c7da73829..9cd70d1a5622 100644 --- a/drivers/firmware/arm_sdei.c +++ b/drivers/firmware/arm_sdei.c @@ -2,6 +2,7 @@ // Copyright (C) 2017 Arm Ltd. #define pr_fmt(fmt) "sdei: " fmt +#include <acpi/ghes.h> #include <linux/acpi.h> #include <linux/arm_sdei.h> #include <linux/arm-smccc.h> @@ -164,6 +165,7 @@ static int invoke_sdei_fn(unsigned long function_id, unsigned long arg0, return err; } +NOKPROBE_SYMBOL(invoke_sdei_fn); static struct sdei_event *sdei_event_find(u32 event_num) { @@ -878,6 +880,7 @@ static void sdei_smccc_smc(unsigned long function_id, { arm_smccc_smc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res); } +NOKPROBE_SYMBOL(sdei_smccc_smc); static void sdei_smccc_hvc(unsigned long function_id, unsigned long arg0, unsigned long arg1, @@ -886,6 +889,74 @@ static void sdei_smccc_hvc(unsigned long function_id, { arm_smccc_hvc(function_id, arg0, arg1, arg2, arg3, arg4, 0, 0, res); } +NOKPROBE_SYMBOL(sdei_smccc_hvc); + +int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb, + sdei_event_callback *critical_cb) +{ + int err; + u64 result; + u32 event_num; + sdei_event_callback *cb; + + if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES)) + return -EOPNOTSUPP; + + event_num = ghes->generic->notify.vector; + if (event_num == 0) { + /* + * Event 0 is reserved by the specification for + * SDEI_EVENT_SIGNAL. + */ + return -EINVAL; + } + + err = sdei_api_event_get_info(event_num, SDEI_EVENT_INFO_EV_PRIORITY, + &result); + if (err) + return err; + + if (result == SDEI_EVENT_PRIORITY_CRITICAL) + cb = critical_cb; + else + cb = normal_cb; + + err = sdei_event_register(event_num, cb, ghes); + if (!err) + err = sdei_event_enable(event_num); + + return err; +} + +int sdei_unregister_ghes(struct ghes *ghes) +{ + int i; + int err; + u32 event_num = ghes->generic->notify.vector; + + might_sleep(); + + if (!IS_ENABLED(CONFIG_ACPI_APEI_GHES)) + return -EOPNOTSUPP; + + /* + * The event may be running on another CPU. Disable it + * to stop new events, then try to unregister a few times. + */ + err = sdei_event_disable(event_num); + if (err) + return err; + + for (i = 0; i < 3; i++) { + err = sdei_event_unregister(event_num); + if (err != -EINPROGRESS) + break; + + schedule(); + } + + return err; +} static int sdei_get_conduit(struct platform_device *pdev) { diff --git a/drivers/firmware/broadcom/Kconfig b/drivers/firmware/broadcom/Kconfig index f77cdb3a041f..64680824f984 100644 --- a/drivers/firmware/broadcom/Kconfig +++ b/drivers/firmware/broadcom/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config BCM47XX_NVRAM bool "Broadcom NVRAM driver" depends on BCM47XX || ARCH_BCM_5301X diff --git a/drivers/firmware/broadcom/Makefile b/drivers/firmware/broadcom/Makefile index f93efc479b8b..72c7fdc20c77 100644 --- a/drivers/firmware/broadcom/Makefile +++ b/drivers/firmware/broadcom/Makefile @@ -1,2 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_BCM47XX_NVRAM) += bcm47xx_nvram.o obj-$(CONFIG_BCM47XX_SPROM) += bcm47xx_sprom.o diff --git a/drivers/firmware/broadcom/bcm47xx_nvram.c b/drivers/firmware/broadcom/bcm47xx_nvram.c index d25f080fcb0d..77eb74666ecb 100644 --- a/drivers/firmware/broadcom/bcm47xx_nvram.c +++ b/drivers/firmware/broadcom/bcm47xx_nvram.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * BCM947xx nvram variable access * * Copyright (C) 2005 Broadcom Corporation * Copyright (C) 2006 Felix Fietkau <nbd@openwrt.org> * Copyright (C) 2010-2012 Hauke Mehrtens <hauke@hauke-m.de> - * - * 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/io.h> diff --git a/drivers/firmware/dmi-id.c b/drivers/firmware/dmi-id.c index 624a11cb07e2..ff39f64f2aae 100644 --- a/drivers/firmware/dmi-id.c +++ b/drivers/firmware/dmi-id.c @@ -1,9 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Export SMBIOS/DMI info via sysfs to userspace * * Copyright 2007, Lennart Poettering - * - * Licensed under GPLv2 */ #include <linux/module.h> diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index ecf2eeb5f6f9..b6180023eba7 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * dmi-sysfs.c * diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 099d83e4e910..35ed56b9c34f 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only #include <linux/types.h> #include <linux/string.h> #include <linux/init.h> @@ -416,11 +417,8 @@ static void __init save_mem_devices(const struct dmi_header *dm, void *v) nr++; } -void __init dmi_memdev_walk(void) +static void __init dmi_memdev_walk(void) { - if (!dmi_available) - return; - if (dmi_walk_early(count_mem_devices) == 0 && dmi_memdev_nr) { dmi_memdev = dmi_alloc(sizeof(*dmi_memdev) * dmi_memdev_nr); if (dmi_memdev) @@ -614,7 +612,7 @@ static int __init dmi_smbios3_present(const u8 *buf) return 1; } -void __init dmi_scan_machine(void) +static void __init dmi_scan_machine(void) { char __iomem *p, *q; char buf[32]; @@ -769,15 +767,20 @@ static int __init dmi_init(void) subsys_initcall(dmi_init); /** - * dmi_set_dump_stack_arch_desc - set arch description for dump_stack() + * dmi_setup - scan and setup DMI system information * - * Invoke dump_stack_set_arch_desc() with DMI system information so that - * DMI identifiers are printed out on task dumps. Arch boot code should - * call this function after dmi_scan_machine() if it wants to print out DMI - * identifiers on task dumps. + * Scan the DMI system information. This setups DMI identifiers + * (dmi_system_id) for printing it out on task dumps and prepares + * DIMM entry information (dmi_memdev_info) from the SMBIOS table + * for using this when reporting memory errors. */ -void __init dmi_set_dump_stack_arch_desc(void) +void __init dmi_setup(void) { + dmi_scan_machine(); + if (!dmi_available) + return; + + dmi_memdev_walk(); dump_stack_set_arch_desc("%s", dmi_ids_string); } @@ -841,7 +844,7 @@ static bool dmi_is_end_of_table(const struct dmi_system_id *dmi) * returns non zero or we hit the end. Callback function is called for * each successful match. Returns the number of matches. * - * dmi_scan_machine must be called before this function is called. + * dmi_setup must be called before this function is called. */ int dmi_check_system(const struct dmi_system_id *list) { @@ -871,7 +874,7 @@ EXPORT_SYMBOL(dmi_check_system); * Walk the blacklist table until the first match is found. Return the * pointer to the matching entry or NULL if there's no match. * - * dmi_scan_machine must be called before this function is called. + * dmi_setup must be called before this function is called. */ const struct dmi_system_id *dmi_first_match(const struct dmi_system_id *list) { diff --git a/drivers/firmware/edd.c b/drivers/firmware/edd.c index 1b82c89a49df..29906e39ab4b 100644 --- a/drivers/firmware/edd.c +++ b/drivers/firmware/edd.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * linux/drivers/firmware/edd.c * Copyright (C) 2002, 2003, 2004 Dell Inc. @@ -17,16 +18,6 @@ * * Please see http://linux.dell.com/edd/results.html for * the list of BIOSs which have been reported to implement EDD. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/module.h> diff --git a/drivers/firmware/efi/Kconfig b/drivers/firmware/efi/Kconfig index 89110dfc7127..d4ea929e8b34 100644 --- a/drivers/firmware/efi/Kconfig +++ b/drivers/firmware/efi/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only menu "EFI (Extensible Firmware Interface) Support" depends on EFI @@ -198,3 +199,9 @@ config EFI_DEV_PATH_PARSER bool depends on ACPI default n + +config EFI_EARLYCON + def_bool y + depends on SERIAL_EARLYCON && !ARM && !IA64 + select FONT_SUPPORT + select ARCH_USE_MEMREMAP_PROT diff --git a/drivers/firmware/efi/Makefile b/drivers/firmware/efi/Makefile index 5f9f5039de50..d2d0d2030620 100644 --- a/drivers/firmware/efi/Makefile +++ b/drivers/firmware/efi/Makefile @@ -30,5 +30,6 @@ arm-obj-$(CONFIG_EFI) := arm-init.o arm-runtime.o obj-$(CONFIG_ARM) += $(arm-obj-y) obj-$(CONFIG_ARM64) += $(arm-obj-y) obj-$(CONFIG_EFI_CAPSULE_LOADER) += capsule-loader.o +obj-$(CONFIG_EFI_EARLYCON) += earlycon.o obj-$(CONFIG_UEFI_CPER_ARM) += cper-arm.o obj-$(CONFIG_UEFI_CPER_X86) += cper-x86.o diff --git a/drivers/firmware/efi/apple-properties.c b/drivers/firmware/efi/apple-properties.c index ac1654f74dc7..0e206c9e0d7a 100644 --- a/drivers/firmware/efi/apple-properties.c +++ b/drivers/firmware/efi/apple-properties.c @@ -1,19 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * apple-properties.c - EFI device properties on Macs * Copyright (C) 2016 Lukas Wunner <lukas@wunner.de> * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License (version 2) as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - * * Note, all properties are considered as u8 arrays. * To get a value of any of them the caller must use device_property_read_u8_array(). */ diff --git a/drivers/firmware/efi/arm-init.c b/drivers/firmware/efi/arm-init.c index 1a6a77df8a5e..311cd349a862 100644 --- a/drivers/firmware/efi/arm-init.c +++ b/drivers/firmware/efi/arm-init.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Extensible Firmware Interface * * Based on Extensible Firmware Interface Specification version 2.4 * * Copyright (C) 2013 - 2015 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ #define pr_fmt(fmt) "efi: " fmt diff --git a/drivers/firmware/efi/arm-runtime.c b/drivers/firmware/efi/arm-runtime.c index 352bd2473162..e2ac5fa5531b 100644 --- a/drivers/firmware/efi/arm-runtime.c +++ b/drivers/firmware/efi/arm-runtime.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Extensible Firmware Interface * * Based on Extensible Firmware Interface Specification version 2.4 * * Copyright (C) 2013, 2014 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ #include <linux/dmi.h> @@ -46,10 +42,10 @@ static struct ptdump_info efi_ptdump_info = { static int __init ptdump_init(void) { - if (!efi_enabled(EFI_RUNTIME_SERVICES)) - return 0; + if (efi_enabled(EFI_RUNTIME_SERVICES)) + ptdump_debugfs_register(&efi_ptdump_info, "efi_page_tables"); - return ptdump_debugfs_register(&efi_ptdump_info, "efi_page_tables"); + return 0; } device_initcall(ptdump_init); @@ -166,13 +162,11 @@ void efi_virtmap_unload(void) static int __init arm_dmi_init(void) { /* - * On arm64/ARM, DMI depends on UEFI, and dmi_scan_machine() needs to + * On arm64/ARM, DMI depends on UEFI, and dmi_setup() needs to * be called early because dmi_id_init(), which is an arch_initcall * itself, depends on dmi_scan_machine() having been called already. */ - dmi_scan_machine(); - if (dmi_available) - dmi_set_dump_stack_arch_desc(); + dmi_setup(); return 0; } core_initcall(arm_dmi_init); diff --git a/drivers/firmware/efi/capsule-loader.c b/drivers/firmware/efi/capsule-loader.c index 96688986da56..b1395133389e 100644 --- a/drivers/firmware/efi/capsule-loader.c +++ b/drivers/firmware/efi/capsule-loader.c @@ -1,10 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * EFI capsule loader driver. * * Copyright 2015 Intel Corporation - * - * This file is part of the Linux kernel, and is made available under - * the terms of the GNU General Public License version 2. */ #define pr_fmt(fmt) "efi: " fmt diff --git a/drivers/firmware/efi/capsule.c b/drivers/firmware/efi/capsule.c index 4938c29b7c5d..598b7800d14e 100644 --- a/drivers/firmware/efi/capsule.c +++ b/drivers/firmware/efi/capsule.c @@ -1,10 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * EFI capsule support. * * Copyright 2013 Intel Corporation; author Matt Fleming - * - * This file is part of the Linux kernel, and is made available under - * the terms of the GNU General Public License version 2. */ #define pr_fmt(fmt) "efi: " fmt diff --git a/drivers/firmware/efi/cper-arm.c b/drivers/firmware/efi/cper-arm.c index 502811344e81..36d3b8b9da47 100644 --- a/drivers/firmware/efi/cper-arm.c +++ b/drivers/firmware/efi/cper-arm.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* * UEFI Common Platform Error Record (CPER) support * * Copyright (C) 2017, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/kernel.h> diff --git a/drivers/firmware/efi/cper.c b/drivers/firmware/efi/cper.c index a7902fccdcfa..8fa977c7861f 100644 --- a/drivers/firmware/efi/cper.c +++ b/drivers/firmware/efi/cper.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * UEFI Common Platform Error Record (CPER) support * @@ -9,19 +10,6 @@ * * For more information about CPER, please refer to Appendix N of UEFI * Specification version 2.4. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version - * 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/kernel.h> @@ -546,19 +534,24 @@ EXPORT_SYMBOL_GPL(cper_estatus_check_header); int cper_estatus_check(const struct acpi_hest_generic_status *estatus) { struct acpi_hest_generic_data *gdata; - unsigned int data_len, gedata_len; + unsigned int data_len, record_size; int rc; rc = cper_estatus_check_header(estatus); if (rc) return rc; + data_len = estatus->data_length; apei_estatus_for_each_section(estatus, gdata) { - gedata_len = acpi_hest_get_error_length(gdata); - if (gedata_len > data_len - acpi_hest_get_size(gdata)) + if (sizeof(struct acpi_hest_generic_data) > data_len) return -EINVAL; - data_len -= acpi_hest_get_record_size(gdata); + + record_size = acpi_hest_get_record_size(gdata); + if (record_size > data_len) + return -EINVAL; + + data_len -= record_size; } if (data_len) return -EINVAL; diff --git a/drivers/firmware/efi/dev-path-parser.c b/drivers/firmware/efi/dev-path-parser.c index 85d1834ee9b7..20123384271c 100644 --- a/drivers/firmware/efi/dev-path-parser.c +++ b/drivers/firmware/efi/dev-path-parser.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * dev-path-parser.c - EFI Device Path parser * Copyright (C) 2016 Lukas Wunner <lukas@wunner.de> @@ -5,14 +6,6 @@ * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License (version 2) as * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. */ #include <linux/acpi.h> @@ -24,9 +17,9 @@ struct acpi_hid_uid { char uid[11]; /* UINT_MAX + null byte */ }; -static int __init match_acpi_dev(struct device *dev, void *data) +static int __init match_acpi_dev(struct device *dev, const void *data) { - struct acpi_hid_uid hid_uid = *(struct acpi_hid_uid *)data; + struct acpi_hid_uid hid_uid = *(const struct acpi_hid_uid *)data; struct acpi_device *adev = to_acpi_device(dev); if (acpi_match_device_ids(adev, hid_uid.hid)) diff --git a/drivers/firmware/efi/earlycon.c b/drivers/firmware/efi/earlycon.c new file mode 100644 index 000000000000..c9a0efca17b0 --- /dev/null +++ b/drivers/firmware/efi/earlycon.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2013 Intel Corporation; author Matt Fleming + */ + +#include <linux/console.h> +#include <linux/efi.h> +#include <linux/font.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/serial_core.h> +#include <linux/screen_info.h> + +#include <asm/early_ioremap.h> + +static const struct font_desc *font; +static u32 efi_x, efi_y; +static u64 fb_base; +static pgprot_t fb_prot; + +static __ref void *efi_earlycon_map(unsigned long start, unsigned long len) +{ + return early_memremap_prot(fb_base + start, len, pgprot_val(fb_prot)); +} + +static __ref void efi_earlycon_unmap(void *addr, unsigned long len) +{ + early_memunmap(addr, len); +} + +static void efi_earlycon_clear_scanline(unsigned int y) +{ + unsigned long *dst; + u16 len; + + len = screen_info.lfb_linelength; + dst = efi_earlycon_map(y*len, len); + if (!dst) + return; + + memset(dst, 0, len); + efi_earlycon_unmap(dst, len); +} + +static void efi_earlycon_scroll_up(void) +{ + unsigned long *dst, *src; + u16 len; + u32 i, height; + + len = screen_info.lfb_linelength; + height = screen_info.lfb_height; + + for (i = 0; i < height - font->height; i++) { + dst = efi_earlycon_map(i*len, len); + if (!dst) + return; + + src = efi_earlycon_map((i + font->height) * len, len); + if (!src) { + efi_earlycon_unmap(dst, len); + return; + } + + memmove(dst, src, len); + + efi_earlycon_unmap(src, len); + efi_earlycon_unmap(dst, len); + } +} + +static void efi_earlycon_write_char(u32 *dst, unsigned char c, unsigned int h) +{ + const u32 color_black = 0x00000000; + const u32 color_white = 0x00ffffff; + const u8 *src; + u8 s8; + int m; + + src = font->data + c * font->height; + s8 = *(src + h); + + for (m = 0; m < 8; m++) { + if ((s8 >> (7 - m)) & 1) + *dst = color_white; + else + *dst = color_black; + dst++; + } +} + +static void +efi_earlycon_write(struct console *con, const char *str, unsigned int num) +{ + struct screen_info *si; + unsigned int len; + const char *s; + void *dst; + + si = &screen_info; + len = si->lfb_linelength; + + while (num) { + unsigned int linemax; + unsigned int h, count = 0; + + for (s = str; *s && *s != '\n'; s++) { + if (count == num) + break; + count++; + } + + linemax = (si->lfb_width - efi_x) / font->width; + if (count > linemax) + count = linemax; + + for (h = 0; h < font->height; h++) { + unsigned int n, x; + + dst = efi_earlycon_map((efi_y + h) * len, len); + if (!dst) + return; + + s = str; + n = count; + x = efi_x; + + while (n-- > 0) { + efi_earlycon_write_char(dst + x*4, *s, h); + x += font->width; + s++; + } + + efi_earlycon_unmap(dst, len); + } + + num -= count; + efi_x += count * font->width; + str += count; + + if (num > 0 && *s == '\n') { + efi_x = 0; + efi_y += font->height; + str++; + num--; + } + + if (efi_x + font->width > si->lfb_width) { + efi_x = 0; + efi_y += font->height; + } + + if (efi_y + font->height > si->lfb_height) { + u32 i; + + efi_y -= font->height; + efi_earlycon_scroll_up(); + + for (i = 0; i < font->height; i++) + efi_earlycon_clear_scanline(efi_y + i); + } + } +} + +static int __init efi_earlycon_setup(struct earlycon_device *device, + const char *opt) +{ + struct screen_info *si; + u16 xres, yres; + u32 i; + + if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI) + return -ENODEV; + + fb_base = screen_info.lfb_base; + if (screen_info.capabilities & VIDEO_CAPABILITY_64BIT_BASE) + fb_base |= (u64)screen_info.ext_lfb_base << 32; + + if (opt && !strcmp(opt, "ram")) + fb_prot = PAGE_KERNEL; + else + fb_prot = pgprot_writecombine(PAGE_KERNEL); + + si = &screen_info; + xres = si->lfb_width; + yres = si->lfb_height; + + /* + * efi_earlycon_write_char() implicitly assumes a framebuffer with + * 32 bits per pixel. + */ + if (si->lfb_depth != 32) + return -ENODEV; + + font = get_default_font(xres, yres, -1, -1); + if (!font) + return -ENODEV; + + efi_y = rounddown(yres, font->height) - font->height; + for (i = 0; i < (yres - efi_y) / font->height; i++) + efi_earlycon_scroll_up(); + + device->con->write = efi_earlycon_write; + return 0; +} +EARLYCON_DECLARE(efifb, efi_earlycon_setup); diff --git a/drivers/firmware/efi/efi-bgrt.c b/drivers/firmware/efi/efi-bgrt.c index b22ccfb0c991..b07c17643210 100644 --- a/drivers/firmware/efi/efi-bgrt.c +++ b/drivers/firmware/efi/efi-bgrt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright 2012 Intel Corporation * Author: Josh Triplett <josh@joshtriplett.org> @@ -5,10 +6,6 @@ * Based on the bgrt driver: * Copyright 2012 Red Hat, Inc <mjg@redhat.com> * Author: Matthew Garrett - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -50,11 +47,6 @@ void __init efi_bgrt_init(struct acpi_table_header *table) bgrt->version); goto out; } - if (bgrt->status & 0xfe) { - pr_notice("Ignoring BGRT: reserved status bits are non-zero %u\n", - bgrt->status); - goto out; - } if (bgrt->image_type != 0) { pr_notice("Ignoring BGRT: invalid image type %u (expected 0)\n", bgrt->image_type); diff --git a/drivers/firmware/efi/efi-pstore.c b/drivers/firmware/efi/efi-pstore.c index 0f7d97917197..9ea13e8d12ec 100644 --- a/drivers/firmware/efi/efi-pstore.c +++ b/drivers/firmware/efi/efi-pstore.c @@ -1,3 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0+ + #include <linux/efi.h> #include <linux/module.h> #include <linux/pstore.h> diff --git a/drivers/firmware/efi/efi.c b/drivers/firmware/efi/efi.c index 4c46ff6f2242..ad3b1f4866b3 100644 --- a/drivers/firmware/efi/efi.c +++ b/drivers/firmware/efi/efi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * efi.c - EFI subsystem * @@ -9,8 +10,6 @@ * allowing the efivarfs to be mounted or the efivars module to be loaded. * The existance of /sys/firmware/efi may also be used by userspace to * determine that the system supports EFI. - * - * This file is released under the GPLv2. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -53,6 +52,7 @@ struct efi __read_mostly efi = { .mem_attr_table = EFI_INVALID_TABLE_ADDR, .rng_seed = EFI_INVALID_TABLE_ADDR, .tpm_log = EFI_INVALID_TABLE_ADDR, + .tpm_final_log = EFI_INVALID_TABLE_ADDR, .mem_reserve = EFI_INVALID_TABLE_ADDR, }; EXPORT_SYMBOL(efi); @@ -485,6 +485,7 @@ static __initdata efi_config_table_type_t common_tables[] = { {EFI_MEMORY_ATTRIBUTES_TABLE_GUID, "MEMATTR", &efi.mem_attr_table}, {LINUX_EFI_RANDOM_SEED_TABLE_GUID, "RNG", &efi.rng_seed}, {LINUX_EFI_TPM_EVENT_LOG_GUID, "TPMEventLog", &efi.tpm_log}, + {LINUX_EFI_TPM_FINAL_LOG_GUID, "TPMFinalLog", &efi.tpm_final_log}, {LINUX_EFI_MEMRESERVE_TABLE_GUID, "MEMRESERVE", &efi.mem_reserve}, {NULL_GUID, NULL, NULL}, }; @@ -592,11 +593,7 @@ int __init efi_config_parse_tables(void *config_tables, int count, int sz, early_memunmap(tbl, sizeof(*tbl)); } - return 0; -} -int __init efi_apply_persistent_mem_reservations(void) -{ if (efi.mem_reserve != EFI_INVALID_TABLE_ADDR) { unsigned long prsv = efi.mem_reserve; @@ -640,6 +637,9 @@ int __init efi_config_init(efi_config_table_type_t *arch_tables) void *config_tables; int sz, ret; + if (efi.systab->nr_tables == 0) + return 0; + if (efi_enabled(EFI_64BIT)) sz = sizeof(efi_config_table_64_t); else @@ -1011,14 +1011,16 @@ int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size) /* first try to find a slot in an existing linked list entry */ for (prsv = efi_memreserve_root->next; prsv; prsv = rsv->next) { - rsv = __va(prsv); + rsv = memremap(prsv, sizeof(*rsv), MEMREMAP_WB); index = atomic_fetch_add_unless(&rsv->count, 1, rsv->size); if (index < rsv->size) { rsv->entry[index].base = addr; rsv->entry[index].size = size; + memunmap(rsv); return 0; } + memunmap(rsv); } /* no slot found - allocate a new linked list entry */ @@ -1026,7 +1028,13 @@ int __ref efi_mem_reserve_persistent(phys_addr_t addr, u64 size) if (!rsv) return -ENOMEM; - rsv->size = EFI_MEMRESERVE_COUNT(PAGE_SIZE); + /* + * The memremap() call above assumes that a linux_efi_memreserve entry + * never crosses a page boundary, so let's ensure that this remains true + * even when kexec'ing a 4k pages kernel from a >4k pages kernel, by + * using SZ_4K explicitly in the size calculation below. + */ + rsv->size = EFI_MEMRESERVE_COUNT(SZ_4K); atomic_set(&rsv->count, 1); rsv->entry[0].base = addr; rsv->entry[0].size = size; diff --git a/drivers/firmware/efi/efibc.c b/drivers/firmware/efi/efibc.c index 503bbe2a9d49..35dccc88ac0a 100644 --- a/drivers/firmware/efi/efibc.c +++ b/drivers/firmware/efi/efibc.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * efibc: control EFI bootloaders which obey LoaderEntryOneShot var * Copyright (c) 2013-2016, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. */ #define pr_fmt(fmt) "efibc: " fmt @@ -51,11 +43,13 @@ static int efibc_set_variable(const char *name, const char *value) efibc_str_to_str16(value, (efi_char16_t *)entry->var.Data); memcpy(&entry->var.VendorGuid, &guid, sizeof(guid)); - ret = efivar_entry_set(entry, - EFI_VARIABLE_NON_VOLATILE - | EFI_VARIABLE_BOOTSERVICE_ACCESS - | EFI_VARIABLE_RUNTIME_ACCESS, - size, entry->var.Data, NULL); + ret = efivar_entry_set_safe(entry->var.VariableName, + entry->var.VendorGuid, + EFI_VARIABLE_NON_VOLATILE + | EFI_VARIABLE_BOOTSERVICE_ACCESS + | EFI_VARIABLE_RUNTIME_ACCESS, + false, size, entry->var.Data); + if (ret) pr_err("failed to set %s EFI variable: 0x%x\n", name, ret); diff --git a/drivers/firmware/efi/efivars.c b/drivers/firmware/efi/efivars.c index 8061667a6765..7576450c8254 100644 --- a/drivers/firmware/efi/efivars.c +++ b/drivers/firmware/efi/efivars.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Originally from efivars.c, * @@ -6,63 +7,6 @@ * * This code takes all variables accessible from EFI runtime and * exports them via sysfs - * - * 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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * Changelog: - * - * 17 May 2004 - Matt Domsch <Matt_Domsch@dell.com> - * remove check for efi_enabled in exit - * add MODULE_VERSION - * - * 26 Apr 2004 - Matt Domsch <Matt_Domsch@dell.com> - * minor bug fixes - * - * 21 Apr 2004 - Matt Tolentino <matthew.e.tolentino@intel.com) - * converted driver to export variable information via sysfs - * and moved to drivers/firmware directory - * bumped revision number to v0.07 to reflect conversion & move - * - * 10 Dec 2002 - Matt Domsch <Matt_Domsch@dell.com> - * fix locking per Peter Chubb's findings - * - * 25 Mar 2002 - Matt Domsch <Matt_Domsch@dell.com> - * move uuid_unparse() to include/asm-ia64/efi.h:efi_guid_to_str() - * - * 12 Feb 2002 - Matt Domsch <Matt_Domsch@dell.com> - * use list_for_each_safe when deleting vars. - * remove ifdef CONFIG_SMP around include <linux/smp.h> - * v0.04 release to linux-ia64@linuxia64.org - * - * 20 April 2001 - Matt Domsch <Matt_Domsch@dell.com> - * Moved vars from /proc/efi to /proc/efi/vars, and made - * efi.c own the /proc/efi directory. - * v0.03 release to linux-ia64@linuxia64.org - * - * 26 March 2001 - Matt Domsch <Matt_Domsch@dell.com> - * At the request of Stephane, moved ownership of /proc/efi - * to efi.c, and now efivars lives under /proc/efi/vars. - * - * 12 March 2001 - Matt Domsch <Matt_Domsch@dell.com> - * Feedback received from Stephane Eranian incorporated. - * efivar_write() checks copy_from_user() return value. - * efivar_read/write() returns proper errno. - * v0.02 release to linux-ia64@linuxia64.org - * - * 26 February 2001 - Matt Domsch <Matt_Domsch@dell.com> - * v0.01 release to linux-ia64@linuxia64.org */ #include <linux/efi.h> diff --git a/drivers/firmware/efi/esrt.c b/drivers/firmware/efi/esrt.c index 5d06bd247d07..d6dd5f503fa2 100644 --- a/drivers/firmware/efi/esrt.c +++ b/drivers/firmware/efi/esrt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * esrt.c * diff --git a/drivers/firmware/efi/fake_mem.c b/drivers/firmware/efi/fake_mem.c index 6c7d60c239b5..9501edc0fcfb 100644 --- a/drivers/firmware/efi/fake_mem.c +++ b/drivers/firmware/efi/fake_mem.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * fake_mem.c * @@ -8,21 +9,6 @@ * By specifying this parameter, you can add arbitrary attribute to * specific memory range by updating original (firmware provided) EFI * memmap. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along with - * this program; if not, see <http://www.gnu.org/licenses/>. - * - * The full GNU General Public License is included in this distribution in - * the file called "COPYING". */ #include <linux/kernel.h> diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index d9845099635e..0460c7581220 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -16,9 +16,9 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -O2 \ # arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly # disable the stackleak plugin -cflags-$(CONFIG_ARM64) := $(subst -pg,,$(KBUILD_CFLAGS)) -fpie \ - $(DISABLE_STACKLEAK_PLUGIN) -cflags-$(CONFIG_ARM) := $(subst -pg,,$(KBUILD_CFLAGS)) \ +cflags-$(CONFIG_ARM64) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ + -fpie $(DISABLE_STACKLEAK_PLUGIN) +cflags-$(CONFIG_ARM) := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) \ -fno-builtin -fpic \ $(call cc-option,-mno-single-pic-base) @@ -52,7 +52,7 @@ lib-$(CONFIG_EFI_ARMSTUB) += arm-stub.o fdt.o string.o random.o \ lib-$(CONFIG_ARM) += arm32-stub.o lib-$(CONFIG_ARM64) += arm64-stub.o -CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) +CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) # # arm64 puts the stub in the kernel proper, which will unnecessarily retain all @@ -71,7 +71,6 @@ CFLAGS_arm64-stub.o := -DTEXT_OFFSET=$(TEXT_OFFSET) extra-$(CONFIG_EFI_ARMSTUB) := $(lib-y) lib-$(CONFIG_EFI_ARMSTUB) := $(patsubst %.o,%.stub.o,$(lib-y)) -STUBCOPY_RM-y := -R *ksymtab* -R *kcrctab* STUBCOPY_FLAGS-$(CONFIG_ARM64) += --prefix-alloc-sections=.init \ --prefix-symbols=__efistub_ STUBCOPY_RELOC-$(CONFIG_ARM64) := R_AARCH64_ABS @@ -86,12 +85,13 @@ $(obj)/%.stub.o: $(obj)/%.o FORCE # this time, use objcopy and leave all sections in place. # quiet_cmd_stubcopy = STUBCPY $@ - cmd_stubcopy = if $(STRIP) --strip-debug $(STUBCOPY_RM-y) -o $@ $<; \ - then if $(OBJDUMP) -r $@ | grep $(STUBCOPY_RELOC-y); \ - then (echo >&2 "$@: absolute symbol references not allowed in the EFI stub"; \ - rm -f $@; /bin/false); \ - else $(OBJCOPY) $(STUBCOPY_FLAGS-y) $< $@; fi \ - else /bin/false; fi + cmd_stubcopy = \ + $(STRIP) --strip-debug -o $@ $<; \ + if $(OBJDUMP) -r $@ | grep $(STUBCOPY_RELOC-y); then \ + echo "$@: absolute symbol references not allowed in the EFI stub" >&2; \ + /bin/false; \ + fi; \ + $(OBJCOPY) $(STUBCOPY_FLAGS-y) $< $@ # # ARM discards the .data section because it disallows r/w data in the diff --git a/drivers/firmware/efi/libstub/arm-stub.c b/drivers/firmware/efi/libstub/arm-stub.c index eee42d5e25ee..c382a48c6678 100644 --- a/drivers/firmware/efi/libstub/arm-stub.c +++ b/drivers/firmware/efi/libstub/arm-stub.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * EFI stub implementation that is shared by arm and arm64 architectures. * This should be #included by the EFI stub implementation files. @@ -6,10 +7,6 @@ * Roy Franz <roy.franz@linaro.org * Copyright (C) 2013 Red Hat, Inc. * Mark Salter <msalter@redhat.com> - * - * This file is part of the Linux kernel, and is made available under the - * terms of the GNU General Public License version 2. - * */ #include <linux/efi.h> @@ -75,9 +72,6 @@ void install_memreserve_table(efi_system_table_t *sys_table_arg) efi_guid_t memreserve_table_guid = LINUX_EFI_MEMRESERVE_TABLE_GUID; efi_status_t status; - if (IS_ENABLED(CONFIG_ARM)) - return; - status = efi_call_early(allocate_pool, EFI_LOADER_DATA, sizeof(*rsv), (void **)&rsv); if (status != EFI_SUCCESS) { @@ -370,6 +364,11 @@ void efi_get_virtmap(efi_memory_desc_t *memory_map, unsigned long map_size, paddr = in->phys_addr; size = in->num_pages * EFI_PAGE_SIZE; + if (novamap()) { + in->virt_addr = in->phys_addr; + continue; + } + /* * Make the mapping compatible with 64k pages: this allows * a 4k page size kernel to kexec a 64k page size kernel and diff --git a/drivers/firmware/efi/libstub/arm32-stub.c b/drivers/firmware/efi/libstub/arm32-stub.c index becbda445913..e8f7aefb6813 100644 --- a/drivers/firmware/efi/libstub/arm32-stub.c +++ b/drivers/firmware/efi/libstub/arm32-stub.c @@ -1,10 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2013 Linaro Ltd; <roy.franz@linaro.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ #include <linux/efi.h> #include <asm/efi.h> diff --git a/drivers/firmware/efi/libstub/arm64-stub.c b/drivers/firmware/efi/libstub/arm64-stub.c index 1b4d465cc5d9..1550d244e996 100644 --- a/drivers/firmware/efi/libstub/arm64-stub.c +++ b/drivers/firmware/efi/libstub/arm64-stub.c @@ -1,13 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2013, 2014 Linaro Ltd; <roy.franz@linaro.org> * * This file implements the EFI boot stub for the arm64 kernel. * Adapted from ARM version by Mark Salter <msalter@redhat.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ /* diff --git a/drivers/firmware/efi/libstub/efi-stub-helper.c b/drivers/firmware/efi/libstub/efi-stub-helper.c index e94975f4655b..1db780c0f07b 100644 --- a/drivers/firmware/efi/libstub/efi-stub-helper.c +++ b/drivers/firmware/efi/libstub/efi-stub-helper.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Helper functions used by the EFI stub on multiple * architectures. This should be #included by the EFI stub * implementation files. * * Copyright 2011 Intel Corporation; author Matt Fleming - * - * This file is part of the Linux kernel, and is made available - * under the terms of the GNU General Public License version 2. - * */ #include <linux/efi.h> @@ -34,6 +31,7 @@ static unsigned long __chunk_size = EFI_READ_CHUNK_SIZE; static int __section(.data) __nokaslr; static int __section(.data) __quiet; +static int __section(.data) __novamap; int __pure nokaslr(void) { @@ -43,6 +41,10 @@ int __pure is_quiet(void) { return __quiet; } +int __pure novamap(void) +{ + return __novamap; +} #define EFI_MMAP_NR_SLACK_SLOTS 8 @@ -482,6 +484,11 @@ efi_status_t efi_parse_options(char const *cmdline) __chunk_size = -1UL; } + if (!strncmp(str, "novamap", 7)) { + str += strlen("novamap"); + __novamap = 1; + } + /* Group words together, delimited by "," */ while (*str && *str != ' ' && *str != ',') str++; @@ -919,3 +926,18 @@ free_map: fail: return status; } + +void *get_efi_config_table(efi_system_table_t *sys_table, efi_guid_t guid) +{ + efi_config_table_t *tables = (efi_config_table_t *)sys_table->tables; + int i; + + for (i = 0; i < sys_table->nr_tables; i++) { + if (efi_guidcmp(tables[i].guid, guid) != 0) + continue; + + return (void *)tables[i].table; + } + + return NULL; +} diff --git a/drivers/firmware/efi/libstub/efistub.h b/drivers/firmware/efi/libstub/efistub.h index 32799cf039ef..7f1556fd867d 100644 --- a/drivers/firmware/efi/libstub/efistub.h +++ b/drivers/firmware/efi/libstub/efistub.h @@ -27,6 +27,7 @@ extern int __pure nokaslr(void); extern int __pure is_quiet(void); +extern int __pure novamap(void); #define pr_efi(sys_table, msg) do { \ if (!is_quiet()) efi_printk(sys_table, "EFI stub: "msg); \ @@ -64,4 +65,17 @@ efi_status_t check_platform_features(efi_system_table_t *sys_table_arg); efi_status_t efi_random_get_seed(efi_system_table_t *sys_table_arg); +void *get_efi_config_table(efi_system_table_t *sys_table, efi_guid_t guid); + +/* Helper macros for the usual case of using simple C variables: */ +#ifndef fdt_setprop_inplace_var +#define fdt_setprop_inplace_var(fdt, node_offset, name, var) \ + fdt_setprop_inplace((fdt), (node_offset), (name), &(var), sizeof(var)) +#endif + +#ifndef fdt_setprop_var +#define fdt_setprop_var(fdt, node_offset, name, var) \ + fdt_setprop((fdt), (node_offset), (name), &(var), sizeof(var)) +#endif + #endif diff --git a/drivers/firmware/efi/libstub/fdt.c b/drivers/firmware/efi/libstub/fdt.c index 0dc7b4987cc2..0bf0190917e0 100644 --- a/drivers/firmware/efi/libstub/fdt.c +++ b/drivers/firmware/efi/libstub/fdt.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0 /* * FDT related Helper functions used by the EFI stub on multiple * architectures. This should be #included by the EFI stub * implementation files. * * Copyright 2013 Linaro Limited; author Roy Franz - * - * This file is part of the Linux kernel, and is made available - * under the terms of the GNU General Public License version 2. - * */ #include <linux/efi.h> @@ -26,10 +23,8 @@ static void fdt_update_cell_size(efi_system_table_t *sys_table, void *fdt) offset = fdt_path_offset(fdt, "/"); /* Set the #address-cells and #size-cells values for an empty tree */ - fdt_setprop_u32(fdt, offset, "#address-cells", - EFI_DT_ADDR_CELLS_DEFAULT); - - fdt_setprop_u32(fdt, offset, "#size-cells", EFI_DT_SIZE_CELLS_DEFAULT); + fdt_setprop_u32(fdt, offset, "#address-cells", EFI_DT_ADDR_CELLS_DEFAULT); + fdt_setprop_u32(fdt, offset, "#size-cells", EFI_DT_SIZE_CELLS_DEFAULT); } static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, @@ -42,7 +37,7 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, u32 fdt_val32; u64 fdt_val64; - /* Do some checks on provided FDT, if it exists*/ + /* Do some checks on provided FDT, if it exists: */ if (orig_fdt) { if (fdt_check_header(orig_fdt)) { pr_efi_err(sys_table, "Device Tree header not valid!\n"); @@ -50,7 +45,7 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, } /* * We don't get the size of the FDT if we get if from a - * configuration table. + * configuration table: */ if (orig_fdt_size && fdt_totalsize(orig_fdt) > orig_fdt_size) { pr_efi_err(sys_table, "Truncated device tree! foo!\n"); @@ -64,8 +59,8 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, status = fdt_create_empty_tree(fdt, new_fdt_size); if (status == 0) { /* - * Any failure from the following function is non - * critical + * Any failure from the following function is + * non-critical: */ fdt_update_cell_size(sys_table, fdt); } @@ -86,12 +81,13 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, if (node < 0) { node = fdt_add_subnode(fdt, 0, "chosen"); if (node < 0) { - status = node; /* node is error code when negative */ + /* 'node' is an error code when negative: */ + status = node; goto fdt_set_fail; } } - if ((cmdline_ptr != NULL) && (strlen(cmdline_ptr) > 0)) { + if (cmdline_ptr != NULL && strlen(cmdline_ptr) > 0) { status = fdt_setprop(fdt, node, "bootargs", cmdline_ptr, strlen(cmdline_ptr) + 1); if (status) @@ -103,13 +99,12 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, u64 initrd_image_end; u64 initrd_image_start = cpu_to_fdt64(initrd_addr); - status = fdt_setprop(fdt, node, "linux,initrd-start", - &initrd_image_start, sizeof(u64)); + status = fdt_setprop_var(fdt, node, "linux,initrd-start", initrd_image_start); if (status) goto fdt_set_fail; + initrd_image_end = cpu_to_fdt64(initrd_addr + initrd_size); - status = fdt_setprop(fdt, node, "linux,initrd-end", - &initrd_image_end, sizeof(u64)); + status = fdt_setprop_var(fdt, node, "linux,initrd-end", initrd_image_end); if (status) goto fdt_set_fail; } @@ -117,30 +112,28 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, /* Add FDT entries for EFI runtime services in chosen node. */ node = fdt_subnode_offset(fdt, 0, "chosen"); fdt_val64 = cpu_to_fdt64((u64)(unsigned long)sys_table); - status = fdt_setprop(fdt, node, "linux,uefi-system-table", - &fdt_val64, sizeof(fdt_val64)); + + status = fdt_setprop_var(fdt, node, "linux,uefi-system-table", fdt_val64); if (status) goto fdt_set_fail; fdt_val64 = U64_MAX; /* placeholder */ - status = fdt_setprop(fdt, node, "linux,uefi-mmap-start", - &fdt_val64, sizeof(fdt_val64)); + + status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-start", fdt_val64); if (status) goto fdt_set_fail; fdt_val32 = U32_MAX; /* placeholder */ - status = fdt_setprop(fdt, node, "linux,uefi-mmap-size", - &fdt_val32, sizeof(fdt_val32)); + + status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-size", fdt_val32); if (status) goto fdt_set_fail; - status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-size", - &fdt_val32, sizeof(fdt_val32)); + status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-desc-size", fdt_val32); if (status) goto fdt_set_fail; - status = fdt_setprop(fdt, node, "linux,uefi-mmap-desc-ver", - &fdt_val32, sizeof(fdt_val32)); + status = fdt_setprop_var(fdt, node, "linux,uefi-mmap-desc-ver", fdt_val32); if (status) goto fdt_set_fail; @@ -150,8 +143,7 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, efi_status = efi_get_random_bytes(sys_table, sizeof(fdt_val64), (u8 *)&fdt_val64); if (efi_status == EFI_SUCCESS) { - status = fdt_setprop(fdt, node, "kaslr-seed", - &fdt_val64, sizeof(fdt_val64)); + status = fdt_setprop_var(fdt, node, "kaslr-seed", fdt_val64); if (status) goto fdt_set_fail; } else if (efi_status != EFI_NOT_FOUND) { @@ -159,7 +151,7 @@ static efi_status_t update_fdt(efi_system_table_t *sys_table, void *orig_fdt, } } - /* shrink the FDT back to its minimum size */ + /* Shrink the FDT back to its minimum size: */ fdt_pack(fdt); return EFI_SUCCESS; @@ -182,26 +174,26 @@ static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map) return EFI_LOAD_ERROR; fdt_val64 = cpu_to_fdt64((unsigned long)*map->map); - err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-start", - &fdt_val64, sizeof(fdt_val64)); + + err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-start", fdt_val64); if (err) return EFI_LOAD_ERROR; fdt_val32 = cpu_to_fdt32(*map->map_size); - err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-size", - &fdt_val32, sizeof(fdt_val32)); + + err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-size", fdt_val32); if (err) return EFI_LOAD_ERROR; fdt_val32 = cpu_to_fdt32(*map->desc_size); - err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-size", - &fdt_val32, sizeof(fdt_val32)); + + err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-desc-size", fdt_val32); if (err) return EFI_LOAD_ERROR; fdt_val32 = cpu_to_fdt32(*map->desc_ver); - err = fdt_setprop_inplace(fdt, node, "linux,uefi-mmap-desc-ver", - &fdt_val32, sizeof(fdt_val32)); + + err = fdt_setprop_inplace_var(fdt, node, "linux,uefi-mmap-desc-ver", fdt_val32); if (err) return EFI_LOAD_ERROR; @@ -209,13 +201,13 @@ static efi_status_t update_fdt_memmap(void *fdt, struct efi_boot_memmap *map) } #ifndef EFI_FDT_ALIGN -#define EFI_FDT_ALIGN EFI_PAGE_SIZE +# define EFI_FDT_ALIGN EFI_PAGE_SIZE #endif struct exit_boot_struct { - efi_memory_desc_t *runtime_map; - int *runtime_entry_count; - void *new_fdt_addr; + efi_memory_desc_t *runtime_map; + int *runtime_entry_count; + void *new_fdt_addr; }; static efi_status_t exit_boot_func(efi_system_table_t *sys_table_arg, @@ -235,7 +227,7 @@ static efi_status_t exit_boot_func(efi_system_table_t *sys_table_arg, } #ifndef MAX_FDT_SIZE -#define MAX_FDT_SIZE SZ_2M +# define MAX_FDT_SIZE SZ_2M #endif /* @@ -266,16 +258,16 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, unsigned long mmap_key; efi_memory_desc_t *memory_map, *runtime_map; efi_status_t status; - int runtime_entry_count = 0; + int runtime_entry_count; struct efi_boot_memmap map; struct exit_boot_struct priv; - map.map = &runtime_map; - map.map_size = &map_size; - map.desc_size = &desc_size; - map.desc_ver = &desc_ver; - map.key_ptr = &mmap_key; - map.buff_size = &buff_size; + map.map = &runtime_map; + map.map_size = &map_size; + map.desc_size = &desc_size; + map.desc_ver = &desc_ver; + map.key_ptr = &mmap_key; + map.buff_size = &buff_size; /* * Get a copy of the current memory map that we will use to prepare @@ -289,15 +281,13 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, return status; } - pr_efi(sys_table, - "Exiting boot services and installing virtual address map...\n"); + pr_efi(sys_table, "Exiting boot services and installing virtual address map...\n"); map.map = &memory_map; status = efi_high_alloc(sys_table, MAX_FDT_SIZE, EFI_FDT_ALIGN, new_fdt_addr, max_addr); if (status != EFI_SUCCESS) { - pr_efi_err(sys_table, - "Unable to allocate memory for new device tree.\n"); + pr_efi_err(sys_table, "Unable to allocate memory for new device tree.\n"); goto fail; } @@ -318,15 +308,19 @@ efi_status_t allocate_new_fdt_and_exit_boot(efi_system_table_t *sys_table, goto fail_free_new_fdt; } - priv.runtime_map = runtime_map; - priv.runtime_entry_count = &runtime_entry_count; - priv.new_fdt_addr = (void *)*new_fdt_addr; - status = efi_exit_boot_services(sys_table, handle, &map, &priv, - exit_boot_func); + runtime_entry_count = 0; + priv.runtime_map = runtime_map; + priv.runtime_entry_count = &runtime_entry_count; + priv.new_fdt_addr = (void *)*new_fdt_addr; + + status = efi_exit_boot_services(sys_table, handle, &map, &priv, exit_boot_func); if (status == EFI_SUCCESS) { efi_set_virtual_address_map_t *svam; + if (novamap()) + return EFI_SUCCESS; + /* Install the new virtual address map */ svam = sys_table->runtime->set_virtual_address_map; status = svam(runtime_entry_count * desc_size, desc_size, @@ -363,31 +357,23 @@ fail_free_new_fdt: fail: sys_table->boottime->free_pool(runtime_map); + return EFI_LOAD_ERROR; } void *get_fdt(efi_system_table_t *sys_table, unsigned long *fdt_size) { - efi_guid_t fdt_guid = DEVICE_TREE_GUID; - efi_config_table_t *tables; - int i; - - tables = (efi_config_table_t *)sys_table->tables; + void *fdt; - for (i = 0; i < sys_table->nr_tables; i++) { - void *fdt; + fdt = get_efi_config_table(sys_table, DEVICE_TREE_GUID); - if (efi_guidcmp(tables[i].guid, fdt_guid) != 0) - continue; + if (!fdt) + return NULL; - fdt = (void *)tables[i].table; - if (fdt_check_header(fdt) != 0) { - pr_efi_err(sys_table, "Invalid header detected on UEFI supplied FDT, ignoring ...\n"); - return NULL; - } - *fdt_size = fdt_totalsize(fdt); - return fdt; + if (fdt_check_header(fdt) != 0) { + pr_efi_err(sys_table, "Invalid header detected on UEFI supplied FDT, ignoring ...\n"); + return NULL; } - - return NULL; + *fdt_size = fdt_totalsize(fdt); + return fdt; } diff --git a/drivers/firmware/efi/libstub/gop.c b/drivers/firmware/efi/libstub/gop.c index 24c461dea7af..0101ca4c13b1 100644 --- a/drivers/firmware/efi/libstub/gop.c +++ b/drivers/firmware/efi/libstub/gop.c @@ -1,10 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0 /* ----------------------------------------------------------------------- * * Copyright 2011 Intel Corporation; author Matt Fleming * - * This file is part of the Linux kernel, and is made available under - * the terms of the GNU General Public License version 2. - * * ----------------------------------------------------------------------- */ #include <linux/efi.h> diff --git a/drivers/firmware/efi/libstub/random.c b/drivers/firmware/efi/libstub/random.c index e0e603a89aa9..b4b1d1dcb5fd 100644 --- a/drivers/firmware/efi/libstub/random.c +++ b/drivers/firmware/efi/libstub/random.c @@ -1,10 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2016 Linaro Ltd; <ard.biesheuvel@linaro.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * */ #include <linux/efi.h> diff --git a/drivers/firmware/efi/libstub/secureboot.c b/drivers/firmware/efi/libstub/secureboot.c index 72d9dfbebf08..edba5e7a3743 100644 --- a/drivers/firmware/efi/libstub/secureboot.c +++ b/drivers/firmware/efi/libstub/secureboot.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Secure boot handling. * @@ -5,9 +6,6 @@ * Roy Franz <roy.franz@linaro.org * Copyright (C) 2013 Red Hat, Inc. * Mark Salter <msalter@redhat.com> - * - * This file is part of the Linux kernel, and is made available under the - * terms of the GNU General Public License version 2. */ #include <linux/efi.h> #include <asm/efi.h> diff --git a/drivers/firmware/efi/libstub/tpm.c b/drivers/firmware/efi/libstub/tpm.c index a90b0b8fc69a..eb9af83e4d59 100644 --- a/drivers/firmware/efi/libstub/tpm.c +++ b/drivers/firmware/efi/libstub/tpm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * TPM handling. * @@ -5,9 +6,6 @@ * Copyright (C) 2017 Google, Inc. * Matthew Garrett <mjg59@google.com> * Thiebaud Weksteen <tweek@google.com> - * - * This file is part of the Linux kernel, and is made available under the - * terms of the GNU General Public License version 2. */ #include <linux/efi.h> #include <linux/tpm_eventlog.h> @@ -59,31 +57,40 @@ void efi_enable_reset_attack_mitigation(efi_system_table_t *sys_table_arg) #endif -static void efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg) +void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table_arg) { efi_guid_t tcg2_guid = EFI_TCG2_PROTOCOL_GUID; efi_guid_t linux_eventlog_guid = LINUX_EFI_TPM_EVENT_LOG_GUID; efi_status_t status; efi_physical_addr_t log_location = 0, log_last_entry = 0; struct linux_efi_tpm_eventlog *log_tbl = NULL; + struct efi_tcg2_final_events_table *final_events_table; unsigned long first_entry_addr, last_entry_addr; size_t log_size, last_entry_size; efi_bool_t truncated; + int version = EFI_TCG2_EVENT_LOG_FORMAT_TCG_2; void *tcg2_protocol = NULL; + int final_events_size = 0; status = efi_call_early(locate_protocol, &tcg2_guid, NULL, &tcg2_protocol); if (status != EFI_SUCCESS) return; - status = efi_call_proto(efi_tcg2_protocol, get_event_log, tcg2_protocol, - EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2, - &log_location, &log_last_entry, &truncated); - if (status != EFI_SUCCESS) - return; + status = efi_call_proto(efi_tcg2_protocol, get_event_log, + tcg2_protocol, version, &log_location, + &log_last_entry, &truncated); + + if (status != EFI_SUCCESS || !log_location) { + version = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2; + status = efi_call_proto(efi_tcg2_protocol, get_event_log, + tcg2_protocol, version, &log_location, + &log_last_entry, &truncated); + if (status != EFI_SUCCESS || !log_location) + return; + + } - if (!log_location) - return; first_entry_addr = (unsigned long) log_location; /* @@ -98,8 +105,23 @@ static void efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg) * We need to calculate its size to deduce the full size of * the logs. */ - last_entry_size = sizeof(struct tcpa_event) + - ((struct tcpa_event *) last_entry_addr)->event_size; + if (version == EFI_TCG2_EVENT_LOG_FORMAT_TCG_2) { + /* + * The TCG2 log format has variable length entries, + * and the information to decode the hash algorithms + * back into a size is contained in the first entry - + * pass a pointer to the final entry (to calculate its + * size) and the first entry (so we know how long each + * digest is) + */ + last_entry_size = + __calc_tpm2_event_size((void *)last_entry_addr, + (void *)(long)log_location, + false); + } else { + last_entry_size = sizeof(struct tcpa_event) + + ((struct tcpa_event *) last_entry_addr)->event_size; + } log_size = log_last_entry - log_location + last_entry_size; } @@ -114,9 +136,37 @@ static void efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg) return; } + /* + * Figure out whether any events have already been logged to the + * final events structure, and if so how much space they take up + */ + final_events_table = get_efi_config_table(sys_table_arg, + LINUX_EFI_TPM_FINAL_LOG_GUID); + if (final_events_table && final_events_table->nr_events) { + struct tcg_pcr_event2_head *header; + int offset; + void *data; + int event_size; + int i = final_events_table->nr_events; + + data = (void *)final_events_table; + offset = sizeof(final_events_table->version) + + sizeof(final_events_table->nr_events); + + while (i > 0) { + header = data + offset + final_events_size; + event_size = __calc_tpm2_event_size(header, + (void *)(long)log_location, + false); + final_events_size += event_size; + i--; + } + } + memset(log_tbl, 0, sizeof(*log_tbl) + log_size); log_tbl->size = log_size; - log_tbl->version = EFI_TCG2_EVENT_LOG_FORMAT_TCG_1_2; + log_tbl->final_events_preboot_size = final_events_size; + log_tbl->version = version; memcpy(log_tbl->log, (void *) first_entry_addr, log_size); status = efi_call_early(install_configuration_table, @@ -128,9 +178,3 @@ static void efi_retrieve_tpm2_eventlog_1_2(efi_system_table_t *sys_table_arg) err_free: efi_call_early(free_pool, log_tbl); } - -void efi_retrieve_tpm2_eventlog(efi_system_table_t *sys_table_arg) -{ - /* Only try to retrieve the logs in 1.2 format. */ - efi_retrieve_tpm2_eventlog_1_2(sys_table_arg); -} diff --git a/drivers/firmware/efi/memattr.c b/drivers/firmware/efi/memattr.c index 8986757eafaf..58452fde92cc 100644 --- a/drivers/firmware/efi/memattr.c +++ b/drivers/firmware/efi/memattr.c @@ -1,9 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2016 Linaro Ltd. <ard.biesheuvel@linaro.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #define pr_fmt(fmt) "efi: memattr: " fmt @@ -94,7 +91,7 @@ static bool entry_is_valid(const efi_memory_desc_t *in, efi_memory_desc_t *out) if (!(md->attribute & EFI_MEMORY_RUNTIME)) continue; - if (md->virt_addr == 0) { + if (md->virt_addr == 0 && md->phys_addr != 0) { /* no virtual mapping has been installed by the stub */ break; } diff --git a/drivers/firmware/efi/runtime-map.c b/drivers/firmware/efi/runtime-map.c index 84a11d0a8023..ad9ddefc9dcb 100644 --- a/drivers/firmware/efi/runtime-map.c +++ b/drivers/firmware/efi/runtime-map.c @@ -1,8 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * linux/drivers/efi/runtime-map.c * Copyright (C) 2013 Red Hat, Inc., Dave Young <dyoung@redhat.com> - * - * This file is released under the GPLv2. */ #include <linux/string.h> diff --git a/drivers/firmware/efi/runtime-wrappers.c b/drivers/firmware/efi/runtime-wrappers.c index 8903b9ccfc2b..65fffaa22210 100644 --- a/drivers/firmware/efi/runtime-wrappers.c +++ b/drivers/firmware/efi/runtime-wrappers.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * runtime-wrappers.c - Runtime Services function call wrappers * @@ -19,8 +20,6 @@ * Copyright (C) 1999-2002 Hewlett-Packard Co. * Copyright (C) 2005-2008 Intel Co. * Copyright (C) 2013 SuSE Labs - * - * This file is released under the GPLv2. */ #define pr_fmt(fmt) "efi: " fmt @@ -85,15 +84,28 @@ struct efi_runtime_work efi_rts_work; pr_err("Failed to queue work to efi_rts_wq.\n"); \ \ exit: \ - efi_rts_work.efi_rts_id = NONE; \ + efi_rts_work.efi_rts_id = EFI_NONE; \ efi_rts_work.status; \ }) +#ifndef arch_efi_save_flags +#define arch_efi_save_flags(state_flags) local_save_flags(state_flags) +#define arch_efi_restore_flags(state_flags) local_irq_restore(state_flags) +#endif + +unsigned long efi_call_virt_save_flags(void) +{ + unsigned long flags; + + arch_efi_save_flags(flags); + return flags; +} + void efi_call_virt_check_flags(unsigned long flags, const char *call) { unsigned long cur_flags, mismatch; - local_save_flags(cur_flags); + cur_flags = efi_call_virt_save_flags(); mismatch = flags ^ cur_flags; if (!WARN_ON_ONCE(mismatch & ARCH_EFI_IRQ_FLAGS_MASK)) @@ -102,7 +114,7 @@ void efi_call_virt_check_flags(unsigned long flags, const char *call) add_taint(TAINT_FIRMWARE_WORKAROUND, LOCKDEP_NOW_UNRELIABLE); pr_err_ratelimited(FW_BUG "IRQ flags corrupted (0x%08lx=>0x%08lx) by EFI %s\n", flags, cur_flags, call); - local_irq_restore(flags); + arch_efi_restore_flags(flags); } /* @@ -147,6 +159,13 @@ void efi_call_virt_check_flags(unsigned long flags, const char *call) static DEFINE_SEMAPHORE(efi_runtime_lock); /* + * Expose the EFI runtime lock to the UV platform + */ +#ifdef CONFIG_X86_UV +extern struct semaphore __efi_uv_runtime_lock __alias(efi_runtime_lock); +#endif + +/* * Calls the appropriate efi_runtime_service() with the appropriate * arguments. * @@ -168,50 +187,50 @@ static void efi_call_rts(struct work_struct *work) arg5 = efi_rts_work.arg5; switch (efi_rts_work.efi_rts_id) { - case GET_TIME: + case EFI_GET_TIME: status = efi_call_virt(get_time, (efi_time_t *)arg1, (efi_time_cap_t *)arg2); break; - case SET_TIME: + case EFI_SET_TIME: status = efi_call_virt(set_time, (efi_time_t *)arg1); break; - case GET_WAKEUP_TIME: + case EFI_GET_WAKEUP_TIME: status = efi_call_virt(get_wakeup_time, (efi_bool_t *)arg1, (efi_bool_t *)arg2, (efi_time_t *)arg3); break; - case SET_WAKEUP_TIME: + case EFI_SET_WAKEUP_TIME: status = efi_call_virt(set_wakeup_time, *(efi_bool_t *)arg1, (efi_time_t *)arg2); break; - case GET_VARIABLE: + case EFI_GET_VARIABLE: status = efi_call_virt(get_variable, (efi_char16_t *)arg1, (efi_guid_t *)arg2, (u32 *)arg3, (unsigned long *)arg4, (void *)arg5); break; - case GET_NEXT_VARIABLE: + case EFI_GET_NEXT_VARIABLE: status = efi_call_virt(get_next_variable, (unsigned long *)arg1, (efi_char16_t *)arg2, (efi_guid_t *)arg3); break; - case SET_VARIABLE: + case EFI_SET_VARIABLE: status = efi_call_virt(set_variable, (efi_char16_t *)arg1, (efi_guid_t *)arg2, *(u32 *)arg3, *(unsigned long *)arg4, (void *)arg5); break; - case QUERY_VARIABLE_INFO: + case EFI_QUERY_VARIABLE_INFO: status = efi_call_virt(query_variable_info, *(u32 *)arg1, (u64 *)arg2, (u64 *)arg3, (u64 *)arg4); break; - case GET_NEXT_HIGH_MONO_COUNT: + case EFI_GET_NEXT_HIGH_MONO_COUNT: status = efi_call_virt(get_next_high_mono_count, (u32 *)arg1); break; - case UPDATE_CAPSULE: + case EFI_UPDATE_CAPSULE: status = efi_call_virt(update_capsule, (efi_capsule_header_t **)arg1, *(unsigned long *)arg2, *(unsigned long *)arg3); break; - case QUERY_CAPSULE_CAPS: + case EFI_QUERY_CAPSULE_CAPS: status = efi_call_virt(query_capsule_caps, (efi_capsule_header_t **)arg1, *(unsigned long *)arg2, (u64 *)arg3, @@ -235,7 +254,7 @@ static efi_status_t virt_efi_get_time(efi_time_t *tm, efi_time_cap_t *tc) if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(GET_TIME, tm, tc, NULL, NULL, NULL); + status = efi_queue_work(EFI_GET_TIME, tm, tc, NULL, NULL, NULL); up(&efi_runtime_lock); return status; } @@ -246,7 +265,7 @@ static efi_status_t virt_efi_set_time(efi_time_t *tm) if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(SET_TIME, tm, NULL, NULL, NULL, NULL); + status = efi_queue_work(EFI_SET_TIME, tm, NULL, NULL, NULL, NULL); up(&efi_runtime_lock); return status; } @@ -259,7 +278,7 @@ static efi_status_t virt_efi_get_wakeup_time(efi_bool_t *enabled, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(GET_WAKEUP_TIME, enabled, pending, tm, NULL, + status = efi_queue_work(EFI_GET_WAKEUP_TIME, enabled, pending, tm, NULL, NULL); up(&efi_runtime_lock); return status; @@ -271,7 +290,7 @@ static efi_status_t virt_efi_set_wakeup_time(efi_bool_t enabled, efi_time_t *tm) if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(SET_WAKEUP_TIME, &enabled, tm, NULL, NULL, + status = efi_queue_work(EFI_SET_WAKEUP_TIME, &enabled, tm, NULL, NULL, NULL); up(&efi_runtime_lock); return status; @@ -287,7 +306,7 @@ static efi_status_t virt_efi_get_variable(efi_char16_t *name, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(GET_VARIABLE, name, vendor, attr, data_size, + status = efi_queue_work(EFI_GET_VARIABLE, name, vendor, attr, data_size, data); up(&efi_runtime_lock); return status; @@ -301,7 +320,7 @@ static efi_status_t virt_efi_get_next_variable(unsigned long *name_size, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(GET_NEXT_VARIABLE, name_size, name, vendor, + status = efi_queue_work(EFI_GET_NEXT_VARIABLE, name_size, name, vendor, NULL, NULL); up(&efi_runtime_lock); return status; @@ -317,7 +336,7 @@ static efi_status_t virt_efi_set_variable(efi_char16_t *name, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(SET_VARIABLE, name, vendor, &attr, &data_size, + status = efi_queue_work(EFI_SET_VARIABLE, name, vendor, &attr, &data_size, data); up(&efi_runtime_lock); return status; @@ -352,7 +371,7 @@ static efi_status_t virt_efi_query_variable_info(u32 attr, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(QUERY_VARIABLE_INFO, &attr, storage_space, + status = efi_queue_work(EFI_QUERY_VARIABLE_INFO, &attr, storage_space, remaining_space, max_variable_size, NULL); up(&efi_runtime_lock); return status; @@ -384,7 +403,7 @@ static efi_status_t virt_efi_get_next_high_mono_count(u32 *count) if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(GET_NEXT_HIGH_MONO_COUNT, count, NULL, NULL, + status = efi_queue_work(EFI_GET_NEXT_HIGH_MONO_COUNT, count, NULL, NULL, NULL, NULL); up(&efi_runtime_lock); return status; @@ -400,7 +419,7 @@ static void virt_efi_reset_system(int reset_type, "could not get exclusive access to the firmware\n"); return; } - efi_rts_work.efi_rts_id = RESET_SYSTEM; + efi_rts_work.efi_rts_id = EFI_RESET_SYSTEM; __efi_call_virt(reset_system, reset_type, status, data_size, data); up(&efi_runtime_lock); } @@ -416,7 +435,7 @@ static efi_status_t virt_efi_update_capsule(efi_capsule_header_t **capsules, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(UPDATE_CAPSULE, capsules, &count, &sg_list, + status = efi_queue_work(EFI_UPDATE_CAPSULE, capsules, &count, &sg_list, NULL, NULL); up(&efi_runtime_lock); return status; @@ -434,7 +453,7 @@ static efi_status_t virt_efi_query_capsule_caps(efi_capsule_header_t **capsules, if (down_interruptible(&efi_runtime_lock)) return EFI_ABORTED; - status = efi_queue_work(QUERY_CAPSULE_CAPS, capsules, &count, + status = efi_queue_work(EFI_QUERY_CAPSULE_CAPS, capsules, &count, max_size, reset_type, NULL); up(&efi_runtime_lock); return status; diff --git a/drivers/firmware/efi/test/Makefile b/drivers/firmware/efi/test/Makefile index bcd4577d40e6..4197088550e6 100644 --- a/drivers/firmware/efi/test/Makefile +++ b/drivers/firmware/efi/test/Makefile @@ -1 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_EFI_TEST) += efi_test.o diff --git a/drivers/firmware/efi/test/efi_test.c b/drivers/firmware/efi/test/efi_test.c index 51ecf7d6da48..877745c3aaf2 100644 --- a/drivers/firmware/efi/test/efi_test.c +++ b/drivers/firmware/efi/test/efi_test.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * EFI Test Driver for Runtime Services * diff --git a/drivers/firmware/efi/test/efi_test.h b/drivers/firmware/efi/test/efi_test.h index 5f4818bf112f..f2446aa1c2e3 100644 --- a/drivers/firmware/efi/test/efi_test.h +++ b/drivers/firmware/efi/test/efi_test.h @@ -1,4 +1,4 @@ -/* SPDX-License-Identifier: GPL-2.0 */ +/* SPDX-License-Identifier: GPL-2.0+ */ /* * EFI Test driver Header * diff --git a/drivers/firmware/efi/tpm.c b/drivers/firmware/efi/tpm.c index 0cbeb3d46b18..1d3f5ca3eaaf 100644 --- a/drivers/firmware/efi/tpm.c +++ b/drivers/firmware/efi/tpm.c @@ -1,17 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2017 Google, Inc. * Thiebaud Weksteen <tweek@google.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ +#define TPM_MEMREMAP(start, size) early_memremap(start, size) +#define TPM_MEMUNMAP(start, size) early_memunmap(start, size) + +#include <asm/early_ioremap.h> #include <linux/efi.h> #include <linux/init.h> #include <linux/memblock.h> +#include <linux/tpm_eventlog.h> -#include <asm/early_ioremap.h> +int efi_tpm_final_log_size; +EXPORT_SYMBOL(efi_tpm_final_log_size); + +static int tpm2_calc_event_log_size(void *data, int count, void *size_info) +{ + struct tcg_pcr_event2_head *header; + int event_size, size = 0; + + while (count > 0) { + header = data + size; + event_size = __calc_tpm2_event_size(header, size_info, true); + if (event_size == 0) + return -1; + size += event_size; + count--; + } + + return size; +} /* * Reserve the memory associated with the TPM Event Log configuration table. @@ -19,22 +39,54 @@ int __init efi_tpm_eventlog_init(void) { struct linux_efi_tpm_eventlog *log_tbl; + struct efi_tcg2_final_events_table *final_tbl; unsigned int tbl_size; + int ret = 0; - if (efi.tpm_log == EFI_INVALID_TABLE_ADDR) + if (efi.tpm_log == EFI_INVALID_TABLE_ADDR) { + /* + * We can't calculate the size of the final events without the + * first entry in the TPM log, so bail here. + */ return 0; + } log_tbl = early_memremap(efi.tpm_log, sizeof(*log_tbl)); if (!log_tbl) { pr_err("Failed to map TPM Event Log table @ 0x%lx\n", - efi.tpm_log); + efi.tpm_log); efi.tpm_log = EFI_INVALID_TABLE_ADDR; return -ENOMEM; } tbl_size = sizeof(*log_tbl) + log_tbl->size; memblock_reserve(efi.tpm_log, tbl_size); + + if (efi.tpm_final_log == EFI_INVALID_TABLE_ADDR) + goto out; + + final_tbl = early_memremap(efi.tpm_final_log, sizeof(*final_tbl)); + + if (!final_tbl) { + pr_err("Failed to map TPM Final Event Log table @ 0x%lx\n", + efi.tpm_final_log); + efi.tpm_final_log = EFI_INVALID_TABLE_ADDR; + ret = -ENOMEM; + goto out; + } + + tbl_size = tpm2_calc_event_log_size((void *)efi.tpm_final_log + + sizeof(final_tbl->version) + + sizeof(final_tbl->nr_events), + final_tbl->nr_events, + log_tbl->log); + memblock_reserve((unsigned long)final_tbl, + tbl_size + sizeof(*final_tbl)); + early_memunmap(final_tbl, sizeof(*final_tbl)); + efi_tpm_final_log_size = tbl_size; + +out: early_memunmap(log_tbl, sizeof(*log_tbl)); - return 0; + return ret; } diff --git a/drivers/firmware/efi/vars.c b/drivers/firmware/efi/vars.c index fceaafd67ec6..436d1776bc7b 100644 --- a/drivers/firmware/efi/vars.c +++ b/drivers/firmware/efi/vars.c @@ -1,22 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Originally from efivars.c * * Copyright (C) 2001,2003,2004 Dell <Matt_Domsch@dell.com> * Copyright (C) 2004 Intel Corporation <matthew.e.tolentino@intel.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. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/capability.h> diff --git a/drivers/firmware/google/Kconfig b/drivers/firmware/google/Kconfig index 91a0404affe2..a3a6ca659ffa 100644 --- a/drivers/firmware/google/Kconfig +++ b/drivers/firmware/google/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only menuconfig GOOGLE_FIRMWARE bool "Google Firmware Drivers" default n diff --git a/drivers/firmware/google/coreboot_table.c b/drivers/firmware/google/coreboot_table.c index 078d3bbe632f..8d132e4f008a 100644 --- a/drivers/firmware/google/coreboot_table.c +++ b/drivers/firmware/google/coreboot_table.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * coreboot_table.c * @@ -5,15 +6,6 @@ * * Copyright 2017 Google Inc. * Copyright 2017 Samuel Holland <samuel@sholland.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/acpi.h> diff --git a/drivers/firmware/google/coreboot_table.h b/drivers/firmware/google/coreboot_table.h index 71a9de6b15fa..7b7b4a6eedda 100644 --- a/drivers/firmware/google/coreboot_table.h +++ b/drivers/firmware/google/coreboot_table.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * coreboot_table.h * @@ -6,21 +7,12 @@ * Copyright 2014 Gerd Hoffmann <kraxel@redhat.com> * Copyright 2017 Google Inc. * Copyright 2017 Samuel Holland <samuel@sholland.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __COREBOOT_TABLE_H #define __COREBOOT_TABLE_H -#include <linux/io.h> +#include <linux/device.h> /* Coreboot table header structure */ struct coreboot_table_header { @@ -91,4 +83,13 @@ int coreboot_driver_register(struct coreboot_driver *driver); /* Unregister a driver that uses the data from a coreboot table. */ void coreboot_driver_unregister(struct coreboot_driver *driver); +/* module_coreboot_driver() - Helper macro for drivers that don't do + * anything special in module init/exit. This eliminates a lot of + * boilerplate. Each module may only use this macro once, and + * calling it replaces module_init() and module_exit() + */ +#define module_coreboot_driver(__coreboot_driver) \ + module_driver(__coreboot_driver, coreboot_driver_register, \ + coreboot_driver_unregister) + #endif /* __COREBOOT_TABLE_H */ diff --git a/drivers/firmware/google/framebuffer-coreboot.c b/drivers/firmware/google/framebuffer-coreboot.c index b8b49c067157..916f26adc595 100644 --- a/drivers/firmware/google/framebuffer-coreboot.c +++ b/drivers/firmware/google/framebuffer-coreboot.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * framebuffer-coreboot.c * @@ -6,15 +7,6 @@ * Copyright 2012-2013 David Herrmann <dh.herrmann@gmail.com> * Copyright 2017 Google Inc. * Copyright 2017 Samuel Holland <samuel@sholland.org> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/device.h> @@ -97,19 +89,7 @@ static struct coreboot_driver framebuffer_driver = { }, .tag = CB_TAG_FRAMEBUFFER, }; - -static int __init coreboot_framebuffer_init(void) -{ - return coreboot_driver_register(&framebuffer_driver); -} - -static void coreboot_framebuffer_exit(void) -{ - coreboot_driver_unregister(&framebuffer_driver); -} - -module_init(coreboot_framebuffer_init); -module_exit(coreboot_framebuffer_exit); +module_coreboot_driver(framebuffer_driver); MODULE_AUTHOR("Samuel Holland <samuel@sholland.org>"); MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/gsmi.c b/drivers/firmware/google/gsmi.c index 82ce1e6d261e..edaa4e5d84ad 100644 --- a/drivers/firmware/google/gsmi.c +++ b/drivers/firmware/google/gsmi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2010 Google Inc. All Rights Reserved. * Author: dlaurie@google.com (Duncan Laurie) diff --git a/drivers/firmware/google/memconsole-coreboot.c b/drivers/firmware/google/memconsole-coreboot.c index b29e10757bfb..fd7f0fbec07e 100644 --- a/drivers/firmware/google/memconsole-coreboot.c +++ b/drivers/firmware/google/memconsole-coreboot.c @@ -1,21 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * memconsole-coreboot.c * * Memory based BIOS console accessed through coreboot table. * * Copyright 2017 Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/device.h> +#include <linux/io.h> #include <linux/kernel.h> #include <linux/module.h> @@ -34,7 +27,7 @@ struct cbmem_cons { #define CURSOR_MASK ((1 << 28) - 1) #define OVERFLOW (1 << 31) -static struct cbmem_cons __iomem *cbmem_console; +static struct cbmem_cons *cbmem_console; static u32 cbmem_console_size; /* @@ -75,7 +68,7 @@ static ssize_t memconsole_coreboot_read(char *buf, loff_t pos, size_t count) static int memconsole_probe(struct coreboot_device *dev) { - struct cbmem_cons __iomem *tmp_cbmc; + struct cbmem_cons *tmp_cbmc; tmp_cbmc = memremap(dev->cbmem_ref.cbmem_addr, sizeof(*tmp_cbmc), MEMREMAP_WB); @@ -85,13 +78,13 @@ static int memconsole_probe(struct coreboot_device *dev) /* Read size only once to prevent overrun attack through /dev/mem. */ cbmem_console_size = tmp_cbmc->size_dont_access_after_boot; - cbmem_console = memremap(dev->cbmem_ref.cbmem_addr, + cbmem_console = devm_memremap(&dev->dev, dev->cbmem_ref.cbmem_addr, cbmem_console_size + sizeof(*cbmem_console), MEMREMAP_WB); memunmap(tmp_cbmc); - if (!cbmem_console) - return -ENOMEM; + if (IS_ERR(cbmem_console)) + return PTR_ERR(cbmem_console); memconsole_setup(memconsole_coreboot_read); @@ -102,9 +95,6 @@ static int memconsole_remove(struct coreboot_device *dev) { memconsole_exit(); - if (cbmem_console) - memunmap(cbmem_console); - return 0; } @@ -116,19 +106,7 @@ static struct coreboot_driver memconsole_driver = { }, .tag = CB_TAG_CBMEM_CONSOLE, }; - -static void coreboot_memconsole_exit(void) -{ - coreboot_driver_unregister(&memconsole_driver); -} - -static int __init coreboot_memconsole_init(void) -{ - return coreboot_driver_register(&memconsole_driver); -} - -module_exit(coreboot_memconsole_exit); -module_init(coreboot_memconsole_init); +module_coreboot_driver(memconsole_driver); MODULE_AUTHOR("Google, Inc."); MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/memconsole-x86-legacy.c b/drivers/firmware/google/memconsole-x86-legacy.c index 19bcbd10855b..3d3c4f6b8194 100644 --- a/drivers/firmware/google/memconsole-x86-legacy.c +++ b/drivers/firmware/google/memconsole-x86-legacy.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * memconsole-x86-legacy.c * * EBDA specific parts of the memory based BIOS console. * * Copyright 2017 Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/kernel.h> diff --git a/drivers/firmware/google/memconsole.c b/drivers/firmware/google/memconsole.c index 166f07c68c02..44d314ad69e4 100644 --- a/drivers/firmware/google/memconsole.c +++ b/drivers/firmware/google/memconsole.c @@ -1,35 +1,28 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * memconsole.c * * Architecture-independent parts of the memory based BIOS console. * * Copyright 2017 Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ -#include <linux/init.h> #include <linux/sysfs.h> #include <linux/kobject.h> #include <linux/module.h> #include "memconsole.h" -static ssize_t (*memconsole_read_func)(char *, loff_t, size_t); - static ssize_t memconsole_read(struct file *filp, struct kobject *kobp, struct bin_attribute *bin_attr, char *buf, loff_t pos, size_t count) { + ssize_t (*memconsole_read_func)(char *, loff_t, size_t); + + memconsole_read_func = bin_attr->private; if (WARN_ON_ONCE(!memconsole_read_func)) return -EIO; + return memconsole_read_func(buf, pos, count); } @@ -40,7 +33,7 @@ static struct bin_attribute memconsole_bin_attr = { void memconsole_setup(ssize_t (*read_func)(char *, loff_t, size_t)) { - memconsole_read_func = read_func; + memconsole_bin_attr.private = read_func; } EXPORT_SYMBOL(memconsole_setup); diff --git a/drivers/firmware/google/memconsole.h b/drivers/firmware/google/memconsole.h index ff1592dc7d1a..aaff2b72b606 100644 --- a/drivers/firmware/google/memconsole.h +++ b/drivers/firmware/google/memconsole.h @@ -1,18 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * memconsole.h * * Internal headers of the memory based BIOS console. * * Copyright 2017 Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __FIRMWARE_GOOGLE_MEMCONSOLE_H diff --git a/drivers/firmware/google/vpd.c b/drivers/firmware/google/vpd.c index c0c0b4e4e281..0739f3b70347 100644 --- a/drivers/firmware/google/vpd.c +++ b/drivers/firmware/google/vpd.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vpd.c * * Driver for exporting VPD content to sysfs. * * Copyright 2017 Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/ctype.h> @@ -254,7 +246,7 @@ static int vpd_section_destroy(struct vpd_section *sec) static int vpd_sections_init(phys_addr_t physaddr) { - struct vpd_cbmem __iomem *temp; + struct vpd_cbmem *temp; struct vpd_cbmem header; int ret = 0; @@ -262,7 +254,7 @@ static int vpd_sections_init(phys_addr_t physaddr) if (!temp) return -ENOMEM; - memcpy_fromio(&header, temp, sizeof(struct vpd_cbmem)); + memcpy(&header, temp, sizeof(struct vpd_cbmem)); memunmap(temp); if (header.magic != VPD_CBMEM_MAGIC) @@ -324,19 +316,7 @@ static struct coreboot_driver vpd_driver = { }, .tag = CB_TAG_VPD, }; - -static int __init coreboot_vpd_init(void) -{ - return coreboot_driver_register(&vpd_driver); -} - -static void __exit coreboot_vpd_exit(void) -{ - coreboot_driver_unregister(&vpd_driver); -} - -module_init(coreboot_vpd_init); -module_exit(coreboot_vpd_exit); +module_coreboot_driver(vpd_driver); MODULE_AUTHOR("Google, Inc."); MODULE_LICENSE("GPL"); diff --git a/drivers/firmware/google/vpd_decode.c b/drivers/firmware/google/vpd_decode.c index 943acaa8aa76..92e3258552fc 100644 --- a/drivers/firmware/google/vpd_decode.c +++ b/drivers/firmware/google/vpd_decode.c @@ -1,22 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vpd_decode.c * * Google VPD decoding routines. * * Copyright 2017 Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ -#include <linux/export.h> - #include "vpd_decode.h" static int vpd_decode_len(const s32 max_len, const u8 *in, diff --git a/drivers/firmware/google/vpd_decode.h b/drivers/firmware/google/vpd_decode.h index be3d62c5ca2f..cf8c2ace155a 100644 --- a/drivers/firmware/google/vpd_decode.h +++ b/drivers/firmware/google/vpd_decode.h @@ -1,18 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * vpd_decode.h * * Google VPD decoding routines. * * Copyright 2017 Google Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __VPD_DECODE_H diff --git a/drivers/firmware/imx/Kconfig b/drivers/firmware/imx/Kconfig index 6a7a7c2c5b5f..42b566f8903f 100644 --- a/drivers/firmware/imx/Kconfig +++ b/drivers/firmware/imx/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only config IMX_SCU bool "IMX SCU Protocol driver" depends on IMX_MBOX diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile index 1b2e15b3c9ca..802c4ad8e8f9 100644 --- a/drivers/firmware/imx/Makefile +++ b/drivers/firmware/imx/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o +obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o diff --git a/drivers/firmware/imx/imx-scu-irq.c b/drivers/firmware/imx/imx-scu-irq.c new file mode 100644 index 000000000000..687121f8c4d5 --- /dev/null +++ b/drivers/firmware/imx/imx-scu-irq.c @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + * + * Implementation of the SCU IRQ functions using MU. + * + */ + +#include <dt-bindings/firmware/imx/rsrc.h> +#include <linux/firmware/imx/ipc.h> +#include <linux/mailbox_client.h> + +#define IMX_SC_IRQ_FUNC_ENABLE 1 +#define IMX_SC_IRQ_FUNC_STATUS 2 +#define IMX_SC_IRQ_NUM_GROUP 4 + +static u32 mu_resource_id; + +struct imx_sc_msg_irq_get_status { + struct imx_sc_rpc_msg hdr; + union { + struct { + u16 resource; + u8 group; + u8 reserved; + } __packed req; + struct { + u32 status; + } resp; + } data; +}; + +struct imx_sc_msg_irq_enable { + struct imx_sc_rpc_msg hdr; + u32 mask; + u16 resource; + u8 group; + u8 enable; +} __packed; + +static struct imx_sc_ipc *imx_sc_irq_ipc_handle; +static struct work_struct imx_sc_irq_work; +static ATOMIC_NOTIFIER_HEAD(imx_scu_irq_notifier_chain); + +int imx_scu_irq_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register( + &imx_scu_irq_notifier_chain, nb); +} +EXPORT_SYMBOL(imx_scu_irq_register_notifier); + +int imx_scu_irq_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister( + &imx_scu_irq_notifier_chain, nb); +} +EXPORT_SYMBOL(imx_scu_irq_unregister_notifier); + +static int imx_scu_irq_notifier_call_chain(unsigned long status, u8 *group) +{ + return atomic_notifier_call_chain(&imx_scu_irq_notifier_chain, + status, (void *)group); +} + +static void imx_scu_irq_work_handler(struct work_struct *work) +{ + struct imx_sc_msg_irq_get_status msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + u32 irq_status; + int ret; + u8 i; + + for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) { + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_IRQ; + hdr->func = IMX_SC_IRQ_FUNC_STATUS; + hdr->size = 2; + + msg.data.req.resource = mu_resource_id; + msg.data.req.group = i; + + ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + if (ret) { + pr_err("get irq group %d status failed, ret %d\n", + i, ret); + return; + } + + irq_status = msg.data.resp.status; + if (!irq_status) + continue; + + imx_scu_irq_notifier_call_chain(irq_status, &i); + } +} + +int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable) +{ + struct imx_sc_msg_irq_enable msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + if (!imx_sc_irq_ipc_handle) + return -EPROBE_DEFER; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_IRQ; + hdr->func = IMX_SC_IRQ_FUNC_ENABLE; + hdr->size = 3; + + msg.resource = mu_resource_id; + msg.group = group; + msg.mask = mask; + msg.enable = enable; + + ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + if (ret) + pr_err("enable irq failed, group %d, mask %d, ret %d\n", + group, mask, ret); + + return ret; +} +EXPORT_SYMBOL(imx_scu_irq_group_enable); + +static void imx_scu_irq_callback(struct mbox_client *c, void *msg) +{ + schedule_work(&imx_sc_irq_work); +} + +int imx_scu_enable_general_irq_channel(struct device *dev) +{ + struct of_phandle_args spec; + struct mbox_client *cl; + struct mbox_chan *ch; + int ret = 0, i = 0; + + ret = imx_scu_get_handle(&imx_sc_irq_ipc_handle); + if (ret) + return ret; + + cl = devm_kzalloc(dev, sizeof(*cl), GFP_KERNEL); + if (!cl) + return -ENOMEM; + + cl->dev = dev; + cl->rx_callback = imx_scu_irq_callback; + + /* SCU general IRQ uses general interrupt channel 3 */ + ch = mbox_request_channel_byname(cl, "gip3"); + if (IS_ERR(ch)) { + ret = PTR_ERR(ch); + dev_err(dev, "failed to request mbox chan gip3, ret %d\n", ret); + devm_kfree(dev, cl); + return ret; + } + + INIT_WORK(&imx_sc_irq_work, imx_scu_irq_work_handler); + + if (!of_parse_phandle_with_args(dev->of_node, "mboxes", + "#mbox-cells", 0, &spec)) + i = of_alias_get_id(spec.np, "mu"); + + /* use mu1 as general mu irq channel if failed */ + if (i < 0) + i = 1; + + mu_resource_id = IMX_SC_R_MU_0A + i; + + return ret; +} +EXPORT_SYMBOL(imx_scu_enable_general_irq_channel); diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c index 2bb1a19c413f..04a24a863d6e 100644 --- a/drivers/firmware/imx/imx-scu.c +++ b/drivers/firmware/imx/imx-scu.c @@ -10,6 +10,7 @@ #include <linux/err.h> #include <linux/firmware/imx/types.h> #include <linux/firmware/imx/ipc.h> +#include <linux/firmware/imx/sci.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/kernel.h> @@ -246,6 +247,11 @@ static int imx_scu_probe(struct platform_device *pdev) imx_sc_ipc_handle = sc_ipc; + ret = imx_scu_enable_general_irq_channel(dev); + if (ret) + dev_warn(dev, + "failed to enable general irq channel: %d\n", ret); + dev_info(dev, "NXP i.MX SCU Initialized\n"); return devm_of_platform_populate(dev); diff --git a/drivers/firmware/imx/misc.c b/drivers/firmware/imx/misc.c index 97f5424dbac9..4b56a587dacd 100644 --- a/drivers/firmware/imx/misc.c +++ b/drivers/firmware/imx/misc.c @@ -18,6 +18,14 @@ struct imx_sc_msg_req_misc_set_ctrl { u16 resource; } __packed; +struct imx_sc_msg_req_cpu_start { + struct imx_sc_rpc_msg hdr; + u32 address_hi; + u32 address_lo; + u16 resource; + u8 enable; +} __packed; + struct imx_sc_msg_req_misc_get_ctrl { struct imx_sc_rpc_msg hdr; u32 ctrl; @@ -97,3 +105,33 @@ int imx_sc_misc_get_control(struct imx_sc_ipc *ipc, u32 resource, return 0; } EXPORT_SYMBOL(imx_sc_misc_get_control); + +/* + * This function starts/stops a CPU identified by @resource + * + * @param[in] ipc IPC handle + * @param[in] resource resource the control is associated with + * @param[in] enable true for start, false for stop + * @param[in] phys_addr initial instruction address to be executed + * + * @return Returns 0 for success and < 0 for errors. + */ +int imx_sc_pm_cpu_start(struct imx_sc_ipc *ipc, u32 resource, + bool enable, u64 phys_addr) +{ + struct imx_sc_msg_req_cpu_start msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_PM; + hdr->func = IMX_SC_PM_FUNC_CPU_START; + hdr->size = 4; + + msg.address_hi = phys_addr >> 32; + msg.address_lo = phys_addr; + msg.resource = resource; + msg.enable = enable; + + return imx_scu_call_rpc(ipc, &msg, true); +} +EXPORT_SYMBOL(imx_sc_pm_cpu_start); diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 407245f2efd0..480cec69e2c9 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -74,7 +74,10 @@ struct imx_sc_pd_range { char *name; u32 rsrc; u8 num; + + /* add domain index */ bool postfix; + u8 start_from; }; struct imx_sc_pd_soc { @@ -84,71 +87,75 @@ struct imx_sc_pd_soc { static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { /* LSIO SS */ - { "lsio-pwm", IMX_SC_R_PWM_0, 8, 1 }, - { "lsio-gpio", IMX_SC_R_GPIO_0, 8, 1 }, - { "lsio-gpt", IMX_SC_R_GPT_0, 5, 1 }, - { "lsio-kpp", IMX_SC_R_KPP, 1, 0 }, - { "lsio-fspi", IMX_SC_R_FSPI_0, 2, 1 }, - { "lsio-mu", IMX_SC_R_MU_0A, 14, 1 }, + { "pwm", IMX_SC_R_PWM_0, 8, true, 0 }, + { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, + { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, + { "kpp", IMX_SC_R_KPP, 1, false, 0 }, + { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, + { "mu", IMX_SC_R_MU_0A, 14, true, 0 }, /* CONN SS */ - { "con-usb", IMX_SC_R_USB_0, 2, 1 }, - { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, 0 }, - { "con-usb2", IMX_SC_R_USB_2, 1, 0 }, - { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, 0 }, - { "con-sdhc", IMX_SC_R_SDHC_0, 3, 1 }, - { "con-enet", IMX_SC_R_ENET_0, 2, 1 }, - { "con-nand", IMX_SC_R_NAND, 1, 0 }, - { "con-mlb", IMX_SC_R_MLB_0, 1, 1 }, - - /* Audio DMA SS */ - { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, 0 }, - { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, 0 }, - { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, 0 }, - { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, 1 }, - { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, 1 }, - { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, 1 }, - { "adma-asrc0", IMX_SC_R_ASRC_0, 1, 0 }, - { "adma-asrc1", IMX_SC_R_ASRC_1, 1, 0 }, - { "adma-esai0", IMX_SC_R_ESAI_0, 1, 0 }, - { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, 0 }, - { "adma-sai", IMX_SC_R_SAI_0, 3, 1 }, - { "adma-amix", IMX_SC_R_AMIX, 1, 0 }, - { "adma-mqs0", IMX_SC_R_MQS_0, 1, 0 }, - { "adma-dsp", IMX_SC_R_DSP, 1, 0 }, - { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, 0 }, - { "adma-can", IMX_SC_R_CAN_0, 3, 1 }, - { "adma-ftm", IMX_SC_R_FTM_0, 2, 1 }, - { "adma-lpi2c", IMX_SC_R_I2C_0, 4, 1 }, - { "adma-adc", IMX_SC_R_ADC_0, 1, 1 }, - { "adma-lcd", IMX_SC_R_LCD_0, 1, 1 }, - { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, 1 }, - { "adma-lpuart", IMX_SC_R_UART_0, 4, 1 }, - { "adma-lpspi", IMX_SC_R_SPI_0, 4, 1 }, - - /* VPU SS */ - { "vpu", IMX_SC_R_VPU, 1, 0 }, - { "vpu-pid", IMX_SC_R_VPU_PID0, 8, 1 }, - { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, 0 }, - { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, 0 }, + { "usb", IMX_SC_R_USB_0, 2, true, 0 }, + { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, + { "usb2", IMX_SC_R_USB_2, 1, false, 0 }, + { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, + { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, + { "enet", IMX_SC_R_ENET_0, 2, true, 0 }, + { "nand", IMX_SC_R_NAND, 1, false, 0 }, + { "mlb", IMX_SC_R_MLB_0, 1, true, 0 }, + + /* AUDIO SS */ + { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, + { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, + { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, + { "dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 }, + { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, + { "dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 }, + { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, + { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, + { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, + { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, + { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, + { "amix", IMX_SC_R_AMIX, 1, false, 0 }, + { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, + { "dsp", IMX_SC_R_DSP, 1, false, 0 }, + { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, + + /* DMA SS */ + { "can", IMX_SC_R_CAN_0, 3, true, 0 }, + { "ftm", IMX_SC_R_FTM_0, 2, true, 0 }, + { "lpi2c", IMX_SC_R_I2C_0, 4, true, 0 }, + { "adc", IMX_SC_R_ADC_0, 1, true, 0 }, + { "lcd", IMX_SC_R_LCD_0, 1, true, 0 }, + { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, + { "lpuart", IMX_SC_R_UART_0, 4, true, 0 }, + { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, + + /* VPU SS */ + { "vpu", IMX_SC_R_VPU, 1, false, 0 }, + { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 }, + { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 }, + { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 }, /* GPU SS */ - { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, 1 }, + { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 }, /* HSIO SS */ - { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, 0 }, - { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, 0 }, - { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, 0 }, + { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, + { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, + { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 }, + + /* MIPI SS */ + { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 }, + { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 }, + { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 }, - /* MIPI/LVDS SS */ - { "mipi0", IMX_SC_R_MIPI_0, 1, 0 }, - { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, 0 }, - { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, 1 }, - { "lvds0", IMX_SC_R_LVDS_0, 1, 0 }, + /* LVDS SS */ + { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 }, /* DC SS */ - { "dc0", IMX_SC_R_DC_0, 1, 0 }, - { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, 1 }, + { "dc0", IMX_SC_R_DC_0, 1, false, 0 }, + { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, }; static const struct imx_sc_pd_soc imx8qxp_scu_pd = { @@ -236,7 +243,7 @@ imx_scu_add_pm_domain(struct device *dev, int idx, if (pd_ranges->postfix) snprintf(sc_pd->name, sizeof(sc_pd->name), - "%s%i", pd_ranges->name, idx); + "%s%i", pd_ranges->name, pd_ranges->start_from + idx); else snprintf(sc_pd->name, sizeof(sc_pd->name), "%s", pd_ranges->name); @@ -322,6 +329,7 @@ static int imx_sc_pd_probe(struct platform_device *pdev) static const struct of_device_id imx_sc_pd_match[] = { { .compatible = "fsl,imx8qxp-scu-pd", &imx8qxp_scu_pd}, + { .compatible = "fsl,scu-pd", &imx8qxp_scu_pd}, { /* sentinel */ } }; diff --git a/drivers/firmware/iscsi_ibft.c b/drivers/firmware/iscsi_ibft.c index 966aef334c42..7e12cbdf957c 100644 --- a/drivers/firmware/iscsi_ibft.c +++ b/drivers/firmware/iscsi_ibft.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2007-2010 Red Hat, Inc. * by Peter Jones <pjones@redhat.com> @@ -8,15 +9,6 @@ * * This code exposes the iSCSI Boot Format Table to userland via sysfs. * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * * Changelog: * * 06 Jan 2010 - Peter Jones <pjones@redhat.com> @@ -63,7 +55,6 @@ * * 27 Aug 2007 - Konrad Rzeszutek <konradr@linux.vnet.ibm.com> * First version exposing iBFT data via a binary /sysfs. (v0.1) - * */ @@ -429,7 +420,7 @@ static ssize_t ibft_attr_show_acpitbl(void *data, int type, char *buf) switch (type) { case ISCSI_BOOT_ACPITBL_SIGNATURE: - str += sprintf_string(str, ACPI_NAME_SIZE, + str += sprintf_string(str, ACPI_NAMESEG_SIZE, entry->header->header.signature); break; case ISCSI_BOOT_ACPITBL_OEM_ID: diff --git a/drivers/firmware/iscsi_ibft_find.c b/drivers/firmware/iscsi_ibft_find.c index 85c656d04bb0..64bb94523281 100644 --- a/drivers/firmware/iscsi_ibft_find.c +++ b/drivers/firmware/iscsi_ibft_find.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2007-2010 Red Hat, Inc. * by Peter Jones <pjones@redhat.com> @@ -7,15 +8,6 @@ * by Konrad Rzeszutek <ketuzsezr@darnok.org> * * This code finds the iSCSI Boot Format Table. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/memblock.h> diff --git a/drivers/firmware/memmap.c b/drivers/firmware/memmap.c index ec4fd253a4e9..24945e2da77b 100644 --- a/drivers/firmware/memmap.c +++ b/drivers/firmware/memmap.c @@ -1,17 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * linux/drivers/firmware/memmap.c * Copyright (C) 2008 SUSE LINUX Products GmbH * by Bernhard Walle <bernhard.walle@gmx.de> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License v2.0 as published by - * the Free Software Foundation - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/string.h> @@ -333,7 +324,7 @@ int __init firmware_map_add_early(u64 start, u64 end, const char *type) { struct firmware_map_entry *entry; - entry = memblock_alloc_nopanic(sizeof(struct firmware_map_entry), + entry = memblock_alloc(sizeof(struct firmware_map_entry), SMP_CACHE_BYTES); if (WARN_ON(!entry)) return -ENOMEM; diff --git a/drivers/firmware/meson/Kconfig b/drivers/firmware/meson/Kconfig index 170d7e8bcdfb..2671dcd0ad92 100644 --- a/drivers/firmware/meson/Kconfig +++ b/drivers/firmware/meson/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only # # Amlogic Secure Monitor driver # diff --git a/drivers/firmware/meson/Makefile b/drivers/firmware/meson/Makefile index 9ab3884f96bc..c6c09483b622 100644 --- a/drivers/firmware/meson/Makefile +++ b/drivers/firmware/meson/Makefile @@ -1 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_MESON_SM) += meson_sm.o diff --git a/drivers/firmware/meson/meson_sm.c b/drivers/firmware/meson/meson_sm.c index 29fbc818a573..8d908a8e0d20 100644 --- a/drivers/firmware/meson/meson_sm.c +++ b/drivers/firmware/meson/meson_sm.c @@ -1,15 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Amlogic Secure Monitor driver * * Copyright (C) 2016 Endless Mobile, Inc. * Author: Carlo Caione <carlo@endlessm.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #define pr_fmt(fmt) "meson-sm: " fmt diff --git a/drivers/firmware/pcdp.c b/drivers/firmware/pcdp.c index e83d6aec0c13..4adeb7a2bdf5 100644 --- a/drivers/firmware/pcdp.c +++ b/drivers/firmware/pcdp.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Parse the EFI PCDP table to locate the console device. * @@ -5,10 +6,6 @@ * Khalid Aziz <khalid.aziz@hp.com> * Alex Williamson <alex.williamson@hp.com> * Bjorn Helgaas <bjorn.helgaas@hp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #include <linux/acpi.h> diff --git a/drivers/firmware/pcdp.h b/drivers/firmware/pcdp.h index e5530608e00d..ce75d1da9e84 100644 --- a/drivers/firmware/pcdp.h +++ b/drivers/firmware/pcdp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Definitions for PCDP-defined console devices * @@ -7,10 +8,6 @@ * (c) Copyright 2002, 2004 Hewlett-Packard Development Company, L.P. * Khalid Aziz <khalid.aziz@hp.com> * Bjorn Helgaas <bjorn.helgaas@hp.com> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #define PCDP_CONSOLE 0 diff --git a/drivers/firmware/psci/Kconfig b/drivers/firmware/psci/Kconfig new file mode 100644 index 000000000000..97944168b5e6 --- /dev/null +++ b/drivers/firmware/psci/Kconfig @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: GPL-2.0-only +config ARM_PSCI_FW + bool + +config ARM_PSCI_CHECKER + bool "ARM PSCI checker" + depends on ARM_PSCI_FW && HOTPLUG_CPU && CPU_IDLE && !TORTURE_TEST + help + Run the PSCI checker during startup. This checks that hotplug and + suspend operations work correctly when using PSCI. + + The torture tests may interfere with the PSCI checker by turning CPUs + on and off through hotplug, so for now torture tests and PSCI checker + are mutually exclusive. diff --git a/drivers/firmware/psci/Makefile b/drivers/firmware/psci/Makefile new file mode 100644 index 000000000000..1956b882470f --- /dev/null +++ b/drivers/firmware/psci/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +# +obj-$(CONFIG_ARM_PSCI_FW) += psci.o +obj-$(CONFIG_ARM_PSCI_CHECKER) += psci_checker.o diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci/psci.c index c80ec1d03274..f82ccd39a913 100644 --- a/drivers/firmware/psci.c +++ b/drivers/firmware/psci/psci.c @@ -1,12 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. * * Copyright (C) 2015 ARM Limited */ @@ -88,6 +81,7 @@ static u32 psci_function_id[PSCI_FN_MAX]; PSCI_1_0_EXT_POWER_STATE_TYPE_MASK) static u32 psci_cpu_suspend_feature; +static bool psci_system_reset2_supported; static inline bool psci_has_ext_power_state(void) { @@ -95,6 +89,11 @@ static inline bool psci_has_ext_power_state(void) PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK; } +static inline bool psci_has_osi_support(void) +{ + return psci_cpu_suspend_feature & PSCI_1_0_OS_INITIATED; +} + static inline bool psci_power_state_loses_context(u32 state) { const u32 mask = psci_has_ext_power_state() ? @@ -253,7 +252,17 @@ static int get_set_conduit_method(struct device_node *np) static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd) { - invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); + if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) && + psci_system_reset2_supported) { + /* + * reset_type[31] = 0 (architectural) + * reset_type[30:0] = 0 (SYSTEM_WARM_RESET) + * cookie = 0 (ignored by the implementation) + */ + invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), 0, 0, 0); + } else { + invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); + } } static void psci_sys_poweroff(void) @@ -270,9 +279,26 @@ static int __init psci_features(u32 psci_func_id) #ifdef CONFIG_CPU_IDLE static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state); +static int psci_dt_parse_state_node(struct device_node *np, u32 *state) +{ + int err = of_property_read_u32(np, "arm,psci-suspend-param", state); + + if (err) { + pr_warn("%pOF missing arm,psci-suspend-param property\n", np); + return err; + } + + if (!psci_power_state_is_valid(*state)) { + pr_warn("Invalid PSCI power state %#x\n", *state); + return -EINVAL; + } + + return 0; +} + static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) { - int i, ret, count = 0; + int i, ret = 0, count = 0; u32 *psci_states; struct device_node *state_node; @@ -291,29 +317,16 @@ static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) return -ENOMEM; for (i = 0; i < count; i++) { - u32 state; - state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); + ret = psci_dt_parse_state_node(state_node, &psci_states[i]); + of_node_put(state_node); - ret = of_property_read_u32(state_node, - "arm,psci-suspend-param", - &state); - if (ret) { - pr_warn(" * %pOF missing arm,psci-suspend-param property\n", - state_node); - of_node_put(state_node); + if (ret) goto free_mem; - } - of_node_put(state_node); - pr_debug("psci-power-state %#x index %d\n", state, i); - if (!psci_power_state_is_valid(state)) { - pr_warn("Invalid PSCI power state %#x\n", state); - ret = -EINVAL; - goto free_mem; - } - psci_states[i] = state; + pr_debug("psci-power-state %#x index %d\n", psci_states[i], i); } + /* Idle states parsed correctly, initialize per-cpu pointer */ per_cpu(psci_power_state, cpu) = psci_states; return 0; @@ -451,6 +464,16 @@ static const struct platform_suspend_ops psci_suspend_ops = { .enter = psci_system_suspend_enter, }; +static void __init psci_init_system_reset2(void) +{ + int ret; + + ret = psci_features(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2)); + + if (ret != PSCI_RET_NOT_SUPPORTED) + psci_system_reset2_supported = true; +} + static void __init psci_init_system_suspend(void) { int ret; @@ -588,6 +611,7 @@ static int __init psci_probe(void) psci_init_smccc(); psci_init_cpu_suspend(); psci_init_system_suspend(); + psci_init_system_reset2(); } return 0; @@ -605,9 +629,9 @@ static int __init psci_0_2_init(struct device_node *np) int err; err = get_set_conduit_method(np); - if (err) - goto out_put_node; + return err; + /* * Starting with v0.2, the PSCI specification introduced a call * (PSCI_VERSION) that allows probing the firmware version, so @@ -615,11 +639,7 @@ static int __init psci_0_2_init(struct device_node *np) * can be carried out according to the specific version reported * by firmware */ - err = psci_probe(); - -out_put_node: - of_node_put(np); - return err; + return psci_probe(); } /* @@ -631,9 +651,8 @@ static int __init psci_0_1_init(struct device_node *np) int err; err = get_set_conduit_method(np); - if (err) - goto out_put_node; + return err; pr_info("Using PSCI v0.1 Function IDs from DT\n"); @@ -657,15 +676,27 @@ static int __init psci_0_1_init(struct device_node *np) psci_ops.migrate = psci_migrate; } -out_put_node: - of_node_put(np); - return err; + return 0; +} + +static int __init psci_1_0_init(struct device_node *np) +{ + int err; + + err = psci_0_2_init(np); + if (err) + return err; + + if (psci_has_osi_support()) + pr_info("OSI mode supported.\n"); + + return 0; } static const struct of_device_id psci_of_match[] __initconst = { { .compatible = "arm,psci", .data = psci_0_1_init}, { .compatible = "arm,psci-0.2", .data = psci_0_2_init}, - { .compatible = "arm,psci-1.0", .data = psci_0_2_init}, + { .compatible = "arm,psci-1.0", .data = psci_1_0_init}, {}, }; @@ -674,6 +705,7 @@ int __init psci_dt_init(void) struct device_node *np; const struct of_device_id *matched_np; psci_initcall_t init_fn; + int ret; np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np); @@ -681,7 +713,10 @@ int __init psci_dt_init(void) return -ENODEV; init_fn = (psci_initcall_t)matched_np->data; - return init_fn(np); + ret = init_fn(np); + + of_node_put(np); + return ret; } #ifdef CONFIG_ACPI diff --git a/drivers/firmware/psci_checker.c b/drivers/firmware/psci/psci_checker.c index 346943657962..f3659443f8c2 100644 --- a/drivers/firmware/psci_checker.c +++ b/drivers/firmware/psci/psci_checker.c @@ -1,12 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only /* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. * * Copyright (C) 2016 ARM Limited */ @@ -366,16 +359,16 @@ static int suspend_test_thread(void *arg) for (;;) { /* Needs to be set first to avoid missing a wakeup. */ set_current_state(TASK_INTERRUPTIBLE); - if (kthread_should_stop()) { - __set_current_state(TASK_RUNNING); + if (kthread_should_park()) break; - } schedule(); } pr_info("CPU %d suspend test results: success %d, shallow states %d, errors %d\n", cpu, nb_suspend, nb_shallow_sleep, nb_err); + kthread_parkme(); + return nb_err; } @@ -440,8 +433,10 @@ static int suspend_tests(void) /* Stop and destroy all threads, get return status. */ - for (i = 0; i < nb_threads; ++i) + for (i = 0; i < nb_threads; ++i) { + err += kthread_park(threads[i]); err += kthread_stop(threads[i]); + } out: cpuidle_resume_and_unlock(); kfree(threads); diff --git a/drivers/firmware/qcom_scm-32.c b/drivers/firmware/qcom_scm-32.c index 4e24e591ae74..215061c581e1 100644 --- a/drivers/firmware/qcom_scm-32.c +++ b/drivers/firmware/qcom_scm-32.c @@ -1,19 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2010,2015, The Linux Foundation. All rights reserved. * Copyright (C) 2015 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. */ #include <linux/slab.h> diff --git a/drivers/firmware/qcom_scm-64.c b/drivers/firmware/qcom_scm-64.c index 688525dd4aee..91d5ad7cf58b 100644 --- a/drivers/firmware/qcom_scm-64.c +++ b/drivers/firmware/qcom_scm-64.c @@ -1,13 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only /* Copyright (c) 2015, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include <linux/io.h> diff --git a/drivers/firmware/qcom_scm.c b/drivers/firmware/qcom_scm.c index af4eee86919d..2ddc118dba1b 100644 --- a/drivers/firmware/qcom_scm.c +++ b/drivers/firmware/qcom_scm.c @@ -1,18 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Qualcomm SCM driver * * Copyright (c) 2010,2015, The Linux Foundation. All rights reserved. * Copyright (C) 2015 Linaro Ltd. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include <linux/platform_device.h> #include <linux/init.h> diff --git a/drivers/firmware/qcom_scm.h b/drivers/firmware/qcom_scm.h index dcd7f7917fc7..99506bd873c0 100644 --- a/drivers/firmware/qcom_scm.h +++ b/drivers/firmware/qcom_scm.h @@ -1,13 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* Copyright (c) 2010-2015, The Linux Foundation. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 and - * only version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __QCOM_SCM_INT_H #define __QCOM_SCM_INT_H diff --git a/drivers/firmware/raspberrypi.c b/drivers/firmware/raspberrypi.c index a13558154ac3..da26a584dca0 100644 --- a/drivers/firmware/raspberrypi.c +++ b/drivers/firmware/raspberrypi.c @@ -20,6 +20,7 @@ #define MBOX_CHAN_PROPERTY 8 static struct platform_device *rpi_hwmon; +static struct platform_device *rpi_clk; struct rpi_firmware { struct mbox_client cl; @@ -207,6 +208,12 @@ rpi_register_hwmon_driver(struct device *dev, struct rpi_firmware *fw) -1, NULL, 0); } +static void rpi_register_clk_driver(struct device *dev) +{ + rpi_clk = platform_device_register_data(dev, "raspberrypi-clk", + -1, NULL, 0); +} + static int rpi_firmware_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -234,16 +241,29 @@ static int rpi_firmware_probe(struct platform_device *pdev) rpi_firmware_print_firmware_revision(fw); rpi_register_hwmon_driver(dev, fw); + rpi_register_clk_driver(dev); return 0; } +static void rpi_firmware_shutdown(struct platform_device *pdev) +{ + struct rpi_firmware *fw = platform_get_drvdata(pdev); + + if (!fw) + return; + + rpi_firmware_property(fw, RPI_FIRMWARE_NOTIFY_REBOOT, NULL, 0); +} + static int rpi_firmware_remove(struct platform_device *pdev) { struct rpi_firmware *fw = platform_get_drvdata(pdev); platform_device_unregister(rpi_hwmon); rpi_hwmon = NULL; + platform_device_unregister(rpi_clk); + rpi_clk = NULL; mbox_free_channel(fw->chan); return 0; @@ -278,6 +298,7 @@ static struct platform_driver rpi_firmware_driver = { .of_match_table = rpi_firmware_of_match, }, .probe = rpi_firmware_probe, + .shutdown = rpi_firmware_shutdown, .remove = rpi_firmware_remove, }; module_platform_driver(rpi_firmware_driver); diff --git a/drivers/firmware/scpi_pm_domain.c b/drivers/firmware/scpi_pm_domain.c index 390aa13391e4..51201600d789 100644 --- a/drivers/firmware/scpi_pm_domain.c +++ b/drivers/firmware/scpi_pm_domain.c @@ -1,19 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * SCPI Generic power domain support. * * Copyright (C) 2016 ARM Ltd. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License along - * with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/err.h> diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig index ff2730d5c468..a887731f50d6 100644 --- a/drivers/firmware/tegra/Kconfig +++ b/drivers/firmware/tegra/Kconfig @@ -1,3 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only menu "Tegra firmware driver" config TEGRA_IVC diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile index 1b826dcca719..49c87e00fafb 100644 --- a/drivers/firmware/tegra/Makefile +++ b/drivers/firmware/tegra/Makefile @@ -1,4 +1,8 @@ +# SPDX-License-Identifier: GPL-2.0-only tegra-bpmp-y = bpmp.o +tegra-bpmp-$(CONFIG_ARCH_TEGRA_210_SOC) += bpmp-tegra210.o +tegra-bpmp-$(CONFIG_ARCH_TEGRA_186_SOC) += bpmp-tegra186.o +tegra-bpmp-$(CONFIG_ARCH_TEGRA_194_SOC) += bpmp-tegra186.o tegra-bpmp-$(CONFIG_DEBUG_FS) += bpmp-debugfs.o obj-$(CONFIG_TEGRA_BPMP) += tegra-bpmp.o obj-$(CONFIG_TEGRA_IVC) += ivc.o diff --git a/drivers/firmware/tegra/bpmp-debugfs.c b/drivers/firmware/tegra/bpmp-debugfs.c index a84df1a8ca2b..636b40d4364d 100644 --- a/drivers/firmware/tegra/bpmp-debugfs.c +++ b/drivers/firmware/tegra/bpmp-debugfs.c @@ -1,15 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2017, NVIDIA CORPORATION. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * */ #include <linux/debugfs.h> #include <linux/dma-mapping.h> diff --git a/drivers/firmware/tegra/bpmp-private.h b/drivers/firmware/tegra/bpmp-private.h new file mode 100644 index 000000000000..54d560c48398 --- /dev/null +++ b/drivers/firmware/tegra/bpmp-private.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2018, NVIDIA CORPORATION. + */ + +#ifndef __FIRMWARE_TEGRA_BPMP_PRIVATE_H +#define __FIRMWARE_TEGRA_BPMP_PRIVATE_H + +#include <soc/tegra/bpmp.h> + +struct tegra_bpmp_ops { + int (*init)(struct tegra_bpmp *bpmp); + void (*deinit)(struct tegra_bpmp *bpmp); + bool (*is_response_ready)(struct tegra_bpmp_channel *channel); + bool (*is_request_ready)(struct tegra_bpmp_channel *channel); + int (*ack_response)(struct tegra_bpmp_channel *channel); + int (*ack_request)(struct tegra_bpmp_channel *channel); + bool (*is_response_channel_free)(struct tegra_bpmp_channel *channel); + bool (*is_request_channel_free)(struct tegra_bpmp_channel *channel); + int (*post_response)(struct tegra_bpmp_channel *channel); + int (*post_request)(struct tegra_bpmp_channel *channel); + int (*ring_doorbell)(struct tegra_bpmp *bpmp); + int (*resume)(struct tegra_bpmp *bpmp); +}; + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ + IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) +extern const struct tegra_bpmp_ops tegra186_bpmp_ops; +#endif +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) +extern const struct tegra_bpmp_ops tegra210_bpmp_ops; +#endif + +#endif diff --git a/drivers/firmware/tegra/bpmp-tegra186.c b/drivers/firmware/tegra/bpmp-tegra186.c new file mode 100644 index 000000000000..ea308751635f --- /dev/null +++ b/drivers/firmware/tegra/bpmp-tegra186.c @@ -0,0 +1,305 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, NVIDIA CORPORATION. + */ + +#include <linux/genalloc.h> +#include <linux/mailbox_client.h> +#include <linux/platform_device.h> + +#include <soc/tegra/bpmp.h> +#include <soc/tegra/bpmp-abi.h> +#include <soc/tegra/ivc.h> + +#include "bpmp-private.h" + +struct tegra186_bpmp { + struct tegra_bpmp *parent; + + struct { + struct gen_pool *pool; + dma_addr_t phys; + void *virt; + } tx, rx; + + struct { + struct mbox_client client; + struct mbox_chan *channel; + } mbox; +}; + +static inline struct tegra_bpmp * +mbox_client_to_bpmp(struct mbox_client *client) +{ + struct tegra186_bpmp *priv; + + priv = container_of(client, struct tegra186_bpmp, mbox.client); + + return priv->parent; +} + +static bool tegra186_bpmp_is_message_ready(struct tegra_bpmp_channel *channel) +{ + void *frame; + + frame = tegra_ivc_read_get_next_frame(channel->ivc); + if (IS_ERR(frame)) { + channel->ib = NULL; + return false; + } + + channel->ib = frame; + + return true; +} + +static bool tegra186_bpmp_is_channel_free(struct tegra_bpmp_channel *channel) +{ + void *frame; + + frame = tegra_ivc_write_get_next_frame(channel->ivc); + if (IS_ERR(frame)) { + channel->ob = NULL; + return false; + } + + channel->ob = frame; + + return true; +} + +static int tegra186_bpmp_ack_message(struct tegra_bpmp_channel *channel) +{ + return tegra_ivc_read_advance(channel->ivc); +} + +static int tegra186_bpmp_post_message(struct tegra_bpmp_channel *channel) +{ + return tegra_ivc_write_advance(channel->ivc); +} + +static int tegra186_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) +{ + struct tegra186_bpmp *priv = bpmp->priv; + int err; + + err = mbox_send_message(priv->mbox.channel, NULL); + if (err < 0) + return err; + + mbox_client_txdone(priv->mbox.channel, 0); + + return 0; +} + +static void tegra186_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data) +{ + struct tegra_bpmp *bpmp = data; + struct tegra186_bpmp *priv = bpmp->priv; + + if (WARN_ON(priv->mbox.channel == NULL)) + return; + + tegra186_bpmp_ring_doorbell(bpmp); +} + +static int tegra186_bpmp_channel_init(struct tegra_bpmp_channel *channel, + struct tegra_bpmp *bpmp, + unsigned int index) +{ + struct tegra186_bpmp *priv = bpmp->priv; + size_t message_size, queue_size; + unsigned int offset; + int err; + + channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc), + GFP_KERNEL); + if (!channel->ivc) + return -ENOMEM; + + message_size = tegra_ivc_align(MSG_MIN_SZ); + queue_size = tegra_ivc_total_queue_size(message_size); + offset = queue_size * index; + + err = tegra_ivc_init(channel->ivc, NULL, + priv->rx.virt + offset, priv->rx.phys + offset, + priv->tx.virt + offset, priv->tx.phys + offset, + 1, message_size, tegra186_bpmp_ivc_notify, + bpmp); + if (err < 0) { + dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n", + index, err); + return err; + } + + init_completion(&channel->completion); + channel->bpmp = bpmp; + + return 0; +} + +static void tegra186_bpmp_channel_reset(struct tegra_bpmp_channel *channel) +{ + /* reset the channel state */ + tegra_ivc_reset(channel->ivc); + + /* sync the channel state with BPMP */ + while (tegra_ivc_notified(channel->ivc)) + ; +} + +static void tegra186_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel) +{ + tegra_ivc_cleanup(channel->ivc); +} + +static void mbox_handle_rx(struct mbox_client *client, void *data) +{ + struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client); + + tegra_bpmp_handle_rx(bpmp); +} + +static int tegra186_bpmp_init(struct tegra_bpmp *bpmp) +{ + struct tegra186_bpmp *priv; + unsigned int i; + int err; + + priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + bpmp->priv = priv; + priv->parent = bpmp; + + priv->tx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 0); + if (!priv->tx.pool) { + dev_err(bpmp->dev, "TX shmem pool not found\n"); + return -ENOMEM; + } + + priv->tx.virt = gen_pool_dma_alloc(priv->tx.pool, 4096, &priv->tx.phys); + if (!priv->tx.virt) { + dev_err(bpmp->dev, "failed to allocate from TX pool\n"); + return -ENOMEM; + } + + priv->rx.pool = of_gen_pool_get(bpmp->dev->of_node, "shmem", 1); + if (!priv->rx.pool) { + dev_err(bpmp->dev, "RX shmem pool not found\n"); + err = -ENOMEM; + goto free_tx; + } + + priv->rx.virt = gen_pool_dma_alloc(priv->rx.pool, 4096, &priv->rx.phys); + if (!priv->rx.virt) { + dev_err(bpmp->dev, "failed to allocate from RX pool\n"); + err = -ENOMEM; + goto free_tx; + } + + err = tegra186_bpmp_channel_init(bpmp->tx_channel, bpmp, + bpmp->soc->channels.cpu_tx.offset); + if (err < 0) + goto free_rx; + + err = tegra186_bpmp_channel_init(bpmp->rx_channel, bpmp, + bpmp->soc->channels.cpu_rx.offset); + if (err < 0) + goto cleanup_tx_channel; + + for (i = 0; i < bpmp->threaded.count; i++) { + unsigned int index = bpmp->soc->channels.thread.offset + i; + + err = tegra186_bpmp_channel_init(&bpmp->threaded_channels[i], + bpmp, index); + if (err < 0) + goto cleanup_channels; + } + + /* mbox registration */ + priv->mbox.client.dev = bpmp->dev; + priv->mbox.client.rx_callback = mbox_handle_rx; + priv->mbox.client.tx_block = false; + priv->mbox.client.knows_txdone = false; + + priv->mbox.channel = mbox_request_channel(&priv->mbox.client, 0); + if (IS_ERR(priv->mbox.channel)) { + err = PTR_ERR(priv->mbox.channel); + dev_err(bpmp->dev, "failed to get HSP mailbox: %d\n", err); + goto cleanup_channels; + } + + tegra186_bpmp_channel_reset(bpmp->tx_channel); + tegra186_bpmp_channel_reset(bpmp->rx_channel); + + for (i = 0; i < bpmp->threaded.count; i++) + tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]); + + return 0; + +cleanup_channels: + for (i = 0; i < bpmp->threaded.count; i++) { + if (!bpmp->threaded_channels[i].bpmp) + continue; + + tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); + } + + tegra186_bpmp_channel_cleanup(bpmp->rx_channel); +cleanup_tx_channel: + tegra186_bpmp_channel_cleanup(bpmp->tx_channel); +free_rx: + gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096); +free_tx: + gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096); + + return err; +} + +static void tegra186_bpmp_deinit(struct tegra_bpmp *bpmp) +{ + struct tegra186_bpmp *priv = bpmp->priv; + unsigned int i; + + mbox_free_channel(priv->mbox.channel); + + for (i = 0; i < bpmp->threaded.count; i++) + tegra186_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); + + tegra186_bpmp_channel_cleanup(bpmp->rx_channel); + tegra186_bpmp_channel_cleanup(bpmp->tx_channel); + + gen_pool_free(priv->rx.pool, (unsigned long)priv->rx.virt, 4096); + gen_pool_free(priv->tx.pool, (unsigned long)priv->tx.virt, 4096); +} + +static int tegra186_bpmp_resume(struct tegra_bpmp *bpmp) +{ + unsigned int i; + + /* reset message channels */ + tegra186_bpmp_channel_reset(bpmp->tx_channel); + tegra186_bpmp_channel_reset(bpmp->rx_channel); + + for (i = 0; i < bpmp->threaded.count; i++) + tegra186_bpmp_channel_reset(&bpmp->threaded_channels[i]); + + return 0; +} + +const struct tegra_bpmp_ops tegra186_bpmp_ops = { + .init = tegra186_bpmp_init, + .deinit = tegra186_bpmp_deinit, + .is_response_ready = tegra186_bpmp_is_message_ready, + .is_request_ready = tegra186_bpmp_is_message_ready, + .ack_response = tegra186_bpmp_ack_message, + .ack_request = tegra186_bpmp_ack_message, + .is_response_channel_free = tegra186_bpmp_is_channel_free, + .is_request_channel_free = tegra186_bpmp_is_channel_free, + .post_response = tegra186_bpmp_post_message, + .post_request = tegra186_bpmp_post_message, + .ring_doorbell = tegra186_bpmp_ring_doorbell, + .resume = tegra186_bpmp_resume, +}; diff --git a/drivers/firmware/tegra/bpmp-tegra210.c b/drivers/firmware/tegra/bpmp-tegra210.c new file mode 100644 index 000000000000..ae15940a078e --- /dev/null +++ b/drivers/firmware/tegra/bpmp-tegra210.c @@ -0,0 +1,243 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2018, NVIDIA CORPORATION. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/io.h> +#include <linux/of.h> +#include <linux/platform_device.h> + +#include <soc/tegra/bpmp.h> + +#include "bpmp-private.h" + +#define TRIGGER_OFFSET 0x000 +#define RESULT_OFFSET(id) (0xc00 + id * 4) +#define TRIGGER_ID_SHIFT 16 +#define TRIGGER_CMD_GET 4 + +#define STA_OFFSET 0 +#define SET_OFFSET 4 +#define CLR_OFFSET 8 + +#define CH_MASK(ch) (0x3 << ((ch) * 2)) +#define SL_SIGL(ch) (0x0 << ((ch) * 2)) +#define SL_QUED(ch) (0x1 << ((ch) * 2)) +#define MA_FREE(ch) (0x2 << ((ch) * 2)) +#define MA_ACKD(ch) (0x3 << ((ch) * 2)) + +struct tegra210_bpmp { + void __iomem *atomics; + void __iomem *arb_sema; + struct irq_data *tx_irq_data; +}; + +static u32 bpmp_channel_status(struct tegra_bpmp *bpmp, unsigned int index) +{ + struct tegra210_bpmp *priv = bpmp->priv; + + return __raw_readl(priv->arb_sema + STA_OFFSET) & CH_MASK(index); +} + +static bool tegra210_bpmp_is_response_ready(struct tegra_bpmp_channel *channel) +{ + unsigned int index = channel->index; + + return bpmp_channel_status(channel->bpmp, index) == MA_ACKD(index); +} + +static bool tegra210_bpmp_is_request_ready(struct tegra_bpmp_channel *channel) +{ + unsigned int index = channel->index; + + return bpmp_channel_status(channel->bpmp, index) == SL_SIGL(index); +} + +static bool +tegra210_bpmp_is_request_channel_free(struct tegra_bpmp_channel *channel) +{ + unsigned int index = channel->index; + + return bpmp_channel_status(channel->bpmp, index) == MA_FREE(index); +} + +static bool +tegra210_bpmp_is_response_channel_free(struct tegra_bpmp_channel *channel) +{ + unsigned int index = channel->index; + + return bpmp_channel_status(channel->bpmp, index) == SL_QUED(index); +} + +static int tegra210_bpmp_post_request(struct tegra_bpmp_channel *channel) +{ + struct tegra210_bpmp *priv = channel->bpmp->priv; + + __raw_writel(CH_MASK(channel->index), priv->arb_sema + CLR_OFFSET); + + return 0; +} + +static int tegra210_bpmp_post_response(struct tegra_bpmp_channel *channel) +{ + struct tegra210_bpmp *priv = channel->bpmp->priv; + + __raw_writel(MA_ACKD(channel->index), priv->arb_sema + SET_OFFSET); + + return 0; +} + +static int tegra210_bpmp_ack_response(struct tegra_bpmp_channel *channel) +{ + struct tegra210_bpmp *priv = channel->bpmp->priv; + + __raw_writel(MA_ACKD(channel->index) ^ MA_FREE(channel->index), + priv->arb_sema + CLR_OFFSET); + + return 0; +} + +static int tegra210_bpmp_ack_request(struct tegra_bpmp_channel *channel) +{ + struct tegra210_bpmp *priv = channel->bpmp->priv; + + __raw_writel(SL_QUED(channel->index), priv->arb_sema + SET_OFFSET); + + return 0; +} + +static int tegra210_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) +{ + struct tegra210_bpmp *priv = bpmp->priv; + struct irq_data *irq_data = priv->tx_irq_data; + + /* + * Tegra Legacy Interrupt Controller (LIC) is used to notify BPMP of + * available messages + */ + if (irq_data->chip->irq_retrigger) + return irq_data->chip->irq_retrigger(irq_data); + + return -EINVAL; +} + +static irqreturn_t rx_irq(int irq, void *data) +{ + struct tegra_bpmp *bpmp = data; + + tegra_bpmp_handle_rx(bpmp); + + return IRQ_HANDLED; +} + +static int tegra210_bpmp_channel_init(struct tegra_bpmp_channel *channel, + struct tegra_bpmp *bpmp, + unsigned int index) +{ + struct tegra210_bpmp *priv = bpmp->priv; + u32 address; + void *p; + + /* Retrieve channel base address from BPMP */ + writel(index << TRIGGER_ID_SHIFT | TRIGGER_CMD_GET, + priv->atomics + TRIGGER_OFFSET); + address = readl(priv->atomics + RESULT_OFFSET(index)); + + p = devm_ioremap(bpmp->dev, address, 0x80); + if (!p) + return -ENOMEM; + + channel->ib = p; + channel->ob = p; + channel->index = index; + init_completion(&channel->completion); + channel->bpmp = bpmp; + + return 0; +} + +static int tegra210_bpmp_init(struct tegra_bpmp *bpmp) +{ + struct platform_device *pdev = to_platform_device(bpmp->dev); + struct tegra210_bpmp *priv; + struct resource *res; + unsigned int i; + int err; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + bpmp->priv = priv; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + priv->atomics = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->atomics)) + return PTR_ERR(priv->atomics); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + priv->arb_sema = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(priv->arb_sema)) + return PTR_ERR(priv->arb_sema); + + err = tegra210_bpmp_channel_init(bpmp->tx_channel, bpmp, + bpmp->soc->channels.cpu_tx.offset); + if (err < 0) + return err; + + err = tegra210_bpmp_channel_init(bpmp->rx_channel, bpmp, + bpmp->soc->channels.cpu_rx.offset); + if (err < 0) + return err; + + for (i = 0; i < bpmp->threaded.count; i++) { + unsigned int index = bpmp->soc->channels.thread.offset + i; + + err = tegra210_bpmp_channel_init(&bpmp->threaded_channels[i], + bpmp, index); + if (err < 0) + return err; + } + + err = platform_get_irq_byname(pdev, "tx"); + if (err < 0) { + dev_err(&pdev->dev, "failed to get TX IRQ: %d\n", err); + return err; + } + + priv->tx_irq_data = irq_get_irq_data(err); + if (!priv->tx_irq_data) { + dev_err(&pdev->dev, "failed to get IRQ data for TX IRQ\n"); + return err; + } + + err = platform_get_irq_byname(pdev, "rx"); + if (err < 0) { + dev_err(&pdev->dev, "failed to get rx IRQ: %d\n", err); + return err; + } + + err = devm_request_irq(&pdev->dev, err, rx_irq, + IRQF_NO_SUSPEND, dev_name(&pdev->dev), bpmp); + if (err < 0) { + dev_err(&pdev->dev, "failed to request IRQ: %d\n", err); + return err; + } + + return 0; +} + +const struct tegra_bpmp_ops tegra210_bpmp_ops = { + .init = tegra210_bpmp_init, + .is_response_ready = tegra210_bpmp_is_response_ready, + .is_request_ready = tegra210_bpmp_is_request_ready, + .ack_response = tegra210_bpmp_ack_response, + .ack_request = tegra210_bpmp_ack_request, + .is_response_channel_free = tegra210_bpmp_is_response_channel_free, + .is_request_channel_free = tegra210_bpmp_is_request_channel_free, + .post_response = tegra210_bpmp_post_response, + .post_request = tegra210_bpmp_post_request, + .ring_doorbell = tegra210_bpmp_ring_doorbell, +}; diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c index 689478b92bce..19c56133234b 100644 --- a/drivers/firmware/tegra/bpmp.c +++ b/drivers/firmware/tegra/bpmp.c @@ -1,14 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2016, NVIDIA CORPORATION. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. */ #include <linux/clk/tegra.h> @@ -26,6 +18,8 @@ #include <soc/tegra/bpmp-abi.h> #include <soc/tegra/ivc.h> +#include "bpmp-private.h" + #define MSG_ACK BIT(0) #define MSG_RING BIT(1) #define TAG_SZ 32 @@ -36,6 +30,14 @@ mbox_client_to_bpmp(struct mbox_client *client) return container_of(client, struct tegra_bpmp, mbox.client); } +static inline const struct tegra_bpmp_ops * +channel_to_ops(struct tegra_bpmp_channel *channel) +{ + struct tegra_bpmp *bpmp = channel->bpmp; + + return bpmp->soc->ops; +} + struct tegra_bpmp *tegra_bpmp_get(struct device *dev) { struct platform_device *pdev; @@ -96,22 +98,21 @@ static bool tegra_bpmp_message_valid(const struct tegra_bpmp_message *msg) (msg->rx.size == 0 || msg->rx.data); } -static bool tegra_bpmp_master_acked(struct tegra_bpmp_channel *channel) +static bool tegra_bpmp_is_response_ready(struct tegra_bpmp_channel *channel) { - void *frame; + const struct tegra_bpmp_ops *ops = channel_to_ops(channel); - frame = tegra_ivc_read_get_next_frame(channel->ivc); - if (IS_ERR(frame)) { - channel->ib = NULL; - return false; - } + return ops->is_response_ready(channel); +} - channel->ib = frame; +static bool tegra_bpmp_is_request_ready(struct tegra_bpmp_channel *channel) +{ + const struct tegra_bpmp_ops *ops = channel_to_ops(channel); - return true; + return ops->is_request_ready(channel); } -static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel) +static int tegra_bpmp_wait_response(struct tegra_bpmp_channel *channel) { unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout; ktime_t end; @@ -119,29 +120,45 @@ static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel) end = ktime_add_us(ktime_get(), timeout); do { - if (tegra_bpmp_master_acked(channel)) + if (tegra_bpmp_is_response_ready(channel)) return 0; } while (ktime_before(ktime_get(), end)); return -ETIMEDOUT; } -static bool tegra_bpmp_master_free(struct tegra_bpmp_channel *channel) +static int tegra_bpmp_ack_response(struct tegra_bpmp_channel *channel) { - void *frame; + const struct tegra_bpmp_ops *ops = channel_to_ops(channel); - frame = tegra_ivc_write_get_next_frame(channel->ivc); - if (IS_ERR(frame)) { - channel->ob = NULL; - return false; - } + return ops->ack_response(channel); +} - channel->ob = frame; +static int tegra_bpmp_ack_request(struct tegra_bpmp_channel *channel) +{ + const struct tegra_bpmp_ops *ops = channel_to_ops(channel); - return true; + return ops->ack_request(channel); } -static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel) +static bool +tegra_bpmp_is_request_channel_free(struct tegra_bpmp_channel *channel) +{ + const struct tegra_bpmp_ops *ops = channel_to_ops(channel); + + return ops->is_request_channel_free(channel); +} + +static bool +tegra_bpmp_is_response_channel_free(struct tegra_bpmp_channel *channel) +{ + const struct tegra_bpmp_ops *ops = channel_to_ops(channel); + + return ops->is_response_channel_free(channel); +} + +static int +tegra_bpmp_wait_request_channel_free(struct tegra_bpmp_channel *channel) { unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout; ktime_t start, now; @@ -149,7 +166,7 @@ static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel) start = ns_to_ktime(local_clock()); do { - if (tegra_bpmp_master_free(channel)) + if (tegra_bpmp_is_request_channel_free(channel)) return 0; now = ns_to_ktime(local_clock()); @@ -158,6 +175,25 @@ static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel) return -ETIMEDOUT; } +static int tegra_bpmp_post_request(struct tegra_bpmp_channel *channel) +{ + const struct tegra_bpmp_ops *ops = channel_to_ops(channel); + + return ops->post_request(channel); +} + +static int tegra_bpmp_post_response(struct tegra_bpmp_channel *channel) +{ + const struct tegra_bpmp_ops *ops = channel_to_ops(channel); + + return ops->post_response(channel); +} + +static int tegra_bpmp_ring_doorbell(struct tegra_bpmp *bpmp) +{ + return bpmp->soc->ops->ring_doorbell(bpmp); +} + static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, void *data, size_t size, int *ret) { @@ -166,7 +202,7 @@ static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel, if (data && size > 0) memcpy(data, channel->ib->data, size); - err = tegra_ivc_read_advance(channel->ivc); + err = tegra_bpmp_ack_response(channel); if (err < 0) return err; @@ -210,7 +246,7 @@ static ssize_t __tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel, if (data && size > 0) memcpy(channel->ob->data, data, size); - return tegra_ivc_write_advance(channel->ivc); + return tegra_bpmp_post_request(channel); } static struct tegra_bpmp_channel * @@ -238,7 +274,7 @@ tegra_bpmp_write_threaded(struct tegra_bpmp *bpmp, unsigned int mrq, channel = &bpmp->threaded_channels[index]; - if (!tegra_bpmp_master_free(channel)) { + if (!tegra_bpmp_is_request_channel_free(channel)) { err = -EBUSY; goto unlock; } @@ -270,7 +306,7 @@ static ssize_t tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel, { int err; - err = tegra_bpmp_wait_master_free(channel); + err = tegra_bpmp_wait_request_channel_free(channel); if (err < 0) return err; @@ -302,13 +338,11 @@ int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp, spin_unlock(&bpmp->atomic_tx_lock); - err = mbox_send_message(bpmp->mbox.channel, NULL); + err = tegra_bpmp_ring_doorbell(bpmp); if (err < 0) return err; - mbox_client_txdone(bpmp->mbox.channel, 0); - - err = tegra_bpmp_wait_ack(channel); + err = tegra_bpmp_wait_response(channel); if (err < 0) return err; @@ -335,12 +369,10 @@ int tegra_bpmp_transfer(struct tegra_bpmp *bpmp, if (IS_ERR(channel)) return PTR_ERR(channel); - err = mbox_send_message(bpmp->mbox.channel, NULL); + err = tegra_bpmp_ring_doorbell(bpmp); if (err < 0) return err; - mbox_client_txdone(bpmp->mbox.channel, 0); - timeout = usecs_to_jiffies(bpmp->soc->channels.thread.timeout); err = wait_for_completion_timeout(&channel->completion, timeout); @@ -369,38 +401,34 @@ void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel, int code, { unsigned long flags = channel->ib->flags; struct tegra_bpmp *bpmp = channel->bpmp; - struct tegra_bpmp_mb_data *frame; int err; if (WARN_ON(size > MSG_DATA_MIN_SZ)) return; - err = tegra_ivc_read_advance(channel->ivc); + err = tegra_bpmp_ack_request(channel); if (WARN_ON(err < 0)) return; if ((flags & MSG_ACK) == 0) return; - frame = tegra_ivc_write_get_next_frame(channel->ivc); - if (WARN_ON(IS_ERR(frame))) + if (WARN_ON(!tegra_bpmp_is_response_channel_free(channel))) return; - frame->code = code; + channel->ob->code = code; if (data && size > 0) - memcpy(frame->data, data, size); + memcpy(channel->ob->data, data, size); - err = tegra_ivc_write_advance(channel->ivc); + err = tegra_bpmp_post_response(channel); if (WARN_ON(err < 0)) return; if (flags & MSG_RING) { - err = mbox_send_message(bpmp->mbox.channel, NULL); + err = tegra_bpmp_ring_doorbell(bpmp); if (WARN_ON(err < 0)) return; - - mbox_client_txdone(bpmp->mbox.channel, 0); } } EXPORT_SYMBOL_GPL(tegra_bpmp_mrq_return); @@ -627,9 +655,8 @@ static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel) complete(&channel->completion); } -static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) +void tegra_bpmp_handle_rx(struct tegra_bpmp *bpmp) { - struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client); struct tegra_bpmp_channel *channel; unsigned int i, count; unsigned long *busy; @@ -638,7 +665,7 @@ static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) count = bpmp->soc->channels.thread.count; busy = bpmp->threaded.busy; - if (tegra_bpmp_master_acked(channel)) + if (tegra_bpmp_is_request_ready(channel)) tegra_bpmp_handle_mrq(bpmp, channel->ib->code, channel); spin_lock(&bpmp->lock); @@ -648,7 +675,7 @@ static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) channel = &bpmp->threaded_channels[i]; - if (tegra_bpmp_master_acked(channel)) { + if (tegra_bpmp_is_response_ready(channel)) { tegra_bpmp_channel_signal(channel); clear_bit(i, busy); } @@ -657,74 +684,9 @@ static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data) spin_unlock(&bpmp->lock); } -static void tegra_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data) -{ - struct tegra_bpmp *bpmp = data; - int err; - - if (WARN_ON(bpmp->mbox.channel == NULL)) - return; - - err = mbox_send_message(bpmp->mbox.channel, NULL); - if (err < 0) - return; - - mbox_client_txdone(bpmp->mbox.channel, 0); -} - -static int tegra_bpmp_channel_init(struct tegra_bpmp_channel *channel, - struct tegra_bpmp *bpmp, - unsigned int index) -{ - size_t message_size, queue_size; - unsigned int offset; - int err; - - channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc), - GFP_KERNEL); - if (!channel->ivc) - return -ENOMEM; - - message_size = tegra_ivc_align(MSG_MIN_SZ); - queue_size = tegra_ivc_total_queue_size(message_size); - offset = queue_size * index; - - err = tegra_ivc_init(channel->ivc, NULL, - bpmp->rx.virt + offset, bpmp->rx.phys + offset, - bpmp->tx.virt + offset, bpmp->tx.phys + offset, - 1, message_size, tegra_bpmp_ivc_notify, - bpmp); - if (err < 0) { - dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n", - index, err); - return err; - } - - init_completion(&channel->completion); - channel->bpmp = bpmp; - - return 0; -} - -static void tegra_bpmp_channel_reset(struct tegra_bpmp_channel *channel) -{ - /* reset the channel state */ - tegra_ivc_reset(channel->ivc); - - /* sync the channel state with BPMP */ - while (tegra_ivc_notified(channel->ivc)) - ; -} - -static void tegra_bpmp_channel_cleanup(struct tegra_bpmp_channel *channel) -{ - tegra_ivc_cleanup(channel->ivc); -} - static int tegra_bpmp_probe(struct platform_device *pdev) { struct tegra_bpmp *bpmp; - unsigned int i; char tag[TAG_SZ]; size_t size; int err; @@ -736,32 +698,6 @@ static int tegra_bpmp_probe(struct platform_device *pdev) bpmp->soc = of_device_get_match_data(&pdev->dev); bpmp->dev = &pdev->dev; - bpmp->tx.pool = of_gen_pool_get(pdev->dev.of_node, "shmem", 0); - if (!bpmp->tx.pool) { - dev_err(&pdev->dev, "TX shmem pool not found\n"); - return -ENOMEM; - } - - bpmp->tx.virt = gen_pool_dma_alloc(bpmp->tx.pool, 4096, &bpmp->tx.phys); - if (!bpmp->tx.virt) { - dev_err(&pdev->dev, "failed to allocate from TX pool\n"); - return -ENOMEM; - } - - bpmp->rx.pool = of_gen_pool_get(pdev->dev.of_node, "shmem", 1); - if (!bpmp->rx.pool) { - dev_err(&pdev->dev, "RX shmem pool not found\n"); - err = -ENOMEM; - goto free_tx; - } - - bpmp->rx.virt = gen_pool_dma_alloc(bpmp->rx.pool, 4096, &bpmp->rx.phys); - if (!bpmp->rx.virt) { - dev_err(&pdev->dev, "failed to allocate from RX pool\n"); - err = -ENOMEM; - goto free_tx; - } - INIT_LIST_HEAD(&bpmp->mrqs); spin_lock_init(&bpmp->lock); @@ -771,81 +707,38 @@ static int tegra_bpmp_probe(struct platform_device *pdev) size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long); bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!bpmp->threaded.allocated) { - err = -ENOMEM; - goto free_rx; - } + if (!bpmp->threaded.allocated) + return -ENOMEM; bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!bpmp->threaded.busy) { - err = -ENOMEM; - goto free_rx; - } + if (!bpmp->threaded.busy) + return -ENOMEM; spin_lock_init(&bpmp->atomic_tx_lock); bpmp->tx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->tx_channel), GFP_KERNEL); - if (!bpmp->tx_channel) { - err = -ENOMEM; - goto free_rx; - } + if (!bpmp->tx_channel) + return -ENOMEM; bpmp->rx_channel = devm_kzalloc(&pdev->dev, sizeof(*bpmp->rx_channel), GFP_KERNEL); - if (!bpmp->rx_channel) { - err = -ENOMEM; - goto free_rx; - } + if (!bpmp->rx_channel) + return -ENOMEM; bpmp->threaded_channels = devm_kcalloc(&pdev->dev, bpmp->threaded.count, sizeof(*bpmp->threaded_channels), GFP_KERNEL); - if (!bpmp->threaded_channels) { - err = -ENOMEM; - goto free_rx; - } - - err = tegra_bpmp_channel_init(bpmp->tx_channel, bpmp, - bpmp->soc->channels.cpu_tx.offset); - if (err < 0) - goto free_rx; + if (!bpmp->threaded_channels) + return -ENOMEM; - err = tegra_bpmp_channel_init(bpmp->rx_channel, bpmp, - bpmp->soc->channels.cpu_rx.offset); + err = bpmp->soc->ops->init(bpmp); if (err < 0) - goto cleanup_tx_channel; - - for (i = 0; i < bpmp->threaded.count; i++) { - err = tegra_bpmp_channel_init( - &bpmp->threaded_channels[i], bpmp, - bpmp->soc->channels.thread.offset + i); - if (err < 0) - goto cleanup_threaded_channels; - } - - /* mbox registration */ - bpmp->mbox.client.dev = &pdev->dev; - bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx; - bpmp->mbox.client.tx_block = false; - bpmp->mbox.client.knows_txdone = false; - - bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0); - if (IS_ERR(bpmp->mbox.channel)) { - err = PTR_ERR(bpmp->mbox.channel); - dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err); - goto cleanup_threaded_channels; - } - - /* reset message channels */ - tegra_bpmp_channel_reset(bpmp->tx_channel); - tegra_bpmp_channel_reset(bpmp->rx_channel); - for (i = 0; i < bpmp->threaded.count; i++) - tegra_bpmp_channel_reset(&bpmp->threaded_channels[i]); + return err; err = tegra_bpmp_request_mrq(bpmp, MRQ_PING, tegra_bpmp_mrq_handle_ping, bpmp); if (err < 0) - goto free_mbox; + goto deinit; err = tegra_bpmp_ping(bpmp); if (err < 0) { @@ -867,17 +760,23 @@ static int tegra_bpmp_probe(struct platform_device *pdev) if (err < 0) goto free_mrq; - err = tegra_bpmp_init_clocks(bpmp); - if (err < 0) - goto free_mrq; + if (of_find_property(pdev->dev.of_node, "#clock-cells", NULL)) { + err = tegra_bpmp_init_clocks(bpmp); + if (err < 0) + goto free_mrq; + } - err = tegra_bpmp_init_resets(bpmp); - if (err < 0) - goto free_mrq; + if (of_find_property(pdev->dev.of_node, "#reset-cells", NULL)) { + err = tegra_bpmp_init_resets(bpmp); + if (err < 0) + goto free_mrq; + } - err = tegra_bpmp_init_powergates(bpmp); - if (err < 0) - goto free_mrq; + if (of_find_property(pdev->dev.of_node, "#power-domain-cells", NULL)) { + err = tegra_bpmp_init_powergates(bpmp); + if (err < 0) + goto free_mrq; + } err = tegra_bpmp_init_debugfs(bpmp); if (err < 0) @@ -887,41 +786,29 @@ static int tegra_bpmp_probe(struct platform_device *pdev) free_mrq: tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp); -free_mbox: - mbox_free_channel(bpmp->mbox.channel); -cleanup_threaded_channels: - for (i = 0; i < bpmp->threaded.count; i++) { - if (bpmp->threaded_channels[i].bpmp) - tegra_bpmp_channel_cleanup(&bpmp->threaded_channels[i]); - } +deinit: + if (bpmp->soc->ops->deinit) + bpmp->soc->ops->deinit(bpmp); - tegra_bpmp_channel_cleanup(bpmp->rx_channel); -cleanup_tx_channel: - tegra_bpmp_channel_cleanup(bpmp->tx_channel); -free_rx: - gen_pool_free(bpmp->rx.pool, (unsigned long)bpmp->rx.virt, 4096); -free_tx: - gen_pool_free(bpmp->tx.pool, (unsigned long)bpmp->tx.virt, 4096); return err; } static int __maybe_unused tegra_bpmp_resume(struct device *dev) { struct tegra_bpmp *bpmp = dev_get_drvdata(dev); - unsigned int i; - - /* reset message channels */ - tegra_bpmp_channel_reset(bpmp->tx_channel); - tegra_bpmp_channel_reset(bpmp->rx_channel); - for (i = 0; i < bpmp->threaded.count; i++) - tegra_bpmp_channel_reset(&bpmp->threaded_channels[i]); - - return 0; + if (bpmp->soc->ops->resume) + return bpmp->soc->ops->resume(bpmp); + else + return 0; } -static SIMPLE_DEV_PM_OPS(tegra_bpmp_pm_ops, NULL, tegra_bpmp_resume); +static const struct dev_pm_ops tegra_bpmp_pm_ops = { + .resume_early = tegra_bpmp_resume, +}; +#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ + IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) static const struct tegra_bpmp_soc tegra186_soc = { .channels = { .cpu_tx = { @@ -938,11 +825,42 @@ static const struct tegra_bpmp_soc tegra186_soc = { .timeout = 0, }, }, + .ops = &tegra186_bpmp_ops, .num_resets = 193, }; +#endif + +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) +static const struct tegra_bpmp_soc tegra210_soc = { + .channels = { + .cpu_tx = { + .offset = 0, + .count = 1, + .timeout = 60 * USEC_PER_SEC, + }, + .thread = { + .offset = 4, + .count = 1, + .timeout = 600 * USEC_PER_SEC, + }, + .cpu_rx = { + .offset = 8, + .count = 1, + .timeout = 0, + }, + }, + .ops = &tegra210_bpmp_ops, +}; +#endif static const struct of_device_id tegra_bpmp_match[] = { +#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC) || \ + IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) { .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc }, +#endif +#if IS_ENABLED(CONFIG_ARCH_TEGRA_210_SOC) + { .compatible = "nvidia,tegra210-bpmp", .data = &tegra210_soc }, +#endif { } }; diff --git a/drivers/firmware/tegra/ivc.c b/drivers/firmware/tegra/ivc.c index 00de793e6423..e2398cd7ca98 100644 --- a/drivers/firmware/tegra/ivc.c +++ b/drivers/firmware/tegra/ivc.c @@ -1,14 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014-2016, NVIDIA CORPORATION. All rights reserved. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. */ #include <soc/tegra/ivc.h> diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 69ed1464175c..cdee0b45943d 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -65,18 +65,36 @@ struct ti_sci_xfers_info { }; /** + * struct ti_sci_rm_type_map - Structure representing TISCI Resource + * management representation of dev_ids. + * @dev_id: TISCI device ID + * @type: Corresponding id as identified by TISCI RM. + * + * Note: This is used only as a work around for using RM range apis + * for AM654 SoC. For future SoCs dev_id will be used as type + * for RM range APIs. In order to maintain ABI backward compatibility + * type is not being changed for AM654 SoC. + */ +struct ti_sci_rm_type_map { + u32 dev_id; + u16 type; +}; + +/** * struct ti_sci_desc - Description of SoC integration * @default_host_id: Host identifier representing the compute entity * @max_rx_timeout_ms: Timeout for communication with SoC (in Milliseconds) * @max_msgs: Maximum number of messages that can be pending * simultaneously in the system * @max_msg_size: Maximum size of data per message that can be handled. + * @rm_type_map: RM resource type mapping structure. */ struct ti_sci_desc { u8 default_host_id; int max_rx_timeout_ms; int max_msgs; int max_msg_size; + struct ti_sci_rm_type_map *rm_type_map; }; /** @@ -146,25 +164,8 @@ static int ti_sci_debug_show(struct seq_file *s, void *unused) return 0; } -/** - * ti_sci_debug_open() - debug file open - * @inode: inode pointer - * @file: file pointer - * - * Return: result of single_open - */ -static int ti_sci_debug_open(struct inode *inode, struct file *file) -{ - return single_open(file, ti_sci_debug_show, inode->i_private); -} - -/* log file operations */ -static const struct file_operations ti_sci_debug_fops = { - .open = ti_sci_debug_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; +/* Provide the log file operations interface*/ +DEFINE_SHOW_ATTRIBUTE(ti_sci_debug); /** * ti_sci_debugfs_create() - Create log debug file @@ -465,9 +466,9 @@ static int ti_sci_cmd_get_revision(struct ti_sci_info *info) struct ti_sci_xfer *xfer; int ret; - /* No need to setup flags since it is expected to respond */ xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_VERSION, - 0x0, sizeof(struct ti_sci_msg_hdr), + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(struct ti_sci_msg_hdr), sizeof(*rev_info)); if (IS_ERR(xfer)) { ret = PTR_ERR(xfer); @@ -595,9 +596,9 @@ static int ti_sci_get_device_state(const struct ti_sci_handle *handle, info = handle_to_ti_sci_info(handle); dev = info->dev; - /* Response is expected, so need of any flags */ xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_GET_DEVICE_STATE, - 0, sizeof(*req), sizeof(*resp)); + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); if (IS_ERR(xfer)) { ret = PTR_ERR(xfer); dev_err(dev, "Message alloc failed(%d)\n", ret); @@ -915,7 +916,7 @@ static int ti_sci_cmd_get_device_resets(const struct ti_sci_handle *handle, * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_set_clock_state(const struct ti_sci_handle *handle, - u32 dev_id, u8 clk_id, + u32 dev_id, u32 clk_id, u32 flags, u8 state) { struct ti_sci_info *info; @@ -943,7 +944,12 @@ static int ti_sci_set_clock_state(const struct ti_sci_handle *handle, } req = (struct ti_sci_msg_req_set_clock_state *)xfer->xfer_buf; req->dev_id = dev_id; - req->clk_id = clk_id; + if (clk_id < 255) { + req->clk_id = clk_id; + } else { + req->clk_id = 255; + req->clk_id_32 = clk_id; + } req->request_state = state; ret = ti_sci_do_xfer(info, xfer); @@ -975,7 +981,7 @@ fail: * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_cmd_get_clock_state(const struct ti_sci_handle *handle, - u32 dev_id, u8 clk_id, + u32 dev_id, u32 clk_id, u8 *programmed_state, u8 *current_state) { struct ti_sci_info *info; @@ -1006,7 +1012,12 @@ static int ti_sci_cmd_get_clock_state(const struct ti_sci_handle *handle, } req = (struct ti_sci_msg_req_get_clock_state *)xfer->xfer_buf; req->dev_id = dev_id; - req->clk_id = clk_id; + if (clk_id < 255) { + req->clk_id = clk_id; + } else { + req->clk_id = 255; + req->clk_id_32 = clk_id; + } ret = ti_sci_do_xfer(info, xfer); if (ret) { @@ -1046,8 +1057,8 @@ fail: * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_cmd_get_clock(const struct ti_sci_handle *handle, u32 dev_id, - u8 clk_id, bool needs_ssc, bool can_change_freq, - bool enable_input_term) + u32 clk_id, bool needs_ssc, + bool can_change_freq, bool enable_input_term) { u32 flags = 0; @@ -1072,7 +1083,7 @@ static int ti_sci_cmd_get_clock(const struct ti_sci_handle *handle, u32 dev_id, * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_cmd_idle_clock(const struct ti_sci_handle *handle, - u32 dev_id, u8 clk_id) + u32 dev_id, u32 clk_id) { return ti_sci_set_clock_state(handle, dev_id, clk_id, 0, MSG_CLOCK_SW_STATE_UNREQ); @@ -1091,7 +1102,7 @@ static int ti_sci_cmd_idle_clock(const struct ti_sci_handle *handle, * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_cmd_put_clock(const struct ti_sci_handle *handle, - u32 dev_id, u8 clk_id) + u32 dev_id, u32 clk_id) { return ti_sci_set_clock_state(handle, dev_id, clk_id, 0, MSG_CLOCK_SW_STATE_AUTO); @@ -1109,7 +1120,7 @@ static int ti_sci_cmd_put_clock(const struct ti_sci_handle *handle, * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_cmd_clk_is_auto(const struct ti_sci_handle *handle, - u32 dev_id, u8 clk_id, bool *req_state) + u32 dev_id, u32 clk_id, bool *req_state) { u8 state = 0; int ret; @@ -1138,7 +1149,7 @@ static int ti_sci_cmd_clk_is_auto(const struct ti_sci_handle *handle, * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_cmd_clk_is_on(const struct ti_sci_handle *handle, u32 dev_id, - u8 clk_id, bool *req_state, bool *curr_state) + u32 clk_id, bool *req_state, bool *curr_state) { u8 c_state = 0, r_state = 0; int ret; @@ -1171,7 +1182,7 @@ static int ti_sci_cmd_clk_is_on(const struct ti_sci_handle *handle, u32 dev_id, * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_cmd_clk_is_off(const struct ti_sci_handle *handle, u32 dev_id, - u8 clk_id, bool *req_state, bool *curr_state) + u32 clk_id, bool *req_state, bool *curr_state) { u8 c_state = 0, r_state = 0; int ret; @@ -1203,7 +1214,7 @@ static int ti_sci_cmd_clk_is_off(const struct ti_sci_handle *handle, u32 dev_id, * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_cmd_clk_set_parent(const struct ti_sci_handle *handle, - u32 dev_id, u8 clk_id, u8 parent_id) + u32 dev_id, u32 clk_id, u32 parent_id) { struct ti_sci_info *info; struct ti_sci_msg_req_set_clock_parent *req; @@ -1230,8 +1241,18 @@ static int ti_sci_cmd_clk_set_parent(const struct ti_sci_handle *handle, } req = (struct ti_sci_msg_req_set_clock_parent *)xfer->xfer_buf; req->dev_id = dev_id; - req->clk_id = clk_id; - req->parent_id = parent_id; + if (clk_id < 255) { + req->clk_id = clk_id; + } else { + req->clk_id = 255; + req->clk_id_32 = clk_id; + } + if (parent_id < 255) { + req->parent_id = parent_id; + } else { + req->parent_id = 255; + req->parent_id_32 = parent_id; + } ret = ti_sci_do_xfer(info, xfer); if (ret) { @@ -1261,7 +1282,7 @@ fail: * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_cmd_clk_get_parent(const struct ti_sci_handle *handle, - u32 dev_id, u8 clk_id, u8 *parent_id) + u32 dev_id, u32 clk_id, u32 *parent_id) { struct ti_sci_info *info; struct ti_sci_msg_req_get_clock_parent *req; @@ -1288,7 +1309,12 @@ static int ti_sci_cmd_clk_get_parent(const struct ti_sci_handle *handle, } req = (struct ti_sci_msg_req_get_clock_parent *)xfer->xfer_buf; req->dev_id = dev_id; - req->clk_id = clk_id; + if (clk_id < 255) { + req->clk_id = clk_id; + } else { + req->clk_id = 255; + req->clk_id_32 = clk_id; + } ret = ti_sci_do_xfer(info, xfer); if (ret) { @@ -1298,10 +1324,14 @@ static int ti_sci_cmd_clk_get_parent(const struct ti_sci_handle *handle, resp = (struct ti_sci_msg_resp_get_clock_parent *)xfer->xfer_buf; - if (!ti_sci_is_response_ack(resp)) + if (!ti_sci_is_response_ack(resp)) { ret = -ENODEV; - else - *parent_id = resp->parent_id; + } else { + if (resp->parent_id < 255) + *parent_id = resp->parent_id; + else + *parent_id = resp->parent_id_32; + } fail: ti_sci_put_one_xfer(&info->minfo, xfer); @@ -1321,8 +1351,8 @@ fail: * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_cmd_clk_get_num_parents(const struct ti_sci_handle *handle, - u32 dev_id, u8 clk_id, - u8 *num_parents) + u32 dev_id, u32 clk_id, + u32 *num_parents) { struct ti_sci_info *info; struct ti_sci_msg_req_get_clock_num_parents *req; @@ -1349,7 +1379,12 @@ static int ti_sci_cmd_clk_get_num_parents(const struct ti_sci_handle *handle, } req = (struct ti_sci_msg_req_get_clock_num_parents *)xfer->xfer_buf; req->dev_id = dev_id; - req->clk_id = clk_id; + if (clk_id < 255) { + req->clk_id = clk_id; + } else { + req->clk_id = 255; + req->clk_id_32 = clk_id; + } ret = ti_sci_do_xfer(info, xfer); if (ret) { @@ -1359,10 +1394,14 @@ static int ti_sci_cmd_clk_get_num_parents(const struct ti_sci_handle *handle, resp = (struct ti_sci_msg_resp_get_clock_num_parents *)xfer->xfer_buf; - if (!ti_sci_is_response_ack(resp)) + if (!ti_sci_is_response_ack(resp)) { ret = -ENODEV; - else - *num_parents = resp->num_parents; + } else { + if (resp->num_parents < 255) + *num_parents = resp->num_parents; + else + *num_parents = resp->num_parents_32; + } fail: ti_sci_put_one_xfer(&info->minfo, xfer); @@ -1390,7 +1429,7 @@ fail: * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_cmd_clk_get_match_freq(const struct ti_sci_handle *handle, - u32 dev_id, u8 clk_id, u64 min_freq, + u32 dev_id, u32 clk_id, u64 min_freq, u64 target_freq, u64 max_freq, u64 *match_freq) { @@ -1419,7 +1458,12 @@ static int ti_sci_cmd_clk_get_match_freq(const struct ti_sci_handle *handle, } req = (struct ti_sci_msg_req_query_clock_freq *)xfer->xfer_buf; req->dev_id = dev_id; - req->clk_id = clk_id; + if (clk_id < 255) { + req->clk_id = clk_id; + } else { + req->clk_id = 255; + req->clk_id_32 = clk_id; + } req->min_freq_hz = min_freq; req->target_freq_hz = target_freq; req->max_freq_hz = max_freq; @@ -1462,7 +1506,7 @@ fail: * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_cmd_clk_set_freq(const struct ti_sci_handle *handle, - u32 dev_id, u8 clk_id, u64 min_freq, + u32 dev_id, u32 clk_id, u64 min_freq, u64 target_freq, u64 max_freq) { struct ti_sci_info *info; @@ -1490,7 +1534,12 @@ static int ti_sci_cmd_clk_set_freq(const struct ti_sci_handle *handle, } req = (struct ti_sci_msg_req_set_clock_freq *)xfer->xfer_buf; req->dev_id = dev_id; - req->clk_id = clk_id; + if (clk_id < 255) { + req->clk_id = clk_id; + } else { + req->clk_id = 255; + req->clk_id_32 = clk_id; + } req->min_freq_hz = min_freq; req->target_freq_hz = target_freq; req->max_freq_hz = max_freq; @@ -1523,7 +1572,7 @@ fail: * Return: 0 if all went well, else returns appropriate error value. */ static int ti_sci_cmd_clk_get_freq(const struct ti_sci_handle *handle, - u32 dev_id, u8 clk_id, u64 *freq) + u32 dev_id, u32 clk_id, u64 *freq) { struct ti_sci_info *info; struct ti_sci_msg_req_get_clock_freq *req; @@ -1550,7 +1599,12 @@ static int ti_sci_cmd_clk_get_freq(const struct ti_sci_handle *handle, } req = (struct ti_sci_msg_req_get_clock_freq *)xfer->xfer_buf; req->dev_id = dev_id; - req->clk_id = clk_id; + if (clk_id < 255) { + req->clk_id = clk_id; + } else { + req->clk_id = 255; + req->clk_id_32 = clk_id; + } ret = ti_sci_do_xfer(info, xfer); if (ret) { @@ -1617,6 +1671,1209 @@ fail: return ret; } +static int ti_sci_get_resource_type(struct ti_sci_info *info, u16 dev_id, + u16 *type) +{ + struct ti_sci_rm_type_map *rm_type_map = info->desc->rm_type_map; + bool found = false; + int i; + + /* If map is not provided then assume dev_id is used as type */ + if (!rm_type_map) { + *type = dev_id; + return 0; + } + + for (i = 0; rm_type_map[i].dev_id; i++) { + if (rm_type_map[i].dev_id == dev_id) { + *type = rm_type_map[i].type; + found = true; + break; + } + } + + if (!found) + return -EINVAL; + + return 0; +} + +/** + * ti_sci_get_resource_range - Helper to get a range of resources assigned + * to a host. Resource is uniquely identified by + * type and subtype. + * @handle: Pointer to TISCI handle. + * @dev_id: TISCI device ID. + * @subtype: Resource assignment subtype that is being requested + * from the given device. + * @s_host: Host processor ID to which the resources are allocated + * @range_start: Start index of the resource range + * @range_num: Number of resources in the range + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_get_resource_range(const struct ti_sci_handle *handle, + u32 dev_id, u8 subtype, u8 s_host, + u16 *range_start, u16 *range_num) +{ + struct ti_sci_msg_resp_get_resource_range *resp; + struct ti_sci_msg_req_get_resource_range *req; + struct ti_sci_xfer *xfer; + struct ti_sci_info *info; + struct device *dev; + u16 type; + int ret = 0; + + if (IS_ERR(handle)) + return PTR_ERR(handle); + if (!handle) + return -EINVAL; + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_GET_RESOURCE_RANGE, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message alloc failed(%d)\n", ret); + return ret; + } + + ret = ti_sci_get_resource_type(info, dev_id, &type); + if (ret) { + dev_err(dev, "rm type lookup failed for %u\n", dev_id); + goto fail; + } + + req = (struct ti_sci_msg_req_get_resource_range *)xfer->xfer_buf; + req->secondary_host = s_host; + req->type = type & MSG_RM_RESOURCE_TYPE_MASK; + req->subtype = subtype & MSG_RM_RESOURCE_SUBTYPE_MASK; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_resp_get_resource_range *)xfer->xfer_buf; + + if (!ti_sci_is_response_ack(resp)) { + ret = -ENODEV; + } else if (!resp->range_start && !resp->range_num) { + ret = -ENODEV; + } else { + *range_start = resp->range_start; + *range_num = resp->range_num; + }; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + +/** + * ti_sci_cmd_get_resource_range - Get a range of resources assigned to host + * that is same as ti sci interface host. + * @handle: Pointer to TISCI handle. + * @dev_id: TISCI device ID. + * @subtype: Resource assignment subtype that is being requested + * from the given device. + * @range_start: Start index of the resource range + * @range_num: Number of resources in the range + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_get_resource_range(const struct ti_sci_handle *handle, + u32 dev_id, u8 subtype, + u16 *range_start, u16 *range_num) +{ + return ti_sci_get_resource_range(handle, dev_id, subtype, + TI_SCI_IRQ_SECONDARY_HOST_INVALID, + range_start, range_num); +} + +/** + * ti_sci_cmd_get_resource_range_from_shost - Get a range of resources + * assigned to a specified host. + * @handle: Pointer to TISCI handle. + * @dev_id: TISCI device ID. + * @subtype: Resource assignment subtype that is being requested + * from the given device. + * @s_host: Host processor ID to which the resources are allocated + * @range_start: Start index of the resource range + * @range_num: Number of resources in the range + * + * Return: 0 if all went fine, else return appropriate error. + */ +static +int ti_sci_cmd_get_resource_range_from_shost(const struct ti_sci_handle *handle, + u32 dev_id, u8 subtype, u8 s_host, + u16 *range_start, u16 *range_num) +{ + return ti_sci_get_resource_range(handle, dev_id, subtype, s_host, + range_start, range_num); +} + +/** + * ti_sci_manage_irq() - Helper api to configure/release the irq route between + * the requested source and destination + * @handle: Pointer to TISCI handle. + * @valid_params: Bit fields defining the validity of certain params + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * @s_host: Secondary host ID to which the irq/event is being + * requested for. + * @type: Request type irq set or release. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_manage_irq(const struct ti_sci_handle *handle, + u32 valid_params, u16 src_id, u16 src_index, + u16 dst_id, u16 dst_host_irq, u16 ia_id, u16 vint, + u16 global_event, u8 vint_status_bit, u8 s_host, + u16 type) +{ + struct ti_sci_msg_req_manage_irq *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_xfer *xfer; + struct ti_sci_info *info; + struct device *dev; + int ret = 0; + + if (IS_ERR(handle)) + return PTR_ERR(handle); + if (!handle) + return -EINVAL; + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, type, TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message alloc failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_req_manage_irq *)xfer->xfer_buf; + req->valid_params = valid_params; + req->src_id = src_id; + req->src_index = src_index; + req->dst_id = dst_id; + req->dst_host_irq = dst_host_irq; + req->ia_id = ia_id; + req->vint = vint; + req->global_event = global_event; + req->vint_status_bit = vint_status_bit; + req->secondary_host = s_host; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; + + ret = ti_sci_is_response_ack(resp) ? 0 : -ENODEV; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + +/** + * ti_sci_set_irq() - Helper api to configure the irq route between the + * requested source and destination + * @handle: Pointer to TISCI handle. + * @valid_params: Bit fields defining the validity of certain params + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * @s_host: Secondary host ID to which the irq/event is being + * requested for. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_set_irq(const struct ti_sci_handle *handle, u32 valid_params, + u16 src_id, u16 src_index, u16 dst_id, + u16 dst_host_irq, u16 ia_id, u16 vint, + u16 global_event, u8 vint_status_bit, u8 s_host) +{ + pr_debug("%s: IRQ set with valid_params = 0x%x from src = %d, index = %d, to dst = %d, irq = %d,via ia_id = %d, vint = %d, global event = %d,status_bit = %d\n", + __func__, valid_params, src_id, src_index, + dst_id, dst_host_irq, ia_id, vint, global_event, + vint_status_bit); + + return ti_sci_manage_irq(handle, valid_params, src_id, src_index, + dst_id, dst_host_irq, ia_id, vint, + global_event, vint_status_bit, s_host, + TI_SCI_MSG_SET_IRQ); +} + +/** + * ti_sci_free_irq() - Helper api to free the irq route between the + * requested source and destination + * @handle: Pointer to TISCI handle. + * @valid_params: Bit fields defining the validity of certain params + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * @s_host: Secondary host ID to which the irq/event is being + * requested for. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_free_irq(const struct ti_sci_handle *handle, u32 valid_params, + u16 src_id, u16 src_index, u16 dst_id, + u16 dst_host_irq, u16 ia_id, u16 vint, + u16 global_event, u8 vint_status_bit, u8 s_host) +{ + pr_debug("%s: IRQ release with valid_params = 0x%x from src = %d, index = %d, to dst = %d, irq = %d,via ia_id = %d, vint = %d, global event = %d,status_bit = %d\n", + __func__, valid_params, src_id, src_index, + dst_id, dst_host_irq, ia_id, vint, global_event, + vint_status_bit); + + return ti_sci_manage_irq(handle, valid_params, src_id, src_index, + dst_id, dst_host_irq, ia_id, vint, + global_event, vint_status_bit, s_host, + TI_SCI_MSG_FREE_IRQ); +} + +/** + * ti_sci_cmd_set_irq() - Configure a host irq route between the requested + * source and destination. + * @handle: Pointer to TISCI handle. + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @vint_irq: Boolean specifying if this interrupt belongs to + * Interrupt Aggregator. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_set_irq(const struct ti_sci_handle *handle, u16 src_id, + u16 src_index, u16 dst_id, u16 dst_host_irq) +{ + u32 valid_params = MSG_FLAG_DST_ID_VALID | MSG_FLAG_DST_HOST_IRQ_VALID; + + return ti_sci_set_irq(handle, valid_params, src_id, src_index, dst_id, + dst_host_irq, 0, 0, 0, 0, 0); +} + +/** + * ti_sci_cmd_set_event_map() - Configure an event based irq route between the + * requested source and Interrupt Aggregator. + * @handle: Pointer to TISCI handle. + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_set_event_map(const struct ti_sci_handle *handle, + u16 src_id, u16 src_index, u16 ia_id, + u16 vint, u16 global_event, + u8 vint_status_bit) +{ + u32 valid_params = MSG_FLAG_IA_ID_VALID | MSG_FLAG_VINT_VALID | + MSG_FLAG_GLB_EVNT_VALID | + MSG_FLAG_VINT_STS_BIT_VALID; + + return ti_sci_set_irq(handle, valid_params, src_id, src_index, 0, 0, + ia_id, vint, global_event, vint_status_bit, 0); +} + +/** + * ti_sci_cmd_free_irq() - Free a host irq route between the between the + * requested source and destination. + * @handle: Pointer to TISCI handle. + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @dst_id: Device ID of the IRQ destination + * @dst_host_irq: IRQ number of the destination device + * @vint_irq: Boolean specifying if this interrupt belongs to + * Interrupt Aggregator. + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_free_irq(const struct ti_sci_handle *handle, u16 src_id, + u16 src_index, u16 dst_id, u16 dst_host_irq) +{ + u32 valid_params = MSG_FLAG_DST_ID_VALID | MSG_FLAG_DST_HOST_IRQ_VALID; + + return ti_sci_free_irq(handle, valid_params, src_id, src_index, dst_id, + dst_host_irq, 0, 0, 0, 0, 0); +} + +/** + * ti_sci_cmd_free_event_map() - Free an event map between the requested source + * and Interrupt Aggregator. + * @handle: Pointer to TISCI handle. + * @src_id: Device ID of the IRQ source + * @src_index: IRQ source index within the source device + * @ia_id: Device ID of the IA, if the IRQ flows through this IA + * @vint: Virtual interrupt to be used within the IA + * @global_event: Global event number to be used for the requesting event + * @vint_status_bit: Virtual interrupt status bit to be used for the event + * + * Return: 0 if all went fine, else return appropriate error. + */ +static int ti_sci_cmd_free_event_map(const struct ti_sci_handle *handle, + u16 src_id, u16 src_index, u16 ia_id, + u16 vint, u16 global_event, + u8 vint_status_bit) +{ + u32 valid_params = MSG_FLAG_IA_ID_VALID | + MSG_FLAG_VINT_VALID | MSG_FLAG_GLB_EVNT_VALID | + MSG_FLAG_VINT_STS_BIT_VALID; + + return ti_sci_free_irq(handle, valid_params, src_id, src_index, 0, 0, + ia_id, vint, global_event, vint_status_bit, 0); +} + +/** + * ti_sci_cmd_ring_config() - configure RA ring + * @handle: Pointer to TI SCI handle. + * @valid_params: Bitfield defining validity of ring configuration + * parameters + * @nav_id: Device ID of Navigator Subsystem from which the ring is + * allocated + * @index: Ring index + * @addr_lo: The ring base address lo 32 bits + * @addr_hi: The ring base address hi 32 bits + * @count: Number of ring elements + * @mode: The mode of the ring + * @size: The ring element size. + * @order_id: Specifies the ring's bus order ID + * + * Return: 0 if all went well, else returns appropriate error value. + * + * See @ti_sci_msg_rm_ring_cfg_req for more info. + */ +static int ti_sci_cmd_ring_config(const struct ti_sci_handle *handle, + u32 valid_params, u16 nav_id, u16 index, + u32 addr_lo, u32 addr_hi, u32 count, + u8 mode, u8 size, u8 order_id) +{ + struct ti_sci_msg_rm_ring_cfg_req *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_xfer *xfer; + struct ti_sci_info *info; + struct device *dev; + int ret = 0; + + if (IS_ERR_OR_NULL(handle)) + return -EINVAL; + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_RM_RING_CFG, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "RM_RA:Message config failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_rm_ring_cfg_req *)xfer->xfer_buf; + req->valid_params = valid_params; + req->nav_id = nav_id; + req->index = index; + req->addr_lo = addr_lo; + req->addr_hi = addr_hi; + req->count = count; + req->mode = mode; + req->size = size; + req->order_id = order_id; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "RM_RA:Mbox config send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; + ret = ti_sci_is_response_ack(resp) ? 0 : -ENODEV; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + dev_dbg(dev, "RM_RA:config ring %u ret:%d\n", index, ret); + return ret; +} + +/** + * ti_sci_cmd_ring_get_config() - get RA ring configuration + * @handle: Pointer to TI SCI handle. + * @nav_id: Device ID of Navigator Subsystem from which the ring is + * allocated + * @index: Ring index + * @addr_lo: Returns ring's base address lo 32 bits + * @addr_hi: Returns ring's base address hi 32 bits + * @count: Returns number of ring elements + * @mode: Returns mode of the ring + * @size: Returns ring element size + * @order_id: Returns ring's bus order ID + * + * Return: 0 if all went well, else returns appropriate error value. + * + * See @ti_sci_msg_rm_ring_get_cfg_req for more info. + */ +static int ti_sci_cmd_ring_get_config(const struct ti_sci_handle *handle, + u32 nav_id, u32 index, u8 *mode, + u32 *addr_lo, u32 *addr_hi, + u32 *count, u8 *size, u8 *order_id) +{ + struct ti_sci_msg_rm_ring_get_cfg_resp *resp; + struct ti_sci_msg_rm_ring_get_cfg_req *req; + struct ti_sci_xfer *xfer; + struct ti_sci_info *info; + struct device *dev; + int ret = 0; + + if (IS_ERR_OR_NULL(handle)) + return -EINVAL; + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_RM_RING_GET_CFG, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, + "RM_RA:Message get config failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_rm_ring_get_cfg_req *)xfer->xfer_buf; + req->nav_id = nav_id; + req->index = index; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "RM_RA:Mbox get config send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_rm_ring_get_cfg_resp *)xfer->xfer_buf; + + if (!ti_sci_is_response_ack(resp)) { + ret = -ENODEV; + } else { + if (mode) + *mode = resp->mode; + if (addr_lo) + *addr_lo = resp->addr_lo; + if (addr_hi) + *addr_hi = resp->addr_hi; + if (count) + *count = resp->count; + if (size) + *size = resp->size; + if (order_id) + *order_id = resp->order_id; + }; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + dev_dbg(dev, "RM_RA:get config ring %u ret:%d\n", index, ret); + return ret; +} + +/** + * ti_sci_cmd_rm_psil_pair() - Pair PSI-L source to destination thread + * @handle: Pointer to TI SCI handle. + * @nav_id: Device ID of Navigator Subsystem which should be used for + * pairing + * @src_thread: Source PSI-L thread ID + * @dst_thread: Destination PSI-L thread ID + * + * Return: 0 if all went well, else returns appropriate error value. + */ +static int ti_sci_cmd_rm_psil_pair(const struct ti_sci_handle *handle, + u32 nav_id, u32 src_thread, u32 dst_thread) +{ + struct ti_sci_msg_psil_pair *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_xfer *xfer; + struct ti_sci_info *info; + struct device *dev; + int ret = 0; + + if (IS_ERR(handle)) + return PTR_ERR(handle); + if (!handle) + return -EINVAL; + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_RM_PSIL_PAIR, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "RM_PSIL:Message reconfig failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_psil_pair *)xfer->xfer_buf; + req->nav_id = nav_id; + req->src_thread = src_thread; + req->dst_thread = dst_thread; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "RM_PSIL:Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; + ret = ti_sci_is_response_ack(resp) ? 0 : -EINVAL; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + +/** + * ti_sci_cmd_rm_psil_unpair() - Unpair PSI-L source from destination thread + * @handle: Pointer to TI SCI handle. + * @nav_id: Device ID of Navigator Subsystem which should be used for + * unpairing + * @src_thread: Source PSI-L thread ID + * @dst_thread: Destination PSI-L thread ID + * + * Return: 0 if all went well, else returns appropriate error value. + */ +static int ti_sci_cmd_rm_psil_unpair(const struct ti_sci_handle *handle, + u32 nav_id, u32 src_thread, u32 dst_thread) +{ + struct ti_sci_msg_psil_unpair *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_xfer *xfer; + struct ti_sci_info *info; + struct device *dev; + int ret = 0; + + if (IS_ERR(handle)) + return PTR_ERR(handle); + if (!handle) + return -EINVAL; + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_RM_PSIL_UNPAIR, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "RM_PSIL:Message reconfig failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_psil_unpair *)xfer->xfer_buf; + req->nav_id = nav_id; + req->src_thread = src_thread; + req->dst_thread = dst_thread; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "RM_PSIL:Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; + ret = ti_sci_is_response_ack(resp) ? 0 : -EINVAL; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + +/** + * ti_sci_cmd_rm_udmap_tx_ch_cfg() - Configure a UDMAP TX channel + * @handle: Pointer to TI SCI handle. + * @params: Pointer to ti_sci_msg_rm_udmap_tx_ch_cfg TX channel config + * structure + * + * Return: 0 if all went well, else returns appropriate error value. + * + * See @ti_sci_msg_rm_udmap_tx_ch_cfg and @ti_sci_msg_rm_udmap_tx_ch_cfg_req for + * more info. + */ +static int ti_sci_cmd_rm_udmap_tx_ch_cfg(const struct ti_sci_handle *handle, + const struct ti_sci_msg_rm_udmap_tx_ch_cfg *params) +{ + struct ti_sci_msg_rm_udmap_tx_ch_cfg_req *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_xfer *xfer; + struct ti_sci_info *info; + struct device *dev; + int ret = 0; + + if (IS_ERR_OR_NULL(handle)) + return -EINVAL; + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TISCI_MSG_RM_UDMAP_TX_CH_CFG, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message TX_CH_CFG alloc failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_rm_udmap_tx_ch_cfg_req *)xfer->xfer_buf; + req->valid_params = params->valid_params; + req->nav_id = params->nav_id; + req->index = params->index; + req->tx_pause_on_err = params->tx_pause_on_err; + req->tx_filt_einfo = params->tx_filt_einfo; + req->tx_filt_pswords = params->tx_filt_pswords; + req->tx_atype = params->tx_atype; + req->tx_chan_type = params->tx_chan_type; + req->tx_supr_tdpkt = params->tx_supr_tdpkt; + req->tx_fetch_size = params->tx_fetch_size; + req->tx_credit_count = params->tx_credit_count; + req->txcq_qnum = params->txcq_qnum; + req->tx_priority = params->tx_priority; + req->tx_qos = params->tx_qos; + req->tx_orderid = params->tx_orderid; + req->fdepth = params->fdepth; + req->tx_sched_priority = params->tx_sched_priority; + req->tx_burst_size = params->tx_burst_size; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send TX_CH_CFG fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; + ret = ti_sci_is_response_ack(resp) ? 0 : -EINVAL; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + dev_dbg(dev, "TX_CH_CFG: chn %u ret:%u\n", params->index, ret); + return ret; +} + +/** + * ti_sci_cmd_rm_udmap_rx_ch_cfg() - Configure a UDMAP RX channel + * @handle: Pointer to TI SCI handle. + * @params: Pointer to ti_sci_msg_rm_udmap_rx_ch_cfg RX channel config + * structure + * + * Return: 0 if all went well, else returns appropriate error value. + * + * See @ti_sci_msg_rm_udmap_rx_ch_cfg and @ti_sci_msg_rm_udmap_rx_ch_cfg_req for + * more info. + */ +static int ti_sci_cmd_rm_udmap_rx_ch_cfg(const struct ti_sci_handle *handle, + const struct ti_sci_msg_rm_udmap_rx_ch_cfg *params) +{ + struct ti_sci_msg_rm_udmap_rx_ch_cfg_req *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_xfer *xfer; + struct ti_sci_info *info; + struct device *dev; + int ret = 0; + + if (IS_ERR_OR_NULL(handle)) + return -EINVAL; + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TISCI_MSG_RM_UDMAP_RX_CH_CFG, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message RX_CH_CFG alloc failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_rm_udmap_rx_ch_cfg_req *)xfer->xfer_buf; + req->valid_params = params->valid_params; + req->nav_id = params->nav_id; + req->index = params->index; + req->rx_fetch_size = params->rx_fetch_size; + req->rxcq_qnum = params->rxcq_qnum; + req->rx_priority = params->rx_priority; + req->rx_qos = params->rx_qos; + req->rx_orderid = params->rx_orderid; + req->rx_sched_priority = params->rx_sched_priority; + req->flowid_start = params->flowid_start; + req->flowid_cnt = params->flowid_cnt; + req->rx_pause_on_err = params->rx_pause_on_err; + req->rx_atype = params->rx_atype; + req->rx_chan_type = params->rx_chan_type; + req->rx_ignore_short = params->rx_ignore_short; + req->rx_ignore_long = params->rx_ignore_long; + req->rx_burst_size = params->rx_burst_size; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send RX_CH_CFG fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; + ret = ti_sci_is_response_ack(resp) ? 0 : -EINVAL; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + dev_dbg(dev, "RX_CH_CFG: chn %u ret:%d\n", params->index, ret); + return ret; +} + +/** + * ti_sci_cmd_rm_udmap_rx_flow_cfg() - Configure UDMAP RX FLOW + * @handle: Pointer to TI SCI handle. + * @params: Pointer to ti_sci_msg_rm_udmap_flow_cfg RX FLOW config + * structure + * + * Return: 0 if all went well, else returns appropriate error value. + * + * See @ti_sci_msg_rm_udmap_flow_cfg and @ti_sci_msg_rm_udmap_flow_cfg_req for + * more info. + */ +static int ti_sci_cmd_rm_udmap_rx_flow_cfg(const struct ti_sci_handle *handle, + const struct ti_sci_msg_rm_udmap_flow_cfg *params) +{ + struct ti_sci_msg_rm_udmap_flow_cfg_req *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_xfer *xfer; + struct ti_sci_info *info; + struct device *dev; + int ret = 0; + + if (IS_ERR_OR_NULL(handle)) + return -EINVAL; + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TISCI_MSG_RM_UDMAP_FLOW_CFG, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "RX_FL_CFG: Message alloc failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_rm_udmap_flow_cfg_req *)xfer->xfer_buf; + req->valid_params = params->valid_params; + req->nav_id = params->nav_id; + req->flow_index = params->flow_index; + req->rx_einfo_present = params->rx_einfo_present; + req->rx_psinfo_present = params->rx_psinfo_present; + req->rx_error_handling = params->rx_error_handling; + req->rx_desc_type = params->rx_desc_type; + req->rx_sop_offset = params->rx_sop_offset; + req->rx_dest_qnum = params->rx_dest_qnum; + req->rx_src_tag_hi = params->rx_src_tag_hi; + req->rx_src_tag_lo = params->rx_src_tag_lo; + req->rx_dest_tag_hi = params->rx_dest_tag_hi; + req->rx_dest_tag_lo = params->rx_dest_tag_lo; + req->rx_src_tag_hi_sel = params->rx_src_tag_hi_sel; + req->rx_src_tag_lo_sel = params->rx_src_tag_lo_sel; + req->rx_dest_tag_hi_sel = params->rx_dest_tag_hi_sel; + req->rx_dest_tag_lo_sel = params->rx_dest_tag_lo_sel; + req->rx_fdq0_sz0_qnum = params->rx_fdq0_sz0_qnum; + req->rx_fdq1_qnum = params->rx_fdq1_qnum; + req->rx_fdq2_qnum = params->rx_fdq2_qnum; + req->rx_fdq3_qnum = params->rx_fdq3_qnum; + req->rx_ps_location = params->rx_ps_location; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "RX_FL_CFG: Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->xfer_buf; + ret = ti_sci_is_response_ack(resp) ? 0 : -EINVAL; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + dev_dbg(info->dev, "RX_FL_CFG: %u ret:%d\n", params->flow_index, ret); + return ret; +} + +/** + * ti_sci_cmd_proc_request() - Command to request a physical processor control + * @handle: Pointer to TI SCI handle + * @proc_id: Processor ID this request is for + * + * Return: 0 if all went well, else returns appropriate error value. + */ +static int ti_sci_cmd_proc_request(const struct ti_sci_handle *handle, + u8 proc_id) +{ + struct ti_sci_msg_req_proc_request *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_info *info; + struct ti_sci_xfer *xfer; + struct device *dev; + int ret = 0; + + if (!handle) + return -EINVAL; + if (IS_ERR(handle)) + return PTR_ERR(handle); + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_PROC_REQUEST, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message alloc failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_req_proc_request *)xfer->xfer_buf; + req->processor_id = proc_id; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->tx_message.buf; + + ret = ti_sci_is_response_ack(resp) ? 0 : -ENODEV; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + +/** + * ti_sci_cmd_proc_release() - Command to release a physical processor control + * @handle: Pointer to TI SCI handle + * @proc_id: Processor ID this request is for + * + * Return: 0 if all went well, else returns appropriate error value. + */ +static int ti_sci_cmd_proc_release(const struct ti_sci_handle *handle, + u8 proc_id) +{ + struct ti_sci_msg_req_proc_release *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_info *info; + struct ti_sci_xfer *xfer; + struct device *dev; + int ret = 0; + + if (!handle) + return -EINVAL; + if (IS_ERR(handle)) + return PTR_ERR(handle); + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_PROC_RELEASE, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message alloc failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_req_proc_release *)xfer->xfer_buf; + req->processor_id = proc_id; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->tx_message.buf; + + ret = ti_sci_is_response_ack(resp) ? 0 : -ENODEV; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + +/** + * ti_sci_cmd_proc_handover() - Command to handover a physical processor + * control to a host in the processor's access + * control list. + * @handle: Pointer to TI SCI handle + * @proc_id: Processor ID this request is for + * @host_id: Host ID to get the control of the processor + * + * Return: 0 if all went well, else returns appropriate error value. + */ +static int ti_sci_cmd_proc_handover(const struct ti_sci_handle *handle, + u8 proc_id, u8 host_id) +{ + struct ti_sci_msg_req_proc_handover *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_info *info; + struct ti_sci_xfer *xfer; + struct device *dev; + int ret = 0; + + if (!handle) + return -EINVAL; + if (IS_ERR(handle)) + return PTR_ERR(handle); + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_PROC_HANDOVER, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message alloc failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_req_proc_handover *)xfer->xfer_buf; + req->processor_id = proc_id; + req->host_id = host_id; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->tx_message.buf; + + ret = ti_sci_is_response_ack(resp) ? 0 : -ENODEV; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + +/** + * ti_sci_cmd_proc_set_config() - Command to set the processor boot + * configuration flags + * @handle: Pointer to TI SCI handle + * @proc_id: Processor ID this request is for + * @config_flags_set: Configuration flags to be set + * @config_flags_clear: Configuration flags to be cleared. + * + * Return: 0 if all went well, else returns appropriate error value. + */ +static int ti_sci_cmd_proc_set_config(const struct ti_sci_handle *handle, + u8 proc_id, u64 bootvector, + u32 config_flags_set, + u32 config_flags_clear) +{ + struct ti_sci_msg_req_set_config *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_info *info; + struct ti_sci_xfer *xfer; + struct device *dev; + int ret = 0; + + if (!handle) + return -EINVAL; + if (IS_ERR(handle)) + return PTR_ERR(handle); + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_SET_CONFIG, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message alloc failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_req_set_config *)xfer->xfer_buf; + req->processor_id = proc_id; + req->bootvector_low = bootvector & TI_SCI_ADDR_LOW_MASK; + req->bootvector_high = (bootvector & TI_SCI_ADDR_HIGH_MASK) >> + TI_SCI_ADDR_HIGH_SHIFT; + req->config_flags_set = config_flags_set; + req->config_flags_clear = config_flags_clear; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->tx_message.buf; + + ret = ti_sci_is_response_ack(resp) ? 0 : -ENODEV; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + +/** + * ti_sci_cmd_proc_set_control() - Command to set the processor boot + * control flags + * @handle: Pointer to TI SCI handle + * @proc_id: Processor ID this request is for + * @control_flags_set: Control flags to be set + * @control_flags_clear: Control flags to be cleared + * + * Return: 0 if all went well, else returns appropriate error value. + */ +static int ti_sci_cmd_proc_set_control(const struct ti_sci_handle *handle, + u8 proc_id, u32 control_flags_set, + u32 control_flags_clear) +{ + struct ti_sci_msg_req_set_ctrl *req; + struct ti_sci_msg_hdr *resp; + struct ti_sci_info *info; + struct ti_sci_xfer *xfer; + struct device *dev; + int ret = 0; + + if (!handle) + return -EINVAL; + if (IS_ERR(handle)) + return PTR_ERR(handle); + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_SET_CTRL, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message alloc failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_req_set_ctrl *)xfer->xfer_buf; + req->processor_id = proc_id; + req->control_flags_set = control_flags_set; + req->control_flags_clear = control_flags_clear; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_hdr *)xfer->tx_message.buf; + + ret = ti_sci_is_response_ack(resp) ? 0 : -ENODEV; + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + +/** + * ti_sci_cmd_get_boot_status() - Command to get the processor boot status + * @handle: Pointer to TI SCI handle + * @proc_id: Processor ID this request is for + * + * Return: 0 if all went well, else returns appropriate error value. + */ +static int ti_sci_cmd_proc_get_status(const struct ti_sci_handle *handle, + u8 proc_id, u64 *bv, u32 *cfg_flags, + u32 *ctrl_flags, u32 *sts_flags) +{ + struct ti_sci_msg_resp_get_status *resp; + struct ti_sci_msg_req_get_status *req; + struct ti_sci_info *info; + struct ti_sci_xfer *xfer; + struct device *dev; + int ret = 0; + + if (!handle) + return -EINVAL; + if (IS_ERR(handle)) + return PTR_ERR(handle); + + info = handle_to_ti_sci_info(handle); + dev = info->dev; + + xfer = ti_sci_get_one_xfer(info, TI_SCI_MSG_GET_STATUS, + TI_SCI_FLAG_REQ_ACK_ON_PROCESSED, + sizeof(*req), sizeof(*resp)); + if (IS_ERR(xfer)) { + ret = PTR_ERR(xfer); + dev_err(dev, "Message alloc failed(%d)\n", ret); + return ret; + } + req = (struct ti_sci_msg_req_get_status *)xfer->xfer_buf; + req->processor_id = proc_id; + + ret = ti_sci_do_xfer(info, xfer); + if (ret) { + dev_err(dev, "Mbox send fail %d\n", ret); + goto fail; + } + + resp = (struct ti_sci_msg_resp_get_status *)xfer->tx_message.buf; + + if (!ti_sci_is_response_ack(resp)) { + ret = -ENODEV; + } else { + *bv = (resp->bootvector_low & TI_SCI_ADDR_LOW_MASK) | + (((u64)resp->bootvector_high << TI_SCI_ADDR_HIGH_SHIFT) & + TI_SCI_ADDR_HIGH_MASK); + *cfg_flags = resp->config_flags; + *ctrl_flags = resp->control_flags; + *sts_flags = resp->status_flags; + } + +fail: + ti_sci_put_one_xfer(&info->minfo, xfer); + + return ret; +} + /* * ti_sci_setup_ops() - Setup the operations structures * @info: pointer to TISCI pointer @@ -1627,6 +2884,12 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) struct ti_sci_core_ops *core_ops = &ops->core_ops; struct ti_sci_dev_ops *dops = &ops->dev_ops; struct ti_sci_clk_ops *cops = &ops->clk_ops; + struct ti_sci_rm_core_ops *rm_core_ops = &ops->rm_core_ops; + struct ti_sci_rm_irq_ops *iops = &ops->rm_irq_ops; + struct ti_sci_rm_ringacc_ops *rops = &ops->rm_ring_ops; + struct ti_sci_rm_psil_ops *psilops = &ops->rm_psil_ops; + struct ti_sci_rm_udmap_ops *udmap_ops = &ops->rm_udmap_ops; + struct ti_sci_proc_ops *pops = &ops->proc_ops; core_ops->reboot_device = ti_sci_cmd_core_reboot; @@ -1657,6 +2920,32 @@ static void ti_sci_setup_ops(struct ti_sci_info *info) cops->get_best_match_freq = ti_sci_cmd_clk_get_match_freq; cops->set_freq = ti_sci_cmd_clk_set_freq; cops->get_freq = ti_sci_cmd_clk_get_freq; + + rm_core_ops->get_range = ti_sci_cmd_get_resource_range; + rm_core_ops->get_range_from_shost = + ti_sci_cmd_get_resource_range_from_shost; + + iops->set_irq = ti_sci_cmd_set_irq; + iops->set_event_map = ti_sci_cmd_set_event_map; + iops->free_irq = ti_sci_cmd_free_irq; + iops->free_event_map = ti_sci_cmd_free_event_map; + + rops->config = ti_sci_cmd_ring_config; + rops->get_config = ti_sci_cmd_ring_get_config; + + psilops->pair = ti_sci_cmd_rm_psil_pair; + psilops->unpair = ti_sci_cmd_rm_psil_unpair; + + udmap_ops->tx_ch_cfg = ti_sci_cmd_rm_udmap_tx_ch_cfg; + udmap_ops->rx_ch_cfg = ti_sci_cmd_rm_udmap_rx_ch_cfg; + udmap_ops->rx_flow_cfg = ti_sci_cmd_rm_udmap_rx_flow_cfg; + + pops->request = ti_sci_cmd_proc_request; + pops->release = ti_sci_cmd_proc_release; + pops->handover = ti_sci_cmd_proc_handover; + pops->set_config = ti_sci_cmd_proc_set_config; + pops->set_control = ti_sci_cmd_proc_set_control; + pops->get_status = ti_sci_cmd_proc_get_status; } /** @@ -1781,6 +3070,227 @@ const struct ti_sci_handle *devm_ti_sci_get_handle(struct device *dev) } EXPORT_SYMBOL_GPL(devm_ti_sci_get_handle); +/** + * ti_sci_get_by_phandle() - Get the TI SCI handle using DT phandle + * @np: device node + * @property: property name containing phandle on TISCI node + * + * NOTE: The function does not track individual clients of the framework + * and is expected to be maintained by caller of TI SCI protocol library. + * ti_sci_put_handle must be balanced with successful ti_sci_get_by_phandle + * Return: pointer to handle if successful, else: + * -EPROBE_DEFER if the instance is not ready + * -ENODEV if the required node handler is missing + * -EINVAL if invalid conditions are encountered. + */ +const struct ti_sci_handle *ti_sci_get_by_phandle(struct device_node *np, + const char *property) +{ + struct ti_sci_handle *handle = NULL; + struct device_node *ti_sci_np; + struct ti_sci_info *info; + struct list_head *p; + + if (!np) { + pr_err("I need a device pointer\n"); + return ERR_PTR(-EINVAL); + } + + ti_sci_np = of_parse_phandle(np, property, 0); + if (!ti_sci_np) + return ERR_PTR(-ENODEV); + + mutex_lock(&ti_sci_list_mutex); + list_for_each(p, &ti_sci_list) { + info = list_entry(p, struct ti_sci_info, node); + if (ti_sci_np == info->dev->of_node) { + handle = &info->handle; + info->users++; + break; + } + } + mutex_unlock(&ti_sci_list_mutex); + of_node_put(ti_sci_np); + + if (!handle) + return ERR_PTR(-EPROBE_DEFER); + + return handle; +} +EXPORT_SYMBOL_GPL(ti_sci_get_by_phandle); + +/** + * devm_ti_sci_get_by_phandle() - Managed get handle using phandle + * @dev: Device pointer requesting TISCI handle + * @property: property name containing phandle on TISCI node + * + * NOTE: This releases the handle once the device resources are + * no longer needed. MUST NOT BE released with ti_sci_put_handle. + * The function does not track individual clients of the framework + * and is expected to be maintained by caller of TI SCI protocol library. + * + * Return: 0 if all went fine, else corresponding error. + */ +const struct ti_sci_handle *devm_ti_sci_get_by_phandle(struct device *dev, + const char *property) +{ + const struct ti_sci_handle *handle; + const struct ti_sci_handle **ptr; + + ptr = devres_alloc(devm_ti_sci_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + handle = ti_sci_get_by_phandle(dev_of_node(dev), property); + + if (!IS_ERR(handle)) { + *ptr = handle; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return handle; +} +EXPORT_SYMBOL_GPL(devm_ti_sci_get_by_phandle); + +/** + * ti_sci_get_free_resource() - Get a free resource from TISCI resource. + * @res: Pointer to the TISCI resource + * + * Return: resource num if all went ok else TI_SCI_RESOURCE_NULL. + */ +u16 ti_sci_get_free_resource(struct ti_sci_resource *res) +{ + unsigned long flags; + u16 set, free_bit; + + raw_spin_lock_irqsave(&res->lock, flags); + for (set = 0; set < res->sets; set++) { + free_bit = find_first_zero_bit(res->desc[set].res_map, + res->desc[set].num); + if (free_bit != res->desc[set].num) { + set_bit(free_bit, res->desc[set].res_map); + raw_spin_unlock_irqrestore(&res->lock, flags); + return res->desc[set].start + free_bit; + } + } + raw_spin_unlock_irqrestore(&res->lock, flags); + + return TI_SCI_RESOURCE_NULL; +} +EXPORT_SYMBOL_GPL(ti_sci_get_free_resource); + +/** + * ti_sci_release_resource() - Release a resource from TISCI resource. + * @res: Pointer to the TISCI resource + * @id: Resource id to be released. + */ +void ti_sci_release_resource(struct ti_sci_resource *res, u16 id) +{ + unsigned long flags; + u16 set; + + raw_spin_lock_irqsave(&res->lock, flags); + for (set = 0; set < res->sets; set++) { + if (res->desc[set].start <= id && + (res->desc[set].num + res->desc[set].start) > id) + clear_bit(id - res->desc[set].start, + res->desc[set].res_map); + } + raw_spin_unlock_irqrestore(&res->lock, flags); +} +EXPORT_SYMBOL_GPL(ti_sci_release_resource); + +/** + * ti_sci_get_num_resources() - Get the number of resources in TISCI resource + * @res: Pointer to the TISCI resource + * + * Return: Total number of available resources. + */ +u32 ti_sci_get_num_resources(struct ti_sci_resource *res) +{ + u32 set, count = 0; + + for (set = 0; set < res->sets; set++) + count += res->desc[set].num; + + return count; +} +EXPORT_SYMBOL_GPL(ti_sci_get_num_resources); + +/** + * devm_ti_sci_get_of_resource() - Get a TISCI resource assigned to a device + * @handle: TISCI handle + * @dev: Device pointer to which the resource is assigned + * @dev_id: TISCI device id to which the resource is assigned + * @of_prop: property name by which the resource are represented + * + * Return: Pointer to ti_sci_resource if all went well else appropriate + * error pointer. + */ +struct ti_sci_resource * +devm_ti_sci_get_of_resource(const struct ti_sci_handle *handle, + struct device *dev, u32 dev_id, char *of_prop) +{ + struct ti_sci_resource *res; + bool valid_set = false; + u32 resource_subtype; + int i, ret; + + res = devm_kzalloc(dev, sizeof(*res), GFP_KERNEL); + if (!res) + return ERR_PTR(-ENOMEM); + + ret = of_property_count_elems_of_size(dev_of_node(dev), of_prop, + sizeof(u32)); + if (ret < 0) { + dev_err(dev, "%s resource type ids not available\n", of_prop); + return ERR_PTR(ret); + } + res->sets = ret; + + res->desc = devm_kcalloc(dev, res->sets, sizeof(*res->desc), + GFP_KERNEL); + if (!res->desc) + return ERR_PTR(-ENOMEM); + + for (i = 0; i < res->sets; i++) { + ret = of_property_read_u32_index(dev_of_node(dev), of_prop, i, + &resource_subtype); + if (ret) + return ERR_PTR(-EINVAL); + + ret = handle->ops.rm_core_ops.get_range(handle, dev_id, + resource_subtype, + &res->desc[i].start, + &res->desc[i].num); + if (ret) { + dev_dbg(dev, "dev = %d subtype %d not allocated for this host\n", + dev_id, resource_subtype); + res->desc[i].start = 0; + res->desc[i].num = 0; + continue; + } + + dev_dbg(dev, "dev = %d, subtype = %d, start = %d, num = %d\n", + dev_id, resource_subtype, res->desc[i].start, + res->desc[i].num); + + valid_set = true; + res->desc[i].res_map = + devm_kzalloc(dev, BITS_TO_LONGS(res->desc[i].num) * + sizeof(*res->desc[i].res_map), GFP_KERNEL); + if (!res->desc[i].res_map) + return ERR_PTR(-ENOMEM); + } + raw_spin_lock_init(&res->lock); + + if (valid_set) + return res; + + return ERR_PTR(-EINVAL); +} + static int tisci_reboot_handler(struct notifier_block *nb, unsigned long mode, void *cmd) { @@ -1801,10 +3311,33 @@ static const struct ti_sci_desc ti_sci_pmmc_k2g_desc = { /* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */ .max_msgs = 20, .max_msg_size = 64, + .rm_type_map = NULL, +}; + +static struct ti_sci_rm_type_map ti_sci_am654_rm_type_map[] = { + {.dev_id = 56, .type = 0x00b}, /* GIC_IRQ */ + {.dev_id = 179, .type = 0x000}, /* MAIN_NAV_UDMASS_IA0 */ + {.dev_id = 187, .type = 0x009}, /* MAIN_NAV_RA */ + {.dev_id = 188, .type = 0x006}, /* MAIN_NAV_UDMAP */ + {.dev_id = 194, .type = 0x007}, /* MCU_NAV_UDMAP */ + {.dev_id = 195, .type = 0x00a}, /* MCU_NAV_RA */ + {.dev_id = 0, .type = 0x000}, /* end of table */ +}; + +/* Description for AM654 */ +static const struct ti_sci_desc ti_sci_pmmc_am654_desc = { + .default_host_id = 12, + /* Conservative duration */ + .max_rx_timeout_ms = 10000, + /* Limited by MBOX_TX_QUEUE_LEN. K2G can handle upto 128 messages! */ + .max_msgs = 20, + .max_msg_size = 60, + .rm_type_map = ti_sci_am654_rm_type_map, }; static const struct of_device_id ti_sci_of_match[] = { {.compatible = "ti,k2g-sci", .data = &ti_sci_pmmc_k2g_desc}, + {.compatible = "ti,am654-sci", .data = &ti_sci_pmmc_am654_desc}, { /* Sentinel */ }, }; MODULE_DEVICE_TABLE(of, ti_sci_of_match); diff --git a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h index 12bf316b68df..f0d068c03944 100644 --- a/drivers/firmware/ti_sci.h +++ b/drivers/firmware/ti_sci.h @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: BSD-3-Clause +/* SPDX-License-Identifier: BSD-3-Clause */ /* * Texas Instruments System Control Interface (TISCI) Protocol * @@ -35,6 +35,50 @@ #define TI_SCI_MSG_QUERY_CLOCK_FREQ 0x010d #define TI_SCI_MSG_GET_CLOCK_FREQ 0x010e +/* Resource Management Requests */ +#define TI_SCI_MSG_GET_RESOURCE_RANGE 0x1500 + +/* IRQ requests */ +#define TI_SCI_MSG_SET_IRQ 0x1000 +#define TI_SCI_MSG_FREE_IRQ 0x1001 + +/* NAVSS resource management */ +/* Ringacc requests */ +#define TI_SCI_MSG_RM_RING_ALLOCATE 0x1100 +#define TI_SCI_MSG_RM_RING_FREE 0x1101 +#define TI_SCI_MSG_RM_RING_RECONFIG 0x1102 +#define TI_SCI_MSG_RM_RING_RESET 0x1103 +#define TI_SCI_MSG_RM_RING_CFG 0x1110 +#define TI_SCI_MSG_RM_RING_GET_CFG 0x1111 + +/* PSI-L requests */ +#define TI_SCI_MSG_RM_PSIL_PAIR 0x1280 +#define TI_SCI_MSG_RM_PSIL_UNPAIR 0x1281 + +#define TI_SCI_MSG_RM_UDMAP_TX_ALLOC 0x1200 +#define TI_SCI_MSG_RM_UDMAP_TX_FREE 0x1201 +#define TI_SCI_MSG_RM_UDMAP_RX_ALLOC 0x1210 +#define TI_SCI_MSG_RM_UDMAP_RX_FREE 0x1211 +#define TI_SCI_MSG_RM_UDMAP_FLOW_CFG 0x1220 +#define TI_SCI_MSG_RM_UDMAP_OPT_FLOW_CFG 0x1221 + +#define TISCI_MSG_RM_UDMAP_TX_CH_CFG 0x1205 +#define TISCI_MSG_RM_UDMAP_TX_CH_GET_CFG 0x1206 +#define TISCI_MSG_RM_UDMAP_RX_CH_CFG 0x1215 +#define TISCI_MSG_RM_UDMAP_RX_CH_GET_CFG 0x1216 +#define TISCI_MSG_RM_UDMAP_FLOW_CFG 0x1230 +#define TISCI_MSG_RM_UDMAP_FLOW_SIZE_THRESH_CFG 0x1231 +#define TISCI_MSG_RM_UDMAP_FLOW_GET_CFG 0x1232 +#define TISCI_MSG_RM_UDMAP_FLOW_SIZE_THRESH_GET_CFG 0x1233 + +/* Processor Control requests */ +#define TI_SCI_MSG_PROC_REQUEST 0xc000 +#define TI_SCI_MSG_PROC_RELEASE 0xc001 +#define TI_SCI_MSG_PROC_HANDOVER 0xc005 +#define TI_SCI_MSG_SET_CONFIG 0xc100 +#define TI_SCI_MSG_SET_CTRL 0xc101 +#define TI_SCI_MSG_GET_STATUS 0xc400 + /** * struct ti_sci_msg_hdr - Generic Message Header for All messages and responses * @type: Type of messages: One of TI_SCI_MSG* values @@ -195,7 +239,8 @@ struct ti_sci_msg_req_set_device_resets { * @dev_id: Device identifier this request is for * @clk_id: Clock identifier for the device for this request. * Each device has it's own set of clock inputs. This indexes - * which clock input to modify. + * which clock input to modify. Set to 255 if clock ID is + * greater than or equal to 255. * @request_state: Request the state for the clock to be set to. * MSG_CLOCK_SW_STATE_UNREQ: The IP does not require this clock, * it can be disabled, regardless of the state of the device @@ -206,6 +251,9 @@ struct ti_sci_msg_req_set_device_resets { * being required by the device.(default) * MSG_CLOCK_SW_STATE_REQ: Configure the clock to be enabled, * regardless of the state of the device. + * @clk_id_32: Clock identifier for the device for this request. + * Only to be used if the clock ID is greater than or equal to + * 255. * * Normally, all required clocks are managed by TISCI entity, this is used * only for specific control *IF* required. Auto managed state is @@ -227,6 +275,7 @@ struct ti_sci_msg_req_set_clock_state { #define MSG_CLOCK_SW_STATE_AUTO 1 #define MSG_CLOCK_SW_STATE_REQ 2 u8 request_state; + u32 clk_id_32; } __packed; /** @@ -235,7 +284,11 @@ struct ti_sci_msg_req_set_clock_state { * @dev_id: Device identifier this request is for * @clk_id: Clock identifier for the device for this request. * Each device has it's own set of clock inputs. This indexes - * which clock input to get state of. + * which clock input to get state of. Set to 255 if the clock + * ID is greater than or equal to 255. + * @clk_id_32: Clock identifier for the device for the request. + * Only to be used if the clock ID is greater than or equal to + * 255. * * Request type is TI_SCI_MSG_GET_CLOCK_STATE, response is state * of the clock @@ -244,6 +297,7 @@ struct ti_sci_msg_req_get_clock_state { struct ti_sci_msg_hdr hdr; u32 dev_id; u8 clk_id; + u32 clk_id_32; } __packed; /** @@ -271,9 +325,13 @@ struct ti_sci_msg_resp_get_clock_state { * @dev_id: Device identifier this request is for * @clk_id: Clock identifier for the device for this request. * Each device has it's own set of clock inputs. This indexes - * which clock input to modify. + * which clock input to modify. Set to 255 if clock ID is + * greater than or equal to 255. * @parent_id: The new clock parent is selectable by an index via this - * parameter. + * parameter. Set to 255 if clock ID is greater than or + * equal to 255. + * @clk_id_32: Clock identifier if @clk_id field is 255. + * @parent_id_32: Parent identifier if @parent_id is 255. * * Request type is TI_SCI_MSG_SET_CLOCK_PARENT, response is generic * ACK / NACK message. @@ -283,6 +341,8 @@ struct ti_sci_msg_req_set_clock_parent { u32 dev_id; u8 clk_id; u8 parent_id; + u32 clk_id_32; + u32 parent_id_32; } __packed; /** @@ -291,7 +351,10 @@ struct ti_sci_msg_req_set_clock_parent { * @dev_id: Device identifier this request is for * @clk_id: Clock identifier for the device for this request. * Each device has it's own set of clock inputs. This indexes - * which clock input to get the parent for. + * which clock input to get the parent for. If this field + * contains 255, the actual clock identifier is stored in + * @clk_id_32. + * @clk_id_32: Clock identifier if the @clk_id field contains 255. * * Request type is TI_SCI_MSG_GET_CLOCK_PARENT, response is parent information */ @@ -299,25 +362,32 @@ struct ti_sci_msg_req_get_clock_parent { struct ti_sci_msg_hdr hdr; u32 dev_id; u8 clk_id; + u32 clk_id_32; } __packed; /** * struct ti_sci_msg_resp_get_clock_parent - Response with clock parent * @hdr: Generic Header - * @parent_id: The current clock parent + * @parent_id: The current clock parent. If set to 255, the current parent + * ID can be found from the @parent_id_32 field. + * @parent_id_32: Current clock parent if @parent_id field is set to + * 255. * * Response to TI_SCI_MSG_GET_CLOCK_PARENT. */ struct ti_sci_msg_resp_get_clock_parent { struct ti_sci_msg_hdr hdr; u8 parent_id; + u32 parent_id_32; } __packed; /** * struct ti_sci_msg_req_get_clock_num_parents - Request to get clock parents * @hdr: Generic header * @dev_id: Device identifier this request is for - * @clk_id: Clock identifier for the device for this request. + * @clk_id: Clock identifier for the device for this request. Set to + * 255 if clock ID is greater than or equal to 255. + * @clk_id_32: Clock identifier if the @clk_id field contains 255. * * This request provides information about how many clock parent options * are available for a given clock to a device. This is typically used @@ -330,18 +400,24 @@ struct ti_sci_msg_req_get_clock_num_parents { struct ti_sci_msg_hdr hdr; u32 dev_id; u8 clk_id; + u32 clk_id_32; } __packed; /** * struct ti_sci_msg_resp_get_clock_num_parents - Response for get clk parents * @hdr: Generic header - * @num_parents: Number of clock parents + * @num_parents: Number of clock parents. If set to 255, the actual + * number of parents is stored into @num_parents_32 + * field instead. + * @num_parents_32: Number of clock parents if @num_parents field is + * set to 255. * * Response to TI_SCI_MSG_GET_NUM_CLOCK_PARENTS */ struct ti_sci_msg_resp_get_clock_num_parents { struct ti_sci_msg_hdr hdr; u8 num_parents; + u32 num_parents_32; } __packed; /** @@ -356,7 +432,9 @@ struct ti_sci_msg_resp_get_clock_num_parents { * @max_freq_hz: The maximum allowable frequency in Hz. This is the maximum * allowable programmed frequency and does not account for clock * tolerances and jitter. - * @clk_id: Clock identifier for the device for this request. + * @clk_id: Clock identifier for the device for this request. Set to + * 255 if clock identifier is greater than or equal to 255. + * @clk_id_32: Clock identifier if @clk_id is set to 255. * * NOTE: Normally clock frequency management is automatically done by TISCI * entity. In case of specific requests, TISCI evaluates capability to achieve @@ -373,6 +451,7 @@ struct ti_sci_msg_req_query_clock_freq { u64 target_freq_hz; u64 max_freq_hz; u8 clk_id; + u32 clk_id_32; } __packed; /** @@ -400,7 +479,9 @@ struct ti_sci_msg_resp_query_clock_freq { * @max_freq_hz: The maximum allowable frequency in Hz. This is the maximum * allowable programmed frequency and does not account for clock * tolerances and jitter. - * @clk_id: Clock identifier for the device for this request. + * @clk_id: Clock identifier for the device for this request. Set to + * 255 if clock ID is greater than or equal to 255. + * @clk_id_32: Clock identifier if @clk_id field is set to 255. * * NOTE: Normally clock frequency management is automatically done by TISCI * entity. In case of specific requests, TISCI evaluates capability to achieve @@ -429,13 +510,16 @@ struct ti_sci_msg_req_set_clock_freq { u64 target_freq_hz; u64 max_freq_hz; u8 clk_id; + u32 clk_id_32; } __packed; /** * struct ti_sci_msg_req_get_clock_freq - Request to get the clock frequency * @hdr: Generic Header * @dev_id: Device identifier this request is for - * @clk_id: Clock identifier for the device for this request. + * @clk_id: Clock identifier for the device for this request. Set to + * 255 if clock ID is greater than or equal to 255. + * @clk_id_32: Clock identifier if @clk_id field is set to 255. * * NOTE: Normally clock frequency management is automatically done by TISCI * entity. In some cases, clock frequencies are configured by host. @@ -447,6 +531,7 @@ struct ti_sci_msg_req_get_clock_freq { struct ti_sci_msg_hdr hdr; u32 dev_id; u8 clk_id; + u32 clk_id_32; } __packed; /** @@ -461,4 +546,872 @@ struct ti_sci_msg_resp_get_clock_freq { u64 freq_hz; } __packed; +#define TI_SCI_IRQ_SECONDARY_HOST_INVALID 0xff + +/** + * struct ti_sci_msg_req_get_resource_range - Request to get a host's assigned + * range of resources. + * @hdr: Generic Header + * @type: Unique resource assignment type + * @subtype: Resource assignment subtype within the resource type. + * @secondary_host: Host processing entity to which the resources are + * allocated. This is required only when the destination + * host id id different from ti sci interface host id, + * else TI_SCI_IRQ_SECONDARY_HOST_INVALID can be passed. + * + * Request type is TI_SCI_MSG_GET_RESOURCE_RANGE. Responded with requested + * resource range which is of type TI_SCI_MSG_GET_RESOURCE_RANGE. + */ +struct ti_sci_msg_req_get_resource_range { + struct ti_sci_msg_hdr hdr; +#define MSG_RM_RESOURCE_TYPE_MASK GENMASK(9, 0) +#define MSG_RM_RESOURCE_SUBTYPE_MASK GENMASK(5, 0) + u16 type; + u8 subtype; + u8 secondary_host; +} __packed; + +/** + * struct ti_sci_msg_resp_get_resource_range - Response to resource get range. + * @hdr: Generic Header + * @range_start: Start index of the resource range. + * @range_num: Number of resources in the range. + * + * Response to request TI_SCI_MSG_GET_RESOURCE_RANGE. + */ +struct ti_sci_msg_resp_get_resource_range { + struct ti_sci_msg_hdr hdr; + u16 range_start; + u16 range_num; +} __packed; + +/** + * struct ti_sci_msg_req_manage_irq - Request to configure/release the route + * between the dev and the host. + * @hdr: Generic Header + * @valid_params: Bit fields defining the validity of interrupt source + * parameters. If a bit is not set, then corresponding + * field is not valid and will not be used for route set. + * Bit field definitions: + * 0 - Valid bit for @dst_id + * 1 - Valid bit for @dst_host_irq + * 2 - Valid bit for @ia_id + * 3 - Valid bit for @vint + * 4 - Valid bit for @global_event + * 5 - Valid bit for @vint_status_bit_index + * 31 - Valid bit for @secondary_host + * @src_id: IRQ source peripheral ID. + * @src_index: IRQ source index within the peripheral + * @dst_id: IRQ Destination ID. Based on the architecture it can be + * IRQ controller or host processor ID. + * @dst_host_irq: IRQ number of the destination host IRQ controller + * @ia_id: Device ID of the interrupt aggregator in which the + * vint resides. + * @vint: Virtual interrupt number if the interrupt route + * is through an interrupt aggregator. + * @global_event: Global event that is to be mapped to interrupt + * aggregator virtual interrupt status bit. + * @vint_status_bit: Virtual interrupt status bit if the interrupt route + * utilizes an interrupt aggregator status bit. + * @secondary_host: Host ID of the IRQ destination computing entity. This is + * required only when destination host id is different + * from ti sci interface host id. + * + * Request type is TI_SCI_MSG_SET/RELEASE_IRQ. + * Response is generic ACK / NACK message. + */ +struct ti_sci_msg_req_manage_irq { + struct ti_sci_msg_hdr hdr; +#define MSG_FLAG_DST_ID_VALID TI_SCI_MSG_FLAG(0) +#define MSG_FLAG_DST_HOST_IRQ_VALID TI_SCI_MSG_FLAG(1) +#define MSG_FLAG_IA_ID_VALID TI_SCI_MSG_FLAG(2) +#define MSG_FLAG_VINT_VALID TI_SCI_MSG_FLAG(3) +#define MSG_FLAG_GLB_EVNT_VALID TI_SCI_MSG_FLAG(4) +#define MSG_FLAG_VINT_STS_BIT_VALID TI_SCI_MSG_FLAG(5) +#define MSG_FLAG_SHOST_VALID TI_SCI_MSG_FLAG(31) + u32 valid_params; + u16 src_id; + u16 src_index; + u16 dst_id; + u16 dst_host_irq; + u16 ia_id; + u16 vint; + u16 global_event; + u8 vint_status_bit; + u8 secondary_host; +} __packed; + +/** + * struct ti_sci_msg_rm_ring_cfg_req - Configure a Navigator Subsystem ring + * + * Configures the non-real-time registers of a Navigator Subsystem ring. + * @hdr: Generic Header + * @valid_params: Bitfield defining validity of ring configuration parameters. + * The ring configuration fields are not valid, and will not be used for + * ring configuration, if their corresponding valid bit is zero. + * Valid bit usage: + * 0 - Valid bit for @tisci_msg_rm_ring_cfg_req addr_lo + * 1 - Valid bit for @tisci_msg_rm_ring_cfg_req addr_hi + * 2 - Valid bit for @tisci_msg_rm_ring_cfg_req count + * 3 - Valid bit for @tisci_msg_rm_ring_cfg_req mode + * 4 - Valid bit for @tisci_msg_rm_ring_cfg_req size + * 5 - Valid bit for @tisci_msg_rm_ring_cfg_req order_id + * @nav_id: Device ID of Navigator Subsystem from which the ring is allocated + * @index: ring index to be configured. + * @addr_lo: 32 LSBs of ring base address to be programmed into the ring's + * RING_BA_LO register + * @addr_hi: 16 MSBs of ring base address to be programmed into the ring's + * RING_BA_HI register. + * @count: Number of ring elements. Must be even if mode is CREDENTIALS or QM + * modes. + * @mode: Specifies the mode the ring is to be configured. + * @size: Specifies encoded ring element size. To calculate the encoded size use + * the formula (log2(size_bytes) - 2), where size_bytes cannot be + * greater than 256. + * @order_id: Specifies the ring's bus order ID. + */ +struct ti_sci_msg_rm_ring_cfg_req { + struct ti_sci_msg_hdr hdr; + u32 valid_params; + u16 nav_id; + u16 index; + u32 addr_lo; + u32 addr_hi; + u32 count; + u8 mode; + u8 size; + u8 order_id; +} __packed; + +/** + * struct ti_sci_msg_rm_ring_get_cfg_req - Get RA ring's configuration + * + * Gets the configuration of the non-real-time register fields of a ring. The + * host, or a supervisor of the host, who owns the ring must be the requesting + * host. The values of the non-real-time registers are returned in + * @ti_sci_msg_rm_ring_get_cfg_resp. + * + * @hdr: Generic Header + * @nav_id: Device ID of Navigator Subsystem from which the ring is allocated + * @index: ring index. + */ +struct ti_sci_msg_rm_ring_get_cfg_req { + struct ti_sci_msg_hdr hdr; + u16 nav_id; + u16 index; +} __packed; + +/** + * struct ti_sci_msg_rm_ring_get_cfg_resp - Ring get configuration response + * + * Response received by host processor after RM has handled + * @ti_sci_msg_rm_ring_get_cfg_req. The response contains the ring's + * non-real-time register values. + * + * @hdr: Generic Header + * @addr_lo: Ring 32 LSBs of base address + * @addr_hi: Ring 16 MSBs of base address. + * @count: Ring number of elements. + * @mode: Ring mode. + * @size: encoded Ring element size + * @order_id: ing order ID. + */ +struct ti_sci_msg_rm_ring_get_cfg_resp { + struct ti_sci_msg_hdr hdr; + u32 addr_lo; + u32 addr_hi; + u32 count; + u8 mode; + u8 size; + u8 order_id; +} __packed; + +/** + * struct ti_sci_msg_psil_pair - Pairs a PSI-L source thread to a destination + * thread + * @hdr: Generic Header + * @nav_id: SoC Navigator Subsystem device ID whose PSI-L config proxy is + * used to pair the source and destination threads. + * @src_thread: PSI-L source thread ID within the PSI-L System thread map. + * + * UDMAP transmit channels mapped to source threads will have their + * TCHAN_THRD_ID register programmed with the destination thread if the pairing + * is successful. + + * @dst_thread: PSI-L destination thread ID within the PSI-L System thread map. + * PSI-L destination threads start at index 0x8000. The request is NACK'd if + * the destination thread is not greater than or equal to 0x8000. + * + * UDMAP receive channels mapped to destination threads will have their + * RCHAN_THRD_ID register programmed with the source thread if the pairing + * is successful. + * + * Request type is TI_SCI_MSG_RM_PSIL_PAIR, response is a generic ACK or NACK + * message. + */ +struct ti_sci_msg_psil_pair { + struct ti_sci_msg_hdr hdr; + u32 nav_id; + u32 src_thread; + u32 dst_thread; +} __packed; + +/** + * struct ti_sci_msg_psil_unpair - Unpairs a PSI-L source thread from a + * destination thread + * @hdr: Generic Header + * @nav_id: SoC Navigator Subsystem device ID whose PSI-L config proxy is + * used to unpair the source and destination threads. + * @src_thread: PSI-L source thread ID within the PSI-L System thread map. + * + * UDMAP transmit channels mapped to source threads will have their + * TCHAN_THRD_ID register cleared if the unpairing is successful. + * + * @dst_thread: PSI-L destination thread ID within the PSI-L System thread map. + * PSI-L destination threads start at index 0x8000. The request is NACK'd if + * the destination thread is not greater than or equal to 0x8000. + * + * UDMAP receive channels mapped to destination threads will have their + * RCHAN_THRD_ID register cleared if the unpairing is successful. + * + * Request type is TI_SCI_MSG_RM_PSIL_UNPAIR, response is a generic ACK or NACK + * message. + */ +struct ti_sci_msg_psil_unpair { + struct ti_sci_msg_hdr hdr; + u32 nav_id; + u32 src_thread; + u32 dst_thread; +} __packed; + +/** + * struct ti_sci_msg_udmap_rx_flow_cfg - UDMAP receive flow configuration + * message + * @hdr: Generic Header + * @nav_id: SoC Navigator Subsystem device ID from which the receive flow is + * allocated + * @flow_index: UDMAP receive flow index for non-optional configuration. + * @rx_ch_index: Specifies the index of the receive channel using the flow_index + * @rx_einfo_present: UDMAP receive flow extended packet info present. + * @rx_psinfo_present: UDMAP receive flow PS words present. + * @rx_error_handling: UDMAP receive flow error handling configuration. Valid + * values are TI_SCI_RM_UDMAP_RX_FLOW_ERR_DROP/RETRY. + * @rx_desc_type: UDMAP receive flow descriptor type. It can be one of + * TI_SCI_RM_UDMAP_RX_FLOW_DESC_HOST/MONO. + * @rx_sop_offset: UDMAP receive flow start of packet offset. + * @rx_dest_qnum: UDMAP receive flow destination queue number. + * @rx_ps_location: UDMAP receive flow PS words location. + * 0 - end of packet descriptor + * 1 - Beginning of the data buffer + * @rx_src_tag_hi: UDMAP receive flow source tag high byte constant + * @rx_src_tag_lo: UDMAP receive flow source tag low byte constant + * @rx_dest_tag_hi: UDMAP receive flow destination tag high byte constant + * @rx_dest_tag_lo: UDMAP receive flow destination tag low byte constant + * @rx_src_tag_hi_sel: UDMAP receive flow source tag high byte selector + * @rx_src_tag_lo_sel: UDMAP receive flow source tag low byte selector + * @rx_dest_tag_hi_sel: UDMAP receive flow destination tag high byte selector + * @rx_dest_tag_lo_sel: UDMAP receive flow destination tag low byte selector + * @rx_size_thresh_en: UDMAP receive flow packet size based free buffer queue + * enable. If enabled, the ti_sci_rm_udmap_rx_flow_opt_cfg also need to be + * configured and sent. + * @rx_fdq0_sz0_qnum: UDMAP receive flow free descriptor queue 0. + * @rx_fdq1_qnum: UDMAP receive flow free descriptor queue 1. + * @rx_fdq2_qnum: UDMAP receive flow free descriptor queue 2. + * @rx_fdq3_qnum: UDMAP receive flow free descriptor queue 3. + * + * For detailed information on the settings, see the UDMAP section of the TRM. + */ +struct ti_sci_msg_udmap_rx_flow_cfg { + struct ti_sci_msg_hdr hdr; + u32 nav_id; + u32 flow_index; + u32 rx_ch_index; + u8 rx_einfo_present; + u8 rx_psinfo_present; + u8 rx_error_handling; + u8 rx_desc_type; + u16 rx_sop_offset; + u16 rx_dest_qnum; + u8 rx_ps_location; + u8 rx_src_tag_hi; + u8 rx_src_tag_lo; + u8 rx_dest_tag_hi; + u8 rx_dest_tag_lo; + u8 rx_src_tag_hi_sel; + u8 rx_src_tag_lo_sel; + u8 rx_dest_tag_hi_sel; + u8 rx_dest_tag_lo_sel; + u8 rx_size_thresh_en; + u16 rx_fdq0_sz0_qnum; + u16 rx_fdq1_qnum; + u16 rx_fdq2_qnum; + u16 rx_fdq3_qnum; +} __packed; + +/** + * struct rm_ti_sci_msg_udmap_rx_flow_opt_cfg - parameters for UDMAP receive + * flow optional configuration + * @hdr: Generic Header + * @nav_id: SoC Navigator Subsystem device ID from which the receive flow is + * allocated + * @flow_index: UDMAP receive flow index for optional configuration. + * @rx_ch_index: Specifies the index of the receive channel using the flow_index + * @rx_size_thresh0: UDMAP receive flow packet size threshold 0. + * @rx_size_thresh1: UDMAP receive flow packet size threshold 1. + * @rx_size_thresh2: UDMAP receive flow packet size threshold 2. + * @rx_fdq0_sz1_qnum: UDMAP receive flow free descriptor queue for size + * threshold 1. + * @rx_fdq0_sz2_qnum: UDMAP receive flow free descriptor queue for size + * threshold 2. + * @rx_fdq0_sz3_qnum: UDMAP receive flow free descriptor queue for size + * threshold 3. + * + * For detailed information on the settings, see the UDMAP section of the TRM. + */ +struct rm_ti_sci_msg_udmap_rx_flow_opt_cfg { + struct ti_sci_msg_hdr hdr; + u32 nav_id; + u32 flow_index; + u32 rx_ch_index; + u16 rx_size_thresh0; + u16 rx_size_thresh1; + u16 rx_size_thresh2; + u16 rx_fdq0_sz1_qnum; + u16 rx_fdq0_sz2_qnum; + u16 rx_fdq0_sz3_qnum; +} __packed; + +/** + * Configures a Navigator Subsystem UDMAP transmit channel + * + * Configures the non-real-time registers of a Navigator Subsystem UDMAP + * transmit channel. The channel index must be assigned to the host defined + * in the TISCI header via the RM board configuration resource assignment + * range list. + * + * @hdr: Generic Header + * + * @valid_params: Bitfield defining validity of tx channel configuration + * parameters. The tx channel configuration fields are not valid, and will not + * be used for ch configuration, if their corresponding valid bit is zero. + * Valid bit usage: + * 0 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_pause_on_err + * 1 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_atype + * 2 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_chan_type + * 3 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_fetch_size + * 4 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::txcq_qnum + * 5 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_priority + * 6 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_qos + * 7 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_orderid + * 8 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_sched_priority + * 9 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_filt_einfo + * 10 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_filt_pswords + * 11 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_supr_tdpkt + * 12 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_credit_count + * 13 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::fdepth + * 14 - Valid bit for @ref ti_sci_msg_rm_udmap_tx_ch_cfg::tx_burst_size + * + * @nav_id: SoC device ID of Navigator Subsystem where tx channel is located + * + * @index: UDMAP transmit channel index. + * + * @tx_pause_on_err: UDMAP transmit channel pause on error configuration to + * be programmed into the tx_pause_on_err field of the channel's TCHAN_TCFG + * register. + * + * @tx_filt_einfo: UDMAP transmit channel extended packet information passing + * configuration to be programmed into the tx_filt_einfo field of the + * channel's TCHAN_TCFG register. + * + * @tx_filt_pswords: UDMAP transmit channel protocol specific word passing + * configuration to be programmed into the tx_filt_pswords field of the + * channel's TCHAN_TCFG register. + * + * @tx_atype: UDMAP transmit channel non Ring Accelerator access pointer + * interpretation configuration to be programmed into the tx_atype field of + * the channel's TCHAN_TCFG register. + * + * @tx_chan_type: UDMAP transmit channel functional channel type and work + * passing mechanism configuration to be programmed into the tx_chan_type + * field of the channel's TCHAN_TCFG register. + * + * @tx_supr_tdpkt: UDMAP transmit channel teardown packet generation suppression + * configuration to be programmed into the tx_supr_tdpkt field of the channel's + * TCHAN_TCFG register. + * + * @tx_fetch_size: UDMAP transmit channel number of 32-bit descriptor words to + * fetch configuration to be programmed into the tx_fetch_size field of the + * channel's TCHAN_TCFG register. The user must make sure to set the maximum + * word count that can pass through the channel for any allowed descriptor type. + * + * @tx_credit_count: UDMAP transmit channel transfer request credit count + * configuration to be programmed into the count field of the TCHAN_TCREDIT + * register. Specifies how many credits for complete TRs are available. + * + * @txcq_qnum: UDMAP transmit channel completion queue configuration to be + * programmed into the txcq_qnum field of the TCHAN_TCQ register. The specified + * completion queue must be assigned to the host, or a subordinate of the host, + * requesting configuration of the transmit channel. + * + * @tx_priority: UDMAP transmit channel transmit priority value to be programmed + * into the priority field of the channel's TCHAN_TPRI_CTRL register. + * + * @tx_qos: UDMAP transmit channel transmit qos value to be programmed into the + * qos field of the channel's TCHAN_TPRI_CTRL register. + * + * @tx_orderid: UDMAP transmit channel bus order id value to be programmed into + * the orderid field of the channel's TCHAN_TPRI_CTRL register. + * + * @fdepth: UDMAP transmit channel FIFO depth configuration to be programmed + * into the fdepth field of the TCHAN_TFIFO_DEPTH register. Sets the number of + * Tx FIFO bytes which are allowed to be stored for the channel. Check the UDMAP + * section of the TRM for restrictions regarding this parameter. + * + * @tx_sched_priority: UDMAP transmit channel tx scheduling priority + * configuration to be programmed into the priority field of the channel's + * TCHAN_TST_SCHED register. + * + * @tx_burst_size: UDMAP transmit channel burst size configuration to be + * programmed into the tx_burst_size field of the TCHAN_TCFG register. + */ +struct ti_sci_msg_rm_udmap_tx_ch_cfg_req { + struct ti_sci_msg_hdr hdr; + u32 valid_params; + u16 nav_id; + u16 index; + u8 tx_pause_on_err; + u8 tx_filt_einfo; + u8 tx_filt_pswords; + u8 tx_atype; + u8 tx_chan_type; + u8 tx_supr_tdpkt; + u16 tx_fetch_size; + u8 tx_credit_count; + u16 txcq_qnum; + u8 tx_priority; + u8 tx_qos; + u8 tx_orderid; + u16 fdepth; + u8 tx_sched_priority; + u8 tx_burst_size; +} __packed; + +/** + * Configures a Navigator Subsystem UDMAP receive channel + * + * Configures the non-real-time registers of a Navigator Subsystem UDMAP + * receive channel. The channel index must be assigned to the host defined + * in the TISCI header via the RM board configuration resource assignment + * range list. + * + * @hdr: Generic Header + * + * @valid_params: Bitfield defining validity of rx channel configuration + * parameters. + * The rx channel configuration fields are not valid, and will not be used for + * ch configuration, if their corresponding valid bit is zero. + * Valid bit usage: + * 0 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_pause_on_err + * 1 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_atype + * 2 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_chan_type + * 3 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_fetch_size + * 4 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rxcq_qnum + * 5 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_priority + * 6 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_qos + * 7 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_orderid + * 8 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_sched_priority + * 9 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::flowid_start + * 10 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::flowid_cnt + * 11 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_ignore_short + * 12 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_ignore_long + * 14 - Valid bit for @ti_sci_msg_rm_udmap_rx_ch_cfg_req::rx_burst_size + * + * @nav_id: SoC device ID of Navigator Subsystem where rx channel is located + * + * @index: UDMAP receive channel index. + * + * @rx_fetch_size: UDMAP receive channel number of 32-bit descriptor words to + * fetch configuration to be programmed into the rx_fetch_size field of the + * channel's RCHAN_RCFG register. + * + * @rxcq_qnum: UDMAP receive channel completion queue configuration to be + * programmed into the rxcq_qnum field of the RCHAN_RCQ register. + * The specified completion queue must be assigned to the host, or a subordinate + * of the host, requesting configuration of the receive channel. + * + * @rx_priority: UDMAP receive channel receive priority value to be programmed + * into the priority field of the channel's RCHAN_RPRI_CTRL register. + * + * @rx_qos: UDMAP receive channel receive qos value to be programmed into the + * qos field of the channel's RCHAN_RPRI_CTRL register. + * + * @rx_orderid: UDMAP receive channel bus order id value to be programmed into + * the orderid field of the channel's RCHAN_RPRI_CTRL register. + * + * @rx_sched_priority: UDMAP receive channel rx scheduling priority + * configuration to be programmed into the priority field of the channel's + * RCHAN_RST_SCHED register. + * + * @flowid_start: UDMAP receive channel additional flows starting index + * configuration to program into the flow_start field of the RCHAN_RFLOW_RNG + * register. Specifies the starting index for flow IDs the receive channel is to + * make use of beyond the default flow. flowid_start and @ref flowid_cnt must be + * set as valid and configured together. The starting flow ID set by + * @ref flowid_cnt must be a flow index within the Navigator Subsystem's subset + * of flows beyond the default flows statically mapped to receive channels. + * The additional flows must be assigned to the host, or a subordinate of the + * host, requesting configuration of the receive channel. + * + * @flowid_cnt: UDMAP receive channel additional flows count configuration to + * program into the flowid_cnt field of the RCHAN_RFLOW_RNG register. + * This field specifies how many flow IDs are in the additional contiguous range + * of legal flow IDs for the channel. @ref flowid_start and flowid_cnt must be + * set as valid and configured together. Disabling the valid_params field bit + * for flowid_cnt indicates no flow IDs other than the default are to be + * allocated and used by the receive channel. @ref flowid_start plus flowid_cnt + * cannot be greater than the number of receive flows in the receive channel's + * Navigator Subsystem. The additional flows must be assigned to the host, or a + * subordinate of the host, requesting configuration of the receive channel. + * + * @rx_pause_on_err: UDMAP receive channel pause on error configuration to be + * programmed into the rx_pause_on_err field of the channel's RCHAN_RCFG + * register. + * + * @rx_atype: UDMAP receive channel non Ring Accelerator access pointer + * interpretation configuration to be programmed into the rx_atype field of the + * channel's RCHAN_RCFG register. + * + * @rx_chan_type: UDMAP receive channel functional channel type and work passing + * mechanism configuration to be programmed into the rx_chan_type field of the + * channel's RCHAN_RCFG register. + * + * @rx_ignore_short: UDMAP receive channel short packet treatment configuration + * to be programmed into the rx_ignore_short field of the RCHAN_RCFG register. + * + * @rx_ignore_long: UDMAP receive channel long packet treatment configuration to + * be programmed into the rx_ignore_long field of the RCHAN_RCFG register. + * + * @rx_burst_size: UDMAP receive channel burst size configuration to be + * programmed into the rx_burst_size field of the RCHAN_RCFG register. + */ +struct ti_sci_msg_rm_udmap_rx_ch_cfg_req { + struct ti_sci_msg_hdr hdr; + u32 valid_params; + u16 nav_id; + u16 index; + u16 rx_fetch_size; + u16 rxcq_qnum; + u8 rx_priority; + u8 rx_qos; + u8 rx_orderid; + u8 rx_sched_priority; + u16 flowid_start; + u16 flowid_cnt; + u8 rx_pause_on_err; + u8 rx_atype; + u8 rx_chan_type; + u8 rx_ignore_short; + u8 rx_ignore_long; + u8 rx_burst_size; +} __packed; + +/** + * Configures a Navigator Subsystem UDMAP receive flow + * + * Configures a Navigator Subsystem UDMAP receive flow's registers. + * Configuration does not include the flow registers which handle size-based + * free descriptor queue routing. + * + * The flow index must be assigned to the host defined in the TISCI header via + * the RM board configuration resource assignment range list. + * + * @hdr: Standard TISCI header + * + * @valid_params + * Bitfield defining validity of rx flow configuration parameters. The + * rx flow configuration fields are not valid, and will not be used for flow + * configuration, if their corresponding valid bit is zero. Valid bit usage: + * 0 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_einfo_present + * 1 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_psinfo_present + * 2 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_error_handling + * 3 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_desc_type + * 4 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_sop_offset + * 5 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_dest_qnum + * 6 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_src_tag_hi + * 7 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_src_tag_lo + * 8 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_dest_tag_hi + * 9 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_dest_tag_lo + * 10 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_src_tag_hi_sel + * 11 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_src_tag_lo_sel + * 12 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_dest_tag_hi_sel + * 13 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_dest_tag_lo_sel + * 14 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_fdq0_sz0_qnum + * 15 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_fdq1_sz0_qnum + * 16 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_fdq2_sz0_qnum + * 17 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_fdq3_sz0_qnum + * 18 - Valid bit for @tisci_msg_rm_udmap_flow_cfg_req::rx_ps_location + * + * @nav_id: SoC device ID of Navigator Subsystem from which the receive flow is + * allocated + * + * @flow_index: UDMAP receive flow index for non-optional configuration. + * + * @rx_einfo_present: + * UDMAP receive flow extended packet info present configuration to be + * programmed into the rx_einfo_present field of the flow's RFLOW_RFA register. + * + * @rx_psinfo_present: + * UDMAP receive flow PS words present configuration to be programmed into the + * rx_psinfo_present field of the flow's RFLOW_RFA register. + * + * @rx_error_handling: + * UDMAP receive flow error handling configuration to be programmed into the + * rx_error_handling field of the flow's RFLOW_RFA register. + * + * @rx_desc_type: + * UDMAP receive flow descriptor type configuration to be programmed into the + * rx_desc_type field field of the flow's RFLOW_RFA register. + * + * @rx_sop_offset: + * UDMAP receive flow start of packet offset configuration to be programmed + * into the rx_sop_offset field of the RFLOW_RFA register. See the UDMAP + * section of the TRM for more information on this setting. Valid values for + * this field are 0-255 bytes. + * + * @rx_dest_qnum: + * UDMAP receive flow destination queue configuration to be programmed into the + * rx_dest_qnum field of the flow's RFLOW_RFA register. The specified + * destination queue must be valid within the Navigator Subsystem and must be + * owned by the host, or a subordinate of the host, requesting allocation and + * configuration of the receive flow. + * + * @rx_src_tag_hi: + * UDMAP receive flow source tag high byte constant configuration to be + * programmed into the rx_src_tag_hi field of the flow's RFLOW_RFB register. + * See the UDMAP section of the TRM for more information on this setting. + * + * @rx_src_tag_lo: + * UDMAP receive flow source tag low byte constant configuration to be + * programmed into the rx_src_tag_lo field of the flow's RFLOW_RFB register. + * See the UDMAP section of the TRM for more information on this setting. + * + * @rx_dest_tag_hi: + * UDMAP receive flow destination tag high byte constant configuration to be + * programmed into the rx_dest_tag_hi field of the flow's RFLOW_RFB register. + * See the UDMAP section of the TRM for more information on this setting. + * + * @rx_dest_tag_lo: + * UDMAP receive flow destination tag low byte constant configuration to be + * programmed into the rx_dest_tag_lo field of the flow's RFLOW_RFB register. + * See the UDMAP section of the TRM for more information on this setting. + * + * @rx_src_tag_hi_sel: + * UDMAP receive flow source tag high byte selector configuration to be + * programmed into the rx_src_tag_hi_sel field of the RFLOW_RFC register. See + * the UDMAP section of the TRM for more information on this setting. + * + * @rx_src_tag_lo_sel: + * UDMAP receive flow source tag low byte selector configuration to be + * programmed into the rx_src_tag_lo_sel field of the RFLOW_RFC register. See + * the UDMAP section of the TRM for more information on this setting. + * + * @rx_dest_tag_hi_sel: + * UDMAP receive flow destination tag high byte selector configuration to be + * programmed into the rx_dest_tag_hi_sel field of the RFLOW_RFC register. See + * the UDMAP section of the TRM for more information on this setting. + * + * @rx_dest_tag_lo_sel: + * UDMAP receive flow destination tag low byte selector configuration to be + * programmed into the rx_dest_tag_lo_sel field of the RFLOW_RFC register. See + * the UDMAP section of the TRM for more information on this setting. + * + * @rx_fdq0_sz0_qnum: + * UDMAP receive flow free descriptor queue 0 configuration to be programmed + * into the rx_fdq0_sz0_qnum field of the flow's RFLOW_RFD register. See the + * UDMAP section of the TRM for more information on this setting. The specified + * free queue must be valid within the Navigator Subsystem and must be owned + * by the host, or a subordinate of the host, requesting allocation and + * configuration of the receive flow. + * + * @rx_fdq1_qnum: + * UDMAP receive flow free descriptor queue 1 configuration to be programmed + * into the rx_fdq1_qnum field of the flow's RFLOW_RFD register. See the + * UDMAP section of the TRM for more information on this setting. The specified + * free queue must be valid within the Navigator Subsystem and must be owned + * by the host, or a subordinate of the host, requesting allocation and + * configuration of the receive flow. + * + * @rx_fdq2_qnum: + * UDMAP receive flow free descriptor queue 2 configuration to be programmed + * into the rx_fdq2_qnum field of the flow's RFLOW_RFE register. See the + * UDMAP section of the TRM for more information on this setting. The specified + * free queue must be valid within the Navigator Subsystem and must be owned + * by the host, or a subordinate of the host, requesting allocation and + * configuration of the receive flow. + * + * @rx_fdq3_qnum: + * UDMAP receive flow free descriptor queue 3 configuration to be programmed + * into the rx_fdq3_qnum field of the flow's RFLOW_RFE register. See the + * UDMAP section of the TRM for more information on this setting. The specified + * free queue must be valid within the Navigator Subsystem and must be owned + * by the host, or a subordinate of the host, requesting allocation and + * configuration of the receive flow. + * + * @rx_ps_location: + * UDMAP receive flow PS words location configuration to be programmed into the + * rx_ps_location field of the flow's RFLOW_RFA register. + */ +struct ti_sci_msg_rm_udmap_flow_cfg_req { + struct ti_sci_msg_hdr hdr; + u32 valid_params; + u16 nav_id; + u16 flow_index; + u8 rx_einfo_present; + u8 rx_psinfo_present; + u8 rx_error_handling; + u8 rx_desc_type; + u16 rx_sop_offset; + u16 rx_dest_qnum; + u8 rx_src_tag_hi; + u8 rx_src_tag_lo; + u8 rx_dest_tag_hi; + u8 rx_dest_tag_lo; + u8 rx_src_tag_hi_sel; + u8 rx_src_tag_lo_sel; + u8 rx_dest_tag_hi_sel; + u8 rx_dest_tag_lo_sel; + u16 rx_fdq0_sz0_qnum; + u16 rx_fdq1_qnum; + u16 rx_fdq2_qnum; + u16 rx_fdq3_qnum; + u8 rx_ps_location; +} __packed; + +/** + * struct ti_sci_msg_req_proc_request - Request a processor + * @hdr: Generic Header + * @processor_id: ID of processor being requested + * + * Request type is TI_SCI_MSG_PROC_REQUEST, response is a generic ACK/NACK + * message. + */ +struct ti_sci_msg_req_proc_request { + struct ti_sci_msg_hdr hdr; + u8 processor_id; +} __packed; + +/** + * struct ti_sci_msg_req_proc_release - Release a processor + * @hdr: Generic Header + * @processor_id: ID of processor being released + * + * Request type is TI_SCI_MSG_PROC_RELEASE, response is a generic ACK/NACK + * message. + */ +struct ti_sci_msg_req_proc_release { + struct ti_sci_msg_hdr hdr; + u8 processor_id; +} __packed; + +/** + * struct ti_sci_msg_req_proc_handover - Handover a processor to a host + * @hdr: Generic Header + * @processor_id: ID of processor being handed over + * @host_id: Host ID the control needs to be transferred to + * + * Request type is TI_SCI_MSG_PROC_HANDOVER, response is a generic ACK/NACK + * message. + */ +struct ti_sci_msg_req_proc_handover { + struct ti_sci_msg_hdr hdr; + u8 processor_id; + u8 host_id; +} __packed; + +/* Boot Vector masks */ +#define TI_SCI_ADDR_LOW_MASK GENMASK_ULL(31, 0) +#define TI_SCI_ADDR_HIGH_MASK GENMASK_ULL(63, 32) +#define TI_SCI_ADDR_HIGH_SHIFT 32 + +/** + * struct ti_sci_msg_req_set_config - Set Processor boot configuration + * @hdr: Generic Header + * @processor_id: ID of processor being configured + * @bootvector_low: Lower 32 bit address (Little Endian) of boot vector + * @bootvector_high: Higher 32 bit address (Little Endian) of boot vector + * @config_flags_set: Optional Processor specific Config Flags to set. + * Setting a bit here implies the corresponding mode + * will be set + * @config_flags_clear: Optional Processor specific Config Flags to clear. + * Setting a bit here implies the corresponding mode + * will be cleared + * + * Request type is TI_SCI_MSG_PROC_HANDOVER, response is a generic ACK/NACK + * message. + */ +struct ti_sci_msg_req_set_config { + struct ti_sci_msg_hdr hdr; + u8 processor_id; + u32 bootvector_low; + u32 bootvector_high; + u32 config_flags_set; + u32 config_flags_clear; +} __packed; + +/** + * struct ti_sci_msg_req_set_ctrl - Set Processor boot control flags + * @hdr: Generic Header + * @processor_id: ID of processor being configured + * @control_flags_set: Optional Processor specific Control Flags to set. + * Setting a bit here implies the corresponding mode + * will be set + * @control_flags_clear:Optional Processor specific Control Flags to clear. + * Setting a bit here implies the corresponding mode + * will be cleared + * + * Request type is TI_SCI_MSG_SET_CTRL, response is a generic ACK/NACK + * message. + */ +struct ti_sci_msg_req_set_ctrl { + struct ti_sci_msg_hdr hdr; + u8 processor_id; + u32 control_flags_set; + u32 control_flags_clear; +} __packed; + +/** + * struct ti_sci_msg_req_get_status - Processor boot status request + * @hdr: Generic Header + * @processor_id: ID of processor whose status is being requested + * + * Request type is TI_SCI_MSG_GET_STATUS, response is an appropriate + * message, or NACK in case of inability to satisfy request. + */ +struct ti_sci_msg_req_get_status { + struct ti_sci_msg_hdr hdr; + u8 processor_id; +} __packed; + +/** + * struct ti_sci_msg_resp_get_status - Processor boot status response + * @hdr: Generic Header + * @processor_id: ID of processor whose status is returned + * @bootvector_low: Lower 32 bit address (Little Endian) of boot vector + * @bootvector_high: Higher 32 bit address (Little Endian) of boot vector + * @config_flags: Optional Processor specific Config Flags set currently + * @control_flags: Optional Processor specific Control Flags set currently + * @status_flags: Optional Processor specific Status Flags set currently + * + * Response structure to a TI_SCI_MSG_GET_STATUS request. + */ +struct ti_sci_msg_resp_get_status { + struct ti_sci_msg_hdr hdr; + u8 processor_id; + u32 bootvector_low; + u32 bootvector_high; + u32 config_flags; + u32 control_flags; + u32 status_flags; +} __packed; + #endif /* __TI_SCI_H */ diff --git a/drivers/firmware/trusted_foundations.c b/drivers/firmware/trusted_foundations.c new file mode 100644 index 000000000000..fc544e19b0a1 --- /dev/null +++ b/drivers/firmware/trusted_foundations.c @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Trusted Foundations support for ARM CPUs + * + * Copyright (c) 2013, NVIDIA Corporation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/of.h> + +#include <linux/firmware/trusted_foundations.h> + +#include <asm/firmware.h> +#include <asm/hardware/cache-l2x0.h> +#include <asm/outercache.h> + +#define TF_CACHE_MAINT 0xfffff100 + +#define TF_CACHE_ENABLE 1 +#define TF_CACHE_DISABLE 2 + +#define TF_SET_CPU_BOOT_ADDR_SMC 0xfffff200 + +#define TF_CPU_PM 0xfffffffc +#define TF_CPU_PM_S3 0xffffffe3 +#define TF_CPU_PM_S2 0xffffffe6 +#define TF_CPU_PM_S2_NO_MC_CLK 0xffffffe5 +#define TF_CPU_PM_S1 0xffffffe4 +#define TF_CPU_PM_S1_NOFLUSH_L2 0xffffffe7 + +static unsigned long cpu_boot_addr; + +static void tf_generic_smc(u32 type, u32 arg1, u32 arg2) +{ + register u32 r0 asm("r0") = type; + register u32 r1 asm("r1") = arg1; + register u32 r2 asm("r2") = arg2; + + asm volatile( + ".arch_extension sec\n\t" + "stmfd sp!, {r4 - r11}\n\t" + __asmeq("%0", "r0") + __asmeq("%1", "r1") + __asmeq("%2", "r2") + "mov r3, #0\n\t" + "mov r4, #0\n\t" + "smc #0\n\t" + "ldmfd sp!, {r4 - r11}\n\t" + : + : "r" (r0), "r" (r1), "r" (r2) + : "memory", "r3", "r12", "lr"); +} + +static int tf_set_cpu_boot_addr(int cpu, unsigned long boot_addr) +{ + cpu_boot_addr = boot_addr; + tf_generic_smc(TF_SET_CPU_BOOT_ADDR_SMC, cpu_boot_addr, 0); + + return 0; +} + +static int tf_prepare_idle(unsigned long mode) +{ + switch (mode) { + case TF_PM_MODE_LP0: + tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S3, cpu_boot_addr); + break; + + case TF_PM_MODE_LP1: + tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S2, cpu_boot_addr); + break; + + case TF_PM_MODE_LP1_NO_MC_CLK: + tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S2_NO_MC_CLK, + cpu_boot_addr); + break; + + case TF_PM_MODE_LP2: + tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S1, cpu_boot_addr); + break; + + case TF_PM_MODE_LP2_NOFLUSH_L2: + tf_generic_smc(TF_CPU_PM, TF_CPU_PM_S1_NOFLUSH_L2, + cpu_boot_addr); + break; + + default: + return -EINVAL; + } + + return 0; +} + +#ifdef CONFIG_CACHE_L2X0 +static void tf_cache_write_sec(unsigned long val, unsigned int reg) +{ + u32 l2x0_way_mask = 0xff; + + switch (reg) { + case L2X0_CTRL: + if (l2x0_saved_regs.aux_ctrl & L310_AUX_CTRL_ASSOCIATIVITY_16) + l2x0_way_mask = 0xffff; + + if (val == L2X0_CTRL_EN) + tf_generic_smc(TF_CACHE_MAINT, TF_CACHE_ENABLE, + l2x0_saved_regs.aux_ctrl); + else + tf_generic_smc(TF_CACHE_MAINT, TF_CACHE_DISABLE, + l2x0_way_mask); + break; + + default: + break; + } +} + +static int tf_init_cache(void) +{ + outer_cache.write_sec = tf_cache_write_sec; + + return 0; +} +#endif /* CONFIG_CACHE_L2X0 */ + +static const struct firmware_ops trusted_foundations_ops = { + .set_cpu_boot_addr = tf_set_cpu_boot_addr, + .prepare_idle = tf_prepare_idle, +#ifdef CONFIG_CACHE_L2X0 + .l2x0_init = tf_init_cache, +#endif +}; + +void register_trusted_foundations(struct trusted_foundations_platform_data *pd) +{ + /* + * we are not using version information for now since currently + * supported SMCs are compatible with all TF releases + */ + register_firmware_ops(&trusted_foundations_ops); +} + +void of_register_trusted_foundations(void) +{ + struct device_node *node; + struct trusted_foundations_platform_data pdata; + int err; + + node = of_find_compatible_node(NULL, NULL, "tlm,trusted-foundations"); + if (!node) + return; + + err = of_property_read_u32(node, "tlm,version-major", + &pdata.version_major); + if (err != 0) + panic("Trusted Foundation: missing version-major property\n"); + err = of_property_read_u32(node, "tlm,version-minor", + &pdata.version_minor); + if (err != 0) + panic("Trusted Foundation: missing version-minor property\n"); + register_trusted_foundations(&pdata); +} + +bool trusted_foundations_registered(void) +{ + return firmware_ops == &trusted_foundations_ops; +} diff --git a/drivers/firmware/xilinx/Kconfig b/drivers/firmware/xilinx/Kconfig index 8f44b9cd295a..bd33bbf70daf 100644 --- a/drivers/firmware/xilinx/Kconfig +++ b/drivers/firmware/xilinx/Kconfig @@ -6,6 +6,7 @@ menu "Zynq MPSoC Firmware Drivers" config ZYNQMP_FIRMWARE bool "Enable Xilinx Zynq MPSoC firmware interface" + select MFD_CORE help Firmware interface driver is used by different drivers to communicate with the firmware for diff --git a/drivers/firmware/xilinx/zynqmp-debug.c b/drivers/firmware/xilinx/zynqmp-debug.c index 2771df6df379..c6d0724da4db 100644 --- a/drivers/firmware/xilinx/zynqmp-debug.c +++ b/drivers/firmware/xilinx/zynqmp-debug.c @@ -90,9 +90,6 @@ static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret) int ret; struct zynqmp_pm_query_data qdata = {0}; - if (!eemi_ops) - return -ENXIO; - switch (pm_id) { case PM_GET_API_VERSION: ret = eemi_ops->get_api_version(&pm_api_version); @@ -163,21 +160,14 @@ static ssize_t zynqmp_pm_debugfs_api_write(struct file *file, strcpy(debugfs_buf, ""); - if (*off != 0 || len == 0) + if (*off != 0 || len <= 1 || len > PAGE_SIZE - 1) return -EINVAL; - kern_buff = kzalloc(len, GFP_KERNEL); - if (!kern_buff) - return -ENOMEM; - + kern_buff = memdup_user_nul(ptr, len); + if (IS_ERR(kern_buff)) + return PTR_ERR(kern_buff); tmp_buff = kern_buff; - ret = strncpy_from_user(kern_buff, ptr, len); - if (ret < 0) { - ret = -EFAULT; - goto err; - } - /* Read the API name from a user request */ pm_api_req = strsep(&kern_buff, " "); diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 9a1c72a9280f..fd3d83745208 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -14,6 +14,7 @@ #include <linux/compiler.h> #include <linux/device.h> #include <linux/init.h> +#include <linux/mfd/core.h> #include <linux/module.h> #include <linux/of.h> #include <linux/of_platform.h> @@ -23,6 +24,14 @@ #include <linux/firmware/xlnx-zynqmp.h> #include "zynqmp-debug.h" +static const struct zynqmp_eemi_ops *eemi_ops_tbl; + +static const struct mfd_cell firmware_devs[] = { + { + .name = "zynqmp_power_controller", + }, +}; + /** * zynqmp_pm_ret_code() - Convert PMU-FW error codes to Linux error codes * @ret_status: PMUFW return code @@ -187,6 +196,29 @@ static int zynqmp_pm_get_api_version(u32 *version) } /** + * zynqmp_pm_get_chipid - Get silicon ID registers + * @idcode: IDCODE register + * @version: version register + * + * Return: Returns the status of the operation and the idcode and version + * registers in @idcode and @version. + */ +static int zynqmp_pm_get_chipid(u32 *idcode, u32 *version) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (!idcode || !version) + return -EINVAL; + + ret = zynqmp_pm_invoke_fn(PM_GET_CHIPID, 0, 0, 0, 0, ret_payload); + *idcode = ret_payload[1]; + *version = ret_payload[2]; + + return ret; +} + +/** * zynqmp_pm_get_trustzone_version() - Get secure trustzone firmware version * @version: Returned version value * @@ -469,8 +501,172 @@ static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, arg1, arg2, out); } +/** + * zynqmp_pm_reset_assert - Request setting of reset (1 - assert, 0 - release) + * @reset: Reset to be configured + * @assert_flag: Flag stating should reset be asserted (1) or + * released (0) + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_reset_assert(const enum zynqmp_pm_reset reset, + const enum zynqmp_pm_reset_action assert_flag) +{ + return zynqmp_pm_invoke_fn(PM_RESET_ASSERT, reset, assert_flag, + 0, 0, NULL); +} + +/** + * zynqmp_pm_reset_get_status - Get status of the reset + * @reset: Reset whose status should be returned + * @status: Returned status + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset, + u32 *status) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (!status) + return -EINVAL; + + ret = zynqmp_pm_invoke_fn(PM_RESET_GET_STATUS, reset, 0, + 0, 0, ret_payload); + *status = ret_payload[1]; + + return ret; +} + +/** + * zynqmp_pm_fpga_load - Perform the fpga load + * @address: Address to write to + * @size: pl bitstream size + * @flags: Bitstream type + * -XILINX_ZYNQMP_PM_FPGA_FULL: FPGA full reconfiguration + * -XILINX_ZYNQMP_PM_FPGA_PARTIAL: FPGA partial reconfiguration + * + * This function provides access to pmufw. To transfer + * the required bitstream into PL. + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_fpga_load(const u64 address, const u32 size, + const u32 flags) +{ + return zynqmp_pm_invoke_fn(PM_FPGA_LOAD, lower_32_bits(address), + upper_32_bits(address), size, flags, NULL); +} + +/** + * zynqmp_pm_fpga_get_status - Read value from PCAP status register + * @value: Value to read + * + * This function provides access to the pmufw to get the PCAP + * status + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_fpga_get_status(u32 *value) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (!value) + return -EINVAL; + + ret = zynqmp_pm_invoke_fn(PM_FPGA_GET_STATUS, 0, 0, 0, 0, ret_payload); + *value = ret_payload[1]; + + return ret; +} + +/** + * zynqmp_pm_init_finalize() - PM call to inform firmware that the caller + * master has initialized its own power management + * + * This API function is to be used for notify the power management controller + * about the completed power management initialization. + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_init_finalize(void) +{ + return zynqmp_pm_invoke_fn(PM_PM_INIT_FINALIZE, 0, 0, 0, 0, NULL); +} + +/** + * zynqmp_pm_set_suspend_mode() - Set system suspend mode + * @mode: Mode to set for system suspend + * + * This API function is used to set mode of system suspend. + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_set_suspend_mode(u32 mode) +{ + return zynqmp_pm_invoke_fn(PM_SET_SUSPEND_MODE, mode, 0, 0, 0, NULL); +} + +/** + * zynqmp_pm_request_node() - Request a node with specific capabilities + * @node: Node ID of the slave + * @capabilities: Requested capabilities of the slave + * @qos: Quality of service (not supported) + * @ack: Flag to specify whether acknowledge is requested + * + * This function is used by master to request particular node from firmware. + * Every master must request node before using it. + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_request_node(const u32 node, const u32 capabilities, + const u32 qos, + const enum zynqmp_pm_request_ack ack) +{ + return zynqmp_pm_invoke_fn(PM_REQUEST_NODE, node, capabilities, + qos, ack, NULL); +} + +/** + * zynqmp_pm_release_node() - Release a node + * @node: Node ID of the slave + * + * This function is used by master to inform firmware that master + * has released node. Once released, master must not use that node + * without re-request. + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_release_node(const u32 node) +{ + return zynqmp_pm_invoke_fn(PM_RELEASE_NODE, node, 0, 0, 0, NULL); +} + +/** + * zynqmp_pm_set_requirement() - PM call to set requirement for PM slaves + * @node: Node ID of the slave + * @capabilities: Requested capabilities of the slave + * @qos: Quality of service (not supported) + * @ack: Flag to specify whether acknowledge is requested + * + * This API function is to be used for slaves a PU already has requested + * to change its capabilities. + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities, + const u32 qos, + const enum zynqmp_pm_request_ack ack) +{ + return zynqmp_pm_invoke_fn(PM_SET_REQUIREMENT, node, capabilities, + qos, ack, NULL); +} + static const struct zynqmp_eemi_ops eemi_ops = { .get_api_version = zynqmp_pm_get_api_version, + .get_chipid = zynqmp_pm_get_chipid, .query_data = zynqmp_pm_query_data, .clock_enable = zynqmp_pm_clock_enable, .clock_disable = zynqmp_pm_clock_disable, @@ -482,6 +678,15 @@ static const struct zynqmp_eemi_ops eemi_ops = { .clock_setparent = zynqmp_pm_clock_setparent, .clock_getparent = zynqmp_pm_clock_getparent, .ioctl = zynqmp_pm_ioctl, + .reset_assert = zynqmp_pm_reset_assert, + .reset_get_status = zynqmp_pm_reset_get_status, + .init_finalize = zynqmp_pm_init_finalize, + .set_suspend_mode = zynqmp_pm_set_suspend_mode, + .request_node = zynqmp_pm_request_node, + .release_node = zynqmp_pm_release_node, + .set_requirement = zynqmp_pm_set_requirement, + .fpga_load = zynqmp_pm_fpga_load, + .fpga_get_status = zynqmp_pm_fpga_get_status, }; /** @@ -491,7 +696,11 @@ static const struct zynqmp_eemi_ops eemi_ops = { */ const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void) { - return &eemi_ops; + if (eemi_ops_tbl) + return eemi_ops_tbl; + else + return ERR_PTR(-EPROBE_DEFER); + } EXPORT_SYMBOL_GPL(zynqmp_pm_get_eemi_ops); @@ -536,13 +745,24 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) pr_info("%s Trustzone version v%d.%d\n", __func__, pm_tz_version >> 16, pm_tz_version & 0xFFFF); + /* Assign eemi_ops_table */ + eemi_ops_tbl = &eemi_ops; + zynqmp_pm_api_debugfs_init(); + ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, firmware_devs, + ARRAY_SIZE(firmware_devs), NULL, 0, NULL); + if (ret) { + dev_err(&pdev->dev, "failed to add MFD devices %d\n", ret); + return ret; + } + return of_platform_populate(dev->of_node, NULL, NULL, dev); } static int zynqmp_firmware_remove(struct platform_device *pdev) { + mfd_remove_devices(&pdev->dev); zynqmp_pm_api_debugfs_exit(); return 0; |