xref: /linux/drivers/platform/x86/amd/hsmp/plat.c (revision cdd30ebb1b9f36159d66f088b61aee264e649d7a)
1e47c018aSSuma Hegde // SPDX-License-Identifier: GPL-2.0
2e47c018aSSuma Hegde /*
3e47c018aSSuma Hegde  * AMD HSMP Platform Driver
4e47c018aSSuma Hegde  * Copyright (c) 2024, AMD.
5e47c018aSSuma Hegde  * All Rights Reserved.
6e47c018aSSuma Hegde  *
7e47c018aSSuma Hegde  * This file provides platform device implementations.
8e47c018aSSuma Hegde  */
9e47c018aSSuma Hegde 
10e47c018aSSuma Hegde #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11e47c018aSSuma Hegde 
12c1691730SSuma Hegde #include <asm/amd_hsmp.h>
13e47c018aSSuma Hegde #include <asm/amd_nb.h>
14e47c018aSSuma Hegde 
15e47c018aSSuma Hegde #include <linux/device.h>
167d3135d1SSuma Hegde #include <linux/module.h>
17e47c018aSSuma Hegde #include <linux/pci.h>
187d3135d1SSuma Hegde #include <linux/platform_device.h>
19e47c018aSSuma Hegde #include <linux/sysfs.h>
20e47c018aSSuma Hegde 
21e47c018aSSuma Hegde #include "hsmp.h"
22e47c018aSSuma Hegde 
237d3135d1SSuma Hegde #define DRIVER_NAME		"amd_hsmp"
247d3135d1SSuma Hegde #define DRIVER_VERSION		"2.3"
257d3135d1SSuma Hegde 
26e47c018aSSuma Hegde /*
27e47c018aSSuma Hegde  * To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox
28e47c018aSSuma Hegde  * register into the SMN_INDEX register, and reads/writes the SMN_DATA reg.
29e47c018aSSuma Hegde  * Below are required SMN address for HSMP Mailbox register offsets in SMU address space
30e47c018aSSuma Hegde  */
31e47c018aSSuma Hegde #define SMN_HSMP_BASE		0x3B00000
32e47c018aSSuma Hegde #define SMN_HSMP_MSG_ID		0x0010534
33e47c018aSSuma Hegde #define SMN_HSMP_MSG_ID_F1A_M0H	0x0010934
34e47c018aSSuma Hegde #define SMN_HSMP_MSG_RESP	0x0010980
35e47c018aSSuma Hegde #define SMN_HSMP_MSG_DATA	0x00109E0
36e47c018aSSuma Hegde 
37e47c018aSSuma Hegde #define HSMP_INDEX_REG		0xc4
38e47c018aSSuma Hegde #define HSMP_DATA_REG		0xc8
39e47c018aSSuma Hegde 
401349dd7dSSuma Hegde static struct hsmp_plat_device *hsmp_pdev;
411349dd7dSSuma Hegde 
amd_hsmp_pci_rdwr(struct hsmp_socket * sock,u32 offset,u32 * value,bool write)42e47c018aSSuma Hegde static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset,
43e47c018aSSuma Hegde 			     u32 *value, bool write)
44e47c018aSSuma Hegde {
45e47c018aSSuma Hegde 	int ret;
46e47c018aSSuma Hegde 
47e47c018aSSuma Hegde 	if (!sock->root)
48e47c018aSSuma Hegde 		return -ENODEV;
49e47c018aSSuma Hegde 
50e47c018aSSuma Hegde 	ret = pci_write_config_dword(sock->root, HSMP_INDEX_REG,
51e47c018aSSuma Hegde 				     sock->mbinfo.base_addr + offset);
52e47c018aSSuma Hegde 	if (ret)
53e47c018aSSuma Hegde 		return ret;
54e47c018aSSuma Hegde 
55e47c018aSSuma Hegde 	ret = (write ? pci_write_config_dword(sock->root, HSMP_DATA_REG, *value)
56e47c018aSSuma Hegde 		     : pci_read_config_dword(sock->root, HSMP_DATA_REG, value));
57e47c018aSSuma Hegde 
58e47c018aSSuma Hegde 	return ret;
59e47c018aSSuma Hegde }
60e47c018aSSuma Hegde 
hsmp_metric_tbl_plat_read(struct file * filp,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t count)61c1691730SSuma Hegde static ssize_t hsmp_metric_tbl_plat_read(struct file *filp, struct kobject *kobj,
62c1691730SSuma Hegde 					 struct bin_attribute *bin_attr, char *buf,
63c1691730SSuma Hegde 					 loff_t off, size_t count)
64e47c018aSSuma Hegde {
65c1691730SSuma Hegde 	struct hsmp_socket *sock;
66c1691730SSuma Hegde 	u16 sock_ind;
67e47c018aSSuma Hegde 
68c1691730SSuma Hegde 	sock_ind = (uintptr_t)bin_attr->private;
691349dd7dSSuma Hegde 	if (sock_ind >= hsmp_pdev->num_sockets)
70c1691730SSuma Hegde 		return -EINVAL;
71e47c018aSSuma Hegde 
721349dd7dSSuma Hegde 	sock = &hsmp_pdev->sock[sock_ind];
73e47c018aSSuma Hegde 
74c1691730SSuma Hegde 	return hsmp_metric_tbl_read(sock, buf, count);
75e47c018aSSuma Hegde }
76e47c018aSSuma Hegde 
hsmp_is_sock_attr_visible(struct kobject * kobj,const struct bin_attribute * battr,int id)77c1691730SSuma Hegde static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj,
7855cb93fdSLinus Torvalds 					 const struct bin_attribute *battr, int id)
79c1691730SSuma Hegde {
80c1691730SSuma Hegde 	u16 sock_ind;
81c1691730SSuma Hegde 
82c1691730SSuma Hegde 	sock_ind = (uintptr_t)battr->private;
83c1691730SSuma Hegde 
841349dd7dSSuma Hegde 	if (id == 0 && sock_ind >= hsmp_pdev->num_sockets)
85c1691730SSuma Hegde 		return SYSFS_GROUP_INVISIBLE;
86c1691730SSuma Hegde 
871349dd7dSSuma Hegde 	if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6)
88c1691730SSuma Hegde 		return battr->attr.mode;
89c1691730SSuma Hegde 
90c1691730SSuma Hegde 	return 0;
91e47c018aSSuma Hegde }
92e47c018aSSuma Hegde 
93c1691730SSuma Hegde /*
94c1691730SSuma Hegde  * AMD supports maximum of 8 sockets in a system.
95c1691730SSuma Hegde  * Static array of 8 + 1(for NULL) elements is created below
96c1691730SSuma Hegde  * to create sysfs groups for sockets.
97c1691730SSuma Hegde  * is_bin_visible function is used to show / hide the necessary groups.
98c1691730SSuma Hegde  */
99c1691730SSuma Hegde #define HSMP_BIN_ATTR(index, _list)					\
100c1691730SSuma Hegde static struct bin_attribute attr##index = {				\
101c1691730SSuma Hegde 	.attr = { .name = HSMP_METRICS_TABLE_NAME, .mode = 0444},	\
102c1691730SSuma Hegde 	.private = (void *)index,					\
103c1691730SSuma Hegde 	.read = hsmp_metric_tbl_plat_read,					\
104c1691730SSuma Hegde 	.size = sizeof(struct hsmp_metric_table),			\
105c1691730SSuma Hegde };									\
106c1691730SSuma Hegde static struct bin_attribute _list[] = {					\
107c1691730SSuma Hegde 	&attr##index,							\
108c1691730SSuma Hegde 	NULL								\
109c1691730SSuma Hegde }
110c1691730SSuma Hegde 
111c1691730SSuma Hegde HSMP_BIN_ATTR(0, *sock0_attr_list);
112c1691730SSuma Hegde HSMP_BIN_ATTR(1, *sock1_attr_list);
113c1691730SSuma Hegde HSMP_BIN_ATTR(2, *sock2_attr_list);
114c1691730SSuma Hegde HSMP_BIN_ATTR(3, *sock3_attr_list);
115c1691730SSuma Hegde HSMP_BIN_ATTR(4, *sock4_attr_list);
116c1691730SSuma Hegde HSMP_BIN_ATTR(5, *sock5_attr_list);
117c1691730SSuma Hegde HSMP_BIN_ATTR(6, *sock6_attr_list);
118c1691730SSuma Hegde HSMP_BIN_ATTR(7, *sock7_attr_list);
119c1691730SSuma Hegde 
120c1691730SSuma Hegde #define HSMP_BIN_ATTR_GRP(index, _list, _name)			\
121c1691730SSuma Hegde static struct attribute_group sock##index##_attr_grp = {	\
122c1691730SSuma Hegde 	.bin_attrs = _list,					\
123c1691730SSuma Hegde 	.is_bin_visible = hsmp_is_sock_attr_visible,		\
124c1691730SSuma Hegde 	.name = #_name,						\
125c1691730SSuma Hegde }
126c1691730SSuma Hegde 
127c1691730SSuma Hegde HSMP_BIN_ATTR_GRP(0, sock0_attr_list, socket0);
128c1691730SSuma Hegde HSMP_BIN_ATTR_GRP(1, sock1_attr_list, socket1);
129c1691730SSuma Hegde HSMP_BIN_ATTR_GRP(2, sock2_attr_list, socket2);
130c1691730SSuma Hegde HSMP_BIN_ATTR_GRP(3, sock3_attr_list, socket3);
131c1691730SSuma Hegde HSMP_BIN_ATTR_GRP(4, sock4_attr_list, socket4);
132c1691730SSuma Hegde HSMP_BIN_ATTR_GRP(5, sock5_attr_list, socket5);
133c1691730SSuma Hegde HSMP_BIN_ATTR_GRP(6, sock6_attr_list, socket6);
134c1691730SSuma Hegde HSMP_BIN_ATTR_GRP(7, sock7_attr_list, socket7);
135c1691730SSuma Hegde 
136c1691730SSuma Hegde static const struct attribute_group *hsmp_groups[] = {
137c1691730SSuma Hegde 	&sock0_attr_grp,
138c1691730SSuma Hegde 	&sock1_attr_grp,
139c1691730SSuma Hegde 	&sock2_attr_grp,
140c1691730SSuma Hegde 	&sock3_attr_grp,
141c1691730SSuma Hegde 	&sock4_attr_grp,
142c1691730SSuma Hegde 	&sock5_attr_grp,
143c1691730SSuma Hegde 	&sock6_attr_grp,
144c1691730SSuma Hegde 	&sock7_attr_grp,
145c1691730SSuma Hegde 	NULL
146c1691730SSuma Hegde };
147c1691730SSuma Hegde 
is_f1a_m0h(void)148e47c018aSSuma Hegde static inline bool is_f1a_m0h(void)
149e47c018aSSuma Hegde {
150e47c018aSSuma Hegde 	if (boot_cpu_data.x86 == 0x1A && boot_cpu_data.x86_model <= 0x0F)
151e47c018aSSuma Hegde 		return true;
152e47c018aSSuma Hegde 
153e47c018aSSuma Hegde 	return false;
154e47c018aSSuma Hegde }
155e47c018aSSuma Hegde 
init_platform_device(struct device * dev)1567d3135d1SSuma Hegde static int init_platform_device(struct device *dev)
157e47c018aSSuma Hegde {
158e47c018aSSuma Hegde 	struct hsmp_socket *sock;
159e47c018aSSuma Hegde 	int ret, i;
160e47c018aSSuma Hegde 
1611349dd7dSSuma Hegde 	for (i = 0; i < hsmp_pdev->num_sockets; i++) {
162e47c018aSSuma Hegde 		if (!node_to_amd_nb(i))
163e47c018aSSuma Hegde 			return -ENODEV;
1641349dd7dSSuma Hegde 		sock = &hsmp_pdev->sock[i];
165e47c018aSSuma Hegde 		sock->root			= node_to_amd_nb(i)->root;
166e47c018aSSuma Hegde 		sock->sock_ind			= i;
167e47c018aSSuma Hegde 		sock->dev			= dev;
168e47c018aSSuma Hegde 		sock->mbinfo.base_addr		= SMN_HSMP_BASE;
169e47c018aSSuma Hegde 		sock->amd_hsmp_rdwr		= amd_hsmp_pci_rdwr;
170e47c018aSSuma Hegde 
171e47c018aSSuma Hegde 		/*
172e47c018aSSuma Hegde 		 * This is a transitional change from non-ACPI to ACPI, only
173e47c018aSSuma Hegde 		 * family 0x1A, model 0x00 platform is supported for both ACPI and non-ACPI.
174e47c018aSSuma Hegde 		 */
175e47c018aSSuma Hegde 		if (is_f1a_m0h())
176e47c018aSSuma Hegde 			sock->mbinfo.msg_id_off	= SMN_HSMP_MSG_ID_F1A_M0H;
177e47c018aSSuma Hegde 		else
178e47c018aSSuma Hegde 			sock->mbinfo.msg_id_off	= SMN_HSMP_MSG_ID;
179e47c018aSSuma Hegde 
180e47c018aSSuma Hegde 		sock->mbinfo.msg_resp_off	= SMN_HSMP_MSG_RESP;
181e47c018aSSuma Hegde 		sock->mbinfo.msg_arg_off	= SMN_HSMP_MSG_DATA;
182e47c018aSSuma Hegde 		sema_init(&sock->hsmp_sem, 1);
183e47c018aSSuma Hegde 
184e47c018aSSuma Hegde 		/* Test the hsmp interface on each socket */
185e47c018aSSuma Hegde 		ret = hsmp_test(i, 0xDEADBEEF);
186e47c018aSSuma Hegde 		if (ret) {
187e47c018aSSuma Hegde 			dev_err(dev, "HSMP test message failed on Fam:%x model:%x\n",
188e47c018aSSuma Hegde 				boot_cpu_data.x86, boot_cpu_data.x86_model);
189e47c018aSSuma Hegde 			dev_err(dev, "Is HSMP disabled in BIOS ?\n");
190e47c018aSSuma Hegde 			return ret;
191e47c018aSSuma Hegde 		}
192e47c018aSSuma Hegde 
193e47c018aSSuma Hegde 		ret = hsmp_cache_proto_ver(i);
194e47c018aSSuma Hegde 		if (ret) {
195e47c018aSSuma Hegde 			dev_err(dev, "Failed to read HSMP protocol version\n");
196e47c018aSSuma Hegde 			return ret;
197e47c018aSSuma Hegde 		}
198c1691730SSuma Hegde 
1991349dd7dSSuma Hegde 		if (hsmp_pdev->proto_ver == HSMP_PROTO_VER6) {
200c1691730SSuma Hegde 			ret = hsmp_get_tbl_dram_base(i);
201c1691730SSuma Hegde 			if (ret)
202c1691730SSuma Hegde 				dev_err(dev, "Failed to init metric table\n");
203c1691730SSuma Hegde 		}
204e47c018aSSuma Hegde 	}
205e47c018aSSuma Hegde 
206e47c018aSSuma Hegde 	return 0;
207e47c018aSSuma Hegde }
2087d3135d1SSuma Hegde 
hsmp_pltdrv_probe(struct platform_device * pdev)2097d3135d1SSuma Hegde static int hsmp_pltdrv_probe(struct platform_device *pdev)
2107d3135d1SSuma Hegde {
2117d3135d1SSuma Hegde 	int ret;
2127d3135d1SSuma Hegde 
2131349dd7dSSuma Hegde 	hsmp_pdev->sock = devm_kcalloc(&pdev->dev, hsmp_pdev->num_sockets,
2141349dd7dSSuma Hegde 				       sizeof(*hsmp_pdev->sock),
2157d3135d1SSuma Hegde 				       GFP_KERNEL);
2161349dd7dSSuma Hegde 	if (!hsmp_pdev->sock)
2177d3135d1SSuma Hegde 		return -ENOMEM;
2187d3135d1SSuma Hegde 
2197d3135d1SSuma Hegde 	ret = init_platform_device(&pdev->dev);
2207d3135d1SSuma Hegde 	if (ret) {
2217d3135d1SSuma Hegde 		dev_err(&pdev->dev, "Failed to init HSMP mailbox\n");
2227d3135d1SSuma Hegde 		return ret;
2237d3135d1SSuma Hegde 	}
2247d3135d1SSuma Hegde 
2257d3135d1SSuma Hegde 	return hsmp_misc_register(&pdev->dev);
2267d3135d1SSuma Hegde }
2277d3135d1SSuma Hegde 
hsmp_pltdrv_remove(struct platform_device * pdev)2287d3135d1SSuma Hegde static void hsmp_pltdrv_remove(struct platform_device *pdev)
2297d3135d1SSuma Hegde {
2307d3135d1SSuma Hegde 	hsmp_misc_deregister();
2317d3135d1SSuma Hegde }
2327d3135d1SSuma Hegde 
2337d3135d1SSuma Hegde static struct platform_driver amd_hsmp_driver = {
2347d3135d1SSuma Hegde 	.probe		= hsmp_pltdrv_probe,
2357d3135d1SSuma Hegde 	.remove		= hsmp_pltdrv_remove,
2367d3135d1SSuma Hegde 	.driver		= {
2377d3135d1SSuma Hegde 		.name	= DRIVER_NAME,
238c1691730SSuma Hegde 		.dev_groups = hsmp_groups,
2397d3135d1SSuma Hegde 	},
2407d3135d1SSuma Hegde };
2417d3135d1SSuma Hegde 
2427d3135d1SSuma Hegde static struct platform_device *amd_hsmp_platdev;
2437d3135d1SSuma Hegde 
hsmp_plat_dev_register(void)2447d3135d1SSuma Hegde static int hsmp_plat_dev_register(void)
2457d3135d1SSuma Hegde {
2467d3135d1SSuma Hegde 	int ret;
2477d3135d1SSuma Hegde 
2487d3135d1SSuma Hegde 	amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE);
2497d3135d1SSuma Hegde 	if (!amd_hsmp_platdev)
2507d3135d1SSuma Hegde 		return -ENOMEM;
2517d3135d1SSuma Hegde 
2527d3135d1SSuma Hegde 	ret = platform_device_add(amd_hsmp_platdev);
2537d3135d1SSuma Hegde 	if (ret)
2547d3135d1SSuma Hegde 		platform_device_put(amd_hsmp_platdev);
2557d3135d1SSuma Hegde 
2567d3135d1SSuma Hegde 	return ret;
2577d3135d1SSuma Hegde }
2587d3135d1SSuma Hegde 
2597d3135d1SSuma Hegde /*
2607d3135d1SSuma Hegde  * This check is only needed for backward compatibility of previous platforms.
2617d3135d1SSuma Hegde  * All new platforms are expected to support ACPI based probing.
2627d3135d1SSuma Hegde  */
legacy_hsmp_support(void)2637d3135d1SSuma Hegde static bool legacy_hsmp_support(void)
2647d3135d1SSuma Hegde {
2657d3135d1SSuma Hegde 	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
2667d3135d1SSuma Hegde 		return false;
2677d3135d1SSuma Hegde 
2687d3135d1SSuma Hegde 	switch (boot_cpu_data.x86) {
2697d3135d1SSuma Hegde 	case 0x19:
2707d3135d1SSuma Hegde 		switch (boot_cpu_data.x86_model) {
2717d3135d1SSuma Hegde 		case 0x00 ... 0x1F:
2727d3135d1SSuma Hegde 		case 0x30 ... 0x3F:
2737d3135d1SSuma Hegde 		case 0x90 ... 0x9F:
2747d3135d1SSuma Hegde 		case 0xA0 ... 0xAF:
2757d3135d1SSuma Hegde 			return true;
2767d3135d1SSuma Hegde 		default:
2777d3135d1SSuma Hegde 			return false;
2787d3135d1SSuma Hegde 		}
2797d3135d1SSuma Hegde 	case 0x1A:
2807d3135d1SSuma Hegde 		switch (boot_cpu_data.x86_model) {
2817d3135d1SSuma Hegde 		case 0x00 ... 0x1F:
2827d3135d1SSuma Hegde 			return true;
2837d3135d1SSuma Hegde 		default:
2847d3135d1SSuma Hegde 			return false;
2857d3135d1SSuma Hegde 		}
2867d3135d1SSuma Hegde 	default:
2877d3135d1SSuma Hegde 		return false;
2887d3135d1SSuma Hegde 	}
2897d3135d1SSuma Hegde 
2907d3135d1SSuma Hegde 	return false;
2917d3135d1SSuma Hegde }
2927d3135d1SSuma Hegde 
hsmp_plt_init(void)2937d3135d1SSuma Hegde static int __init hsmp_plt_init(void)
2947d3135d1SSuma Hegde {
2957d3135d1SSuma Hegde 	int ret = -ENODEV;
2967d3135d1SSuma Hegde 
2977d3135d1SSuma Hegde 	if (!legacy_hsmp_support()) {
2987d3135d1SSuma Hegde 		pr_info("HSMP is not supported on Family:%x model:%x\n",
2997d3135d1SSuma Hegde 			boot_cpu_data.x86, boot_cpu_data.x86_model);
3007d3135d1SSuma Hegde 		return ret;
3017d3135d1SSuma Hegde 	}
3027d3135d1SSuma Hegde 
3031349dd7dSSuma Hegde 	hsmp_pdev = get_hsmp_pdev();
3041349dd7dSSuma Hegde 	if (!hsmp_pdev)
3051349dd7dSSuma Hegde 		return -ENOMEM;
3061349dd7dSSuma Hegde 
3077d3135d1SSuma Hegde 	/*
3087d3135d1SSuma Hegde 	 * amd_nb_num() returns number of SMN/DF interfaces present in the system
3097d3135d1SSuma Hegde 	 * if we have N SMN/DF interfaces that ideally means N sockets
3107d3135d1SSuma Hegde 	 */
3111349dd7dSSuma Hegde 	hsmp_pdev->num_sockets = amd_nb_num();
3121349dd7dSSuma Hegde 	if (hsmp_pdev->num_sockets == 0 || hsmp_pdev->num_sockets > MAX_AMD_SOCKETS)
3137d3135d1SSuma Hegde 		return ret;
3147d3135d1SSuma Hegde 
3157d3135d1SSuma Hegde 	ret = platform_driver_register(&amd_hsmp_driver);
3167d3135d1SSuma Hegde 	if (ret)
3177d3135d1SSuma Hegde 		return ret;
3187d3135d1SSuma Hegde 
3197d3135d1SSuma Hegde 	ret = hsmp_plat_dev_register();
3207d3135d1SSuma Hegde 	if (ret)
3217d3135d1SSuma Hegde 		platform_driver_unregister(&amd_hsmp_driver);
3227d3135d1SSuma Hegde 
3237d3135d1SSuma Hegde 	return ret;
3247d3135d1SSuma Hegde }
3257d3135d1SSuma Hegde 
hsmp_plt_exit(void)3267d3135d1SSuma Hegde static void __exit hsmp_plt_exit(void)
3277d3135d1SSuma Hegde {
3287d3135d1SSuma Hegde 	platform_device_unregister(amd_hsmp_platdev);
3297d3135d1SSuma Hegde 	platform_driver_unregister(&amd_hsmp_driver);
3307d3135d1SSuma Hegde }
3317d3135d1SSuma Hegde 
3327d3135d1SSuma Hegde device_initcall(hsmp_plt_init);
3337d3135d1SSuma Hegde module_exit(hsmp_plt_exit);
3347d3135d1SSuma Hegde 
335*cdd30ebbSPeter Zijlstra MODULE_IMPORT_NS("AMD_HSMP");
3367d3135d1SSuma Hegde MODULE_DESCRIPTION("AMD HSMP Platform Interface Driver");
3377d3135d1SSuma Hegde MODULE_VERSION(DRIVER_VERSION);
3387d3135d1SSuma Hegde MODULE_LICENSE("GPL");
339