f39885663a3c7aed93259428fe3b4afdf04f8a88
[platform/kernel/u-boot.git] / common / iotrace.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) 2014 Google, Inc.
4  */
5
6 #define IOTRACE_IMPL
7
8 #include <common.h>
9 #include <mapmem.h>
10 #include <asm/io.h>
11
12 DECLARE_GLOBAL_DATA_PTR;
13
14 /* Support up to the machine word length for now */
15 typedef ulong iovalue_t;
16
17 enum iotrace_flags {
18         IOT_8 = 0,
19         IOT_16,
20         IOT_32,
21
22         IOT_READ = 0 << 3,
23         IOT_WRITE = 1 << 3,
24 };
25
26 /**
27  * struct iotrace_record - Holds a single I/O trace record
28  *
29  * @flags: I/O access type
30  * @addr: Address of access
31  * @value: Value written or read
32  */
33 struct iotrace_record {
34         enum iotrace_flags flags;
35         phys_addr_t addr;
36         iovalue_t value;
37 };
38
39 /**
40  * struct iotrace - current trace status and checksum
41  *
42  * @start:      Start address of iotrace buffer
43  * @size:       Size of iotrace buffer in bytes
44  * @offset:     Current write offset into iotrace buffer
45  * @region_start: Address of IO region to trace
46  * @region_size: Size of region to trace. if 0 will trace all address space
47  * @crc32:      Current value of CRC chceksum of trace records
48  * @enabled:    true if enabled, false if disabled
49  */
50 static struct iotrace {
51         ulong start;
52         ulong size;
53         ulong offset;
54         ulong region_start;
55         ulong region_size;
56         u32 crc32;
57         bool enabled;
58 } iotrace;
59
60 static void add_record(int flags, const void *ptr, ulong value)
61 {
62         struct iotrace_record srec, *rec = &srec;
63
64         /*
65          * We don't support iotrace before relocation. Since the trace buffer
66          * is set up by a command, it can't be enabled at present. To change
67          * this we would need to set the iotrace buffer at build-time. See
68          * lib/trace.c for how this might be done if you are interested.
69          */
70         if (!(gd->flags & GD_FLG_RELOC) || !iotrace.enabled)
71                 return;
72
73         if (iotrace.region_size)
74                 if ((ulong)ptr < iotrace.region_start ||
75                     (ulong)ptr > iotrace.region_start + iotrace.region_size)
76                         return;
77
78         /* Store it if there is room */
79         if (iotrace.offset + sizeof(*rec) < iotrace.size) {
80                 rec = (struct iotrace_record *)map_sysmem(
81                                         iotrace.start + iotrace.offset,
82                                         sizeof(value));
83         }
84
85         rec->flags = flags;
86         rec->addr = map_to_sysmem(ptr);
87         rec->value = value;
88
89         /* Update our checksum */
90         iotrace.crc32 = crc32(iotrace.crc32, (unsigned char *)rec,
91                               sizeof(*rec));
92
93         iotrace.offset += sizeof(struct iotrace_record);
94 }
95
96 u32 iotrace_readl(const void *ptr)
97 {
98         u32 v;
99
100         v = readl(ptr);
101         add_record(IOT_32 | IOT_READ, ptr, v);
102
103         return v;
104 }
105
106 void iotrace_writel(ulong value, const void *ptr)
107 {
108         add_record(IOT_32 | IOT_WRITE, ptr, value);
109         writel(value, ptr);
110 }
111
112 u16 iotrace_readw(const void *ptr)
113 {
114         u32 v;
115
116         v = readw(ptr);
117         add_record(IOT_16 | IOT_READ, ptr, v);
118
119         return v;
120 }
121
122 void iotrace_writew(ulong value, const void *ptr)
123 {
124         add_record(IOT_16 | IOT_WRITE, ptr, value);
125         writew(value, ptr);
126 }
127
128 u8 iotrace_readb(const void *ptr)
129 {
130         u32 v;
131
132         v = readb(ptr);
133         add_record(IOT_8 | IOT_READ, ptr, v);
134
135         return v;
136 }
137
138 void iotrace_writeb(ulong value, const void *ptr)
139 {
140         add_record(IOT_8 | IOT_WRITE, ptr, value);
141         writeb(value, ptr);
142 }
143
144 void iotrace_reset_checksum(void)
145 {
146         iotrace.crc32 = 0;
147 }
148
149 u32 iotrace_get_checksum(void)
150 {
151         return iotrace.crc32;
152 }
153
154 void iotrace_set_region(ulong start, ulong size)
155 {
156         iotrace.region_start = start;
157         iotrace.region_size = size;
158 }
159
160 void iotrace_reset_region(void)
161 {
162         iotrace.region_start = 0;
163         iotrace.region_size = 0;
164 }
165
166 void iotrace_get_region(ulong *start, ulong *size)
167 {
168         *start = iotrace.region_start;
169         *size = iotrace.region_size;
170 }
171
172 void iotrace_set_enabled(int enable)
173 {
174         iotrace.enabled = enable;
175 }
176
177 int iotrace_get_enabled(void)
178 {
179         return iotrace.enabled;
180 }
181
182 void iotrace_set_buffer(ulong start, ulong size)
183 {
184         iotrace.start = start;
185         iotrace.size = size;
186         iotrace.offset = 0;
187         iotrace.crc32 = 0;
188 }
189
190 void iotrace_get_buffer(ulong *start, ulong *size, ulong *offset, ulong *count)
191 {
192         *start = iotrace.start;
193         *size = iotrace.size;
194         *offset = iotrace.offset;
195         *count = iotrace.offset / sizeof(struct iotrace_record);
196 }