2 * Copyright (C) 2011 Nokia Corporation
3 * Author: Imre Deak <imre.deak@nokia.com>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 #include <linux/vmalloc.h>
22 #include "img_types.h"
25 #include "pvr_trace_cmd.h"
27 #include "pvr_bridge_km.h"
29 /* Need to be log2 size. */
30 #define PVR_TBUF_SIZE (1 << (CONFIG_PVR_TRACE_CMD_BUF_SHIFT + PAGE_SHIFT))
32 #define PVR_TRCMD_INDENT 3
34 static struct pvr_trcmd_buf {
37 char data[PVR_TBUF_SIZE];
40 DEFINE_MUTEX(pvr_trcmd_mutex); /* protects tbuf */
46 unsigned long long time;
52 size_t (*print)(char *dst, size_t dst_size, const void *tbuf);
55 static size_t prn_syn(const char *name, char *dst, size_t dst_size,
56 const struct pvr_trcmd_syn *ts)
63 len = scnprintf(dst, dst_size, "%*s%s", PVR_TRCMD_INDENT, "", name);
64 len += scnprintf(&dst[len], dst_size - len, " addr:%08lx", ts->addr);
65 len += scnprintf(&dst[len], dst_size - len,
66 " rop/c:%8lu/%8lu wop/c:%8lu/%8lu\n",
67 ts->rd_pend, ts->rd_comp, ts->wr_pend, ts->wr_comp);
72 static size_t trcmd_prn_syn(char *dst, size_t dst_size, const void *tbuf)
74 const struct pvr_trcmd_syn *ts = tbuf;
76 return prn_syn("syn ", dst, dst_size, ts);
79 static size_t trcmd_prn_sgxkick(char *dst, size_t dst_size, const void *tbuf)
81 const struct pvr_trcmd_sgxkick *d = tbuf;
85 len = prn_syn("tatq_syn", dst, dst_size, &d->tatq_syn);
86 len += prn_syn("3dtq_syn", &dst[len], dst_size - len, &d->_3dtq_syn);
87 for (i = 0; i < SGX_MAX_SRC_SYNCS; i++) {
90 snprintf(sname, sizeof(sname), "src_syn%d", i);
91 len += prn_syn(sname, &dst[len], dst_size - len,
94 len += prn_syn("dst_syn ", &dst[len], dst_size - len, &d->dst_syn);
95 len += prn_syn("ta3d_syn", &dst[len], dst_size - len, &d->ta3d_syn);
96 len += scnprintf(&dst[len], dst_size - len, "%*sctx %08lx\n",
97 PVR_TRCMD_INDENT, "", d->ctx);
102 static size_t trcmd_prn_sgxtfer(char *dst, size_t dst_size, const void *tbuf)
104 const struct pvr_trcmd_sgxtransfer *d = tbuf;
107 len = prn_syn("ta_syn ", dst, dst_size, &d->ta_syn);
108 len += prn_syn("3d_syn ", &dst[len], dst_size - len, &d->_3d_syn);
109 len += prn_syn("src_syn ", &dst[len], dst_size - len, &d->src_syn);
110 len += prn_syn("dst_syn ", &dst[len], dst_size - len, &d->dst_syn);
111 len += scnprintf(&dst[len], dst_size - len, "%*sctx %08lx\n",
112 PVR_TRCMD_INDENT, "", d->ctx);
117 static struct trcmd_desc trcmd_desc_table[] = {
118 [PVR_TRCMD_SGX_FIRSTKICK] = { "sgx_first_kick", trcmd_prn_sgxkick },
119 [PVR_TRCMD_SGX_KICK] = { "sgx_kick", trcmd_prn_sgxkick },
120 [PVR_TRCMD_TFER_KICK] = { "sgx_tfer_kick", trcmd_prn_sgxtfer },
121 [PVR_TRCMD_SGX_QBLT_FLPREQ] = { "sgx_qblt_flip", NULL },
122 [PVR_TRCMD_SGX_QBLT_UPDREQ] = { "sgx_qblt_update", NULL },
123 [PVR_TRCMD_SGX_QBLT_SYNCHK] = { "sgx_qblt_synchk", trcmd_prn_syn },
124 [PVR_TRCMD_SGX_QBLT_SYNREQ] = { "sgx_qblt_synreq", trcmd_prn_syn },
125 [PVR_TRCMD_SGX_QBLT_SYNCOMP] = { "sgx_qblt_syn_comp", trcmd_prn_syn },
126 [PVR_TRCMD_SGX_QBLT_FLPCOMP] = { "sgx_qblt_flip_comp", NULL },
127 [PVR_TRCMD_SGX_QBLT_UPDCOMP] = { "sgx_qblt_update_comp", NULL }
131 static inline int tbuf_idx_add(int val, int delta)
134 val &= PVR_TBUF_SIZE - 1;
139 static size_t prn_frame(const struct tbuf_frame *f, char *dst, size_t dst_size)
141 const struct trcmd_desc *desc;
142 unsigned long long sec;
143 unsigned long usec_frac;
146 desc = &trcmd_desc_table[f->type];
149 usec_frac = do_div(sec, 1000000000) / 1000;
151 len = scnprintf(dst, dst_size, "[%5llu.%06lu] %s[%ld]: %s\n",
152 sec, usec_frac, f->pname, f->pid, desc->name);
154 len += desc->print(&dst[len], dst_size - len, (void *)(f + 1));
159 int pvr_trcmd_create_snapshot(u8 **snapshot_ret, size_t *snapshot_size)
166 read_idx = tbuf.read_idx;
167 size = tbuf_idx_add(tbuf.write_idx, -read_idx);
168 snapshot = vmalloc(size);
172 tail_size = min_t(size_t, size, PVR_TBUF_SIZE - read_idx);
173 memcpy(snapshot, &tbuf.data[read_idx], tail_size);
174 memcpy(&snapshot[tail_size], tbuf.data, size - tail_size);
176 *snapshot_ret = snapshot;
177 *snapshot_size = size;
182 void pvr_trcmd_destroy_snapshot(void *snapshot)
187 size_t pvr_trcmd_print(char *dst, size_t dst_size, const u8 *snapshot,
188 size_t snapshot_size, loff_t *snapshot_ofs)
192 if (*snapshot_ofs >= snapshot_size)
196 snapshot_size -= *snapshot_ofs;
198 while (snapshot_size) {
199 const struct tbuf_frame *f;
202 if (WARN_ON_ONCE(snapshot_size < 4))
205 f = (struct tbuf_frame *)&snapshot[*snapshot_ofs];
206 if (WARN_ON_ONCE(!f->size || f->size > snapshot_size ||
207 f->type >= ARRAY_SIZE(trcmd_desc_table)))
210 if (f->type != PVR_TRCMD_PAD)
211 this_len = prn_frame(f, &dst[dst_len],
216 if (dst_len + this_len + 1 == dst_size) {
217 /* drop the last printed frame */
223 *snapshot_ofs += f->size;
225 snapshot_size -= f->size;
231 static void *tbuf_get_space(size_t size)
237 if (tbuf_idx_add(tbuf.read_idx - 1, -tbuf.write_idx) < size) {
239 * Trace buffer overflow, discard the frame that will
240 * be overwritten by the next write.
242 struct tbuf_frame *f =
243 (void *)&tbuf.data[tbuf.read_idx];
245 buf_idx = tbuf.read_idx;
246 tbuf.read_idx = tbuf_idx_add(tbuf.read_idx,
248 } else if (PVR_TBUF_SIZE - tbuf.write_idx < size) {
249 struct tbuf_frame *f =
250 (void *)&tbuf.data[tbuf.write_idx];
252 * Not enough space until the end of trace buffer,
253 * rewind to the beginning. Frames are sizeof(long)
254 * aligned, thus we are guaranteed to have space for
255 * the following two fields.
257 f->size = PVR_TBUF_SIZE - tbuf.write_idx;
258 f->type = PVR_TRCMD_PAD;
264 ret = &tbuf.data[tbuf.write_idx];
265 tbuf.write_idx = tbuf_idx_add(tbuf.write_idx, size);
270 void *pvr_trcmd_alloc(unsigned type, int pid, const char *pname, size_t size)
272 struct tbuf_frame *f;
275 size = ALIGN(size, __alignof__(*f));
276 total_size = sizeof(*f) + size;
277 f = tbuf_get_space(total_size);
278 f->size = total_size;
281 f->time = cpu_clock(smp_processor_id());
282 strlcpy(f->pname, pname, sizeof(f->pname));
287 void pvr_trcmd_set_syn(struct pvr_trcmd_syn *ts,
288 const struct PVRSRV_KERNEL_SYNC_INFO *si)
290 struct PVRSRV_SYNC_DATA *sd = si->psSyncData;
292 ts->rd_pend = sd->ui32ReadOpsPending;
293 ts->rd_comp = sd->ui32ReadOpsComplete;
294 ts->wr_pend = sd->ui32WriteOpsPending;
295 ts->wr_comp = sd->ui32WriteOpsComplete;
296 ts->addr = si->sWriteOpsCompleteDevVAddr.uiAddr - 4;