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
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCAAdFiEEunHlEgbzHrJD3ZPhAEG6vDF+4pgFAl65dykACgkQAEG6vDF+
 4pjNhBAAnUQhcRdUqVRo/sQ3VQ2G0arGnZF0w83GFoMwHFixL5bISFOP/FU27MEq
 9QNYxFhG8Yj6pl2gNExN2cmdxGdr4lf+KWLWfKuHOaF+HWSs4G8FarycGQpLnf7n
 ErPW+1WP/8JFXykiqU+K7d2n0q7YlL25kF3VjPQaXpwkd2/cz9YxbUL8jOwF+LzL
 8rErVxnDGrQisxrk6A6eQ5cWh9D2Itr+hho1Cgyhwj5K3Z8PSorA24Jd+hpHvjt0
 7oZjl/GQjqdytOXsZ/OxcvGdsid8afFFnmyEfH++knq8KjuwdieM5v2TAjq7oXlO
 gMB4ztJfq5QM1bbRo0IuHdEMs1Abs/h1qGOeDQStN2M1VP5sDG/NiZYarlW+NdbM
 ph2RsrtulOQoiXdhWx2UEHSXL2asdjSdwX9cCpYCxYy2BD/NCe97eglspVFFP+E8
 /Hj/5ULAPxjnsLnToAudU3zgVxT6Ag0Pdt7K6YfmzGL7r/io+IQ8GJpdFefQit1S
 8ucewpVaDdvmfmbRATjB9HWN+hnoF1lySp4X2svY2HMURXP0CN0fl3m0FFdLkoBA
 v/Czu5iRwFsn0bBsXdTy3hvgiYWYS8gsMlQXa3TYGzherQgWNKQ8U+ScpKoo94Ln
 tTYb0/Gh5bSB47c1vkfsF5sq1KLdF1r/REqZHLp3MkHgOOnrouc=
 =qqL7
 -----END PGP SIGNATURE-----

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: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
Arnd Bergmann 2020-05-25 16:23:55 +02:00
commit a7afae50e2
13 changed files with 344 additions and 34 deletions

View File

@ -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:

View File

@ -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

View File

@ -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 {

View File

@ -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);

View File

@ -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,6 +202,83 @@ __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)
{
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;
}
unpack_scmi_header(msg_hdr, &xfer->hdr);
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? */
if (!test_bit(xfer_id, minfo->xfer_alloc_table)) {
dev_err(dev, "message for %d is not expected!\n", xfer_id);
info->desc->ops->clear_channel(cinfo);
return;
}
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);
info->desc->ops->fetch_response(cinfo, xfer);
trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq,
msg_type);
if (msg_type == MSG_TYPE_DELAYED_RESP) {
info->desc->ops->clear_channel(cinfo);
complete(xfer->async_done);
} else {
complete(&xfer->done);
}
}
/** /**
* scmi_rx_callback() - callback for receiving messages * scmi_rx_callback() - callback for receiving messages
* *
@ -214,36 +293,21 @@ __scmi_xfer_put(struct scmi_xfers_info *minfo, struct scmi_xfer *xfer)
*/ */
void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr) 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); u16 xfer_id = MSG_XTRACT_TOKEN(msg_hdr);
u8 msg_type = MSG_XTRACT_TYPE(msg_hdr); u8 msg_type = MSG_XTRACT_TYPE(msg_hdr);
struct device *dev = cinfo->dev;
struct scmi_xfer *xfer;
if (msg_type == MSG_TYPE_NOTIFICATION) switch (msg_type) {
return; /* Notifications not yet supported */ case MSG_TYPE_NOTIFICATION:
scmi_handle_notification(cinfo, msg_hdr);
/* Are we even expecting this? */ break;
if (!test_bit(xfer_id, minfo->xfer_alloc_table)) { case MSG_TYPE_COMMAND:
dev_err(dev, "message for %d is not expected!\n", xfer_id); case MSG_TYPE_DELAYED_RESP:
return; scmi_handle_response(cinfo, xfer_id, msg_type);
break;
default:
WARN_ONCE(1, "received unknown msg_type:%d\n", msg_type);
break;
} }
xfer = &minfo->xfer_block[xfer_id];
scmi_dump_header_dbg(dev, &xfer->hdr);
info->desc->ops->fetch_response(cinfo, xfer);
trace_scmi_rx_done(xfer->transfer_id, xfer->hdr.id,
xfer->hdr.protocol_id, xfer->hdr.seq,
msg_type);
if (msg_type == MSG_TYPE_DELAYED_RESP)
complete(xfer->async_done);
else
complete(&xfer->done);
} }
/** /**
@ -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 */ },
}; };

View File

@ -158,6 +158,21 @@ static void mailbox_fetch_response(struct scmi_chan_info *cinfo,
shmem_fetch_response(smbox->shmem, xfer); shmem_fetch_response(smbox->shmem, xfer);
} }
static void mailbox_fetch_notification(struct scmi_chan_info *cinfo,
size_t max_len, struct scmi_xfer *xfer)
{
struct scmi_mailbox *smbox = cinfo->transport_info;
shmem_fetch_notification(smbox->shmem, max_len, xfer);
}
static void mailbox_clear_channel(struct scmi_chan_info *cinfo)
{
struct scmi_mailbox *smbox = cinfo->transport_info;
shmem_clear_channel(smbox->shmem);
}
static bool static bool
mailbox_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer) mailbox_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer)
{ {
@ -173,6 +188,8 @@ static struct scmi_transport_ops scmi_mailbox_ops = {
.send_message = mailbox_send_message, .send_message = mailbox_send_message,
.mark_txdone = mailbox_mark_txdone, .mark_txdone = mailbox_mark_txdone,
.fetch_response = mailbox_fetch_response, .fetch_response = mailbox_fetch_response,
.fetch_notification = mailbox_fetch_notification,
.clear_channel = mailbox_clear_channel,
.poll_done = mailbox_poll_done, .poll_done = mailbox_poll_done,
}; };

View File

@ -27,6 +27,11 @@ enum scmi_performance_protocol_cmd {
PERF_DESCRIBE_FASTCHANNEL = 0xb, PERF_DESCRIBE_FASTCHANNEL = 0xb,
}; };
enum scmi_performance_protocol_notify {
PERFORMANCE_LIMITS_CHANGED = 0x0,
PERFORMANCE_LEVEL_CHANGED = 0x1,
};
struct scmi_opp { struct scmi_opp {
u32 perf; u32 perf;
u32 power; u32 power;

View File

@ -12,6 +12,12 @@ enum scmi_power_protocol_cmd {
POWER_STATE_SET = 0x4, POWER_STATE_SET = 0x4,
POWER_STATE_GET = 0x5, POWER_STATE_GET = 0x5,
POWER_STATE_NOTIFY = 0x6, POWER_STATE_NOTIFY = 0x6,
POWER_STATE_CHANGE_REQUESTED_NOTIFY = 0x7,
};
enum scmi_power_protocol_notify {
POWER_STATE_CHANGED = 0x0,
POWER_STATE_CHANGE_REQUESTED = 0x1,
}; };
struct scmi_msg_resp_power_attributes { struct scmi_msg_resp_power_attributes {

View File

@ -14,6 +14,10 @@ enum scmi_sensor_protocol_cmd {
SENSOR_READING_GET = 0x6, SENSOR_READING_GET = 0x6,
}; };
enum scmi_sensor_protocol_notify {
SENSOR_TRIP_POINT_EVENT = 0x0,
};
struct scmi_msg_resp_sensor_attributes { struct scmi_msg_resp_sensor_attributes {
__le16 num_sensors; __le16 num_sensors;
u8 max_requests; u8 max_requests;

View File

@ -67,6 +67,21 @@ void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len); memcpy_fromio(xfer->rx.buf, shmem->msg_payload + 4, xfer->rx.len);
} }
void shmem_fetch_notification(struct scmi_shared_mem __iomem *shmem,
size_t max_len, struct scmi_xfer *xfer)
{
/* Skip only the length of header in shmem area i.e 4 bytes */
xfer->rx.len = min_t(size_t, max_len, ioread32(&shmem->length) - 4);
/* Take a copy to the rx buffer.. */
memcpy_fromio(xfer->rx.buf, shmem->msg_payload, xfer->rx.len);
}
void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem)
{
iowrite32(SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE, &shmem->channel_status);
}
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)
{ {

View File

@ -0,0 +1,153 @@
// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Management Interface (SCMI) Message SMC/HVC
* Transport driver
*
* Copyright 2020 NXP
*/
#include <linux/arm-smccc.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include "common.h"
/**
* struct scmi_smc - Structure representing a SCMI smc transport
*
* @cinfo: SCMI channel info
* @shmem: Transmit/Receive shared memory area
* @func_id: smc/hvc call function id
*/
struct scmi_smc {
struct scmi_chan_info *cinfo;
struct scmi_shared_mem __iomem *shmem;
struct mutex shmem_lock;
u32 func_id;
};
static bool smc_chan_available(struct device *dev, int idx)
{
struct device_node *np = of_parse_phandle(dev->of_node, "shmem", 0);
if (!np)
return false;
of_node_put(np);
return true;
}
static int smc_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
bool tx)
{
struct device *cdev = cinfo->dev;
struct scmi_smc *scmi_info;
resource_size_t size;
struct resource res;
struct device_node *np;
u32 func_id;
int ret;
if (!tx)
return -ENODEV;
scmi_info = devm_kzalloc(dev, sizeof(*scmi_info), GFP_KERNEL);
if (!scmi_info)
return -ENOMEM;
np = of_parse_phandle(cdev->of_node, "shmem", 0);
ret = of_address_to_resource(np, 0, &res);
of_node_put(np);
if (ret) {
dev_err(cdev, "failed to get SCMI Tx shared memory\n");
return ret;
}
size = resource_size(&res);
scmi_info->shmem = devm_ioremap(dev, res.start, size);
if (!scmi_info->shmem) {
dev_err(dev, "failed to ioremap SCMI Tx shared memory\n");
return -EADDRNOTAVAIL;
}
ret = of_property_read_u32(dev->of_node, "arm,smc-id", &func_id);
if (ret < 0)
return ret;
scmi_info->func_id = func_id;
scmi_info->cinfo = cinfo;
mutex_init(&scmi_info->shmem_lock);
cinfo->transport_info = scmi_info;
return 0;
}
static int smc_chan_free(int id, void *p, void *data)
{
struct scmi_chan_info *cinfo = p;
struct scmi_smc *scmi_info = cinfo->transport_info;
cinfo->transport_info = NULL;
scmi_info->cinfo = NULL;
scmi_free_channel(cinfo, data, id);
return 0;
}
static int smc_send_message(struct scmi_chan_info *cinfo,
struct scmi_xfer *xfer)
{
struct scmi_smc *scmi_info = cinfo->transport_info;
struct arm_smccc_res res;
mutex_lock(&scmi_info->shmem_lock);
shmem_tx_prepare(scmi_info->shmem, xfer);
arm_smccc_1_1_invoke(scmi_info->func_id, 0, 0, 0, 0, 0, 0, 0, &res);
scmi_rx_callback(scmi_info->cinfo, shmem_read_header(scmi_info->shmem));
mutex_unlock(&scmi_info->shmem_lock);
/* Only SMCCC_RET_NOT_SUPPORTED is valid error code */
if (res.a0)
return -EOPNOTSUPP;
return 0;
}
static void smc_fetch_response(struct scmi_chan_info *cinfo,
struct scmi_xfer *xfer)
{
struct scmi_smc *scmi_info = cinfo->transport_info;
shmem_fetch_response(scmi_info->shmem, xfer);
}
static bool
smc_poll_done(struct scmi_chan_info *cinfo, struct scmi_xfer *xfer)
{
struct scmi_smc *scmi_info = cinfo->transport_info;
return shmem_poll_done(scmi_info->shmem, xfer);
}
static struct scmi_transport_ops scmi_smc_ops = {
.chan_available = smc_chan_available,
.chan_setup = smc_chan_setup,
.chan_free = smc_chan_free,
.send_message = smc_send_message,
.fetch_response = smc_fetch_response,
.poll_done = smc_poll_done,
};
const struct scmi_desc scmi_smc_desc = {
.ops = &scmi_smc_ops,
.max_rx_timeout_ms = 30,
.max_msg = 1,
.max_msg_size = 128,
};

View File

@ -4,6 +4,10 @@
* *
* Copyright (C) 2018 ARM Ltd. * Copyright (C) 2018 ARM Ltd.
*/ */
#ifndef _LINUX_SCMI_PROTOCOL_H
#define _LINUX_SCMI_PROTOCOL_H
#include <linux/device.h> #include <linux/device.h>
#include <linux/types.h> #include <linux/types.h>
@ -319,3 +323,5 @@ static inline void scmi_driver_unregister(struct scmi_driver *driver) {}
typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *); typedef int (*scmi_prot_init_fn_t)(struct scmi_handle *);
int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn); int scmi_protocol_register(int protocol_id, scmi_prot_init_fn_t fn);
void scmi_protocol_unregister(int protocol_id); void scmi_protocol_unregister(int protocol_id);
#endif /* _LINUX_SCMI_PROTOCOL_H */

View File

@ -4,6 +4,10 @@
* *
* Copyright (C) 2014 ARM Ltd. * Copyright (C) 2014 ARM Ltd.
*/ */
#ifndef _LINUX_SCPI_PROTOCOL_H
#define _LINUX_SCPI_PROTOCOL_H
#include <linux/types.h> #include <linux/types.h>
struct scpi_opp { struct scpi_opp {
@ -71,3 +75,5 @@ struct scpi_ops *get_scpi_ops(void);
#else #else
static inline struct scpi_ops *get_scpi_ops(void) { return NULL; } static inline struct scpi_ops *get_scpi_ops(void) { return NULL; }
#endif #endif
#endif /* _LINUX_SCPI_PROTOCOL_H */