diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 17:35:05 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-18 17:39:31 +0000 |
commit | 85c675d0d09a45a135bddd15d7b385f8758c32fb (patch) | |
tree | 76267dbc9b9a130337be3640948fe397b04ac629 /drivers/platform/x86/amd/hsmp.c | |
parent | Adding upstream version 6.6.15. (diff) | |
download | linux-85c675d0d09a45a135bddd15d7b385f8758c32fb.tar.xz linux-85c675d0d09a45a135bddd15d7b385f8758c32fb.zip |
Adding upstream version 6.7.7.upstream/6.7.7
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/platform/x86/amd/hsmp.c')
-rw-r--r-- | drivers/platform/x86/amd/hsmp.c | 241 |
1 files changed, 213 insertions, 28 deletions
diff --git a/drivers/platform/x86/amd/hsmp.c b/drivers/platform/x86/amd/hsmp.c index 31382ef52e..b55d80e291 100644 --- a/drivers/platform/x86/amd/hsmp.c +++ b/drivers/platform/x86/amd/hsmp.c @@ -20,7 +20,7 @@ #include <linux/semaphore.h> #define DRIVER_NAME "amd_hsmp" -#define DRIVER_VERSION "1.0" +#define DRIVER_VERSION "2.0" /* HSMP Status / Error codes */ #define HSMP_STATUS_NOT_READY 0x00 @@ -47,9 +47,29 @@ #define HSMP_INDEX_REG 0xc4 #define HSMP_DATA_REG 0xc8 -static struct semaphore *hsmp_sem; +#define HSMP_CDEV_NAME "hsmp_cdev" +#define HSMP_DEVNODE_NAME "hsmp" +#define HSMP_METRICS_TABLE_NAME "metrics_bin" -static struct miscdevice hsmp_device; +#define HSMP_ATTR_GRP_NAME_SIZE 10 + +struct hsmp_socket { + struct bin_attribute hsmp_attr; + void __iomem *metric_tbl_addr; + struct semaphore hsmp_sem; + char name[HSMP_ATTR_GRP_NAME_SIZE]; + u16 sock_ind; +}; + +struct hsmp_plat_device { + struct miscdevice hsmp_device; + struct hsmp_socket *sock; + struct device *dev; + u32 proto_ver; + u16 num_sockets; +}; + +static struct hsmp_plat_device plat_dev; static int amd_hsmp_rdwr(struct pci_dev *root, u32 address, u32 *value, bool write) @@ -188,6 +208,7 @@ static int validate_message(struct hsmp_message *msg) int hsmp_send_message(struct hsmp_message *msg) { + struct hsmp_socket *sock = &plat_dev.sock[msg->sock_ind]; struct amd_northbridge *nb; int ret; @@ -208,14 +229,13 @@ int hsmp_send_message(struct hsmp_message *msg) * In SMP system timeout of 100 millisecs should * be enough for the previous thread to finish the operation */ - ret = down_timeout(&hsmp_sem[msg->sock_ind], - msecs_to_jiffies(HSMP_MSG_TIMEOUT)); + ret = down_timeout(&sock->hsmp_sem, msecs_to_jiffies(HSMP_MSG_TIMEOUT)); if (ret < 0) return ret; ret = __hsmp_send_message(nb->root, msg); - up(&hsmp_sem[msg->sock_ind]); + up(&sock->hsmp_sem); return ret; } @@ -317,32 +337,198 @@ static const struct file_operations hsmp_fops = { .compat_ioctl = hsmp_ioctl, }; +static 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) +{ + struct hsmp_socket *sock = bin_attr->private; + struct hsmp_message msg = { 0 }; + int ret; + + /* Do not support lseek(), reads entire metric table */ + if (count < bin_attr->size) { + dev_err(plat_dev.dev, "Wrong buffer size\n"); + return -EINVAL; + } + + if (!sock) { + dev_err(plat_dev.dev, "Failed to read attribute private data\n"); + return -EINVAL; + } + + msg.msg_id = HSMP_GET_METRIC_TABLE; + msg.sock_ind = sock->sock_ind; + + ret = hsmp_send_message(&msg); + if (ret) + return ret; + memcpy_fromio(buf, sock->metric_tbl_addr, bin_attr->size); + + return bin_attr->size; +} + +static int hsmp_get_tbl_dram_base(u16 sock_ind) +{ + struct hsmp_socket *sock = &plat_dev.sock[sock_ind]; + struct hsmp_message msg = { 0 }; + phys_addr_t dram_addr; + int ret; + + msg.sock_ind = sock_ind; + msg.response_sz = hsmp_msg_desc_table[HSMP_GET_METRIC_TABLE_DRAM_ADDR].response_sz; + msg.msg_id = HSMP_GET_METRIC_TABLE_DRAM_ADDR; + + ret = hsmp_send_message(&msg); + if (ret) + return ret; + + /* + * calculate the metric table DRAM address from lower and upper 32 bits + * sent from SMU and ioremap it to virtual address. + */ + dram_addr = msg.args[0] | ((u64)(msg.args[1]) << 32); + if (!dram_addr) { + dev_err(plat_dev.dev, "Invalid DRAM address for metric table\n"); + return -ENOMEM; + } + sock->metric_tbl_addr = devm_ioremap(plat_dev.dev, dram_addr, + sizeof(struct hsmp_metric_table)); + if (!sock->metric_tbl_addr) { + dev_err(plat_dev.dev, "Failed to ioremap metric table addr\n"); + return -ENOMEM; + } + return 0; +} + +static umode_t hsmp_is_sock_attr_visible(struct kobject *kobj, + struct bin_attribute *battr, int id) +{ + if (plat_dev.proto_ver == HSMP_PROTO_VER6) + return battr->attr.mode; + else + return 0; +} + +static int hsmp_init_metric_tbl_bin_attr(struct bin_attribute **hattrs, u16 sock_ind) +{ + struct bin_attribute *hattr = &plat_dev.sock[sock_ind].hsmp_attr; + + sysfs_bin_attr_init(hattr); + hattr->attr.name = HSMP_METRICS_TABLE_NAME; + hattr->attr.mode = 0444; + hattr->read = hsmp_metric_tbl_read; + hattr->size = sizeof(struct hsmp_metric_table); + hattr->private = &plat_dev.sock[sock_ind]; + hattrs[0] = hattr; + + if (plat_dev.proto_ver == HSMP_PROTO_VER6) + return (hsmp_get_tbl_dram_base(sock_ind)); + else + return 0; +} + +/* One bin sysfs for metrics table*/ +#define NUM_HSMP_ATTRS 1 + +static int hsmp_create_sysfs_interface(void) +{ + const struct attribute_group **hsmp_attr_grps; + struct bin_attribute **hsmp_bin_attrs; + struct attribute_group *attr_grp; + int ret; + u16 i; + + /* String formatting is currently limited to u8 sockets */ + if (WARN_ON(plat_dev.num_sockets > U8_MAX)) + return -ERANGE; + + hsmp_attr_grps = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group *) * + (plat_dev.num_sockets + 1), GFP_KERNEL); + if (!hsmp_attr_grps) + return -ENOMEM; + + /* Create a sysfs directory for each socket */ + for (i = 0; i < plat_dev.num_sockets; i++) { + attr_grp = devm_kzalloc(plat_dev.dev, sizeof(struct attribute_group), GFP_KERNEL); + if (!attr_grp) + return -ENOMEM; + + snprintf(plat_dev.sock[i].name, HSMP_ATTR_GRP_NAME_SIZE, "socket%u", (u8)i); + attr_grp->name = plat_dev.sock[i].name; + + /* Null terminated list of attributes */ + hsmp_bin_attrs = devm_kzalloc(plat_dev.dev, sizeof(struct bin_attribute *) * + (NUM_HSMP_ATTRS + 1), GFP_KERNEL); + if (!hsmp_bin_attrs) + return -ENOMEM; + + attr_grp->bin_attrs = hsmp_bin_attrs; + attr_grp->is_bin_visible = hsmp_is_sock_attr_visible; + hsmp_attr_grps[i] = attr_grp; + + /* Now create the leaf nodes */ + ret = hsmp_init_metric_tbl_bin_attr(hsmp_bin_attrs, i); + if (ret) + return ret; + } + return devm_device_add_groups(plat_dev.dev, hsmp_attr_grps); +} + +static int hsmp_cache_proto_ver(void) +{ + struct hsmp_message msg = { 0 }; + int ret; + + msg.msg_id = HSMP_GET_PROTO_VER; + msg.sock_ind = 0; + msg.response_sz = hsmp_msg_desc_table[HSMP_GET_PROTO_VER].response_sz; + + ret = hsmp_send_message(&msg); + if (!ret) + plat_dev.proto_ver = msg.args[0]; + + return ret; +} + static int hsmp_pltdrv_probe(struct platform_device *pdev) { - int i; + int ret, i; - hsmp_sem = devm_kzalloc(&pdev->dev, - (amd_nb_num() * sizeof(struct semaphore)), - GFP_KERNEL); - if (!hsmp_sem) + plat_dev.sock = devm_kzalloc(&pdev->dev, + (plat_dev.num_sockets * sizeof(struct hsmp_socket)), + GFP_KERNEL); + if (!plat_dev.sock) return -ENOMEM; + plat_dev.dev = &pdev->dev; + + for (i = 0; i < plat_dev.num_sockets; i++) { + sema_init(&plat_dev.sock[i].hsmp_sem, 1); + plat_dev.sock[i].sock_ind = i; + } - for (i = 0; i < amd_nb_num(); i++) - sema_init(&hsmp_sem[i], 1); + plat_dev.hsmp_device.name = HSMP_CDEV_NAME; + plat_dev.hsmp_device.minor = MISC_DYNAMIC_MINOR; + plat_dev.hsmp_device.fops = &hsmp_fops; + plat_dev.hsmp_device.parent = &pdev->dev; + plat_dev.hsmp_device.nodename = HSMP_DEVNODE_NAME; + plat_dev.hsmp_device.mode = 0644; + + ret = hsmp_cache_proto_ver(); + if (ret) { + dev_err(plat_dev.dev, "Failed to read HSMP protocol version\n"); + return ret; + } - hsmp_device.name = "hsmp_cdev"; - hsmp_device.minor = MISC_DYNAMIC_MINOR; - hsmp_device.fops = &hsmp_fops; - hsmp_device.parent = &pdev->dev; - hsmp_device.nodename = "hsmp"; - hsmp_device.mode = 0644; + ret = hsmp_create_sysfs_interface(); + if (ret) + dev_err(plat_dev.dev, "Failed to create HSMP sysfs interface\n"); - return misc_register(&hsmp_device); + return misc_register(&plat_dev.hsmp_device); } static void hsmp_pltdrv_remove(struct platform_device *pdev) { - misc_deregister(&hsmp_device); + misc_deregister(&plat_dev.hsmp_device); } static struct platform_driver amd_hsmp_driver = { @@ -358,7 +544,6 @@ static struct platform_device *amd_hsmp_platdev; static int __init hsmp_plt_init(void) { int ret = -ENODEV; - u16 num_sockets; int i; if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD || boot_cpu_data.x86 < 0x19) { @@ -371,18 +556,18 @@ static int __init hsmp_plt_init(void) * 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 */ - num_sockets = amd_nb_num(); - if (num_sockets == 0) + plat_dev.num_sockets = amd_nb_num(); + if (plat_dev.num_sockets == 0) return ret; /* Test the hsmp interface on each socket */ - for (i = 0; i < num_sockets; i++) { + for (i = 0; i < plat_dev.num_sockets; i++) { ret = hsmp_test(i, 0xDEADBEEF); if (ret) { - pr_err("HSMP is not supported on Fam:%x model:%x\n", + pr_err("HSMP test message failed on Fam:%x model:%x\n", boot_cpu_data.x86, boot_cpu_data.x86_model); - pr_err("Or Is HSMP disabled in BIOS ?\n"); - return -EOPNOTSUPP; + pr_err("Is HSMP disabled in BIOS ?\n"); + return ret; } } |