stm class: Add MIPI SyS-T protocol support
[linux-2.6-microblaze.git] / drivers / hwtracing / stm / p_sys-t.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * MIPI SyS-T framing protocol for STM devices.
4  * Copyright (c) 2018, Intel Corporation.
5  */
6
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>
13 #include "stm.h"
14
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,
22         MIPI_SYST_TYPE_CLOCK,
23 };
24
25 enum sys_t_message_severity {
26         MIPI_SYST_SEVERITY_MAX  = 0,
27         MIPI_SYST_SEVERITY_FATAL,
28         MIPI_SYST_SEVERITY_ERROR,
29         MIPI_SYST_SEVERITY_WARNING,
30         MIPI_SYST_SEVERITY_INFO,
31         MIPI_SYST_SEVERITY_USER1,
32         MIPI_SYST_SEVERITY_USER2,
33         MIPI_SYST_SEVERITY_DEBUG,
34 };
35
36 enum sys_t_message_build_subtype {
37         MIPI_SYST_BUILD_ID_COMPACT32 = 0,
38         MIPI_SYST_BUILD_ID_COMPACT64,
39         MIPI_SYST_BUILD_ID_LONG,
40 };
41
42 enum sys_t_message_clock_subtype {
43         MIPI_SYST_CLOCK_TRANSPORT_SYNC = 1,
44 };
45
46 enum sys_t_message_string_subtype {
47         MIPI_SYST_STRING_GENERIC        = 1,
48         MIPI_SYST_STRING_FUNCTIONENTER,
49         MIPI_SYST_STRING_FUNCTIONEXIT,
50         MIPI_SYST_STRING_INVALIDPARAM   = 5,
51         MIPI_SYST_STRING_ASSERT         = 7,
52         MIPI_SYST_STRING_PRINTF_32      = 11,
53         MIPI_SYST_STRING_PRINTF_64      = 12,
54 };
55
56 #define MIPI_SYST_TYPE(t)               ((u32)(MIPI_SYST_TYPE_ ## t))
57 #define MIPI_SYST_SEVERITY(s)           ((u32)(MIPI_SYST_SEVERITY_ ## s) << 4)
58 #define MIPI_SYST_OPT_LOC               BIT(8)
59 #define MIPI_SYST_OPT_LEN               BIT(9)
60 #define MIPI_SYST_OPT_CHK               BIT(10)
61 #define MIPI_SYST_OPT_TS                BIT(11)
62 #define MIPI_SYST_UNIT(u)               ((u32)(u) << 12)
63 #define MIPI_SYST_ORIGIN(o)             ((u32)(o) << 16)
64 #define MIPI_SYST_OPT_GUID              BIT(23)
65 #define MIPI_SYST_SUBTYPE(s)            ((u32)(MIPI_SYST_ ## s) << 24)
66 #define MIPI_SYST_UNITLARGE(u)          (MIPI_SYST_UNIT(u & 0xf) | \
67                                          MIPI_SYST_ORIGIN(u >> 4))
68 #define MIPI_SYST_TYPES(t, s)           (MIPI_SYST_TYPE(t) | \
69                                          MIPI_SYST_SUBTYPE(t ## _ ## s))
70
71 #define DATA_HEADER     (MIPI_SYST_TYPES(STRING, GENERIC)       | \
72                          MIPI_SYST_SEVERITY(INFO)               | \
73                          MIPI_SYST_OPT_GUID)
74
75 struct sys_t_policy_node {
76         uuid_t          uuid;
77         bool            do_len;
78         unsigned long   ts_interval;
79 };
80
81 struct sys_t_output {
82         struct sys_t_policy_node        node;
83         unsigned long   ts_jiffies;
84 };
85
86 static void sys_t_policy_node_init(void *priv)
87 {
88         struct sys_t_policy_node *pn = priv;
89
90         generate_random_uuid(pn->uuid.b);
91 }
92
93 static int sys_t_output_open(void *priv, struct stm_output *output)
94 {
95         struct sys_t_policy_node *pn = priv;
96         struct sys_t_output *opriv;
97
98         opriv = kzalloc(sizeof(*opriv), GFP_ATOMIC);
99         if (!opriv)
100                 return -ENOMEM;
101
102         memcpy(&opriv->node, pn, sizeof(opriv->node));
103         output->pdrv_private = opriv;
104
105         return 0;
106 }
107
108 static void sys_t_output_close(struct stm_output *output)
109 {
110         kfree(output->pdrv_private);
111 }
112
113 static ssize_t sys_t_policy_uuid_show(struct config_item *item,
114                                       char *page)
115 {
116         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
117
118         return sprintf(page, "%pU\n", &pn->uuid);
119 }
120
121 static ssize_t
122 sys_t_policy_uuid_store(struct config_item *item, const char *page,
123                         size_t count)
124 {
125         struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
126         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
127         int ret;
128
129         mutex_lock(mutexp);
130         ret = uuid_parse(page, &pn->uuid);
131         mutex_unlock(mutexp);
132
133         return ret < 0 ? ret : count;
134 }
135
136 CONFIGFS_ATTR(sys_t_policy_, uuid);
137
138 static ssize_t sys_t_policy_do_len_show(struct config_item *item,
139                                       char *page)
140 {
141         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
142
143         return sprintf(page, "%d\n", pn->do_len);
144 }
145
146 static ssize_t
147 sys_t_policy_do_len_store(struct config_item *item, const char *page,
148                         size_t count)
149 {
150         struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
151         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
152         int ret;
153
154         mutex_lock(mutexp);
155         ret = kstrtobool(page, &pn->do_len);
156         mutex_unlock(mutexp);
157
158         return ret ? ret : count;
159 }
160
161 CONFIGFS_ATTR(sys_t_policy_, do_len);
162
163 static ssize_t sys_t_policy_ts_interval_show(struct config_item *item,
164                                              char *page)
165 {
166         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
167
168         return sprintf(page, "%u\n", jiffies_to_msecs(pn->ts_interval));
169 }
170
171 static ssize_t
172 sys_t_policy_ts_interval_store(struct config_item *item, const char *page,
173                                size_t count)
174 {
175         struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
176         struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
177         unsigned int ms;
178         int ret;
179
180         mutex_lock(mutexp);
181         ret = kstrtouint(page, 10, &ms);
182         mutex_unlock(mutexp);
183
184         if (!ret) {
185                 pn->ts_interval = msecs_to_jiffies(ms);
186                 return count;
187         }
188
189         return ret;
190 }
191
192 CONFIGFS_ATTR(sys_t_policy_, ts_interval);
193
194 static struct configfs_attribute *sys_t_policy_attrs[] = {
195         &sys_t_policy_attr_uuid,
196         &sys_t_policy_attr_do_len,
197         &sys_t_policy_attr_ts_interval,
198         NULL,
199 };
200
201 static inline bool sys_t_need_ts(struct sys_t_output *op)
202 {
203         if (op->node.ts_interval &&
204             time_after(op->ts_jiffies + op->node.ts_interval, jiffies)) {
205                 op->ts_jiffies = jiffies;
206
207                 return true;
208         }
209
210         return false;
211 }
212
213 static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output,
214                            unsigned int chan, const char *buf, size_t count)
215 {
216         struct sys_t_output *op = output->pdrv_private;
217         unsigned int c = output->channel + chan;
218         unsigned int m = output->master;
219         const unsigned char nil = 0;
220         u32 header = DATA_HEADER;
221         ssize_t sz;
222
223         /* We require an existing policy node to proceed */
224         if (!op)
225                 return -EINVAL;
226
227         if (op->node.do_len)
228                 header |= MIPI_SYST_OPT_LEN;
229         if (sys_t_need_ts(op))
230                 header |= MIPI_SYST_OPT_TS;
231
232         /*
233          * STP framing rules for SyS-T frames:
234          *   * the first packet of the SyS-T frame is timestamped;
235          *   * the last packet is a FLAG.
236          */
237         /* Message layout: HEADER / GUID / [LENGTH /][TIMESTAMP /] DATA */
238         /* HEADER */
239         sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED,
240                           4, (u8 *)&header);
241         if (sz <= 0)
242                 return sz;
243
244         /* GUID */
245         sz = stm_data_write(data, m, c, false, op->node.uuid.b, UUID_SIZE);
246         if (sz <= 0)
247                 return sz;
248
249         /* [LENGTH] */
250         if (op->node.do_len) {
251                 u16 length = count;
252
253                 sz = data->packet(data, m, c, STP_PACKET_DATA, 0, 2,
254                                   (u8 *)&length);
255                 if (sz <= 0)
256                         return sz;
257         }
258
259         /* [TIMESTAMP] */
260         if (header & MIPI_SYST_OPT_TS) {
261                 u64 ts = ktime_get_real_ns();
262
263                 sz = stm_data_write(data, m, c, false, &ts, sizeof(ts));
264                 if (sz <= 0)
265                         return sz;
266         }
267
268         /* DATA */
269         sz = stm_data_write(data, m, c, false, buf, count);
270         if (sz > 0)
271                 data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil);
272
273         return sz;
274 }
275
276 static const struct stm_protocol_driver sys_t_pdrv = {
277         .owner                  = THIS_MODULE,
278         .name                   = "p_sys-t",
279         .priv_sz                = sizeof(struct sys_t_policy_node),
280         .write                  = sys_t_write,
281         .policy_attr            = sys_t_policy_attrs,
282         .policy_node_init       = sys_t_policy_node_init,
283         .output_open            = sys_t_output_open,
284         .output_close           = sys_t_output_close,
285 };
286
287 static int sys_t_stm_init(void)
288 {
289         return stm_register_protocol(&sys_t_pdrv);
290 }
291
292 static void sys_t_stm_exit(void)
293 {
294         stm_unregister_protocol(&sys_t_pdrv);
295 }
296
297 module_init(sys_t_stm_init);
298 module_exit(sys_t_stm_exit);
299
300 MODULE_LICENSE("GPL v2");
301 MODULE_DESCRIPTION("MIPI SyS-T STM framing protocol driver");
302 MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");