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