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 12e47c018aSSuma Hegde #include <asm/amd_nb.h> 13e47c018aSSuma Hegde 14e47c018aSSuma Hegde #include <linux/device.h> 157d3135d1SSuma Hegde #include <linux/module.h> 16e47c018aSSuma Hegde #include <linux/pci.h> 177d3135d1SSuma Hegde #include <linux/platform_device.h> 18e47c018aSSuma Hegde #include <linux/sysfs.h> 19e47c018aSSuma Hegde 20e47c018aSSuma Hegde #include "hsmp.h" 21e47c018aSSuma Hegde 227d3135d1SSuma Hegde #define DRIVER_NAME "amd_hsmp" 237d3135d1SSuma Hegde #define DRIVER_VERSION "2.3" 247d3135d1SSuma Hegde 25e47c018aSSuma Hegde /* 26e47c018aSSuma Hegde * To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox 27e47c018aSSuma Hegde * register into the SMN_INDEX register, and reads/writes the SMN_DATA reg. 28e47c018aSSuma Hegde * Below are required SMN address for HSMP Mailbox register offsets in SMU address space 29e47c018aSSuma Hegde */ 30e47c018aSSuma Hegde #define SMN_HSMP_BASE 0x3B00000 31e47c018aSSuma Hegde #define SMN_HSMP_MSG_ID 0x0010534 32e47c018aSSuma Hegde #define SMN_HSMP_MSG_ID_F1A_M0H 0x0010934 33e47c018aSSuma Hegde #define SMN_HSMP_MSG_RESP 0x0010980 34e47c018aSSuma Hegde #define SMN_HSMP_MSG_DATA 0x00109E0 35e47c018aSSuma Hegde 36e47c018aSSuma Hegde #define HSMP_INDEX_REG 0xc4 37e47c018aSSuma Hegde #define HSMP_DATA_REG 0xc8 38e47c018aSSuma Hegde 39e47c018aSSuma Hegde static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset, 40e47c018aSSuma Hegde u32 *value, bool write) 41e47c018aSSuma Hegde { 42e47c018aSSuma Hegde int ret; 43e47c018aSSuma Hegde 44e47c018aSSuma Hegde if (!sock->root) 45e47c018aSSuma Hegde return -ENODEV; 46e47c018aSSuma Hegde 47e47c018aSSuma Hegde ret = pci_write_config_dword(sock->root, HSMP_INDEX_REG, 48e47c018aSSuma Hegde sock->mbinfo.base_addr + offset); 49e47c018aSSuma Hegde if (ret) 50e47c018aSSuma Hegde return ret; 51e47c018aSSuma Hegde 52e47c018aSSuma Hegde ret = (write ? pci_write_config_dword(sock->root, HSMP_DATA_REG, *value) 53e47c018aSSuma Hegde : pci_read_config_dword(sock->root, HSMP_DATA_REG, value)); 54e47c018aSSuma Hegde 55e47c018aSSuma Hegde return ret; 56e47c018aSSuma Hegde } 57e47c018aSSuma Hegde 587d3135d1SSuma Hegde static int hsmp_create_non_acpi_sysfs_if(struct device *dev) 59e47c018aSSuma Hegde { 60e47c018aSSuma Hegde const struct attribute_group **hsmp_attr_grps; 61e47c018aSSuma Hegde struct attribute_group *attr_grp; 62e47c018aSSuma Hegde u16 i; 63e47c018aSSuma Hegde 648e75dff5SSuma Hegde hsmp_attr_grps = devm_kcalloc(dev, hsmp_pdev.num_sockets + 1, 65e47c018aSSuma Hegde sizeof(*hsmp_attr_grps), 66e47c018aSSuma Hegde GFP_KERNEL); 67e47c018aSSuma Hegde if (!hsmp_attr_grps) 68e47c018aSSuma Hegde return -ENOMEM; 69e47c018aSSuma Hegde 70e47c018aSSuma Hegde /* Create a sysfs directory for each socket */ 718e75dff5SSuma Hegde for (i = 0; i < hsmp_pdev.num_sockets; i++) { 72e47c018aSSuma Hegde attr_grp = devm_kzalloc(dev, sizeof(struct attribute_group), 73e47c018aSSuma Hegde GFP_KERNEL); 74e47c018aSSuma Hegde if (!attr_grp) 75e47c018aSSuma Hegde return -ENOMEM; 76e47c018aSSuma Hegde 778e75dff5SSuma Hegde snprintf(hsmp_pdev.sock[i].name, HSMP_ATTR_GRP_NAME_SIZE, "socket%u", (u8)i); 788e75dff5SSuma Hegde attr_grp->name = hsmp_pdev.sock[i].name; 79e47c018aSSuma Hegde attr_grp->is_bin_visible = hsmp_is_sock_attr_visible; 80e47c018aSSuma Hegde hsmp_attr_grps[i] = attr_grp; 81e47c018aSSuma Hegde 82e47c018aSSuma Hegde hsmp_create_attr_list(attr_grp, dev, i); 83e47c018aSSuma Hegde } 84e47c018aSSuma Hegde 85e47c018aSSuma Hegde return device_add_groups(dev, hsmp_attr_grps); 86e47c018aSSuma Hegde } 87e47c018aSSuma Hegde 88e47c018aSSuma Hegde static inline bool is_f1a_m0h(void) 89e47c018aSSuma Hegde { 90e47c018aSSuma Hegde if (boot_cpu_data.x86 == 0x1A && boot_cpu_data.x86_model <= 0x0F) 91e47c018aSSuma Hegde return true; 92e47c018aSSuma Hegde 93e47c018aSSuma Hegde return false; 94e47c018aSSuma Hegde } 95e47c018aSSuma Hegde 967d3135d1SSuma Hegde static int init_platform_device(struct device *dev) 97e47c018aSSuma Hegde { 98e47c018aSSuma Hegde struct hsmp_socket *sock; 99e47c018aSSuma Hegde int ret, i; 100e47c018aSSuma Hegde 1018e75dff5SSuma Hegde for (i = 0; i < hsmp_pdev.num_sockets; i++) { 102e47c018aSSuma Hegde if (!node_to_amd_nb(i)) 103e47c018aSSuma Hegde return -ENODEV; 1048e75dff5SSuma Hegde sock = &hsmp_pdev.sock[i]; 105e47c018aSSuma Hegde sock->root = node_to_amd_nb(i)->root; 106e47c018aSSuma Hegde sock->sock_ind = i; 107e47c018aSSuma Hegde sock->dev = dev; 108e47c018aSSuma Hegde sock->mbinfo.base_addr = SMN_HSMP_BASE; 109e47c018aSSuma Hegde sock->amd_hsmp_rdwr = amd_hsmp_pci_rdwr; 110e47c018aSSuma Hegde 111e47c018aSSuma Hegde /* 112e47c018aSSuma Hegde * This is a transitional change from non-ACPI to ACPI, only 113e47c018aSSuma Hegde * family 0x1A, model 0x00 platform is supported for both ACPI and non-ACPI. 114e47c018aSSuma Hegde */ 115e47c018aSSuma Hegde if (is_f1a_m0h()) 116e47c018aSSuma Hegde sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID_F1A_M0H; 117e47c018aSSuma Hegde else 118e47c018aSSuma Hegde sock->mbinfo.msg_id_off = SMN_HSMP_MSG_ID; 119e47c018aSSuma Hegde 120e47c018aSSuma Hegde sock->mbinfo.msg_resp_off = SMN_HSMP_MSG_RESP; 121e47c018aSSuma Hegde sock->mbinfo.msg_arg_off = SMN_HSMP_MSG_DATA; 122e47c018aSSuma Hegde sema_init(&sock->hsmp_sem, 1); 123e47c018aSSuma Hegde 124e47c018aSSuma Hegde /* Test the hsmp interface on each socket */ 125e47c018aSSuma Hegde ret = hsmp_test(i, 0xDEADBEEF); 126e47c018aSSuma Hegde if (ret) { 127e47c018aSSuma Hegde dev_err(dev, "HSMP test message failed on Fam:%x model:%x\n", 128e47c018aSSuma Hegde boot_cpu_data.x86, boot_cpu_data.x86_model); 129e47c018aSSuma Hegde dev_err(dev, "Is HSMP disabled in BIOS ?\n"); 130e47c018aSSuma Hegde return ret; 131e47c018aSSuma Hegde } 132e47c018aSSuma Hegde 133e47c018aSSuma Hegde ret = hsmp_cache_proto_ver(i); 134e47c018aSSuma Hegde if (ret) { 135e47c018aSSuma Hegde dev_err(dev, "Failed to read HSMP protocol version\n"); 136e47c018aSSuma Hegde return ret; 137e47c018aSSuma Hegde } 138e47c018aSSuma Hegde } 139e47c018aSSuma Hegde 140e47c018aSSuma Hegde return 0; 141e47c018aSSuma Hegde } 1427d3135d1SSuma Hegde 1437d3135d1SSuma Hegde static int hsmp_pltdrv_probe(struct platform_device *pdev) 1447d3135d1SSuma Hegde { 1457d3135d1SSuma Hegde int ret; 1467d3135d1SSuma Hegde 1477d3135d1SSuma Hegde hsmp_pdev.sock = devm_kcalloc(&pdev->dev, hsmp_pdev.num_sockets, 1487d3135d1SSuma Hegde sizeof(*hsmp_pdev.sock), 1497d3135d1SSuma Hegde GFP_KERNEL); 1507d3135d1SSuma Hegde if (!hsmp_pdev.sock) 1517d3135d1SSuma Hegde return -ENOMEM; 1527d3135d1SSuma Hegde 1537d3135d1SSuma Hegde ret = init_platform_device(&pdev->dev); 1547d3135d1SSuma Hegde if (ret) { 1557d3135d1SSuma Hegde dev_err(&pdev->dev, "Failed to init HSMP mailbox\n"); 1567d3135d1SSuma Hegde return ret; 1577d3135d1SSuma Hegde } 1587d3135d1SSuma Hegde 1597d3135d1SSuma Hegde ret = hsmp_create_non_acpi_sysfs_if(&pdev->dev); 1607d3135d1SSuma Hegde if (ret) 1617d3135d1SSuma Hegde dev_err(&pdev->dev, "Failed to create HSMP sysfs interface\n"); 1627d3135d1SSuma Hegde 1637d3135d1SSuma Hegde return hsmp_misc_register(&pdev->dev); 1647d3135d1SSuma Hegde } 1657d3135d1SSuma Hegde 1667d3135d1SSuma Hegde static void hsmp_pltdrv_remove(struct platform_device *pdev) 1677d3135d1SSuma Hegde { 1687d3135d1SSuma Hegde hsmp_misc_deregister(); 1697d3135d1SSuma Hegde } 1707d3135d1SSuma Hegde 1717d3135d1SSuma Hegde static struct platform_driver amd_hsmp_driver = { 1727d3135d1SSuma Hegde .probe = hsmp_pltdrv_probe, 1737d3135d1SSuma Hegde .remove = hsmp_pltdrv_remove, 1747d3135d1SSuma Hegde .driver = { 1757d3135d1SSuma Hegde .name = DRIVER_NAME, 1767d3135d1SSuma Hegde }, 1777d3135d1SSuma Hegde }; 1787d3135d1SSuma Hegde 1797d3135d1SSuma Hegde static struct platform_device *amd_hsmp_platdev; 1807d3135d1SSuma Hegde 1817d3135d1SSuma Hegde static int hsmp_plat_dev_register(void) 1827d3135d1SSuma Hegde { 1837d3135d1SSuma Hegde int ret; 1847d3135d1SSuma Hegde 1857d3135d1SSuma Hegde amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE); 1867d3135d1SSuma Hegde if (!amd_hsmp_platdev) 1877d3135d1SSuma Hegde return -ENOMEM; 1887d3135d1SSuma Hegde 1897d3135d1SSuma Hegde ret = platform_device_add(amd_hsmp_platdev); 1907d3135d1SSuma Hegde if (ret) 1917d3135d1SSuma Hegde platform_device_put(amd_hsmp_platdev); 1927d3135d1SSuma Hegde 1937d3135d1SSuma Hegde return ret; 1947d3135d1SSuma Hegde } 1957d3135d1SSuma Hegde 1967d3135d1SSuma Hegde /* 1977d3135d1SSuma Hegde * This check is only needed for backward compatibility of previous platforms. 1987d3135d1SSuma Hegde * All new platforms are expected to support ACPI based probing. 1997d3135d1SSuma Hegde */ 2007d3135d1SSuma Hegde static bool legacy_hsmp_support(void) 2017d3135d1SSuma Hegde { 2027d3135d1SSuma Hegde if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) 2037d3135d1SSuma Hegde return false; 2047d3135d1SSuma Hegde 2057d3135d1SSuma Hegde switch (boot_cpu_data.x86) { 2067d3135d1SSuma Hegde case 0x19: 2077d3135d1SSuma Hegde switch (boot_cpu_data.x86_model) { 2087d3135d1SSuma Hegde case 0x00 ... 0x1F: 2097d3135d1SSuma Hegde case 0x30 ... 0x3F: 2107d3135d1SSuma Hegde case 0x90 ... 0x9F: 2117d3135d1SSuma Hegde case 0xA0 ... 0xAF: 2127d3135d1SSuma Hegde return true; 2137d3135d1SSuma Hegde default: 2147d3135d1SSuma Hegde return false; 2157d3135d1SSuma Hegde } 2167d3135d1SSuma Hegde case 0x1A: 2177d3135d1SSuma Hegde switch (boot_cpu_data.x86_model) { 2187d3135d1SSuma Hegde case 0x00 ... 0x1F: 2197d3135d1SSuma Hegde return true; 2207d3135d1SSuma Hegde default: 2217d3135d1SSuma Hegde return false; 2227d3135d1SSuma Hegde } 2237d3135d1SSuma Hegde default: 2247d3135d1SSuma Hegde return false; 2257d3135d1SSuma Hegde } 2267d3135d1SSuma Hegde 2277d3135d1SSuma Hegde return false; 2287d3135d1SSuma Hegde } 2297d3135d1SSuma Hegde 2307d3135d1SSuma Hegde static int __init hsmp_plt_init(void) 2317d3135d1SSuma Hegde { 2327d3135d1SSuma Hegde int ret = -ENODEV; 2337d3135d1SSuma Hegde 2347d3135d1SSuma Hegde if (!legacy_hsmp_support()) { 2357d3135d1SSuma Hegde pr_info("HSMP is not supported on Family:%x model:%x\n", 2367d3135d1SSuma Hegde boot_cpu_data.x86, boot_cpu_data.x86_model); 2377d3135d1SSuma Hegde return ret; 2387d3135d1SSuma Hegde } 2397d3135d1SSuma Hegde 2407d3135d1SSuma Hegde /* 2417d3135d1SSuma Hegde * amd_nb_num() returns number of SMN/DF interfaces present in the system 2427d3135d1SSuma Hegde * if we have N SMN/DF interfaces that ideally means N sockets 2437d3135d1SSuma Hegde */ 2447d3135d1SSuma Hegde hsmp_pdev.num_sockets = amd_nb_num(); 2457d3135d1SSuma Hegde if (hsmp_pdev.num_sockets == 0 || hsmp_pdev.num_sockets > MAX_AMD_SOCKETS) 2467d3135d1SSuma Hegde return ret; 2477d3135d1SSuma Hegde 2487d3135d1SSuma Hegde ret = platform_driver_register(&amd_hsmp_driver); 2497d3135d1SSuma Hegde if (ret) 2507d3135d1SSuma Hegde return ret; 2517d3135d1SSuma Hegde 2527d3135d1SSuma Hegde ret = hsmp_plat_dev_register(); 2537d3135d1SSuma Hegde if (ret) 2547d3135d1SSuma Hegde platform_driver_unregister(&amd_hsmp_driver); 2557d3135d1SSuma Hegde 2567d3135d1SSuma Hegde return ret; 2577d3135d1SSuma Hegde } 2587d3135d1SSuma Hegde 2597d3135d1SSuma Hegde static void __exit hsmp_plt_exit(void) 2607d3135d1SSuma Hegde { 2617d3135d1SSuma Hegde platform_device_unregister(amd_hsmp_platdev); 2627d3135d1SSuma Hegde platform_driver_unregister(&amd_hsmp_driver); 2637d3135d1SSuma Hegde } 2647d3135d1SSuma Hegde 2657d3135d1SSuma Hegde device_initcall(hsmp_plt_init); 2667d3135d1SSuma Hegde module_exit(hsmp_plt_exit); 2677d3135d1SSuma Hegde 268*4fc0366eSSuma Hegde MODULE_IMPORT_NS(AMD_HSMP); 2697d3135d1SSuma Hegde MODULE_DESCRIPTION("AMD HSMP Platform Interface Driver"); 2707d3135d1SSuma Hegde MODULE_VERSION(DRIVER_VERSION); 2717d3135d1SSuma Hegde MODULE_LICENSE("GPL"); 272