From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=217.140.110.172 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1793638480; b=kdbwLbaqqE/QpqL8NaUvkJnPxGpfCU4vHwHfAcTB4cozK88ZXpqKPsrox3J+WfhCizMa KEKgHpxc0jYl+O7O1UsO6h2E6+Lp4Ght6VnW1W2t5U93G1g9zxbSdl1ohnAB4QZB4JOvX csT1f+sqpnY22CMPGzVzzQbTlIQHm7RJ68HSANjbi+e8lqD/rP/xHm1gJgJkOEGnXwJeB HzbTsM26PpjQN4rKt3JLl5C0nffurBVHviDEjTql8jDSx1g8mq0otjxbqRKfnXmlhY9ou fdqOaW6eBhCMr9vTq+pN8ebfzF7PC4TowNJE+JtzrJHjK/FJPdcEPD0qmUwM//Mst8g== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1793638480; h=DMARC-Filter:Received:Received:DKIM-Signature:From:To:Cc:Subject: Date:Message-ID:X-Mailer:In-Reply-To:References:MIME-Version: Content-Transfer-Encoding; bh=+J2NYd8ay0QrXgi9qYBw+MMENP6C1nyqElknyPaZSdo=; b=I/v4iPecJkjhOPWEI9SKkhNv5x96KiMM74ae8/2OZiyGyo8oRUskscWwc7r8rAt6N4gn s94OveVJ7iYqa/xa0rjf4q/3+II7ycOBnJM0wog6bse9RmWFVF0JY/2KpXmc7TDAKU3oa +BVSSJ1SVnNYzDoiR1vCvhjbFfCYg58aa+WMriwEMO47Sc4bdsC2xQn3Tn2F0wJkmHnGn ZfgReVL3wxuRzHEbMx1rV5x0XN3hxC3c1Dj2MDwXlT2q+qs2XfLGxIBme2TFRFkTFWvub WxU3qwKzChy63yMn0hdu/VmrEcDJhtXfskbo2gCZuhu2puYnB9s6jcyHtzHSLQoQ9Vg== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass header.from=arm.com; spf=pass smtp.mailfrom=arm.com; dkim=pass header.d=arm.com header.i=@arm.com header.a=rsa-sha256 header.s=foss header.b=VP01+2dU; arc=none smtp.remote-ip=217.140.110.172 Received: from foss.arm.com (foss.arm.com [217.140.110.172]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 3357360913D7; Mon, 2 Nov 2026 16:54:40 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org 3357360913D7 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=arm.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=arm.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (1024-bit key, unprotected) header.d=arm.com header.i=@arm.com header.a=rsa-sha256 header.s=foss header.b=VP01+2dU Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.121.207.14]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 7839416F3; Mon, 2 Nov 2026 08:54:36 -0800 (PST) Received: from e142021.fritz.box (usa-sjc-mx-foss1.foss.arm.com [172.31.20.19]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id A662E3F59E; Mon, 2 Nov 2026 08:54:36 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=arm.com; s=foss; t=1793638479; bh=TTQVKzHFqone59X5Z0GG6n5IMSWDCJV3150KBk/mdwo=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=VP01+2dUP91YuMVH+dmlvlEtYde1uJbEmlN85JBu3P2xflCk3tNBhN2iM6BsG7DA7 75fhDBfs2PanvziITaqsW2jo0+7Sdo4NYRajv2ET60wviP3jn7l/aqaBWtCyACrasi mPWolg9UHYQ6MFGS9jb3jVvw30tsKKGpwm1n/LxY= From: Andre Przywara To: Mark Rutland , Lorenzo Pieralisi , Sudeep Holla Cc: Salman Nabi , Vedashree Vidwans , Trilok Soni , Nirmoy Das , vsethi@nvidia.com, Varun Wadekar , linux-arm-kernel@lists.infradead.org, linux-kernel@vger.kernel.org, Rob Herring , Krzysztof Kozlowski , Conor Dooley , devicetree@vger.kernel.org Subject: [PATCH v5 RESEND 3/6] firmware: smccc: lfa: Register ACPI notification Date: Mon, 2 Nov 2026 17:54:17 +0100 Message-ID: <20261102165421.3651202-4-andre.przywara@arm.com> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20261102165421.3651202-1-andre.przywara@arm.com> References: <20261102165421.3651202-1-andre.przywara@arm.com> X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Transfer-Encoding: 8bit From: Vedashree Vidwans The Arm LFA spec describes an ACPI notification mechanism, where the platform (firmware) can notify an LFA client about newly available firmware imag updates ("pending images" in LFA terms). Add a faux device after discovering the existence of an LFA agent via the SMCCC discovery mechnism, and use that device to check for the ACPI notification description. Register this when one is provided. The notification just conveys the fact that at least one firmware image has now a pending update, it doesn't say which, also there could be more than one pending. Loop through all images to find every which needs to be activated, and trigger the activation. We need to do this is a loop, since an activation might change the number and the status of available images. Signed-off-by: Vedashree Vidwans [Andre: convert from platform driver to smccc bus] Signed-off-by: Andre Przywara --- drivers/firmware/smccc/lfa_fw.c | 154 +++++++++++++++++++++++++++++++- 1 file changed, 153 insertions(+), 1 deletion(-) diff --git a/drivers/firmware/smccc/lfa_fw.c b/drivers/firmware/smccc/lfa_fw.c index db2a7fe344d5d..4b4daf79611bb 100644 --- a/drivers/firmware/smccc/lfa_fw.c +++ b/drivers/firmware/smccc/lfa_fw.c @@ -33,12 +33,14 @@ * skipped by setting the force_cpu_rendezvous sysfs file to false. */ +#include #include #include #include #include #include #include +#include #include #include #include @@ -49,11 +51,13 @@ #include #include #include +#include #include #include #include +#define DRIVER_NAME "ARM_LFA" #undef pr_fmt #define pr_fmt(fmt) "Arm LFA: " fmt @@ -753,6 +757,135 @@ static void clean_fw_components_tree(void) } } +/* + * Iterate over all FW components and trigger activation of all activatible and + * pending components. + * Since we would need to re-enumerate the list of all components after a + * successful activation, and this could trigger infinite loops if a firmware + * component still appears as pending, we create an array of update candidates + * first, then activate each one of them. + */ +static void activate_pending_components(void) +{ + struct fw_component **all_comps; + struct fw_component *comp; + struct kobject *kobj; + int nr_comps = 0; + int ret, i; + + down_read(&smc_lock); + spin_lock(&lfa_kset->list_lock); + all_comps = kmalloc_array(list_count_nodes(&lfa_kset->list), + sizeof(*all_comps), GFP_NOWAIT); + if (!all_comps) { + spin_unlock(&lfa_kset->list_lock); + up_read(&smc_lock); + pr_warn("LFA: automatic activation failed: -ENOMEM\n"); + return; + } + + list_for_each_entry(kobj, &lfa_kset->list, entry) { + comp = kobj_to_fw_component(kobj); + + if (comp->fw_seq_id == -1) + continue; /* Invalid FW component */ + + _update_fw_component_pending(comp); + if (comp->activation_capable && comp->activation_pending) { + kobject_get(&comp->kobj); + all_comps[nr_comps++] = comp; + } + } + spin_unlock(&lfa_kset->list_lock); + up_read(&smc_lock); + + for (i = 0; i < nr_comps; i++) { + comp = all_comps[i]; + + ret = prime_fw_component(comp); + if (ret) { + pr_warn("LFA: LFA_PRIME failed for component %s\n", + kobject_name(&comp->kobj)); + kobject_put(&comp->kobj); + continue; + } + + ret = activate_fw_component(comp); + if (ret) { + pr_warn("LFA: LFA_ACTIVATE failed for component %s\n", + kobject_name(&comp->kobj)); + kobject_put(&comp->kobj); + continue; + } + + pr_info("%s: automatic activation succeeded\n", + kobject_name(&comp->kobj)); + kobject_put(&comp->kobj); + } + + kfree(all_comps); +} + +#ifdef CONFIG_ACPI +static void lfa_acpi_notify_handler(acpi_handle handle, u32 event, void *data) +{ + /* The spec explicitly mentions 0x80 only for the update event. */ + if (event != 0x80) + return; + + activate_pending_components(); +} + +static int lfa_register_acpi(struct device *dev) +{ + struct acpi_device *acpi_dev; + acpi_handle handle; + acpi_status status; + + acpi_dev = acpi_dev_get_first_match_dev("ARML0003", NULL, -1); + if (!acpi_dev) + return -ENODEV; + handle = acpi_device_handle(acpi_dev); + if (!handle) { + acpi_dev_put(acpi_dev); + return -ENODEV; + } + + /* Register notify handler that indicates LFA updates are available */ + status = acpi_install_notify_handler(handle, ACPI_DEVICE_NOTIFY, + lfa_acpi_notify_handler, NULL); + if (ACPI_FAILURE(status)) { + acpi_dev_put(acpi_dev); + return -EIO; + } + + ACPI_COMPANION_SET(dev, acpi_dev); + + return 0; +} + +static void lfa_remove_acpi(struct device *dev) +{ + struct acpi_device *acpi_dev = ACPI_COMPANION(dev); + acpi_handle handle = acpi_device_handle(acpi_dev); + + if (handle) + acpi_remove_notify_handler(handle, + ACPI_DEVICE_NOTIFY, + lfa_acpi_notify_handler); + acpi_dev_put(acpi_dev); +} +#else /* !CONFIG_ACPI */ +static int lfa_register_acpi(struct device *dev) +{ + return -ENODEV; +} + +static void lfa_remove_acpi(struct device *dev) +{ +} +#endif + static bool lfa_smccc_has_all_functions(void) { struct arm_smccc_1_2_regs regs = { 0 }, res; @@ -813,13 +946,32 @@ static int lfa_smccc_probe(struct arm_smccc_device *sdev) if (err != 0) { kset_unregister(lfa_kset); destroy_workqueue(fw_comps_update_wq); + return err; + } + + if (!acpi_disabled) { + err = lfa_register_acpi(&sdev->dev); + if (!err) + pr_info("registered LFA ACPI notification\n"); + } + + /* + * An ACPI notification or an interrupt are optional, but we must + * handle errors that occurred during their setup. + */ + if (err != -ENODEV) { + kset_unregister(lfa_kset); + destroy_workqueue(fw_comps_update_wq); + return err; } - return err; + return 0; } static void lfa_smccc_remove(struct arm_smccc_device *sdev) { + if (!acpi_disabled) + lfa_remove_acpi(&sdev->dev); clean_fw_components_tree(); flush_workqueue(fw_comps_update_wq); destroy_workqueue(fw_comps_update_wq); -- 2.43.0