Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.6.
  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 platform device implementations.
  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/device.h>
 16#include <linux/module.h>
 17#include <linux/pci.h>
 18#include <linux/platform_device.h>
 19#include <linux/sysfs.h>
 20
 21#include "hsmp.h"
 22
 23#define DRIVER_NAME		"amd_hsmp"
 24#define DRIVER_VERSION		"2.3"
 25
 26/*
 27 * To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox
 28 * register into the SMN_INDEX register, and reads/writes the SMN_DATA reg.
 29 * Below are required SMN address for HSMP Mailbox register offsets in SMU address space
 30 */
 31#define SMN_HSMP_BASE		0x3B00000
 32#define SMN_HSMP_MSG_ID		0x0010534
 33#define SMN_HSMP_MSG_ID_F1A_M0H	0x0010934
 34#define SMN_HSMP_MSG_RESP	0x0010980
 35#define SMN_HSMP_MSG_DATA	0x00109E0
 36
 37#define HSMP_INDEX_REG		0xc4
 38#define HSMP_DATA_REG		0xc8
 39
 40static struct hsmp_plat_device *hsmp_pdev;
 41
 42static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset,
 43			     u32 *value, bool write)
 44{
 45	int ret;
 46
 47	if (!sock->root)
 48		return -ENODEV;
 49
 50	ret = pci_write_config_dword(sock->root, HSMP_INDEX_REG,
 51				     sock->mbinfo.base_addr + offset);
 52	if (ret)
 53		return ret;
 54
 55	ret = (write ? pci_write_config_dword(sock->root, HSMP_DATA_REG, *value)
 56		     : pci_read_config_dword(sock->root, HSMP_DATA_REG, value));
 57
 58	return ret;
 59}
 60
 61static ssize_t hsmp_metric_tbl_plat_read(struct file *filp, struct kobject *kobj,
 62					 struct bin_attribute *bin_attr, char *buf,
 63					 loff_t off, size_t count)
 64{
 65	struct hsmp_socket *sock;
 66	u16 sock_ind;
 67
 68	sock_ind = (uintptr_t)bin_attr->private;
 69	if (sock_ind >= hsmp_pdev->num_sockets)
 70		return -EINVAL;
 71
 72	sock = &hsmp_pdev->sock[sock_ind];
 73
 74	return hsmp_metric_tbl_read(sock, buf, count);
 75}
 76
 77static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj,
 78					 const struct bin_attribute *battr, int id)
 79{
 80	u16 sock_ind;
 81
 82	sock_ind = (uintptr_t)battr->private;
 83
 84	if (id == 0 && sock_ind >= hsmp_pdev->num_sockets)
 85		return SYSFS_GROUP_INVISIBLE;
 86
 87	if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6)
 88		return battr->attr.mode;
 89
 90	return 0;
 91}
 92
 93/*
 94 * AMD supports maximum of 8 sockets in a system.
 95 * Static array of 8 + 1(for NULL) elements is created below
 96 * to create sysfs groups for sockets.
 97 * is_bin_visible function is used to show / hide the necessary groups.
 98 */
 99#define HSMP_BIN_ATTR(index, _list)					\
100static struct bin_attribute attr##index = {				\
101	.attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444},	\
102	.private = (void *)index,					\
103	.read = hsmp_metric_tbl_plat_read,					\
104	.size = sizeof(struct hsmp_metric_table),			\
105};									\
106static struct bin_attribute _list[] = {					\
107	&attr##index,							\
108	NULL								\
109}
110
111HSMP_BIN_ATTR(0, *sock0_attr_list);
112HSMP_BIN_ATTR(1, *sock1_attr_list);
113HSMP_BIN_ATTR(2, *sock2_attr_list);
114HSMP_BIN_ATTR(3, *sock3_attr_list);
115HSMP_BIN_ATTR(4, *sock4_attr_list);
116HSMP_BIN_ATTR(5, *sock5_attr_list);
117HSMP_BIN_ATTR(6, *sock6_attr_list);
118HSMP_BIN_ATTR(7, *sock7_attr_list);
119
120#define HSMP_BIN_ATTR_GRP(index, _list, _name)			\
121static struct attribute_group sock##index##_attr_grp = {	\
122	.bin_attrs = _list,					\
123	.is_bin_visible = hsmp_is_sock_attr_visible,		\
124	.name = #_name,						\
125}
126
127HSMP_BIN_ATTR_GRP(0, sock0_attr_list, socket0);
128HSMP_BIN_ATTR_GRP(1, sock1_attr_list, socket1);
129HSMP_BIN_ATTR_GRP(2, sock2_attr_list, socket2);
130HSMP_BIN_ATTR_GRP(3, sock3_attr_list, socket3);
131HSMP_BIN_ATTR_GRP(4, sock4_attr_list, socket4);
132HSMP_BIN_ATTR_GRP(5, sock5_attr_list, socket5);
133HSMP_BIN_ATTR_GRP(6, sock6_attr_list, socket6);
134HSMP_BIN_ATTR_GRP(7, sock7_attr_list, socket7);
135
136static const struct attribute_group *hsmp_groups[] = {
137	&sock0_attr_grp,
138	&sock1_attr_grp,
139	&sock2_attr_grp,
140	&sock3_attr_grp,
141	&sock4_attr_grp,
142	&sock5_attr_grp,
143	&sock6_attr_grp,
144	&sock7_attr_grp,
145	NULL
146};
147
148static inline bool is_f1a_m0h(void)
149{
150	if (boot_cpu_data.x86 == 0x1A && boot_cpu_data.x86_model <= 0x0F)
151		return true;
152
153	return false;
154}
155
156static int init_platform_device(struct device *dev)
157{
158	struct hsmp_socket *sock;
159	int ret, i;
160
161	for (i = 0; i < hsmp_pdev->num_sockets; i++) {
162		if (!node_to_amd_nb(i))
163			return -ENODEV;
164		sock = &hsmp_pdev->sock[i];
165		sock->root			= node_to_amd_nb(i)->root;
166		sock->sock_ind			= i;
167		sock->dev			= dev;
168		sock->mbinfo.base_addr		= SMN_HSMP_BASE;
169		sock->amd_hsmp_rdwr		= amd_hsmp_pci_rdwr;
170
171		/*
172		 * This is a transitional change from non-ACPI to ACPI, only
173		 * family 0x1A, model 0x00 platform is supported for both ACPI and non-ACPI.
174		 */
175		if (is_f1a_m0h())
176			sock->mbinfo.msg_id_off	= SMN_HSMP_MSG_ID_F1A_M0H;
177		else
178			sock->mbinfo.msg_id_off	= SMN_HSMP_MSG_ID;
179
180		sock->mbinfo.msg_resp_off	= SMN_HSMP_MSG_RESP;
181		sock->mbinfo.msg_arg_off	= SMN_HSMP_MSG_DATA;
182		sema_init(&sock->hsmp_sem, 1);
183
184		/* Test the hsmp interface on each socket */
185		ret = hsmp_test(i, 0xDEADBEEF);
186		if (ret) {
187			dev_err(dev, "HSMP test message failed on Fam:%x model:%x\n",
188				boot_cpu_data.x86, boot_cpu_data.x86_model);
189			dev_err(dev, "Is HSMP disabled in BIOS ?\n");
190			return ret;
191		}
192
193		ret = hsmp_cache_proto_ver(i);
194		if (ret) {
195			dev_err(dev, "Failed to read HSMP protocol version\n");
196			return ret;
197		}
198
199		if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) {
200			ret = hsmp_get_tbl_dram_base(i);
201			if (ret)
202				dev_err(dev, "Failed to init metric table\n");
203		}
204	}
205
206	return 0;
207}
208
209static int hsmp_pltdrv_probe(struct platform_device *pdev)
210{
211	int ret;
212
213	hsmp_pdev->sock = devm_kcalloc(&pdev->dev, hsmp_pdev->num_sockets,
214				       sizeof(*hsmp_pdev->sock),
215				       GFP_KERNEL);
216	if (!hsmp_pdev->sock)
217		return -ENOMEM;
218
219	ret = init_platform_device(&pdev->dev);
220	if (ret) {
221		dev_err(&pdev->dev, "Failed to init HSMP mailbox\n");
222		return ret;
223	}
224
225	return hsmp_misc_register(&pdev->dev);
226}
227
228static void hsmp_pltdrv_remove(struct platform_device *pdev)
229{
230	hsmp_misc_deregister();
231}
232
233static struct platform_driver amd_hsmp_driver = {
234	.probe		= hsmp_pltdrv_probe,
235	.remove		= hsmp_pltdrv_remove,
236	.driver		= {
237		.name	= DRIVER_NAME,
238		.dev_groups = hsmp_groups,
239	},
240};
241
242static struct platform_device *amd_hsmp_platdev;
243
244static int hsmp_plat_dev_register(void)
245{
246	int ret;
247
248	amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE);
249	if (!amd_hsmp_platdev)
250		return -ENOMEM;
251
252	ret = platform_device_add(amd_hsmp_platdev);
253	if (ret)
254		platform_device_put(amd_hsmp_platdev);
255
256	return ret;
257}
258
259/*
260 * This check is only needed for backward compatibility of previous platforms.
261 * All new platforms are expected to support ACPI based probing.
262 */
263static bool legacy_hsmp_support(void)
264{
265	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
266		return false;
267
268	switch (boot_cpu_data.x86) {
269	case 0x19:
270		switch (boot_cpu_data.x86_model) {
271		case 0x00 ... 0x1F:
272		case 0x30 ... 0x3F:
273		case 0x90 ... 0x9F:
274		case 0xA0 ... 0xAF:
275			return true;
276		default:
277			return false;
278		}
279	case 0x1A:
280		switch (boot_cpu_data.x86_model) {
281		case 0x00 ... 0x1F:
282			return true;
283		default:
284			return false;
285		}
286	default:
287		return false;
288	}
289
290	return false;
291}
292
293static int __init hsmp_plt_init(void)
294{
295	int ret = -ENODEV;
296
297	if (!legacy_hsmp_support()) {
298		pr_info("HSMP is not supported on Family:%x model:%x\n",
299			boot_cpu_data.x86, boot_cpu_data.x86_model);
300		return ret;
301	}
302
303	hsmp_pdev = get_hsmp_pdev();
304	if (!hsmp_pdev)
305		return -ENOMEM;
306
307	/*
308	 * amd_nb_num() returns number of SMN/DF interfaces present in the system
309	 * if we have N SMN/DF interfaces that ideally means N sockets
310	 */
311	hsmp_pdev->num_sockets = amd_nb_num();
312	if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_SOCKETS)
313		return ret;
314
315	ret = platform_driver_register(&amd_hsmp_driver);
316	if (ret)
317		return ret;
318
319	ret = hsmp_plat_dev_register();
320	if (ret)
321		platform_driver_unregister(&amd_hsmp_driver);
322
323	return ret;
324}
325
326static void __exit hsmp_plt_exit(void)
327{
328	platform_device_unregister(amd_hsmp_platdev);
329	platform_driver_unregister(&amd_hsmp_driver);
330}
331
332device_initcall(hsmp_plt_init);
333module_exit(hsmp_plt_exit);
334
335MODULE_IMPORT_NS("AMD_HSMP");
336MODULE_DESCRIPTION("AMD HSMP Platform Interface Driver");
337MODULE_VERSION(DRIVER_VERSION);
338MODULE_LICENSE("GPL");