Merge branch 'u-boot-tegra/master' into 'u-boot-arm/master'
[platform/kernel/u-boot.git] / common / iotrace.c
1 /*
2  * Copyright (c) 2014 Google, Inc.
3  *
4  * SPDX-License-Identifier:     GPL-2.0+
5  */
6
7 #define IOTRACE_IMPL
8
9 #include <common.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  * @crc32:      Current value of CRC chceksum of trace records
46  * @enabled:    true if enabled, false if disabled
47  */
48 static struct iotrace {
49         ulong start;
50         ulong size;
51         ulong offset;
52         u32 crc32;
53         bool enabled;
54 } iotrace;
55
56 static void add_record(int flags, const void *ptr, ulong value)
57 {
58         struct iotrace_record srec, *rec = &srec;
59
60         /*
61          * We don't support iotrace before relocation. Since the trace buffer
62          * is set up by a command, it can't be enabled at present. To change
63          * this we would need to set the iotrace buffer at build-time. See
64          * lib/trace.c for how this might be done if you are interested.
65          */
66         if (!(gd->flags & GD_FLG_RELOC) || !iotrace.enabled)
67                 return;
68
69         /* Store it if there is room */
70         if (iotrace.offset + sizeof(*rec) < iotrace.size) {
71                 rec = (struct iotrace_record *)map_sysmem(
72                                         iotrace.start + iotrace.offset,
73                                         sizeof(value));
74         }
75
76         rec->flags = flags;
77         rec->addr = map_to_sysmem(ptr);
78         rec->value = value;
79
80         /* Update our checksum */
81         iotrace.crc32 = crc32(iotrace.crc32, (unsigned char *)rec,
82                               sizeof(*rec));
83
84         iotrace.offset += sizeof(struct iotrace_record);
85 }
86
87 u32 iotrace_readl(const void *ptr)
88 {
89         u32 v;
90
91         v = readl(ptr);
92         add_record(IOT_32 | IOT_READ, ptr, v);
93
94         return v;
95 }
96
97 void iotrace_writel(ulong value, const void *ptr)
98 {
99         add_record(IOT_32 | IOT_WRITE, ptr, value);
100         writel(value, ptr);
101 }
102
103 u16 iotrace_readw(const void *ptr)
104 {
105         u32 v;
106
107         v = readw(ptr);
108         add_record(IOT_16 | IOT_READ, ptr, v);
109
110         return v;
111 }
112
113 void iotrace_writew(ulong value, const void *ptr)
114 {
115         add_record(IOT_16 | IOT_WRITE, ptr, value);
116         writew(value, ptr);
117 }
118
119 u8 iotrace_readb(const void *ptr)
120 {
121         u32 v;
122
123         v = readb(ptr);
124         add_record(IOT_8 | IOT_READ, ptr, v);
125
126         return v;
127 }
128
129 void iotrace_writeb(ulong value, const void *ptr)
130 {
131         add_record(IOT_8 | IOT_WRITE, ptr, value);
132         writeb(value, ptr);
133 }
134
135 void iotrace_reset_checksum(void)
136 {
137         iotrace.crc32 = 0;
138 }
139
140 u32 iotrace_get_checksum(void)
141 {
142         return iotrace.crc32;
143 }
144
145 void iotrace_set_enabled(int enable)
146 {
147         iotrace.enabled = enable;
148 }
149
150 int iotrace_get_enabled(void)
151 {
152         return iotrace.enabled;
153 }
154
155 void iotrace_set_buffer(ulong start, ulong size)
156 {
157         iotrace.start = start;
158         iotrace.size = size;
159         iotrace.offset = 0;
160         iotrace.crc32 = 0;
161 }
162
163 void iotrace_get_buffer(ulong *start, ulong *size, ulong *offset, ulong *count)
164 {
165         *start = iotrace.start;
166         *size = iotrace.size;
167         *offset = iotrace.offset;
168         *count = iotrace.offset / sizeof(struct iotrace_record);
169 }