Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * AMD HSMP Platform Driver
  4 * Copyright (c) 2024, AMD.
  5 * All Rights Reserved.
  6 *
  7 * This file provides an ACPI based driver implementation for HSMP interface.
  8 */
  9
 10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 11
 12#include <asm/amd_hsmp.h>
 13#include <asm/amd_nb.h>
 14
 15#include <linux/acpi.h>
 16#include <linux/device.h>
 17#include <linux/dev_printk.h>
 18#include <linux/ioport.h>
 19#include <linux/kstrtox.h>
 20#include <linux/module.h>
 21#include <linux/platform_device.h>
 22#include <linux/sysfs.h>
 23#include <linux/uuid.h>
 24
 25#include <uapi/asm-generic/errno-base.h>
 26
 27#include "hsmp.h"
 28
 29#define DRIVER_NAME		"amd_hsmp"
 30#define DRIVER_VERSION		"2.3"
 31#define ACPI_HSMP_DEVICE_HID	"AMDI0097"
 32
 33/* These are the strings specified in ACPI table */
 34#define MSG_IDOFF_STR		"MsgIdOffset"
 35#define MSG_ARGOFF_STR		"MsgArgOffset"
 36#define MSG_RESPOFF_STR		"MsgRspOffset"
 37
 38static struct hsmp_plat_device *hsmp_pdev;
 39
 40static int amd_hsmp_acpi_rdwr(struct hsmp_socket *sock, u32 offset,
 41			      u32 *value, bool write)
 42{
 43	if (write)
 44		iowrite32(*value, sock->virt_base_addr + offset);
 45	else
 46		*value = ioread32(sock->virt_base_addr + offset);
 47
 48	return 0;
 49}
 50
 51/* This is the UUID used for HSMP */
 52static const guid_t acpi_hsmp_uuid = GUID_INIT(0xb74d619d, 0x5707, 0x48bd,
 53						0xa6, 0x9f, 0x4e, 0xa2,
 54						0x87, 0x1f, 0xc2, 0xf6);
 55
 56static inline bool is_acpi_hsmp_uuid(union acpi_object *obj)
 57{
 58	if (obj->type == ACPI_TYPE_BUFFER && obj->buffer.length == UUID_SIZE)
 59		return guid_equal((guid_t *)obj->buffer.pointer, &acpi_hsmp_uuid);
 60
 61	return false;
 62}
 63
 64static inline int hsmp_get_uid(struct device *dev, u16 *sock_ind)
 65{
 66	char *uid;
 67
 68	/*
 69	 * UID (ID00, ID01..IDXX) is used for differentiating sockets,
 70	 * read it and strip the "ID" part of it and convert the remaining
 71	 * bytes to integer.
 72	 */
 73	uid = acpi_device_uid(ACPI_COMPANION(dev));
 74
 75	return kstrtou16(uid + 2, 10, sock_ind);
 76}
 77
 78static acpi_status hsmp_resource(struct acpi_resource *res, void *data)
 79{
 80	struct hsmp_socket *sock = data;
 81	struct resource r;
 82
 83	switch (res->type) {
 84	case ACPI_RESOURCE_TYPE_FIXED_MEMORY32:
 85		if (!acpi_dev_resource_memory(res, &r))
 86			return AE_ERROR;
 87		if (!r.start || r.end < r.start || !(r.flags & IORESOURCE_MEM_WRITEABLE))
 88			return AE_ERROR;
 89		sock->mbinfo.base_addr = r.start;
 90		sock->mbinfo.size = resource_size(&r);
 91		break;
 92	case ACPI_RESOURCE_TYPE_END_TAG:
 93		break;
 94	default:
 95		return AE_ERROR;
 96	}
 97
 98	return AE_OK;
 99}
100
101static int hsmp_read_acpi_dsd(struct hsmp_socket *sock)
102{
103	struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER, NULL };
104	union acpi_object *guid, *mailbox_package;
105	union acpi_object *dsd;
106	acpi_status status;
107	int ret = 0;
108	int j;
109
110	status = acpi_evaluate_object_typed(ACPI_HANDLE(sock->dev), "_DSD", NULL,
111					    &buf, ACPI_TYPE_PACKAGE);
112	if (ACPI_FAILURE(status)) {
113		dev_err(sock->dev, "Failed to read mailbox reg offsets from DSD table, err: %s\n",
114			acpi_format_exception(status));
115		return -ENODEV;
116	}
117
118	dsd = buf.pointer;
119
120	/* HSMP _DSD property should contain 2 objects.
121	 * 1. guid which is an acpi object of type ACPI_TYPE_BUFFER
122	 * 2. mailbox which is an acpi object of type ACPI_TYPE_PACKAGE
123	 *    This mailbox object contains 3 more acpi objects of type
124	 *    ACPI_TYPE_PACKAGE for holding msgid, msgresp, msgarg offsets
125	 *    these packages inturn contain 2 acpi objects of type
126	 *    ACPI_TYPE_STRING and ACPI_TYPE_INTEGER
127	 */
128	if (!dsd || dsd->type != ACPI_TYPE_PACKAGE || dsd->package.count != 2) {
129		ret = -EINVAL;
130		goto free_buf;
131	}
132
133	guid = &dsd->package.elements[0];
134	mailbox_package = &dsd->package.elements[1];
135	if (!is_acpi_hsmp_uuid(guid) || mailbox_package->type != ACPI_TYPE_PACKAGE) {
136		dev_err(sock->dev, "Invalid hsmp _DSD table data\n");
137		ret = -EINVAL;
138		goto free_buf;
139	}
140
141	for (j = 0; j < mailbox_package->package.count; j++) {
142		union acpi_object *msgobj, *msgstr, *msgint;
143
144		msgobj	= &mailbox_package->package.elements[j];
145		msgstr	= &msgobj->package.elements[0];
146		msgint	= &msgobj->package.elements[1];
147
148		/* package should have 1 string and 1 integer object */
149		if (msgobj->type != ACPI_TYPE_PACKAGE ||
150		    msgstr->type != ACPI_TYPE_STRING ||
151		    msgint->type != ACPI_TYPE_INTEGER) {
152			ret = -EINVAL;
153			goto free_buf;
154		}
155
156		if (!strncmp(msgstr->string.pointer, MSG_IDOFF_STR,
157			     msgstr->string.length)) {
158			sock->mbinfo.msg_id_off = msgint->integer.value;
159		} else if (!strncmp(msgstr->string.pointer, MSG_RESPOFF_STR,
160				    msgstr->string.length)) {
161			sock->mbinfo.msg_resp_off =  msgint->integer.value;
162		} else if (!strncmp(msgstr->string.pointer, MSG_ARGOFF_STR,
163				    msgstr->string.length)) {
164			sock->mbinfo.msg_arg_off = msgint->integer.value;
165		} else {
166			ret = -ENOENT;
167			goto free_buf;
168		}
169	}
170
171	if (!sock->mbinfo.msg_id_off || !sock->mbinfo.msg_resp_off ||
172	    !sock->mbinfo.msg_arg_off)
173		ret = -EINVAL;
174
175free_buf:
176	ACPI_FREE(buf.pointer);
177	return ret;
178}
179
180static int hsmp_read_acpi_crs(struct hsmp_socket *sock)
181{
182	acpi_status status;
183
184	status = acpi_walk_resources(ACPI_HANDLE(sock->dev), METHOD_NAME__CRS,
185				     hsmp_resource, sock);
186	if (ACPI_FAILURE(status)) {
187		dev_err(sock->dev, "Failed to look up MP1 base address from CRS method, err: %s\n",
188			acpi_format_exception(status));
189		return -EINVAL;
190	}
191	if (!sock->mbinfo.base_addr || !sock->mbinfo.size)
192		return -EINVAL;
193
194	/* The mapped region should be un-cached */
195	sock->virt_base_addr = devm_ioremap_uc(sock->dev, sock->mbinfo.base_addr,
196					       sock->mbinfo.size);
197	if (!sock->virt_base_addr) {
198		dev_err(sock->dev, "Failed to ioremap MP1 base address\n");
199		return -ENOMEM;
200	}
201
202	return 0;
203}
204
205/* Parse the ACPI table to read the data */
206static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind)
207{
208	struct hsmp_socket *sock = &hsmp_pdev->sock[sock_ind];
209	int ret;
210
211	sock->sock_ind		= sock_ind;
212	sock->dev		= dev;
213	sock->amd_hsmp_rdwr	= amd_hsmp_acpi_rdwr;
214
215	sema_init(&sock->hsmp_sem, 1);
216
217	dev_set_drvdata(dev, sock);
218
219	/* Read MP1 base address from CRS method */
220	ret = hsmp_read_acpi_crs(sock);
221	if (ret)
222		return ret;
223
224	/* Read mailbox offsets from DSD table */
225	return hsmp_read_acpi_dsd(sock);
226}
227
228static ssize_t hsmp_metric_tbl_acpi_read(struct file *filp, struct kobject *kobj,
229					 struct bin_attribute *bin_attr, char *buf,
230					 loff_t off, size_t count)
231{
232	struct device *dev = container_of(kobj, struct device, kobj);
233	struct hsmp_socket *sock = dev_get_drvdata(dev);
234
235	return hsmp_metric_tbl_read(sock, buf, count);
236}
237
238static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj,
239					 const struct bin_attribute *battr, int id)
240{
241	if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6)
242		return battr->attr.mode;
243
244	return 0;
245}
246
247static int init_acpi(struct device *dev)
248{
249	u16 sock_ind;
250	int ret;
251
252	ret = hsmp_get_uid(dev, &sock_ind);
253	if (ret)
254		return ret;
255	if (sock_ind >= hsmp_pdev->num_sockets)
256		return -EINVAL;
257
258	ret = hsmp_parse_acpi_table(dev, sock_ind);
259	if (ret) {
260		dev_err(dev, "Failed to parse ACPI table\n");
261		return ret;
262	}
263
264	/* Test the hsmp interface */
265	ret = hsmp_test(sock_ind, 0xDEADBEEF);
266	if (ret) {
267		dev_err(dev, "HSMP test message failed on Fam:%x model:%x\n",
268			boot_cpu_data.x86, boot_cpu_data.x86_model);
269		dev_err(dev, "Is HSMP disabled in BIOS ?\n");
270		return ret;
271	}
272
273	ret = hsmp_cache_proto_ver(sock_ind);
274	if (ret) {
275		dev_err(dev, "Failed to read HSMP protocol version\n");
276		return ret;
277	}
278
279	if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) {
280		ret = hsmp_get_tbl_dram_base(sock_ind);
281		if (ret)
282			dev_err(dev, "Failed to init metric table\n");
283	}
284
285	return ret;
286}
287
288static struct bin_attribute  hsmp_metric_tbl_attr = {
289	.attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444},
290	.read = hsmp_metric_tbl_acpi_read,
291	.size = sizeof(struct hsmp_metric_table),
292};
293
294static struct bin_attribute *hsmp_attr_list[] = {
295	&hsmp_metric_tbl_attr,
296	NULL
297};
298
299static struct attribute_group hsmp_attr_grp = {
300	.bin_attrs = hsmp_attr_list,
301	.is_bin_visible = hsmp_is_sock_attr_visible,
302};
303
304static const struct attribute_group *hsmp_groups[] = {
305	&hsmp_attr_grp,
306	NULL
307};
308
309static const struct acpi_device_id amd_hsmp_acpi_ids[] = {
310	{ACPI_HSMP_DEVICE_HID, 0},
311	{}
312};
313MODULE_DEVICE_TABLE(acpi, amd_hsmp_acpi_ids);
314
315static int hsmp_acpi_probe(struct platform_device *pdev)
316{
317	int ret;
318
319	hsmp_pdev = get_hsmp_pdev();
320	if (!hsmp_pdev)
321		return -ENOMEM;
322
323	if (!hsmp_pdev->is_probed) {
324		hsmp_pdev->num_sockets = amd_nb_num();
325		if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_SOCKETS)
326			return -ENODEV;
327
328		hsmp_pdev->sock = devm_kcalloc(&pdev->dev, hsmp_pdev->num_sockets,
329					       sizeof(*hsmp_pdev->sock),
330					       GFP_KERNEL);
331		if (!hsmp_pdev->sock)
332			return -ENOMEM;
333	}
334
335	ret = init_acpi(&pdev->dev);
336	if (ret) {
337		dev_err(&pdev->dev, "Failed to initialize HSMP interface.\n");
338		return ret;
339	}
340
341	if (!hsmp_pdev->is_probed) {
342		ret = hsmp_misc_register(&pdev->dev);
343		if (ret)
344			return ret;
345		hsmp_pdev->is_probed = true;
346	}
347
348	return 0;
349}
350
351static void hsmp_acpi_remove(struct platform_device *pdev)
352{
353	/*
354	 * We register only one misc_device even on multi-socket system.
355	 * So, deregister should happen only once.
356	 */
357	if (hsmp_pdev->is_probed) {
358		hsmp_misc_deregister();
359		hsmp_pdev->is_probed = false;
360	}
361}
362
363static struct platform_driver amd_hsmp_driver = {
364	.probe		= hsmp_acpi_probe,
365	.remove		= hsmp_acpi_remove,
366	.driver		= {
367		.name	= DRIVER_NAME,
368		.acpi_match_table = amd_hsmp_acpi_ids,
369		.dev_groups = hsmp_groups,
370	},
371};
372
373module_platform_driver(amd_hsmp_driver);
374
375MODULE_IMPORT_NS("AMD_HSMP");
376MODULE_DESCRIPTION("AMD HSMP Platform Interface Driver");
377MODULE_VERSION(DRIVER_VERSION);
378MODULE_LICENSE("GPL");