Commit a7afae50 authored by Arnd Bergmann's avatar Arnd Bergmann

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@bogusSigned-off-by: default avatarArnd Bergmann <arnd@arndb.de>
parents 0e698dfa 5a897e3a
...@@ -14,7 +14,7 @@ Required properties: ...@@ -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. ...@@ -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:
......
...@@ -2,6 +2,8 @@ ...@@ -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
...@@ -14,6 +14,13 @@ enum scmi_base_protocol_cmd { ...@@ -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 {
......
...@@ -178,6 +178,8 @@ struct scmi_chan_info { ...@@ -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 { ...@@ -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 { ...@@ -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, ...@@ -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);
...@@ -76,6 +76,7 @@ struct scmi_xfers_info { ...@@ -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 { ...@@ -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) ...@@ -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,10 +271,43 @@ void scmi_rx_callback(struct scmi_chan_info *cinfo, u32 msg_hdr) ...@@ -240,10 +271,43 @@ 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;
}
} }
/** /**
...@@ -525,13 +589,13 @@ int scmi_handle_put(const struct scmi_handle *handle) ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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); ...@@ -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 */ },
}; };
......
...@@ -158,6 +158,21 @@ static void mailbox_fetch_response(struct scmi_chan_info *cinfo, ...@@ -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 = { ...@@ -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,
}; };
......
...@@ -27,6 +27,11 @@ enum scmi_performance_protocol_cmd { ...@@ -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;
......
...@@ -12,6 +12,12 @@ enum scmi_power_protocol_cmd { ...@@ -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 {
......
...@@ -14,6 +14,10 @@ enum scmi_sensor_protocol_cmd { ...@@ -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;
......
...@@ -67,6 +67,21 @@ void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem, ...@@ -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)
{ {
......
// 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,
};
...@@ -4,6 +4,10 @@ ...@@ -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) {} ...@@ -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 */
...@@ -4,6 +4,10 @@ ...@@ -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); ...@@ -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 */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment