[PATCH v2 04/18] firmware: arm_scmi: add common infrastructure and support for base protocol
Julien Thierry
julien.thierry at arm.com
Tue Sep 5 06:39:38 PDT 2017
Hi Sudeep,
On 04/08/17 15:31, Sudeep Holla wrote:
> The base protocol describes the properties of the implementation and
> provide generic error management. The base protocol provides commands
> to describe protocol version, discover implementation specific
> attributes and vendor/sub-vendor identification, list of protocols
> implemented and the various agents are in the system including OSPM
> and the platform. It also supports registering for notifications of
> platform errors.
>
> This protocol is mandatory. This patch adds support for the same along
> with some basic infrastructure to add support for other protocols.
>
> Cc: Arnd Bergmann <arnd at arndb.de>
> Signed-off-by: Sudeep Holla <sudeep.holla at arm.com>
> ---
> drivers/firmware/arm_scmi/Makefile | 2 +-
> drivers/firmware/arm_scmi/base.c | 293 +++++++++++++++++++++++++++++++++++++
> drivers/firmware/arm_scmi/common.h | 46 ++++++
> drivers/firmware/arm_scmi/driver.c | 67 +++++++++
> include/linux/scmi_protocol.h | 28 ++++
> 5 files changed, 435 insertions(+), 1 deletion(-)
> create mode 100644 drivers/firmware/arm_scmi/base.c
>
> diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
> index 58e94c95e523..21d01d1d6b9c 100644
> --- a/drivers/firmware/arm_scmi/Makefile
> +++ b/drivers/firmware/arm_scmi/Makefile
> @@ -1,2 +1,2 @@
> obj-$(CONFIG_ARM_SCMI_PROTOCOL) = arm_scmi.o
> -arm_scmi-y = driver.o
> +arm_scmi-y = base.o driver.o
> diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c
> new file mode 100644
> index 000000000000..9bfffbe95c21
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/base.c
> @@ -0,0 +1,293 @@
> +/*
> + * System Control and Management Interface (SCMI) Base Protocol
> + *
> + * Copyright (C) 2017 ARM Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope it will be useful, but WITHOUT
> + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
> + * more details.
> + *
> + * You should have received a copy of the GNU General Public License along
> + * with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "common.h"
> +
> +enum scmi_base_protocol_cmd {
> + BASE_DISCOVER_VENDOR = 0x3,
> + BASE_DISCOVER_SUB_VENDOR = 0x4,
> + BASE_DISCOVER_IMPLEMENT_VERSION = 0x5,
> + BASE_DISCOVER_LIST_PROTOCOLS = 0x6,
> + BASE_DISCOVER_AGENT = 0x7,
> + BASE_NOTIFY_ERRORS = 0x8,
> +};
> +
> +struct scmi_msg_resp_base_attributes {
> + u8 num_protocols;
> + u8 num_agents;
> + __le16 reserved;
> +};
> +
> +/**
> + * scmi_base_attributes_get() - gets the implementation details
> + * that are associated with the base protocol.
> + *
> + * @handle - SCMI entity handle
> + *
> + * Return: 0 on success, else appropriate SCMI error.
> + */
> +static int scmi_base_attributes_get(const struct scmi_handle *handle)
> +{
> + int ret;
> + struct scmi_xfer *t;
> + struct scmi_msg_resp_base_attributes *attr_info;
> + struct scmi_revision_info *rev = handle->version;
> +
> + ret = scmi_one_xfer_init(handle, PROTOCOL_ATTRIBUTES,
> + SCMI_PROTOCOL_BASE, 0, sizeof(*attr_info), &t);
> + if (ret)
> + return ret;
> +
> + ret = scmi_do_xfer(handle, t);
> + if (!ret) {
> + attr_info = t->rx.buf;
> + rev->num_protocols = attr_info->num_protocols;
> + rev->num_agents = attr_info->num_agents;
> + }
> +
> + scmi_one_xfer_put(handle, t);
> + return ret;
> +}
> +
> +/**
> + * scmi_base_vendor_id_get() - gets vendor/subvendor identifier ASCII string.
> + *
> + * @handle - SCMI entity handle
> + * @sub_vendor - specify true if sub-vendor ID is needed
> + *
> + * Return: 0 on success, else appropriate SCMI error.
> + */
> +static int
> +scmi_base_vendor_id_get(const struct scmi_handle *handle, bool sub_vendor)
> +{
> + u8 cmd;
> + int ret, size;
> + char *vendor_id;
> + struct scmi_xfer *t;
> + struct scmi_revision_info *rev = handle->version;
> +
> + if (sub_vendor) {
> + cmd = BASE_DISCOVER_SUB_VENDOR;
> + vendor_id = rev->sub_vendor_id;
> + size = ARRAY_SIZE(rev->sub_vendor_id);
> + } else {
> + cmd = BASE_DISCOVER_VENDOR;
> + vendor_id = rev->vendor_id;
> + size = ARRAY_SIZE(rev->vendor_id);
> + }
> +
> + ret = scmi_one_xfer_init(handle, cmd, SCMI_PROTOCOL_BASE, 0, size, &t);
> + if (ret)
> + return ret;
> +
> + ret = scmi_do_xfer(handle, t);
> + if (!ret)
> + memcpy(vendor_id, t->rx.buf, size);
> +
> + scmi_one_xfer_put(handle, t);
> + return ret;
> +}
> +
> +/**
> + * scmi_base_implementation_version_get() - gets a vendor-specific
> + * implementation 32-bit version. The format of the version number is
> + * vendor-specific
> + *
> + * @handle - SCMI entity handle
> + *
> + * Return: 0 on success, else appropriate SCMI error.
> + */
> +static int
> +scmi_base_implementation_version_get(const struct scmi_handle *handle)
> +{
> + int ret;
> + u32 *impl_ver;
> + struct scmi_xfer *t;
> + struct scmi_revision_info *rev = handle->version;
> +
> + ret = scmi_one_xfer_init(handle, BASE_DISCOVER_IMPLEMENT_VERSION,
> + SCMI_PROTOCOL_BASE, 0, sizeof(*impl_ver), &t);
> + if (ret)
> + return ret;
> +
> + ret = scmi_do_xfer(handle, t);
> + if (!ret) {
> + impl_ver = t->rx.buf;
> + rev->impl_ver = le32_to_cpu(*impl_ver);
> + }
> +
> + scmi_one_xfer_put(handle, t);
> + return ret;
> +}
> +
> +/**
> + * scmi_base_implementation_list_get() - gets the list of protocols it is
> + * OSPM is allowed to access
> + *
> + * @handle - SCMI entity handle
> + * @protocols_imp - pointer to hold the list of protocol identifiers
> + *
> + * Return: 0 on success, else appropriate SCMI error.
> + */
> +static int scmi_base_implementation_list_get(const struct scmi_handle *handle,
> + u8 *protocols_imp)
> +{
> + u8 *list;
> + int ret, loop;
> + struct scmi_xfer *t;
> + __le32 *num_skip, *num_ret;
> + u32 tot_num_ret = 0, loop_num_ret;
> + struct device *dev = handle->dev;
> +
> + ret = scmi_one_xfer_init(handle, BASE_DISCOVER_LIST_PROTOCOLS,
> + SCMI_PROTOCOL_BASE, sizeof(*num_skip), 0, &t);
> + if (ret)
> + return ret;
> +
> + num_skip = t->tx.buf;
> + num_ret = t->rx.buf;
> + list = t->rx.buf + sizeof(*num_ret);
> +
> + do {
> + /* Set the number of protocols to be skipped/already read */
> + *num_skip = cpu_to_le32(tot_num_ret);
> +
> + ret = scmi_do_xfer(handle, t);
> + if (ret)
> + break;
> +
> + loop_num_ret = le32_to_cpu(*num_ret);
> + if (tot_num_ret + loop_num_ret > MAX_PROTOCOLS_IMP) {
> + dev_err(dev, "No. of Protocol > MAX_PROTOCOLS_IMP");
> + break;
> + }
> +
> + for (loop = 0; loop < loop_num_ret; loop++)
> + protocols_imp[tot_num_ret + loop] = *(list + loop);
> +
> + tot_num_ret += loop_num_ret;
> + } while (loop_num_ret);
> +
> + scmi_one_xfer_put(handle, t);
> + return ret;
> +}
> +
> +/**
> + * scmi_base_discover_agent_get() - discover the name of an agent
> + *
> + * @handle - SCMI entity handle
> + * @id - Agent identifier
> + * @name - Agent identifier ASCII string
> + *
> + * An agent id of 0 is reserved to identify the platform itself.
> + * Generally operating system is represented as "OSPM"
> + *
> + * Return: 0 on success, else appropriate SCMI error.
> + */
> +static int scmi_base_discover_agent_get(const struct scmi_handle *handle,
> + int id, char *name)
> +{
> + int ret;
> + struct scmi_xfer *t;
> +
> + ret = scmi_one_xfer_init(handle, BASE_DISCOVER_AGENT,
> + SCMI_PROTOCOL_BASE, sizeof(__le32),
> + SCMI_MAX_STR_SIZE, &t);
> + if (ret)
> + return ret;
> +
> + *(__le32 *)t->tx.buf = cpu_to_le32(id);
> +
> + ret = scmi_do_xfer(handle, t);
> + if (!ret)
> + memcpy(name, t->rx.buf, SCMI_MAX_STR_SIZE);
> +
> + scmi_one_xfer_put(handle, t);
> + return ret;
> +}
> +
> +/**
> + * scmi_base_error_notifications_enable() - register/unregister for
> + * notifications of errors in the platform
> + *
> + * @handle - SCMI entity handle
> + * @enable - Enable/Disable the notification
> + *
> + * Return: 0 on success, else appropriate SCMI error.
> + */
> +static int
> +scmi_base_error_notifications_enable(const struct scmi_handle *handle, bool en)
> +{
> + int ret;
> + struct scmi_xfer *t;
> +
> + ret = scmi_one_xfer_init(handle, BASE_NOTIFY_ERRORS, SCMI_PROTOCOL_BASE,
> + sizeof(__le32), 0, &t);
> + if (ret)
> + return ret;
> +
> + *(__le32 *)t->tx.buf = cpu_to_le32(en & BIT(0));
> +
> + ret = scmi_do_xfer(handle, t);
> +
> + scmi_one_xfer_put(handle, t);
> + return ret;
> +}
> +
> +int scmi_base_protocol_init(struct scmi_handle *h)
> +{
> + int id, ret;
> + u8 *prot_imp;
> + u32 version;
> + char name[SCMI_MAX_STR_SIZE];
> + const struct scmi_handle *handle = h;
> + struct device *dev = handle->dev;
> + struct scmi_revision_info *rev = handle->version;
> +
> + ret = scmi_version_get(handle, SCMI_PROTOCOL_BASE, &version);
> + if (ret)
> + return ret;
> +
> + prot_imp = devm_kcalloc(dev, MAX_PROTOCOLS_IMP, sizeof(u8), GFP_KERNEL);
> + if (!prot_imp)
> + return -ENOMEM;
> +
> + rev->major_ver = PROTOCOL_REV_MAJOR(version),
> + rev->minor_ver = PROTOCOL_REV_MINOR(version);
> +
> + scmi_base_attributes_get(handle);
> + scmi_base_vendor_id_get(handle, false);
> + scmi_base_vendor_id_get(handle, true);
> + scmi_base_implementation_version_get(handle);
> + scmi_base_implementation_list_get(handle, prot_imp);
> + scmi_base_error_notifications_enable(handle, true);
> + scmi_setup_protocol_implemented(handle, prot_imp);
> +
> + dev_info(dev, "SCMI Protocol v%d.%d '%s:%s' Firmware version 0x%x\n",
> + rev->major_ver, rev->minor_ver, rev->vendor_id,
> + rev->sub_vendor_id, rev->impl_ver);
> + dev_dbg(dev, "Found %d protocol(s) %d agent(s)\n", rev->num_protocols,
> + rev->num_agents);
> +
> + for (id = 0; id < rev->num_agents; id++) {
> + scmi_base_discover_agent_get(handle, id, name);
> + dev_dbg(dev, "Agent %d: %s\n", id, name);
> + }
> +
> + return 0;
> +}
> diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h
> index a6829afc17e3..e3fe5d9acc82 100644
> --- a/drivers/firmware/arm_scmi/common.h
> +++ b/drivers/firmware/arm_scmi/common.h
> @@ -19,9 +19,50 @@
> */
>
> #include <linux/completion.h>
> +#include <linux/device.h>
> +#include <linux/errno.h>
> +#include <linux/kernel.h>
> #include <linux/scmi_protocol.h>
> #include <linux/types.h>
>
> +#define PROTOCOL_REV_MINOR_BITS 16
> +#define PROTOCOL_REV_MINOR_MASK ((1U << PROTOCOL_REV_MINOR_BITS) - 1)
> +#define PROTOCOL_REV_MAJOR(x) ((x) >> PROTOCOL_REV_MINOR_BITS)
> +#define PROTOCOL_REV_MINOR(x) ((x) & PROTOCOL_REV_MINOR_MASK)
> +#define MAX_PROTOCOLS_IMP 16
> +
> +enum scmi_std_protocol {
> + SCMI_PROTOCOL_BASE = 0x10,
> + SCMI_PROTOCOL_POWER = 0x11,
> + SCMI_PROTOCOL_SYSTEM = 0x12,
> + SCMI_PROTOCOL_PERF = 0x13,
> + SCMI_PROTOCOL_CLOCK = 0x14,
> + SCMI_PROTOCOL_SENSOR = 0x15,
> +};
> +
> +enum scmi_common_cmd {
> + PROTOCOL_VERSION = 0x0,
> + PROTOCOL_ATTRIBUTES = 0x1,
> + PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
> +};
> +
> +/**
> + * struct scmi_msg_resp_prot_version - Response for a message
> + *
> + * @major_version: Major version of the ABI that firmware supports
> + * @minor_version: Minor version of the ABI that firmware supports
> + *
> + * In general, ABI version changes follow the rule that minor version increments
> + * are backward compatible. Major revision changes in ABI may not be
> + * backward compatible.
> + *
> + * Response to a generic message with message type SCMI_MSG_VERSION
> + */
> +struct scmi_msg_resp_prot_version {
> + __le16 minor_version;
> + __le16 major_version;
> +};
> +
> /**
> * struct scmi_msg_hdr - Message(Tx/Rx) header
> *
> @@ -72,3 +113,8 @@ void scmi_one_xfer_put(const struct scmi_handle *h, struct scmi_xfer *xfer);
> int scmi_do_xfer(const struct scmi_handle *h, struct scmi_xfer *xfer);
> int scmi_one_xfer_init(const struct scmi_handle *h, u8 msg_id, u8 prot_id,
> size_t tx_size, size_t rx_size, struct scmi_xfer **p);
> +int scmi_version_get(const struct scmi_handle *h, u8 protocol, u32 *version);
> +void scmi_setup_protocol_implemented(const struct scmi_handle *handle,
> + u8 *prot_imp);
> +
> +int scmi_base_protocol_init(struct scmi_handle *h);
> diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c
> index 139d6980f270..601d0d7210d9 100644
> --- a/drivers/firmware/arm_scmi/driver.c
> +++ b/drivers/firmware/arm_scmi/driver.c
> @@ -108,18 +108,22 @@ struct scmi_desc {
> * @dev: Device pointer
> * @desc: SoC description for this instance
> * @handle: Instance of SCMI handle to send to clients
> + * @version: SCMI revision information containing protocol version,
> + * implementation version and (sub-)vendor identification.
> * @cl: Mailbox Client
> * @tx_chan: Transmit mailbox channel
> * @rx_chan: Receive mailbox channel
> * @tx_payload: Transmit mailbox channel payload area
> * @rx_payload: Receive mailbox channel payload area
> * @minfo: Message info
> + * @protocols_imp: list of protocols implemented
> * @node: list head
> * @users: Number of users of this instance
> */
> struct scmi_info {
> struct device *dev;
> const struct scmi_desc *desc;
> + struct scmi_revision_info version;
> struct scmi_handle handle;
> struct mbox_client cl;
> struct mbox_chan *tx_chan;
> @@ -127,6 +131,7 @@ struct scmi_info {
> void __iomem *tx_payload;
> void __iomem *rx_payload;
> struct scmi_xfers_info minfo;
> + u8 *protocols_imp;
Both the base protocol and driver part rely on this being of size
MAX_PROTOCOLS_IMP (if existing).
Could this be a "u8 protocols_imp[MAX_PROTOCOLS_IMP]" instead? Or at
least add a comment making it clearer at the scmi_info definition that
this array will have MAX_PROTOCOLS_IMP elements in all scmi_info instances?
Other than that, patch seems fine.
Cheers,
--
Julien Thierry
More information about the linux-arm-kernel
mailing list