xref: /openbmc/linux/drivers/platform/x86/intel/sdsi.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
12546c600SDavid E. Box // SPDX-License-Identifier: GPL-2.0
22546c600SDavid E. Box /*
34ea62915SDavid E. Box  * Intel On Demand (Software Defined Silicon) driver
42546c600SDavid E. Box  *
52546c600SDavid E. Box  * Copyright (c) 2022, Intel Corporation.
62546c600SDavid E. Box  * All Rights Reserved.
72546c600SDavid E. Box  *
82546c600SDavid E. Box  * Author: "David E. Box" <david.e.box@linux.intel.com>
92546c600SDavid E. Box  */
102546c600SDavid E. Box 
112546c600SDavid E. Box #include <linux/auxiliary_bus.h>
122546c600SDavid E. Box #include <linux/bits.h>
132546c600SDavid E. Box #include <linux/bitfield.h>
142546c600SDavid E. Box #include <linux/device.h>
152546c600SDavid E. Box #include <linux/iopoll.h>
162546c600SDavid E. Box #include <linux/kernel.h>
172546c600SDavid E. Box #include <linux/module.h>
182546c600SDavid E. Box #include <linux/pci.h>
192546c600SDavid E. Box #include <linux/slab.h>
202546c600SDavid E. Box #include <linux/sysfs.h>
212546c600SDavid E. Box #include <linux/types.h>
222546c600SDavid E. Box #include <linux/uaccess.h>
232546c600SDavid E. Box 
242546c600SDavid E. Box #include "vsec.h"
252546c600SDavid E. Box 
262546c600SDavid E. Box #define ACCESS_TYPE_BARID		2
272546c600SDavid E. Box #define ACCESS_TYPE_LOCAL		3
282546c600SDavid E. Box 
292546c600SDavid E. Box #define SDSI_MIN_SIZE_DWORDS		276
302546c600SDavid E. Box #define SDSI_SIZE_MAILBOX		1024
3125612c0fSDavid E. Box #define SDSI_SIZE_REGS			80
322546c600SDavid E. Box #define SDSI_SIZE_CMD			sizeof(u64)
332546c600SDavid E. Box 
342546c600SDavid E. Box /*
352546c600SDavid E. Box  * Write messages are currently up to the size of the mailbox
362546c600SDavid E. Box  * while read messages are up to 4 times the size of the
372546c600SDavid E. Box  * mailbox, sent in packets
382546c600SDavid E. Box  */
392546c600SDavid E. Box #define SDSI_SIZE_WRITE_MSG		SDSI_SIZE_MAILBOX
402546c600SDavid E. Box #define SDSI_SIZE_READ_MSG		(SDSI_SIZE_MAILBOX * 4)
412546c600SDavid E. Box 
422546c600SDavid E. Box #define SDSI_ENABLED_FEATURES_OFFSET	16
43aa546b28SDavid E. Box #define SDSI_FEATURE_SDSI		BIT(3)
44a96f1b9cSDavid E. Box #define SDSI_FEATURE_METERING		BIT(26)
45aa546b28SDavid E. Box 
462546c600SDavid E. Box #define SDSI_SOCKET_ID_OFFSET		64
472546c600SDavid E. Box #define SDSI_SOCKET_ID			GENMASK(3, 0)
482546c600SDavid E. Box 
492546c600SDavid E. Box #define SDSI_MBOX_CMD_SUCCESS		0x40
502546c600SDavid E. Box #define SDSI_MBOX_CMD_TIMEOUT		0x80
512546c600SDavid E. Box 
52*14f6f0e3SDavid E. Box #define MBOX_TIMEOUT_US			500000
532546c600SDavid E. Box #define MBOX_TIMEOUT_ACQUIRE_US		1000
542546c600SDavid E. Box #define MBOX_POLLING_PERIOD_US		100
55679c7a3fSDavid E. Box #define MBOX_ACQUIRE_NUM_RETRIES	5
56679c7a3fSDavid E. Box #define MBOX_ACQUIRE_RETRY_DELAY_MS	500
572546c600SDavid E. Box #define MBOX_MAX_PACKETS		4
582546c600SDavid E. Box 
592546c600SDavid E. Box #define MBOX_OWNER_NONE			0x00
602546c600SDavid E. Box #define MBOX_OWNER_INBAND		0x01
612546c600SDavid E. Box 
622546c600SDavid E. Box #define CTRL_RUN_BUSY			BIT(0)
632546c600SDavid E. Box #define CTRL_READ_WRITE			BIT(1)
642546c600SDavid E. Box #define CTRL_SOM			BIT(2)
652546c600SDavid E. Box #define CTRL_EOM			BIT(3)
662546c600SDavid E. Box #define CTRL_OWNER			GENMASK(5, 4)
672546c600SDavid E. Box #define CTRL_COMPLETE			BIT(6)
682546c600SDavid E. Box #define CTRL_READY			BIT(7)
692546c600SDavid E. Box #define CTRL_STATUS			GENMASK(15, 8)
702546c600SDavid E. Box #define CTRL_PACKET_SIZE		GENMASK(31, 16)
712546c600SDavid E. Box #define CTRL_MSG_SIZE			GENMASK(63, 48)
722546c600SDavid E. Box 
732546c600SDavid E. Box #define DISC_TABLE_SIZE			12
742546c600SDavid E. Box #define DT_ACCESS_TYPE			GENMASK(3, 0)
752546c600SDavid E. Box #define DT_SIZE				GENMASK(27, 12)
762546c600SDavid E. Box #define DT_TBIR				GENMASK(2, 0)
772546c600SDavid E. Box #define DT_OFFSET(v)			((v) & GENMASK(31, 3))
782546c600SDavid E. Box 
7925612c0fSDavid E. Box #define SDSI_GUID_V1			0x006DD191
8025612c0fSDavid E. Box #define GUID_V1_CNTRL_SIZE		8
8125612c0fSDavid E. Box #define GUID_V1_REGS_SIZE		72
8225612c0fSDavid E. Box #define SDSI_GUID_V2			0xF210D9EF
8325612c0fSDavid E. Box #define GUID_V2_CNTRL_SIZE		16
8425612c0fSDavid E. Box #define GUID_V2_REGS_SIZE		80
8525612c0fSDavid E. Box 
862546c600SDavid E. Box enum sdsi_command {
87a96f1b9cSDavid E. Box 	SDSI_CMD_PROVISION_AKC		= 0x0004,
88a96f1b9cSDavid E. Box 	SDSI_CMD_PROVISION_CAP		= 0x0008,
89a96f1b9cSDavid E. Box 	SDSI_CMD_READ_STATE		= 0x0010,
90a96f1b9cSDavid E. Box 	SDSI_CMD_READ_METER		= 0x0014,
912546c600SDavid E. Box };
922546c600SDavid E. Box 
932546c600SDavid E. Box struct sdsi_mbox_info {
942546c600SDavid E. Box 	u64	*payload;
9500dd3aceSDavid E. Box 	void	*buffer;
962546c600SDavid E. Box 	int	size;
972546c600SDavid E. Box };
982546c600SDavid E. Box 
992546c600SDavid E. Box struct disc_table {
1002546c600SDavid E. Box 	u32	access_info;
1012546c600SDavid E. Box 	u32	guid;
1022546c600SDavid E. Box 	u32	offset;
1032546c600SDavid E. Box };
1042546c600SDavid E. Box 
1052546c600SDavid E. Box struct sdsi_priv {
1062546c600SDavid E. Box 	struct mutex		mb_lock;	/* Mailbox access lock */
1072546c600SDavid E. Box 	struct device		*dev;
1082546c600SDavid E. Box 	void __iomem		*control_addr;
1092546c600SDavid E. Box 	void __iomem		*mbox_addr;
1102546c600SDavid E. Box 	void __iomem		*regs_addr;
11125612c0fSDavid E. Box 	int			control_size;
11225612c0fSDavid E. Box 	int			maibox_size;
11325612c0fSDavid E. Box 	int			registers_size;
1142546c600SDavid E. Box 	u32			guid;
115aa546b28SDavid E. Box 	u32			features;
1162546c600SDavid E. Box };
1172546c600SDavid E. Box 
1182546c600SDavid E. Box /* SDSi mailbox operations must be performed using 64bit mov instructions */
1192546c600SDavid E. Box static __always_inline void
sdsi_memcpy64_toio(u64 __iomem * to,const u64 * from,size_t count_bytes)1202546c600SDavid E. Box sdsi_memcpy64_toio(u64 __iomem *to, const u64 *from, size_t count_bytes)
1212546c600SDavid E. Box {
1222546c600SDavid E. Box 	size_t count = count_bytes / sizeof(*to);
1232546c600SDavid E. Box 	int i;
1242546c600SDavid E. Box 
1252546c600SDavid E. Box 	for (i = 0; i < count; i++)
1262546c600SDavid E. Box 		writeq(from[i], &to[i]);
1272546c600SDavid E. Box }
1282546c600SDavid E. Box 
1292546c600SDavid E. Box static __always_inline void
sdsi_memcpy64_fromio(u64 * to,const u64 __iomem * from,size_t count_bytes)1302546c600SDavid E. Box sdsi_memcpy64_fromio(u64 *to, const u64 __iomem *from, size_t count_bytes)
1312546c600SDavid E. Box {
1322546c600SDavid E. Box 	size_t count = count_bytes / sizeof(*to);
1332546c600SDavid E. Box 	int i;
1342546c600SDavid E. Box 
1352546c600SDavid E. Box 	for (i = 0; i < count; i++)
1362546c600SDavid E. Box 		to[i] = readq(&from[i]);
1372546c600SDavid E. Box }
1382546c600SDavid E. Box 
sdsi_complete_transaction(struct sdsi_priv * priv)1392546c600SDavid E. Box static inline void sdsi_complete_transaction(struct sdsi_priv *priv)
1402546c600SDavid E. Box {
1412546c600SDavid E. Box 	u64 control = FIELD_PREP(CTRL_COMPLETE, 1);
1422546c600SDavid E. Box 
1432546c600SDavid E. Box 	lockdep_assert_held(&priv->mb_lock);
1442546c600SDavid E. Box 	writeq(control, priv->control_addr);
1452546c600SDavid E. Box }
1462546c600SDavid E. Box 
sdsi_status_to_errno(u32 status)1472546c600SDavid E. Box static int sdsi_status_to_errno(u32 status)
1482546c600SDavid E. Box {
1492546c600SDavid E. Box 	switch (status) {
1502546c600SDavid E. Box 	case SDSI_MBOX_CMD_SUCCESS:
1512546c600SDavid E. Box 		return 0;
1522546c600SDavid E. Box 	case SDSI_MBOX_CMD_TIMEOUT:
1532546c600SDavid E. Box 		return -ETIMEDOUT;
1542546c600SDavid E. Box 	default:
1552546c600SDavid E. Box 		return -EIO;
1562546c600SDavid E. Box 	}
1572546c600SDavid E. Box }
1582546c600SDavid E. Box 
sdsi_mbox_cmd_read(struct sdsi_priv * priv,struct sdsi_mbox_info * info,size_t * data_size)1592546c600SDavid E. Box static int sdsi_mbox_cmd_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info,
1602546c600SDavid E. Box 			      size_t *data_size)
1612546c600SDavid E. Box {
1622546c600SDavid E. Box 	struct device *dev = priv->dev;
1632546c600SDavid E. Box 	u32 total, loop, eom, status, message_size;
1642546c600SDavid E. Box 	u64 control;
1652546c600SDavid E. Box 	int ret;
1662546c600SDavid E. Box 
1672546c600SDavid E. Box 	lockdep_assert_held(&priv->mb_lock);
1682546c600SDavid E. Box 
1692546c600SDavid E. Box 	/* Format and send the read command */
1702546c600SDavid E. Box 	control = FIELD_PREP(CTRL_EOM, 1) |
1712546c600SDavid E. Box 		  FIELD_PREP(CTRL_SOM, 1) |
1722546c600SDavid E. Box 		  FIELD_PREP(CTRL_RUN_BUSY, 1) |
1732546c600SDavid E. Box 		  FIELD_PREP(CTRL_PACKET_SIZE, info->size);
1742546c600SDavid E. Box 	writeq(control, priv->control_addr);
1752546c600SDavid E. Box 
1762546c600SDavid E. Box 	/* For reads, data sizes that are larger than the mailbox size are read in packets. */
1772546c600SDavid E. Box 	total = 0;
1782546c600SDavid E. Box 	loop = 0;
1792546c600SDavid E. Box 	do {
18000dd3aceSDavid E. Box 		void *buf = info->buffer + (SDSI_SIZE_MAILBOX * loop);
1812546c600SDavid E. Box 		u32 packet_size;
1822546c600SDavid E. Box 
1832546c600SDavid E. Box 		/* Poll on ready bit */
1842546c600SDavid E. Box 		ret = readq_poll_timeout(priv->control_addr, control, control & CTRL_READY,
1852546c600SDavid E. Box 					 MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_US);
1862546c600SDavid E. Box 		if (ret)
1872546c600SDavid E. Box 			break;
1882546c600SDavid E. Box 
1892546c600SDavid E. Box 		eom = FIELD_GET(CTRL_EOM, control);
1902546c600SDavid E. Box 		status = FIELD_GET(CTRL_STATUS, control);
1912546c600SDavid E. Box 		packet_size = FIELD_GET(CTRL_PACKET_SIZE, control);
1922546c600SDavid E. Box 		message_size = FIELD_GET(CTRL_MSG_SIZE, control);
1932546c600SDavid E. Box 
1942546c600SDavid E. Box 		ret = sdsi_status_to_errno(status);
1952546c600SDavid E. Box 		if (ret)
1962546c600SDavid E. Box 			break;
1972546c600SDavid E. Box 
1982546c600SDavid E. Box 		/* Only the last packet can be less than the mailbox size. */
1992546c600SDavid E. Box 		if (!eom && packet_size != SDSI_SIZE_MAILBOX) {
2002546c600SDavid E. Box 			dev_err(dev, "Invalid packet size\n");
2012546c600SDavid E. Box 			ret = -EPROTO;
2022546c600SDavid E. Box 			break;
2032546c600SDavid E. Box 		}
2042546c600SDavid E. Box 
2052546c600SDavid E. Box 		if (packet_size > SDSI_SIZE_MAILBOX) {
2062546c600SDavid E. Box 			dev_err(dev, "Packet size too large\n");
2072546c600SDavid E. Box 			ret = -EPROTO;
2082546c600SDavid E. Box 			break;
2092546c600SDavid E. Box 		}
2102546c600SDavid E. Box 
21100dd3aceSDavid E. Box 		sdsi_memcpy64_fromio(buf, priv->mbox_addr, round_up(packet_size, SDSI_SIZE_CMD));
2122546c600SDavid E. Box 
2132546c600SDavid E. Box 		total += packet_size;
2142546c600SDavid E. Box 
2152546c600SDavid E. Box 		sdsi_complete_transaction(priv);
2162546c600SDavid E. Box 	} while (!eom && ++loop < MBOX_MAX_PACKETS);
2172546c600SDavid E. Box 
2182546c600SDavid E. Box 	if (ret) {
2192546c600SDavid E. Box 		sdsi_complete_transaction(priv);
2202546c600SDavid E. Box 		return ret;
2212546c600SDavid E. Box 	}
2222546c600SDavid E. Box 
2232546c600SDavid E. Box 	if (!eom) {
2242546c600SDavid E. Box 		dev_err(dev, "Exceeded read attempts\n");
2252546c600SDavid E. Box 		return -EPROTO;
2262546c600SDavid E. Box 	}
2272546c600SDavid E. Box 
2282546c600SDavid E. Box 	/* Message size check is only valid for multi-packet transfers */
2292546c600SDavid E. Box 	if (loop && total != message_size)
2302546c600SDavid E. Box 		dev_warn(dev, "Read count %u differs from expected count %u\n",
2312546c600SDavid E. Box 			 total, message_size);
2322546c600SDavid E. Box 
2332546c600SDavid E. Box 	*data_size = total;
2342546c600SDavid E. Box 
2352546c600SDavid E. Box 	return 0;
2362546c600SDavid E. Box }
2372546c600SDavid E. Box 
sdsi_mbox_cmd_write(struct sdsi_priv * priv,struct sdsi_mbox_info * info)2382546c600SDavid E. Box static int sdsi_mbox_cmd_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
2392546c600SDavid E. Box {
2402546c600SDavid E. Box 	u64 control;
2412546c600SDavid E. Box 	u32 status;
2422546c600SDavid E. Box 	int ret;
2432546c600SDavid E. Box 
2442546c600SDavid E. Box 	lockdep_assert_held(&priv->mb_lock);
2452546c600SDavid E. Box 
2462546c600SDavid E. Box 	/* Write rest of the payload */
2472546c600SDavid E. Box 	sdsi_memcpy64_toio(priv->mbox_addr + SDSI_SIZE_CMD, info->payload + 1,
2482546c600SDavid E. Box 			   info->size - SDSI_SIZE_CMD);
2492546c600SDavid E. Box 
2502546c600SDavid E. Box 	/* Format and send the write command */
2512546c600SDavid E. Box 	control = FIELD_PREP(CTRL_EOM, 1) |
2522546c600SDavid E. Box 		  FIELD_PREP(CTRL_SOM, 1) |
2532546c600SDavid E. Box 		  FIELD_PREP(CTRL_RUN_BUSY, 1) |
2542546c600SDavid E. Box 		  FIELD_PREP(CTRL_READ_WRITE, 1) |
2552546c600SDavid E. Box 		  FIELD_PREP(CTRL_PACKET_SIZE, info->size);
2562546c600SDavid E. Box 	writeq(control, priv->control_addr);
2572546c600SDavid E. Box 
258a30393b3SDavid E. Box 	/* Poll on ready bit */
259a30393b3SDavid E. Box 	ret = readq_poll_timeout(priv->control_addr, control, control & CTRL_READY,
2602546c600SDavid E. Box 				 MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_US);
2612546c600SDavid E. Box 
2622546c600SDavid E. Box 	if (ret)
2632546c600SDavid E. Box 		goto release_mbox;
2642546c600SDavid E. Box 
2652546c600SDavid E. Box 	status = FIELD_GET(CTRL_STATUS, control);
2662546c600SDavid E. Box 	ret = sdsi_status_to_errno(status);
2672546c600SDavid E. Box 
2682546c600SDavid E. Box release_mbox:
2692546c600SDavid E. Box 	sdsi_complete_transaction(priv);
2702546c600SDavid E. Box 
2712546c600SDavid E. Box 	return ret;
2722546c600SDavid E. Box }
2732546c600SDavid E. Box 
sdsi_mbox_acquire(struct sdsi_priv * priv,struct sdsi_mbox_info * info)2742546c600SDavid E. Box static int sdsi_mbox_acquire(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
2752546c600SDavid E. Box {
2762546c600SDavid E. Box 	u64 control;
2772546c600SDavid E. Box 	u32 owner;
278679c7a3fSDavid E. Box 	int ret, retries = 0;
2792546c600SDavid E. Box 
2802546c600SDavid E. Box 	lockdep_assert_held(&priv->mb_lock);
2812546c600SDavid E. Box 
2822546c600SDavid E. Box 	/* Check mailbox is available */
2832546c600SDavid E. Box 	control = readq(priv->control_addr);
2842546c600SDavid E. Box 	owner = FIELD_GET(CTRL_OWNER, control);
2852546c600SDavid E. Box 	if (owner != MBOX_OWNER_NONE)
2862546c600SDavid E. Box 		return -EBUSY;
2872546c600SDavid E. Box 
288679c7a3fSDavid E. Box 	/*
289679c7a3fSDavid E. Box 	 * If there has been no recent transaction and no one owns the mailbox,
290679c7a3fSDavid E. Box 	 * we should acquire it in under 1ms. However, if we've accessed it
291679c7a3fSDavid E. Box 	 * recently it may take up to 2.1 seconds to acquire it again.
292679c7a3fSDavid E. Box 	 */
293679c7a3fSDavid E. Box 	do {
2942546c600SDavid E. Box 		/* Write first qword of payload */
2952546c600SDavid E. Box 		writeq(info->payload[0], priv->mbox_addr);
2962546c600SDavid E. Box 
2972546c600SDavid E. Box 		/* Check for ownership */
2982546c600SDavid E. Box 		ret = readq_poll_timeout(priv->control_addr, control,
299679c7a3fSDavid E. Box 			FIELD_GET(CTRL_OWNER, control) == MBOX_OWNER_INBAND,
3002546c600SDavid E. Box 			MBOX_POLLING_PERIOD_US, MBOX_TIMEOUT_ACQUIRE_US);
3012546c600SDavid E. Box 
302679c7a3fSDavid E. Box 		if (FIELD_GET(CTRL_OWNER, control) == MBOX_OWNER_NONE &&
303679c7a3fSDavid E. Box 		    retries++ < MBOX_ACQUIRE_NUM_RETRIES) {
304679c7a3fSDavid E. Box 			msleep(MBOX_ACQUIRE_RETRY_DELAY_MS);
305679c7a3fSDavid E. Box 			continue;
306679c7a3fSDavid E. Box 		}
307679c7a3fSDavid E. Box 
308679c7a3fSDavid E. Box 		/* Either we got it or someone else did. */
309679c7a3fSDavid E. Box 		break;
310679c7a3fSDavid E. Box 	} while (true);
311679c7a3fSDavid E. Box 
3122546c600SDavid E. Box 	return ret;
3132546c600SDavid E. Box }
3142546c600SDavid E. Box 
sdsi_mbox_write(struct sdsi_priv * priv,struct sdsi_mbox_info * info)3152546c600SDavid E. Box static int sdsi_mbox_write(struct sdsi_priv *priv, struct sdsi_mbox_info *info)
3162546c600SDavid E. Box {
3172546c600SDavid E. Box 	int ret;
3182546c600SDavid E. Box 
3192546c600SDavid E. Box 	lockdep_assert_held(&priv->mb_lock);
3202546c600SDavid E. Box 
3212546c600SDavid E. Box 	ret = sdsi_mbox_acquire(priv, info);
3222546c600SDavid E. Box 	if (ret)
3232546c600SDavid E. Box 		return ret;
3242546c600SDavid E. Box 
3252546c600SDavid E. Box 	return sdsi_mbox_cmd_write(priv, info);
3262546c600SDavid E. Box }
3272546c600SDavid E. Box 
sdsi_mbox_read(struct sdsi_priv * priv,struct sdsi_mbox_info * info,size_t * data_size)3282546c600SDavid E. Box static int sdsi_mbox_read(struct sdsi_priv *priv, struct sdsi_mbox_info *info, size_t *data_size)
3292546c600SDavid E. Box {
3302546c600SDavid E. Box 	int ret;
3312546c600SDavid E. Box 
3322546c600SDavid E. Box 	lockdep_assert_held(&priv->mb_lock);
3332546c600SDavid E. Box 
3342546c600SDavid E. Box 	ret = sdsi_mbox_acquire(priv, info);
3352546c600SDavid E. Box 	if (ret)
3362546c600SDavid E. Box 		return ret;
3372546c600SDavid E. Box 
3382546c600SDavid E. Box 	return sdsi_mbox_cmd_read(priv, info, data_size);
3392546c600SDavid E. Box }
3402546c600SDavid E. Box 
sdsi_provision(struct sdsi_priv * priv,char * buf,size_t count,enum sdsi_command command)3412546c600SDavid E. Box static ssize_t sdsi_provision(struct sdsi_priv *priv, char *buf, size_t count,
3422546c600SDavid E. Box 			      enum sdsi_command command)
3432546c600SDavid E. Box {
3442546c600SDavid E. Box 	struct sdsi_mbox_info info;
3452546c600SDavid E. Box 	int ret;
3462546c600SDavid E. Box 
3472546c600SDavid E. Box 	if (count > (SDSI_SIZE_WRITE_MSG - SDSI_SIZE_CMD))
3482546c600SDavid E. Box 		return -EOVERFLOW;
3492546c600SDavid E. Box 
3502546c600SDavid E. Box 	/* Qword aligned message + command qword */
3512546c600SDavid E. Box 	info.size = round_up(count, SDSI_SIZE_CMD) + SDSI_SIZE_CMD;
3522546c600SDavid E. Box 
3532546c600SDavid E. Box 	info.payload = kzalloc(info.size, GFP_KERNEL);
3542546c600SDavid E. Box 	if (!info.payload)
3552546c600SDavid E. Box 		return -ENOMEM;
3562546c600SDavid E. Box 
3572546c600SDavid E. Box 	/* Copy message to payload buffer */
3582546c600SDavid E. Box 	memcpy(info.payload, buf, count);
3592546c600SDavid E. Box 
3602546c600SDavid E. Box 	/* Command is last qword of payload buffer */
3612546c600SDavid E. Box 	info.payload[(info.size - SDSI_SIZE_CMD) / SDSI_SIZE_CMD] = command;
3622546c600SDavid E. Box 
3632546c600SDavid E. Box 	ret = mutex_lock_interruptible(&priv->mb_lock);
3642546c600SDavid E. Box 	if (ret)
3652546c600SDavid E. Box 		goto free_payload;
3662546c600SDavid E. Box 	ret = sdsi_mbox_write(priv, &info);
3672546c600SDavid E. Box 	mutex_unlock(&priv->mb_lock);
3682546c600SDavid E. Box 
3692546c600SDavid E. Box free_payload:
3702546c600SDavid E. Box 	kfree(info.payload);
3712546c600SDavid E. Box 
3722546c600SDavid E. Box 	if (ret)
3732546c600SDavid E. Box 		return ret;
3742546c600SDavid E. Box 
3752546c600SDavid E. Box 	return count;
3762546c600SDavid E. Box }
3772546c600SDavid E. Box 
provision_akc_write(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,char * buf,loff_t off,size_t count)3782546c600SDavid E. Box static ssize_t provision_akc_write(struct file *filp, struct kobject *kobj,
3792546c600SDavid E. Box 				   struct bin_attribute *attr, char *buf, loff_t off,
3802546c600SDavid E. Box 				   size_t count)
3812546c600SDavid E. Box {
3822546c600SDavid E. Box 	struct device *dev = kobj_to_dev(kobj);
3832546c600SDavid E. Box 	struct sdsi_priv *priv = dev_get_drvdata(dev);
3842546c600SDavid E. Box 
3852546c600SDavid E. Box 	if (off)
3862546c600SDavid E. Box 		return -ESPIPE;
3872546c600SDavid E. Box 
3882546c600SDavid E. Box 	return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_AKC);
3892546c600SDavid E. Box }
3902546c600SDavid E. Box static BIN_ATTR_WO(provision_akc, SDSI_SIZE_WRITE_MSG);
3912546c600SDavid E. Box 
provision_cap_write(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,char * buf,loff_t off,size_t count)3922546c600SDavid E. Box static ssize_t provision_cap_write(struct file *filp, struct kobject *kobj,
3932546c600SDavid E. Box 				   struct bin_attribute *attr, char *buf, loff_t off,
3942546c600SDavid E. Box 				   size_t count)
3952546c600SDavid E. Box {
3962546c600SDavid E. Box 	struct device *dev = kobj_to_dev(kobj);
3972546c600SDavid E. Box 	struct sdsi_priv *priv = dev_get_drvdata(dev);
3982546c600SDavid E. Box 
3992546c600SDavid E. Box 	if (off)
4002546c600SDavid E. Box 		return -ESPIPE;
4012546c600SDavid E. Box 
4022546c600SDavid E. Box 	return sdsi_provision(priv, buf, count, SDSI_CMD_PROVISION_CAP);
4032546c600SDavid E. Box }
4042546c600SDavid E. Box static BIN_ATTR_WO(provision_cap, SDSI_SIZE_WRITE_MSG);
4052546c600SDavid E. Box 
406a96f1b9cSDavid E. Box static ssize_t
certificate_read(u64 command,struct sdsi_priv * priv,char * buf,loff_t off,size_t count)407a96f1b9cSDavid E. Box certificate_read(u64 command, struct sdsi_priv *priv, char *buf, loff_t off,
4082546c600SDavid E. Box 		 size_t count)
4092546c600SDavid E. Box {
4102546c600SDavid E. Box 	struct sdsi_mbox_info info;
4112546c600SDavid E. Box 	size_t size;
4122546c600SDavid E. Box 	int ret;
4132546c600SDavid E. Box 
4142546c600SDavid E. Box 	if (off)
4152546c600SDavid E. Box 		return 0;
4162546c600SDavid E. Box 
4172546c600SDavid E. Box 	/* Buffer for return data */
4182546c600SDavid E. Box 	info.buffer = kmalloc(SDSI_SIZE_READ_MSG, GFP_KERNEL);
4192546c600SDavid E. Box 	if (!info.buffer)
4202546c600SDavid E. Box 		return -ENOMEM;
4212546c600SDavid E. Box 
4222546c600SDavid E. Box 	info.payload = &command;
4232546c600SDavid E. Box 	info.size = sizeof(command);
4242546c600SDavid E. Box 
4252546c600SDavid E. Box 	ret = mutex_lock_interruptible(&priv->mb_lock);
4262546c600SDavid E. Box 	if (ret)
4272546c600SDavid E. Box 		goto free_buffer;
4282546c600SDavid E. Box 	ret = sdsi_mbox_read(priv, &info, &size);
4292546c600SDavid E. Box 	mutex_unlock(&priv->mb_lock);
4302546c600SDavid E. Box 	if (ret < 0)
4312546c600SDavid E. Box 		goto free_buffer;
4322546c600SDavid E. Box 
4332546c600SDavid E. Box 	if (size > count)
4342546c600SDavid E. Box 		size = count;
4352546c600SDavid E. Box 
4362546c600SDavid E. Box 	memcpy(buf, info.buffer, size);
4372546c600SDavid E. Box 
4382546c600SDavid E. Box free_buffer:
4392546c600SDavid E. Box 	kfree(info.buffer);
4402546c600SDavid E. Box 
4412546c600SDavid E. Box 	if (ret)
4422546c600SDavid E. Box 		return ret;
4432546c600SDavid E. Box 
4442546c600SDavid E. Box 	return size;
4452546c600SDavid E. Box }
446a96f1b9cSDavid E. Box 
447a96f1b9cSDavid E. Box static ssize_t
state_certificate_read(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,char * buf,loff_t off,size_t count)448a96f1b9cSDavid E. Box state_certificate_read(struct file *filp, struct kobject *kobj,
449a96f1b9cSDavid E. Box 		       struct bin_attribute *attr, char *buf, loff_t off,
450a96f1b9cSDavid E. Box 		       size_t count)
451a96f1b9cSDavid E. Box {
452a96f1b9cSDavid E. Box 	struct device *dev = kobj_to_dev(kobj);
453a96f1b9cSDavid E. Box 	struct sdsi_priv *priv = dev_get_drvdata(dev);
454a96f1b9cSDavid E. Box 
455a96f1b9cSDavid E. Box 	return certificate_read(SDSI_CMD_READ_STATE, priv, buf, off, count);
456a96f1b9cSDavid E. Box }
457a96f1b9cSDavid E. Box static BIN_ATTR_ADMIN_RO(state_certificate, SDSI_SIZE_READ_MSG);
458a96f1b9cSDavid E. Box 
459a96f1b9cSDavid E. Box static ssize_t
meter_certificate_read(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,char * buf,loff_t off,size_t count)460a96f1b9cSDavid E. Box meter_certificate_read(struct file *filp, struct kobject *kobj,
461a96f1b9cSDavid E. Box 		       struct bin_attribute *attr, char *buf, loff_t off,
462a96f1b9cSDavid E. Box 		       size_t count)
463a96f1b9cSDavid E. Box {
464a96f1b9cSDavid E. Box 	struct device *dev = kobj_to_dev(kobj);
465a96f1b9cSDavid E. Box 	struct sdsi_priv *priv = dev_get_drvdata(dev);
466a96f1b9cSDavid E. Box 
467a96f1b9cSDavid E. Box 	return certificate_read(SDSI_CMD_READ_METER, priv, buf, off, count);
468a96f1b9cSDavid E. Box }
469a96f1b9cSDavid E. Box static BIN_ATTR_ADMIN_RO(meter_certificate, SDSI_SIZE_READ_MSG);
4702546c600SDavid E. Box 
registers_read(struct file * filp,struct kobject * kobj,struct bin_attribute * attr,char * buf,loff_t off,size_t count)4712546c600SDavid E. Box static ssize_t registers_read(struct file *filp, struct kobject *kobj,
4722546c600SDavid E. Box 			      struct bin_attribute *attr, char *buf, loff_t off,
4732546c600SDavid E. Box 			      size_t count)
4742546c600SDavid E. Box {
4752546c600SDavid E. Box 	struct device *dev = kobj_to_dev(kobj);
4762546c600SDavid E. Box 	struct sdsi_priv *priv = dev_get_drvdata(dev);
4772546c600SDavid E. Box 	void __iomem *addr = priv->regs_addr;
47825612c0fSDavid E. Box 	int size =  priv->registers_size;
47925612c0fSDavid E. Box 
48025612c0fSDavid E. Box 	/*
48125612c0fSDavid E. Box 	 * The check below is performed by the sysfs caller based on the static
48225612c0fSDavid E. Box 	 * file size. But this may be greater than the actual size which is based
48325612c0fSDavid E. Box 	 * on the GUID. So check here again based on actual size before reading.
48425612c0fSDavid E. Box 	 */
48525612c0fSDavid E. Box 	if (off >= size)
48625612c0fSDavid E. Box 		return 0;
48725612c0fSDavid E. Box 
48825612c0fSDavid E. Box 	if (off + count > size)
48925612c0fSDavid E. Box 		count = size - off;
4902546c600SDavid E. Box 
4912546c600SDavid E. Box 	memcpy_fromio(buf, addr + off, count);
4922546c600SDavid E. Box 
4932546c600SDavid E. Box 	return count;
4942546c600SDavid E. Box }
495a96f1b9cSDavid E. Box static BIN_ATTR_ADMIN_RO(registers, SDSI_SIZE_REGS);
4962546c600SDavid E. Box 
4972546c600SDavid E. Box static struct bin_attribute *sdsi_bin_attrs[] = {
4982546c600SDavid E. Box 	&bin_attr_registers,
4992546c600SDavid E. Box 	&bin_attr_state_certificate,
500a96f1b9cSDavid E. Box 	&bin_attr_meter_certificate,
5012546c600SDavid E. Box 	&bin_attr_provision_akc,
5022546c600SDavid E. Box 	&bin_attr_provision_cap,
5032546c600SDavid E. Box 	NULL
5042546c600SDavid E. Box };
5052546c600SDavid E. Box 
506aa546b28SDavid E. Box static umode_t
sdsi_battr_is_visible(struct kobject * kobj,struct bin_attribute * attr,int n)507aa546b28SDavid E. Box sdsi_battr_is_visible(struct kobject *kobj, struct bin_attribute *attr, int n)
508aa546b28SDavid E. Box {
509aa546b28SDavid E. Box 	struct device *dev = kobj_to_dev(kobj);
510aa546b28SDavid E. Box 	struct sdsi_priv *priv = dev_get_drvdata(dev);
511aa546b28SDavid E. Box 
512aa546b28SDavid E. Box 	/* Registers file is always readable if the device is present */
513aa546b28SDavid E. Box 	if (attr == &bin_attr_registers)
514aa546b28SDavid E. Box 		return attr->attr.mode;
515aa546b28SDavid E. Box 
516aa546b28SDavid E. Box 	/* All other attributes not visible if BIOS has not enabled On Demand */
517aa546b28SDavid E. Box 	if (!(priv->features & SDSI_FEATURE_SDSI))
518aa546b28SDavid E. Box 		return 0;
519aa546b28SDavid E. Box 
520a96f1b9cSDavid E. Box 	if (attr == &bin_attr_meter_certificate)
521a96f1b9cSDavid E. Box 		return (priv->features & SDSI_FEATURE_METERING) ?
522a96f1b9cSDavid E. Box 				attr->attr.mode : 0;
523a96f1b9cSDavid E. Box 
524aa546b28SDavid E. Box 	return attr->attr.mode;
525aa546b28SDavid E. Box }
526aa546b28SDavid E. Box 
guid_show(struct device * dev,struct device_attribute * attr,char * buf)5272546c600SDavid E. Box static ssize_t guid_show(struct device *dev, struct device_attribute *attr, char *buf)
5282546c600SDavid E. Box {
5292546c600SDavid E. Box 	struct sdsi_priv *priv = dev_get_drvdata(dev);
5302546c600SDavid E. Box 
5312546c600SDavid E. Box 	return sysfs_emit(buf, "0x%x\n", priv->guid);
5322546c600SDavid E. Box }
5332546c600SDavid E. Box static DEVICE_ATTR_RO(guid);
5342546c600SDavid E. Box 
5352546c600SDavid E. Box static struct attribute *sdsi_attrs[] = {
5362546c600SDavid E. Box 	&dev_attr_guid.attr,
5372546c600SDavid E. Box 	NULL
5382546c600SDavid E. Box };
5392546c600SDavid E. Box 
5402546c600SDavid E. Box static const struct attribute_group sdsi_group = {
5412546c600SDavid E. Box 	.attrs = sdsi_attrs,
5422546c600SDavid E. Box 	.bin_attrs = sdsi_bin_attrs,
543aa546b28SDavid E. Box 	.is_bin_visible = sdsi_battr_is_visible,
5442546c600SDavid E. Box };
5452546c600SDavid E. Box __ATTRIBUTE_GROUPS(sdsi);
5462546c600SDavid E. Box 
sdsi_get_layout(struct sdsi_priv * priv,struct disc_table * table)54725612c0fSDavid E. Box static int sdsi_get_layout(struct sdsi_priv *priv, struct disc_table *table)
54825612c0fSDavid E. Box {
54925612c0fSDavid E. Box 	switch (table->guid) {
55025612c0fSDavid E. Box 	case SDSI_GUID_V1:
55125612c0fSDavid E. Box 		priv->control_size = GUID_V1_CNTRL_SIZE;
55225612c0fSDavid E. Box 		priv->registers_size = GUID_V1_REGS_SIZE;
55325612c0fSDavid E. Box 		break;
55425612c0fSDavid E. Box 	case SDSI_GUID_V2:
55525612c0fSDavid E. Box 		priv->control_size = GUID_V2_CNTRL_SIZE;
55625612c0fSDavid E. Box 		priv->registers_size = GUID_V2_REGS_SIZE;
55725612c0fSDavid E. Box 		break;
55825612c0fSDavid E. Box 	default:
55925612c0fSDavid E. Box 		dev_err(priv->dev, "Unrecognized GUID 0x%x\n", table->guid);
56025612c0fSDavid E. Box 		return -EINVAL;
56125612c0fSDavid E. Box 	}
56225612c0fSDavid E. Box 	return 0;
56325612c0fSDavid E. Box }
56425612c0fSDavid E. Box 
sdsi_map_mbox_registers(struct sdsi_priv * priv,struct pci_dev * parent,struct disc_table * disc_table,struct resource * disc_res)5652546c600SDavid E. Box static int sdsi_map_mbox_registers(struct sdsi_priv *priv, struct pci_dev *parent,
5662546c600SDavid E. Box 				   struct disc_table *disc_table, struct resource *disc_res)
5672546c600SDavid E. Box {
5682546c600SDavid E. Box 	u32 access_type = FIELD_GET(DT_ACCESS_TYPE, disc_table->access_info);
5692546c600SDavid E. Box 	u32 size = FIELD_GET(DT_SIZE, disc_table->access_info);
5702546c600SDavid E. Box 	u32 tbir = FIELD_GET(DT_TBIR, disc_table->offset);
5712546c600SDavid E. Box 	u32 offset = DT_OFFSET(disc_table->offset);
5722546c600SDavid E. Box 	struct resource res = {};
5732546c600SDavid E. Box 
5742546c600SDavid E. Box 	/* Starting location of SDSi MMIO region based on access type */
5752546c600SDavid E. Box 	switch (access_type) {
5762546c600SDavid E. Box 	case ACCESS_TYPE_LOCAL:
5772546c600SDavid E. Box 		if (tbir) {
5782546c600SDavid E. Box 			dev_err(priv->dev, "Unsupported BAR index %u for access type %u\n",
5792546c600SDavid E. Box 				tbir, access_type);
5802546c600SDavid E. Box 			return -EINVAL;
5812546c600SDavid E. Box 		}
5822546c600SDavid E. Box 
5832546c600SDavid E. Box 		/*
5842546c600SDavid E. Box 		 * For access_type LOCAL, the base address is as follows:
5852546c600SDavid E. Box 		 * base address = end of discovery region + base offset + 1
5862546c600SDavid E. Box 		 */
5872546c600SDavid E. Box 		res.start = disc_res->end + offset + 1;
5882546c600SDavid E. Box 		break;
5892546c600SDavid E. Box 
5902546c600SDavid E. Box 	case ACCESS_TYPE_BARID:
5912546c600SDavid E. Box 		res.start = pci_resource_start(parent, tbir) + offset;
5922546c600SDavid E. Box 		break;
5932546c600SDavid E. Box 
5942546c600SDavid E. Box 	default:
5952546c600SDavid E. Box 		dev_err(priv->dev, "Unrecognized access_type %u\n", access_type);
5962546c600SDavid E. Box 		return -EINVAL;
5972546c600SDavid E. Box 	}
5982546c600SDavid E. Box 
5992546c600SDavid E. Box 	res.end = res.start + size * sizeof(u32) - 1;
6002546c600SDavid E. Box 	res.flags = IORESOURCE_MEM;
6012546c600SDavid E. Box 
6022546c600SDavid E. Box 	priv->control_addr = devm_ioremap_resource(priv->dev, &res);
6032546c600SDavid E. Box 	if (IS_ERR(priv->control_addr))
6042546c600SDavid E. Box 		return PTR_ERR(priv->control_addr);
6052546c600SDavid E. Box 
60625612c0fSDavid E. Box 	priv->mbox_addr = priv->control_addr + priv->control_size;
6072546c600SDavid E. Box 	priv->regs_addr = priv->mbox_addr + SDSI_SIZE_MAILBOX;
6082546c600SDavid E. Box 
609aa546b28SDavid E. Box 	priv->features = readq(priv->regs_addr + SDSI_ENABLED_FEATURES_OFFSET);
6102546c600SDavid E. Box 
6112546c600SDavid E. Box 	return 0;
6122546c600SDavid E. Box }
6132546c600SDavid E. Box 
sdsi_probe(struct auxiliary_device * auxdev,const struct auxiliary_device_id * id)6142546c600SDavid E. Box static int sdsi_probe(struct auxiliary_device *auxdev, const struct auxiliary_device_id *id)
6152546c600SDavid E. Box {
6162546c600SDavid E. Box 	struct intel_vsec_device *intel_cap_dev = auxdev_to_ivdev(auxdev);
6172546c600SDavid E. Box 	struct disc_table disc_table;
6182546c600SDavid E. Box 	struct resource *disc_res;
6192546c600SDavid E. Box 	void __iomem *disc_addr;
6202546c600SDavid E. Box 	struct sdsi_priv *priv;
6212546c600SDavid E. Box 	int ret;
6222546c600SDavid E. Box 
6232546c600SDavid E. Box 	priv = devm_kzalloc(&auxdev->dev, sizeof(*priv), GFP_KERNEL);
6242546c600SDavid E. Box 	if (!priv)
6252546c600SDavid E. Box 		return -ENOMEM;
6262546c600SDavid E. Box 
6272546c600SDavid E. Box 	priv->dev = &auxdev->dev;
6282546c600SDavid E. Box 	mutex_init(&priv->mb_lock);
6292546c600SDavid E. Box 	auxiliary_set_drvdata(auxdev, priv);
6302546c600SDavid E. Box 
6312546c600SDavid E. Box 	/* Get the SDSi discovery table */
6322546c600SDavid E. Box 	disc_res = &intel_cap_dev->resource[0];
6332546c600SDavid E. Box 	disc_addr = devm_ioremap_resource(&auxdev->dev, disc_res);
6342546c600SDavid E. Box 	if (IS_ERR(disc_addr))
6352546c600SDavid E. Box 		return PTR_ERR(disc_addr);
6362546c600SDavid E. Box 
6372546c600SDavid E. Box 	memcpy_fromio(&disc_table, disc_addr, DISC_TABLE_SIZE);
6382546c600SDavid E. Box 
6392546c600SDavid E. Box 	priv->guid = disc_table.guid;
6402546c600SDavid E. Box 
64125612c0fSDavid E. Box 	/* Get guid based layout info */
64225612c0fSDavid E. Box 	ret = sdsi_get_layout(priv, &disc_table);
64325612c0fSDavid E. Box 	if (ret)
64425612c0fSDavid E. Box 		return ret;
64525612c0fSDavid E. Box 
6462546c600SDavid E. Box 	/* Map the SDSi mailbox registers */
6472546c600SDavid E. Box 	ret = sdsi_map_mbox_registers(priv, intel_cap_dev->pcidev, &disc_table, disc_res);
6482546c600SDavid E. Box 	if (ret)
6492546c600SDavid E. Box 		return ret;
6502546c600SDavid E. Box 
6512546c600SDavid E. Box 	return 0;
6522546c600SDavid E. Box }
6532546c600SDavid E. Box 
6542546c600SDavid E. Box static const struct auxiliary_device_id sdsi_aux_id_table[] = {
6552546c600SDavid E. Box 	{ .name = "intel_vsec.sdsi" },
6562546c600SDavid E. Box 	{}
6572546c600SDavid E. Box };
6582546c600SDavid E. Box MODULE_DEVICE_TABLE(auxiliary, sdsi_aux_id_table);
6592546c600SDavid E. Box 
6602546c600SDavid E. Box static struct auxiliary_driver sdsi_aux_driver = {
6612546c600SDavid E. Box 	.driver = {
6622546c600SDavid E. Box 		.dev_groups = sdsi_groups,
6632546c600SDavid E. Box 	},
6642546c600SDavid E. Box 	.id_table	= sdsi_aux_id_table,
6652546c600SDavid E. Box 	.probe		= sdsi_probe,
6662546c600SDavid E. Box 	/* No remove. All resources are handled under devm */
6672546c600SDavid E. Box };
6682546c600SDavid E. Box module_auxiliary_driver(sdsi_aux_driver);
6692546c600SDavid E. Box 
6702546c600SDavid E. Box MODULE_AUTHOR("David E. Box <david.e.box@linux.intel.com>");
6714ea62915SDavid E. Box MODULE_DESCRIPTION("Intel On Demand (SDSi) driver");
6722546c600SDavid E. Box MODULE_LICENSE("GPL");
673