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