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 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 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 77c1691730SSuma Hegde static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj, 78*55cb93fdSLinus 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 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 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 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 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 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 */ 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 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 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 3354fc0366eSSuma Hegde 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