- const: samsung,exynos7-hsi2c
- items:
- enum:
+ - google,gs101-hsi2c
- samsung,exynos850-hsi2c
- const: samsung,exynosautov9-hsi2c
- const: samsung,exynos5-hsi2c # Exynos5250 and Exynos5420
"^emc-table@[0-9]+$":
$ref: "#/$defs/emc-table"
- "^emc-tables@[a-z0-9-]+$":
+ "^emc-tables@[a-f0-9-]+$":
type: object
properties:
reg:
compatible:
contains:
enum:
+ - google,gs101-peric0-sysreg
+ - google,gs101-peric1-sysreg
- samsung,exynos850-cmgp-sysreg
- samsung,exynos850-peri-sysreg
- samsung,exynos850-sysreg
F: drivers/pinctrl/renesas/
PIN CONTROLLER - SAMSUNG
-M: Tomasz Figa <tomasz.figa@gmail.com>
M: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
M: Sylwester Nawrocki <s.nawrocki@samsung.com>
R: Alim Akhtar <alim.akhtar@samsung.com>
SAMSUNG SOC CLOCK DRIVERS
M: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
M: Sylwester Nawrocki <s.nawrocki@samsung.com>
-M: Tomasz Figa <tomasz.figa@gmail.com>
M: Chanwoo Choi <cw00.choi@samsung.com>
R: Alim Akhtar <alim.akhtar@samsung.com>
L: linux-samsung-soc@vger.kernel.org
F: drivers/cpufreq/sc[mp]i-cpufreq.c
F: drivers/firmware/arm_scmi/
F: drivers/firmware/arm_scpi.c
+F: drivers/hwmon/scmi-hwmon.c
F: drivers/pmdomain/arm/
F: drivers/powercap/arm_scmi_powercap.c
F: drivers/regulator/scmi-regulator.c
config TEGRA_ACONNECT
tristate "Tegra ACONNECT Bus Driver"
- depends on ARCH_TEGRA_210_SOC
+ depends on ARCH_TEGRA
depends on OF && PM
help
Driver for the Tegra ACONNECT bus which is used to interface with
- the devices inside the Audio Processing Engine (APE) for Tegra210.
+ the devices inside the Audio Processing Engine (APE) for
+ Tegra210 and later.
config TEGRA_GMI
tristate "Tegra Generic Memory Interface bus driver"
};
ATTRIBUTE_GROUPS(ffa_device_attributes);
-struct bus_type ffa_bus_type = {
+const struct bus_type ffa_bus_type = {
.name = "arm_ffa",
.match = ffa_device_match,
.probe = ffa_device_probe,
return ret;
}
+static int scmi_protocol_table_register(const struct scmi_device_id *id_table)
+{
+ int ret = 0;
+ const struct scmi_device_id *entry;
+
+ for (entry = id_table; entry->name && ret == 0; entry++)
+ ret = scmi_protocol_device_request(entry);
+
+ return ret;
+}
+
/**
* scmi_protocol_device_unrequest - Helper to unrequest a device
*
mutex_unlock(&scmi_requested_devices_mtx);
}
+static void
+scmi_protocol_table_unregister(const struct scmi_device_id *id_table)
+{
+ const struct scmi_device_id *entry;
+
+ for (entry = id_table; entry->name; entry++)
+ scmi_protocol_device_unrequest(entry);
+}
+
static const struct scmi_device_id *
scmi_dev_match_id(struct scmi_device *scmi_dev, struct scmi_driver *scmi_drv)
{
scmi_drv->remove(scmi_dev);
}
-struct bus_type scmi_bus_type = {
+const struct bus_type scmi_bus_type = {
.name = "scmi_protocol",
.match = scmi_dev_match,
.probe = scmi_dev_probe,
if (!driver->probe)
return -EINVAL;
- retval = scmi_protocol_device_request(driver->id_table);
+ retval = scmi_protocol_table_register(driver->id_table);
if (retval)
return retval;
void scmi_driver_unregister(struct scmi_driver *driver)
{
driver_unregister(&driver->driver);
- scmi_protocol_device_unrequest(driver->id_table);
+ scmi_protocol_table_unregister(driver->id_table);
}
EXPORT_SYMBOL_GPL(scmi_driver_unregister);
#include "notify.h"
/* Updated only after ALL the mandatory features for that version are merged */
-#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20001
+#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30000
enum scmi_clock_protocol_cmd {
CLOCK_ATTRIBUTES = 0x3,
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,
#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;
};
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,
- u8 oem_type, u32 oem_val, bool atomic);
+ 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, u8 oem_type, u32 *attributes,
- bool *enabled, u32 *oem_val, bool atomic);
+ 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_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)
}
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;
+
+ if (!ph->hops->protocol_msg_check(ph,
+ CLOCK_RATE_CHANGE_REQUESTED_NOTIFY,
+ NULL))
+ ci->notify_rate_change_requested_cmd = true;
+ }
+
return ret;
}
return ret;
}
+static int
+scmi_clock_get_permissions(const struct scmi_protocol_handle *ph, u32 clk_id,
+ struct scmi_clock_info *clk)
+{
+ struct scmi_xfer *t;
+ u32 perm;
+ int ret;
+
+ ret = ph->xops->xfer_get_init(ph, CLOCK_GET_PERMISSIONS,
+ sizeof(clk_id), sizeof(perm), &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(clk_id, t->tx.buf);
+
+ ret = ph->xops->do_xfer(ph, t);
+ if (!ret) {
+ perm = get_unaligned_le32(t->rx.buf);
+
+ clk->state_ctrl_forbidden = !(perm & CLOCK_STATE_CONTROL_ALLOWED);
+ clk->rate_ctrl_forbidden = !(perm & CLOCK_RATE_CONTROL_ALLOWED);
+ clk->parent_ctrl_forbidden = !(perm & CLOCK_PARENT_CONTROL_ALLOWED);
+ }
+
+ ph->xops->xfer_put(ph, t);
+
+ return ret;
+}
+
static int scmi_clock_attributes_get(const struct scmi_protocol_handle *ph,
- u32 clk_id, struct scmi_clock_info *clk,
+ u32 clk_id, struct clock_info *cinfo,
u32 version)
{
int ret;
u32 attributes;
struct scmi_xfer *t;
struct scmi_msg_resp_clock_attributes *attr;
+ struct scmi_clock_info *clk = cinfo->clk + clk_id;
ret = ph->xops->xfer_get_init(ph, CLOCK_ATTRIBUTES,
sizeof(clk_id), sizeof(*attr), &t);
NULL, clk->name,
SCMI_MAX_STR_SIZE);
- if (SUPPORTS_RATE_CHANGED_NOTIF(attributes))
+ if (cinfo->notify_rate_changed_cmd &&
+ SUPPORTS_RATE_CHANGED_NOTIF(attributes))
clk->rate_changed_notifications = true;
- if (SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes))
+ if (cinfo->notify_rate_change_requested_cmd &&
+ SUPPORTS_RATE_CHANGE_REQUESTED_NOTIF(attributes))
clk->rate_change_requested_notifications = true;
- if (SUPPORTS_PARENT_CLOCK(attributes))
- scmi_clock_possible_parents(ph, clk_id, clk);
+ if (PROTOCOL_REV_MAJOR(version) >= 0x3) {
+ if (SUPPORTS_PARENT_CLOCK(attributes))
+ scmi_clock_possible_parents(ph, clk_id, clk);
+ if (SUPPORTS_GET_PERMISSIONS(attributes))
+ scmi_clock_get_permissions(ph, clk_id, clk);
+ if (SUPPORTS_EXTENDED_CONFIG(attributes))
+ clk->extended_config = true;
+ }
}
return ret;
struct scmi_xfer *t;
struct scmi_clock_set_rate *cfg;
struct clock_info *ci = ph->get_priv(ph);
+ struct scmi_clock_info *clk;
+
+ clk = scmi_clock_domain_lookup(ci, clk_id);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ if (clk->rate_ctrl_forbidden)
+ return -EACCES;
ret = ph->xops->xfer_get_init(ph, CLOCK_RATE_SET, sizeof(*cfg), 0, &t);
if (ret)
static int
scmi_clock_config_set(const struct scmi_protocol_handle *ph, u32 clk_id,
- enum clk_state state, u8 __unused0, u32 __unused1,
+ enum clk_state state,
+ enum scmi_clock_oem_config __unused0, u32 __unused1,
bool atomic)
{
int ret;
struct clock_info *ci = ph->get_priv(ph);
struct scmi_clock_info *clk;
- if (clk_id >= ci->num_clocks)
- return -EINVAL;
-
- clk = ci->clk + clk_id;
+ clk = scmi_clock_domain_lookup(ci, clk_id);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
if (parent_id >= clk->num_parents)
return -EINVAL;
+ if (clk->parent_ctrl_forbidden)
+ return -EACCES;
+
ret = ph->xops->xfer_get_init(ph, CLOCK_PARENT_SET,
sizeof(*cfg), 0, &t);
if (ret)
return ret;
}
-/* For SCMI clock v2.1 and onwards */
+/* For SCMI clock v3.0 and onwards */
static int
scmi_clock_config_set_v2(const struct scmi_protocol_handle *ph, u32 clk_id,
- enum clk_state state, u8 oem_type, u32 oem_val,
+ enum clk_state state,
+ enum scmi_clock_oem_config oem_type, u32 oem_val,
bool atomic)
{
int ret;
bool atomic)
{
struct clock_info *ci = ph->get_priv(ph);
+ struct scmi_clock_info *clk;
+
+ clk = scmi_clock_domain_lookup(ci, clk_id);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ if (clk->state_ctrl_forbidden)
+ return -EACCES;
return ci->clock_config_set(ph, clk_id, CLK_STATE_ENABLE,
NULL_OEM_TYPE, 0, atomic);
bool atomic)
{
struct clock_info *ci = ph->get_priv(ph);
+ struct scmi_clock_info *clk;
+
+ clk = scmi_clock_domain_lookup(ci, clk_id);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ if (clk->state_ctrl_forbidden)
+ return -EACCES;
return ci->clock_config_set(ph, clk_id, CLK_STATE_DISABLE,
NULL_OEM_TYPE, 0, atomic);
}
-/* For SCMI clock v2.1 and onwards */
+/* For SCMI clock v3.0 and onwards */
static int
scmi_clock_config_get_v2(const struct scmi_protocol_handle *ph, u32 clk_id,
- u8 oem_type, u32 *attributes, bool *enabled,
- u32 *oem_val, bool atomic)
+ enum scmi_clock_oem_config oem_type, u32 *attributes,
+ bool *enabled, u32 *oem_val, bool atomic)
{
int ret;
u32 flags;
static int
scmi_clock_config_get(const struct scmi_protocol_handle *ph, u32 clk_id,
- u8 oem_type, u32 *attributes, bool *enabled,
- u32 *oem_val, bool atomic)
+ enum scmi_clock_oem_config oem_type, u32 *attributes,
+ bool *enabled, u32 *oem_val, bool atomic)
{
int ret;
struct scmi_xfer *t;
}
static int scmi_clock_config_oem_set(const struct scmi_protocol_handle *ph,
- u32 clk_id, u8 oem_type, u32 oem_val,
- bool atomic)
+ u32 clk_id,
+ enum scmi_clock_oem_config oem_type,
+ u32 oem_val, bool atomic)
{
struct clock_info *ci = ph->get_priv(ph);
+ struct scmi_clock_info *clk;
+
+ clk = scmi_clock_domain_lookup(ci, clk_id);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ if (!clk->extended_config)
+ return -EOPNOTSUPP;
return ci->clock_config_set(ph, clk_id, CLK_STATE_UNCHANGED,
oem_type, oem_val, atomic);
}
static int scmi_clock_config_oem_get(const struct scmi_protocol_handle *ph,
- u32 clk_id, u8 oem_type, u32 *oem_val,
- u32 *attributes, bool atomic)
+ u32 clk_id,
+ enum scmi_clock_oem_config oem_type,
+ u32 *oem_val, u32 *attributes, bool atomic)
{
struct clock_info *ci = ph->get_priv(ph);
+ struct scmi_clock_info *clk;
+
+ clk = scmi_clock_domain_lookup(ci, clk_id);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ if (!clk->extended_config)
+ return -EOPNOTSUPP;
return ci->clock_config_get(ph, clk_id, oem_type, attributes,
NULL, oem_val, atomic);
struct scmi_clock_info *clk;
struct clock_info *ci = ph->get_priv(ph);
- if (clk_id >= ci->num_clocks)
+ clk = scmi_clock_domain_lookup(ci, clk_id);
+ if (IS_ERR(clk))
return NULL;
- clk = ci->clk + clk_id;
if (!clk->name[0])
return NULL;
.parent_get = scmi_clock_get_parent,
};
+static bool scmi_clk_notify_supported(const struct scmi_protocol_handle *ph,
+ u8 evt_id, u32 src_id)
+{
+ bool supported;
+ struct scmi_clock_info *clk;
+ struct clock_info *ci = ph->get_priv(ph);
+
+ if (evt_id >= ARRAY_SIZE(evt_2_cmd))
+ return false;
+
+ clk = scmi_clock_domain_lookup(ci, src_id);
+ if (IS_ERR(clk))
+ return false;
+
+ if (evt_id == SCMI_EVENT_CLOCK_RATE_CHANGED)
+ supported = clk->rate_changed_notifications;
+ else
+ supported = clk->rate_change_requested_notifications;
+
+ return supported;
+}
+
static int scmi_clk_rate_notify(const struct scmi_protocol_handle *ph,
u32 clk_id, int message_id, bool enable)
{
};
static const struct scmi_event_ops clk_event_ops = {
+ .is_notify_supported = scmi_clk_notify_supported,
.get_num_sources = scmi_clk_get_num_sources,
.set_notify_enabled = scmi_clk_set_notify_enabled,
.fill_custom_report = scmi_clk_fill_custom_report,
for (clkid = 0; clkid < cinfo->num_clocks; clkid++) {
struct scmi_clock_info *clk = cinfo->clk + clkid;
- ret = scmi_clock_attributes_get(ph, clkid, clk, version);
+ ret = scmi_clock_attributes_get(ph, clkid, cinfo, version);
if (!ret)
scmi_clock_describe_rates_get(ph, clkid, clk);
}
- if (PROTOCOL_REV_MAJOR(version) >= 0x2 &&
- PROTOCOL_REV_MINOR(version) >= 0x1) {
+ if (PROTOCOL_REV_MAJOR(version) >= 0x3) {
cinfo->clock_config_set = scmi_clock_config_set_v2;
cinfo->clock_config_get = scmi_clock_config_get_v2;
} else {
void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
u8 *prot_imp);
-extern struct bus_type scmi_bus_type;
+extern const struct bus_type scmi_bus_type;
#define SCMI_BUS_NOTIFY_DEVICE_REQUEST 0
#define SCMI_BUS_NOTIFY_DEVICE_UNREQUEST 1
void shmem_clear_channel(struct scmi_shared_mem __iomem *shmem);
bool shmem_poll_done(struct scmi_shared_mem __iomem *shmem,
struct scmi_xfer *xfer);
+bool shmem_channel_free(struct scmi_shared_mem __iomem *shmem);
/* declarations for message passing transports */
struct scmi_msg_payld;
* @users: A refcount to track effective users of this protocol.
* @priv: Reference for optional protocol private data.
* @version: Protocol version supported by the platform as detected at runtime.
+ * @negotiated_version: When the platform supports a newer protocol version,
+ * the agent will try to negotiate with the platform the
+ * usage of the newest version known to it, since
+ * backward compatibility is NOT automatically assured.
+ * This field is NON-zero when a successful negotiation
+ * has completed.
* @ph: An embedded protocol handle that will be passed down to protocol
* initialization code to identify this instance.
*
refcount_t users;
void *priv;
unsigned int version;
+ unsigned int negotiated_version;
struct scmi_protocol_handle ph;
};
#endif
}
+/**
+ * scmi_protocol_msg_check - Check protocol message attributes
+ *
+ * @ph: A reference to the protocol handle.
+ * @message_id: The ID of the message to check.
+ * @attributes: A parameter to optionally return the retrieved message
+ * attributes, in case of Success.
+ *
+ * An helper to check protocol message attributes for a specific protocol
+ * and message pair.
+ *
+ * Return: 0 on SUCCESS
+ */
+static int scmi_protocol_msg_check(const struct scmi_protocol_handle *ph,
+ u32 message_id, u32 *attributes)
+{
+ int ret;
+ struct scmi_xfer *t;
+
+ ret = xfer_get_init(ph, PROTOCOL_MESSAGE_ATTRIBUTES,
+ sizeof(__le32), 0, &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(message_id, t->tx.buf);
+ ret = do_xfer(ph, t);
+ if (!ret && attributes)
+ *attributes = get_unaligned_le32(t->rx.buf);
+ xfer_put(ph, t);
+
+ return ret;
+}
+
static const struct scmi_proto_helpers_ops helpers_ops = {
.extended_name_get = scmi_common_extended_name_get,
.iter_response_init = scmi_iterator_init,
.iter_response_run = scmi_iterator_run,
+ .protocol_msg_check = scmi_protocol_msg_check,
.fastchannel_init = scmi_common_fastchannel_init,
.fastchannel_db_ring = scmi_common_fastchannel_db_ring,
};
return pi->handle->version;
}
+/**
+ * scmi_protocol_version_negotiate - Negotiate protocol version
+ *
+ * @ph: A reference to the protocol handle.
+ *
+ * An helper to negotiate a protocol version different from the latest
+ * advertised as supported from the platform: on Success backward
+ * compatibility is assured by the platform.
+ *
+ * Return: 0 on Success
+ */
+static int scmi_protocol_version_negotiate(struct scmi_protocol_handle *ph)
+{
+ int ret;
+ struct scmi_xfer *t;
+ struct scmi_protocol_instance *pi = ph_to_pi(ph);
+
+ /* At first check if NEGOTIATE_PROTOCOL_VERSION is supported ... */
+ ret = scmi_protocol_msg_check(ph, NEGOTIATE_PROTOCOL_VERSION, NULL);
+ if (ret)
+ return ret;
+
+ /* ... then attempt protocol version negotiation */
+ ret = xfer_get_init(ph, NEGOTIATE_PROTOCOL_VERSION,
+ sizeof(__le32), 0, &t);
+ if (ret)
+ return ret;
+
+ put_unaligned_le32(pi->proto->supported_version, t->tx.buf);
+ ret = do_xfer(ph, t);
+ if (!ret)
+ pi->negotiated_version = pi->proto->supported_version;
+
+ xfer_put(ph, t);
+
+ return ret;
+}
+
/**
* scmi_alloc_init_protocol_instance - Allocate and initialize a protocol
* instance descriptor.
devres_close_group(handle->dev, pi->gid);
dev_dbg(handle->dev, "Initialized protocol: 0x%X\n", pi->proto->id);
- if (pi->version > proto->supported_version)
- dev_warn(handle->dev,
- "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X."
- "Backward compatibility is NOT assured.\n",
- pi->version, pi->proto->id);
+ if (pi->version > proto->supported_version) {
+ ret = scmi_protocol_version_negotiate(&pi->ph);
+ if (!ret) {
+ dev_info(handle->dev,
+ "Protocol 0x%X successfully negotiated version 0x%X\n",
+ proto->id, pi->negotiated_version);
+ } else {
+ dev_warn(handle->dev,
+ "Detected UNSUPPORTED higher version 0x%X for protocol 0x%X.\n",
+ pi->version, pi->proto->id);
+ dev_warn(handle->dev,
+ "Trying version 0x%X. Backward compatibility is NOT assured.\n",
+ pi->proto->supported_version);
+ }
+ }
return pi;
{
struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl);
+ /*
+ * An A2P IRQ is NOT valid when received while the platform still has
+ * the ownership of the channel, because the platform at first releases
+ * the SMT channel and then sends the completion interrupt.
+ *
+ * This addresses a possible race condition in which a spurious IRQ from
+ * a previous timed-out reply which arrived late could be wrongly
+ * associated with the next pending transaction.
+ */
+ if (cl->knows_txdone && !shmem_channel_free(smbox->shmem)) {
+ dev_warn(smbox->cinfo->dev, "Ignoring spurious A2P IRQ !\n");
+ return;
+ }
+
scmi_rx_callback(smbox->cinfo, shmem_read_header(smbox->shmem), NULL);
}
#define PROTO_ID_MASK GENMASK(31, 24)
#define EVT_ID_MASK GENMASK(23, 16)
#define SRC_ID_MASK GENMASK(15, 0)
+#define NOTIF_UNSUPP -1
/*
* Builds an unsigned 32bit key from the given input tuple to be used
pd->ph = ph;
for (i = 0; i < ee->num_events; i++, evt++) {
+ int id;
struct scmi_registered_event *r_evt;
r_evt = devm_kzalloc(ni->handle->dev, sizeof(*r_evt),
if (!r_evt->report)
return -ENOMEM;
+ for (id = 0; id < r_evt->num_sources; id++)
+ if (ee->ops->is_notify_supported &&
+ !ee->ops->is_notify_supported(ph, r_evt->evt->id, id))
+ refcount_set(&r_evt->sources[id], NOTIF_UNSUPP);
+
pd->registered_events[i] = r_evt;
/* Ensure events are updated */
smp_wmb();
int ret = 0;
sid = &r_evt->sources[src_id];
- if (refcount_read(sid) == 0) {
+ if (refcount_read(sid) == NOTIF_UNSUPP) {
+ dev_dbg(r_evt->proto->ph->dev,
+ "Notification NOT supported - proto_id:%d evt_id:%d src_id:%d",
+ r_evt->proto->id, r_evt->evt->id,
+ src_id);
+ ret = -EOPNOTSUPP;
+ } else if (refcount_read(sid) == 0) {
ret = REVT_NOTIFY_ENABLE(r_evt, r_evt->evt->id,
src_id);
if (!ret)
} else {
for (; num_sources; src_id++, num_sources--) {
sid = &r_evt->sources[src_id];
+ if (refcount_read(sid) == NOTIF_UNSUPP)
+ continue;
if (refcount_dec_and_test(sid))
REVT_NOTIFY_DISABLE(r_evt,
r_evt->evt->id, src_id);
/**
* struct scmi_event_ops - Protocol helpers called by the notification core.
+ * @is_notify_supported: Return 0 if the specified notification for the
+ * specified resource (src_id) is supported.
* @get_num_sources: Returns the number of possible events' sources for this
* protocol
* @set_notify_enabled: Enable/disable the required evt_id/src_id notifications
* process context.
*/
struct scmi_event_ops {
+ bool (*is_notify_supported)(const struct scmi_protocol_handle *ph,
+ u8 evt_id, u32 src_id);
int (*get_num_sources)(const struct scmi_protocol_handle *ph);
int (*set_notify_enabled)(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id, bool enabled);
* @rx_len: Response size
* @mu: Mutex protection on channel access
* @cinfo: SCMI channel information
- * @shmem: Virtual base address of the shared memory
- * @req: Shared memory protocol handle for SCMI request and synchronous response
+ * @req: union for SCMI interface
+ * @req.shmem: Virtual base address of the shared memory
+ * @req.msg: Shared memory protocol handle for SCMI request and
+ * synchronous response
* @tee_shm: TEE shared memory handle @req or NULL if using IOMEM shmem
* @link: Reference in agent's channel list
*/
enum scmi_power_scale power_scale;
u64 stats_addr;
u32 stats_size;
+ bool notify_lvl_cmd;
+ bool notify_lim_cmd;
struct perf_dom_info *dom_info;
};
}
ph->xops->xfer_put(ph, t);
+
+ if (!ret) {
+ if (!ph->hops->protocol_msg_check(ph, PERF_NOTIFY_LEVEL, NULL))
+ pi->notify_lvl_cmd = true;
+
+ if (!ph->hops->protocol_msg_check(ph, PERF_NOTIFY_LIMITS, NULL))
+ pi->notify_lim_cmd = true;
+ }
+
return ret;
}
static int
scmi_perf_domain_attributes_get(const struct scmi_protocol_handle *ph,
struct perf_dom_info *dom_info,
+ bool notify_lim_cmd, bool notify_lvl_cmd,
u32 version)
{
int ret;
dom_info->set_limits = SUPPORTS_SET_LIMITS(flags);
dom_info->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);
+ if (notify_lim_cmd)
+ dom_info->perf_limit_notify =
+ SUPPORTS_PERF_LIMIT_NOTIFY(flags);
+ if (notify_lvl_cmd)
+ dom_info->perf_level_notify =
+ SUPPORTS_PERF_LEVEL_NOTIFY(flags);
dom_info->perf_fastchannels = SUPPORTS_PERF_FASTCHANNELS(flags);
if (PROTOCOL_REV_MAJOR(version) >= 0x4)
dom_info->level_indexing_mode =
le32_to_cpu(attr->sustained_freq_khz);
dom_info->sustained_perf_level =
le32_to_cpu(attr->sustained_perf_level);
+ /*
+ * sustained_freq_khz = mult_factor * sustained_perf_level
+ * mult_factor must be non zero positive integer(not fraction)
+ */
if (!dom_info->sustained_freq_khz ||
!dom_info->sustained_perf_level ||
- dom_info->level_indexing_mode)
+ dom_info->level_indexing_mode) {
/* CPUFreq converts to kHz, hence default 1000 */
dom_info->mult_factor = 1000;
- else
+ } else {
dom_info->mult_factor =
(dom_info->sustained_freq_khz * 1000UL)
/ dom_info->sustained_perf_level;
+ if ((dom_info->sustained_freq_khz * 1000UL) %
+ dom_info->sustained_perf_level)
+ dev_warn(ph->dev,
+ "multiplier for domain %d rounded\n",
+ dom_info->id);
+ }
+ if (!dom_info->mult_factor)
+ dev_warn(ph->dev,
+ "Wrong sustained perf/frequency(domain %d)\n",
+ dom_info->id);
+
strscpy(dom_info->info.name, attr->name,
SCMI_SHORT_NAME_MAX_SIZE);
}
dom_info->id, NULL, dom_info->info.name,
SCMI_MAX_STR_SIZE);
+ xa_init(&dom_info->opps_by_lvl);
if (dom_info->level_indexing_mode) {
xa_init(&dom_info->opps_by_idx);
- xa_init(&dom_info->opps_by_lvl);
hash_init(dom_info->opps_by_freq);
}
}
static inline void
-process_response_opp(struct scmi_opp *opp, unsigned int loop_idx,
+process_response_opp(struct device *dev, struct perf_dom_info *dom,
+ struct scmi_opp *opp, unsigned int loop_idx,
const struct scmi_msg_resp_perf_describe_levels *r)
{
+ int ret;
+
opp->perf = le32_to_cpu(r->opp[loop_idx].perf_val);
opp->power = le32_to_cpu(r->opp[loop_idx].power);
opp->trans_latency_us =
le16_to_cpu(r->opp[loop_idx].transition_latency_us);
+
+ ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
+ if (ret)
+ dev_warn(dev, "Failed to add opps_by_lvl at %d - ret:%d\n",
+ opp->perf, ret);
}
static inline void
-process_response_opp_v4(struct perf_dom_info *dom, struct scmi_opp *opp,
- unsigned int loop_idx,
+process_response_opp_v4(struct device *dev, struct perf_dom_info *dom,
+ struct scmi_opp *opp, unsigned int loop_idx,
const struct scmi_msg_resp_perf_describe_levels_v4 *r)
{
+ int ret;
+
opp->perf = le32_to_cpu(r->opp[loop_idx].perf_val);
opp->power = le32_to_cpu(r->opp[loop_idx].power);
opp->trans_latency_us =
le16_to_cpu(r->opp[loop_idx].transition_latency_us);
+ ret = xa_insert(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
+ if (ret)
+ dev_warn(dev, "Failed to add opps_by_lvl at %d - ret:%d\n",
+ opp->perf, ret);
+
/* Note that PERF v4 reports always five 32-bit words */
opp->indicative_freq = le32_to_cpu(r->opp[loop_idx].indicative_freq);
if (dom->level_indexing_mode) {
opp->level_index = le32_to_cpu(r->opp[loop_idx].level_index);
- xa_store(&dom->opps_by_idx, opp->level_index, opp, GFP_KERNEL);
- xa_store(&dom->opps_by_lvl, opp->perf, opp, GFP_KERNEL);
+ ret = xa_insert(&dom->opps_by_idx, opp->level_index, opp,
+ GFP_KERNEL);
+ if (ret)
+ dev_warn(dev,
+ "Failed to add opps_by_idx at %d - ret:%d\n",
+ opp->level_index, ret);
+
hash_add(dom->opps_by_freq, &opp->hash, opp->indicative_freq);
}
}
opp = &p->perf_dom->opp[st->desc_index + st->loop_idx];
if (PROTOCOL_REV_MAJOR(p->version) <= 0x3)
- process_response_opp(opp, st->loop_idx, response);
+ process_response_opp(ph->dev, p->perf_dom, opp, st->loop_idx,
+ response);
else
- process_response_opp_v4(p->perf_dom, opp, st->loop_idx,
+ process_response_opp_v4(ph->dev, p->perf_dom, opp, st->loop_idx,
response);
p->perf_dom->opp_count++;
.power_scale_get = scmi_power_scale_get,
};
+static bool scmi_perf_notify_supported(const struct scmi_protocol_handle *ph,
+ u8 evt_id, u32 src_id)
+{
+ bool supported;
+ struct perf_dom_info *dom;
+
+ if (evt_id >= ARRAY_SIZE(evt_2_cmd))
+ return false;
+
+ dom = scmi_perf_domain_lookup(ph, src_id);
+ if (IS_ERR(dom))
+ return false;
+
+ if (evt_id == SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED)
+ supported = dom->perf_limit_notify;
+ else
+ supported = dom->perf_level_notify;
+
+ return supported;
+}
+
static int scmi_perf_set_notify_enabled(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id, bool enable)
{
return ret;
}
+static int
+scmi_perf_xlate_opp_to_freq(struct perf_dom_info *dom,
+ unsigned int index, unsigned long *freq)
+{
+ struct scmi_opp *opp;
+
+ if (!dom || !freq)
+ return -EINVAL;
+
+ if (!dom->level_indexing_mode) {
+ opp = xa_load(&dom->opps_by_lvl, index);
+ if (!opp)
+ return -ENODEV;
+
+ *freq = opp->perf * dom->mult_factor;
+ } else {
+ opp = xa_load(&dom->opps_by_idx, index);
+ if (!opp)
+ return -ENODEV;
+
+ *freq = opp->indicative_freq * dom->mult_factor;
+ }
+
+ return 0;
+}
+
static void *scmi_perf_fill_custom_report(const struct scmi_protocol_handle *ph,
u8 evt_id, ktime_t timestamp,
const void *payld, size_t payld_sz,
void *report, u32 *src_id)
{
+ int ret;
void *rep = NULL;
+ struct perf_dom_info *dom;
switch (evt_id) {
case SCMI_EVENT_PERFORMANCE_LIMITS_CHANGED:
{
const struct scmi_perf_limits_notify_payld *p = payld;
struct scmi_perf_limits_report *r = report;
+ unsigned long freq_min, freq_max;
if (sizeof(*p) != payld_sz)
break;
r->domain_id = le32_to_cpu(p->domain_id);
r->range_max = le32_to_cpu(p->range_max);
r->range_min = le32_to_cpu(p->range_min);
+ /* Check if the reported domain exist at all */
+ dom = scmi_perf_domain_lookup(ph, r->domain_id);
+ if (IS_ERR(dom))
+ break;
+ /*
+ * Event will be reported from this point on...
+ * ...even if, later, xlated frequencies were not retrieved.
+ */
*src_id = r->domain_id;
rep = r;
+
+ ret = scmi_perf_xlate_opp_to_freq(dom, r->range_max, &freq_max);
+ if (ret)
+ break;
+
+ ret = scmi_perf_xlate_opp_to_freq(dom, r->range_min, &freq_min);
+ if (ret)
+ break;
+
+ /* Report translated freqs ONLY if both available */
+ r->range_max_freq = freq_max;
+ r->range_min_freq = freq_min;
+
break;
}
case SCMI_EVENT_PERFORMANCE_LEVEL_CHANGED:
{
const struct scmi_perf_level_notify_payld *p = payld;
struct scmi_perf_level_report *r = report;
+ unsigned long freq;
if (sizeof(*p) != payld_sz)
break;
r->timestamp = timestamp;
r->agent_id = le32_to_cpu(p->agent_id);
r->domain_id = le32_to_cpu(p->domain_id);
+ /* Report translated freqs ONLY if available */
r->performance_level = le32_to_cpu(p->performance_level);
+ /* Check if the reported domain exist at all */
+ dom = scmi_perf_domain_lookup(ph, r->domain_id);
+ if (IS_ERR(dom))
+ break;
+ /*
+ * Event will be reported from this point on...
+ * ...even if, later, xlated frequencies were not retrieved.
+ */
*src_id = r->domain_id;
rep = r;
+
+ /* Report translated freqs ONLY if available */
+ ret = scmi_perf_xlate_opp_to_freq(dom, r->performance_level,
+ &freq);
+ if (ret)
+ break;
+
+ r->performance_level_freq = freq;
+
break;
}
default:
};
static const struct scmi_event_ops perf_event_ops = {
+ .is_notify_supported = scmi_perf_notify_supported,
.get_num_sources = scmi_perf_get_num_sources,
.set_notify_enabled = scmi_perf_set_notify_enabled,
.fill_custom_report = scmi_perf_fill_custom_report,
struct perf_dom_info *dom = pinfo->dom_info + domain;
dom->id = domain;
- scmi_perf_domain_attributes_get(ph, dom, version);
+ scmi_perf_domain_attributes_get(ph, dom, pinfo->notify_lim_cmd,
+ pinfo->notify_lvl_cmd, version);
scmi_perf_describe_levels_get(ph, dom, version);
if (dom->perf_fastchannels)
struct scmi_power_info {
u32 version;
+ bool notify_state_change_cmd;
int num_domains;
u64 stats_addr;
u32 stats_size;
}
ph->xops->xfer_put(ph, t);
+
+ if (!ret)
+ if (!ph->hops->protocol_msg_check(ph, POWER_STATE_NOTIFY, NULL))
+ pi->notify_state_change_cmd = true;
+
return ret;
}
static int
scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
u32 domain, struct power_dom_info *dom_info,
- u32 version)
+ u32 version, bool notify_state_change_cmd)
{
int ret;
u32 flags;
if (!ret) {
flags = le32_to_cpu(attr->flags);
- dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags);
+ if (notify_state_change_cmd)
+ dom_info->state_set_notify =
+ SUPPORTS_STATE_SET_NOTIFY(flags);
dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);
dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
return ret;
}
+static bool scmi_power_notify_supported(const struct scmi_protocol_handle *ph,
+ u8 evt_id, u32 src_id)
+{
+ struct power_dom_info *dom;
+ struct scmi_power_info *pinfo = ph->get_priv(ph);
+
+ if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED ||
+ src_id >= pinfo->num_domains)
+ return false;
+
+ dom = pinfo->dom_info + src_id;
+ return dom->state_set_notify;
+}
+
static int scmi_power_set_notify_enabled(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id, bool enable)
{
};
static const struct scmi_event_ops power_event_ops = {
+ .is_notify_supported = scmi_power_notify_supported,
.get_num_sources = scmi_power_get_num_sources,
.set_notify_enabled = scmi_power_set_notify_enabled,
.fill_custom_report = scmi_power_fill_custom_report,
for (domain = 0; domain < pinfo->num_domains; domain++) {
struct power_dom_info *dom = pinfo->dom_info + domain;
- scmi_power_domain_attributes_get(ph, domain, dom, version);
+ scmi_power_domain_attributes_get(ph, domain, dom, version,
+ pinfo->notify_state_change_cmd);
}
pinfo->version = version;
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;
};
}
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;
}
flags = le32_to_cpu(resp->attributes);
dom_info->id = domain;
- dom_info->notify_powercap_cap_change =
- SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
- dom_info->notify_powercap_measurement_change =
- SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
+ if (pinfo->notify_cap_cmd)
+ dom_info->notify_powercap_cap_change =
+ SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
+ if (pinfo->notify_measurements_cmd)
+ dom_info->notify_powercap_measurement_change =
+ SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
dom_info->async_powercap_cap_set =
SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
dom_info->powercap_cap_config =
return ret;
}
+static bool
+scmi_powercap_notify_supported(const struct scmi_protocol_handle *ph,
+ u8 evt_id, u32 src_id)
+{
+ bool supported = false;
+ const struct scmi_powercap_info *dom_info;
+ struct powercap_info *pi = ph->get_priv(ph);
+
+ if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
+ return false;
+
+ dom_info = pi->powercaps + src_id;
+ if (evt_id == SCMI_EVENT_POWERCAP_CAP_CHANGED)
+ supported = dom_info->notify_powercap_cap_change;
+ else if (evt_id == SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED)
+ supported = dom_info->notify_powercap_measurement_change;
+
+ return supported;
+}
+
static int
scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id, bool enable)
};
static const struct scmi_event_ops powercap_event_ops = {
+ .is_notify_supported = scmi_powercap_notify_supported,
.get_num_sources = scmi_powercap_get_num_sources,
.set_notify_enabled = scmi_powercap_set_notify_enabled,
.fill_custom_report = scmi_powercap_fill_custom_report,
PROTOCOL_VERSION = 0x0,
PROTOCOL_ATTRIBUTES = 0x1,
PROTOCOL_MESSAGE_ATTRIBUTES = 0x2,
+ NEGOTIATE_PROTOCOL_VERSION = 0x10,
};
/**
* provided in @ops.
* @iter_response_run: A common helper to trigger the run of a previously
* initialized iterator.
+ * @protocol_msg_check: A common helper to check is a specific protocol message
+ * is supported.
* @fastchannel_init: A common helper used to initialize FC descriptors by
* gathering FC descriptions from the SCMI platform server.
* @fastchannel_db_ring: A common helper to ring a FC doorbell.
unsigned int max_resources, u8 msg_id,
size_t tx_size, void *priv);
int (*iter_response_run)(void *iter);
+ int (*protocol_msg_check)(const struct scmi_protocol_handle *ph,
+ u32 message_id, u32 *attributes);
void (*fastchannel_init)(const struct scmi_protocol_handle *ph,
u8 describe_id, u32 message_id,
u32 valid_size, u32 domain,
int i;
for (i = 0; i < num_chans; i++) {
- void *xret;
struct scmi_raw_queue *q;
q = scmi_raw_queue_init(raw);
goto err_xa;
}
- xret = xa_store(&raw->chans_q, channels[i], q,
+ ret = xa_insert(&raw->chans_q, channels[i], q,
GFP_KERNEL);
- if (xa_err(xret)) {
+ if (ret) {
dev_err(dev,
"Fail to allocate Raw queue 0x%02X\n",
channels[i]);
- ret = xa_err(xret);
goto err_xa;
}
}
dev = raw->handle->dev;
q = scmi_raw_queue_select(raw, idx,
SCMI_XFER_IS_CHAN_SET(xfer) ? chan_id : 0);
+ if (!q) {
+ dev_warn(dev,
+ "RAW[%d] - NO queue for chan 0x%X. Dropping report.\n",
+ idx, chan_id);
+ return;
+ }
/*
* Grab the msg_q_lock upfront to avoid a possible race between
struct scmi_reset_info {
u32 version;
int num_domains;
+ bool notify_reset_cmd;
struct reset_dom_info *dom_info;
};
}
ph->xops->xfer_put(ph, t);
+
+ if (!ret)
+ if (!ph->hops->protocol_msg_check(ph, RESET_NOTIFY, NULL))
+ pi->notify_reset_cmd = true;
+
return ret;
}
static int
scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
- u32 domain, struct reset_dom_info *dom_info,
- u32 version)
+ struct scmi_reset_info *pinfo,
+ u32 domain, u32 version)
{
int ret;
u32 attributes;
struct scmi_xfer *t;
struct scmi_msg_resp_reset_domain_attributes *attr;
+ struct reset_dom_info *dom_info = pinfo->dom_info + domain;
ret = ph->xops->xfer_get_init(ph, RESET_DOMAIN_ATTRIBUTES,
sizeof(domain), sizeof(*attr), &t);
attributes = le32_to_cpu(attr->attributes);
dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes);
- dom_info->reset_notify = SUPPORTS_NOTIFY_RESET(attributes);
+ if (pinfo->notify_reset_cmd)
+ dom_info->reset_notify =
+ SUPPORTS_NOTIFY_RESET(attributes);
dom_info->latency_us = le32_to_cpu(attr->latency);
if (dom_info->latency_us == U32_MAX)
dom_info->latency_us = 0;
.deassert = scmi_reset_domain_deassert,
};
+static bool scmi_reset_notify_supported(const struct scmi_protocol_handle *ph,
+ u8 evt_id, u32 src_id)
+{
+ struct reset_dom_info *dom;
+ struct scmi_reset_info *pi = ph->get_priv(ph);
+
+ if (evt_id != SCMI_EVENT_RESET_ISSUED || src_id >= pi->num_domains)
+ return false;
+
+ dom = pi->dom_info + src_id;
+
+ return dom->reset_notify;
+}
+
static int scmi_reset_notify(const struct scmi_protocol_handle *ph,
u32 domain_id, bool enable)
{
};
static const struct scmi_event_ops reset_event_ops = {
+ .is_notify_supported = scmi_reset_notify_supported,
.get_num_sources = scmi_reset_get_num_sources,
.set_notify_enabled = scmi_reset_set_notify_enabled,
.fill_custom_report = scmi_reset_fill_custom_report,
if (!pinfo->dom_info)
return -ENOMEM;
- for (domain = 0; domain < pinfo->num_domains; domain++) {
- struct reset_dom_info *dom = pinfo->dom_info + domain;
-
- scmi_reset_domain_attributes_get(ph, domain, dom, version);
- }
+ for (domain = 0; domain < pinfo->num_domains; domain++)
+ scmi_reset_domain_attributes_get(ph, pinfo, domain, version);
pinfo->version = version;
return ph->set_priv(ph, pinfo, version);
struct sensors_info {
u32 version;
+ bool notify_trip_point_cmd;
+ bool notify_continuos_update_cmd;
int num_sensors;
int max_requests;
u64 reg_addr;
}
ph->xops->xfer_put(ph, t);
+
+ if (!ret) {
+ if (!ph->hops->protocol_msg_check(ph,
+ SENSOR_TRIP_POINT_NOTIFY, NULL))
+ si->notify_trip_point_cmd = true;
+
+ if (!ph->hops->protocol_msg_check(ph,
+ SENSOR_CONTINUOUS_UPDATE_NOTIFY,
+ NULL))
+ si->notify_continuos_update_cmd = true;
+ }
+
return ret;
}
* Such bitfields are assumed to be zeroed on non
* relevant fw versions...assuming fw not buggy !
*/
- s->update = SUPPORTS_UPDATE_NOTIFY(attrl);
+ if (si->notify_continuos_update_cmd)
+ s->update = SUPPORTS_UPDATE_NOTIFY(attrl);
s->timestamped = SUPPORTS_TIMESTAMP(attrl);
if (s->timestamped)
s->tstamp_scale = S32_EXT(SENSOR_TSTAMP_EXP(attrl));
.config_set = scmi_sensor_config_set,
};
+static bool scmi_sensor_notify_supported(const struct scmi_protocol_handle *ph,
+ u8 evt_id, u32 src_id)
+{
+ bool supported = false;
+ const struct scmi_sensor_info *s;
+ struct sensors_info *sinfo = ph->get_priv(ph);
+
+ s = scmi_sensor_info_get(ph, src_id);
+ if (!s)
+ return false;
+
+ if (evt_id == SCMI_EVENT_SENSOR_TRIP_POINT_EVENT)
+ supported = sinfo->notify_trip_point_cmd;
+ else if (evt_id == SCMI_EVENT_SENSOR_UPDATE)
+ supported = s->update;
+
+ return supported;
+}
+
static int scmi_sensor_set_notify_enabled(const struct scmi_protocol_handle *ph,
u8 evt_id, u32 src_id, bool enable)
{
};
static const struct scmi_event_ops sensor_event_ops = {
+ .is_notify_supported = scmi_sensor_notify_supported,
.get_num_sources = scmi_sensor_get_num_sources,
.set_notify_enabled = scmi_sensor_set_notify_enabled,
.fill_custom_report = scmi_sensor_fill_custom_report,
#include <linux/processor.h>
#include <linux/types.h>
-#include <asm-generic/bug.h>
+#include <linux/bug.h>
#include "common.h"
(SCMI_SHMEM_CHAN_STAT_CHANNEL_ERROR |
SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
}
+
+bool shmem_channel_free(struct scmi_shared_mem __iomem *shmem)
+{
+ return (ioread32(&shmem->channel_status) &
+ SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
+}
struct scmi_chan_info *cinfo = p;
struct scmi_smc *scmi_info = cinfo->transport_info;
+ /*
+ * Different protocols might share the same chan info, so a previous
+ * smc_chan_free call might have already freed the structure.
+ */
+ if (!scmi_info)
+ return 0;
+
/* Ignore any possible further reception on the IRQ path */
if (scmi_info->irq > 0)
free_irq(scmi_info->irq, scmi_info);
struct scmi_system_info {
u32 version;
bool graceful_timeout_supported;
+ bool power_state_notify_cmd;
};
+static bool scmi_system_notify_supported(const struct scmi_protocol_handle *ph,
+ u8 evt_id, u32 src_id)
+{
+ struct scmi_system_info *pinfo = ph->get_priv(ph);
+
+ if (evt_id != SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER)
+ return false;
+
+ return pinfo->power_state_notify_cmd;
+}
+
static int scmi_system_request_notify(const struct scmi_protocol_handle *ph,
bool enable)
{
};
static const struct scmi_event_ops system_event_ops = {
+ .is_notify_supported = scmi_system_notify_supported,
.set_notify_enabled = scmi_system_set_notify_enabled,
.fill_custom_report = scmi_system_fill_custom_report,
};
if (PROTOCOL_REV_MAJOR(pinfo->version) >= 0x2)
pinfo->graceful_timeout_supported = true;
+ if (!ph->hops->protocol_msg_check(ph, SYSTEM_POWER_STATE_NOTIFY, NULL))
+ pinfo->power_state_notify_cmd = true;
+
return ph->set_priv(ph, pinfo, version);
}
root_path_buf = kzalloc(root_path_buf_len, GFP_KERNEL);
if (!root_path_buf)
- goto out;
+ return NULL;
root_path = dentry_path(bpmp->debugfs_mirror, root_path_buf,
root_path_buf_len);
static unsigned long irq_state;
static LIST_HEAD(device_list);
-#ifdef CONFIG_DEBUG_FS
static void do_emif_regdump_show(struct seq_file *s, struct emif_data *emif,
struct emif_regs *regs)
{
DEFINE_SHOW_ATTRIBUTE(emif_mr4);
-static int __init_or_module emif_debugfs_init(struct emif_data *emif)
+static void emif_debugfs_init(struct emif_data *emif)
{
- emif->debugfs_root = debugfs_create_dir(dev_name(emif->dev), NULL);
- debugfs_create_file("regcache_dump", S_IRUGO, emif->debugfs_root, emif,
- &emif_regdump_fops);
- debugfs_create_file("mr4", S_IRUGO, emif->debugfs_root, emif,
- &emif_mr4_fops);
- return 0;
-}
-
-static void __exit emif_debugfs_exit(struct emif_data *emif)
-{
- debugfs_remove_recursive(emif->debugfs_root);
- emif->debugfs_root = NULL;
-}
-#else
-static inline int __init_or_module emif_debugfs_init(struct emif_data *emif)
-{
- return 0;
+ if (IS_ENABLED(CONFIG_DEBUG_FS)) {
+ emif->debugfs_root = debugfs_create_dir(dev_name(emif->dev), NULL);
+ debugfs_create_file("regcache_dump", S_IRUGO, emif->debugfs_root, emif,
+ &emif_regdump_fops);
+ debugfs_create_file("mr4", S_IRUGO, emif->debugfs_root, emif,
+ &emif_mr4_fops);
+ }
}
-static inline void __exit emif_debugfs_exit(struct emif_data *emif)
+static void emif_debugfs_exit(struct emif_data *emif)
{
+ if (IS_ENABLED(CONFIG_DEBUG_FS)) {
+ debugfs_remove_recursive(emif->debugfs_root);
+ emif->debugfs_root = NULL;
+ }
}
-#endif
/*
* Get bus width used by EMIF. Note that this may be different from the
clear_all_interrupts(emif);
}
-static int __init_or_module setup_interrupts(struct emif_data *emif, u32 irq)
+static int setup_interrupts(struct emif_data *emif, u32 irq)
{
u32 interrupts, type;
void __iomem *base = emif->base;
}
-static void __init_or_module emif_onetime_settings(struct emif_data *emif)
+static void emif_onetime_settings(struct emif_data *emif)
{
u32 pwr_mgmt_ctrl, zq, temp_alert_cfg;
void __iomem *base = emif->base;
return valid;
}
-#if defined(CONFIG_OF)
-static void __init_or_module of_get_custom_configs(struct device_node *np_emif,
+static void of_get_custom_configs(struct device_node *np_emif,
struct emif_data *emif)
{
struct emif_custom_configs *cust_cfgs = NULL;
emif->plat_data->custom_configs = cust_cfgs;
}
-static void __init_or_module of_get_ddr_info(struct device_node *np_emif,
+static void of_get_ddr_info(struct device_node *np_emif,
struct device_node *np_ddr,
struct ddr_device_info *dev_info)
{
dev_info->io_width = __fls(io_width) - 1;
}
-static struct emif_data * __init_or_module of_get_memory_device_details(
+static struct emif_data *of_get_memory_device_details(
struct device_node *np_emif, struct device *dev)
{
struct emif_data *emif = NULL;
return emif;
}
-#else
-
-static struct emif_data * __init_or_module of_get_memory_device_details(
- struct device_node *np_emif, struct device *dev)
-{
- return NULL;
-}
-#endif
-
-static struct emif_data *__init_or_module get_device_details(
+static struct emif_data *get_device_details(
struct platform_device *pdev)
{
u32 size;
return NULL;
}
-static int __init_or_module emif_probe(struct platform_device *pdev)
+static int emif_probe(struct platform_device *pdev)
{
struct emif_data *emif;
int irq, ret;
return -ENODEV;
}
-static void __exit emif_remove(struct platform_device *pdev)
+static void emif_remove(struct platform_device *pdev)
{
struct emif_data *emif = platform_get_drvdata(pdev);
#endif
static struct platform_driver emif_driver = {
- .remove_new = __exit_p(emif_remove),
+ .probe = emif_probe,
+ .remove_new = emif_remove,
.shutdown = emif_shutdown,
.driver = {
.name = "emif",
},
};
-module_platform_driver_probe(emif_driver, emif_probe);
+module_platform_driver(emif_driver);
MODULE_DESCRIPTION("TI EMIF SDRAM Controller Driver");
MODULE_LICENSE("GPL");
chip process corner, temperatures and other factors. Then DVFS
driver could apply SVS bank voltage to PMIC/Buck.
+config MTK_SOCINFO
+ tristate "MediaTek SoC Information"
+ default y
+ depends on NVMEM_MTK_EFUSE
+ help
+ The MediaTek SoC Information (mtk-socinfo) driver provides
+ information about the SoC to the userspace including the
+ manufacturer name, marketing name and soc name.
+
endmenu
obj-$(CONFIG_MTK_MMSYS) += mtk-mmsys.o
obj-$(CONFIG_MTK_MMSYS) += mtk-mutex.o
obj-$(CONFIG_MTK_SVS) += mtk-svs.o
+obj-$(CONFIG_MTK_SOCINFO) += mtk-socinfo.o
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2023 MediaTek Inc.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/pm_runtime.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/device.h>
+#include <linux/device/bus.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/string.h>
+#include <linux/sys_soc.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+#define MTK_SOCINFO_ENTRY(_soc_name, _segment_name, _marketing_name, _cell_data1, _cell_data2) {\
+ .soc_name = _soc_name, \
+ .segment_name = _segment_name, \
+ .marketing_name = _marketing_name, \
+ .cell_data = {_cell_data1, _cell_data2} \
+}
+#define CELL_NOT_USED (0xFFFFFFFF)
+#define MAX_CELLS (2)
+
+struct mtk_socinfo {
+ struct device *dev;
+ struct name_data *name_data;
+ struct socinfo_data *socinfo_data;
+ struct soc_device *soc_dev;
+};
+
+struct socinfo_data {
+ char *soc_name;
+ char *segment_name;
+ char *marketing_name;
+ u32 cell_data[MAX_CELLS];
+};
+
+static const char *cell_names[MAX_CELLS] = {"socinfo-data1", "socinfo-data2"};
+
+static struct socinfo_data socinfo_data_table[] = {
+ MTK_SOCINFO_ENTRY("MT8173", "MT8173V/AC", "MT8173", 0x6CA20004, 0x10000000),
+ MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000840),
+ MTK_SOCINFO_ENTRY("MT8183", "MT8183V/AZA", "Kompanio 500", 0x00010043, 0x00000940),
+ MTK_SOCINFO_ENTRY("MT8186", "MT8186GV/AZA", "Kompanio 520", 0x81861001, CELL_NOT_USED),
+ MTK_SOCINFO_ENTRY("MT8186T", "MT8186TV/AZA", "Kompanio 528", 0x81862001, CELL_NOT_USED),
+ MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/AZA", "Kompanio 830", 0x81880000, 0x00000010),
+ MTK_SOCINFO_ENTRY("MT8188", "MT8188GV/HZA", "Kompanio 830", 0x81880000, 0x00000011),
+ MTK_SOCINFO_ENTRY("MT8192", "MT8192V/AZA", "Kompanio 820", 0x00001100, 0x00040080),
+ MTK_SOCINFO_ENTRY("MT8192T", "MT8192V/ATZA", "Kompanio 828", 0x00000100, 0x000400C0),
+ MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EZA", "Kompanio 1200", 0x81950300, CELL_NOT_USED),
+ MTK_SOCINFO_ENTRY("MT8195", "MT8195GV/EHZA", "Kompanio 1200", 0x81950304, CELL_NOT_USED),
+ MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EZA", "Kompanio 1380", 0x81950400, CELL_NOT_USED),
+ MTK_SOCINFO_ENTRY("MT8195", "MT8195TV/EHZA", "Kompanio 1380", 0x81950404, CELL_NOT_USED),
+};
+
+static int mtk_socinfo_create_socinfo_node(struct mtk_socinfo *mtk_socinfop)
+{
+ struct soc_device_attribute *attrs;
+ static char machine[30] = {0};
+ static const char *soc_manufacturer = "MediaTek";
+
+ attrs = devm_kzalloc(mtk_socinfop->dev, sizeof(*attrs), GFP_KERNEL);
+ if (!attrs)
+ return -ENOMEM;
+
+ snprintf(machine, sizeof(machine), "%s (%s)", mtk_socinfop->socinfo_data->marketing_name,
+ mtk_socinfop->socinfo_data->soc_name);
+ attrs->family = soc_manufacturer;
+ attrs->machine = machine;
+
+ mtk_socinfop->soc_dev = soc_device_register(attrs);
+ if (IS_ERR(mtk_socinfop->soc_dev))
+ return PTR_ERR(mtk_socinfop->soc_dev);
+
+ dev_info(mtk_socinfop->dev, "%s %s SoC detected.\n", soc_manufacturer, attrs->machine);
+ return 0;
+}
+
+static u32 mtk_socinfo_read_cell(struct device *dev, const char *name)
+{
+ struct nvmem_device *nvmemp;
+ struct device_node *np, *nvmem_node = dev->parent->of_node;
+ u32 offset;
+ u32 cell_val = CELL_NOT_USED;
+
+ /* should never fail since the nvmem driver registers this child */
+ nvmemp = nvmem_device_find(nvmem_node, device_match_of_node);
+ if (IS_ERR(nvmemp))
+ goto out;
+
+ np = of_get_child_by_name(nvmem_node, name);
+ if (!np)
+ goto put_device;
+
+ if (of_property_read_u32_index(np, "reg", 0, &offset))
+ goto put_node;
+
+ nvmem_device_read(nvmemp, offset, sizeof(cell_val), &cell_val);
+
+put_node:
+ of_node_put(np);
+put_device:
+ nvmem_device_put(nvmemp);
+out:
+ return cell_val;
+}
+
+static int mtk_socinfo_get_socinfo_data(struct mtk_socinfo *mtk_socinfop)
+{
+ unsigned int i, j;
+ unsigned int num_cell_data = 0;
+ u32 cell_data[MAX_CELLS] = {0};
+ bool match_socinfo;
+ int match_socinfo_index = -1;
+
+ for (i = 0; i < MAX_CELLS; i++) {
+ cell_data[i] = mtk_socinfo_read_cell(mtk_socinfop->dev, cell_names[i]);
+ if (cell_data[i] != CELL_NOT_USED)
+ num_cell_data++;
+ else
+ break;
+ }
+
+ if (!num_cell_data)
+ return -ENOENT;
+
+ for (i = 0; i < ARRAY_SIZE(socinfo_data_table); i++) {
+ match_socinfo = true;
+ for (j = 0; j < num_cell_data; j++) {
+ if (cell_data[j] != socinfo_data_table[i].cell_data[j]) {
+ match_socinfo = false;
+ break;
+ }
+ }
+ if (match_socinfo) {
+ mtk_socinfop->socinfo_data = &(socinfo_data_table[i]);
+ match_socinfo_index = i;
+ break;
+ }
+ }
+
+ return match_socinfo_index >= 0 ? match_socinfo_index : -ENOENT;
+}
+
+static int mtk_socinfo_probe(struct platform_device *pdev)
+{
+ struct mtk_socinfo *mtk_socinfop;
+ int ret;
+
+ mtk_socinfop = devm_kzalloc(&pdev->dev, sizeof(*mtk_socinfop), GFP_KERNEL);
+ if (!mtk_socinfop)
+ return -ENOMEM;
+
+ mtk_socinfop->dev = &pdev->dev;
+
+ ret = mtk_socinfo_get_socinfo_data(mtk_socinfop);
+ if (ret < 0)
+ return dev_err_probe(mtk_socinfop->dev, ret, "Failed to get socinfo data\n");
+
+ ret = mtk_socinfo_create_socinfo_node(mtk_socinfop);
+ if (ret)
+ return dev_err_probe(mtk_socinfop->dev, ret, "Cannot create node\n");
+
+ platform_set_drvdata(pdev, mtk_socinfop);
+ return 0;
+}
+
+static void mtk_socinfo_remove(struct platform_device *pdev)
+{
+ struct mtk_socinfo *mtk_socinfop = platform_get_drvdata(pdev);
+
+ soc_device_unregister(mtk_socinfop->soc_dev);
+}
+
+static struct platform_driver mtk_socinfo = {
+ .probe = mtk_socinfo_probe,
+ .remove_new = mtk_socinfo_remove,
+ .driver = {
+ .name = "mtk-socinfo",
+ },
+};
+module_platform_driver(mtk_socinfo);
+
+MODULE_AUTHOR("William-TW LIN <william-tw.lin@mediatek.com>");
+MODULE_DESCRIPTION("MediaTek socinfo driver");
+MODULE_LICENSE("GPL");
select SYS_SUPPORTS_SH_CMT
select SYS_SUPPORTS_SH_TMU
+config ARCH_RCAR_GEN4
+ bool
+ select ARCH_RCAR_GEN3
+
config ARCH_RMOBILE
bool
select PM
config ARCH_R8A779F0
bool "ARM64 Platform support for R-Car S4-8"
- select ARCH_RCAR_GEN3
+ select ARCH_RCAR_GEN4
select SYSC_R8A779F0
help
This enables support for the Renesas R-Car S4-8 SoC.
config ARCH_R8A779A0
bool "ARM64 Platform support for R-Car V3U"
- select ARCH_RCAR_GEN3
+ select ARCH_RCAR_GEN4
select SYSC_R8A779A0
help
This enables support for the Renesas R-Car V3U SoC.
config ARCH_R8A779G0
bool "ARM64 Platform support for R-Car V4H"
- select ARCH_RCAR_GEN3
+ select ARCH_RCAR_GEN4
select SYSC_R8A779G0
help
This enables support for the Renesas R-Car V4H SoC.
+config ARCH_R8A779H0
+ bool "ARM64 Platform support for R-Car V4M"
+ select ARCH_RCAR_GEN4
+ select SYSC_R8A779H0
+ help
+ This enables support for the Renesas R-Car V4M SoC.
+
config ARCH_R8A774C0
bool "ARM64 Platform support for RZ/G2E"
select ARCH_RCAR_GEN3
{ .compatible = "renesas,r8a779a0-rst", .data = &rcar_rst_v3u },
{ .compatible = "renesas,r8a779f0-rst", .data = &rcar_rst_gen4 },
{ .compatible = "renesas,r8a779g0-rst", .data = &rcar_rst_gen4 },
+ { .compatible = "renesas,r8a779h0-rst", .data = &rcar_rst_gen4 },
{ /* sentinel */ }
};
.id = 0x5c,
};
+static const struct renesas_soc soc_rcar_v4m __initconst __maybe_unused = {
+ .family = &fam_rcar_gen4,
+ .id = 0x5d,
+};
+
static const struct renesas_soc soc_shmobile_ag5 __initconst __maybe_unused = {
.family = &fam_shmobile,
.id = 0x37,
#ifdef CONFIG_ARCH_R8A779G0
{ .compatible = "renesas,r8a779g0", .data = &soc_rcar_v4h },
#endif
+#ifdef CONFIG_ARCH_R8A779H0
+ { .compatible = "renesas,r8a779h0", .data = &soc_rcar_v4m },
+#endif
#ifdef CONFIG_ARCH_R9A07G043
#ifdef CONFIG_RISCV
{ .compatible = "renesas,r9a07g043", .data = &soc_rz_five },
help
Enable support for the NVIDIA Tegra234 SoC.
+config ARCH_TEGRA_241_SOC
+ bool "NVIDIA Tegra241 SoC"
+ help
+ Enable support for the NVIDIA Tegra241 SoC.
+
endif
endif
* Copyright (c) 2013-2023, NVIDIA CORPORATION. All rights reserved.
*/
+#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/device.h>
#include <linux/kobject.h>
#include <linux/init.h>
#include <linux/io.h>
+#include <linux/mod_devicetable.h>
#include <linux/nvmem-consumer.h>
#include <linux/nvmem-provider.h>
#include <linux/of.h>
fuse->clk = NULL;
}
+static void tegra_fuse_print_sku_info(struct tegra_sku_info *tegra_sku_info)
+{
+ pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n",
+ tegra_revision_name[tegra_sku_info->revision],
+ tegra_sku_info->sku_id, tegra_sku_info->cpu_process_id,
+ tegra_sku_info->soc_process_id);
+ pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n",
+ tegra_sku_info->cpu_speedo_id, tegra_sku_info->soc_speedo_id);
+}
+
+static int tegra_fuse_add_lookups(struct tegra_fuse *fuse)
+{
+ fuse->lookups = kmemdup_array(fuse->soc->lookups, sizeof(*fuse->lookups),
+ fuse->soc->num_lookups, GFP_KERNEL);
+ if (!fuse->lookups)
+ return -ENOMEM;
+
+ nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups);
+
+ return 0;
+}
+
static int tegra_fuse_probe(struct platform_device *pdev)
{
void __iomem *base = fuse->base;
return PTR_ERR(fuse->base);
fuse->phys = res->start;
- fuse->clk = devm_clk_get(&pdev->dev, "fuse");
- if (IS_ERR(fuse->clk)) {
- if (PTR_ERR(fuse->clk) != -EPROBE_DEFER)
- dev_err(&pdev->dev, "failed to get FUSE clock: %ld",
- PTR_ERR(fuse->clk));
+ /* Initialize the soc data and lookups if using ACPI boot. */
+ if (is_acpi_node(dev_fwnode(&pdev->dev)) && !fuse->soc) {
+ u8 chip;
- return PTR_ERR(fuse->clk);
+ tegra_acpi_init_apbmisc();
+
+ chip = tegra_get_chip_id();
+ switch (chip) {
+#if defined(CONFIG_ARCH_TEGRA_194_SOC)
+ case TEGRA194:
+ fuse->soc = &tegra194_fuse_soc;
+ break;
+#endif
+#if defined(CONFIG_ARCH_TEGRA_234_SOC)
+ case TEGRA234:
+ fuse->soc = &tegra234_fuse_soc;
+ break;
+#endif
+#if defined(CONFIG_ARCH_TEGRA_241_SOC)
+ case TEGRA241:
+ fuse->soc = &tegra241_fuse_soc;
+ break;
+#endif
+ default:
+ return dev_err_probe(&pdev->dev, -EINVAL, "Unsupported SoC: %02x\n", chip);
+ }
+
+ fuse->soc->init(fuse);
+ tegra_fuse_print_sku_info(&tegra_sku_info);
+ tegra_soc_device_register();
+
+ err = tegra_fuse_add_lookups(fuse);
+ if (err)
+ return dev_err_probe(&pdev->dev, err, "failed to add FUSE lookups\n");
}
+ fuse->clk = devm_clk_get_optional(&pdev->dev, "fuse");
+ if (IS_ERR(fuse->clk))
+ return dev_err_probe(&pdev->dev, PTR_ERR(fuse->clk), "failed to get FUSE clock\n");
+
platform_set_drvdata(pdev, fuse);
fuse->dev = &pdev->dev;
}
fuse->rst = devm_reset_control_get_optional(&pdev->dev, "fuse");
- if (IS_ERR(fuse->rst)) {
- err = PTR_ERR(fuse->rst);
- dev_err(&pdev->dev, "failed to get FUSE reset: %pe\n",
- fuse->rst);
- return err;
- }
+ if (IS_ERR(fuse->rst))
+ return dev_err_probe(&pdev->dev, PTR_ERR(fuse->rst), "failed to get FUSE reset\n");
/*
* FUSE clock is enabled at a boot time, hence this resume/suspend
SET_SYSTEM_SLEEP_PM_OPS(tegra_fuse_suspend, tegra_fuse_resume)
};
+static const struct acpi_device_id tegra_fuse_acpi_match[] = {
+ { "NVDA200F" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(acpi, tegra_fuse_acpi_match);
+
static struct platform_driver tegra_fuse_driver = {
.driver = {
.name = "tegra-fuse",
.of_match_table = tegra_fuse_match,
+ .acpi_match_table = tegra_fuse_acpi_match,
.pm = &tegra_fuse_pm,
.suppress_bind_attrs = true,
},
int tegra_fuse_readl(unsigned long offset, u32 *value)
{
- if (!fuse->read || !fuse->clk)
+ if (!fuse->dev)
+ return -EPROBE_DEFER;
+
+ /*
+ * Wait for fuse->clk to be initialized if device-tree boot is used.
+ */
+ if (is_of_node(dev_fwnode(fuse->dev)) && !fuse->clk)
+ return -EPROBE_DEFER;
+
+ if (!fuse->read)
return -EPROBE_DEFER;
if (IS_ERR(fuse->clk))
};
#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \
- IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
+ IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \
+ IS_ENABLED(CONFIG_ARCH_TEGRA_241_SOC)
static ssize_t platform_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
};
#endif
-struct device * __init tegra_soc_device_register(void)
+struct device *tegra_soc_device_register(void)
{
struct soc_device_attribute *attr;
struct soc_device *dev;
const struct of_device_id *match;
struct device_node *np;
struct resource regs;
+ int err;
tegra_init_apbmisc();
fuse->soc->init(fuse);
- pr_info("Tegra Revision: %s SKU: %d CPU Process: %d SoC Process: %d\n",
- tegra_revision_name[tegra_sku_info.revision],
- tegra_sku_info.sku_id, tegra_sku_info.cpu_process_id,
- tegra_sku_info.soc_process_id);
- pr_debug("Tegra CPU Speedo ID %d, SoC Speedo ID %d\n",
- tegra_sku_info.cpu_speedo_id, tegra_sku_info.soc_speedo_id);
+ tegra_fuse_print_sku_info(&tegra_sku_info);
- if (fuse->soc->lookups) {
- size_t size = sizeof(*fuse->lookups) * fuse->soc->num_lookups;
-
- fuse->lookups = kmemdup(fuse->soc->lookups, size, GFP_KERNEL);
- if (fuse->lookups)
- nvmem_add_cell_lookups(fuse->lookups, fuse->soc->num_lookups);
- }
+ err = tegra_fuse_add_lookups(fuse);
+ if (err)
+ pr_err("failed to add FUSE lookups\n");
- return 0;
+ return err;
}
early_initcall(tegra_init_fuse);
defined(CONFIG_ARCH_TEGRA_210_SOC) || \
defined(CONFIG_ARCH_TEGRA_186_SOC) || \
defined(CONFIG_ARCH_TEGRA_194_SOC) || \
- defined(CONFIG_ARCH_TEGRA_234_SOC)
+ defined(CONFIG_ARCH_TEGRA_234_SOC) || \
+ defined(CONFIG_ARCH_TEGRA_241_SOC)
static u32 tegra30_fuse_read_early(struct tegra_fuse *fuse, unsigned int offset)
{
if (WARN_ON(!fuse->base))
.clk_suspend_on = false,
};
#endif
+
+#if defined(CONFIG_ARCH_TEGRA_241_SOC)
+static const struct tegra_fuse_info tegra241_fuse_info = {
+ .read = tegra30_fuse_read,
+ .size = 0x16008,
+ .spare = 0xcf0,
+};
+
+static const struct nvmem_keepout tegra241_fuse_keepouts[] = {
+ { .start = 0xc, .end = 0x1600c }
+};
+
+const struct tegra_fuse_soc tegra241_fuse_soc = {
+ .init = tegra30_fuse_init,
+ .info = &tegra241_fuse_info,
+ .keepouts = tegra241_fuse_keepouts,
+ .num_keepouts = ARRAY_SIZE(tegra241_fuse_keepouts),
+ .soc_attr_group = &tegra194_soc_attr_group,
+};
+#endif
void tegra_init_revision(void);
void tegra_init_apbmisc(void);
+void tegra_acpi_init_apbmisc(void);
u32 __init tegra_fuse_read_spare(unsigned int spare);
u32 __init tegra_fuse_read_early(unsigned int offset);
#endif
#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC) || \
- IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC)
+ IS_ENABLED(CONFIG_ARCH_TEGRA_234_SOC) || \
+ IS_ENABLED(CONFIG_ARCH_TEGRA_241_SOC)
extern const struct attribute_group tegra194_soc_attr_group;
#endif
extern const struct tegra_fuse_soc tegra234_fuse_soc;
#endif
+#ifdef CONFIG_ARCH_TEGRA_241_SOC
+extern const struct tegra_fuse_soc tegra241_fuse_soc;
+#endif
+
#endif
* Copyright (c) 2014-2023, NVIDIA CORPORATION. All rights reserved.
*/
+#include <linux/acpi.h>
#include <linux/export.h>
#include <linux/io.h>
#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
#include <linux/of.h>
#include <linux/of_address.h>
switch (tegra_get_chip_id()) {
case TEGRA194:
case TEGRA234:
+ case TEGRA241:
case TEGRA264:
if (tegra_get_platform() == 0)
return true;
tegra_sku_info.platform = tegra_get_platform();
}
-void __init tegra_init_apbmisc(void)
+static void tegra_init_apbmisc_resources(struct resource *apbmisc,
+ struct resource *straps)
{
void __iomem *strapping_base;
+
+ apbmisc_base = ioremap(apbmisc->start, resource_size(apbmisc));
+ if (apbmisc_base)
+ chipid = readl_relaxed(apbmisc_base + 4);
+ else
+ pr_err("failed to map APBMISC registers\n");
+
+ strapping_base = ioremap(straps->start, resource_size(straps));
+ if (strapping_base) {
+ strapping = readl_relaxed(strapping_base);
+ iounmap(strapping_base);
+ } else {
+ pr_err("failed to map strapping options registers\n");
+ }
+}
+
+/**
+ * tegra_init_apbmisc - Initializes Tegra APBMISC and Strapping registers.
+ *
+ * This is called during early init as some of the old 32-bit ARM code needs
+ * information from the APBMISC registers very early during boot.
+ */
+void __init tegra_init_apbmisc(void)
+{
struct resource apbmisc, straps;
struct device_node *np;
}
}
- apbmisc_base = ioremap(apbmisc.start, resource_size(&apbmisc));
- if (!apbmisc_base) {
- pr_err("failed to map APBMISC registers\n");
- } else {
- chipid = readl_relaxed(apbmisc_base + 4);
+ tegra_init_apbmisc_resources(&apbmisc, &straps);
+ long_ram_code = of_property_read_bool(np, "nvidia,long-ram-code");
+
+put:
+ of_node_put(np);
+}
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id apbmisc_acpi_match[] = {
+ { "NVDA2010" },
+ { /* sentinel */ }
+};
+
+void tegra_acpi_init_apbmisc(void)
+{
+ struct resource *resources[2] = { NULL };
+ struct resource_entry *rentry;
+ struct acpi_device *adev = NULL;
+ struct list_head resource_list;
+ int rcount = 0;
+ int ret;
+
+ adev = acpi_dev_get_first_match_dev(apbmisc_acpi_match[0].id, NULL, -1);
+ if (!adev)
+ return;
+
+ INIT_LIST_HEAD(&resource_list);
+
+ ret = acpi_dev_get_memory_resources(adev, &resource_list);
+ if (ret < 0) {
+ pr_err("failed to get APBMISC memory resources");
+ goto out_put_acpi_dev;
}
- strapping_base = ioremap(straps.start, resource_size(&straps));
- if (!strapping_base) {
- pr_err("failed to map strapping options registers\n");
- } else {
- strapping = readl_relaxed(strapping_base);
- iounmap(strapping_base);
+ /*
+ * Get required memory resources.
+ *
+ * resources[0]: apbmisc.
+ * resources[1]: straps.
+ */
+ resource_list_for_each_entry(rentry, &resource_list) {
+ if (rcount >= ARRAY_SIZE(resources))
+ break;
+
+ resources[rcount++] = rentry->res;
}
- long_ram_code = of_property_read_bool(np, "nvidia,long-ram-code");
+ if (!resources[0]) {
+ pr_err("failed to get APBMISC registers\n");
+ goto out_free_resource_list;
+ }
-put:
- of_node_put(np);
+ if (!resources[1]) {
+ pr_err("failed to get strapping options registers\n");
+ goto out_free_resource_list;
+ }
+
+ tegra_init_apbmisc_resources(resources[0], resources[1]);
+
+out_free_resource_list:
+ acpi_dev_free_resource_list(&resource_list);
+
+out_put_acpi_dev:
+ acpi_dev_put(adev);
+}
+#else
+void tegra_acpi_init_apbmisc(void)
+{
}
+#endif
* drivers/soc/tegra/pmc.c
*
* Copyright (c) 2010 Google, Inc
- * Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved.
+ * Copyright (c) 2018-2024, NVIDIA CORPORATION. All rights reserved.
*
* Author:
* Colin Cross <ccross@google.com>
bool has_blink_output;
bool has_usb_sleepwalk;
bool supports_core_domain;
+ bool has_single_mmio_aperture;
};
/**
return TEGRA_IO_PAD_VOLTAGE_3V3;
}
-/**
- * tegra_io_rail_power_on() - enable power to I/O rail
- * @id: Tegra I/O pad ID for which to enable power
- *
- * See also: tegra_io_pad_power_enable()
- */
-int tegra_io_rail_power_on(unsigned int id)
-{
- return tegra_io_pad_power_enable(id);
-}
-EXPORT_SYMBOL(tegra_io_rail_power_on);
-
-/**
- * tegra_io_rail_power_off() - disable power to I/O rail
- * @id: Tegra I/O pad ID for which to disable power
- *
- * See also: tegra_io_pad_power_disable()
- */
-int tegra_io_rail_power_off(unsigned int id)
-{
- return tegra_io_pad_power_disable(id);
-}
-EXPORT_SYMBOL(tegra_io_rail_power_off);
-
#ifdef CONFIG_PM_SLEEP
enum tegra_suspend_mode tegra_pmc_get_suspend_mode(void)
{
if (IS_ERR(base))
return PTR_ERR(base);
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wake");
- if (res) {
+ if (pmc->soc->has_single_mmio_aperture) {
+ pmc->wake = base;
+ pmc->aotag = base;
+ pmc->scratch = base;
+ } else {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "wake");
pmc->wake = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pmc->wake))
return PTR_ERR(pmc->wake);
- } else {
- pmc->wake = base;
- }
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aotag");
- if (res) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "aotag");
pmc->aotag = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pmc->aotag))
return PTR_ERR(pmc->aotag);
- } else {
- pmc->aotag = base;
- }
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "scratch");
- if (res) {
- pmc->scratch = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(pmc->scratch))
- return PTR_ERR(pmc->scratch);
- } else {
- pmc->scratch = base;
+ /* "scratch" is an optional aperture */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "scratch");
+ if (res) {
+ pmc->scratch = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(pmc->scratch))
+ return PTR_ERR(pmc->scratch);
+ } else {
+ pmc->scratch = NULL;
+ }
}
pmc->clk = devm_clk_get_optional(&pdev->dev, "pclk");
* PMC should be last resort for restarting since it soft-resets
* CPU without resetting everything else.
*/
- err = devm_register_reboot_notifier(&pdev->dev,
- &tegra_pmc_reboot_notifier);
- if (err) {
- dev_err(&pdev->dev, "unable to register reboot notifier, %d\n",
- err);
- return err;
+ if (pmc->scratch) {
+ err = devm_register_reboot_notifier(&pdev->dev,
+ &tegra_pmc_reboot_notifier);
+ if (err) {
+ dev_err(&pdev->dev,
+ "unable to register reboot notifier, %d\n",
+ err);
+ return err;
+ }
}
err = devm_register_sys_off_handler(&pdev->dev,
.num_pmc_clks = 0,
.has_blink_output = true,
.has_usb_sleepwalk = true,
+ .has_single_mmio_aperture = true,
};
static const char * const tegra30_powergates[] = {
.num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
.has_blink_output = true,
.has_usb_sleepwalk = true,
+ .has_single_mmio_aperture = true,
};
static const char * const tegra114_powergates[] = {
.num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
.has_blink_output = true,
.has_usb_sleepwalk = true,
+ .has_single_mmio_aperture = true,
};
static const char * const tegra124_powergates[] = {
.num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
.has_blink_output = true,
.has_usb_sleepwalk = true,
+ .has_single_mmio_aperture = true,
};
static const char * const tegra210_powergates[] = {
.num_pmc_clks = ARRAY_SIZE(tegra_pmc_clks_data),
.has_blink_output = true,
.has_usb_sleepwalk = true,
+ .has_single_mmio_aperture = true,
};
static const struct tegra_io_pad_soc tegra186_io_pads[] = {
.num_pmc_clks = 0,
.has_blink_output = false,
.has_usb_sleepwalk = false,
+ .has_single_mmio_aperture = false,
};
static const struct tegra_io_pad_soc tegra194_io_pads[] = {
.num_pmc_clks = 0,
.has_blink_output = false,
.has_usb_sleepwalk = false,
+ .has_single_mmio_aperture = false,
};
static const struct tegra_io_pad_soc tegra234_io_pads[] = {
};
static const struct tegra_wake_event tegra234_wake_events[] = {
+ TEGRA_WAKE_GPIO("sd-wake", 8, 0, TEGRA234_MAIN_GPIO(G, 7)),
TEGRA_WAKE_IRQ("pmu", 24, 209),
TEGRA_WAKE_GPIO("power", 29, 1, TEGRA234_AON_GPIO(EE, 4)),
TEGRA_WAKE_GPIO("mgbe", 56, 0, TEGRA234_MAIN_GPIO(Y, 3)),
.pmc_clks_data = NULL,
.num_pmc_clks = 0,
.has_blink_output = false,
+ .has_single_mmio_aperture = false,
};
static const struct of_device_id tegra_pmc_match[] = {
#define module_ffa_driver(__ffa_driver) \
module_driver(__ffa_driver, ffa_register, ffa_unregister)
-extern struct bus_type ffa_bus_type;
+extern const struct bus_type ffa_bus_type;
/* FFA transport related */
struct ffa_partition_info {
bool rate_discrete;
bool rate_changed_notifications;
bool rate_change_requested_notifications;
+ bool state_ctrl_forbidden;
+ bool rate_ctrl_forbidden;
+ bool parent_ctrl_forbidden;
+ bool extended_config;
union {
struct {
int num_rates;
struct scmi_device;
struct scmi_protocol_handle;
+enum scmi_clock_oem_config {
+ SCMI_CLOCK_CFG_DUTY_CYCLE = 0x1,
+ SCMI_CLOCK_CFG_PHASE,
+ SCMI_CLOCK_CFG_OEM_START = 0x80,
+ SCMI_CLOCK_CFG_OEM_END = 0xFF,
+};
+
/**
* struct scmi_clk_proto_ops - represents the various operations provided
* by SCMI Clock Protocol
int (*state_get)(const struct scmi_protocol_handle *ph, u32 clk_id,
bool *enabled, bool atomic);
int (*config_oem_get)(const struct scmi_protocol_handle *ph, u32 clk_id,
- u8 oem_type, u32 *oem_val, u32 *attributes,
- bool atomic);
+ enum scmi_clock_oem_config oem_type,
+ u32 *oem_val, u32 *attributes, bool atomic);
int (*config_oem_set)(const struct scmi_protocol_handle *ph, u32 clk_id,
- u8 oem_type, u32 oem_val, bool atomic);
+ enum scmi_clock_oem_config oem_type,
+ u32 oem_val, bool atomic);
int (*parent_get)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 *parent_id);
int (*parent_set)(const struct scmi_protocol_handle *ph, u32 clk_id, u32 parent_id);
};
unsigned int domain_id;
unsigned int range_max;
unsigned int range_min;
+ unsigned long range_max_freq;
+ unsigned long range_min_freq;
};
struct scmi_perf_level_report {
unsigned int agent_id;
unsigned int domain_id;
unsigned int performance_level;
+ unsigned long performance_level_freq;
};
struct scmi_sensor_trip_point_report {
extern void *kmemdup(const void *src, size_t len, gfp_t gfp) __realloc_size(2);
extern void *kvmemdup(const void *src, size_t len, gfp_t gfp) __realloc_size(2);
extern char *kmemdup_nul(const char *s, size_t len, gfp_t gfp);
+extern void *kmemdup_array(const void *src, size_t element_size, size_t count, gfp_t gfp);
extern char **argv_split(gfp_t gfp, const char *str, int *argcp);
extern void argv_free(char **argv);
#define TEGRA186 0x18
#define TEGRA194 0x19
#define TEGRA234 0x23
+#define TEGRA241 0x24
#define TEGRA264 0x26
#define TEGRA_FUSE_SKU_CALIB_0 0xf0
TEGRA_IO_PAD_AO_HV,
};
-/* deprecated, use TEGRA_IO_PAD_{HDMI,LVDS} instead */
-#define TEGRA_IO_RAIL_HDMI TEGRA_IO_PAD_HDMI
-#define TEGRA_IO_RAIL_LVDS TEGRA_IO_PAD_LVDS
-
#ifdef CONFIG_SOC_TEGRA_PMC
int tegra_powergate_power_on(unsigned int id);
int tegra_powergate_power_off(unsigned int id);
int tegra_io_pad_power_enable(enum tegra_io_pad id);
int tegra_io_pad_power_disable(enum tegra_io_pad id);
-/* deprecated, use tegra_io_pad_power_{enable,disable}() instead */
-int tegra_io_rail_power_on(unsigned int id);
-int tegra_io_rail_power_off(unsigned int id);
-
void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode);
void tegra_pmc_enter_suspend_mode(enum tegra_suspend_mode mode);
return -ENOSYS;
}
-static inline int tegra_io_rail_power_on(unsigned int id)
-{
- return -ENOSYS;
-}
-
-static inline int tegra_io_rail_power_off(unsigned int id)
-{
- return -ENOSYS;
-}
-
static inline void tegra_pmc_set_suspend_mode(enum tegra_suspend_mode mode)
{
}
}
EXPORT_SYMBOL(kmemdup);
+/**
+ * kmemdup_array - duplicate a given array.
+ *
+ * @src: array to duplicate.
+ * @element_size: size of each element of array.
+ * @count: number of elements to duplicate from array.
+ * @gfp: GFP mask to use.
+ *
+ * Return: duplicated array of @src or %NULL in case of error,
+ * result is physically contiguous. Use kfree() to free.
+ */
+void *kmemdup_array(const void *src, size_t element_size, size_t count, gfp_t gfp)
+{
+ return kmemdup(src, size_mul(element_size, count), gfp);
+}
+EXPORT_SYMBOL(kmemdup_array);
+
/**
* kvmemdup - duplicate region of memory
*