// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Management Interface (SCMI) Clock Protocol
*
* Copyright (C) 2018-2022 ARM Ltd.
*/
#include <linux/module.h>
#include <linux/limits.h>
#include <linux/sort.h>
#include "protocols.h"
#include "notify.h"
/* Updated only after ALL the mandatory features for that version are merged */
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
enum scmi_clock_protocol_cmd {
CLOCK_ATTRIBUTES = 0x3,
CLOCK_DESCRIBE_RATES = 0x4,
CLOCK_RATE_SET = 0x5,
CLOCK_RATE_GET = 0x6,
CLOCK_CONFIG_SET = 0x7,
CLOCK_NAME_GET = 0x8,
CLOCK_RATE_NOTIFY = 0x9,
CLOCK_RATE_CHANGE_REQUESTED_NOTIFY = 0xA,
CLOCK_CONFIG_GET = 0xB,
CLOCK_POSSIBLE_PARENTS_GET = 0xC,
CLOCK_PARENT_SET = 0xD,
CLOCK_PARENT_GET = 0xE,
CLOCK_GET_PERMISSIONS = 0xF,
};
#define CLOCK_STATE_CONTROL_ALLOWED BIT(31)
#define CLOCK_PARENT_CONTROL_ALLOWED BIT(30)
#define CLOCK_RATE_CONTROL_ALLOWED BIT(29)
enum clk_state {
CLK_STATE_DISABLE,
CLK_STATE_ENABLE,
CLK_STATE_RESERVED,
CLK_STATE_UNCHANGED,
};
struct scmi_msg_resp_clock_protocol_attributes {
__le16 num_clocks;
u8 max_async_req;
u8 reserved;
};
struct scmi_msg_resp_clock_attributes {
__le32 attributes;
#define SUPPORTS_RATE_CHANGED_NOTIF(x) ((x) & BIT(31))
#define SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(x) ((x) & BIT(30))
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29))
#define SUPPORTS_PARENT_CLOCK(x) ((x) & BIT(28))
#define SUPPORTS_EXTENDED_CONFIG(x) ((x) & BIT(27))
#define SUPPORTS_GET_PERMISSIONS(x) ((x) & BIT(1))
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
__le32 clock_enable_latency;
};
struct scmi_msg_clock_possible_parents {
__le32 id;
__le32 skip_parents;
};
struct scmi_msg_resp_clock_possible_parents {
__le32 num_parent_flags;
#define NUM_PARENTS_RETURNED(x) ((x) & 0xff)
#define NUM_PARENTS_REMAINING(x) ((x) >> 24)
__le32 possible_parents[];
};
struct scmi_msg_clock_set_parent {
__le32 id;
__le32 parent_id;
};
struct scmi_msg_clock_config_set {
__le32 id;
__le32 attributes;
};
/* Valid only from SCMI clock v2.1 */
struct scmi_msg_clock_config_set_v2 {
__le32 id;
__le32 attributes;
#define NULL_OEM_TYPE 0
#define REGMASK_OEM_TYPE_SET GENMASK(23, 16)
#define REGMASK_CLK_STATE GENMASK(1, 0)
__le32 oem_config_val;
};
struct scmi_msg_clock_config_get {
__le32 id;
__le32 flags;
#define REGMASK_OEM_TYPE_GET GENMASK(7, 0)
};
struct scmi_msg_resp_clock_config_get {
__le32 attributes;
__le32 config;
#define IS_CLK_ENABLED(x) le32_get_bits((x), BIT(0))
__le32 oem_config_val;
};
struct scmi_msg_clock_describe_rates {
__le32 id;
__le32 rate_index;
};
struct scmi_msg_resp_clock_describe_rates {
__le32 num_rates_flags;
#define NUM_RETURNED(x) ((x) & 0xfff)
#define RATE_DISCRETE(x) !((x) & BIT(12))
#define NUM_REMAINING(x) ((x) >> 16)
struct {
__le32 value_low;
__le32 value_high;
} rate[];
#define RATE_TO_U64(X) \
({ \
typeof(X) x = (X); \
le32_to_cpu((x).value_low) | (u64)le32_to_cpu((x).value_high) << 32; \
})
};
struct scmi_clock_set_rate {
__le32 flags;
#define CLOCK_SET_ASYNC BIT(0)
#define CLOCK_SET_IGNORE_RESP BIT(1)
#define CLOCK_SET_ROUND_UP BIT(2)
#define CLOCK_SET_ROUND_AUTO BIT(3)
__le32 id;
__le32 value_low;
__le32 value_high;
};
struct scmi_msg_resp_set_rate_complete {
__le32 id;
__le32 rate_low;
__le32 rate_high;
};
struct scmi_msg_clock_rate_notify {
__le32 clk_id;
__le32 notify_enable;
};
struct scmi_clock_rate_notify_payld {
__le32 agent_id;
__le32 clock_id;
__le32 rate_low;
__le32 rate_high;
};
struct clock_info {
u32 version;
int num_clocks;
int max_async_req;
bool notify_rate_changed_cmd;
bool notify_rate_change_requested_cmd;
atomic_t cur_async_req;
struct scmi_clock_info *clk;
int (*clock_config_set)(const struct scmi_protocol_handle *ph,
u32 clk_id, enum clk_state state,
enum scmi_clock_oem_config oem_type,
u32 oem_val, bool atomic);
int (*clock_config_get)(const struct scmi_protocol_handle *ph,
u32 clk_id, enum scmi_clock_oem_config oem_type,
u32 *attributes, bool *enabled, u32 *oem_val,
bool atomic);
};
static enum scmi_clock_protocol_cmd evt_2_cmd[] = {
CLOCK_RATE_NOTIFY,
CLOCK_RATE_CHANGE_REQUESTED_NOTIFY,
};
static inline struct scmi_clock_info *
scmi_clock_domain_lookup(struct clock_info *ci, u32 clk_id)
{
if (clk_id >= ci->num_clocks)
return ERR_PTR(-EINVAL);
return ci->clk + clk_id;
}
static int
scmi_clock_protocol_attributes_get(const struct scmi_protocol_handle *ph,
struct clock_info *ci)
{
int ret;
struct scmi_xfer *t;
struct scmi_msg_resp_clock_protocol_attributes *attr;
ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
0, sizeof(*attr), &t);
if (ret)
return ret;
attr = t->rx.buf;
ret = ph->xops->do_xfer(ph, t);
if (!ret) {
ci->num_clocks = le16_to_cpu(attr->num_clocks);
ci->max_async_req = attr->max_async_req;
}
ph->xops->xfer_put(ph, t);
if (!ret) {
if (!ph->hops->protocol_msg_check(ph, CLOCK_RATE_NOTIFY, NULL))
ci->notify_rate_changed_cmd = true;