| /* SPDX-License-Identifier: GPL-2.0 */ |
| /* |
| * Thunderbolt tracing support |
| * |
| * Copyright (C) 2024, Intel Corporation |
| * Author: Mika Westerberg <mika.westerberg@linux.intel.com> |
| * Gil Fine <gil.fine@intel.com> |
| */ |
| |
| #undef TRACE_SYSTEM |
| #define TRACE_SYSTEM thunderbolt |
| |
| #if !defined(TB_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ) |
| #define TB_TRACE_H_ |
| |
| #include <linux/trace_seq.h> |
| #include <linux/tracepoint.h> |
| |
| #include "tb_msgs.h" |
| |
| #define tb_cfg_type_name(type) { type, #type } |
| #define show_type_name(val) \ |
| __print_symbolic(val, \ |
| tb_cfg_type_name(TB_CFG_PKG_READ), \ |
| tb_cfg_type_name(TB_CFG_PKG_WRITE), \ |
| tb_cfg_type_name(TB_CFG_PKG_ERROR), \ |
| tb_cfg_type_name(TB_CFG_PKG_NOTIFY_ACK), \ |
| tb_cfg_type_name(TB_CFG_PKG_EVENT), \ |
| tb_cfg_type_name(TB_CFG_PKG_XDOMAIN_REQ), \ |
| tb_cfg_type_name(TB_CFG_PKG_XDOMAIN_RESP), \ |
| tb_cfg_type_name(TB_CFG_PKG_OVERRIDE), \ |
| tb_cfg_type_name(TB_CFG_PKG_RESET), \ |
| tb_cfg_type_name(TB_CFG_PKG_ICM_EVENT), \ |
| tb_cfg_type_name(TB_CFG_PKG_ICM_CMD), \ |
| tb_cfg_type_name(TB_CFG_PKG_ICM_RESP)) |
| |
| #ifndef TB_TRACE_HELPERS |
| #define TB_TRACE_HELPERS |
| static inline const char *show_data_read_write(struct trace_seq *p, |
| const u32 *data) |
| { |
| const struct cfg_read_pkg *msg = (const struct cfg_read_pkg *)data; |
| const char *ret = trace_seq_buffer_ptr(p); |
| |
| trace_seq_printf(p, "offset=%#x, len=%u, port=%d, config=%#x, seq=%d, ", |
| msg->addr.offset, msg->addr.length, msg->addr.port, |
| msg->addr.space, msg->addr.seq); |
| |
| return ret; |
| } |
| |
| static inline const char *show_data_error(struct trace_seq *p, const u32 *data) |
| { |
| const struct cfg_error_pkg *msg = (const struct cfg_error_pkg *)data; |
| const char *ret = trace_seq_buffer_ptr(p); |
| |
| trace_seq_printf(p, "error=%#x, port=%d, plug=%#x, ", msg->error, |
| msg->port, msg->pg); |
| |
| return ret; |
| } |
| |
| static inline const char *show_data_event(struct trace_seq *p, const u32 *data) |
| { |
| const struct cfg_event_pkg *msg = (const struct cfg_event_pkg *)data; |
| const char *ret = trace_seq_buffer_ptr(p); |
| |
| trace_seq_printf(p, "port=%d, unplug=%#x, ", msg->port, msg->unplug); |
| |
| return ret; |
| } |
| |
| static inline const char *show_route(struct trace_seq *p, const u32 *data) |
| { |
| const struct tb_cfg_header *header = (const struct tb_cfg_header *)data; |
| const char *ret = trace_seq_buffer_ptr(p); |
| |
| trace_seq_printf(p, "route=%llx, ", tb_cfg_get_route(header)); |
| |
| return ret; |
| } |
| |
| static inline const char *show_data(struct trace_seq *p, u8 type, |
| const u32 *data, u32 length) |
| { |
| const char *ret = trace_seq_buffer_ptr(p); |
| const char *prefix = ""; |
| int i; |
| |
| switch (type) { |
| case TB_CFG_PKG_READ: |
| case TB_CFG_PKG_WRITE: |
| show_route(p, data); |
| show_data_read_write(p, data); |
| break; |
| |
| case TB_CFG_PKG_ERROR: |
| show_route(p, data); |
| show_data_error(p, data); |
| break; |
| |
| case TB_CFG_PKG_EVENT: |
| show_route(p, data); |
| show_data_event(p, data); |
| break; |
| |
| case TB_CFG_PKG_ICM_EVENT: |
| case TB_CFG_PKG_ICM_CMD: |
| case TB_CFG_PKG_ICM_RESP: |
| /* ICM messages always target the host router */ |
| trace_seq_puts(p, "route=0, "); |
| break; |
| |
| default: |
| show_route(p, data); |
| break; |
| } |
| |
| trace_seq_printf(p, "data=["); |
| for (i = 0; i < length; i++) { |
| trace_seq_printf(p, "%s0x%08x", prefix, data[i]); |
| prefix = ", "; |
| } |
| trace_seq_printf(p, "]"); |
| trace_seq_putc(p, 0); |
| |
| return ret; |
| } |
| #endif |
| |
| DECLARE_EVENT_CLASS(tb_raw, |
| TP_PROTO(int index, u8 type, const void *data, size_t size), |
| TP_ARGS(index, type, data, size), |
| TP_STRUCT__entry( |
| __field(int, index) |
| __field(u8, type) |
| __field(size_t, size) |
| __dynamic_array(u32, data, size / 4) |
| ), |
| TP_fast_assign( |
| __entry->index = index; |
| __entry->type = type; |
| __entry->size = size / 4; |
| memcpy(__get_dynamic_array(data), data, size); |
| ), |
| TP_printk("type=%s, size=%zd, domain=%d, %s", |
| show_type_name(__entry->type), __entry->size, __entry->index, |
| show_data(p, __entry->type, __get_dynamic_array(data), |
| __entry->size) |
| ) |
| ); |
| |
| DEFINE_EVENT(tb_raw, tb_tx, |
| TP_PROTO(int index, u8 type, const void *data, size_t size), |
| TP_ARGS(index, type, data, size) |
| ); |
| |
| DEFINE_EVENT(tb_raw, tb_event, |
| TP_PROTO(int index, u8 type, const void *data, size_t size), |
| TP_ARGS(index, type, data, size) |
| ); |
| |
| TRACE_EVENT(tb_rx, |
| TP_PROTO(int index, u8 type, const void *data, size_t size, bool dropped), |
| TP_ARGS(index, type, data, size, dropped), |
| TP_STRUCT__entry( |
| __field(int, index) |
| __field(u8, type) |
| __field(size_t, size) |
| __dynamic_array(u32, data, size / 4) |
| __field(bool, dropped) |
| ), |
| TP_fast_assign( |
| __entry->index = index; |
| __entry->type = type; |
| __entry->size = size / 4; |
| memcpy(__get_dynamic_array(data), data, size); |
| __entry->dropped = dropped; |
| ), |
| TP_printk("type=%s, dropped=%u, size=%zd, domain=%d, %s", |
| show_type_name(__entry->type), __entry->dropped, |
| __entry->size, __entry->index, |
| show_data(p, __entry->type, __get_dynamic_array(data), |
| __entry->size) |
| ) |
| ); |
| |
| #endif /* TB_TRACE_H_ */ |
| |
| #undef TRACE_INCLUDE_PATH |
| #define TRACE_INCLUDE_PATH . |
| |
| #undef TRACE_INCLUDE_FILE |
| #define TRACE_INCLUDE_FILE trace |
| |
| /* This part must be outside protection */ |
| #include <trace/define_trace.h> |