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