> Subject: [PATCH v2 1/5] firmware: arm_scmi: Add discovery of SCMI v2.0
> performance fastchannels
> 
> SCMI v2.0 adds support for "FastChannel", a lightweight unidirectional
> channel that is dedicated to a single SCMI message type for controlling a
> specific platform resource. They do not use a message header as they are
> specialized for a single message.
> 
> Only PERFORMANCE_LIMITS_{SET,GET} and
> PERFORMANCE_LEVEL_{SET,GET} commands are supported over
> fastchannels. As they are optional, they need to be discovered by
> PERFORMANCE_DESCRIBE_FASTCHANNEL command.
> Further {LIMIT,LEVEL}_SET commands can have optional doorbell support.
> 
> Add support for discovery of these fastchannels.
> 
> Cc: Ionela Voinescu <ionela.voine...@arm.com>
> Cc: Chris Redpath <chris.redp...@arm.com>
> Cc: Quentin Perret <quentin.per...@arm.com>
> Signed-off-by: Sudeep Holla <sudeep.ho...@arm.com>
> ---
>  drivers/firmware/arm_scmi/perf.c | 153
> ++++++++++++++++++++++++++++++-
>  1 file changed, 149 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/firmware/arm_scmi/perf.c
> b/drivers/firmware/arm_scmi/perf.c
> index 3c8ae7cc35de..6cce3e82e81e 100644
> --- a/drivers/firmware/arm_scmi/perf.c
> +++ b/drivers/firmware/arm_scmi/perf.c
> @@ -5,7 +5,9 @@
>   * Copyright (C) 2018 ARM Ltd.
>   */
> 
> +#include <linux/bits.h>
>  #include <linux/of.h>
> +#include <linux/io.h>
>  #include <linux/platform_device.h>
>  #include <linux/pm_opp.h>
>  #include <linux/sort.h>
> @@ -21,6 +23,7 @@ enum scmi_performance_protocol_cmd {
>       PERF_LEVEL_GET = 0x8,
>       PERF_NOTIFY_LIMITS = 0x9,
>       PERF_NOTIFY_LEVEL = 0xa,
> +     PERF_DESCRIBE_FASTCHANNEL = 0xb,
>  };
> 
>  struct scmi_opp {
> @@ -44,6 +47,7 @@ struct scmi_msg_resp_perf_domain_attributes {
>  #define SUPPORTS_SET_PERF_LVL(x)     ((x) & BIT(30))
>  #define SUPPORTS_PERF_LIMIT_NOTIFY(x)        ((x) & BIT(29))
>  #define SUPPORTS_PERF_LEVEL_NOTIFY(x)        ((x) & BIT(28))
> +#define SUPPORTS_PERF_FASTCHANNELS(x)        ((x) & BIT(27))
>       __le32 rate_limit_us;
>       __le32 sustained_freq_khz;
>       __le32 sustained_perf_level;
> @@ -87,17 +91,56 @@ struct scmi_msg_resp_perf_describe_levels {
>       } opp[0];
>  };
> 
> +struct scmi_perf_get_fc_info {
> +     __le32 domain;
> +     __le32 message_id;
> +};
> +
> +struct scmi_msg_resp_perf_desc_fc {
> +     __le32 attr;
> +#define SUPPORTS_DOORBELL(x)         ((x) & BIT(0))
> +#define DOORBELL_REG_WIDTH(x)                FIELD_GET(GENMASK(2, 1), (x))
> +     __le32 rate_limit;
> +     __le32 chan_addr_low;
> +     __le32 chan_addr_high;
> +     __le32 chan_size;
> +     __le32 db_addr_low;
> +     __le32 db_addr_high;
> +     __le32 db_set_lmask;
> +     __le32 db_set_hmask;
> +     __le32 db_preserve_lmask;
> +     __le32 db_preserve_hmask;
> +};
> +
> +struct scmi_fc_db_info {
> +     int width;
> +     u64 set;
> +     u64 mask;
> +     void __iomem *addr;
> +};
> +
> +struct scmi_fc_info {
> +     void __iomem *level_set_addr;
> +     void __iomem *limit_set_addr;
> +     void __iomem *level_get_addr;
> +     void __iomem *limit_get_addr;
> +     struct scmi_fc_db_info *level_set_db;
> +     struct scmi_fc_db_info *limit_set_db;
> +};
> +
>  struct perf_dom_info {
>       bool set_limits;
>       bool set_perf;
>       bool perf_limit_notify;
>       bool perf_level_notify;
> +     bool perf_fastchannels;
>       u32 opp_count;
>       u32 sustained_freq_khz;
>       u32 sustained_perf_level;
>       u32 mult_factor;
>       char name[SCMI_MAX_STR_SIZE];
>       struct scmi_opp opp[MAX_OPPS];
> +     struct scmi_fc_info *fc_info;
>  };
> 
>  struct scmi_perf_info {
> @@ -162,6 +205,7 @@ scmi_perf_domain_attributes_get(const struct
> scmi_handle *handle, u32 domain,
>               dom_info->set_perf = SUPPORTS_SET_PERF_LVL(flags);
>               dom_info->perf_limit_notify =
> SUPPORTS_PERF_LIMIT_NOTIFY(flags);
>               dom_info->perf_level_notify =
> SUPPORTS_PERF_LEVEL_NOTIFY(flags);
> +             dom_info->perf_fastchannels =
> SUPPORTS_PERF_FASTCHANNELS(flags);
>               dom_info->sustained_freq_khz =
>                                       le32_to_cpu(attr->sustained_freq_khz);
>               dom_info->sustained_perf_level =
> @@ -250,7 +294,7 @@ scmi_perf_describe_levels_get(const struct
> scmi_handle *handle, u32 domain,  }
> 
>  static int scmi_perf_limits_set(const struct scmi_handle *handle, u32
> domain,
> -                             u32 max_perf, u32 min_perf)
> +                                u32 max_perf, u32 min_perf)
>  {
>       int ret;
>       struct scmi_xfer *t;
> @@ -273,7 +317,7 @@ static int scmi_perf_limits_set(const struct
> scmi_handle *handle, u32 domain,  }
> 
>  static int scmi_perf_limits_get(const struct scmi_handle *handle, u32
> domain,
> -                             u32 *max_perf, u32 *min_perf)
> +                                u32 *max_perf, u32 *min_perf)
>  {
>       int ret;
>       struct scmi_xfer *t;
> @@ -299,7 +343,7 @@ static int scmi_perf_limits_get(const struct
> scmi_handle *handle, u32 domain,  }
> 
>  static int scmi_perf_level_set(const struct scmi_handle *handle, u32
> domain,
> -                            u32 level, bool poll)
> +                               u32 level, bool poll)
>  {
>       int ret;
>       struct scmi_xfer *t;
> @@ -322,7 +366,7 @@ static int scmi_perf_level_set(const struct
> scmi_handle *handle, u32 domain,  }
> 
>  static int scmi_perf_level_get(const struct scmi_handle *handle, u32
> domain,
> -                            u32 *level, bool poll)
> +                               u32 *level, bool poll)
>  {
>       int ret;
>       struct scmi_xfer *t;
> @@ -343,6 +387,104 @@ static int scmi_perf_level_get(const struct
> scmi_handle *handle, u32 domain,
>       return ret;
>  }
> 
> +static bool scmi_perf_fc_size_is_valid(u32 msg, u32 size) {
> +     if ((msg == PERF_LEVEL_GET || msg == PERF_LEVEL_SET) && size == 4)
> +             return true;
> +     if ((msg == PERF_LIMITS_GET || msg == PERF_LIMITS_SET) && size == 8)
> +             return true;
> +     return false;
> +}
> +
> +static void
> +scmi_perf_domain_desc_fc(const struct scmi_handle *handle, u32 domain,
> +                      u32 message_id, void __iomem **p_addr,
> +                      struct scmi_fc_db_info **p_db)
> +{
> +     int ret;
> +     u32 flags;
> +     u64 phys_addr;
> +     u8 size;
> +     void __iomem *addr;
> +     struct scmi_xfer *t;
> +     struct scmi_fc_db_info *db;
> +     struct scmi_perf_get_fc_info *info;
> +     struct scmi_msg_resp_perf_desc_fc *resp;
> +
> +     if (!p_addr)
> +             return;
> +
> +     ret = scmi_xfer_get_init(handle, PERF_DESCRIBE_FASTCHANNEL,
> +                              SCMI_PROTOCOL_PERF,
> +                              sizeof(*info), sizeof(*resp), &t);
> +     if (ret)
> +             return;
> +
> +     info = t->tx.buf;
> +     info->domain = cpu_to_le32(domain);
> +     info->message_id = cpu_to_le32(message_id);
> +
> +     ret = scmi_do_xfer(handle, t);
> +     if (ret)
> +             goto err_xfer;
> +
> +     resp = t->rx.buf;
> +     flags = le32_to_cpu(resp->attr);
> +     size = le32_to_cpu(resp->chan_size);
> +     if (!scmi_perf_fc_size_is_valid(message_id, size))
> +             goto err_xfer;
> +
> +     phys_addr = le32_to_cpu(resp->chan_addr_low);
> +     phys_addr |= (u64)le32_to_cpu(resp->chan_addr_high) << 32;
> +     addr = devm_ioremap(handle->dev, phys_addr, size);
> +     if (!addr)
> +             goto err_xfer;
> +     *p_addr = addr;
> +
> +     if (p_db && SUPPORTS_DOORBELL(flags)) {
> +             db = devm_kzalloc(handle->dev, sizeof(*db), GFP_KERNEL);
> +             if (!db)
> +                     goto err_xfer;
> +
> +             size = 1 << DOORBELL_REG_WIDTH(flags);
> +             phys_addr = le32_to_cpu(resp->db_addr_low);
> +             phys_addr |= (u64)le32_to_cpu(resp->db_addr_high) << 32;
> +             addr = devm_ioremap(handle->dev, phys_addr, size);
> +             if (!addr)
> +                     goto err_xfer;
> +
> +             db->addr = addr;
> +             db->width = size;
> +             db->set = le32_to_cpu(resp->db_set_lmask);
> +             db->set |= (u64)le32_to_cpu(resp->db_set_hmask) << 32;
> +             db->mask = le32_to_cpu(resp->db_preserve_lmask);
> +             db->mask |= (u64)le32_to_cpu(resp->db_preserve_hmask) << 32;
> +             *p_db = db;
> +     }
> +err_xfer:
> +     scmi_xfer_put(handle, t);
> +}
> +
> +static void scmi_perf_domain_init_fc(const struct scmi_handle *handle,
> +                                  u32 domain, struct scmi_fc_info **p_fc) {
> +     struct scmi_fc_info *fc;
> +
> +     fc = devm_kzalloc(handle->dev, sizeof(*fc), GFP_KERNEL);
> +     if (!fc)
> +             return;
> +
> +     scmi_perf_domain_desc_fc(handle, domain, PERF_LEVEL_SET,
> +                              &fc->level_set_addr, &fc->level_set_db);
> +     scmi_perf_domain_desc_fc(handle, domain, PERF_LEVEL_GET,
> +                              &fc->level_get_addr, NULL);
> +     scmi_perf_domain_desc_fc(handle, domain, PERF_LIMITS_SET,
> +                              &fc->limit_set_addr, &fc->limit_set_db);
> +     scmi_perf_domain_desc_fc(handle, domain, PERF_LIMITS_GET,
> +                              &fc->limit_get_addr, NULL);
> +     *p_fc = fc;
> +}
> +
>  /* Device specific ops */
>  static int scmi_dev_domain_id(struct device *dev)  { @@ -494,6 +636,9
> @@ static int scmi_perf_protocol_init(struct scmi_handle *handle)
> 
>               scmi_perf_domain_attributes_get(handle, domain, dom);
>               scmi_perf_describe_levels_get(handle, domain, dom);
> +
> +             if (dom->perf_fastchannels)
> +                     scmi_perf_domain_init_fc(handle, domain, &dom->fc_info);
>       }
> 
>       handle->perf_ops = &perf_ops;

Reviewed-by: Peng Fan <peng....@nxp.com>

> --
> 2.17.1

Reply via email to