+ select AMD_HSMP
+ help
Host System Management Port (HSMP) interface is a mailbox interface
between the x86 core and the System Management Unit (SMU) firmware.
+ The driver provides a way for user space tools to monitor and manage
+ system management functionality on EPYC and MI300A server CPUs
+ from AMD.
+
+ This option supports platform device based probing.
+ You may enable this, if your platform BIOS does not provide
+ HSMP ACPI object.
If you choose to compile this driver as a module the module will be
called amd_hsmp.
+
+endmenu
diff --git a/drivers/platform/x86/amd/hsmp/Makefile b/drivers/platform/x86/amd/hsmp/Makefile
index 0cc92865c0a2..18d9a0d1e8c5 100644
--- a/drivers/platform/x86/amd/hsmp/Makefile
+++ b/drivers/platform/x86/amd/hsmp/Makefile
@@ -4,5 +4,7 @@
# AMD HSMP Driver
#
-obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o
-amd_hsmp-objs := hsmp.o plat.o acpi.o
+obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o
+amd_hsmp-objs := hsmp.o
+amd_hsmp-$(CONFIG_AMD_HSMP_PLAT) += plat.o
+amd_hsmp-$(CONFIG_AMD_HSMP_ACPI) += acpi.o
diff --git a/drivers/platform/x86/amd/hsmp/acpi.c b/drivers/platform/x86/amd/hsmp/acpi.c
index 6f8e7962266a..766617e6adc7 100644
--- a/drivers/platform/x86/amd/hsmp/acpi.c
+++ b/drivers/platform/x86/amd/hsmp/acpi.c
@@ -9,11 +9,15 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+#include <asm/amd_nb.h>
+
#include <linux/acpi.h>
#include <linux/device.h>
#include <linux/dev_printk.h>
#include <linux/ioport.h>
#include <linux/kstrtox.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
#include <linux/sysfs.h>
#include <linux/uuid.h>
@@ -21,6 +25,10 @@
#include "hsmp.h"
+#define DRIVER_NAME "amd_hsmp"
+#define DRIVER_VERSION "2.3"
+#define ACPI_HSMP_DEVICE_HID "AMDI0097"
+
/* These are the strings specified in ACPI table */
#define MSG_IDOFF_STR "MsgIdOffset"
#define MSG_ARGOFF_STR "MsgArgOffset"
@@ -200,7 +208,6 @@ static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind)
sock->sock_ind = sock_ind;
sock->dev = dev;
sock->amd_hsmp_rdwr = amd_hsmp_acpi_rdwr;
- hsmp_pdev.is_acpi_device = true;
sema_init(&sock->hsmp_sem, 1);
@@ -213,7 +220,7 @@ static int hsmp_parse_acpi_table(struct device *dev, u16 sock_ind)
return hsmp_read_acpi_dsd(sock);
}
-int hsmp_create_acpi_sysfs_if(struct device *dev)
+static int hsmp_create_acpi_sysfs_if(struct device *dev)
{
struct attribute_group *attr_grp;
u16 sock_ind;
@@ -236,7 +243,7 @@ int hsmp_create_acpi_sysfs_if(struct device *dev)
return devm_device_add_group(dev, attr_grp);
}
-int init_acpi(struct device *dev)
+static int init_acpi(struct device *dev)
{
u16 sock_ind;
int ret;
@@ -270,3 +277,72 @@ int init_acpi(struct device *dev)
return ret;
}
+
+static const struct acpi_device_id amd_hsmp_acpi_ids[] = {
+ {ACPI_HSMP_DEVICE_HID, 0},
+ {}
+};
+MODULE_DEVICE_TABLE(acpi, amd_hsmp_acpi_ids);
+
+static int hsmp_acpi_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ if (!hsmp_pdev.is_probed) {
+ hsmp_pdev.num_sockets = amd_nb_num();
+ if (hsmp_pdev.num_sockets == 0 || hsmp_pdev.num_sockets > MAX_AMD_SOCKETS)
+ return -ENODEV;
+
+ hsmp_pdev.sock = devm_kcalloc(&pdev->dev, hsmp_pdev.num_sockets,
+ sizeof(*hsmp_pdev.sock),
+ GFP_KERNEL);
+ if (!hsmp_pdev.sock)
+ return -ENOMEM;
+ }
+
+ ret = init_acpi(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to initialize HSMP interface.\n");
+ return ret;
+ }
+
+ ret = hsmp_create_acpi_sysfs_if(&pdev->dev);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to create HSMP sysfs interface\n");
+
+ if (!hsmp_pdev.is_probed) {
+ ret = hsmp_misc_register(&pdev->dev);
+ if (ret)
+ return ret;
+ hsmp_pdev.is_probed = true;
+ }
+
+ return 0;
+}
+
+static void hsmp_acpi_remove(struct platform_device *pdev)
+{
+ /*
+ * We register only one misc_device even on multi-socket system.
+ * So, deregister should happen only once.
+ */
+ if (hsmp_pdev.is_probed) {
+ hsmp_misc_deregister();
+ hsmp_pdev.is_probed = false;
+ }
+}
+
+static struct platform_driver amd_hsmp_driver = {
+ .probe = hsmp_acpi_probe,
+ .remove_new = hsmp_acpi_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ .acpi_match_table = amd_hsmp_acpi_ids,
+ },
+};
+
+module_platform_driver(amd_hsmp_driver);
+
+MODULE_DESCRIPTION("AMD HSMP Platform Interface Driver");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c
index 78945750d590..5e0c9c36f435 100644
--- a/drivers/platform/x86/amd/hsmp/hsmp.c
+++ b/drivers/platform/x86/amd/hsmp/hsmp.c
@@ -15,17 +15,11 @@
#include <linux/acpi.h>
#include <linux/delay.h>
#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
#include <linux/semaphore.h>
#include <linux/sysfs.h>
#include "hsmp.h"
-#define DRIVER_NAME "amd_hsmp"
-#define DRIVER_VERSION "2.2"
-#define ACPI_HSMP_DEVICE_HID "AMDI0097"
-
/* HSMP Status / Error codes */
#define HSMP_STATUS_NOT_READY 0x00
#define HSMP_STATUS_OK 0x01
@@ -228,7 +222,7 @@ int hsmp_test(u16 sock_ind, u32 value)
return ret;
}
-static long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
+long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
int __user *arguser = (int __user *)arg;
struct hsmp_message msg = { 0 };
@@ -284,12 +278,6 @@ static long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg)
return 0;
}
-static const struct file_operations hsmp_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = hsmp_ioctl,
- .compat_ioctl = hsmp_ioctl,
-};
-
ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count)
@@ -414,193 +402,25 @@ int hsmp_cache_proto_ver(u16 sock_ind)
return ret;
}
-static const struct acpi_device_id amd_hsmp_acpi_ids[] = {
- {ACPI_HSMP_DEVICE_HID, 0},
- {}
-};
-MODULE_DEVICE_TABLE(acpi, amd_hsmp_acpi_ids);
-
-static bool check_acpi_support(struct device *dev)
-{
- struct acpi_device *adev = ACPI_COMPANION(dev);
-
- if (adev && !acpi_match_device_ids(adev, amd_hsmp_acpi_ids))
- return true;
-
- return false;
-}
-
-static int hsmp_pltdrv_probe(struct platform_device *pdev)
-{
- int ret;
-
- /*
- * On ACPI supported BIOS, there is an ACPI HSMP device added for
- * each socket, so the per socket probing, but the memory allocated for
- * sockets should be contiguous to access it as an array,
- * Hence allocate memory for all the sockets at once instead of allocating
- * on each probe.
- */
- if (!hsmp_pdev.is_probed) {
- hsmp_pdev.sock = devm_kcalloc(&pdev->dev, hsmp_pdev.num_sockets,
- sizeof(*hsmp_pdev.sock),
- GFP_KERNEL);
- if (!hsmp_pdev.sock)
- return -ENOMEM;
- }
- if (check_acpi_support(&pdev->dev)) {
- ret = init_acpi(&pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to init HSMP mailbox\n");
- return ret;
- }
- ret = hsmp_create_acpi_sysfs_if(&pdev->dev);
- if (ret)
- dev_err(&pdev->dev, "Failed to create HSMP sysfs interface\n");
- } else {
- ret = init_platform_device(&pdev->dev);
- if (ret) {
- dev_err(&pdev->dev, "Failed to init HSMP mailbox\n");
- return ret;
- }
- ret = hsmp_create_non_acpi_sysfs_if(&pdev->dev);
- if (ret)
- dev_err(&pdev->dev, "Failed to create HSMP sysfs interface\n");
- }
-
- if (!hsmp_pdev.is_probed) {
- hsmp_pdev.mdev.name = HSMP_CDEV_NAME;
- hsmp_pdev.mdev.minor = MISC_DYNAMIC_MINOR;
- hsmp_pdev.mdev.fops = &hsmp_fops;
- hsmp_pdev.mdev.parent = &pdev->dev;
- hsmp_pdev.mdev.nodename = HSMP_DEVNODE_NAME;
- hsmp_pdev.mdev.mode = 0644;
-
- ret = misc_register(&hsmp_pdev.mdev);
- if (ret)
- return ret;
-
- hsmp_pdev.is_probed = true;
- }
-
- return 0;
-
-}
-
-static void hsmp_pltdrv_remove(struct platform_device *pdev)
-{
- /*
- * We register only one misc_device even on multi socket system.
- * So, deregister should happen only once.
- */
- if (hsmp_pdev.is_probed) {
- misc_deregister(&hsmp_pdev.mdev);
- hsmp_pdev.is_probed = false;
- }
-}
-
-static struct platform_driver amd_hsmp_driver = {
- .probe = hsmp_pltdrv_probe,
- .remove_new = hsmp_pltdrv_remove,
- .driver = {
- .name = DRIVER_NAME,
- .acpi_match_table = amd_hsmp_acpi_ids,
- },
+static const struct file_operations hsmp_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = hsmp_ioctl,
+ .compat_ioctl = hsmp_ioctl,
};
-static struct platform_device *amd_hsmp_platdev;
-
-static int hsmp_plat_dev_register(void)
+int hsmp_misc_register(struct device *dev)
{
- int ret;
-
- amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE);
- if (!amd_hsmp_platdev)
- return -ENOMEM;
-
- ret = platform_device_add(amd_hsmp_platdev);
- if (ret)
- platform_device_put(amd_hsmp_platdev);
-
- return ret;
+ hsmp_pdev.mdev.name = HSMP_CDEV_NAME;
+ hsmp_pdev.mdev.minor = MISC_DYNAMIC_MINOR;
+ hsmp_pdev.mdev.fops = &hsmp_fops;
+ hsmp_pdev.mdev.parent = dev;
+ hsmp_pdev.mdev.nodename = HSMP_DEVNODE_NAME;
+ hsmp_pdev.mdev.mode = 0644;
+
+ return misc_register(&hsmp_pdev.mdev);
}
-/*
- * This check is only needed for backward compatibility of previous platforms.
- * All new platforms are expected to support ACPI based probing.
- */
-static bool legacy_hsmp_support(void)
+void hsmp_misc_deregister(void)
{
- if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
- return false;
-
- switch (boot_cpu_data.x86) {
- case 0x19:
- switch (boot_cpu_data.x86_model) {
- case 0x00 ... 0x1F:
- case 0x30 ... 0x3F:
- case 0x90 ... 0x9F:
- case 0xA0 ... 0xAF:
- return true;
- default:
- return false;
- }
- case 0x1A:
- switch (boot_cpu_data.x86_model) {
- case 0x00 ... 0x1F:
- return true;
- default:
- return false;
- }
- default:
- return false;
- }
-
- return false;
+ misc_deregister(&hsmp_pdev.mdev);
}
-
-static int __init hsmp_plt_init(void)
-{
- int ret = -ENODEV;
-
- /*
- * amd_nb_num() returns number of SMN/DF interfaces present in the system
- * if we have N SMN/DF interfaces that ideally means N sockets
- */
- hsmp_pdev.num_sockets = amd_nb_num();
- if (hsmp_pdev.num_sockets == 0 || hsmp_pdev.num_sockets > MAX_AMD_SOCKETS)
- return ret;
-
- ret = platform_driver_register(&amd_hsmp_driver);
- if (ret)
- return ret;
-
- if (!hsmp_pdev.is_acpi_device) {
- if (legacy_hsmp_support()) {
- /* Not ACPI device, but supports HSMP, register a plat_dev */
- ret = hsmp_plat_dev_register();
- } else {
- /* Not ACPI, Does not support HSMP */
- pr_info("HSMP is not supported on Family:%x model:%x\n",
- boot_cpu_data.x86, boot_cpu_data.x86_model);
- ret = -ENODEV;
- }
- if (ret)
- platform_driver_unregister(&amd_hsmp_driver);
- }
-
- return ret;
-}
-
-static void __exit hsmp_plt_exit(void)
-{
- platform_device_unregister(amd_hsmp_platdev);
- platform_driver_unregister(&amd_hsmp_driver);
-}
-
-device_initcall(hsmp_plt_init);
-module_exit(hsmp_plt_exit);
-
-MODULE_DESCRIPTION("AMD HSMP Platform Interface Driver");
-MODULE_VERSION(DRIVER_VERSION);
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/platform/x86/amd/hsmp/hsmp.h b/drivers/platform/x86/amd/hsmp/hsmp.h
index 9c5b9c263fc1..9ab50bc74676 100644
--- a/drivers/platform/x86/amd/hsmp/hsmp.h
+++ b/drivers/platform/x86/amd/hsmp/hsmp.h
@@ -52,7 +52,6 @@ struct hsmp_plat_device {
struct hsmp_socket *sock;
u32 proto_ver;
u16 num_sockets;
- bool is_acpi_device;
bool is_probed;
};
@@ -61,14 +60,13 @@ extern struct hsmp_plat_device hsmp_pdev;
ssize_t hsmp_metric_tbl_read(struct file *filp, struct kobject *kobj,
struct bin_attribute *bin_attr, char *buf,
loff_t off, size_t count);
-int hsmp_create_non_acpi_sysfs_if(struct device *dev);
-int hsmp_create_acpi_sysfs_if(struct device *dev);
int hsmp_cache_proto_ver(u16 sock_ind);
umode_t hsmp_is_sock_attr_visible(struct kobject *kobj,
struct bin_attribute *battr, int id);
int hsmp_create_attr_list(struct attribute_group *attr_grp,
struct device *dev, u16 sock_ind);
int hsmp_test(u16 sock_ind, u32 value);
-int init_platform_device(struct device *dev);
-int init_acpi(struct device *dev);
+long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg);
+void hsmp_misc_deregister(void);
+int hsmp_misc_register(struct device *dev);
#endif /* HSMP_H */
diff --git a/drivers/platform/x86/amd/hsmp/plat.c b/drivers/platform/x86/amd/hsmp/plat.c
index e18cf82478a0..e49b53f8c5e3 100644
--- a/drivers/platform/x86/amd/hsmp/plat.c
+++ b/drivers/platform/x86/amd/hsmp/plat.c
@@ -12,11 +12,16 @@
#include <asm/amd_nb.h>
#include <linux/device.h>
+#include <linux/module.h>
#include <linux/pci.h>
+#include <linux/platform_device.h>
#include <linux/sysfs.h>
#include "hsmp.h"
+#define DRIVER_NAME "amd_hsmp"
+#define DRIVER_VERSION "2.3"
+
/*
* To access specific HSMP mailbox register, s/w writes the SMN address of HSMP mailbox
* register into the SMN_INDEX register, and reads/writes the SMN_DATA reg.
@@ -50,7 +55,13 @@ static int amd_hsmp_pci_rdwr(struct hsmp_socket *sock, u32 offset,
return ret;
}
-int hsmp_create_non_acpi_sysfs_if(struct device *dev)
+static const struct file_operations hsmp_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = hsmp_ioctl,
+ .compat_ioctl = hsmp_ioctl,
+};
+
+static int hsmp_create_non_acpi_sysfs_if(struct device *dev)
{
const struct attribute_group **hsmp_attr_grps;
struct attribute_group *attr_grp;
@@ -88,7 +99,7 @@ static inline bool is_f1a_m0h(void)
return false;
}
-int init_platform_device(struct device *dev)
+static int init_platform_device(struct device *dev)
{
struct hsmp_socket *sock;
int ret, i;
@@ -134,3 +145,132 @@ int init_platform_device(struct device *dev)
return 0;
}
+
+static int hsmp_pltdrv_probe(struct platform_device *pdev)
+{
+ int ret;
+
+ hsmp_pdev.sock = devm_kcalloc(&pdev->dev, hsmp_pdev.num_sockets,
+ sizeof(*hsmp_pdev.sock),
+ GFP_KERNEL);
+ if (!hsmp_pdev.sock)
+ return -ENOMEM;
+
+ ret = init_platform_device(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to init HSMP mailbox\n");
+ return ret;
+ }
+
+ ret = hsmp_create_non_acpi_sysfs_if(&pdev->dev);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to create HSMP sysfs interface\n");
+
+ return hsmp_misc_register(&pdev->dev);
+}
+
+static void hsmp_pltdrv_remove(struct platform_device *pdev)
+{
+ hsmp_misc_deregister();
+}
+
+static struct platform_driver amd_hsmp_driver = {
+ .probe = hsmp_pltdrv_probe,
+ .remove_new = hsmp_pltdrv_remove,
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+};
+
+static struct platform_device *amd_hsmp_platdev;
+
+static int hsmp_plat_dev_register(void)
+{
+ int ret;
+
+ amd_hsmp_platdev = platform_device_alloc(DRIVER_NAME, PLATFORM_DEVID_NONE);
+ if (!amd_hsmp_platdev)
+ return -ENOMEM;
+
+ ret = platform_device_add(amd_hsmp_platdev);
+ if (ret)
+ platform_device_put(amd_hsmp_platdev);
+
+ return ret;
+}
+
+/*
+ * This check is only needed for backward compatibility of previous platforms.
+ * All new platforms are expected to support ACPI based probing.
+ */
+static bool legacy_hsmp_support(void)
+{
+ if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
+ return false;
+
+ switch (boot_cpu_data.x86) {
+ case 0x19:
+ switch (boot_cpu_data.x86_model) {
+ case 0x00 ... 0x1F:
+ case 0x30 ... 0x3F:
+ case 0x90 ... 0x9F:
+ case 0xA0 ... 0xAF:
+ return true;
+ default:
+ return false;
+ }
+ case 0x1A:
+ switch (boot_cpu_data.x86_model) {
+ case 0x00 ... 0x1F:
+ return true;
+ default:
+ return false;
+ }
+ default:
+ return false;
+ }
+
+ return false;
+}
+
+static int __init hsmp_plt_init(void)
+{
+ int ret = -ENODEV;
+
+ if (!legacy_hsmp_support()) {
+ pr_info("HSMP is not supported on Family:%x model:%x\n",
+ boot_cpu_data.x86, boot_cpu_data.x86_model);
+ return ret;
+ }
+
+ /*
+ * amd_nb_num() returns number of SMN/DF interfaces present in the system
+ * if we have N SMN/DF interfaces that ideally means N sockets
+ */
+ hsmp_pdev.num_sockets = amd_nb_num();
+ if (hsmp_pdev.num_sockets == 0 || hsmp_pdev.num_sockets > MAX_AMD_SOCKETS)
+ return ret;
+
+ ret = platform_driver_register(&amd_hsmp_driver);
+ if (ret)
+ return ret;
+
+ ret = hsmp_plat_dev_register();
+ if (ret)
+ platform_driver_unregister(&amd_hsmp_driver);
+
+ return ret;
+}
+
+static void __exit hsmp_plt_exit(void)
+{
+ platform_device_unregister(amd_hsmp_platdev);
+ platform_driver_unregister(&amd_hsmp_driver);
+}
+
+device_initcall(hsmp_plt_init);
+module_exit(hsmp_plt_exit);
+
+MODULE_DESCRIPTION("AMD HSMP Platform Interface Driver");
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");