Enable this feature if you want to set up and install the PCC Address
          Space handler to handle PCC OpRegion in the firmware.
 
+config ACPI_FFH
+       bool "ACPI FFH Address Space"
+       default n
+       help
+         The FFH(Fixed Function Hardware) Address Space also referred as FFH
+         Operation Region allows to define platform specific opregion.
+
+         Enable this feature if you want to set up and install the FFH Address
+         Space handler to handle FFH OpRegion in the firmware.
+
 source "drivers/acpi/pmic/Kconfig"
 
 config ACPI_VIOT
 
 acpi-$(CONFIG_ACPI_WATCHDOG)   += acpi_watchdog.o
 acpi-$(CONFIG_ACPI_PRMT)       += prmt.o
 acpi-$(CONFIG_ACPI_PCC)                += acpi_pcc.o
+acpi-$(CONFIG_ACPI_FFH)                += acpi_ffh.o
 
 # Address translation
 acpi-$(CONFIG_ACPI_ADXL)       += acpi_adxl.o
 
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Author: Sudeep Holla <sudeep.holla@arm.com>
+ * Copyright 2022 Arm Limited
+ */
+#include <linux/kernel.h>
+#include <linux/acpi.h>
+#include <linux/completion.h>
+#include <linux/idr.h>
+#include <linux/io.h>
+
+#include <linux/arm-smccc.h>
+
+static struct acpi_ffh_info ffh_ctx;
+
+int __weak acpi_ffh_address_space_arch_setup(void *handler_ctxt,
+                                            void **region_ctxt)
+{
+       return -EOPNOTSUPP;
+}
+
+int __weak acpi_ffh_address_space_arch_handler(acpi_integer *value,
+                                              void *region_context)
+{
+       return -EOPNOTSUPP;
+}
+
+static acpi_status
+acpi_ffh_address_space_setup(acpi_handle region_handle, u32 function,
+                            void *handler_context,  void **region_context)
+{
+       return acpi_ffh_address_space_arch_setup(handler_context,
+                                                region_context);
+}
+
+static acpi_status
+acpi_ffh_address_space_handler(u32 function, acpi_physical_address addr,
+                              u32 bits, acpi_integer *value,
+                              void *handler_context, void *region_context)
+{
+       return acpi_ffh_address_space_arch_handler(value, region_context);
+}
+
+void __init acpi_init_ffh(void)
+{
+       acpi_status status;
+
+       status = acpi_install_address_space_handler(ACPI_ROOT_OBJECT,
+                                                   ACPI_ADR_SPACE_FIXED_HARDWARE,
+                                                   &acpi_ffh_address_space_handler,
+                                                   &acpi_ffh_address_space_setup,
+                                                   &ffh_ctx);
+       if (ACPI_FAILURE(status))
+               pr_alert("OperationRegion handler could not be installed\n");
+}
 
 
 bool osc_sb_cppc2_support_acked;
 
+bool osc_sb_ffh_opregion_support_confirmed;
+
 static u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48";
 static void acpi_bus_osc_negotiate_platform_control(void)
 {
                        capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_NATIVE_USB4_SUPPORT;
                osc_cpc_flexible_adr_space_confirmed =
                        capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_CPC_FLEXIBLE_ADR_SPACE;
+               osc_sb_ffh_opregion_support_confirmed =
+                       capbuf_ret[OSC_SUPPORT_DWORD] & OSC_SB_FFH_OPR_SUPPORT;
        }
 
        kfree(context.ret.pointer);
                disable_acpi();
                return result;
        }
+       if (osc_sb_ffh_opregion_support_confirmed)
+               acpi_init_ffh();
 
        pci_mmcfg_late_init();
        acpi_iort_init();
 
 #define OSC_SB_CPC_FLEXIBLE_ADR_SPACE          0x00004000
 #define OSC_SB_NATIVE_USB4_SUPPORT             0x00040000
 #define OSC_SB_PRM_SUPPORT                     0x00200000
+#define OSC_SB_FFH_OPR_SUPPORT                 0x00400000
 
 extern bool osc_sb_apei_support_acked;
 extern bool osc_pc_lpi_support_confirmed;
 static inline void acpi_init_pcc(void) { }
 #endif
 
+#ifdef CONFIG_ACPI_FFH
+void acpi_init_ffh(void);
+#else
+static inline void acpi_init_ffh(void) { }
+#endif
+
 #ifdef CONFIG_ACPI
 extern void acpi_device_notify(struct device *dev);
 extern void acpi_device_notify_remove(struct device *dev);