Commit a7afae50 authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

Merge tag 'scmi-updates-5.8' of...

Merge tag 'scmi-updates-5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into arm/drivers

ARM SCMI/SCPI updates for v5.8

1. Addition of ARM SMC/HVC as SCMI transport type with required
   abstraction already in place
2. Initial infrastructure support to add SCMI notifications from
   platform to agents
3. Miscellaneous fix adding header include guards

* tag 'scmi-updates-5.8' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux:
  firmware: arm_scmi: fix psci dependency
  firmware: arm_scmi: Fix return error code in smc_send_message
  firmware: arm_scmi: Fix handling of unexpected delayed responses
  firmware: arm_scmi: Clear channel for delayed responses
  firmware: arm_scmi: Clear channel on reception of unexpected responses
  firmware: arm_scmi: Rename .clear_notification() transport_ops
  firmware: arm_scmi: Add support for notifications message processing
  firmware: arm_scmi: Add notifications support in transport layer
  firmware: arm_scmi: Update protocol commands and notification list
  firmware: arm_scmi: Add receive buffer support for notifications
  firmware: arm_scpi: Add include guard to linux/scpi_protocol.h
  firmware: arm_scmi: Add include guard to linux/scmi_protocol.h
  firmware: arm_scmi: Drop checking for shmem property in parent node
  firmware: arm_scmi: Check shmem property for channel availablity
  firmware: arm_scmi: Drop empty stub for smc_mark_txdone
  firmware: arm_scmi: Make mutex channel specific
  firmware: arm_scmi: Add smc/hvc transport
  dt-bindings: arm: Add smc/hvc transport for SCMI

Link: https://lore.kernel.org/r/20200512110357.GA26454@bogus


Signed-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 0e698dfa 5a897e3a
Loading
Loading
Loading
Loading
+2 −1
Original line number Original line Diff line number Diff line
@@ -14,7 +14,7 @@ Required properties:


The scmi node with the following properties shall be under the /firmware/ node.
The scmi node with the following properties shall be under the /firmware/ node.


- compatible : shall be "arm,scmi"
- compatible : shall be "arm,scmi" or "arm,scmi-smc" for smc/hvc transports
- mboxes: List of phandle and mailbox channel specifiers. It should contain
- mboxes: List of phandle and mailbox channel specifiers. It should contain
	  exactly one or two mailboxes, one for transmitting messages("tx")
	  exactly one or two mailboxes, one for transmitting messages("tx")
	  and another optional for receiving the notifications("rx") if
	  and another optional for receiving the notifications("rx") if
@@ -25,6 +25,7 @@ The scmi node with the following properties shall be under the /firmware/ node.
	  protocol identifier for a given sub-node.
	  protocol identifier for a given sub-node.
- #size-cells : should be '0' as 'reg' property doesn't have any size
- #size-cells : should be '0' as 'reg' property doesn't have any size
	  associated with it.
	  associated with it.
- arm,smc-id : SMC id required when using smc or hvc transports


Optional properties:
Optional properties:


+3 −1
Original line number Original line Diff line number Diff line
@@ -2,6 +2,8 @@
obj-y	= scmi-bus.o scmi-driver.o scmi-protocols.o scmi-transport.o
obj-y	= scmi-bus.o scmi-driver.o scmi-protocols.o scmi-transport.o
scmi-bus-y = bus.o
scmi-bus-y = bus.o
scmi-driver-y = driver.o
scmi-driver-y = driver.o
scmi-transport-y = mailbox.o shmem.o
scmi-transport-y = shmem.o
scmi-transport-$(CONFIG_MAILBOX) += mailbox.o
scmi-transport-$(CONFIG_ARM_PSCI_FW) += smc.o
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
scmi-protocols-y = base.o clock.o perf.o power.o reset.o sensors.o
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
+7 −0
Original line number Original line Diff line number Diff line
@@ -14,6 +14,13 @@ enum scmi_base_protocol_cmd {
	BASE_DISCOVER_LIST_PROTOCOLS = 0x6,
	BASE_DISCOVER_LIST_PROTOCOLS = 0x6,
	BASE_DISCOVER_AGENT = 0x7,
	BASE_DISCOVER_AGENT = 0x7,
	BASE_NOTIFY_ERRORS = 0x8,
	BASE_NOTIFY_ERRORS = 0x8,
	BASE_SET_DEVICE_PERMISSIONS = 0x9,
	BASE_SET_PROTOCOL_PERMISSIONS = 0xa,
	BASE_RESET_AGENT_CONFIGURATION = 0xb,
};

enum scmi_base_protocol_notify {
	BASE_ERROR_EVENT = 0x0,
};
};


struct scmi_msg_resp_base_attributes {
struct scmi_msg_resp_base_attributes {
+11 −0
Original line number Original line Diff line number Diff line
@@ -178,6 +178,8 @@ struct scmi_chan_info {
 * @send_message: Callback to send a message
 * @send_message: Callback to send a message
 * @mark_txdone: Callback to mark tx as done
 * @mark_txdone: Callback to mark tx as done
 * @fetch_response: Callback to fetch response
 * @fetch_response: Callback to fetch response
 * @fetch_notification: Callback to fetch notification
 * @clear_channel: Callback to clear a channel
 * @poll_done: Callback to poll transfer status
 * @poll_done: Callback to poll transfer status
 */
 */
struct scmi_transport_ops {
struct scmi_transport_ops {
@@ -190,6 +192,9 @@ struct scmi_transport_ops {
	void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret);
	void (*mark_txdone)(struct scmi_chan_info *cinfo, int ret);
	void (*fetch_response)(struct scmi_chan_info *cinfo,
	void (*fetch_response)(struct scmi_chan_info *cinfo,
			       struct scmi_xfer *xfer);
			       struct scmi_xfer *xfer);
	void (*fetch_notification)(struct scmi_chan_info *cinfo,
				   size_t max_len, struct scmi_xfer *xfer);
	void (*clear_channel)(struct scmi_chan_info *cinfo);
	bool (*poll_done)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer);
	bool (*poll_done)(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer);
};
};


@@ -210,6 +215,9 @@ struct scmi_desc {
};
};


extern const struct scmi_desc scmi_mailbox_desc;
extern const struct scmi_desc scmi_mailbox_desc;
#ifdef CONFIG_HAVE_ARM_SMCCC
extern const struct scmi_desc scmi_smc_desc;
#endif


void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr);
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr);
void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id);
void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id);
@@ -222,5 +230,8 @@ void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem);
u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem);
void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
			  struct scmi_xfer *xfer);
			  struct scmi_xfer *xfer);
void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem,
			      size_t max_len, struct scmi_xfer *xfer);
void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem);
bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
		     struct scmi_xfer *xfer);
		     struct scmi_xfer *xfer);
+105 −28
Original line number Original line Diff line number Diff line
@@ -76,6 +76,7 @@ struct scmi_xfers_info {
 *	implementation version and (sub-)vendor identification.
 *	implementation version and (sub-)vendor identification.
 * @handle: Instance of SCMI handle to send to clients
 * @handle: Instance of SCMI handle to send to clients
 * @tx_minfo: Universal Transmit Message management info
 * @tx_minfo: Universal Transmit Message management info
 * @rx_minfo: Universal Receive Message management info
 * @tx_idr: IDR object to map protocol id to Tx channel info pointer
 * @tx_idr: IDR object to map protocol id to Tx channel info pointer
 * @rx_idr: IDR object to map protocol id to Rx channel info pointer
 * @rx_idr: IDR object to map protocol id to Rx channel info pointer
 * @protocols_imp: List of protocols implemented, currently maximum of
 * @protocols_imp: List of protocols implemented, currently maximum of
@@ -89,6 +90,7 @@ struct scmi_info {
	struct scmi_revision_info version;
	struct scmi_revision_info version;
	struct scmi_handle handle;
	struct scmi_handle handle;
	struct scmi_xfers_info tx_minfo;
	struct scmi_xfers_info tx_minfo;
	struct scmi_xfers_info rx_minfo;
	struct idr tx_idr;
	struct idr tx_idr;
	struct idr rx_idr;
	struct idr rx_idr;
	u8 *protocols_imp;
	u8 *protocols_imp;
@@ -200,37 +202,66 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer)
	spin_unlock_irqrestore(&minfo->xfer_lock, flags);
	spin_unlock_irqrestore(&minfo->xfer_lock, flags);
}
}


/**
static void scmi_handle_notification(struct scmi_chan_info *cinfo, u32 msg_hdr)
 * scmi_rx_callback() - callback for receiving messages
 *
 * @cinfo: SCMI channel info
 * @msg_hdr: Message header
 *
 * Processes one received message to appropriate transfer information and
 * signals completion of the transfer.
 *
 * NOTE: This function will be invoked in IRQ context, hence should be
 * as optimal as possible.
 */
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
{
{
	struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
	struct scmi_xfers_info *minfo = &info->tx_minfo;
	u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr);
	u8 msg_type = MSG_XTRACT_TYPE(msg_hdr);
	struct device *dev = cinfo->dev;
	struct scmi_xfer *xfer;
	struct scmi_xfer *xfer;
	struct device *dev = cinfo->dev;
	struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
	struct scmi_xfers_info *minfo = &info->rx_minfo;

	xfer = scmi_xfer_get(cinfo->handle, minfo);
	if (IS_ERR(xfer)) {
		dev_err(dev, "failed to get free message slot (%ld)\n",
			PTR_ERR(xfer));
		info->desc->ops->clear_channel(cinfo);
		return;
	}


	if (msg_type == MSG_TYPE_NOTIFICATION)
	unpack_scmi_header(msg_hdr, &xfer->hdr);
		return; /* Notifications not yet supported */
	scmi_dump_header_dbg(dev, &xfer->hdr);
	info->desc->ops->fetch_notification(cinfo, info->desc->max_msg_size,
					    xfer);

	trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id,
			   xfer->hdr.protocol_id, xfer->hdr.seq,
			   MSG_TYPE_NOTIFICATION);

	__scmi_xfer_put(minfo, xfer);

	info->desc->ops->clear_channel(cinfo);
}

static void scmi_handle_response(struct scmi_chan_info *cinfo,
				 u16 xfer_id, u8 msg_type)
{
	struct scmi_xfer *xfer;
	struct device *dev = cinfo->dev;
	struct scmi_info *info = handle_to_scmi_info(cinfo->handle);
	struct scmi_xfers_info *minfo = &info->tx_minfo;


	/* Are we even expecting this? */
	/* Are we even expecting this? */
	if (!test_bit(xfer_id, minfo->xfer_alloc_table)) {
	if (!test_bit(xfer_id, minfo->xfer_alloc_table)) {
		dev_err(dev, "message for %d is not expected!\n", xfer_id);
		dev_err(dev, "message for %d is not expected!\n", xfer_id);
		info->desc->ops->clear_channel(cinfo);
		return;
		return;
	}
	}


	xfer = &minfo->xfer_block[xfer_id];
	xfer = &minfo->xfer_block[xfer_id];
	/*
	 * Even if a response was indeed expected on this slot at this point,
	 * a buggy platform could wrongly reply feeding us an unexpected
	 * delayed response we're not prepared to handle: bail-out safely
	 * blaming firmware.
	 */
	if (unlikely(msg_type == MSG_TYPE_DELAYED_RESP && !xfer->async_done)) {
		dev_err(dev,
			"Delayed Response for %d not expected! Buggy F/W ?\n",
			xfer_id);
		info->desc->ops->clear_channel(cinfo);
		/* It was unexpected, so nobody will clear the xfer if not us */
		__scmi_xfer_put(minfo, xfer);
		return;
	}


	scmi_dump_header_dbg(dev, &xfer->hdr);
	scmi_dump_header_dbg(dev, &xfer->hdr);


@@ -240,11 +271,44 @@ void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
			   xfer->hdr.protocol_id, xfer->hdr.seq,
			   xfer->hdr.protocol_id, xfer->hdr.seq,
			   msg_type);
			   msg_type);


	if (msg_type == MSG_TYPE_DELAYED_RESP)
	if (msg_type == MSG_TYPE_DELAYED_RESP) {
		info->desc->ops->clear_channel(cinfo);
		complete(xfer->async_done);
		complete(xfer->async_done);
	else
	} else {
		complete(&xfer->done);
		complete(&xfer->done);
	}
	}
}

/**
 * scmi_rx_callback() - callback for receiving messages
 *
 * @cinfo: SCMI channel info
 * @msg_hdr: Message header
 *
 * Processes one received message to appropriate transfer information and
 * signals completion of the transfer.
 *
 * NOTE: This function will be invoked in IRQ context, hence should be
 * as optimal as possible.
 */
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr)
{
	u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr);
	u8 msg_type = MSG_XTRACT_TYPE(msg_hdr);

	switch (msg_type) {
	case MSG_TYPE_NOTIFICATION:
		scmi_handle_notification(cinfo, msg_hdr);
		break;
	case MSG_TYPE_COMMAND:
	case MSG_TYPE_DELAYED_RESP:
		scmi_handle_response(cinfo, xfer_id, msg_type);
		break;
	default:
		WARN_ONCE(1, "received unknown msg_type:%d\n", msg_type);
		break;
	}
}


/**
/**
 * scmi_xfer_put() - Release a transmit message
 * scmi_xfer_put() - Release a transmit message
@@ -525,13 +589,13 @@ int scmi_handle_put(const struct scmi_handle *handle)
	return 0;
	return 0;
}
}


static int scmi_xfer_info_init(struct scmi_info *sinfo)
static int __scmi_xfer_info_init(struct scmi_info *sinfo,
				 struct scmi_xfers_info *info)
{
{
	int i;
	int i;
	struct scmi_xfer *xfer;
	struct scmi_xfer *xfer;
	struct device *dev = sinfo->dev;
	struct device *dev = sinfo->dev;
	const struct scmi_desc *desc = sinfo->desc;
	const struct scmi_desc *desc = sinfo->desc;
	struct scmi_xfers_info *info = &sinfo->tx_minfo;


	/* Pre-allocated messages, no more than what hdr.seq can support */
	/* Pre-allocated messages, no more than what hdr.seq can support */
	if (WARN_ON(desc->max_msg >= MSG_TOKEN_MAX)) {
	if (WARN_ON(desc->max_msg >= MSG_TOKEN_MAX)) {
@@ -566,6 +630,16 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo)
	return 0;
	return 0;
}
}


static int scmi_xfer_info_init(struct scmi_info *sinfo)
{
	int ret = __scmi_xfer_info_init(sinfo, &sinfo->tx_minfo);

	if (!ret && idr_find(&sinfo->rx_idr, SCMI_PROTOCOL_BASE))
		ret = __scmi_xfer_info_init(sinfo, &sinfo->rx_minfo);

	return ret;
}

static int scmi_chan_setup(struct scmi_info *info, struct device *dev,
static int scmi_chan_setup(struct scmi_info *info, struct device *dev,
			   int prot_id, bool tx)
			   int prot_id, bool tx)
{
{
@@ -699,10 +773,6 @@ static int scmi_probe(struct platform_device *pdev)
	info->desc = desc;
	info->desc = desc;
	INIT_LIST_HEAD(&info->node);
	INIT_LIST_HEAD(&info->node);


	ret = scmi_xfer_info_init(info);
	if (ret)
		return ret;

	platform_set_drvdata(pdev, info);
	platform_set_drvdata(pdev, info);
	idr_init(&info->tx_idr);
	idr_init(&info->tx_idr);
	idr_init(&info->rx_idr);
	idr_init(&info->rx_idr);
@@ -715,6 +785,10 @@ static int scmi_probe(struct platform_device *pdev)
	if (ret)
	if (ret)
		return ret;
		return ret;


	ret = scmi_xfer_info_init(info);
	if (ret)
		return ret;

	ret = scmi_base_protocol_init(handle);
	ret = scmi_base_protocol_init(handle);
	if (ret) {
	if (ret) {
		dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
		dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
@@ -827,6 +901,9 @@ ATTRIBUTE_GROUPS(versions);
/* Each compatible listed below must have descriptor associated with it */
/* Each compatible listed below must have descriptor associated with it */
static const struct of_device_id scmi_of_match[] = {
static const struct of_device_id scmi_of_match[] = {
	{ .compatible = "arm,scmi", .data = &scmi_mailbox_desc },
	{ .compatible = "arm,scmi", .data = &scmi_mailbox_desc },
#ifdef CONFIG_ARM_PSCI_FW
	{ .compatible = "arm,scmi-smc", .data = &scmi_smc_desc},
#endif
	{ /* Sentinel */ },
	{ /* Sentinel */ },
};
};


Loading