1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * System Control and Management Interface (SCMI) NXP BBM Protocol 4 * 5 * Copyright 2024 NXP 6 */ 7 8 #define pr_fmt(fmt) "SCMI Notifications BBM - " fmt 9 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 0x10000 22 23 enum 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 50 struct scmi_imx_bbm_info { 51 u32 version; 52 int nr_rtc; 53 int nr_gpr; 54 }; 55 56 struct scmi_msg_imx_bbm_protocol_attributes { 57 __le32 attributes; 58 }; 59 60 struct scmi_imx_bbm_set_time { 61 __le32 id; 62 __le32 flags; 63 __le32 value_low; 64 __le32 value_high; 65 }; 66 67 struct scmi_imx_bbm_get_time { 68 __le32 id; 69 __le32 flags; 70 }; 71 72 struct scmi_imx_bbm_alarm_time { 73 __le32 id; 74 __le32 flags; 75 __le32 value_low; 76 __le32 value_high; 77 }; 78 79 struct scmi_msg_imx_bbm_rtc_notify { 80 __le32 rtc_id; 81 __le32 flags; 82 }; 83 84 struct scmi_msg_imx_bbm_button_notify { 85 __le32 flags; 86 }; 87 88 struct scmi_imx_bbm_notify_payld { 89 __le32 flags; 90 }; 91 92 static 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 116 static 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 154 static enum scmi_imx_bbm_protocol_cmd evt_2_cmd[] = { 155 IMX_BBM_RTC_NOTIFY, 156 IMX_BBM_BUTTON_NOTIFY 157 }; 158 159 static 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 176 static 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 209 static 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 222 static 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 227 static 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 235 static 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 263 static 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 292 static 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 321 static 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 339 static 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 346 static 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 370 static 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 }; 380 module_scmi_protocol(scmi_imx_bbm); 381 382 MODULE_DESCRIPTION("i.MX SCMI BBM driver"); 383 MODULE_LICENSE("GPL"); 384