File Index
Files and directories
Sign in
FILE

plat.c

File preview with syntax highlighting.

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