diff options
Diffstat (limited to 'drivers/platform/x86/intel/sdsi.c')
-rw-r--r-- | drivers/platform/x86/intel/sdsi.c | 153 |
1 files changed, 95 insertions, 58 deletions
diff --git a/drivers/platform/x86/intel/sdsi.c b/drivers/platform/x86/intel/sdsi.c index 556e7c6dbb05..30d1c2caf984 100644 --- a/drivers/platform/x86/intel/sdsi.c +++ b/drivers/platform/x86/intel/sdsi.c @@ -12,17 +12,17 @@ #include <linux/bits.h> #include <linux/bitfield.h> #include <linux/device.h> +#include <linux/intel_vsec.h> #include <linux/iopoll.h> #include <linux/kernel.h> #include <linux/module.h> +#include <linux/overflow.h> #include <linux/pci.h> #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/types.h> #include <linux/uaccess.h> -#include "vsec.h" - #define ACCESS_TYPE_BARID 2 #define ACCESS_TYPE_LOCAL 3 @@ -66,6 +66,8 @@ #define CTRL_OWNER GENMASK(5, 4) #define CTRL_COMPLETE BIT(6) #define CTRL_READY BIT(7) +#define CTRL_INBAND_LOCK BIT(32) +#define CTRL_METER_ENABLE_DRAM BIT(33) #define CTRL_STATUS GENMASK(15, 8) #define CTRL_PACKET_SIZE GENMASK(31, 16) #define CTRL_MSG_SIZE GENMASK(63, 48) @@ -93,6 +95,7 @@ enum sdsi_command { struct sdsi_mbox_info { u64 *payload; void *buffer; + u64 control_flags; int size; }; @@ -156,8 +159,8 @@ static int sdsi_status_to_errno(u32 status) } } -static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info, - size_t *data_size) +static int sdsi_mbox_poll(struct sdsi_priv *priv, struct sdsi_mbox_info *info, + size_t *data_size) { struct device *dev = priv->dev; u32 total, loop, eom, status, message_size; @@ -166,18 +169,10 @@ static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *inf lockdep_assert_held(&priv->mb_lock); - /* Format and send the read command */ - control = FIELD_PREP(CTRL_EOM, 1) | - FIELD_PREP(CTRL_SOM, 1) | - FIELD_PREP(CTRL_RUN_BUSY, 1) | - FIELD_PREP(CTRL_PACKET_SIZE, info->size); - writeq(control, priv->control_addr); - /* For reads, data sizes that are larger than the mailbox size are read in packets. */ total = 0; loop = 0; do { - void *buf = info->buffer + (SDSI_SIZE_MAILBOX * loop); u32 packet_size; /* Poll on ready bit */ @@ -195,6 +190,11 @@ static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *inf if (ret) break; + if (!packet_size) { + sdsi_complete_transaction(priv); + break; + } + /* Only the last packet can be less than the mailbox size. */ if (!eom && packet_size != SDSI_SIZE_MAILBOX) { dev_err(dev, "Invalid packet size\n"); @@ -208,9 +208,13 @@ static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *inf break; } - sdsi_memcpy64_fromio(buf, priv->mbox_addr, round_up(packet_size, SDSI_SIZE_CMD)); + if (info->buffer) { + void *buf = info->buffer + array_size(SDSI_SIZE_MAILBOX, loop); - total += packet_size; + sdsi_memcpy64_fromio(buf, priv->mbox_addr, + round_up(packet_size, SDSI_SIZE_CMD)); + total += packet_size; + } sdsi_complete_transaction(priv); } while (!eom && ++loop < MBOX_MAX_PACKETS); @@ -230,16 +234,34 @@ static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *inf dev_warn(dev, "Read count %u differs from expected count %u\n", total, message_size); - *data_size = total; + if (data_size) + *data_size = total; return 0; } -static int sdsi_mbox_cmd_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info) +static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info, + size_t *data_size) +{ + u64 control; + + lockdep_assert_held(&priv->mb_lock); + + /* Format and send the read command */ + control = FIELD_PREP(CTRL_EOM, 1) | + FIELD_PREP(CTRL_SOM, 1) | + FIELD_PREP(CTRL_RUN_BUSY, 1) | + FIELD_PREP(CTRL_PACKET_SIZE, info->size) | + info->control_flags; + writeq(control, priv->control_addr); + + return sdsi_mbox_poll(priv, info, data_size); +} + +static int sdsi_mbox_cmd_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info, + size_t *data_size) { u64 control; - u32 status; - int ret; lockdep_assert_held(&priv->mb_lock); @@ -252,23 +274,11 @@ static int sdsi_mbox_cmd_write(struct sdsi_priv *priv, struct sdsi_mbox_info *in FIELD_PREP(CTRL_SOM, 1) | FIELD_PREP(CTRL_RUN_BUSY, 1) | FIELD_PREP(CTRL_READ_WRITE, 1) | + FIELD_PREP(CTRL_MSG_SIZE, info->size) | FIELD_PREP(CTRL_PACKET_SIZE, info->size); writeq(control, priv->control_addr); - /* Poll on ready bit */ - ret = readq_poll_timeout(priv->control_addr, control, control & CTRL_READY, - MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_US); - - if (ret) - goto release_mbox; - - status = FIELD_GET(CTRL_STATUS, control); - ret = sdsi_status_to_errno(status); - -release_mbox: - sdsi_complete_transaction(priv); - - return ret; + return sdsi_mbox_poll(priv, info, data_size); } static int sdsi_mbox_acquire(struct sdsi_priv *priv, struct sdsi_mbox_info *info) @@ -312,7 +322,8 @@ static int sdsi_mbox_acquire(struct sdsi_priv *priv, struct sdsi_mbox_info *info return ret; } -static int sdsi_mbox_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info) +static int sdsi_mbox_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info, + size_t *data_size) { int ret; @@ -322,7 +333,7 @@ static int sdsi_mbox_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info) if (ret) return ret; - return sdsi_mbox_cmd_write(priv, info); + return sdsi_mbox_cmd_write(priv, info, data_size); } static int sdsi_mbox_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info, size_t *data_size) @@ -338,15 +349,24 @@ static int sdsi_mbox_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info, s return sdsi_mbox_cmd_read(priv, info, data_size); } +static bool sdsi_ib_locked(struct sdsi_priv *priv) +{ + return !!FIELD_GET(CTRL_INBAND_LOCK, readq(priv->control_addr)); +} + static ssize_t sdsi_provision(struct sdsi_priv *priv, char *buf, size_t count, enum sdsi_command command) { - struct sdsi_mbox_info info; + struct sdsi_mbox_info info = {}; int ret; if (count > (SDSI_SIZE_WRITE_MSG - SDSI_SIZE_CMD)) return -EOVERFLOW; + /* Make sure In-band lock is not set */ + if (sdsi_ib_locked(priv)) + return -EPERM; + /* Qword aligned message + command qword */ info.size = round_up(count, SDSI_SIZE_CMD) + SDSI_SIZE_CMD; @@ -363,7 +383,9 @@ static ssize_t sdsi_provision(struct sdsi_priv *priv, char *buf, size_t count, ret = mutex_lock_interruptible(&priv->mb_lock); if (ret) goto free_payload; - ret = sdsi_mbox_write(priv, &info); + + ret = sdsi_mbox_write(priv, &info, NULL); + mutex_unlock(&priv->mb_lock); free_payload: @@ -376,8 +398,8 @@ free_payload: } static ssize_t provision_akc_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, - size_t count) + const struct bin_attribute *attr, char *buf, + loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); @@ -387,11 +409,11 @@ static ssize_t provision_akc_write(struct file *filp, struct kobject *kobj, return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_AKC); } -static BIN_ATTR_WO(provision_akc, SDSI_SIZE_WRITE_MSG); +static const BIN_ATTR_WO(provision_akc, SDSI_SIZE_WRITE_MSG); static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, - size_t count) + const struct bin_attribute *attr, char *buf, + loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); @@ -401,13 +423,13 @@ static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj, return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_CAP); } -static BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG); +static const BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG); static ssize_t -certificate_read(u64 command, struct sdsi_priv *priv, char *buf, loff_t off, - size_t count) +certificate_read(u64 command, u64 control_flags, struct sdsi_priv *priv, + char *buf, loff_t off, size_t count) { - struct sdsi_mbox_info info; + struct sdsi_mbox_info info = {}; size_t size; int ret; @@ -421,6 +443,7 @@ certificate_read(u64 command, struct sdsi_priv *priv, char *buf, loff_t off, info.payload = &command; info.size = sizeof(command); + info.control_flags = control_flags; ret = mutex_lock_interruptible(&priv->mb_lock); if (ret) @@ -446,31 +469,44 @@ free_buffer: static ssize_t state_certificate_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); - return certificate_read(SDSI_CMD_READ_STATE, priv, buf, off, count); + return certificate_read(SDSI_CMD_READ_STATE, 0, priv, buf, off, count); } -static BIN_ATTR_ADMIN_RO(state_certificate, SDSI_SIZE_READ_MSG); +static const BIN_ATTR_ADMIN_RO(state_certificate, SDSI_SIZE_READ_MSG); static ssize_t meter_certificate_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, + const struct bin_attribute *attr, char *buf, loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); - return certificate_read(SDSI_CMD_READ_METER, priv, buf, off, count); + return certificate_read(SDSI_CMD_READ_METER, 0, priv, buf, off, count); +} +static const BIN_ATTR_ADMIN_RO(meter_certificate, SDSI_SIZE_READ_MSG); + +static ssize_t +meter_current_read(struct file *filp, struct kobject *kobj, + const struct bin_attribute *attr, char *buf, loff_t off, + size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct sdsi_priv *priv = dev_get_drvdata(dev); + + return certificate_read(SDSI_CMD_READ_METER, CTRL_METER_ENABLE_DRAM, + priv, buf, off, count); } -static BIN_ATTR_ADMIN_RO(meter_certificate, SDSI_SIZE_READ_MSG); +static const BIN_ATTR_ADMIN_RO(meter_current, SDSI_SIZE_READ_MSG); static ssize_t registers_read(struct file *filp, struct kobject *kobj, - struct bin_attribute *attr, char *buf, loff_t off, - size_t count) + const struct bin_attribute *attr, char *buf, + loff_t off, size_t count) { struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); @@ -492,19 +528,20 @@ static ssize_t registers_read(struct file *filp, struct kobject *kobj, return count; } -static BIN_ATTR_ADMIN_RO(registers, SDSI_SIZE_REGS); +static const BIN_ATTR_ADMIN_RO(registers, SDSI_SIZE_REGS); -static struct bin_attribute *sdsi_bin_attrs[] = { +static const struct bin_attribute *const sdsi_bin_attrs[] = { &bin_attr_registers, &bin_attr_state_certificate, &bin_attr_meter_certificate, + &bin_attr_meter_current, &bin_attr_provision_akc, &bin_attr_provision_cap, NULL }; static umode_t -sdsi_battr_is_visible(struct kobject *kobj, struct bin_attribute *attr, int n) +sdsi_battr_is_visible(struct kobject *kobj, const struct bin_attribute *attr, int n) { struct device *dev = kobj_to_dev(kobj); struct sdsi_priv *priv = dev_get_drvdata(dev); @@ -517,7 +554,7 @@ sdsi_battr_is_visible(struct kobject *kobj, struct bin_attribute *attr, int n) if (!(priv->features & SDSI_FEATURE_SDSI)) return 0; - if (attr == &bin_attr_meter_certificate) + if (attr == &bin_attr_meter_certificate || attr == &bin_attr_meter_current) return (priv->features & SDSI_FEATURE_METERING) ? attr->attr.mode : 0; @@ -539,7 +576,7 @@ static struct attribute *sdsi_attrs[] = { static const struct attribute_group sdsi_group = { .attrs = sdsi_attrs, - .bin_attrs = sdsi_bin_attrs, + .bin_attrs_new = sdsi_bin_attrs, .is_bin_visible = sdsi_battr_is_visible, }; __ATTRIBUTE_GROUPS(sdsi); |