diff options
Diffstat (limited to 'drivers/platform/x86/amd/hsmp/hsmp.c')
-rw-r--r-- | drivers/platform/x86/amd/hsmp/hsmp.c | 90 |
1 files changed, 74 insertions, 16 deletions
diff --git a/drivers/platform/x86/amd/hsmp/hsmp.c b/drivers/platform/x86/amd/hsmp/hsmp.c index 227b4ad4a51a..19f82c1d3090 100644 --- a/drivers/platform/x86/amd/hsmp/hsmp.c +++ b/drivers/platform/x86/amd/hsmp/hsmp.c @@ -7,8 +7,7 @@ * This file provides a device implementation for HSMP interface */ -#include <asm/amd_hsmp.h> -#include <asm/amd_nb.h> +#include <asm/amd/hsmp.h> #include <linux/acpi.h> #include <linux/delay.h> @@ -33,7 +32,11 @@ #define HSMP_WR true #define HSMP_RD false -#define DRIVER_VERSION "2.3" +/* + * When same message numbers are used for both GET and SET operation, + * bit:31 indicates whether its SET or GET operation. + */ +#define CHECK_GET_BIT BIT(31) static struct hsmp_plat_device hsmp_pdev; @@ -94,7 +97,7 @@ static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *ms short_sleep = jiffies + msecs_to_jiffies(HSMP_SHORT_SLEEP); timeout = jiffies + msecs_to_jiffies(HSMP_MSG_TIMEOUT); - while (time_before(jiffies, timeout)) { + while (true) { ret = sock->amd_hsmp_rdwr(sock, mbinfo->msg_resp_off, &mbox_status, HSMP_RD); if (ret) { dev_err(sock->dev, "Error %d reading mailbox status\n", ret); @@ -103,6 +106,10 @@ static int __hsmp_send_message(struct hsmp_socket *sock, struct hsmp_message *ms if (mbox_status != HSMP_STATUS_NOT_READY) break; + + if (!time_before(jiffies, timeout)) + break; + if (time_before(jiffies, short_sleep)) usleep_range(50, 100); else @@ -167,11 +174,28 @@ static int validate_message(struct hsmp_message *msg) if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_RSVD) return -ENOMSG; - /* num_args and response_sz against the HSMP spec */ - if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args || - msg->response_sz != hsmp_msg_desc_table[msg->msg_id].response_sz) + /* + * num_args passed by user should match the num_args specified in + * message description table. + */ + if (msg->num_args != hsmp_msg_desc_table[msg->msg_id].num_args) return -EINVAL; + /* + * Some older HSMP SET messages are updated to add GET in the same message. + * In these messages, GET returns the current value and SET also returns + * the successfully set value. To support this GET and SET in same message + * while maintaining backward compatibility for the HSMP users, + * hsmp_msg_desc_table[] indicates only maximum allowed response_sz. + */ + if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET) { + if (msg->response_sz > hsmp_msg_desc_table[msg->msg_id].response_sz) + return -EINVAL; + } else { + /* only HSMP_SET or HSMP_GET messages go through this strict check */ + if (msg->response_sz != hsmp_msg_desc_table[msg->msg_id].response_sz) + return -EINVAL; + } return 0; } @@ -190,13 +214,7 @@ int hsmp_send_message(struct hsmp_message *msg) return -ENODEV; sock = &hsmp_pdev.sock[msg->sock_ind]; - /* - * The time taken by smu operation to complete is between - * 10us to 1ms. Sometime it may take more time. - * In SMP system timeout of 100 millisecs should - * be enough for the previous thread to finish the operation - */ - ret = down_timeout(&sock->hsmp_sem, msecs_to_jiffies(HSMP_MSG_TIMEOUT)); + ret = down_interruptible(&sock->hsmp_sem); if (ret < 0) return ret; @@ -208,6 +226,29 @@ int hsmp_send_message(struct hsmp_message *msg) } EXPORT_SYMBOL_NS_GPL(hsmp_send_message, "AMD_HSMP"); +int hsmp_msg_get_nargs(u16 sock_ind, u32 msg_id, u32 *data, u8 num_args) +{ + struct hsmp_message msg = {}; + unsigned int i; + int ret; + + if (!data) + return -EINVAL; + msg.msg_id = msg_id; + msg.sock_ind = sock_ind; + msg.response_sz = num_args; + + ret = hsmp_send_message(&msg); + if (ret) + return ret; + + for (i = 0; i < num_args; i++) + data[i] = msg.args[i]; + + return 0; +} +EXPORT_SYMBOL_NS_GPL(hsmp_msg_get_nargs, "AMD_HSMP"); + int hsmp_test(u16 sock_ind, u32 value) { struct hsmp_message msg = { 0 }; @@ -239,6 +280,18 @@ int hsmp_test(u16 sock_ind, u32 value) } EXPORT_SYMBOL_NS_GPL(hsmp_test, "AMD_HSMP"); +static bool is_get_msg(struct hsmp_message *msg) +{ + if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_GET) + return true; + + if (hsmp_msg_desc_table[msg->msg_id].type == HSMP_SET_GET && + (msg->args[0] & CHECK_GET_BIT)) + return true; + + return false; +} + long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) { int __user *arguser = (int __user *)arg; @@ -261,7 +314,7 @@ long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) * Device is opened in O_WRONLY mode * Execute only set/configure commands */ - if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_SET) + if (is_get_msg(&msg)) return -EPERM; break; case FMODE_READ: @@ -269,7 +322,7 @@ long hsmp_ioctl(struct file *fp, unsigned int cmd, unsigned long arg) * Device is opened in O_RDONLY mode * Execute only get/monitor commands */ - if (hsmp_msg_desc_table[msg.msg_id].type != HSMP_GET) + if (!is_get_msg(&msg)) return -EPERM; break; case FMODE_READ | FMODE_WRITE: @@ -303,6 +356,11 @@ ssize_t hsmp_metric_tbl_read(struct hsmp_socket *sock, char *buf, size_t size) if (!sock || !buf) return -EINVAL; + if (!sock->metric_tbl_addr) { + dev_err(sock->dev, "Metrics table address not available\n"); + return -ENOMEM; + } + /* Do not support lseek(), also don't allow more than the size of metric table */ if (size != sizeof(struct hsmp_metric_table)) { dev_err(sock->dev, "Wrong buffer size\n"); |