// SPDX-License-Identifier: GPL-2.0
/*
* System Control and Management Interface (SCMI) Powercap Protocol
*
* Copyright (C) 2022 ARM Ltd.
*/
#define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt
#include <linux/bitfield.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/scmi_protocol.h>
#include <trace/events/scmi.h>
#include "protocols.h"
#include "notify.h"
/* Updated only after ALL the mandatory features for that version are merged */
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000
enum scmi_powercap_protocol_cmd {
POWERCAP_DOMAIN_ATTRIBUTES = 0x3,
POWERCAP_CAP_GET = 0x4,
POWERCAP_CAP_SET = 0x5,
POWERCAP_PAI_GET = 0x6,
POWERCAP_PAI_SET = 0x7,
POWERCAP_DOMAIN_NAME_GET = 0x8,
POWERCAP_MEASUREMENTS_GET = 0x9,
POWERCAP_CAP_NOTIFY = 0xa,
POWERCAP_MEASUREMENTS_NOTIFY = 0xb,
POWERCAP_DESCRIBE_FASTCHANNEL = 0xc,
};
enum {
POWERCAP_FC_CAP,
POWERCAP_FC_PAI,
POWERCAP_FC_MAX,
};
struct scmi_msg_resp_powercap_domain_attributes {
__le32 attributes;
#define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(x) ((x) & BIT(31))
#define SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(x) ((x) & BIT(30))
#define SUPPORTS_ASYNC_POWERCAP_CAP_SET(x) ((x) & BIT(29))
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(28))
#define SUPPORTS_POWERCAP_CAP_CONFIGURATION(x) ((x) & BIT(27))
#define SUPPORTS_POWERCAP_MONITORING(x) ((x) & BIT(26))
#define SUPPORTS_POWERCAP_PAI_CONFIGURATION(x) ((x) & BIT(25))
#define SUPPORTS_POWERCAP_FASTCHANNELS(x) ((x) & BIT(22))
#define POWERCAP_POWER_UNIT(x) \
(FIELD_GET(GENMASK(24, 23), (x)))
#define SUPPORTS_POWER_UNITS_MW(x) \
(POWERCAP_POWER_UNIT(x) == 0x2)
#define SUPPORTS_POWER_UNITS_UW(x) \
(POWERCAP_POWER_UNIT(x) == 0x1)
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
__le32 min_pai;
__le32 max_pai;
__le32 pai_step;
__le32 min_power_cap;
__le32 max_power_cap;
__le32 power_cap_step;
__le32 sustainable_power;
__le32 accuracy;
__le32 parent_id;
};
struct scmi_msg_powercap_set_cap_or_pai {
__le32 domain;
__le32 flags;
#define CAP_SET_ASYNC BIT(1)
#define CAP_SET_IGNORE_DRESP BIT(0)
__le32 value;
};
struct scmi_msg_resp_powercap_cap_set_complete {
__le32 domain;
__le32 power_cap;
};
struct scmi_msg_resp_powercap_meas_get {
__le32 power;
__le32 pai;
};
struct scmi_msg_powercap_notify_cap {
__le32 domain;
__le32 notify_enable;
};
struct scmi_msg_powercap_notify_thresh {
__le32 domain;
__le32 notify_enable;
__le32 power_thresh_low;
__le32 power_thresh_high;
};
struct scmi_powercap_cap_changed_notify_payld {
__le32 agent_id;
__le32 domain_id;
__le32 power_cap;
__le32 pai;
};
struct scmi_powercap_meas_changed_notify_payld {
__le32 agent_id;
__le32 domain_id;
__le32 power;
};
struct scmi_powercap_state {
bool enabled;
u32 last_pcap;
bool meas_notif_enabled;
u64 thresholds;
#define THRESH_LOW(p, id) \
(lower_32_bits((p)->states[(id)].thresholds))
#define THRESH_HIGH(p, id) \
(upper_32_bits((p)->states[(id)].thresholds))
};
struct powercap_info {
u32 version;
int num_domains;
bool notify_cap_cmd;
bool notify_measurements_cmd;
struct scmi_powercap_state *states;
struct scmi_powercap_info *powercaps;
};
static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {
POWERCAP_CAP_NOTIFY,
POWERCAP_MEASUREMENTS_NOTIFY,
};
static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
u32 domain, int message_id, bool enable);
static int
scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph,
struct powercap_info *pi)
{
int ret;
struct scmi_xfer *t;
ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
sizeof(u32), &t);
if (ret)
return ret;
ret = ph->xops->do_xfer(ph, t);
if (!ret) {
u32 attributes;
attributes = get_unaligned_le32(t->rx.buf);
pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes);
}
ph->xops->xfer_put(ph, t);
if (!ret) {
if (!ph->hops->protocol_msg_check(ph,
POWERCAP_CAP_NOTIFY, NULL))
pi->notify_cap_cmd = true;
if (!ph->hops->protocol_msg_check(ph,
POWERCAP_MEASUREMENTS_NOTIFY,
NULL))
pi->notify_measurements_cmd = true;
}
return ret;
}
static inline int
scmi_powercap_validate(unsigned int min_val, unsigned int max_val,
unsigned int step_val, bool configurable)