xref: /openbmc/linux/drivers/hwtracing/stm/p_sys-t.c (revision 8be98d2f2a0a262f8bf8a0bc1fdf522b3c7aab17)
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  #define CLOCK_SYNC_HEADER	(MIPI_SYST_TYPES(CLOCK, TRANSPORT_SYNC)	| \
76  				 MIPI_SYST_SEVERITY(MAX))
77  
78  struct sys_t_policy_node {
79  	uuid_t		uuid;
80  	bool		do_len;
81  	unsigned long	ts_interval;
82  	unsigned long	clocksync_interval;
83  };
84  
85  struct sys_t_output {
86  	struct sys_t_policy_node	node;
87  	unsigned long	ts_jiffies;
88  	unsigned long	clocksync_jiffies;
89  };
90  
sys_t_policy_node_init(void * priv)91  static void sys_t_policy_node_init(void *priv)
92  {
93  	struct sys_t_policy_node *pn = priv;
94  
95  	uuid_gen(&pn->uuid);
96  }
97  
sys_t_output_open(void * priv,struct stm_output * output)98  static int sys_t_output_open(void *priv, struct stm_output *output)
99  {
100  	struct sys_t_policy_node *pn = priv;
101  	struct sys_t_output *opriv;
102  
103  	opriv = kzalloc(sizeof(*opriv), GFP_ATOMIC);
104  	if (!opriv)
105  		return -ENOMEM;
106  
107  	memcpy(&opriv->node, pn, sizeof(opriv->node));
108  	output->pdrv_private = opriv;
109  
110  	return 0;
111  }
112  
sys_t_output_close(struct stm_output * output)113  static void sys_t_output_close(struct stm_output *output)
114  {
115  	kfree(output->pdrv_private);
116  }
117  
sys_t_policy_uuid_show(struct config_item * item,char * page)118  static ssize_t sys_t_policy_uuid_show(struct config_item *item,
119  				      char *page)
120  {
121  	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
122  
123  	return sprintf(page, "%pU\n", &pn->uuid);
124  }
125  
126  static ssize_t
sys_t_policy_uuid_store(struct config_item * item,const char * page,size_t count)127  sys_t_policy_uuid_store(struct config_item *item, const char *page,
128  			size_t count)
129  {
130  	struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
131  	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
132  	int ret;
133  
134  	mutex_lock(mutexp);
135  	ret = uuid_parse(page, &pn->uuid);
136  	mutex_unlock(mutexp);
137  
138  	return ret < 0 ? ret : count;
139  }
140  
141  CONFIGFS_ATTR(sys_t_policy_, uuid);
142  
sys_t_policy_do_len_show(struct config_item * item,char * page)143  static ssize_t sys_t_policy_do_len_show(struct config_item *item,
144  				      char *page)
145  {
146  	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
147  
148  	return sprintf(page, "%d\n", pn->do_len);
149  }
150  
151  static ssize_t
sys_t_policy_do_len_store(struct config_item * item,const char * page,size_t count)152  sys_t_policy_do_len_store(struct config_item *item, const char *page,
153  			size_t count)
154  {
155  	struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
156  	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
157  	int ret;
158  
159  	mutex_lock(mutexp);
160  	ret = kstrtobool(page, &pn->do_len);
161  	mutex_unlock(mutexp);
162  
163  	return ret ? ret : count;
164  }
165  
166  CONFIGFS_ATTR(sys_t_policy_, do_len);
167  
sys_t_policy_ts_interval_show(struct config_item * item,char * page)168  static ssize_t sys_t_policy_ts_interval_show(struct config_item *item,
169  					     char *page)
170  {
171  	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
172  
173  	return sprintf(page, "%u\n", jiffies_to_msecs(pn->ts_interval));
174  }
175  
176  static ssize_t
sys_t_policy_ts_interval_store(struct config_item * item,const char * page,size_t count)177  sys_t_policy_ts_interval_store(struct config_item *item, const char *page,
178  			       size_t count)
179  {
180  	struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
181  	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
182  	unsigned int ms;
183  	int ret;
184  
185  	mutex_lock(mutexp);
186  	ret = kstrtouint(page, 10, &ms);
187  	mutex_unlock(mutexp);
188  
189  	if (!ret) {
190  		pn->ts_interval = msecs_to_jiffies(ms);
191  		return count;
192  	}
193  
194  	return ret;
195  }
196  
197  CONFIGFS_ATTR(sys_t_policy_, ts_interval);
198  
sys_t_policy_clocksync_interval_show(struct config_item * item,char * page)199  static ssize_t sys_t_policy_clocksync_interval_show(struct config_item *item,
200  						    char *page)
201  {
202  	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
203  
204  	return sprintf(page, "%u\n", jiffies_to_msecs(pn->clocksync_interval));
205  }
206  
207  static ssize_t
sys_t_policy_clocksync_interval_store(struct config_item * item,const char * page,size_t count)208  sys_t_policy_clocksync_interval_store(struct config_item *item,
209  				      const char *page, size_t count)
210  {
211  	struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
212  	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
213  	unsigned int ms;
214  	int ret;
215  
216  	mutex_lock(mutexp);
217  	ret = kstrtouint(page, 10, &ms);
218  	mutex_unlock(mutexp);
219  
220  	if (!ret) {
221  		pn->clocksync_interval = msecs_to_jiffies(ms);
222  		return count;
223  	}
224  
225  	return ret;
226  }
227  
228  CONFIGFS_ATTR(sys_t_policy_, clocksync_interval);
229  
230  static struct configfs_attribute *sys_t_policy_attrs[] = {
231  	&sys_t_policy_attr_uuid,
232  	&sys_t_policy_attr_do_len,
233  	&sys_t_policy_attr_ts_interval,
234  	&sys_t_policy_attr_clocksync_interval,
235  	NULL,
236  };
237  
sys_t_need_ts(struct sys_t_output * op)238  static inline bool sys_t_need_ts(struct sys_t_output *op)
239  {
240  	if (op->node.ts_interval &&
241  	    time_after(jiffies, op->ts_jiffies + op->node.ts_interval)) {
242  		op->ts_jiffies = jiffies;
243  
244  		return true;
245  	}
246  
247  	return false;
248  }
249  
sys_t_need_clock_sync(struct sys_t_output * op)250  static bool sys_t_need_clock_sync(struct sys_t_output *op)
251  {
252  	if (op->node.clocksync_interval &&
253  	    time_after(jiffies,
254  		       op->clocksync_jiffies + op->node.clocksync_interval)) {
255  		op->clocksync_jiffies = jiffies;
256  
257  		return true;
258  	}
259  
260  	return false;
261  }
262  
263  static ssize_t
sys_t_clock_sync(struct stm_data * data,unsigned int m,unsigned int c)264  sys_t_clock_sync(struct stm_data *data, unsigned int m, unsigned int c)
265  {
266  	u32 header = CLOCK_SYNC_HEADER;
267  	const unsigned char nil = 0;
268  	u64 payload[2]; /* Clock value and frequency */
269  	ssize_t sz;
270  
271  	sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED,
272  			  4, (u8 *)&header);
273  	if (sz <= 0)
274  		return sz;
275  
276  	payload[0] = ktime_get_real_ns();
277  	payload[1] = NSEC_PER_SEC;
278  	sz = stm_data_write(data, m, c, false, &payload, sizeof(payload));
279  	if (sz <= 0)
280  		return sz;
281  
282  	data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil);
283  
284  	return sizeof(header) + sizeof(payload);
285  }
286  
sys_t_write(struct stm_data * data,struct stm_output * output,unsigned int chan,const char * buf,size_t count)287  static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output,
288  			   unsigned int chan, const char *buf, size_t count)
289  {
290  	struct sys_t_output *op = output->pdrv_private;
291  	unsigned int c = output->channel + chan;
292  	unsigned int m = output->master;
293  	const unsigned char nil = 0;
294  	u32 header = DATA_HEADER;
295  	u8 uuid[UUID_SIZE];
296  	ssize_t sz;
297  
298  	/* We require an existing policy node to proceed */
299  	if (!op)
300  		return -EINVAL;
301  
302  	if (sys_t_need_clock_sync(op)) {
303  		sz = sys_t_clock_sync(data, m, c);
304  		if (sz <= 0)
305  			return sz;
306  	}
307  
308  	if (op->node.do_len)
309  		header |= MIPI_SYST_OPT_LEN;
310  	if (sys_t_need_ts(op))
311  		header |= MIPI_SYST_OPT_TS;
312  
313  	/*
314  	 * STP framing rules for SyS-T frames:
315  	 *   * the first packet of the SyS-T frame is timestamped;
316  	 *   * the last packet is a FLAG.
317  	 */
318  	/* Message layout: HEADER / GUID / [LENGTH /][TIMESTAMP /] DATA */
319  	/* HEADER */
320  	sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED,
321  			  4, (u8 *)&header);
322  	if (sz <= 0)
323  		return sz;
324  
325  	/* GUID */
326  	export_uuid(uuid, &op->node.uuid);
327  	sz = stm_data_write(data, m, c, false, uuid, sizeof(op->node.uuid));
328  	if (sz <= 0)
329  		return sz;
330  
331  	/* [LENGTH] */
332  	if (op->node.do_len) {
333  		u16 length = count;
334  
335  		sz = data->packet(data, m, c, STP_PACKET_DATA, 0, 2,
336  				  (u8 *)&length);
337  		if (sz <= 0)
338  			return sz;
339  	}
340  
341  	/* [TIMESTAMP] */
342  	if (header & MIPI_SYST_OPT_TS) {
343  		u64 ts = ktime_get_real_ns();
344  
345  		sz = stm_data_write(data, m, c, false, &ts, sizeof(ts));
346  		if (sz <= 0)
347  			return sz;
348  	}
349  
350  	/* DATA */
351  	sz = stm_data_write(data, m, c, false, buf, count);
352  	if (sz > 0)
353  		data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil);
354  
355  	return sz;
356  }
357  
358  static const struct stm_protocol_driver sys_t_pdrv = {
359  	.owner			= THIS_MODULE,
360  	.name			= "p_sys-t",
361  	.priv_sz		= sizeof(struct sys_t_policy_node),
362  	.write			= sys_t_write,
363  	.policy_attr		= sys_t_policy_attrs,
364  	.policy_node_init	= sys_t_policy_node_init,
365  	.output_open		= sys_t_output_open,
366  	.output_close		= sys_t_output_close,
367  };
368  
sys_t_stm_init(void)369  static int sys_t_stm_init(void)
370  {
371  	return stm_register_protocol(&sys_t_pdrv);
372  }
373  
sys_t_stm_exit(void)374  static void sys_t_stm_exit(void)
375  {
376  	stm_unregister_protocol(&sys_t_pdrv);
377  }
378  
379  module_init(sys_t_stm_init);
380  module_exit(sys_t_stm_exit);
381  
382  MODULE_LICENSE("GPL v2");
383  MODULE_DESCRIPTION("MIPI SyS-T STM framing protocol driver");
384  MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
385