xref: /linux/drivers/hwtracing/stm/p_sys-t.c (revision 36ec807b627b4c0a0a382f0ae48eac7187d14b2b)
1d69d5e83SAlexander Shishkin // SPDX-License-Identifier: GPL-2.0
2d69d5e83SAlexander Shishkin /*
3d69d5e83SAlexander Shishkin  * MIPI SyS-T framing protocol for STM devices.
4d69d5e83SAlexander Shishkin  * Copyright (c) 2018, Intel Corporation.
5d69d5e83SAlexander Shishkin  */
6d69d5e83SAlexander Shishkin 
7d69d5e83SAlexander Shishkin #include <linux/configfs.h>
8d69d5e83SAlexander Shishkin #include <linux/module.h>
9d69d5e83SAlexander Shishkin #include <linux/device.h>
10d69d5e83SAlexander Shishkin #include <linux/slab.h>
11d69d5e83SAlexander Shishkin #include <linux/uuid.h>
12d69d5e83SAlexander Shishkin #include <linux/stm.h>
13d69d5e83SAlexander Shishkin #include "stm.h"
14d69d5e83SAlexander Shishkin 
15d69d5e83SAlexander Shishkin enum sys_t_message_type {
16d69d5e83SAlexander Shishkin 	MIPI_SYST_TYPE_BUILD	= 0,
17d69d5e83SAlexander Shishkin 	MIPI_SYST_TYPE_SHORT32,
18d69d5e83SAlexander Shishkin 	MIPI_SYST_TYPE_STRING,
19d69d5e83SAlexander Shishkin 	MIPI_SYST_TYPE_CATALOG,
20d69d5e83SAlexander Shishkin 	MIPI_SYST_TYPE_RAW	= 6,
21d69d5e83SAlexander Shishkin 	MIPI_SYST_TYPE_SHORT64,
22d69d5e83SAlexander Shishkin 	MIPI_SYST_TYPE_CLOCK,
23*3c720592SMikhail Lappo 	MIPI_SYST_TYPE_SBD,
24d69d5e83SAlexander Shishkin };
25d69d5e83SAlexander Shishkin 
26d69d5e83SAlexander Shishkin enum sys_t_message_severity {
27d69d5e83SAlexander Shishkin 	MIPI_SYST_SEVERITY_MAX	= 0,
28d69d5e83SAlexander Shishkin 	MIPI_SYST_SEVERITY_FATAL,
29d69d5e83SAlexander Shishkin 	MIPI_SYST_SEVERITY_ERROR,
30d69d5e83SAlexander Shishkin 	MIPI_SYST_SEVERITY_WARNING,
31d69d5e83SAlexander Shishkin 	MIPI_SYST_SEVERITY_INFO,
32d69d5e83SAlexander Shishkin 	MIPI_SYST_SEVERITY_USER1,
33d69d5e83SAlexander Shishkin 	MIPI_SYST_SEVERITY_USER2,
34d69d5e83SAlexander Shishkin 	MIPI_SYST_SEVERITY_DEBUG,
35d69d5e83SAlexander Shishkin };
36d69d5e83SAlexander Shishkin 
37d69d5e83SAlexander Shishkin enum sys_t_message_build_subtype {
38d69d5e83SAlexander Shishkin 	MIPI_SYST_BUILD_ID_COMPACT32 = 0,
39d69d5e83SAlexander Shishkin 	MIPI_SYST_BUILD_ID_COMPACT64,
40d69d5e83SAlexander Shishkin 	MIPI_SYST_BUILD_ID_LONG,
41d69d5e83SAlexander Shishkin };
42d69d5e83SAlexander Shishkin 
43d69d5e83SAlexander Shishkin enum sys_t_message_clock_subtype {
44d69d5e83SAlexander Shishkin 	MIPI_SYST_CLOCK_TRANSPORT_SYNC = 1,
45d69d5e83SAlexander Shishkin };
46d69d5e83SAlexander Shishkin 
47d69d5e83SAlexander Shishkin enum sys_t_message_string_subtype {
48d69d5e83SAlexander Shishkin 	MIPI_SYST_STRING_GENERIC	= 1,
49d69d5e83SAlexander Shishkin 	MIPI_SYST_STRING_FUNCTIONENTER,
50d69d5e83SAlexander Shishkin 	MIPI_SYST_STRING_FUNCTIONEXIT,
51d69d5e83SAlexander Shishkin 	MIPI_SYST_STRING_INVALIDPARAM	= 5,
52d69d5e83SAlexander Shishkin 	MIPI_SYST_STRING_ASSERT		= 7,
53d69d5e83SAlexander Shishkin 	MIPI_SYST_STRING_PRINTF_32	= 11,
54d69d5e83SAlexander Shishkin 	MIPI_SYST_STRING_PRINTF_64	= 12,
55d69d5e83SAlexander Shishkin };
56d69d5e83SAlexander Shishkin 
57*3c720592SMikhail Lappo /**
58*3c720592SMikhail Lappo  * enum sys_t_message_sbd_subtype - SyS-T SBD message subtypes
59*3c720592SMikhail Lappo  * @MIPI_SYST_SBD_ID32: SBD message with 32-bit message ID
60*3c720592SMikhail Lappo  * @MIPI_SYST_SBD_ID64: SBD message with 64-bit message ID
61*3c720592SMikhail Lappo  *
62*3c720592SMikhail Lappo  * Structured Binary Data messages can send information of arbitrary length,
63*3c720592SMikhail Lappo  * together with ID's that describe BLOB's content and layout.
64*3c720592SMikhail Lappo  */
65*3c720592SMikhail Lappo enum sys_t_message_sbd_subtype {
66*3c720592SMikhail Lappo 	MIPI_SYST_SBD_ID32 = 0,
67*3c720592SMikhail Lappo 	MIPI_SYST_SBD_ID64 = 1,
68*3c720592SMikhail Lappo };
69*3c720592SMikhail Lappo 
70d69d5e83SAlexander Shishkin #define MIPI_SYST_TYPE(t)		((u32)(MIPI_SYST_TYPE_ ## t))
71d69d5e83SAlexander Shishkin #define MIPI_SYST_SEVERITY(s)		((u32)(MIPI_SYST_SEVERITY_ ## s) << 4)
72d69d5e83SAlexander Shishkin #define MIPI_SYST_OPT_LOC		BIT(8)
73d69d5e83SAlexander Shishkin #define MIPI_SYST_OPT_LEN		BIT(9)
74d69d5e83SAlexander Shishkin #define MIPI_SYST_OPT_CHK		BIT(10)
75d69d5e83SAlexander Shishkin #define MIPI_SYST_OPT_TS		BIT(11)
76d69d5e83SAlexander Shishkin #define MIPI_SYST_UNIT(u)		((u32)(u) << 12)
77d69d5e83SAlexander Shishkin #define MIPI_SYST_ORIGIN(o)		((u32)(o) << 16)
78d69d5e83SAlexander Shishkin #define MIPI_SYST_OPT_GUID		BIT(23)
79d69d5e83SAlexander Shishkin #define MIPI_SYST_SUBTYPE(s)		((u32)(MIPI_SYST_ ## s) << 24)
80d69d5e83SAlexander Shishkin #define MIPI_SYST_UNITLARGE(u)		(MIPI_SYST_UNIT(u & 0xf) | \
81d69d5e83SAlexander Shishkin 					 MIPI_SYST_ORIGIN(u >> 4))
82d69d5e83SAlexander Shishkin #define MIPI_SYST_TYPES(t, s)		(MIPI_SYST_TYPE(t) | \
83d69d5e83SAlexander Shishkin 					 MIPI_SYST_SUBTYPE(t ## _ ## s))
84d69d5e83SAlexander Shishkin 
85d69d5e83SAlexander Shishkin #define DATA_HEADER	(MIPI_SYST_TYPES(STRING, GENERIC)	| \
86d69d5e83SAlexander Shishkin 			 MIPI_SYST_SEVERITY(INFO)		| \
87d69d5e83SAlexander Shishkin 			 MIPI_SYST_OPT_GUID)
88d69d5e83SAlexander Shishkin 
8939f10239SAlexander Shishkin #define CLOCK_SYNC_HEADER	(MIPI_SYST_TYPES(CLOCK, TRANSPORT_SYNC)	| \
9039f10239SAlexander Shishkin 				 MIPI_SYST_SEVERITY(MAX))
9139f10239SAlexander Shishkin 
92*3c720592SMikhail Lappo /*
93*3c720592SMikhail Lappo  * SyS-T and ftrace headers are compatible to an extent that ftrace event ID
94*3c720592SMikhail Lappo  * and args can be treated as SyS-T SBD message with 64-bit ID and arguments
95*3c720592SMikhail Lappo  * BLOB right behind the header without modification. Bits [16:63] coming
96*3c720592SMikhail Lappo  * together with message ID from ftrace aren't used by SBD and must be zeroed.
97*3c720592SMikhail Lappo  *
98*3c720592SMikhail Lappo  *         0       15  16   23  24     31  32   39  40  63
99*3c720592SMikhail Lappo  * ftrace: <event_id>  <flags>  <preempt>  <-pid->  <----> <args>
100*3c720592SMikhail Lappo  * SBD:    <------- msg_id ------------------------------> <BLOB>
101*3c720592SMikhail Lappo  */
102*3c720592SMikhail Lappo #define SBD_HEADER (MIPI_SYST_TYPES(SBD, ID64) | \
103*3c720592SMikhail Lappo 			 MIPI_SYST_SEVERITY(INFO)		| \
104*3c720592SMikhail Lappo 			 MIPI_SYST_OPT_GUID)
105*3c720592SMikhail Lappo 
106d69d5e83SAlexander Shishkin struct sys_t_policy_node {
107d69d5e83SAlexander Shishkin 	uuid_t		uuid;
108d69d5e83SAlexander Shishkin 	bool		do_len;
109d69d5e83SAlexander Shishkin 	unsigned long	ts_interval;
11039f10239SAlexander Shishkin 	unsigned long	clocksync_interval;
111d69d5e83SAlexander Shishkin };
112d69d5e83SAlexander Shishkin 
113d69d5e83SAlexander Shishkin struct sys_t_output {
114d69d5e83SAlexander Shishkin 	struct sys_t_policy_node	node;
115d69d5e83SAlexander Shishkin 	unsigned long	ts_jiffies;
11639f10239SAlexander Shishkin 	unsigned long	clocksync_jiffies;
117d69d5e83SAlexander Shishkin };
118d69d5e83SAlexander Shishkin 
119d69d5e83SAlexander Shishkin static void sys_t_policy_node_init(void *priv)
120d69d5e83SAlexander Shishkin {
121d69d5e83SAlexander Shishkin 	struct sys_t_policy_node *pn = priv;
122d69d5e83SAlexander Shishkin 
12341c9f7fbSAndy Shevchenko 	uuid_gen(&pn->uuid);
124d69d5e83SAlexander Shishkin }
125d69d5e83SAlexander Shishkin 
126d69d5e83SAlexander Shishkin static int sys_t_output_open(void *priv, struct stm_output *output)
127d69d5e83SAlexander Shishkin {
128d69d5e83SAlexander Shishkin 	struct sys_t_policy_node *pn = priv;
129d69d5e83SAlexander Shishkin 	struct sys_t_output *opriv;
130d69d5e83SAlexander Shishkin 
131d69d5e83SAlexander Shishkin 	opriv = kzalloc(sizeof(*opriv), GFP_ATOMIC);
132d69d5e83SAlexander Shishkin 	if (!opriv)
133d69d5e83SAlexander Shishkin 		return -ENOMEM;
134d69d5e83SAlexander Shishkin 
135d69d5e83SAlexander Shishkin 	memcpy(&opriv->node, pn, sizeof(opriv->node));
136d69d5e83SAlexander Shishkin 	output->pdrv_private = opriv;
137d69d5e83SAlexander Shishkin 
138d69d5e83SAlexander Shishkin 	return 0;
139d69d5e83SAlexander Shishkin }
140d69d5e83SAlexander Shishkin 
141d69d5e83SAlexander Shishkin static void sys_t_output_close(struct stm_output *output)
142d69d5e83SAlexander Shishkin {
143d69d5e83SAlexander Shishkin 	kfree(output->pdrv_private);
144d69d5e83SAlexander Shishkin }
145d69d5e83SAlexander Shishkin 
146d69d5e83SAlexander Shishkin static ssize_t sys_t_policy_uuid_show(struct config_item *item,
147d69d5e83SAlexander Shishkin 				      char *page)
148d69d5e83SAlexander Shishkin {
149d69d5e83SAlexander Shishkin 	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
150d69d5e83SAlexander Shishkin 
151d69d5e83SAlexander Shishkin 	return sprintf(page, "%pU\n", &pn->uuid);
152d69d5e83SAlexander Shishkin }
153d69d5e83SAlexander Shishkin 
154d69d5e83SAlexander Shishkin static ssize_t
155d69d5e83SAlexander Shishkin sys_t_policy_uuid_store(struct config_item *item, const char *page,
156d69d5e83SAlexander Shishkin 			size_t count)
157d69d5e83SAlexander Shishkin {
158d69d5e83SAlexander Shishkin 	struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
159d69d5e83SAlexander Shishkin 	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
160d69d5e83SAlexander Shishkin 	int ret;
161d69d5e83SAlexander Shishkin 
162d69d5e83SAlexander Shishkin 	mutex_lock(mutexp);
163d69d5e83SAlexander Shishkin 	ret = uuid_parse(page, &pn->uuid);
164d69d5e83SAlexander Shishkin 	mutex_unlock(mutexp);
165d69d5e83SAlexander Shishkin 
166d69d5e83SAlexander Shishkin 	return ret < 0 ? ret : count;
167d69d5e83SAlexander Shishkin }
168d69d5e83SAlexander Shishkin 
169d69d5e83SAlexander Shishkin CONFIGFS_ATTR(sys_t_policy_, uuid);
170d69d5e83SAlexander Shishkin 
171d69d5e83SAlexander Shishkin static ssize_t sys_t_policy_do_len_show(struct config_item *item,
172d69d5e83SAlexander Shishkin 				      char *page)
173d69d5e83SAlexander Shishkin {
174d69d5e83SAlexander Shishkin 	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
175d69d5e83SAlexander Shishkin 
176d69d5e83SAlexander Shishkin 	return sprintf(page, "%d\n", pn->do_len);
177d69d5e83SAlexander Shishkin }
178d69d5e83SAlexander Shishkin 
179d69d5e83SAlexander Shishkin static ssize_t
180d69d5e83SAlexander Shishkin sys_t_policy_do_len_store(struct config_item *item, const char *page,
181d69d5e83SAlexander Shishkin 			size_t count)
182d69d5e83SAlexander Shishkin {
183d69d5e83SAlexander Shishkin 	struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
184d69d5e83SAlexander Shishkin 	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
185d69d5e83SAlexander Shishkin 	int ret;
186d69d5e83SAlexander Shishkin 
187d69d5e83SAlexander Shishkin 	mutex_lock(mutexp);
188d69d5e83SAlexander Shishkin 	ret = kstrtobool(page, &pn->do_len);
189d69d5e83SAlexander Shishkin 	mutex_unlock(mutexp);
190d69d5e83SAlexander Shishkin 
191d69d5e83SAlexander Shishkin 	return ret ? ret : count;
192d69d5e83SAlexander Shishkin }
193d69d5e83SAlexander Shishkin 
194d69d5e83SAlexander Shishkin CONFIGFS_ATTR(sys_t_policy_, do_len);
195d69d5e83SAlexander Shishkin 
196d69d5e83SAlexander Shishkin static ssize_t sys_t_policy_ts_interval_show(struct config_item *item,
197d69d5e83SAlexander Shishkin 					     char *page)
198d69d5e83SAlexander Shishkin {
199d69d5e83SAlexander Shishkin 	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
200d69d5e83SAlexander Shishkin 
201d69d5e83SAlexander Shishkin 	return sprintf(page, "%u\n", jiffies_to_msecs(pn->ts_interval));
202d69d5e83SAlexander Shishkin }
203d69d5e83SAlexander Shishkin 
204d69d5e83SAlexander Shishkin static ssize_t
205d69d5e83SAlexander Shishkin sys_t_policy_ts_interval_store(struct config_item *item, const char *page,
206d69d5e83SAlexander Shishkin 			       size_t count)
207d69d5e83SAlexander Shishkin {
208d69d5e83SAlexander Shishkin 	struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
209d69d5e83SAlexander Shishkin 	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
210d69d5e83SAlexander Shishkin 	unsigned int ms;
211d69d5e83SAlexander Shishkin 	int ret;
212d69d5e83SAlexander Shishkin 
213d69d5e83SAlexander Shishkin 	mutex_lock(mutexp);
214d69d5e83SAlexander Shishkin 	ret = kstrtouint(page, 10, &ms);
215d69d5e83SAlexander Shishkin 	mutex_unlock(mutexp);
216d69d5e83SAlexander Shishkin 
217d69d5e83SAlexander Shishkin 	if (!ret) {
218d69d5e83SAlexander Shishkin 		pn->ts_interval = msecs_to_jiffies(ms);
219d69d5e83SAlexander Shishkin 		return count;
220d69d5e83SAlexander Shishkin 	}
221d69d5e83SAlexander Shishkin 
222d69d5e83SAlexander Shishkin 	return ret;
223d69d5e83SAlexander Shishkin }
224d69d5e83SAlexander Shishkin 
225d69d5e83SAlexander Shishkin CONFIGFS_ATTR(sys_t_policy_, ts_interval);
226d69d5e83SAlexander Shishkin 
22739f10239SAlexander Shishkin static ssize_t sys_t_policy_clocksync_interval_show(struct config_item *item,
22839f10239SAlexander Shishkin 						    char *page)
22939f10239SAlexander Shishkin {
23039f10239SAlexander Shishkin 	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
23139f10239SAlexander Shishkin 
23239f10239SAlexander Shishkin 	return sprintf(page, "%u\n", jiffies_to_msecs(pn->clocksync_interval));
23339f10239SAlexander Shishkin }
23439f10239SAlexander Shishkin 
23539f10239SAlexander Shishkin static ssize_t
23639f10239SAlexander Shishkin sys_t_policy_clocksync_interval_store(struct config_item *item,
23739f10239SAlexander Shishkin 				      const char *page, size_t count)
23839f10239SAlexander Shishkin {
23939f10239SAlexander Shishkin 	struct mutex *mutexp = &item->ci_group->cg_subsys->su_mutex;
24039f10239SAlexander Shishkin 	struct sys_t_policy_node *pn = to_pdrv_policy_node(item);
24139f10239SAlexander Shishkin 	unsigned int ms;
24239f10239SAlexander Shishkin 	int ret;
24339f10239SAlexander Shishkin 
24439f10239SAlexander Shishkin 	mutex_lock(mutexp);
24539f10239SAlexander Shishkin 	ret = kstrtouint(page, 10, &ms);
24639f10239SAlexander Shishkin 	mutex_unlock(mutexp);
24739f10239SAlexander Shishkin 
24839f10239SAlexander Shishkin 	if (!ret) {
24939f10239SAlexander Shishkin 		pn->clocksync_interval = msecs_to_jiffies(ms);
25039f10239SAlexander Shishkin 		return count;
25139f10239SAlexander Shishkin 	}
25239f10239SAlexander Shishkin 
25339f10239SAlexander Shishkin 	return ret;
25439f10239SAlexander Shishkin }
25539f10239SAlexander Shishkin 
25639f10239SAlexander Shishkin CONFIGFS_ATTR(sys_t_policy_, clocksync_interval);
25739f10239SAlexander Shishkin 
258d69d5e83SAlexander Shishkin static struct configfs_attribute *sys_t_policy_attrs[] = {
259d69d5e83SAlexander Shishkin 	&sys_t_policy_attr_uuid,
260d69d5e83SAlexander Shishkin 	&sys_t_policy_attr_do_len,
261d69d5e83SAlexander Shishkin 	&sys_t_policy_attr_ts_interval,
26239f10239SAlexander Shishkin 	&sys_t_policy_attr_clocksync_interval,
263d69d5e83SAlexander Shishkin 	NULL,
264d69d5e83SAlexander Shishkin };
265d69d5e83SAlexander Shishkin 
266d69d5e83SAlexander Shishkin static inline bool sys_t_need_ts(struct sys_t_output *op)
267d69d5e83SAlexander Shishkin {
268d69d5e83SAlexander Shishkin 	if (op->node.ts_interval &&
269283f87c0SAlexander Shishkin 	    time_after(jiffies, op->ts_jiffies + op->node.ts_interval)) {
270d69d5e83SAlexander Shishkin 		op->ts_jiffies = jiffies;
271d69d5e83SAlexander Shishkin 
272d69d5e83SAlexander Shishkin 		return true;
273d69d5e83SAlexander Shishkin 	}
274d69d5e83SAlexander Shishkin 
275d69d5e83SAlexander Shishkin 	return false;
276d69d5e83SAlexander Shishkin }
277d69d5e83SAlexander Shishkin 
27839f10239SAlexander Shishkin static bool sys_t_need_clock_sync(struct sys_t_output *op)
27939f10239SAlexander Shishkin {
28039f10239SAlexander Shishkin 	if (op->node.clocksync_interval &&
281283f87c0SAlexander Shishkin 	    time_after(jiffies,
282283f87c0SAlexander Shishkin 		       op->clocksync_jiffies + op->node.clocksync_interval)) {
28339f10239SAlexander Shishkin 		op->clocksync_jiffies = jiffies;
28439f10239SAlexander Shishkin 
28539f10239SAlexander Shishkin 		return true;
28639f10239SAlexander Shishkin 	}
28739f10239SAlexander Shishkin 
28839f10239SAlexander Shishkin 	return false;
28939f10239SAlexander Shishkin }
29039f10239SAlexander Shishkin 
29139f10239SAlexander Shishkin static ssize_t
29239f10239SAlexander Shishkin sys_t_clock_sync(struct stm_data *data, unsigned int m, unsigned int c)
29339f10239SAlexander Shishkin {
29439f10239SAlexander Shishkin 	u32 header = CLOCK_SYNC_HEADER;
29539f10239SAlexander Shishkin 	const unsigned char nil = 0;
29639f10239SAlexander Shishkin 	u64 payload[2]; /* Clock value and frequency */
29739f10239SAlexander Shishkin 	ssize_t sz;
29839f10239SAlexander Shishkin 
29939f10239SAlexander Shishkin 	sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED,
30039f10239SAlexander Shishkin 			  4, (u8 *)&header);
30139f10239SAlexander Shishkin 	if (sz <= 0)
30239f10239SAlexander Shishkin 		return sz;
30339f10239SAlexander Shishkin 
30439f10239SAlexander Shishkin 	payload[0] = ktime_get_real_ns();
30539f10239SAlexander Shishkin 	payload[1] = NSEC_PER_SEC;
30639f10239SAlexander Shishkin 	sz = stm_data_write(data, m, c, false, &payload, sizeof(payload));
30739f10239SAlexander Shishkin 	if (sz <= 0)
30839f10239SAlexander Shishkin 		return sz;
30939f10239SAlexander Shishkin 
31039f10239SAlexander Shishkin 	data->packet(data, m, c, STP_PACKET_FLAG, 0, 0, &nil);
31139f10239SAlexander Shishkin 
31239f10239SAlexander Shishkin 	return sizeof(header) + sizeof(payload);
31339f10239SAlexander Shishkin }
31439f10239SAlexander Shishkin 
315*3c720592SMikhail Lappo static inline u32 sys_t_header(struct stm_source_data *source)
316*3c720592SMikhail Lappo {
317*3c720592SMikhail Lappo 	if (source && source->type == STM_FTRACE)
318*3c720592SMikhail Lappo 		return SBD_HEADER;
319*3c720592SMikhail Lappo 	return DATA_HEADER;
320*3c720592SMikhail Lappo }
321*3c720592SMikhail Lappo 
322*3c720592SMikhail Lappo static ssize_t sys_t_write_data(struct stm_data *data,
323*3c720592SMikhail Lappo 				struct stm_source_data *source,
324*3c720592SMikhail Lappo 				unsigned int master, unsigned int channel,
325*3c720592SMikhail Lappo 				bool ts_first, const void *buf, size_t count)
326*3c720592SMikhail Lappo {
327*3c720592SMikhail Lappo 	ssize_t sz;
328*3c720592SMikhail Lappo 	const unsigned char nil = 0;
329*3c720592SMikhail Lappo 
330*3c720592SMikhail Lappo 	/*
331*3c720592SMikhail Lappo 	 * Ftrace is zero-copy compatible with SyS-T SBD, but requires
332*3c720592SMikhail Lappo 	 * special handling of first 64 bits. Trim and send them separately
333*3c720592SMikhail Lappo 	 * to avoid damage on original ftrace buffer.
334*3c720592SMikhail Lappo 	 */
335*3c720592SMikhail Lappo 	if (source && source->type == STM_FTRACE) {
336*3c720592SMikhail Lappo 		u64 compat_ftrace_header;
337*3c720592SMikhail Lappo 		ssize_t header_sz;
338*3c720592SMikhail Lappo 		ssize_t buf_sz;
339*3c720592SMikhail Lappo 
340*3c720592SMikhail Lappo 		if (count < sizeof(compat_ftrace_header))
341*3c720592SMikhail Lappo 			return -EINVAL;
342*3c720592SMikhail Lappo 
343*3c720592SMikhail Lappo 		/* SBD only makes use of low 16 bits (event ID) from ftrace event */
344*3c720592SMikhail Lappo 		compat_ftrace_header = *(u64 *)buf & 0xffff;
345*3c720592SMikhail Lappo 		header_sz = stm_data_write(data, master, channel, false,
346*3c720592SMikhail Lappo 					   &compat_ftrace_header,
347*3c720592SMikhail Lappo 					   sizeof(compat_ftrace_header));
348*3c720592SMikhail Lappo 		if (header_sz != sizeof(compat_ftrace_header))
349*3c720592SMikhail Lappo 			return header_sz;
350*3c720592SMikhail Lappo 
351*3c720592SMikhail Lappo 		buf_sz = stm_data_write(data, master, channel, false,
352*3c720592SMikhail Lappo 					buf + header_sz, count - header_sz);
353*3c720592SMikhail Lappo 		if (buf_sz != count - header_sz)
354*3c720592SMikhail Lappo 			return buf_sz;
355*3c720592SMikhail Lappo 		sz = header_sz + buf_sz;
356*3c720592SMikhail Lappo 	} else {
357*3c720592SMikhail Lappo 		sz = stm_data_write(data, master, channel, false, buf, count);
358*3c720592SMikhail Lappo 	}
359*3c720592SMikhail Lappo 
360*3c720592SMikhail Lappo 	if (sz <= 0)
361*3c720592SMikhail Lappo 		return sz;
362*3c720592SMikhail Lappo 
363*3c720592SMikhail Lappo 	data->packet(data, master, channel, STP_PACKET_FLAG, 0, 0, &nil);
364*3c720592SMikhail Lappo 
365*3c720592SMikhail Lappo 	return sz;
366*3c720592SMikhail Lappo }
367*3c720592SMikhail Lappo 
368d69d5e83SAlexander Shishkin static ssize_t sys_t_write(struct stm_data *data, struct stm_output *output,
369ee27f44eSMikhail Lappo 			   unsigned int chan, const char *buf, size_t count,
370ee27f44eSMikhail Lappo 			   struct stm_source_data *source)
371d69d5e83SAlexander Shishkin {
372d69d5e83SAlexander Shishkin 	struct sys_t_output *op = output->pdrv_private;
373d69d5e83SAlexander Shishkin 	unsigned int c = output->channel + chan;
374d69d5e83SAlexander Shishkin 	unsigned int m = output->master;
375*3c720592SMikhail Lappo 	u32 header = sys_t_header(source);
37641c9f7fbSAndy Shevchenko 	u8 uuid[UUID_SIZE];
377d69d5e83SAlexander Shishkin 	ssize_t sz;
378d69d5e83SAlexander Shishkin 
379d69d5e83SAlexander Shishkin 	/* We require an existing policy node to proceed */
380d69d5e83SAlexander Shishkin 	if (!op)
381d69d5e83SAlexander Shishkin 		return -EINVAL;
382d69d5e83SAlexander Shishkin 
38339f10239SAlexander Shishkin 	if (sys_t_need_clock_sync(op)) {
38439f10239SAlexander Shishkin 		sz = sys_t_clock_sync(data, m, c);
38539f10239SAlexander Shishkin 		if (sz <= 0)
38639f10239SAlexander Shishkin 			return sz;
38739f10239SAlexander Shishkin 	}
38839f10239SAlexander Shishkin 
389d69d5e83SAlexander Shishkin 	if (op->node.do_len)
390d69d5e83SAlexander Shishkin 		header |= MIPI_SYST_OPT_LEN;
391d69d5e83SAlexander Shishkin 	if (sys_t_need_ts(op))
392d69d5e83SAlexander Shishkin 		header |= MIPI_SYST_OPT_TS;
393d69d5e83SAlexander Shishkin 
394d69d5e83SAlexander Shishkin 	/*
395d69d5e83SAlexander Shishkin 	 * STP framing rules for SyS-T frames:
396d69d5e83SAlexander Shishkin 	 *   * the first packet of the SyS-T frame is timestamped;
397d69d5e83SAlexander Shishkin 	 *   * the last packet is a FLAG.
398d69d5e83SAlexander Shishkin 	 */
399d69d5e83SAlexander Shishkin 	/* Message layout: HEADER / GUID / [LENGTH /][TIMESTAMP /] DATA */
400d69d5e83SAlexander Shishkin 	/* HEADER */
401d69d5e83SAlexander Shishkin 	sz = data->packet(data, m, c, STP_PACKET_DATA, STP_PACKET_TIMESTAMPED,
402d69d5e83SAlexander Shishkin 			  4, (u8 *)&header);
403d69d5e83SAlexander Shishkin 	if (sz <= 0)
404d69d5e83SAlexander Shishkin 		return sz;
405d69d5e83SAlexander Shishkin 
406d69d5e83SAlexander Shishkin 	/* GUID */
40741c9f7fbSAndy Shevchenko 	export_uuid(uuid, &op->node.uuid);
40841c9f7fbSAndy Shevchenko 	sz = stm_data_write(data, m, c, false, uuid, sizeof(op->node.uuid));
409d69d5e83SAlexander Shishkin 	if (sz <= 0)
410d69d5e83SAlexander Shishkin 		return sz;
411d69d5e83SAlexander Shishkin 
412d69d5e83SAlexander Shishkin 	/* [LENGTH] */
413d69d5e83SAlexander Shishkin 	if (op->node.do_len) {
414d69d5e83SAlexander Shishkin 		u16 length = count;
415d69d5e83SAlexander Shishkin 
416d69d5e83SAlexander Shishkin 		sz = data->packet(data, m, c, STP_PACKET_DATA, 0, 2,
417d69d5e83SAlexander Shishkin 				  (u8 *)&length);
418d69d5e83SAlexander Shishkin 		if (sz <= 0)
419d69d5e83SAlexander Shishkin 			return sz;
420d69d5e83SAlexander Shishkin 	}
421d69d5e83SAlexander Shishkin 
422d69d5e83SAlexander Shishkin 	/* [TIMESTAMP] */
423d69d5e83SAlexander Shishkin 	if (header & MIPI_SYST_OPT_TS) {
424d69d5e83SAlexander Shishkin 		u64 ts = ktime_get_real_ns();
425d69d5e83SAlexander Shishkin 
426d69d5e83SAlexander Shishkin 		sz = stm_data_write(data, m, c, false, &ts, sizeof(ts));
427d69d5e83SAlexander Shishkin 		if (sz <= 0)
428d69d5e83SAlexander Shishkin 			return sz;
429d69d5e83SAlexander Shishkin 	}
430d69d5e83SAlexander Shishkin 
431d69d5e83SAlexander Shishkin 	/* DATA */
432*3c720592SMikhail Lappo 	return sys_t_write_data(data, source, m, c, false, buf, count);
433d69d5e83SAlexander Shishkin }
434d69d5e83SAlexander Shishkin 
435d69d5e83SAlexander Shishkin static const struct stm_protocol_driver sys_t_pdrv = {
436d69d5e83SAlexander Shishkin 	.owner			= THIS_MODULE,
437d69d5e83SAlexander Shishkin 	.name			= "p_sys-t",
438d69d5e83SAlexander Shishkin 	.priv_sz		= sizeof(struct sys_t_policy_node),
439d69d5e83SAlexander Shishkin 	.write			= sys_t_write,
440d69d5e83SAlexander Shishkin 	.policy_attr		= sys_t_policy_attrs,
441d69d5e83SAlexander Shishkin 	.policy_node_init	= sys_t_policy_node_init,
442d69d5e83SAlexander Shishkin 	.output_open		= sys_t_output_open,
443d69d5e83SAlexander Shishkin 	.output_close		= sys_t_output_close,
444d69d5e83SAlexander Shishkin };
445d69d5e83SAlexander Shishkin 
446d69d5e83SAlexander Shishkin static int sys_t_stm_init(void)
447d69d5e83SAlexander Shishkin {
448d69d5e83SAlexander Shishkin 	return stm_register_protocol(&sys_t_pdrv);
449d69d5e83SAlexander Shishkin }
450d69d5e83SAlexander Shishkin 
451d69d5e83SAlexander Shishkin static void sys_t_stm_exit(void)
452d69d5e83SAlexander Shishkin {
453d69d5e83SAlexander Shishkin 	stm_unregister_protocol(&sys_t_pdrv);
454d69d5e83SAlexander Shishkin }
455d69d5e83SAlexander Shishkin 
456d69d5e83SAlexander Shishkin module_init(sys_t_stm_init);
457d69d5e83SAlexander Shishkin module_exit(sys_t_stm_exit);
458d69d5e83SAlexander Shishkin 
459d69d5e83SAlexander Shishkin MODULE_LICENSE("GPL v2");
460d69d5e83SAlexander Shishkin MODULE_DESCRIPTION("MIPI SyS-T STM framing protocol driver");
461d69d5e83SAlexander Shishkin MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
462