1 // SPDX-License-Identifier: GPL-2.0
3 * MIPI SyS-T framing protocol for STM devices.
4 * Copyright (c) 2018, Intel Corporation.
7 #include <linux/configfs.h>
8 #include <linux/module.h>
9 #include <linux/device.h>
10 #include <linux/slab.h>
11 #include <linux/uuid.h>
12 #include <linux/stm.h>
15 enum sys_t_message_type {
16 MIPI_SYST_TYPE_BUILD = 0,
17 MIPI_SYST_TYPE_SHORT32,
18 MIPI_SYST_TYPE_STRING,
19 MIPI_SYST_TYPE_CATALOG,
20 MIPI_SYST_TYPE_RAW = 6,
21 MIPI_SYST_TYPE_SHORT64,
26 enum sys_t_message_severity {
27 MIPI_SYST_SEVERITY_MAX = 0,
28 MIPI_SYST_SEVERITY_FATAL,
29 MIPI_SYST_SEVERITY_ERROR,
30 MIPI_SYST_SEVERITY_WARNING,
31 MIPI_SYST_SEVERITY_INFO,
32 MIPI_SYST_SEVERITY_USER1,
33 MIPI_SYST_SEVERITY_USER2,
34 MIPI_SYST_SEVERITY_DEBUG,
37 enum sys_t_message_build_subtype {
38 MIPI_SYST_BUILD_ID_COMPACT32 = 0,
39 MIPI_SYST_BUILD_ID_COMPACT64,
40 MIPI_SYST_BUILD_ID_LONG,
43 enum sys_t_message_clock_subtype {
44 MIPI_SYST_CLOCK_TRANSPORT_SYNC = 1,
47 enum sys_t_message_string_subtype {
48 MIPI_SYST_STRING_GENERIC = 1,
49 MIPI_SYST_STRING_FUNCTIONENTER,
50 MIPI_SYST_STRING_FUNCTIONEXIT,
51 MIPI_SYST_STRING_INVALIDPARAM = 5,
52 MIPI_SYST_STRING_ASSERT = 7,
53 MIPI_SYST_STRING_PRINTF_32 = 11,
54 MIPI_SYST_STRING_PRINTF_64 = 12,
58 * enum sys_t_message_sbd_subtype - SyS-T SBD message subtypes
59 * @MIPI_SYST_SBD_ID32: SBD message with 32-bit message ID
60 * @MIPI_SYST_SBD_ID64: SBD message with 64-bit message ID
62 * Structured Binary Data messages can send information of arbitrary length,
63 * together with ID's that describe BLOB's content and layout.
65 enum sys_t_message_sbd_subtype {
66 MIPI_SYST_SBD_ID32 = 0,
67 MIPI_SYST_SBD_ID64 = 1,
70 #define MIPI_SYST_TYPE(t) ((u32)(MIPI_SYST_TYPE_ ## t))
71 #define MIPI_SYST_SEVERITY(s) ((u32)(MIPI_SYST_SEVERITY_ ## s) << 4)
72 #define MIPI_SYST_OPT_LOC BIT(8)
73 #define MIPI_SYST_OPT_LEN BIT(9)
74 #define MIPI_SYST_OPT_CHK BIT(10)
75 #define MIPI_SYST_OPT_TS BIT(11)
76 #define MIPI_SYST_UNIT(u) ((u32)(u) << 12)
77 #define MIPI_SYST_ORIGIN(o) ((u32)(o) << 16)
78 #define MIPI_SYST_OPT_GUID BIT(23)
79 #define MIPI_SYST_SUBTYPE(s) ((u32)(MIPI_SYST_ ## s) << 24)
80 #define MIPI_SYST_UNITLARGE(u) (MIPI_SYST_UNIT(u & 0xf) | \
81 MIPI_SYST_ORIGIN(u >> 4))
82 #define MIPI_SYST_TYPES(t, s) (MIPI_SYST_TYPE(t) | \
83 MIPI_SYST_SUBTYPE(t ## _ ## s))
85 #define DATA_HEADER (MIPI_SYST_TYPES(STRING, GENERIC) | \
86 MIPI_SYST_SEVERITY(INFO) | \
89 #define CLOCK_SYNC_HEADER (MIPI_SYST_TYPES(CLOCK, TRANSPORT_SYNC) | \
90 MIPI_SYST_SEVERITY(MAX))
93 * SyS-T and ftrace headers are compatible to an extent that ftrace event ID
94 * and args can be treated as SyS-T SBD message with 64-bit ID and arguments
95 * BLOB right behind the header without modification. Bits [16:63] coming
96 * together with message ID from ftrace aren't used by SBD and must be zeroed.
98 * 0 15 16 23 24 31 32 39 40 63
99 * ftrace: <event_id> <flags> <preempt> <-pid-> <----> <args>
100 * SBD: <------- msg_id ------------------------------> <BLOB>
102 #define SBD_HEADER (MIPI_SYST_TYPES(SBD, ID64) | \
103 MIPI_SYST_SEVERITY(INFO) | \
106 struct sys_t_policy_node {
109 unsigned long ts_interval;
110 unsigned long clocksync_interval;
113 struct sys_t_output {
114 struct sys_t_policy_node node;
115 unsigned long ts_jiffies;
116 unsigned long clocksync_jiffies;
119 static void sys_t_policy_node_init(void *priv)
121 struct sys_t_policy_node *pn = priv;
126 static int sys_t_output_open(void *priv, struct stm_output *output)
128 struct sys_t_policy_node *pn = priv;
129 struct sys_t_output *opriv;
131 opriv = kzalloc(sizeof(*opriv), GFP_ATOMIC);
135 memcpy(&opriv->node, pn, sizeof(opriv->node));
136 output->pdrv_private = opriv;
141 static void sys_t_output_close(struct stm_output *output)
143 kfree(output->pdrv_private);
146 static ssize_t sys_t_policy_uuid_show(struct config_item *item,
149 struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
151 return sprintf(page, "%pU\n", &pn->uuid);
155 sys_t_policy_uuid_store(struct config_item *item, const char *page,
158 struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
159 struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
163 ret = uuid_parse(page, &pn->uuid);
164 mutex_unlock(mutexp);
166 return ret < 0 ? ret : count;
169 CONFIGFS_ATTR(sys_t_policy_, uuid);
171 static ssize_t sys_t_policy_do_len_show(struct config_item *item,
174 struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
176 return sprintf(page, "%d\n", pn->do_len);
180 sys_t_policy_do_len_store(struct config_item *item, const char *page,
183 struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
184 struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
188 ret = kstrtobool(page, &pn->do_len);
189 mutex_unlock(mutexp);
191 return ret ? ret : count;
194 CONFIGFS_ATTR(sys_t_policy_, do_len);
196 static ssize_t sys_t_policy_ts_interval_show(struct config_item *item,
199 struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
201 return sprintf(page, "%u\n", jiffies_to_msecs(pn->ts_interval));
205 sys_t_policy_ts_interval_store(struct config_item *item, const char *page,
208 struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
209 struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
214 ret = kstrtouint(page, 10, &ms);
215 mutex_unlock(mutexp);
218 pn->ts_interval = msecs_to_jiffies(ms);
225 CONFIGFS_ATTR(sys_t_policy_, ts_interval);
227 static ssize_t sys_t_policy_clocksync_interval_show(struct config_item *item,
230 struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
232 return sprintf(page, "%u\n", jiffies_to_msecs(pn->clocksync_interval));
236 sys_t_policy_clocksync_interval_store(struct config_item *item,
237 const char *page, size_t count)
239 struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
240 struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
245 ret = kstrtouint(page, 10, &ms);
246 mutex_unlock(mutexp);
249 pn->clocksync_interval = msecs_to_jiffies(ms);
256 CONFIGFS_ATTR(sys_t_policy_, clocksync_interval);
258 static struct configfs_attribute *sys_t_policy_attrs[] = {
259 &sys_t_policy_attr_uuid,
260 &sys_t_policy_attr_do_len,
261 &sys_t_policy_attr_ts_interval,
262 &sys_t_policy_attr_clocksync_interval,
266 static inline bool sys_t_need_ts(struct sys_t_output *op)
268 if (op->node.ts_interval &&
269 time_after(jiffies, op->ts_jiffies + op->node.ts_interval)) {
270 op->ts_jiffies = jiffies;
278 static bool sys_t_need_clock_sync(struct sys_t_output *op)
280 if (op->node.clocksync_interval &&
282 op->clocksync_jiffies + op->node.clocksync_interval)) {
283 op->clocksync_jiffies = jiffies;
292 sys_t_clock_sync(struct stm_data *data, unsigned int m, unsigned int c)
294 u32 header = CLOCK_SYNC_HEADER;
295 const unsigned char nil = 0;
296 u64 payload[2]; /* Clock value and frequency */
299 sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED,
304 payload[0] = ktime_get_real_ns();
305 payload[1] = NSEC_PER_SEC;
306 sz = stm_data_write(data, m, c, false, &payload, sizeof(payload));
310 data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil);
312 return sizeof(header) + sizeof(payload);
315 static inline u32 sys_t_header(struct stm_source_data *source)
317 if (source && source->type == STM_FTRACE)
322 static ssize_t sys_t_write_data(struct stm_data *data,
323 struct stm_source_data *source,
324 unsigned int master, unsigned int channel,
325 bool ts_first, const void *buf, size_t count)
328 const unsigned char nil = 0;
331 * Ftrace is zero-copy compatible with SyS-T SBD, but requires
332 * special handling of first 64 bits. Trim and send them separately
333 * to avoid damage on original ftrace buffer.
335 if (source && source->type == STM_FTRACE) {
336 u64 compat_ftrace_header;
340 if (count < sizeof(compat_ftrace_header))
343 /* SBD only makes use of low 16 bits (event ID) from ftrace event */
344 compat_ftrace_header = *(u64 *)buf & 0xffff;
345 header_sz = stm_data_write(data, master, channel, false,
346 &compat_ftrace_header,
347 sizeof(compat_ftrace_header));
348 if (header_sz != sizeof(compat_ftrace_header))
351 buf_sz = stm_data_write(data, master, channel, false,
352 buf + header_sz, count - header_sz);
353 if (buf_sz != count - header_sz)
355 sz = header_sz + buf_sz;
357 sz = stm_data_write(data, master, channel, false, buf, count);
363 data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil);
368 static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output,
369 unsigned int chan, const char *buf, size_t count,
370 struct stm_source_data *source)
372 struct sys_t_output *op = output->pdrv_private;
373 unsigned int c = output->channel + chan;
374 unsigned int m = output->master;
375 u32 header = sys_t_header(source);
379 /* We require an existing policy node to proceed */
383 if (sys_t_need_clock_sync(op)) {
384 sz = sys_t_clock_sync(data, m, c);
390 header |= MIPI_SYST_OPT_LEN;
391 if (sys_t_need_ts(op))
392 header |= MIPI_SYST_OPT_TS;
395 * STP framing rules for SyS-T frames:
396 * * the first packet of the SyS-T frame is timestamped;
397 * * the last packet is a FLAG.
399 /* Message layout: HEADER / GUID / [LENGTH /][TIMESTAMP /] DATA */
401 sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED,
407 export_uuid(uuid, &op->node.uuid);
408 sz = stm_data_write(data, m, c, false, uuid, sizeof(op->node.uuid));
413 if (op->node.do_len) {
416 sz = data->packet(data, m, c, STP_PACKET_DATA, 0, 2,
423 if (header & MIPI_SYST_OPT_TS) {
424 u64 ts = ktime_get_real_ns();
426 sz = stm_data_write(data, m, c, false, &ts, sizeof(ts));
432 return sys_t_write_data(data, source, m, c, false, buf, count);
435 static const struct stm_protocol_driver sys_t_pdrv = {
436 .owner = THIS_MODULE,
438 .priv_sz = sizeof(struct sys_t_policy_node),
439 .write = sys_t_write,
440 .policy_attr = sys_t_policy_attrs,
441 .policy_node_init = sys_t_policy_node_init,
442 .output_open = sys_t_output_open,
443 .output_close = sys_t_output_close,
446 static int sys_t_stm_init(void)
448 return stm_register_protocol(&sys_t_pdrv);
451 static void sys_t_stm_exit(void)
453 stm_unregister_protocol(&sys_t_pdrv);
456 module_init(sys_t_stm_init);
457 module_exit(sys_t_stm_exit);
459 MODULE_LICENSE("GPL v2");
460 MODULE_DESCRIPTION("MIPI SyS-T STM framing protocol driver");