// SPDX-License-Identifier: GPL-2.0 /* * System Control and Management Interface (SCMI) NXP BBM Protocol * * Copyright 2024 NXP */ #define pr_fmt(fmt) "SCMI Notifications BBM - " fmt #include #include #include #include #include #include #include #include "../../protocols.h" #include "../../notify.h" #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000 enum scmi_imx_bbm_protocol_cmd { IMX_BBM_GPR_SET = 0x3, IMX_BBM_GPR_GET = 0x4, IMX_BBM_RTC_ATTRIBUTES = 0x5, IMX_BBM_RTC_TIME_SET = 0x6, IMX_BBM_RTC_TIME_GET = 0x7, IMX_BBM_RTC_ALARM_SET = 0x8, IMX_BBM_BUTTON_GET = 0x9, IMX_BBM_RTC_NOTIFY = 0xA, IMX_BBM_BUTTON_NOTIFY = 0xB, }; #define GET_RTCS_NR(x) le32_get_bits((x), GENMASK(23, 16)) #define GET_GPRS_NR(x) le32_get_bits((x), GENMASK(15, 0)) #define SCMI_IMX_BBM_NOTIFY_RTC_UPDATED BIT(2) #define SCMI_IMX_BBM_NOTIFY_RTC_ROLLOVER BIT(1) #define SCMI_IMX_BBM_NOTIFY_RTC_ALARM BIT(0) #define SCMI_IMX_BBM_RTC_ALARM_ENABLE_FLAG BIT(0) #define SCMI_IMX_BBM_NOTIFY_RTC_FLAG \ (SCMI_IMX_BBM_NOTIFY_RTC_UPDATED | SCMI_IMX_BBM_NOTIFY_RTC_ROLLOVER | \ SCMI_IMX_BBM_NOTIFY_RTC_ALARM) #define SCMI_IMX_BBM_EVENT_RTC_MASK GENMASK(31, 24) struct scmi_imx_bbm_info { u32 version; int nr_rtc; int nr_gpr; }; struct scmi_msg_imx_bbm_protocol_attributes { __le32 attributes; }; struct scmi_imx_bbm_set_time { __le32 id; __le32 flags; __le32 value_low; __le32 value_high; }; struct scmi_imx_bbm_get_time { __le32 id; __le32 flags; }; struct scmi_imx_bbm_alarm_time { __le32 id; __le32 flags; __le32 value_low; __le32 value_high; }; struct scmi_msg_imx_bbm_rtc_notify { __le32 rtc_id; __le32 flags; }; struct scmi_msg_imx_bbm_button_notify { __le32 flags; }; struct scmi_imx_bbm_notify_payld { __le32 flags; }; static int scmi_imx_bbm_attributes_get(const struct scmi_protocol_handle *ph, struct scmi_imx_bbm_info *pi) { int ret; struct scmi_xfer *t; struct scmi_msg_imx_bbm_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) { pi->nr_rtc = GET_RTCS_NR(attr->attributes); pi->nr_gpr = GET_GPRS_NR(attr->attributes); } ph->xops->xfer_put(ph, t); return ret; } static int scmi_imx_bbm_notify(const struct scmi_protocol_handle *ph, u32 src_id, int message_id, bool enable) { int ret; struct scmi_xfer *t; if (message_id == IMX_BBM_RTC_NOTIFY) { struct scmi_msg_imx_bbm_rtc_notify *rtc_notify; ret = ph->xops->xfer_get_init(ph, message_id, sizeof(*rtc_notify), 0, &t); if (ret) return ret; rtc_notify = t->tx.buf; rtc_notify->rtc_id = cpu_to_le32(0); rtc_notify->flags = cpu_to_le32(enable ? SCMI_IMX_BBM_NOTIFY_RTC_FLAG : 0); } else if (message_id == IMX_BBM_BUTTON_NOTIFY) { struct scmi_msg_imx_bbm_button_notify *button_notify; ret = ph->xops->xfer_get_init(ph, message_id, sizeof(*button_notify), 0, &t); if (ret) return ret; button_notify = t->tx.buf; button_notify->flags = cpu_to_le32(enable ? 1 : 0); } else { return -EINVAL; } ret = ph->xops->do_xfer(ph, t); ph->xops->xfer_put(ph, t); return ret; } static enum scmi_imx_bbm_protocol_cmd evt_2_cmd[] = { IMX_BBM_RTC_NOTIFY, IMX_BBM_BUTTON_NOTIFY }; static int scmi_imx_bbm_set_notify_enabled(const struct scmi_protocol_handle *ph, u8 evt_id, u32 src_id, bool enable) { int ret, cmd_id; if (evt_id >= ARRAY_SIZE(evt_2_cmd)) return -EINVAL; cmd_id = evt_2_cmd[evt_id]; ret = scmi_imx_bbm_notify(ph, src_id, cmd_id, enable); if (ret) pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n", evt_id, src_id, ret); return ret; } static void *scmi_imx_bbm_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_imx_bbm_notify_payld *p = payld; struct scmi_imx_bbm_notif_report *r = report; if (sizeof(*p) != payld_sz) return NULL; if (evt_id == SCMI_EVENT_IMX_BBM_RTC) { r->is_rtc = true; r->is_button = false; r->timestamp = timestamp; r->rtc_id = le32_get_bits(p->flags, SCMI_IMX_BBM_EVENT_RTC_MASK); r->rtc_evt = le32_get_bits(p->flags, SCMI_IMX_BBM_NOTIFY_RTC_FLAG); dev_dbg(ph->dev, "RTC: %d evt: %x\n", r->rtc_id, r->rtc_evt); *src_id = r->rtc_evt; } else if (evt_id == SCMI_EVENT_IMX_BBM_BUTTON) { r->is_rtc = false; r->is_button = true; r->timestamp = timestamp; dev_dbg(ph->dev, "BBM Button\n"); *src_id = 0; } else { WARN_ON_ONCE(1); return NULL; } return r; } static const struct scmi_event scmi_imx_bbm_events[] = { { .id = SCMI_EVENT_IMX_BBM_RTC, .max_payld_sz = sizeof(struct scmi_imx_bbm_notify_payld), .max_report_sz = sizeof(struct scmi_imx_bbm_notif_report), }, { .id = SCMI_EVENT_IMX_BBM_BUTTON, .max_payld_sz = sizeof(struct scmi_imx_bbm_notify_payld), .max_report_sz = sizeof(struct scmi_imx_bbm_notif_report), }, }; static const struct scmi_event_ops scmi_imx_bbm_event_ops = { .set_notify_enabled = scmi_imx_bbm_set_notify_enabled, .fill_custom_report = scmi_imx_bbm_fill_custom_report, }; static const struct scmi_protocol_events scmi_imx_bbm_protocol_events = { .queue_sz = SCMI_PROTO_QUEUE_SZ, .ops = &scmi_imx_bbm_event_ops, .evts = scmi_imx_bbm_events, .num_events = ARRAY_SIZE(scmi_imx_bbm_events), .num_sources = 1, }; static int scmi_imx_bbm_rtc_time_set(const struct scmi_protocol_handle *ph, u32 rtc_id, u64 sec) { struct scmi_imx_bbm_info *pi = ph->get_priv(ph); struct scmi_imx_bbm_set_time *cfg; struct scmi_xfer *t; int ret; if (rtc_id >= pi->nr_rtc) return -EINVAL; ret = ph->xops->xfer_get_init(ph, IMX_BBM_RTC_TIME_SET, sizeof(*cfg), 0, &t); if (ret) return ret; cfg = t->tx.buf; cfg->id = cpu_to_le32(rtc_id); cfg->flags = 0; cfg->value_low = cpu_to_le32(lower_32_bits(sec)); cfg->value_high = cpu_to_le32(upper_32_bits(sec)); ret = ph->xops->do_xfer(ph, t); ph->xops->xfer_put(ph, t); return ret; } static int scmi_imx_bbm_rtc_time_get(const struct scmi_protocol_handle *ph, u32 rtc_id, u64 *value) { struct scmi_imx_bbm_info *pi = ph->get_priv(ph); struct scmi_imx_bbm_get_time *cfg; struct scmi_xfer *t; int ret; if (rtc_id >= pi->nr_rtc) return -EINVAL; ret = ph->xops->xfer_get_init(ph, IMX_BBM_RTC_TIME_GET, sizeof(*cfg), sizeof(u64), &t); if (ret) return ret; cfg = t->tx.buf; cfg->id = cpu_to_le32(rtc_id); cfg->flags = 0; ret = ph->xops->do_xfer(ph, t); if (!ret) *value = get_unaligned_le64(t->rx.buf); ph->xops->xfer_put(ph, t); return ret; } static int scmi_imx_bbm_rtc_alarm_set(const struct scmi_protocol_handle *ph, u32 rtc_id, bool enable, u64 sec) { struct scmi_imx_bbm_info *pi = ph->get_priv(ph); struct scmi_imx_bbm_alarm_time *cfg; struct scmi_xfer *t; int ret; if (rtc_id >= pi->nr_rtc) return -EINVAL; ret = ph->xops->xfer_get_init(ph, IMX_BBM_RTC_ALARM_SET, sizeof(*cfg), 0, &t); if (ret) return ret; cfg = t->tx.buf; cfg->id = cpu_to_le32(rtc_id); cfg->flags = enable ? cpu_to_le32(SCMI_IMX_BBM_RTC_ALARM_ENABLE_FLAG) : 0; cfg->value_low = cpu_to_le32(lower_32_bits(sec)); cfg->value_high = cpu_to_le32(upper_32_bits(sec)); ret = ph->xops->do_xfer(ph, t); ph->xops->xfer_put(ph, t); return ret; } static int scmi_imx_bbm_button_get(const struct scmi_protocol_handle *ph, u32 *state) { struct scmi_xfer *t; int ret; ret = ph->xops->xfer_get_init(ph, IMX_BBM_BUTTON_GET, 0, sizeof(u32), &t); if (ret) return ret; ret = ph->xops->do_xfer(ph, t); if (!ret) *state = get_unaligned_le32(t->rx.buf); ph->xops->xfer_put(ph, t); return ret; } static const struct scmi_imx_bbm_proto_ops scmi_imx_bbm_proto_ops = { .rtc_time_get = scmi_imx_bbm_rtc_time_get, .rtc_time_set = scmi_imx_bbm_rtc_time_set, .rtc_alarm_set = scmi_imx_bbm_rtc_alarm_set, .button_get = scmi_imx_bbm_button_get, }; static int scmi_imx_bbm_protocol_init(const struct scmi_protocol_handle *ph) { u32 version; int ret; struct scmi_imx_bbm_info *binfo; ret = ph->xops->version_get(ph, &version); if (ret) return ret; dev_info(ph->dev, "NXP SM BBM Version %d.%d\n", PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); binfo = devm_kzalloc(ph->dev, sizeof(*binfo), GFP_KERNEL); if (!binfo) return -ENOMEM; ret = scmi_imx_bbm_attributes_get(ph, binfo); if (ret) return ret; return ph->set_priv(ph, binfo, version); } static const struct scmi_protocol scmi_imx_bbm = { .id = SCMI_PROTOCOL_IMX_BBM, .owner = THIS_MODULE, .instance_init = &scmi_imx_bbm_protocol_init, .ops = &scmi_imx_bbm_proto_ops, .events = &scmi_imx_bbm_protocol_events, .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, .vendor_id = "NXP", .sub_vendor_id = "IMX", }; module_scmi_protocol(scmi_imx_bbm); MODULE_DESCRIPTION("i.MX SCMI BBM driver"); MODULE_LICENSE("GPL");