platform/x86/amd/pmf: Add heartbeat signal support
[linux-2.6-block.git] / drivers / platform / x86 / amd / pmf / acpi.c
CommitLineData
5eb315eb
SS
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * AMD Platform Management Framework Driver
4 *
5 * Copyright (c) 2022, Advanced Micro Devices, Inc.
6 * All Rights Reserved.
7 *
8 * Author: Shyam Sundar S K <Shyam-sundar.S-k@amd.com>
9 */
10
11#include <linux/acpi.h>
12#include "pmf.h"
13
14static union acpi_object *apmf_if_call(struct amd_pmf_dev *pdev, int fn, struct acpi_buffer *param)
15{
16 struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
17 acpi_handle ahandle = ACPI_HANDLE(pdev->dev);
18 struct acpi_object_list apmf_if_arg_list;
19 union acpi_object apmf_if_args[2];
20 acpi_status status;
21
22 apmf_if_arg_list.count = 2;
23 apmf_if_arg_list.pointer = &apmf_if_args[0];
24
25 apmf_if_args[0].type = ACPI_TYPE_INTEGER;
26 apmf_if_args[0].integer.value = fn;
27
28 if (param) {
29 apmf_if_args[1].type = ACPI_TYPE_BUFFER;
30 apmf_if_args[1].buffer.length = param->length;
31 apmf_if_args[1].buffer.pointer = param->pointer;
32 } else {
33 apmf_if_args[1].type = ACPI_TYPE_INTEGER;
34 apmf_if_args[1].integer.value = 0;
35 }
36
37 status = acpi_evaluate_object(ahandle, "APMF", &apmf_if_arg_list, &buffer);
38 if (ACPI_FAILURE(status)) {
39 dev_err(pdev->dev, "APMF method:%d call failed\n", fn);
40 kfree(buffer.pointer);
41 return NULL;
42 }
43
44 return buffer.pointer;
45}
46
47static int apmf_if_call_store_buffer(struct amd_pmf_dev *pdev, int fn, void *dest, size_t out_sz)
48{
49 union acpi_object *info;
50 size_t size;
51 int err = 0;
52
53 info = apmf_if_call(pdev, fn, NULL);
54 if (!info)
55 return -EIO;
56
57 if (info->type != ACPI_TYPE_BUFFER) {
58 dev_err(pdev->dev, "object is not a buffer\n");
59 err = -EINVAL;
60 goto out;
61 }
62
63 if (info->buffer.length < 2) {
64 dev_err(pdev->dev, "buffer too small\n");
65 err = -EINVAL;
66 goto out;
67 }
68
69 size = *(u16 *)info->buffer.pointer;
70 if (info->buffer.length < size) {
71 dev_err(pdev->dev, "buffer smaller then headersize %u < %zu\n",
72 info->buffer.length, size);
73 err = -EINVAL;
74 goto out;
75 }
76
77 if (size < out_sz) {
78 dev_err(pdev->dev, "buffer too small %zu\n", size);
79 err = -EINVAL;
80 goto out;
81 }
82
83 memcpy(dest, info->buffer.pointer, out_sz);
84
85out:
86 kfree(info);
87 return err;
88}
89
90int is_apmf_func_supported(struct amd_pmf_dev *pdev, unsigned long index)
91{
92 /* If bit-n is set, that indicates function n+1 is supported */
93 return !!(pdev->supported_func & BIT(index - 1));
94}
95
4c71ae41
SS
96int apmf_get_static_slider_granular(struct amd_pmf_dev *pdev,
97 struct apmf_static_slider_granular_output *data)
98{
99 if (!is_apmf_func_supported(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR))
100 return -EINVAL;
101
102 return apmf_if_call_store_buffer(pdev, APMF_FUNC_STATIC_SLIDER_GRANULAR,
103 data, sizeof(*data));
104}
105
b9ab888b
SS
106static void apmf_sbios_heartbeat_notify(struct work_struct *work)
107{
108 struct amd_pmf_dev *dev = container_of(work, struct amd_pmf_dev, heart_beat.work);
109 union acpi_object *info;
110
111 dev_dbg(dev->dev, "Sending heartbeat to SBIOS\n");
112 info = apmf_if_call(dev, APMF_FUNC_SBIOS_HEARTBEAT, NULL);
113 if (!info)
114 goto out;
115
116 schedule_delayed_work(&dev->heart_beat, msecs_to_jiffies(dev->hb_interval * 1000));
117
118out:
119 kfree(info);
120}
121
5eb315eb
SS
122static int apmf_if_verify_interface(struct amd_pmf_dev *pdev)
123{
124 struct apmf_verify_interface output;
125 int err;
126
127 err = apmf_if_call_store_buffer(pdev, APMF_FUNC_VERIFY_INTERFACE, &output, sizeof(output));
128 if (err)
129 return err;
130
131 pdev->supported_func = output.supported_functions;
132 dev_dbg(pdev->dev, "supported functions:0x%x notifications:0x%x\n",
133 output.supported_functions, output.notification_mask);
134
135 return 0;
136}
137
138static int apmf_get_system_params(struct amd_pmf_dev *dev)
139{
140 struct apmf_system_params params;
141 int err;
142
143 if (!is_apmf_func_supported(dev, APMF_FUNC_GET_SYS_PARAMS))
144 return -EINVAL;
145
146 err = apmf_if_call_store_buffer(dev, APMF_FUNC_GET_SYS_PARAMS, &params, sizeof(params));
147 if (err)
148 return err;
149
b9ab888b 150 dev_dbg(dev->dev, "system params mask:0x%x flags:0x%x cmd_code:0x%x heartbeat:%d\n",
5eb315eb
SS
151 params.valid_mask,
152 params.flags,
b9ab888b
SS
153 params.command_code,
154 params.heartbeat_int);
5eb315eb 155 params.flags = params.flags & params.valid_mask;
b9ab888b 156 dev->hb_interval = params.heartbeat_int;
5eb315eb
SS
157
158 return 0;
159}
160
b9ab888b
SS
161void apmf_acpi_deinit(struct amd_pmf_dev *pmf_dev)
162{
163 if (pmf_dev->hb_interval)
164 cancel_delayed_work_sync(&pmf_dev->heart_beat);
165}
166
5eb315eb
SS
167int apmf_acpi_init(struct amd_pmf_dev *pmf_dev)
168{
169 int ret;
170
171 ret = apmf_if_verify_interface(pmf_dev);
172 if (ret) {
173 dev_err(pmf_dev->dev, "APMF verify interface failed :%d\n", ret);
174 goto out;
175 }
176
177 ret = apmf_get_system_params(pmf_dev);
178 if (ret) {
179 dev_err(pmf_dev->dev, "APMF apmf_get_system_params failed :%d\n", ret);
180 goto out;
181 }
182
b9ab888b
SS
183 if (pmf_dev->hb_interval) {
184 /* send heartbeats only if the interval is not zero */
185 INIT_DELAYED_WORK(&pmf_dev->heart_beat, apmf_sbios_heartbeat_notify);
186 schedule_delayed_work(&pmf_dev->heart_beat, 0);
187 }
188
5eb315eb
SS
189out:
190 return ret;
191}