Re: [RFC PATCH 3/8] firmware: arm_scmi: add common infrastructure and support for base protocol

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 




On Wed, Jun 7, 2017 at 9:10 AM, Sudeep Holla <sudeep.holla@xxxxxxx> 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.
>
> Signed-off-by: Sudeep Holla <sudeep.holla@xxxxxxx>
> ---
>  drivers/firmware/arm_scmi/Makefile |   2 +-
>  drivers/firmware/arm_scmi/base.c   | 290 +++++++++++++++++++++++++++++++++++++
>  drivers/firmware/arm_scmi/common.h |  46 ++++++
>  drivers/firmware/arm_scmi/driver.c |  67 +++++++++
>  include/linux/scmi_protocol.h      |  28 ++++
>  5 files changed, 432 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..1191a409ea73
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/base.c
> @@ -0,0 +1,290 @@
> +/*
> + * 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;
> +} __packed;
> +
> +/**
> + * 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(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 = (struct scmi_msg_resp_base_attributes *)t->rx.buf;
> +               rev->num_protocols = attr_info->num_protocols;
> +               rev->num_agents = attr_info->num_agents;
> +       }
> +
> +       scmi_put_one_xfer(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(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_put_one_xfer(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(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 = (u32 *)t->rx.buf;
> +               rev->impl_ver = le32_to_cpu(*impl_ver);
> +       }
> +

Should be (!ret)

> +       scmi_put_one_xfer(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(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 = (__le32 *)t->tx.buf;
> +       num_ret = (__le32 *)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_put_one_xfer(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(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_put_one_xfer(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(struct scmi_handle *handle,
> +                                               bool enable)
> +{
> +       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(enable & BIT(0));
> +
> +       ret = scmi_do_xfer(handle, t);
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +int scmi_base_protocol_init(struct scmi_handle *handle)
> +{
> +       int id, ret;
> +       u8 *prot_imp;
> +       u32 version;
> +       char name[SCMI_MAX_STR_SIZE];
> +       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 %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 a3038efa3a8d..24bc51dcc6c5 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;
> +} __packed;
> +
>  /**
>   * struct scmi_msg_hdr - Message(Tx/Rx) header
>   *
> @@ -72,3 +113,8 @@ void scmi_put_one_xfer(struct scmi_handle *h, struct scmi_xfer *xfer);
>  int scmi_do_xfer(struct scmi_handle *h, struct scmi_xfer *xfer);
>  int scmi_one_xfer_init(struct scmi_handle *h, u8 msg_id, u8 msg_prot_id,
>                        size_t tx_size, size_t rx_size, struct scmi_xfer **p);
> +int scmi_version_get(struct scmi_handle *h, u8 protocol, u32 *version);
> +bool scmi_is_protocol_implemented(struct scmi_handle *h, u8 prot_id);
> +void scmi_setup_protocol_implemented(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 f01e0643ac7d..7b653c932edc 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;
>         struct list_head node;
>         int users;
>  };
> @@ -445,6 +450,57 @@ int scmi_one_xfer_init(struct scmi_handle *handle, u8 msg_id, u8 msg_prot_id,
>  }
>
>  /**
> + * scmi_version_get() - command to get the revision of the SCMI entity
> + *
> + * @handle: Handle to SCMI entity information
> + *
> + * Updates the SCMI information in the internal data structure.
> + *
> + * Return: 0 if all went fine, else return appropriate error.
> + */
> +int scmi_version_get(struct scmi_handle *handle, u8 protocol, u32 *version)
> +{
> +       int ret;
> +       __le32 *rev_info;
> +       struct scmi_xfer *t;
> +
> +       ret = scmi_one_xfer_init(handle, PROTOCOL_VERSION, protocol, 0,
> +                                sizeof(*version), &t);
> +       if (ret)
> +               return ret;
> +
> +       rev_info = (__le32 *)t->rx.buf;
> +
> +       ret = scmi_do_xfer(handle, t);
> +       if (!ret)
> +               *version = le32_to_cpu(*rev_info);
> +
> +       scmi_put_one_xfer(handle, t);
> +       return ret;
> +}
> +
> +void scmi_setup_protocol_implemented(struct scmi_handle *handle, u8 *prot_imp)
> +{
> +       struct scmi_info *info = handle_to_scmi_info(handle);
> +
> +       info->protocols_imp = prot_imp;
> +}
> +
> +bool scmi_is_protocol_implemented(struct scmi_handle *handle, u8 prot_id)
> +{
> +       int i;
> +       struct scmi_info *info = handle_to_scmi_info(handle);
> +
> +       if (!info->protocols_imp)
> +               return false;
> +
> +       for (i = 0; i < MAX_PROTOCOLS_IMP; i++)
> +               if (info->protocols_imp[i] == prot_id)
> +                       return true;
> +       return false;
> +}
> +
> +/**
>   * scmi_handle_get() - Get the  SCMI handle for a device
>   *
>   * @dev: pointer to device for which we want SCMI handle
> @@ -642,6 +698,11 @@ static int scmi_probe(struct platform_device *pdev)
>
>         desc = of_match_device(scmi_of_match, dev)->data;
>
> +       if (of_property_match_string(np, "method", "mailbox-doorbell") < 0) {
> +               dev_err(dev, "invalid method property in %s\n", np->full_name);
> +               return -EINVAL;
> +       }
> +
>         info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
>         if (!info)
>                 return -ENOMEM;
> @@ -687,6 +748,12 @@ static int scmi_probe(struct platform_device *pdev)
>
>         handle = &info->handle;
>         handle->dev = info->dev;
> +       handle->version = &info->version;
> +       ret = scmi_base_protocol_init(handle);
> +       if (ret) {
> +               dev_err(dev, "unable to communicate with SCMI(%d)\n", ret);
> +               goto out;
> +       }
>
>         mutex_lock(&scmi_list_mutex);
>         list_add_tail(&info->node, &scmi_list);
> diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h
> index 0c795a765110..901976fe211f 100644
> --- a/include/linux/scmi_protocol.h
> +++ b/include/linux/scmi_protocol.h
> @@ -17,13 +17,41 @@
>   */
>  #include <linux/types.h>
>
> +#define SCMI_MAX_STR_SIZE              16
> +
> +/**
> + * struct scmi_revision_info - version information structure
> + *
> + * @major_ver: Major ABI version. Change here implies risk of backward
> + *     compatibility break.
> + * @minor_ver: Minor ABI version. Change here implies new feature addition,
> + *     or compatible change in ABI.
> + * @num_protocols: Number of protocols that are implemented, excluding the
> + *     base protocol.
> + * @num_agents: Number of agents in the system.
> + * @impl_ver: A vendor-specific implementation version.
> + * @vendor_id: A vendor identifier(Null terminated ASCII string)
> + * @sub_vendor_id: A sub-vendor identifier(Null terminated ASCII string)
> + */
> +struct scmi_revision_info {
> +       u16 major_ver;
> +       u16 minor_ver;
> +       u8 num_protocols;
> +       u8 num_agents;
> +       u32 impl_ver;
> +       char vendor_id[SCMI_MAX_STR_SIZE];
> +       char sub_vendor_id[SCMI_MAX_STR_SIZE];
> +};
> +
>  /**
>   * struct scmi_handle - Handle returned to ARM SCMI clients for usage.
>   *
>   * @dev: pointer to the SCMI device
> + * @version: pointer to the structure containing SCMI version information
>   */
>  struct scmi_handle {
>         struct device *dev;
> +       struct scmi_revision_info *version;
>  };
>
>  #if IS_REACHABLE(CONFIG_ARM_SCMI_PROTOCOL)
> --
> 2.7.4
>
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]
  Powered by Linux