384 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * System Control and Management Interface (SCMI) NXP BBM Protocol4 *5 * Copyright 2024 NXP6 */7 8#define pr_fmt(fmt) "SCMI Notifications BBM - " fmt9 10#include <linux/bits.h>11#include <linux/io.h>12#include <linux/module.h>13#include <linux/of.h>14#include <linux/platform_device.h>15#include <linux/scmi_protocol.h>16#include <linux/scmi_imx_protocol.h>17 18#include "../../protocols.h"19#include "../../notify.h"20 21#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x1000022 23enum scmi_imx_bbm_protocol_cmd {24 IMX_BBM_GPR_SET = 0x3,25 IMX_BBM_GPR_GET = 0x4,26 IMX_BBM_RTC_ATTRIBUTES = 0x5,27 IMX_BBM_RTC_TIME_SET = 0x6,28 IMX_BBM_RTC_TIME_GET = 0x7,29 IMX_BBM_RTC_ALARM_SET = 0x8,30 IMX_BBM_BUTTON_GET = 0x9,31 IMX_BBM_RTC_NOTIFY = 0xA,32 IMX_BBM_BUTTON_NOTIFY = 0xB,33};34 35#define GET_RTCS_NR(x) le32_get_bits((x), GENMASK(23, 16))36#define GET_GPRS_NR(x) le32_get_bits((x), GENMASK(15, 0))37 38#define SCMI_IMX_BBM_NOTIFY_RTC_UPDATED BIT(2)39#define SCMI_IMX_BBM_NOTIFY_RTC_ROLLOVER BIT(1)40#define SCMI_IMX_BBM_NOTIFY_RTC_ALARM BIT(0)41 42#define SCMI_IMX_BBM_RTC_ALARM_ENABLE_FLAG BIT(0)43 44#define SCMI_IMX_BBM_NOTIFY_RTC_FLAG \45 (SCMI_IMX_BBM_NOTIFY_RTC_UPDATED | SCMI_IMX_BBM_NOTIFY_RTC_ROLLOVER | \46 SCMI_IMX_BBM_NOTIFY_RTC_ALARM)47 48#define SCMI_IMX_BBM_EVENT_RTC_MASK GENMASK(31, 24)49 50struct scmi_imx_bbm_info {51 u32 version;52 int nr_rtc;53 int nr_gpr;54};55 56struct scmi_msg_imx_bbm_protocol_attributes {57 __le32 attributes;58};59 60struct scmi_imx_bbm_set_time {61 __le32 id;62 __le32 flags;63 __le32 value_low;64 __le32 value_high;65};66 67struct scmi_imx_bbm_get_time {68 __le32 id;69 __le32 flags;70};71 72struct scmi_imx_bbm_alarm_time {73 __le32 id;74 __le32 flags;75 __le32 value_low;76 __le32 value_high;77};78 79struct scmi_msg_imx_bbm_rtc_notify {80 __le32 rtc_id;81 __le32 flags;82};83 84struct scmi_msg_imx_bbm_button_notify {85 __le32 flags;86};87 88struct scmi_imx_bbm_notify_payld {89 __le32 flags;90};91 92static int scmi_imx_bbm_attributes_get(const struct scmi_protocol_handle *ph,93 struct scmi_imx_bbm_info *pi)94{95 int ret;96 struct scmi_xfer *t;97 struct scmi_msg_imx_bbm_protocol_attributes *attr;98 99 ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, sizeof(*attr), &t);100 if (ret)101 return ret;102 103 attr = t->rx.buf;104 105 ret = ph->xops->do_xfer(ph, t);106 if (!ret) {107 pi->nr_rtc = GET_RTCS_NR(attr->attributes);108 pi->nr_gpr = GET_GPRS_NR(attr->attributes);109 }110 111 ph->xops->xfer_put(ph, t);112 113 return ret;114}115 116static int scmi_imx_bbm_notify(const struct scmi_protocol_handle *ph,117 u32 src_id, int message_id, bool enable)118{119 int ret;120 struct scmi_xfer *t;121 122 if (message_id == IMX_BBM_RTC_NOTIFY) {123 struct scmi_msg_imx_bbm_rtc_notify *rtc_notify;124 125 ret = ph->xops->xfer_get_init(ph, message_id,126 sizeof(*rtc_notify), 0, &t);127 if (ret)128 return ret;129 130 rtc_notify = t->tx.buf;131 rtc_notify->rtc_id = cpu_to_le32(0);132 rtc_notify->flags =133 cpu_to_le32(enable ? SCMI_IMX_BBM_NOTIFY_RTC_FLAG : 0);134 } else if (message_id == IMX_BBM_BUTTON_NOTIFY) {135 struct scmi_msg_imx_bbm_button_notify *button_notify;136 137 ret = ph->xops->xfer_get_init(ph, message_id,138 sizeof(*button_notify), 0, &t);139 if (ret)140 return ret;141 142 button_notify = t->tx.buf;143 button_notify->flags = cpu_to_le32(enable ? 1 : 0);144 } else {145 return -EINVAL;146 }147 148 ret = ph->xops->do_xfer(ph, t);149 150 ph->xops->xfer_put(ph, t);151 return ret;152}153 154static enum scmi_imx_bbm_protocol_cmd evt_2_cmd[] = {155 IMX_BBM_RTC_NOTIFY,156 IMX_BBM_BUTTON_NOTIFY157};158 159static int scmi_imx_bbm_set_notify_enabled(const struct scmi_protocol_handle *ph,160 u8 evt_id, u32 src_id, bool enable)161{162 int ret, cmd_id;163 164 if (evt_id >= ARRAY_SIZE(evt_2_cmd))165 return -EINVAL;166 167 cmd_id = evt_2_cmd[evt_id];168 ret = scmi_imx_bbm_notify(ph, src_id, cmd_id, enable);169 if (ret)170 pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",171 evt_id, src_id, ret);172 173 return ret;174}175 176static void *scmi_imx_bbm_fill_custom_report(const struct scmi_protocol_handle *ph,177 u8 evt_id, ktime_t timestamp,178 const void *payld, size_t payld_sz,179 void *report, u32 *src_id)180{181 const struct scmi_imx_bbm_notify_payld *p = payld;182 struct scmi_imx_bbm_notif_report *r = report;183 184 if (sizeof(*p) != payld_sz)185 return NULL;186 187 if (evt_id == SCMI_EVENT_IMX_BBM_RTC) {188 r->is_rtc = true;189 r->is_button = false;190 r->timestamp = timestamp;191 r->rtc_id = le32_get_bits(p->flags, SCMI_IMX_BBM_EVENT_RTC_MASK);192 r->rtc_evt = le32_get_bits(p->flags, SCMI_IMX_BBM_NOTIFY_RTC_FLAG);193 dev_dbg(ph->dev, "RTC: %d evt: %x\n", r->rtc_id, r->rtc_evt);194 *src_id = r->rtc_evt;195 } else if (evt_id == SCMI_EVENT_IMX_BBM_BUTTON) {196 r->is_rtc = false;197 r->is_button = true;198 r->timestamp = timestamp;199 dev_dbg(ph->dev, "BBM Button\n");200 *src_id = 0;201 } else {202 WARN_ON_ONCE(1);203 return NULL;204 }205 206 return r;207}208 209static const struct scmi_event scmi_imx_bbm_events[] = {210 {211 .id = SCMI_EVENT_IMX_BBM_RTC,212 .max_payld_sz = sizeof(struct scmi_imx_bbm_notify_payld),213 .max_report_sz = sizeof(struct scmi_imx_bbm_notif_report),214 },215 {216 .id = SCMI_EVENT_IMX_BBM_BUTTON,217 .max_payld_sz = sizeof(struct scmi_imx_bbm_notify_payld),218 .max_report_sz = sizeof(struct scmi_imx_bbm_notif_report),219 },220};221 222static const struct scmi_event_ops scmi_imx_bbm_event_ops = {223 .set_notify_enabled = scmi_imx_bbm_set_notify_enabled,224 .fill_custom_report = scmi_imx_bbm_fill_custom_report,225};226 227static const struct scmi_protocol_events scmi_imx_bbm_protocol_events = {228 .queue_sz = SCMI_PROTO_QUEUE_SZ,229 .ops = &scmi_imx_bbm_event_ops,230 .evts = scmi_imx_bbm_events,231 .num_events = ARRAY_SIZE(scmi_imx_bbm_events),232 .num_sources = 1,233};234 235static int scmi_imx_bbm_rtc_time_set(const struct scmi_protocol_handle *ph,236 u32 rtc_id, u64 sec)237{238 struct scmi_imx_bbm_info *pi = ph->get_priv(ph);239 struct scmi_imx_bbm_set_time *cfg;240 struct scmi_xfer *t;241 int ret;242 243 if (rtc_id >= pi->nr_rtc)244 return -EINVAL;245 246 ret = ph->xops->xfer_get_init(ph, IMX_BBM_RTC_TIME_SET, sizeof(*cfg), 0, &t);247 if (ret)248 return ret;249 250 cfg = t->tx.buf;251 cfg->id = cpu_to_le32(rtc_id);252 cfg->flags = 0;253 cfg->value_low = cpu_to_le32(lower_32_bits(sec));254 cfg->value_high = cpu_to_le32(upper_32_bits(sec));255 256 ret = ph->xops->do_xfer(ph, t);257 258 ph->xops->xfer_put(ph, t);259 260 return ret;261}262 263static int scmi_imx_bbm_rtc_time_get(const struct scmi_protocol_handle *ph,264 u32 rtc_id, u64 *value)265{266 struct scmi_imx_bbm_info *pi = ph->get_priv(ph);267 struct scmi_imx_bbm_get_time *cfg;268 struct scmi_xfer *t;269 int ret;270 271 if (rtc_id >= pi->nr_rtc)272 return -EINVAL;273 274 ret = ph->xops->xfer_get_init(ph, IMX_BBM_RTC_TIME_GET, sizeof(*cfg),275 sizeof(u64), &t);276 if (ret)277 return ret;278 279 cfg = t->tx.buf;280 cfg->id = cpu_to_le32(rtc_id);281 cfg->flags = 0;282 283 ret = ph->xops->do_xfer(ph, t);284 if (!ret)285 *value = get_unaligned_le64(t->rx.buf);286 287 ph->xops->xfer_put(ph, t);288 289 return ret;290}291 292static int scmi_imx_bbm_rtc_alarm_set(const struct scmi_protocol_handle *ph,293 u32 rtc_id, bool enable, u64 sec)294{295 struct scmi_imx_bbm_info *pi = ph->get_priv(ph);296 struct scmi_imx_bbm_alarm_time *cfg;297 struct scmi_xfer *t;298 int ret;299 300 if (rtc_id >= pi->nr_rtc)301 return -EINVAL;302 303 ret = ph->xops->xfer_get_init(ph, IMX_BBM_RTC_ALARM_SET, sizeof(*cfg), 0, &t);304 if (ret)305 return ret;306 307 cfg = t->tx.buf;308 cfg->id = cpu_to_le32(rtc_id);309 cfg->flags = enable ?310 cpu_to_le32(SCMI_IMX_BBM_RTC_ALARM_ENABLE_FLAG) : 0;311 cfg->value_low = cpu_to_le32(lower_32_bits(sec));312 cfg->value_high = cpu_to_le32(upper_32_bits(sec));313 314 ret = ph->xops->do_xfer(ph, t);315 316 ph->xops->xfer_put(ph, t);317 318 return ret;319}320 321static int scmi_imx_bbm_button_get(const struct scmi_protocol_handle *ph, u32 *state)322{323 struct scmi_xfer *t;324 int ret;325 326 ret = ph->xops->xfer_get_init(ph, IMX_BBM_BUTTON_GET, 0, sizeof(u32), &t);327 if (ret)328 return ret;329 330 ret = ph->xops->do_xfer(ph, t);331 if (!ret)332 *state = get_unaligned_le32(t->rx.buf);333 334 ph->xops->xfer_put(ph, t);335 336 return ret;337}338 339static const struct scmi_imx_bbm_proto_ops scmi_imx_bbm_proto_ops = {340 .rtc_time_get = scmi_imx_bbm_rtc_time_get,341 .rtc_time_set = scmi_imx_bbm_rtc_time_set,342 .rtc_alarm_set = scmi_imx_bbm_rtc_alarm_set,343 .button_get = scmi_imx_bbm_button_get,344};345 346static int scmi_imx_bbm_protocol_init(const struct scmi_protocol_handle *ph)347{348 u32 version;349 int ret;350 struct scmi_imx_bbm_info *binfo;351 352 ret = ph->xops->version_get(ph, &version);353 if (ret)354 return ret;355 356 dev_info(ph->dev, "NXP SM BBM Version %d.%d\n",357 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));358 359 binfo = devm_kzalloc(ph->dev, sizeof(*binfo), GFP_KERNEL);360 if (!binfo)361 return -ENOMEM;362 363 ret = scmi_imx_bbm_attributes_get(ph, binfo);364 if (ret)365 return ret;366 367 return ph->set_priv(ph, binfo, version);368}369 370static const struct scmi_protocol scmi_imx_bbm = {371 .id = SCMI_PROTOCOL_IMX_BBM,372 .owner = THIS_MODULE,373 .instance_init = &scmi_imx_bbm_protocol_init,374 .ops = &scmi_imx_bbm_proto_ops,375 .events = &scmi_imx_bbm_protocol_events,376 .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,377 .vendor_id = "NXP",378 .sub_vendor_id = "IMX",379};380module_scmi_protocol(scmi_imx_bbm);381 382MODULE_DESCRIPTION("i.MX SCMI BBM driver");383MODULE_LICENSE("GPL");384