diff mbox series

[v3,05/11] platform/x86/amd/pmf: Add heartbeat signal support

Message ID 20220802112545.2118632-6-Shyam-sundar.S-k@amd.com (mailing list archive)
State Superseded, archived
Headers show
Series platform/x86/amd/pmf: Introduce AMD PMF Driver | expand

Commit Message

Shyam Sundar S K Aug. 2, 2022, 11:25 a.m. UTC
PMF driver can send periodic heartbeat signals to OEM BIOS. When BIOS does
not receive the signal after a period of time, it can infer that AMDPMF
has hung or failed to load.

In this situation, BIOS can fallback to legacy operations. OEM can modify
the time interval of the signal or completely disable signals through
ACPI Method.

Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
---
 drivers/platform/x86/amd/pmf/acpi.c | 37 +++++++++++++++++++++++++++--
 drivers/platform/x86/amd/pmf/core.c |  1 +
 drivers/platform/x86/amd/pmf/pmf.h  |  5 ++++
 3 files changed, 41 insertions(+), 2 deletions(-)

Comments

Hans de Goede Aug. 2, 2022, 12:52 p.m. UTC | #1
Hi,

On 8/2/22 13:25, Shyam Sundar S K wrote:
> PMF driver can send periodic heartbeat signals to OEM BIOS. When BIOS does
> not receive the signal after a period of time, it can infer that AMDPMF
> has hung or failed to load.
> 
> In this situation, BIOS can fallback to legacy operations. OEM can modify
> the time interval of the signal or completely disable signals through
> ACPI Method.
> 
> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
> ---
>  drivers/platform/x86/amd/pmf/acpi.c | 37 +++++++++++++++++++++++++++--
>  drivers/platform/x86/amd/pmf/core.c |  1 +
>  drivers/platform/x86/amd/pmf/pmf.h  |  5 ++++
>  3 files changed, 41 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
> index 717ee81a5f73..c3f87265eeae 100644
> --- a/drivers/platform/x86/amd/pmf/acpi.c
> +++ b/drivers/platform/x86/amd/pmf/acpi.c
> @@ -105,6 +105,25 @@ int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
>  									 data, sizeof(*data));
>  }
>  
> +static void apmf_sbios_heartbeat_notify(struct work_struct *work)
> +{
> +	struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, heart_beat.work);
> +	union acpi_object *info;
> +	int err = 0;
> +
> +	dev_dbg(dev->dev, "Sending heartbeat to SBIOS\n");
> +	info = apmf_if_call(dev, APMF_FUNC_SBIOS_HEARTBEAT, NULL);
> +	if (!info) {
> +		err = -EIO;

I did not notice this before, but err gets set here and then never used.

Maybe instead log an error when the call fails ?

Also the work is not re-queued on an error here, I assume this is on
purpose ?

Regards,

Hans


> +		goto out;
> +	}
> +
> +	schedule_delayed_work(&dev->heart_beat, msecs_to_jiffies(dev->hb_interval * 1000));
> +
> +out:
> +	kfree(info);
> +}
> +
>  static int apmf_if_verify_interface(struct amd_pmf_dev *pdev)
>  {
>  	struct apmf_verify_interface output;
> @@ -133,15 +152,23 @@ static int apmf_get_system_params(struct amd_pmf_dev *dev)
>  	if (err)
>  		return err;
>  
> -	dev_dbg(dev->dev, "system params mask:0x%x flags:0x%x cmd_code:0x%x\n",
> +	dev_dbg(dev->dev, "system params mask:0x%x flags:0x%x cmd_code:0x%x heartbeat:%d\n",
>  		params.valid_mask,
>  		params.flags,
> -		params.command_code);
> +		params.command_code,
> +		params.heartbeat_int);
>  	params.flags = params.flags & params.valid_mask;
> +	dev->hb_interval = params.heartbeat_int;
>  
>  	return 0;
>  }
>  
> +void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
> +{
> +	if (pmf_dev->hb_interval)
> +		cancel_delayed_work_sync(&pmf_dev->heart_beat);
> +}
> +
>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>  {
>  	int ret;
> @@ -158,6 +185,12 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>  		goto out;
>  	}
>  
> +	if (pmf_dev->hb_interval) {
> +		/* send heartbeats only if the interval is not zero */
> +		INIT_DELAYED_WORK(&pmf_dev->heart_beat, apmf_sbios_heartbeat_notify);
> +		schedule_delayed_work(&pmf_dev->heart_beat, 0);
> +	}
> +
>  out:
>  	return ret;
>  }
> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
> index 032d9dc5e09f..87a1f9b27d2c 100644
> --- a/drivers/platform/x86/amd/pmf/core.c
> +++ b/drivers/platform/x86/amd/pmf/core.c
> @@ -279,6 +279,7 @@ static int amd_pmf_remove(struct platform_device *pdev)
>  
>  	mutex_destroy(&dev->lock);
>  	amd_pmf_deinit_features(dev);
> +	apmf_acpi_deinit(dev);
>  	amd_pmf_dbgfs_unregister(dev);
>  	kfree(dev->buf);
>  	return 0;
> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
> index e13542359403..5b85a7fe0f38 100644
> --- a/drivers/platform/x86/amd/pmf/pmf.h
> +++ b/drivers/platform/x86/amd/pmf/pmf.h
> @@ -17,6 +17,7 @@
>  /* APMF Functions */
>  #define APMF_FUNC_VERIFY_INTERFACE			0
>  #define APMF_FUNC_GET_SYS_PARAMS			1
> +#define APMF_FUNC_SBIOS_HEARTBEAT			4
>  #define APMF_FUNC_STATIC_SLIDER_GRANULAR       9
>  
>  /* Message Definitions */
> @@ -53,6 +54,7 @@ struct apmf_system_params {
>  	u32 valid_mask;
>  	u32 flags;
>  	u8 command_code;
> +	u32 heartbeat_int;
>  } __packed;
>  
>  enum amd_stt_skin_temp {
> @@ -91,6 +93,8 @@ struct amd_pmf_dev {
>  	enum platform_profile_option current_profile;
>  	struct platform_profile_handler pprof;
>  	struct dentry *dbgfs_dir;
> +	int hb_interval; /* SBIOS heartbeat interval */
> +	struct delayed_work heart_beat;
>  };
>  
>  struct apmf_sps_prop_granular {
> @@ -116,6 +120,7 @@ struct amd_pmf_static_slider_granular {
>  
>  /* Core Layer */
>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
> +void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
>  int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index);
>  int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
>  int amd_pmf_get_power_source(void);
Hans de Goede Aug. 2, 2022, 12:57 p.m. UTC | #2
Hi,

On 8/2/22 14:52, Hans de Goede wrote:
> Hi,
> 
> On 8/2/22 13:25, Shyam Sundar S K wrote:
>> PMF driver can send periodic heartbeat signals to OEM BIOS. When BIOS does
>> not receive the signal after a period of time, it can infer that AMDPMF
>> has hung or failed to load.
>>
>> In this situation, BIOS can fallback to legacy operations. OEM can modify
>> the time interval of the signal or completely disable signals through
>> ACPI Method.
>>
>> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>> ---
>>  drivers/platform/x86/amd/pmf/acpi.c | 37 +++++++++++++++++++++++++++--
>>  drivers/platform/x86/amd/pmf/core.c |  1 +
>>  drivers/platform/x86/amd/pmf/pmf.h  |  5 ++++
>>  3 files changed, 41 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
>> index 717ee81a5f73..c3f87265eeae 100644
>> --- a/drivers/platform/x86/amd/pmf/acpi.c
>> +++ b/drivers/platform/x86/amd/pmf/acpi.c
>> @@ -105,6 +105,25 @@ int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
>>  									 data, sizeof(*data));
>>  }
>>  
>> +static void apmf_sbios_heartbeat_notify(struct work_struct *work)
>> +{
>> +	struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, heart_beat.work);
>> +	union acpi_object *info;
>> +	int err = 0;
>> +
>> +	dev_dbg(dev->dev, "Sending heartbeat to SBIOS\n");
>> +	info = apmf_if_call(dev, APMF_FUNC_SBIOS_HEARTBEAT, NULL);
>> +	if (!info) {
>> +		err = -EIO;
> 
> I did not notice this before, but err gets set here and then never used.
> 
> Maybe instead log an error when the call fails ?

I just realized that apmf_if_call() already logs an error, so
when we add the value of the fn argument (APMF_FUNC_SBIOS_HEARTBEAT)
then there is no need for extra logging here.

Note err getting set but never used will cause warnings with
some compilers, please drop the err variable.

> Also the work is not re-queued on an error here, I assume this is on
> purpose ?

Regards,

Hans



>> +		goto out;
>> +	}
>> +
>> +	schedule_delayed_work(&dev->heart_beat, msecs_to_jiffies(dev->hb_interval * 1000));
>> +
>> +out:
>> +	kfree(info);
>> +}
>> +
>>  static int apmf_if_verify_interface(struct amd_pmf_dev *pdev)
>>  {
>>  	struct apmf_verify_interface output;
>> @@ -133,15 +152,23 @@ static int apmf_get_system_params(struct amd_pmf_dev *dev)
>>  	if (err)
>>  		return err;
>>  
>> -	dev_dbg(dev->dev, "system params mask:0x%x flags:0x%x cmd_code:0x%x\n",
>> +	dev_dbg(dev->dev, "system params mask:0x%x flags:0x%x cmd_code:0x%x heartbeat:%d\n",
>>  		params.valid_mask,
>>  		params.flags,
>> -		params.command_code);
>> +		params.command_code,
>> +		params.heartbeat_int);
>>  	params.flags = params.flags & params.valid_mask;
>> +	dev->hb_interval = params.heartbeat_int;
>>  
>>  	return 0;
>>  }
>>  
>> +void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
>> +{
>> +	if (pmf_dev->hb_interval)
>> +		cancel_delayed_work_sync(&pmf_dev->heart_beat);
>> +}
>> +
>>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>>  {
>>  	int ret;
>> @@ -158,6 +185,12 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>>  		goto out;
>>  	}
>>  
>> +	if (pmf_dev->hb_interval) {
>> +		/* send heartbeats only if the interval is not zero */
>> +		INIT_DELAYED_WORK(&pmf_dev->heart_beat, apmf_sbios_heartbeat_notify);
>> +		schedule_delayed_work(&pmf_dev->heart_beat, 0);
>> +	}
>> +
>>  out:
>>  	return ret;
>>  }
>> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
>> index 032d9dc5e09f..87a1f9b27d2c 100644
>> --- a/drivers/platform/x86/amd/pmf/core.c
>> +++ b/drivers/platform/x86/amd/pmf/core.c
>> @@ -279,6 +279,7 @@ static int amd_pmf_remove(struct platform_device *pdev)
>>  
>>  	mutex_destroy(&dev->lock);
>>  	amd_pmf_deinit_features(dev);
>> +	apmf_acpi_deinit(dev);
>>  	amd_pmf_dbgfs_unregister(dev);
>>  	kfree(dev->buf);
>>  	return 0;
>> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
>> index e13542359403..5b85a7fe0f38 100644
>> --- a/drivers/platform/x86/amd/pmf/pmf.h
>> +++ b/drivers/platform/x86/amd/pmf/pmf.h
>> @@ -17,6 +17,7 @@
>>  /* APMF Functions */
>>  #define APMF_FUNC_VERIFY_INTERFACE			0
>>  #define APMF_FUNC_GET_SYS_PARAMS			1
>> +#define APMF_FUNC_SBIOS_HEARTBEAT			4
>>  #define APMF_FUNC_STATIC_SLIDER_GRANULAR       9
>>  
>>  /* Message Definitions */
>> @@ -53,6 +54,7 @@ struct apmf_system_params {
>>  	u32 valid_mask;
>>  	u32 flags;
>>  	u8 command_code;
>> +	u32 heartbeat_int;
>>  } __packed;
>>  
>>  enum amd_stt_skin_temp {
>> @@ -91,6 +93,8 @@ struct amd_pmf_dev {
>>  	enum platform_profile_option current_profile;
>>  	struct platform_profile_handler pprof;
>>  	struct dentry *dbgfs_dir;
>> +	int hb_interval; /* SBIOS heartbeat interval */
>> +	struct delayed_work heart_beat;
>>  };
>>  
>>  struct apmf_sps_prop_granular {
>> @@ -116,6 +120,7 @@ struct amd_pmf_static_slider_granular {
>>  
>>  /* Core Layer */
>>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
>> +void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
>>  int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index);
>>  int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
>>  int amd_pmf_get_power_source(void);
Shyam Sundar S K Aug. 2, 2022, 2:23 p.m. UTC | #3
Hi Hans,

On 8/2/2022 6:22 PM, Hans de Goede wrote:
> Hi,
> 
> On 8/2/22 13:25, Shyam Sundar S K wrote:
>> PMF driver can send periodic heartbeat signals to OEM BIOS. When BIOS does
>> not receive the signal after a period of time, it can infer that AMDPMF
>> has hung or failed to load.
>>
>> In this situation, BIOS can fallback to legacy operations. OEM can modify
>> the time interval of the signal or completely disable signals through
>> ACPI Method.
>>
>> Reviewed-by: Hans de Goede <hdegoede@redhat.com>
>> Signed-off-by: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
>> ---
>>  drivers/platform/x86/amd/pmf/acpi.c | 37 +++++++++++++++++++++++++++--
>>  drivers/platform/x86/amd/pmf/core.c |  1 +
>>  drivers/platform/x86/amd/pmf/pmf.h  |  5 ++++
>>  3 files changed, 41 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
>> index 717ee81a5f73..c3f87265eeae 100644
>> --- a/drivers/platform/x86/amd/pmf/acpi.c
>> +++ b/drivers/platform/x86/amd/pmf/acpi.c
>> @@ -105,6 +105,25 @@ int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
>>  									 data, sizeof(*data));
>>  }
>>  
>> +static void apmf_sbios_heartbeat_notify(struct work_struct *work)
>> +{
>> +	struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, heart_beat.work);
>> +	union acpi_object *info;
>> +	int err = 0;
>> +
>> +	dev_dbg(dev->dev, "Sending heartbeat to SBIOS\n");
>> +	info = apmf_if_call(dev, APMF_FUNC_SBIOS_HEARTBEAT, NULL);
>> +	if (!info) {
>> +		err = -EIO;
> 
> I did not notice this before, but err gets set here and then never used.
> 
> Maybe instead log an error when the call fails ?
> 
> Also the work is not re-queued on an error here, I assume this is on
> purpose ?

Yes, that's right. We should not re-queue if the call fails and leave
the control for legacy operations.

Thanks,
Shyam

> 
> Regards,
> 
> Hans
> 
> 
>> +		goto out;
>> +	}
>> +
>> +	schedule_delayed_work(&dev->heart_beat, msecs_to_jiffies(dev->hb_interval * 1000));
>> +
>> +out:
>> +	kfree(info);
>> +}
>> +
>>  static int apmf_if_verify_interface(struct amd_pmf_dev *pdev)
>>  {
>>  	struct apmf_verify_interface output;
>> @@ -133,15 +152,23 @@ static int apmf_get_system_params(struct amd_pmf_dev *dev)
>>  	if (err)
>>  		return err;
>>  
>> -	dev_dbg(dev->dev, "system params mask:0x%x flags:0x%x cmd_code:0x%x\n",
>> +	dev_dbg(dev->dev, "system params mask:0x%x flags:0x%x cmd_code:0x%x heartbeat:%d\n",
>>  		params.valid_mask,
>>  		params.flags,
>> -		params.command_code);
>> +		params.command_code,
>> +		params.heartbeat_int);
>>  	params.flags = params.flags & params.valid_mask;
>> +	dev->hb_interval = params.heartbeat_int;
>>  
>>  	return 0;
>>  }
>>  
>> +void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
>> +{
>> +	if (pmf_dev->hb_interval)
>> +		cancel_delayed_work_sync(&pmf_dev->heart_beat);
>> +}
>> +
>>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>>  {
>>  	int ret;
>> @@ -158,6 +185,12 @@ int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
>>  		goto out;
>>  	}
>>  
>> +	if (pmf_dev->hb_interval) {
>> +		/* send heartbeats only if the interval is not zero */
>> +		INIT_DELAYED_WORK(&pmf_dev->heart_beat, apmf_sbios_heartbeat_notify);
>> +		schedule_delayed_work(&pmf_dev->heart_beat, 0);
>> +	}
>> +
>>  out:
>>  	return ret;
>>  }
>> diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
>> index 032d9dc5e09f..87a1f9b27d2c 100644
>> --- a/drivers/platform/x86/amd/pmf/core.c
>> +++ b/drivers/platform/x86/amd/pmf/core.c
>> @@ -279,6 +279,7 @@ static int amd_pmf_remove(struct platform_device *pdev)
>>  
>>  	mutex_destroy(&dev->lock);
>>  	amd_pmf_deinit_features(dev);
>> +	apmf_acpi_deinit(dev);
>>  	amd_pmf_dbgfs_unregister(dev);
>>  	kfree(dev->buf);
>>  	return 0;
>> diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
>> index e13542359403..5b85a7fe0f38 100644
>> --- a/drivers/platform/x86/amd/pmf/pmf.h
>> +++ b/drivers/platform/x86/amd/pmf/pmf.h
>> @@ -17,6 +17,7 @@
>>  /* APMF Functions */
>>  #define APMF_FUNC_VERIFY_INTERFACE			0
>>  #define APMF_FUNC_GET_SYS_PARAMS			1
>> +#define APMF_FUNC_SBIOS_HEARTBEAT			4
>>  #define APMF_FUNC_STATIC_SLIDER_GRANULAR       9
>>  
>>  /* Message Definitions */
>> @@ -53,6 +54,7 @@ struct apmf_system_params {
>>  	u32 valid_mask;
>>  	u32 flags;
>>  	u8 command_code;
>> +	u32 heartbeat_int;
>>  } __packed;
>>  
>>  enum amd_stt_skin_temp {
>> @@ -91,6 +93,8 @@ struct amd_pmf_dev {
>>  	enum platform_profile_option current_profile;
>>  	struct platform_profile_handler pprof;
>>  	struct dentry *dbgfs_dir;
>> +	int hb_interval; /* SBIOS heartbeat interval */
>> +	struct delayed_work heart_beat;
>>  };
>>  
>>  struct apmf_sps_prop_granular {
>> @@ -116,6 +120,7 @@ struct amd_pmf_static_slider_granular {
>>  
>>  /* Core Layer */
>>  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
>> +void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
>>  int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index);
>>  int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
>>  int amd_pmf_get_power_source(void);
>
diff mbox series

Patch

diff --git a/drivers/platform/x86/amd/pmf/acpi.c b/drivers/platform/x86/amd/pmf/acpi.c
index 717ee81a5f73..c3f87265eeae 100644
--- a/drivers/platform/x86/amd/pmf/acpi.c
+++ b/drivers/platform/x86/amd/pmf/acpi.c
@@ -105,6 +105,25 @@  int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
 									 data, sizeof(*data));
 }
 
+static void apmf_sbios_heartbeat_notify(struct work_struct *work)
+{
+	struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, heart_beat.work);
+	union acpi_object *info;
+	int err = 0;
+
+	dev_dbg(dev->dev, "Sending heartbeat to SBIOS\n");
+	info = apmf_if_call(dev, APMF_FUNC_SBIOS_HEARTBEAT, NULL);
+	if (!info) {
+		err = -EIO;
+		goto out;
+	}
+
+	schedule_delayed_work(&dev->heart_beat, msecs_to_jiffies(dev->hb_interval * 1000));
+
+out:
+	kfree(info);
+}
+
 static int apmf_if_verify_interface(struct amd_pmf_dev *pdev)
 {
 	struct apmf_verify_interface output;
@@ -133,15 +152,23 @@  static int apmf_get_system_params(struct amd_pmf_dev *dev)
 	if (err)
 		return err;
 
-	dev_dbg(dev->dev, "system params mask:0x%x flags:0x%x cmd_code:0x%x\n",
+	dev_dbg(dev->dev, "system params mask:0x%x flags:0x%x cmd_code:0x%x heartbeat:%d\n",
 		params.valid_mask,
 		params.flags,
-		params.command_code);
+		params.command_code,
+		params.heartbeat_int);
 	params.flags = params.flags & params.valid_mask;
+	dev->hb_interval = params.heartbeat_int;
 
 	return 0;
 }
 
+void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
+{
+	if (pmf_dev->hb_interval)
+		cancel_delayed_work_sync(&pmf_dev->heart_beat);
+}
+
 int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
 {
 	int ret;
@@ -158,6 +185,12 @@  int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
 		goto out;
 	}
 
+	if (pmf_dev->hb_interval) {
+		/* send heartbeats only if the interval is not zero */
+		INIT_DELAYED_WORK(&pmf_dev->heart_beat, apmf_sbios_heartbeat_notify);
+		schedule_delayed_work(&pmf_dev->heart_beat, 0);
+	}
+
 out:
 	return ret;
 }
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index 032d9dc5e09f..87a1f9b27d2c 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -279,6 +279,7 @@  static int amd_pmf_remove(struct platform_device *pdev)
 
 	mutex_destroy(&dev->lock);
 	amd_pmf_deinit_features(dev);
+	apmf_acpi_deinit(dev);
 	amd_pmf_dbgfs_unregister(dev);
 	kfree(dev->buf);
 	return 0;
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index e13542359403..5b85a7fe0f38 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -17,6 +17,7 @@ 
 /* APMF Functions */
 #define APMF_FUNC_VERIFY_INTERFACE			0
 #define APMF_FUNC_GET_SYS_PARAMS			1
+#define APMF_FUNC_SBIOS_HEARTBEAT			4
 #define APMF_FUNC_STATIC_SLIDER_GRANULAR       9
 
 /* Message Definitions */
@@ -53,6 +54,7 @@  struct apmf_system_params {
 	u32 valid_mask;
 	u32 flags;
 	u8 command_code;
+	u32 heartbeat_int;
 } __packed;
 
 enum amd_stt_skin_temp {
@@ -91,6 +93,8 @@  struct amd_pmf_dev {
 	enum platform_profile_option current_profile;
 	struct platform_profile_handler pprof;
 	struct dentry *dbgfs_dir;
+	int hb_interval; /* SBIOS heartbeat interval */
+	struct delayed_work heart_beat;
 };
 
 struct apmf_sps_prop_granular {
@@ -116,6 +120,7 @@  struct amd_pmf_static_slider_granular {
 
 /* Core Layer */
 int apmf_acpi_init(struct amd_pmf_dev *pmf_dev);
+void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev);
 int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index);
 int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data);
 int amd_pmf_get_power_source(void);