// SPDX-License-Identifier: GPL-2.0 /* * System Control and Management Interface (SCMI) MTK Tinysys Protocol * * Copyright (C) 2021 Mediatek Inc. */ #define pr_fmt(fmt) "SCMI Notifications TINYSYS - " fmt #include #include #include #include #include "protocol.h" enum scmi_tinysys_protocol_cmd { TINYSYS_COMMON_SET = 0x3, TINYSYS_COMMON_GET = 0x4, TINYSYS_POWER_STATE_NOTIFY = 0x5, }; struct scmi_tinysys_common_set_state { __le32 reserv; __le32 p0; __le32 p1; __le32 p2; __le32 p3; __le32 p4; __le32 p5; }; struct scmi_tinysys_common_get_state { __le32 reserv; __le32 p0; __le32 p1; }; struct scmi_tinysys_notify { __le32 reserv; __le32 f_id; __le32 notify_enable; }; struct scmi_tinysys_notifier_payld { __le32 f_id; __le32 p1; __le32 p2; __le32 p3; __le32 p4; }; struct scmi_tinysys_info { u32 version; int num_domains; }; static int scmi_tinysys_common_set(const struct scmi_protocol_handle *ph, u32 p0, u32 p1, u32 p2, u32 p3, u32 p4, u32 p5) { int ret; struct scmi_xfer *t; struct scmi_tinysys_common_set_state *st; ret = ph->xops->xfer_get_init(ph, TINYSYS_COMMON_SET, sizeof(*st), 0, &t); if (ret) return ret; st = t->tx.buf; st->p0 = cpu_to_le32(p0); st->p1 = cpu_to_le32(p1); st->p2 = cpu_to_le32(p2); st->p3 = cpu_to_le32(p3); st->p4 = cpu_to_le32(p4); st->p5 = cpu_to_le32(p5); ret = ph->xops->do_xfer(ph, t); ph->xops->xfer_put(ph, t); return ret; } static int scmi_tinysys_common_get(const struct scmi_protocol_handle *ph, u32 p0, u32 p1, struct scmi_tinysys_status *rvalue) { int ret; struct scmi_xfer *t; struct scmi_tinysys_common_get_state *gt; ret = ph->xops->xfer_get_init(ph, TINYSYS_COMMON_GET, sizeof(*gt), sizeof(*rvalue), &t); if (ret) return ret; gt = t->tx.buf; gt->p0 = cpu_to_le32(p0); gt->p1 = cpu_to_le32(p1); ret = ph->xops->do_xfer(ph, t); if (!ret) memcpy((void *)rvalue, (t->rx.buf), sizeof(*rvalue)); ph->xops->xfer_put(ph, t); return ret; } static int scmi_tinysys_request_notify(const struct scmi_protocol_handle *ph, u32 src_id, bool enable) { int ret; struct scmi_xfer *t; struct scmi_tinysys_notify *notify; ret = ph->xops->xfer_get_init(ph, TINYSYS_POWER_STATE_NOTIFY, sizeof(*notify), 0, &t); if (ret) return ret; notify = t->tx.buf; notify->f_id = src_id; notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0; ret = ph->xops->do_xfer(ph, t); ph->xops->xfer_put(ph, t); return ret; } static int scmi_tinysys_set_notify_enabled(const struct scmi_protocol_handle *ph, u8 evt_id, u32 src_id, bool enable) { int ret; ret = scmi_tinysys_request_notify(ph, src_id, enable); if (ret) pr_debug("FAIL_ENABLE - evt[%X] - ret:%d\n", evt_id, ret); return ret; } static void *scmi_tinysys_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) { const struct scmi_tinysys_notifier_payld *p = payld; struct scmi_tinysys_notifier_report *r = report; if (evt_id != SCMI_EVENT_TINYSYS_NOTIFIER || sizeof(*p) != payld_sz) return NULL; r->timestamp = timestamp; r->f_id = le32_to_cpu(p->f_id); r->p1_status = le32_to_cpu(p->p1); r->p2_status = le32_to_cpu(p->p2); r->p3_status = le32_to_cpu(p->p3); r->p4_status = le32_to_cpu(p->p4); *src_id = p->f_id; return r; } static int scmi_tinysys_attributes_get(const struct scmi_protocol_handle *ph, struct scmi_tinysys_info *pi) { int ret; struct scmi_xfer *t; u32 attr; ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, sizeof(attr), &t); if (ret) return ret; ret = ph->xops->do_xfer(ph, t); if (!ret) { attr = get_unaligned_le32(t->rx.buf); pi->num_domains = attr; } ph->xops->xfer_put(ph, t); return ret; } static int scmi_tinysys_get_num_sources(const struct scmi_protocol_handle *ph) { struct scmi_tinysys_info *pinfo = ph->get_priv(ph); if (!pinfo) return -EINVAL; return pinfo->num_domains; } static const struct scmi_event tinysys_events[] = { { .id = SCMI_EVENT_TINYSYS_NOTIFIER, .max_payld_sz = sizeof(struct scmi_tinysys_notifier_payld), .max_report_sz = sizeof(struct scmi_tinysys_notifier_report), }, }; static const struct scmi_tinysys_proto_ops tinysys_proto_ops = { .common_set = scmi_tinysys_common_set, .common_get = scmi_tinysys_common_get, }; static const struct scmi_event_ops tinysys_event_ops = { .get_num_sources = scmi_tinysys_get_num_sources, .set_notify_enabled = scmi_tinysys_set_notify_enabled, .fill_custom_report = scmi_tinysys_fill_custom_report, }; static const struct scmi_protocol_events tinysys_protocol_events = { .queue_sz = 4 * SCMI_PROTO_QUEUE_SZ, .ops = &tinysys_event_ops, .evts = tinysys_events, .num_events = ARRAY_SIZE(tinysys_events), }; static int scmi_tinysys_protocol_init(const struct scmi_protocol_handle *ph) { u32 version; struct scmi_tinysys_info *pinfo; ph->xops->version_get(ph, &version); dev_dbg(ph->dev, "Tinysys Protocol Version %d.%d\n", PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL); if (!pinfo) return -ENOMEM; scmi_tinysys_attributes_get(ph, pinfo); pinfo->version = version; return ph->set_priv(ph, pinfo); } const struct scmi_protocol scmi_tinysys_protocol = { .id = SCMI_PROTOCOL_TINYSYS, .owner = THIS_MODULE, .instance_init = &scmi_tinysys_protocol_init, .ops = &tinysys_proto_ops, .events = &tinysys_protocol_events, }; int scmi_tinysys_register(void) { return scmi_protocol_register(&scmi_tinysys_protocol); } MODULE_DESCRIPTION("SCMI tinysys protocol"); MODULE_LICENSE("GPL v2");