Training courses

Kernel and Embedded Linux

Bootlin training courses

Embedded Linux, kernel,
Yocto Project, Buildroot, real-time,
graphics, boot time, debugging...

Bootlin logo

Elixir Cross Referencer

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
/* SPDX-License-Identifier: GPL-2.0 */

#ifndef __DRIVER_USB_TYPEC_UCSI_H
#define __DRIVER_USB_TYPEC_UCSI_H

#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/types.h>
#include <linux/usb/typec.h>

/* -------------------------------------------------------------------------- */

/* Command Status and Connector Change Indication (CCI) data structure */
struct ucsi_cci {
	u8:1; /* reserved */
	u8 connector_change:7;
	u8 data_length;
	u16:9; /* reserved */
	u16 not_supported:1;
	u16 cancel_complete:1;
	u16 reset_complete:1;
	u16 busy:1;
	u16 ack_complete:1;
	u16 error:1;
	u16 cmd_complete:1;
} __packed;

/* Default fields in CONTROL data structure */
struct ucsi_command {
	u8 cmd;
	u8 length;
	u64 data:48;
} __packed;

/* ACK Command structure */
struct ucsi_ack_cmd {
	u8 cmd;
	u8 length;
	u8 cci_ack:1;
	u8 cmd_ack:1;
	u8:6; /* reserved */
} __packed;

/* Connector Reset Command structure */
struct ucsi_con_rst {
	u8 cmd;
	u8 length;
	u8 con_num:7;
	u8 hard_reset:1;
} __packed;

/* Set USB Operation Mode Command structure */
struct ucsi_uor_cmd {
	u8 cmd;
	u8 length;
	u16 con_num:7;
	u16 role:3;
#define UCSI_UOR_ROLE_DFP			BIT(0)
#define UCSI_UOR_ROLE_UFP			BIT(1)
#define UCSI_UOR_ROLE_DRP			BIT(2)
	u16:6; /* reserved */
} __packed;

/* Get Alternate Modes Command structure */
struct ucsi_altmode_cmd {
	u8 cmd;
	u8 length;
	u8 recipient;
#define UCSI_RECIPIENT_CON			0
#define UCSI_RECIPIENT_SOP			1
#define UCSI_RECIPIENT_SOP_P			2
#define UCSI_RECIPIENT_SOP_PP			3
	u8 con_num;
	u8 offset;
	u8 num_altmodes;
} __packed;

struct ucsi_control {
	union {
		u64 raw_cmd;
		struct ucsi_command cmd;
		struct ucsi_uor_cmd uor;
		struct ucsi_ack_cmd ack;
		struct ucsi_con_rst con_rst;
		struct ucsi_altmode_cmd alt;
	};
};

#define __UCSI_CMD(_ctrl_, _cmd_)					\
{									\
	(_ctrl_).raw_cmd = 0;						\
	(_ctrl_).cmd.cmd = _cmd_;					\
}

/* Helper for preparing ucsi_control for CONNECTOR_RESET command. */
#define UCSI_CMD_CONNECTOR_RESET(_ctrl_, _con_, _hard_)			\
{									\
	__UCSI_CMD(_ctrl_, UCSI_CONNECTOR_RESET)			\
	(_ctrl_).con_rst.con_num = (_con_)->num;			\
	(_ctrl_).con_rst.hard_reset = _hard_;				\
}

/* Helper for preparing ucsi_control for ACK_CC_CI command. */
#define UCSI_CMD_ACK(_ctrl_, _ack_)					\
{									\
	__UCSI_CMD(_ctrl_, UCSI_ACK_CC_CI)				\
	(_ctrl_).ack.cci_ack = ((_ack_) == UCSI_ACK_EVENT);		\
	(_ctrl_).ack.cmd_ack = ((_ack_) == UCSI_ACK_CMD);		\
}

/* Helper for preparing ucsi_control for SET_NOTIFY_ENABLE command. */
#define UCSI_CMD_SET_NTFY_ENABLE(_ctrl_, _ntfys_)			\
{									\
	__UCSI_CMD(_ctrl_, UCSI_SET_NOTIFICATION_ENABLE)		\
	(_ctrl_).cmd.data = _ntfys_;					\
}

/* Helper for preparing ucsi_control for GET_CAPABILITY command. */
#define UCSI_CMD_GET_CAPABILITY(_ctrl_)					\
{									\
	__UCSI_CMD(_ctrl_, UCSI_GET_CAPABILITY)				\
}

/* Helper for preparing ucsi_control for GET_CONNECTOR_CAPABILITY command. */
#define UCSI_CMD_GET_CONNECTOR_CAPABILITY(_ctrl_, _con_)		\
{									\
	__UCSI_CMD(_ctrl_, UCSI_GET_CONNECTOR_CAPABILITY)		\
	(_ctrl_).cmd.data = _con_;					\
}

/* Helper for preparing ucsi_control for GET_ALTERNATE_MODES command. */
#define UCSI_CMD_GET_ALTERNATE_MODES(_ctrl_, _r_, _con_num_, _o_, _num_)\
{									\
	__UCSI_CMD((_ctrl_), UCSI_GET_ALTERNATE_MODES)			\
	_ctrl_.alt.recipient = (_r_);					\
	_ctrl_.alt.con_num = (_con_num_);				\
	_ctrl_.alt.offset = (_o_);					\
	_ctrl_.alt.num_altmodes = (_num_) - 1;				\
}

/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */
#define UCSI_CMD_GET_CAM_SUPPORTED(_ctrl_, _con_)			\
{									\
	__UCSI_CMD((_ctrl_), UCSI_GET_CAM_SUPPORTED)			\
	_ctrl_.cmd.data = (_con_);					\
}

/* Helper for preparing ucsi_control for GET_CAM_SUPPORTED command. */
#define UCSI_CMD_GET_CURRENT_CAM(_ctrl_, _con_)			\
{									\
	__UCSI_CMD((_ctrl_), UCSI_GET_CURRENT_CAM)			\
	_ctrl_.cmd.data = (_con_);					\
}

/* Helper for preparing ucsi_control for GET_CONNECTOR_STATUS command. */
#define UCSI_CMD_GET_CONNECTOR_STATUS(_ctrl_, _con_)			\
{									\
	__UCSI_CMD(_ctrl_, UCSI_GET_CONNECTOR_STATUS)			\
	(_ctrl_).cmd.data = _con_;					\
}

#define __UCSI_ROLE(_ctrl_, _cmd_, _con_num_)				\
{									\
	__UCSI_CMD(_ctrl_, _cmd_)					\
	(_ctrl_).uor.con_num = _con_num_;				\
	(_ctrl_).uor.role = UCSI_UOR_ROLE_DRP;				\
}

/* Helper for preparing ucsi_control for SET_UOR command. */
#define UCSI_CMD_SET_UOR(_ctrl_, _con_, _role_)				\
{									\
	__UCSI_ROLE(_ctrl_, UCSI_SET_UOR, (_con_)->num)		\
	(_ctrl_).uor.role |= (_role_) == TYPEC_HOST ? UCSI_UOR_ROLE_DFP : \
			  UCSI_UOR_ROLE_UFP;				\
}

/* Helper for preparing ucsi_control for SET_PDR command. */
#define UCSI_CMD_SET_PDR(_ctrl_, _con_, _role_)			\
{									\
	__UCSI_ROLE(_ctrl_, UCSI_SET_PDR, (_con_)->num)		\
	(_ctrl_).uor.role |= (_role_) == TYPEC_SOURCE ? UCSI_UOR_ROLE_DFP : \
			UCSI_UOR_ROLE_UFP;				\
}

/* Commands */
#define UCSI_PPM_RESET			0x01
#define UCSI_CANCEL			0x02
#define UCSI_CONNECTOR_RESET		0x03
#define UCSI_ACK_CC_CI			0x04
#define UCSI_SET_NOTIFICATION_ENABLE	0x05
#define UCSI_GET_CAPABILITY		0x06
#define UCSI_GET_CONNECTOR_CAPABILITY	0x07
#define UCSI_SET_UOM			0x08
#define UCSI_SET_UOR			0x09
#define UCSI_SET_PDM			0x0a
#define UCSI_SET_PDR			0x0b
#define UCSI_GET_ALTERNATE_MODES	0x0c
#define UCSI_GET_CAM_SUPPORTED		0x0d
#define UCSI_GET_CURRENT_CAM		0x0e
#define UCSI_SET_NEW_CAM		0x0f
#define UCSI_GET_PDOS			0x10
#define UCSI_GET_CABLE_PROPERTY		0x11
#define UCSI_GET_CONNECTOR_STATUS	0x12
#define UCSI_GET_ERROR_STATUS		0x13

/* ACK_CC_CI commands */
#define UCSI_ACK_EVENT			1
#define UCSI_ACK_CMD			2

/* Bits for SET_NOTIFICATION_ENABLE command */
#define UCSI_ENABLE_NTFY_CMD_COMPLETE		BIT(0)
#define UCSI_ENABLE_NTFY_EXT_PWR_SRC_CHANGE	BIT(1)
#define UCSI_ENABLE_NTFY_PWR_OPMODE_CHANGE	BIT(2)
#define UCSI_ENABLE_NTFY_CAP_CHANGE		BIT(5)
#define UCSI_ENABLE_NTFY_PWR_LEVEL_CHANGE	BIT(6)
#define UCSI_ENABLE_NTFY_PD_RESET_COMPLETE	BIT(7)
#define UCSI_ENABLE_NTFY_CAM_CHANGE		BIT(8)
#define UCSI_ENABLE_NTFY_BAT_STATUS_CHANGE	BIT(9)
#define UCSI_ENABLE_NTFY_PARTNER_CHANGE		BIT(11)
#define UCSI_ENABLE_NTFY_PWR_DIR_CHANGE		BIT(12)
#define UCSI_ENABLE_NTFY_CONNECTOR_CHANGE	BIT(14)
#define UCSI_ENABLE_NTFY_ERROR			BIT(15)
#define UCSI_ENABLE_NTFY_ALL			0xdbe7

/* Error information returned by PPM in response to GET_ERROR_STATUS command. */
#define UCSI_ERROR_UNREGONIZED_CMD		BIT(0)
#define UCSI_ERROR_INVALID_CON_NUM		BIT(1)
#define UCSI_ERROR_INVALID_CMD_ARGUMENT		BIT(2)
#define UCSI_ERROR_INCOMPATIBLE_PARTNER		BIT(3)
#define UCSI_ERROR_CC_COMMUNICATION_ERR		BIT(4)
#define UCSI_ERROR_DEAD_BATTERY			BIT(5)
#define UCSI_ERROR_CONTRACT_NEGOTIATION_FAIL	BIT(6)

/* Data structure filled by PPM in response to GET_CAPABILITY command. */
struct ucsi_capability {
	u32 attributes;
#define UCSI_CAP_ATTR_DISABLE_STATE		BIT(0)
#define UCSI_CAP_ATTR_BATTERY_CHARGING		BIT(1)
#define UCSI_CAP_ATTR_USB_PD			BIT(2)
#define UCSI_CAP_ATTR_TYPEC_CURRENT		BIT(6)
#define UCSI_CAP_ATTR_POWER_AC_SUPPLY		BIT(8)
#define UCSI_CAP_ATTR_POWER_OTHER		BIT(10)
#define UCSI_CAP_ATTR_POWER_VBUS		BIT(14)
	u32 num_connectors:8;
	u32 features:24;
#define UCSI_CAP_SET_UOM			BIT(0)
#define UCSI_CAP_SET_PDM			BIT(1)
#define UCSI_CAP_ALT_MODE_DETAILS		BIT(2)
#define UCSI_CAP_ALT_MODE_OVERRIDE		BIT(3)
#define UCSI_CAP_PDO_DETAILS			BIT(4)
#define UCSI_CAP_CABLE_DETAILS			BIT(5)
#define UCSI_CAP_EXT_SUPPLY_NOTIFICATIONS	BIT(6)
#define UCSI_CAP_PD_RESET			BIT(7)
	u8 num_alt_modes;
	u8 reserved;
	u16 bc_version;
	u16 pd_version;
	u16 typec_version;
} __packed;

/* Data structure filled by PPM in response to GET_CONNECTOR_CAPABILITY cmd. */
struct ucsi_connector_capability {
	u8 op_mode;
#define UCSI_CONCAP_OPMODE_DFP			BIT(0)
#define UCSI_CONCAP_OPMODE_UFP			BIT(1)
#define UCSI_CONCAP_OPMODE_DRP			BIT(2)
#define UCSI_CONCAP_OPMODE_AUDIO_ACCESSORY	BIT(3)
#define UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY	BIT(4)
#define UCSI_CONCAP_OPMODE_USB2			BIT(5)
#define UCSI_CONCAP_OPMODE_USB3			BIT(6)
#define UCSI_CONCAP_OPMODE_ALT_MODE		BIT(7)
	u8 provider:1;
	u8 consumer:1;
	u8:6; /* reserved */
} __packed;

struct ucsi_altmode {
	u16 svid;
	u32 mid;
} __packed;

/* Data structure filled by PPM in response to GET_CABLE_PROPERTY command. */
struct ucsi_cable_property {
	u16 speed_supported;
	u8 current_capability;
	u8 vbus_in_cable:1;
	u8 active_cable:1;
	u8 directionality:1;
	u8 plug_type:2;
#define UCSI_CABLE_PROPERTY_PLUG_TYPE_A		0
#define UCSI_CABLE_PROPERTY_PLUG_TYPE_B		1
#define UCSI_CABLE_PROPERTY_PLUG_TYPE_C		2
#define UCSI_CABLE_PROPERTY_PLUG_OTHER		3
	u8 mode_support:1;
	u8:2; /* reserved */
	u8 latency:4;
	u8:4; /* reserved */
} __packed;

/* Data structure filled by PPM in response to GET_CONNECTOR_STATUS command. */
struct ucsi_connector_status {
	u16 change;
#define UCSI_CONSTAT_EXT_SUPPLY_CHANGE		BIT(1)
#define UCSI_CONSTAT_POWER_OPMODE_CHANGE	BIT(2)
#define UCSI_CONSTAT_PDOS_CHANGE		BIT(5)
#define UCSI_CONSTAT_POWER_LEVEL_CHANGE		BIT(6)
#define UCSI_CONSTAT_PD_RESET_COMPLETE		BIT(7)
#define UCSI_CONSTAT_CAM_CHANGE			BIT(8)
#define UCSI_CONSTAT_BC_CHANGE			BIT(9)
#define UCSI_CONSTAT_PARTNER_CHANGE		BIT(11)
#define UCSI_CONSTAT_POWER_DIR_CHANGE		BIT(12)
#define UCSI_CONSTAT_CONNECT_CHANGE		BIT(14)
#define UCSI_CONSTAT_ERROR			BIT(15)
	u16 pwr_op_mode:3;
#define UCSI_CONSTAT_PWR_OPMODE_NONE		0
#define UCSI_CONSTAT_PWR_OPMODE_DEFAULT		1
#define UCSI_CONSTAT_PWR_OPMODE_BC		2
#define UCSI_CONSTAT_PWR_OPMODE_PD		3
#define UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5	4
#define UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0	5
	u16 connected:1;
	u16 pwr_dir:1;
	u16 partner_flags:8;
#define UCSI_CONSTAT_PARTNER_FLAG_USB		BIT(0)
#define UCSI_CONSTAT_PARTNER_FLAG_ALT_MODE	BIT(1)
	u16 partner_type:3;
#define UCSI_CONSTAT_PARTNER_TYPE_DFP		1
#define UCSI_CONSTAT_PARTNER_TYPE_UFP		2
#define UCSI_CONSTAT_PARTNER_TYPE_CABLE		3 /* Powered Cable */
#define UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP	4 /* Powered Cable */
#define UCSI_CONSTAT_PARTNER_TYPE_DEBUG		5
#define UCSI_CONSTAT_PARTNER_TYPE_AUDIO		6
	u32 request_data_obj;
	u8 bc_status:2;
#define UCSI_CONSTAT_BC_NOT_CHARGING		0
#define UCSI_CONSTAT_BC_NOMINAL_CHARGING	1
#define UCSI_CONSTAT_BC_SLOW_CHARGING		2
#define UCSI_CONSTAT_BC_TRICKLE_CHARGING	3
	u8 provider_cap_limit_reason:4;
#define UCSI_CONSTAT_CAP_PWR_LOWERED		0
#define UCSI_CONSTAT_CAP_PWR_BUDGET_LIMIT	1
	u8:2; /* reserved */
} __packed;

/* -------------------------------------------------------------------------- */

struct ucsi;

struct ucsi_data {
	u16 version;
	u16 reserved;
	union {
		u32 raw_cci;
		struct ucsi_cci cci;
	};
	struct ucsi_control ctrl;
	u32 message_in[4];
	u32 message_out[4];
} __packed;

/*
 * struct ucsi_ppm - Interface to UCSI Platform Policy Manager
 * @data: memory location to the UCSI data structures
 * @cmd: UCSI command execution routine
 * @sync: Refresh UCSI mailbox (the data structures)
 */
struct ucsi_ppm {
	struct ucsi_data *data;
	int (*cmd)(struct ucsi_ppm *, struct ucsi_control *);
	int (*sync)(struct ucsi_ppm *);
};

struct ucsi *ucsi_register_ppm(struct device *dev, struct ucsi_ppm *ppm);
void ucsi_unregister_ppm(struct ucsi *ucsi);
void ucsi_notify(struct ucsi *ucsi);

/* -------------------------------------------------------------------------- */

enum ucsi_status {
	UCSI_IDLE = 0,
	UCSI_BUSY,
	UCSI_ERROR,
};

struct ucsi {
	struct device *dev;
	struct ucsi_ppm *ppm;

	enum ucsi_status status;
	struct completion complete;
	struct ucsi_capability cap;
	struct ucsi_connector *connector;

	struct work_struct work;

	/* PPM Communication lock */
	struct mutex ppm_lock;

	/* PPM communication flags */
	unsigned long flags;
#define EVENT_PENDING	0
#define COMMAND_PENDING	1
#define ACK_PENDING	2
};

#define UCSI_MAX_SVID		5
#define UCSI_MAX_ALTMODES	(UCSI_MAX_SVID * 6)

struct ucsi_connector {
	int num;

	struct ucsi *ucsi;
	struct mutex lock; /* port lock */
	struct work_struct work;
	struct completion complete;

	struct typec_port *port;
	struct typec_partner *partner;

	struct typec_altmode *port_altmode[UCSI_MAX_ALTMODES];
	struct typec_altmode *partner_altmode[UCSI_MAX_ALTMODES];

	struct typec_capability typec_cap;

	struct ucsi_connector_status status;
	struct ucsi_connector_capability cap;
};

int ucsi_send_command(struct ucsi *ucsi, struct ucsi_control *ctrl,
		      void *retval, size_t size);

void ucsi_altmode_update_active(struct ucsi_connector *con);
int ucsi_resume(struct ucsi *ucsi);

#if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
struct typec_altmode *
ucsi_register_displayport(struct ucsi_connector *con,
			  bool override, int offset,
			  struct typec_altmode_desc *desc);

void ucsi_displayport_remove_partner(struct typec_altmode *adev);

#else
static inline struct typec_altmode *
ucsi_register_displayport(struct ucsi_connector *con,
			  bool override, int offset,
			  struct typec_altmode_desc *desc)
{
	return NULL;
}

static inline void
ucsi_displayport_remove_partner(struct typec_altmode *adev) { }
#endif /* CONFIG_TYPEC_DP_ALTMODE */

#endif /* __DRIVER_USB_TYPEC_UCSI_H */