162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Intel On Demand (Software Defined Silicon) driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2022, Intel Corporation. 662306a36Sopenharmony_ci * All Rights Reserved. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Author: "David E. Box" <david.e.box@linux.intel.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/auxiliary_bus.h> 1262306a36Sopenharmony_ci#include <linux/bits.h> 1362306a36Sopenharmony_ci#include <linux/bitfield.h> 1462306a36Sopenharmony_ci#include <linux/device.h> 1562306a36Sopenharmony_ci#include <linux/iopoll.h> 1662306a36Sopenharmony_ci#include <linux/kernel.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/pci.h> 1962306a36Sopenharmony_ci#include <linux/slab.h> 2062306a36Sopenharmony_ci#include <linux/sysfs.h> 2162306a36Sopenharmony_ci#include <linux/types.h> 2262306a36Sopenharmony_ci#include <linux/uaccess.h> 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci#include "vsec.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define ACCESS_TYPE_BARID 2 2762306a36Sopenharmony_ci#define ACCESS_TYPE_LOCAL 3 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci#define SDSI_MIN_SIZE_DWORDS 276 3062306a36Sopenharmony_ci#define SDSI_SIZE_MAILBOX 1024 3162306a36Sopenharmony_ci#define SDSI_SIZE_REGS 80 3262306a36Sopenharmony_ci#define SDSI_SIZE_CMD sizeof(u64) 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci/* 3562306a36Sopenharmony_ci * Write messages are currently up to the size of the mailbox 3662306a36Sopenharmony_ci * while read messages are up to 4 times the size of the 3762306a36Sopenharmony_ci * mailbox, sent in packets 3862306a36Sopenharmony_ci */ 3962306a36Sopenharmony_ci#define SDSI_SIZE_WRITE_MSG SDSI_SIZE_MAILBOX 4062306a36Sopenharmony_ci#define SDSI_SIZE_READ_MSG (SDSI_SIZE_MAILBOX * 4) 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define SDSI_ENABLED_FEATURES_OFFSET 16 4362306a36Sopenharmony_ci#define SDSI_FEATURE_SDSI BIT(3) 4462306a36Sopenharmony_ci#define SDSI_FEATURE_METERING BIT(26) 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci#define SDSI_SOCKET_ID_OFFSET 64 4762306a36Sopenharmony_ci#define SDSI_SOCKET_ID GENMASK(3, 0) 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define SDSI_MBOX_CMD_SUCCESS 0x40 5062306a36Sopenharmony_ci#define SDSI_MBOX_CMD_TIMEOUT 0x80 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#define MBOX_TIMEOUT_US 500000 5362306a36Sopenharmony_ci#define MBOX_TIMEOUT_ACQUIRE_US 1000 5462306a36Sopenharmony_ci#define MBOX_POLLING_PERIOD_US 100 5562306a36Sopenharmony_ci#define MBOX_ACQUIRE_NUM_RETRIES 5 5662306a36Sopenharmony_ci#define MBOX_ACQUIRE_RETRY_DELAY_MS 500 5762306a36Sopenharmony_ci#define MBOX_MAX_PACKETS 4 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define MBOX_OWNER_NONE 0x00 6062306a36Sopenharmony_ci#define MBOX_OWNER_INBAND 0x01 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#define CTRL_RUN_BUSY BIT(0) 6362306a36Sopenharmony_ci#define CTRL_READ_WRITE BIT(1) 6462306a36Sopenharmony_ci#define CTRL_SOM BIT(2) 6562306a36Sopenharmony_ci#define CTRL_EOM BIT(3) 6662306a36Sopenharmony_ci#define CTRL_OWNER GENMASK(5, 4) 6762306a36Sopenharmony_ci#define CTRL_COMPLETE BIT(6) 6862306a36Sopenharmony_ci#define CTRL_READY BIT(7) 6962306a36Sopenharmony_ci#define CTRL_STATUS GENMASK(15, 8) 7062306a36Sopenharmony_ci#define CTRL_PACKET_SIZE GENMASK(31, 16) 7162306a36Sopenharmony_ci#define CTRL_MSG_SIZE GENMASK(63, 48) 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci#define DISC_TABLE_SIZE 12 7462306a36Sopenharmony_ci#define DT_ACCESS_TYPE GENMASK(3, 0) 7562306a36Sopenharmony_ci#define DT_SIZE GENMASK(27, 12) 7662306a36Sopenharmony_ci#define DT_TBIR GENMASK(2, 0) 7762306a36Sopenharmony_ci#define DT_OFFSET(v) ((v) & GENMASK(31, 3)) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define SDSI_GUID_V1 0x006DD191 8062306a36Sopenharmony_ci#define GUID_V1_CNTRL_SIZE 8 8162306a36Sopenharmony_ci#define GUID_V1_REGS_SIZE 72 8262306a36Sopenharmony_ci#define SDSI_GUID_V2 0xF210D9EF 8362306a36Sopenharmony_ci#define GUID_V2_CNTRL_SIZE 16 8462306a36Sopenharmony_ci#define GUID_V2_REGS_SIZE 80 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_cienum sdsi_command { 8762306a36Sopenharmony_ci SDSI_CMD_PROVISION_AKC = 0x0004, 8862306a36Sopenharmony_ci SDSI_CMD_PROVISION_CAP = 0x0008, 8962306a36Sopenharmony_ci SDSI_CMD_READ_STATE = 0x0010, 9062306a36Sopenharmony_ci SDSI_CMD_READ_METER = 0x0014, 9162306a36Sopenharmony_ci}; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistruct sdsi_mbox_info { 9462306a36Sopenharmony_ci u64 *payload; 9562306a36Sopenharmony_ci void *buffer; 9662306a36Sopenharmony_ci int size; 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistruct disc_table { 10062306a36Sopenharmony_ci u32 access_info; 10162306a36Sopenharmony_ci u32 guid; 10262306a36Sopenharmony_ci u32 offset; 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistruct sdsi_priv { 10662306a36Sopenharmony_ci struct mutex mb_lock; /* Mailbox access lock */ 10762306a36Sopenharmony_ci struct device *dev; 10862306a36Sopenharmony_ci void __iomem *control_addr; 10962306a36Sopenharmony_ci void __iomem *mbox_addr; 11062306a36Sopenharmony_ci void __iomem *regs_addr; 11162306a36Sopenharmony_ci int control_size; 11262306a36Sopenharmony_ci int maibox_size; 11362306a36Sopenharmony_ci int registers_size; 11462306a36Sopenharmony_ci u32 guid; 11562306a36Sopenharmony_ci u32 features; 11662306a36Sopenharmony_ci}; 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci/* SDSi mailbox operations must be performed using 64bit mov instructions */ 11962306a36Sopenharmony_cistatic __always_inline void 12062306a36Sopenharmony_cisdsi_memcpy64_toio(u64 __iomem *to, const u64 *from, size_t count_bytes) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci size_t count = count_bytes / sizeof(*to); 12362306a36Sopenharmony_ci int i; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci for (i = 0; i < count; i++) 12662306a36Sopenharmony_ci writeq(from[i], &to[i]); 12762306a36Sopenharmony_ci} 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic __always_inline void 13062306a36Sopenharmony_cisdsi_memcpy64_fromio(u64 *to, const u64 __iomem *from, size_t count_bytes) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci size_t count = count_bytes / sizeof(*to); 13362306a36Sopenharmony_ci int i; 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci for (i = 0; i < count; i++) 13662306a36Sopenharmony_ci to[i] = readq(&from[i]); 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_cistatic inline void sdsi_complete_transaction(struct sdsi_priv *priv) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci u64 control = FIELD_PREP(CTRL_COMPLETE, 1); 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci lockdep_assert_held(&priv->mb_lock); 14462306a36Sopenharmony_ci writeq(control, priv->control_addr); 14562306a36Sopenharmony_ci} 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_cistatic int sdsi_status_to_errno(u32 status) 14862306a36Sopenharmony_ci{ 14962306a36Sopenharmony_ci switch (status) { 15062306a36Sopenharmony_ci case SDSI_MBOX_CMD_SUCCESS: 15162306a36Sopenharmony_ci return 0; 15262306a36Sopenharmony_ci case SDSI_MBOX_CMD_TIMEOUT: 15362306a36Sopenharmony_ci return -ETIMEDOUT; 15462306a36Sopenharmony_ci default: 15562306a36Sopenharmony_ci return -EIO; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cistatic int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info, 16062306a36Sopenharmony_ci size_t *data_size) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci struct device *dev = priv->dev; 16362306a36Sopenharmony_ci u32 total, loop, eom, status, message_size; 16462306a36Sopenharmony_ci u64 control; 16562306a36Sopenharmony_ci int ret; 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci lockdep_assert_held(&priv->mb_lock); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci /* Format and send the read command */ 17062306a36Sopenharmony_ci control = FIELD_PREP(CTRL_EOM, 1) | 17162306a36Sopenharmony_ci FIELD_PREP(CTRL_SOM, 1) | 17262306a36Sopenharmony_ci FIELD_PREP(CTRL_RUN_BUSY, 1) | 17362306a36Sopenharmony_ci FIELD_PREP(CTRL_PACKET_SIZE, info->size); 17462306a36Sopenharmony_ci writeq(control, priv->control_addr); 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci /* For reads, data sizes that are larger than the mailbox size are read in packets. */ 17762306a36Sopenharmony_ci total = 0; 17862306a36Sopenharmony_ci loop = 0; 17962306a36Sopenharmony_ci do { 18062306a36Sopenharmony_ci void *buf = info->buffer + (SDSI_SIZE_MAILBOX * loop); 18162306a36Sopenharmony_ci u32 packet_size; 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci /* Poll on ready bit */ 18462306a36Sopenharmony_ci ret = readq_poll_timeout(priv->control_addr, control, control & CTRL_READY, 18562306a36Sopenharmony_ci MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_US); 18662306a36Sopenharmony_ci if (ret) 18762306a36Sopenharmony_ci break; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci eom = FIELD_GET(CTRL_EOM, control); 19062306a36Sopenharmony_ci status = FIELD_GET(CTRL_STATUS, control); 19162306a36Sopenharmony_ci packet_size = FIELD_GET(CTRL_PACKET_SIZE, control); 19262306a36Sopenharmony_ci message_size = FIELD_GET(CTRL_MSG_SIZE, control); 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci ret = sdsi_status_to_errno(status); 19562306a36Sopenharmony_ci if (ret) 19662306a36Sopenharmony_ci break; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci /* Only the last packet can be less than the mailbox size. */ 19962306a36Sopenharmony_ci if (!eom && packet_size != SDSI_SIZE_MAILBOX) { 20062306a36Sopenharmony_ci dev_err(dev, "Invalid packet size\n"); 20162306a36Sopenharmony_ci ret = -EPROTO; 20262306a36Sopenharmony_ci break; 20362306a36Sopenharmony_ci } 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci if (packet_size > SDSI_SIZE_MAILBOX) { 20662306a36Sopenharmony_ci dev_err(dev, "Packet size too large\n"); 20762306a36Sopenharmony_ci ret = -EPROTO; 20862306a36Sopenharmony_ci break; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci sdsi_memcpy64_fromio(buf, priv->mbox_addr, round_up(packet_size, SDSI_SIZE_CMD)); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci total += packet_size; 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci sdsi_complete_transaction(priv); 21662306a36Sopenharmony_ci } while (!eom && ++loop < MBOX_MAX_PACKETS); 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci if (ret) { 21962306a36Sopenharmony_ci sdsi_complete_transaction(priv); 22062306a36Sopenharmony_ci return ret; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci if (!eom) { 22462306a36Sopenharmony_ci dev_err(dev, "Exceeded read attempts\n"); 22562306a36Sopenharmony_ci return -EPROTO; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci /* Message size check is only valid for multi-packet transfers */ 22962306a36Sopenharmony_ci if (loop && total != message_size) 23062306a36Sopenharmony_ci dev_warn(dev, "Read count %u differs from expected count %u\n", 23162306a36Sopenharmony_ci total, message_size); 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci *data_size = total; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci} 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_cistatic int sdsi_mbox_cmd_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info) 23962306a36Sopenharmony_ci{ 24062306a36Sopenharmony_ci u64 control; 24162306a36Sopenharmony_ci u32 status; 24262306a36Sopenharmony_ci int ret; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci lockdep_assert_held(&priv->mb_lock); 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci /* Write rest of the payload */ 24762306a36Sopenharmony_ci sdsi_memcpy64_toio(priv->mbox_addr + SDSI_SIZE_CMD, info->payload + 1, 24862306a36Sopenharmony_ci info->size - SDSI_SIZE_CMD); 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* Format and send the write command */ 25162306a36Sopenharmony_ci control = FIELD_PREP(CTRL_EOM, 1) | 25262306a36Sopenharmony_ci FIELD_PREP(CTRL_SOM, 1) | 25362306a36Sopenharmony_ci FIELD_PREP(CTRL_RUN_BUSY, 1) | 25462306a36Sopenharmony_ci FIELD_PREP(CTRL_READ_WRITE, 1) | 25562306a36Sopenharmony_ci FIELD_PREP(CTRL_PACKET_SIZE, info->size); 25662306a36Sopenharmony_ci writeq(control, priv->control_addr); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Poll on ready bit */ 25962306a36Sopenharmony_ci ret = readq_poll_timeout(priv->control_addr, control, control & CTRL_READY, 26062306a36Sopenharmony_ci MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_US); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci if (ret) 26362306a36Sopenharmony_ci goto release_mbox; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci status = FIELD_GET(CTRL_STATUS, control); 26662306a36Sopenharmony_ci ret = sdsi_status_to_errno(status); 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_cirelease_mbox: 26962306a36Sopenharmony_ci sdsi_complete_transaction(priv); 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci return ret; 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int sdsi_mbox_acquire(struct sdsi_priv *priv, struct sdsi_mbox_info *info) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci u64 control; 27762306a36Sopenharmony_ci u32 owner; 27862306a36Sopenharmony_ci int ret, retries = 0; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci lockdep_assert_held(&priv->mb_lock); 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* Check mailbox is available */ 28362306a36Sopenharmony_ci control = readq(priv->control_addr); 28462306a36Sopenharmony_ci owner = FIELD_GET(CTRL_OWNER, control); 28562306a36Sopenharmony_ci if (owner != MBOX_OWNER_NONE) 28662306a36Sopenharmony_ci return -EBUSY; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* 28962306a36Sopenharmony_ci * If there has been no recent transaction and no one owns the mailbox, 29062306a36Sopenharmony_ci * we should acquire it in under 1ms. However, if we've accessed it 29162306a36Sopenharmony_ci * recently it may take up to 2.1 seconds to acquire it again. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci do { 29462306a36Sopenharmony_ci /* Write first qword of payload */ 29562306a36Sopenharmony_ci writeq(info->payload[0], priv->mbox_addr); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Check for ownership */ 29862306a36Sopenharmony_ci ret = readq_poll_timeout(priv->control_addr, control, 29962306a36Sopenharmony_ci FIELD_GET(CTRL_OWNER, control) == MBOX_OWNER_INBAND, 30062306a36Sopenharmony_ci MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_ACQUIRE_US); 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (FIELD_GET(CTRL_OWNER, control) == MBOX_OWNER_NONE && 30362306a36Sopenharmony_ci retries++ < MBOX_ACQUIRE_NUM_RETRIES) { 30462306a36Sopenharmony_ci msleep(MBOX_ACQUIRE_RETRY_DELAY_MS); 30562306a36Sopenharmony_ci continue; 30662306a36Sopenharmony_ci } 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Either we got it or someone else did. */ 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci } while (true); 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci return ret; 31362306a36Sopenharmony_ci} 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic int sdsi_mbox_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci int ret; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci lockdep_assert_held(&priv->mb_lock); 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci ret = sdsi_mbox_acquire(priv, info); 32262306a36Sopenharmony_ci if (ret) 32362306a36Sopenharmony_ci return ret; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci return sdsi_mbox_cmd_write(priv, info); 32662306a36Sopenharmony_ci} 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic int sdsi_mbox_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info, size_t *data_size) 32962306a36Sopenharmony_ci{ 33062306a36Sopenharmony_ci int ret; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci lockdep_assert_held(&priv->mb_lock); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci ret = sdsi_mbox_acquire(priv, info); 33562306a36Sopenharmony_ci if (ret) 33662306a36Sopenharmony_ci return ret; 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return sdsi_mbox_cmd_read(priv, info, data_size); 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic ssize_t sdsi_provision(struct sdsi_priv *priv, char *buf, size_t count, 34262306a36Sopenharmony_ci enum sdsi_command command) 34362306a36Sopenharmony_ci{ 34462306a36Sopenharmony_ci struct sdsi_mbox_info info; 34562306a36Sopenharmony_ci int ret; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci if (count > (SDSI_SIZE_WRITE_MSG - SDSI_SIZE_CMD)) 34862306a36Sopenharmony_ci return -EOVERFLOW; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Qword aligned message + command qword */ 35162306a36Sopenharmony_ci info.size = round_up(count, SDSI_SIZE_CMD) + SDSI_SIZE_CMD; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci info.payload = kzalloc(info.size, GFP_KERNEL); 35462306a36Sopenharmony_ci if (!info.payload) 35562306a36Sopenharmony_ci return -ENOMEM; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* Copy message to payload buffer */ 35862306a36Sopenharmony_ci memcpy(info.payload, buf, count); 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* Command is last qword of payload buffer */ 36162306a36Sopenharmony_ci info.payload[(info.size - SDSI_SIZE_CMD) / SDSI_SIZE_CMD] = command; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci ret = mutex_lock_interruptible(&priv->mb_lock); 36462306a36Sopenharmony_ci if (ret) 36562306a36Sopenharmony_ci goto free_payload; 36662306a36Sopenharmony_ci ret = sdsi_mbox_write(priv, &info); 36762306a36Sopenharmony_ci mutex_unlock(&priv->mb_lock); 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_cifree_payload: 37062306a36Sopenharmony_ci kfree(info.payload); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci if (ret) 37362306a36Sopenharmony_ci return ret; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci return count; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_cistatic ssize_t provision_akc_write(struct file *filp, struct kobject *kobj, 37962306a36Sopenharmony_ci struct bin_attribute *attr, char *buf, loff_t off, 38062306a36Sopenharmony_ci size_t count) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 38362306a36Sopenharmony_ci struct sdsi_priv *priv = dev_get_drvdata(dev); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci if (off) 38662306a36Sopenharmony_ci return -ESPIPE; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_AKC); 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_cistatic BIN_ATTR_WO(provision_akc, SDSI_SIZE_WRITE_MSG); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic ssize_t provision_cap_write(struct file *filp, struct kobject *kobj, 39362306a36Sopenharmony_ci struct bin_attribute *attr, char *buf, loff_t off, 39462306a36Sopenharmony_ci size_t count) 39562306a36Sopenharmony_ci{ 39662306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 39762306a36Sopenharmony_ci struct sdsi_priv *priv = dev_get_drvdata(dev); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (off) 40062306a36Sopenharmony_ci return -ESPIPE; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_CAP); 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_cistatic BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG); 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_cistatic ssize_t 40762306a36Sopenharmony_cicertificate_read(u64 command, struct sdsi_priv *priv, char *buf, loff_t off, 40862306a36Sopenharmony_ci size_t count) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct sdsi_mbox_info info; 41162306a36Sopenharmony_ci size_t size; 41262306a36Sopenharmony_ci int ret; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (off) 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci /* Buffer for return data */ 41862306a36Sopenharmony_ci info.buffer = kmalloc(SDSI_SIZE_READ_MSG, GFP_KERNEL); 41962306a36Sopenharmony_ci if (!info.buffer) 42062306a36Sopenharmony_ci return -ENOMEM; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci info.payload = &command; 42362306a36Sopenharmony_ci info.size = sizeof(command); 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci ret = mutex_lock_interruptible(&priv->mb_lock); 42662306a36Sopenharmony_ci if (ret) 42762306a36Sopenharmony_ci goto free_buffer; 42862306a36Sopenharmony_ci ret = sdsi_mbox_read(priv, &info, &size); 42962306a36Sopenharmony_ci mutex_unlock(&priv->mb_lock); 43062306a36Sopenharmony_ci if (ret < 0) 43162306a36Sopenharmony_ci goto free_buffer; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci if (size > count) 43462306a36Sopenharmony_ci size = count; 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci memcpy(buf, info.buffer, size); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cifree_buffer: 43962306a36Sopenharmony_ci kfree(info.buffer); 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (ret) 44262306a36Sopenharmony_ci return ret; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci return size; 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic ssize_t 44862306a36Sopenharmony_cistate_certificate_read(struct file *filp, struct kobject *kobj, 44962306a36Sopenharmony_ci struct bin_attribute *attr, char *buf, loff_t off, 45062306a36Sopenharmony_ci size_t count) 45162306a36Sopenharmony_ci{ 45262306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 45362306a36Sopenharmony_ci struct sdsi_priv *priv = dev_get_drvdata(dev); 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci return certificate_read(SDSI_CMD_READ_STATE, priv, buf, off, count); 45662306a36Sopenharmony_ci} 45762306a36Sopenharmony_cistatic BIN_ATTR_ADMIN_RO(state_certificate, SDSI_SIZE_READ_MSG); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_cistatic ssize_t 46062306a36Sopenharmony_cimeter_certificate_read(struct file *filp, struct kobject *kobj, 46162306a36Sopenharmony_ci struct bin_attribute *attr, char *buf, loff_t off, 46262306a36Sopenharmony_ci size_t count) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 46562306a36Sopenharmony_ci struct sdsi_priv *priv = dev_get_drvdata(dev); 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci return certificate_read(SDSI_CMD_READ_METER, priv, buf, off, count); 46862306a36Sopenharmony_ci} 46962306a36Sopenharmony_cistatic BIN_ATTR_ADMIN_RO(meter_certificate, SDSI_SIZE_READ_MSG); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_cistatic ssize_t registers_read(struct file *filp, struct kobject *kobj, 47262306a36Sopenharmony_ci struct bin_attribute *attr, char *buf, loff_t off, 47362306a36Sopenharmony_ci size_t count) 47462306a36Sopenharmony_ci{ 47562306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 47662306a36Sopenharmony_ci struct sdsi_priv *priv = dev_get_drvdata(dev); 47762306a36Sopenharmony_ci void __iomem *addr = priv->regs_addr; 47862306a36Sopenharmony_ci int size = priv->registers_size; 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci /* 48162306a36Sopenharmony_ci * The check below is performed by the sysfs caller based on the static 48262306a36Sopenharmony_ci * file size. But this may be greater than the actual size which is based 48362306a36Sopenharmony_ci * on the GUID. So check here again based on actual size before reading. 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_ci if (off >= size) 48662306a36Sopenharmony_ci return 0; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci if (off + count > size) 48962306a36Sopenharmony_ci count = size - off; 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci memcpy_fromio(buf, addr + off, count); 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci return count; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_cistatic BIN_ATTR_ADMIN_RO(registers, SDSI_SIZE_REGS); 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_cistatic struct bin_attribute *sdsi_bin_attrs[] = { 49862306a36Sopenharmony_ci &bin_attr_registers, 49962306a36Sopenharmony_ci &bin_attr_state_certificate, 50062306a36Sopenharmony_ci &bin_attr_meter_certificate, 50162306a36Sopenharmony_ci &bin_attr_provision_akc, 50262306a36Sopenharmony_ci &bin_attr_provision_cap, 50362306a36Sopenharmony_ci NULL 50462306a36Sopenharmony_ci}; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_cistatic umode_t 50762306a36Sopenharmony_cisdsi_battr_is_visible(struct kobject *kobj, struct bin_attribute *attr, int n) 50862306a36Sopenharmony_ci{ 50962306a36Sopenharmony_ci struct device *dev = kobj_to_dev(kobj); 51062306a36Sopenharmony_ci struct sdsi_priv *priv = dev_get_drvdata(dev); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci /* Registers file is always readable if the device is present */ 51362306a36Sopenharmony_ci if (attr == &bin_attr_registers) 51462306a36Sopenharmony_ci return attr->attr.mode; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci /* All other attributes not visible if BIOS has not enabled On Demand */ 51762306a36Sopenharmony_ci if (!(priv->features & SDSI_FEATURE_SDSI)) 51862306a36Sopenharmony_ci return 0; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci if (attr == &bin_attr_meter_certificate) 52162306a36Sopenharmony_ci return (priv->features & SDSI_FEATURE_METERING) ? 52262306a36Sopenharmony_ci attr->attr.mode : 0; 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return attr->attr.mode; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci struct sdsi_priv *priv = dev_get_drvdata(dev); 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci return sysfs_emit(buf, "0x%x\n", priv->guid); 53262306a36Sopenharmony_ci} 53362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(guid); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic struct attribute *sdsi_attrs[] = { 53662306a36Sopenharmony_ci &dev_attr_guid.attr, 53762306a36Sopenharmony_ci NULL 53862306a36Sopenharmony_ci}; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_cistatic const struct attribute_group sdsi_group = { 54162306a36Sopenharmony_ci .attrs = sdsi_attrs, 54262306a36Sopenharmony_ci .bin_attrs = sdsi_bin_attrs, 54362306a36Sopenharmony_ci .is_bin_visible = sdsi_battr_is_visible, 54462306a36Sopenharmony_ci}; 54562306a36Sopenharmony_ci__ATTRIBUTE_GROUPS(sdsi); 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_cistatic int sdsi_get_layout(struct sdsi_priv *priv, struct disc_table *table) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci switch (table->guid) { 55062306a36Sopenharmony_ci case SDSI_GUID_V1: 55162306a36Sopenharmony_ci priv->control_size = GUID_V1_CNTRL_SIZE; 55262306a36Sopenharmony_ci priv->registers_size = GUID_V1_REGS_SIZE; 55362306a36Sopenharmony_ci break; 55462306a36Sopenharmony_ci case SDSI_GUID_V2: 55562306a36Sopenharmony_ci priv->control_size = GUID_V2_CNTRL_SIZE; 55662306a36Sopenharmony_ci priv->registers_size = GUID_V2_REGS_SIZE; 55762306a36Sopenharmony_ci break; 55862306a36Sopenharmony_ci default: 55962306a36Sopenharmony_ci dev_err(priv->dev, "Unrecognized GUID 0x%x\n", table->guid); 56062306a36Sopenharmony_ci return -EINVAL; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci return 0; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct pci_dev *parent, 56662306a36Sopenharmony_ci struct disc_table *disc_table, struct resource *disc_res) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci u32 access_type = FIELD_GET(DT_ACCESS_TYPE, disc_table->access_info); 56962306a36Sopenharmony_ci u32 size = FIELD_GET(DT_SIZE, disc_table->access_info); 57062306a36Sopenharmony_ci u32 tbir = FIELD_GET(DT_TBIR, disc_table->offset); 57162306a36Sopenharmony_ci u32 offset = DT_OFFSET(disc_table->offset); 57262306a36Sopenharmony_ci struct resource res = {}; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci /* Starting location of SDSi MMIO region based on access type */ 57562306a36Sopenharmony_ci switch (access_type) { 57662306a36Sopenharmony_ci case ACCESS_TYPE_LOCAL: 57762306a36Sopenharmony_ci if (tbir) { 57862306a36Sopenharmony_ci dev_err(priv->dev, "Unsupported BAR index %u for access type %u\n", 57962306a36Sopenharmony_ci tbir, access_type); 58062306a36Sopenharmony_ci return -EINVAL; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci /* 58462306a36Sopenharmony_ci * For access_type LOCAL, the base address is as follows: 58562306a36Sopenharmony_ci * base address = end of discovery region + base offset + 1 58662306a36Sopenharmony_ci */ 58762306a36Sopenharmony_ci res.start = disc_res->end + offset + 1; 58862306a36Sopenharmony_ci break; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci case ACCESS_TYPE_BARID: 59162306a36Sopenharmony_ci res.start = pci_resource_start(parent, tbir) + offset; 59262306a36Sopenharmony_ci break; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci default: 59562306a36Sopenharmony_ci dev_err(priv->dev, "Unrecognized access_type %u\n", access_type); 59662306a36Sopenharmony_ci return -EINVAL; 59762306a36Sopenharmony_ci } 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci res.end = res.start + size * sizeof(u32) - 1; 60062306a36Sopenharmony_ci res.flags = IORESOURCE_MEM; 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_ci priv->control_addr = devm_ioremap_resource(priv->dev, &res); 60362306a36Sopenharmony_ci if (IS_ERR(priv->control_addr)) 60462306a36Sopenharmony_ci return PTR_ERR(priv->control_addr); 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci priv->mbox_addr = priv->control_addr + priv->control_size; 60762306a36Sopenharmony_ci priv->regs_addr = priv->mbox_addr + SDSI_SIZE_MAILBOX; 60862306a36Sopenharmony_ci 60962306a36Sopenharmony_ci priv->features = readq(priv->regs_addr + SDSI_ENABLED_FEATURES_OFFSET); 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci return 0; 61262306a36Sopenharmony_ci} 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_cistatic int sdsi_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci struct intel_vsec_device *intel_cap_dev = auxdev_to_ivdev(auxdev); 61762306a36Sopenharmony_ci struct disc_table disc_table; 61862306a36Sopenharmony_ci struct resource *disc_res; 61962306a36Sopenharmony_ci void __iomem *disc_addr; 62062306a36Sopenharmony_ci struct sdsi_priv *priv; 62162306a36Sopenharmony_ci int ret; 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL); 62462306a36Sopenharmony_ci if (!priv) 62562306a36Sopenharmony_ci return -ENOMEM; 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci priv->dev = &auxdev->dev; 62862306a36Sopenharmony_ci mutex_init(&priv->mb_lock); 62962306a36Sopenharmony_ci auxiliary_set_drvdata(auxdev, priv); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci /* Get the SDSi discovery table */ 63262306a36Sopenharmony_ci disc_res = &intel_cap_dev->resource[0]; 63362306a36Sopenharmony_ci disc_addr = devm_ioremap_resource(&auxdev->dev, disc_res); 63462306a36Sopenharmony_ci if (IS_ERR(disc_addr)) 63562306a36Sopenharmony_ci return PTR_ERR(disc_addr); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci memcpy_fromio(&disc_table, disc_addr, DISC_TABLE_SIZE); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci priv->guid = disc_table.guid; 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* Get guid based layout info */ 64262306a36Sopenharmony_ci ret = sdsi_get_layout(priv, &disc_table); 64362306a36Sopenharmony_ci if (ret) 64462306a36Sopenharmony_ci return ret; 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* Map the SDSi mailbox registers */ 64762306a36Sopenharmony_ci ret = sdsi_map_mbox_registers(priv, intel_cap_dev->pcidev, &disc_table, disc_res); 64862306a36Sopenharmony_ci if (ret) 64962306a36Sopenharmony_ci return ret; 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci return 0; 65262306a36Sopenharmony_ci} 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_cistatic const struct auxiliary_device_id sdsi_aux_id_table[] = { 65562306a36Sopenharmony_ci { .name = "intel_vsec.sdsi" }, 65662306a36Sopenharmony_ci {} 65762306a36Sopenharmony_ci}; 65862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(auxiliary, sdsi_aux_id_table); 65962306a36Sopenharmony_ci 66062306a36Sopenharmony_cistatic struct auxiliary_driver sdsi_aux_driver = { 66162306a36Sopenharmony_ci .driver = { 66262306a36Sopenharmony_ci .dev_groups = sdsi_groups, 66362306a36Sopenharmony_ci }, 66462306a36Sopenharmony_ci .id_table = sdsi_aux_id_table, 66562306a36Sopenharmony_ci .probe = sdsi_probe, 66662306a36Sopenharmony_ci /* No remove. All resources are handled under devm */ 66762306a36Sopenharmony_ci}; 66862306a36Sopenharmony_cimodule_auxiliary_driver(sdsi_aux_driver); 66962306a36Sopenharmony_ci 67062306a36Sopenharmony_ciMODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>"); 67162306a36Sopenharmony_ciMODULE_DESCRIPTION("Intel On Demand (SDSi) driver"); 67262306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 673