cvmx-pow.c revision 215990
1/***********************license start***************
2 * Copyright (c) 2003-2010  Cavium Networks (support@cavium.com). All rights
3 * reserved.
4 *
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions are
8 * met:
9 *
10 *   * Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 *
13 *   * Redistributions in binary form must reproduce the above
14 *     copyright notice, this list of conditions and the following
15 *     disclaimer in the documentation and/or other materials provided
16 *     with the distribution.
17
18 *   * Neither the name of Cavium Networks nor the names of
19 *     its contributors may be used to endorse or promote products
20 *     derived from this software without specific prior written
21 *     permission.
22
23 * This Software, including technical data, may be subject to U.S. export  control
24 * laws, including the U.S. Export Administration Act and its  associated
25 * regulations, and may be subject to export or import  regulations in other
26 * countries.
27
28 * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
29 * AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
30 * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
31 * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
32 * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
33 * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
34 * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
35 * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
36 * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
37 * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
38 ***********************license end**************************************/
39
40
41
42
43
44
45
46/**
47 * @file
48 *
49 * Interface to the hardware Packet Order / Work unit.
50 *
51 * <hr>$Revision: 29727 $<hr>
52 */
53
54#include "cvmx.h"
55#include "cvmx-pow.h"
56
57/**
58 * @INTERNAL
59 * This structure stores the internal POW state captured by
60 * cvmx_pow_capture(). It is purposely not exposed to the user
61 * since the format may change without notice.
62 */
63typedef struct
64{
65    cvmx_pow_tag_load_resp_t sstatus[16][8];
66    cvmx_pow_tag_load_resp_t smemload[2048][3];
67    cvmx_pow_tag_load_resp_t sindexload[16][4];
68} __cvmx_pow_dump_t;
69
70typedef enum
71{
72    CVMX_POW_LIST_UNKNOWN=0,
73    CVMX_POW_LIST_FREE=1,
74    CVMX_POW_LIST_INPUT=2,
75    CVMX_POW_LIST_CORE=CVMX_POW_LIST_INPUT+8,
76    CVMX_POW_LIST_DESCHED=CVMX_POW_LIST_CORE+16,
77    CVMX_POW_LIST_NOSCHED=CVMX_POW_LIST_DESCHED+16,
78} __cvmx_pow_list_types_t;
79
80static const char *__cvmx_pow_list_names[] = {
81    "Unknown",
82    "Free List",
83    "Queue 0", "Queue 1", "Queue 2", "Queue 3",
84    "Queue 4", "Queue 5", "Queue 6", "Queue 7",
85    "Core 0", "Core 1", "Core 2", "Core 3",
86    "Core 4", "Core 5", "Core 6", "Core 7",
87    "Core 8", "Core 9", "Core 10", "Core 11",
88    "Core 12", "Core 13", "Core 14", "Core 15",
89    "Desched 0", "Desched 1", "Desched 2", "Desched 3",
90    "Desched 4", "Desched 5", "Desched 6", "Desched 7",
91    "Desched 8", "Desched 9", "Desched 10", "Desched 11",
92    "Desched 12", "Desched 13", "Desched 14", "Desched 15",
93    "Nosched 0", "Nosched 1", "Nosched 2", "Nosched 3",
94    "Nosched 4", "Nosched 5", "Nosched 6", "Nosched 7",
95    "Nosched 8", "Nosched 9", "Nosched 10", "Nosched 11",
96    "Nosched 12", "Nosched 13", "Nosched 14", "Nosched 15"
97};
98
99
100/**
101 * Return the number of POW entries supported by this chip
102 *
103 * @return Number of POW entries
104 */
105int cvmx_pow_get_num_entries(void)
106{
107    if (OCTEON_IS_MODEL(OCTEON_CN30XX))
108        return 64;
109    else if (OCTEON_IS_MODEL(OCTEON_CN31XX) || OCTEON_IS_MODEL(OCTEON_CN50XX))
110        return 256;
111    else if (OCTEON_IS_MODEL(OCTEON_CN52XX))
112        return 512;
113    else if (OCTEON_IS_MODEL(OCTEON_CN63XX))
114	return 1024;
115    else
116        return 2048;
117}
118
119
120/**
121 * Store the current POW internal state into the supplied
122 * buffer. It is recommended that you pass a buffer of at least
123 * 128KB. The format of the capture may change based on SDK
124 * version and Octeon chip.
125 *
126 * @param buffer Buffer to store capture into
127 * @param buffer_size
128 *               The size of the supplied buffer
129 *
130 * @return Zero on sucess, negative on failure
131 */
132int cvmx_pow_capture(void *buffer, int buffer_size)
133{
134    __cvmx_pow_dump_t *dump = (__cvmx_pow_dump_t*)buffer;
135    int num_cores;
136    int num_pow_entries = cvmx_pow_get_num_entries();
137    int core;
138    int index;
139    int bits;
140
141    if (buffer_size < (int)sizeof(__cvmx_pow_dump_t))
142    {
143        cvmx_dprintf("cvmx_pow_capture: Buffer too small\n");
144        return -1;
145    }
146
147    num_cores = cvmx_octeon_num_cores();
148
149    /* Read all core related state */
150    for (core=0; core<num_cores; core++)
151    {
152        cvmx_pow_load_addr_t load_addr;
153        load_addr.u64 = 0;
154        load_addr.sstatus.mem_region = CVMX_IO_SEG;
155        load_addr.sstatus.is_io = 1;
156        load_addr.sstatus.did = CVMX_OCT_DID_TAG_TAG1;
157        load_addr.sstatus.coreid = core;
158        for (bits=0; bits<8; bits++)
159        {
160            load_addr.sstatus.get_rev = (bits & 1) != 0;
161            load_addr.sstatus.get_cur = (bits & 2) != 0;
162            load_addr.sstatus.get_wqp = (bits & 4) != 0;
163            if ((load_addr.sstatus.get_cur == 0) && load_addr.sstatus.get_rev)
164                dump->sstatus[core][bits].u64 = -1;
165            else
166                dump->sstatus[core][bits].u64 = cvmx_read_csr(load_addr.u64);
167        }
168    }
169
170    /* Read all internal POW entries */
171    for (index=0; index<num_pow_entries; index++)
172    {
173        cvmx_pow_load_addr_t load_addr;
174        load_addr.u64 = 0;
175        load_addr.smemload.mem_region = CVMX_IO_SEG;
176        load_addr.smemload.is_io = 1;
177        load_addr.smemload.did = CVMX_OCT_DID_TAG_TAG2;
178        load_addr.smemload.index = index;
179        for (bits=0; bits<3; bits++)
180        {
181            load_addr.smemload.get_des = (bits & 1) != 0;
182            load_addr.smemload.get_wqp = (bits & 2) != 0;
183            dump->smemload[index][bits].u64 = cvmx_read_csr(load_addr.u64);
184        }
185    }
186
187    /* Read all group and queue pointers */
188    for (index=0; index<16; index++)
189    {
190        cvmx_pow_load_addr_t load_addr;
191        load_addr.u64 = 0;
192        load_addr.sindexload.mem_region = CVMX_IO_SEG;
193        load_addr.sindexload.is_io = 1;
194        load_addr.sindexload.did = CVMX_OCT_DID_TAG_TAG3;
195        load_addr.sindexload.qosgrp = index;
196        for (bits=0; bits<4; bits++)
197        {
198            load_addr.sindexload.get_rmt =  (bits & 1) != 0;
199            load_addr.sindexload.get_des_get_tail =  (bits & 2) != 0;
200            /* The first pass only has 8 valid index values */
201            if ((load_addr.sindexload.get_rmt == 0) &&
202                (load_addr.sindexload.get_des_get_tail == 0) &&
203                (index >= 8))
204                dump->sindexload[index][bits].u64 = -1;
205            else
206                dump->sindexload[index][bits].u64 = cvmx_read_csr(load_addr.u64);
207        }
208    }
209    return 0;
210}
211
212
213/**
214 * Function to display a POW internal queue to the user
215 *
216 * @param name       User visible name for the queue
217 * @param name_param Parameter for printf in creating the name
218 * @param valid      Set if the queue contains any elements
219 * @param has_one    Set if the queue contains exactly one element
220 * @param head       The head pointer
221 * @param tail       The tail pointer
222 */
223static void __cvmx_pow_display_list(const char *name, int name_param, int valid, int has_one, uint64_t head, uint64_t tail)
224{
225    printf(name, name_param);
226    printf(": ");
227    if (valid)
228    {
229        if (has_one)
230            printf("One element index=%llu(0x%llx)\n", CAST64(head), CAST64(head));
231        else
232            printf("Multiple elements head=%llu(0x%llx) tail=%llu(0x%llx)\n", CAST64(head), CAST64(head), CAST64(tail), CAST64(tail));
233    }
234    else
235        printf("Empty\n");
236}
237
238
239/**
240 * Mark which list a POW entry is on. Print a warning message if the
241 * entry is already on a list. This happens if the POW changed while
242 * the capture was running.
243 *
244 * @param entry_num  Entry number to mark
245 * @param entry_type List type
246 * @param entry_list Array to store marks
247 *
248 * @return Zero on success, negative if already on a list
249 */
250static int __cvmx_pow_entry_mark_list(int entry_num, __cvmx_pow_list_types_t entry_type, uint8_t entry_list[])
251{
252    if (entry_list[entry_num] == 0)
253    {
254        entry_list[entry_num] = entry_type;
255        return 0;
256    }
257    else
258    {
259        printf("\nWARNING: Entry %d already on list %s, but we tried to add it to %s\n",
260               entry_num, __cvmx_pow_list_names[entry_list[entry_num]], __cvmx_pow_list_names[entry_type]);
261        return -1;
262    }
263}
264
265
266/**
267 * Display a list and mark all elements on the list as belonging to
268 * the list.
269 *
270 * @param entry_type Type of the list to display and mark
271 * @param dump       POW capture data
272 * @param entry_list Array to store marks in
273 * @param valid      Set if the queue contains any elements
274 * @param has_one    Set if the queue contains exactly one element
275 * @param head       The head pointer
276 * @param tail       The tail pointer
277 */
278static void __cvmx_pow_display_list_and_walk(__cvmx_pow_list_types_t entry_type,
279                                             __cvmx_pow_dump_t *dump, uint8_t entry_list[],
280                                             int valid, int has_one, uint64_t head, uint64_t tail)
281{
282    __cvmx_pow_display_list(__cvmx_pow_list_names[entry_type], 0, valid, has_one, head, tail);
283    if (valid)
284    {
285        if (has_one)
286            __cvmx_pow_entry_mark_list(head, entry_type, entry_list);
287        else
288        {
289            while (head != tail)
290            {
291                if (__cvmx_pow_entry_mark_list(head, entry_type, entry_list))
292                    break;
293                head = dump->smemload[head][0].s_smemload0.next_index;
294            }
295            __cvmx_pow_entry_mark_list(tail, entry_type, entry_list);
296        }
297    }
298}
299
300
301/**
302 * Dump a POW capture to the console in a human readable format.
303 *
304 * @param buffer POW capture from cvmx_pow_capture()
305 * @param buffer_size
306 *               Size of the buffer
307 */
308void cvmx_pow_display(void *buffer, int buffer_size)
309{
310    __cvmx_pow_dump_t *dump = (__cvmx_pow_dump_t*)buffer;
311    int num_pow_entries = cvmx_pow_get_num_entries();
312    int num_cores;
313    int core;
314    int index;
315    uint8_t entry_list[2048];
316
317    if (buffer_size < (int)sizeof(__cvmx_pow_dump_t))
318    {
319        cvmx_dprintf("cvmx_pow_dump: Buffer too small\n");
320        return;
321    }
322
323    memset(entry_list, 0, sizeof(entry_list));
324    num_cores = cvmx_octeon_num_cores();
325
326    printf("POW Display Start\n");
327
328    /* Print the free list info */
329    __cvmx_pow_display_list_and_walk(CVMX_POW_LIST_FREE, dump, entry_list,
330                                     dump->sindexload[0][0].sindexload0.free_val,
331                                     dump->sindexload[0][0].sindexload0.free_one,
332                                     dump->sindexload[0][0].sindexload0.free_head,
333                                     dump->sindexload[0][0].sindexload0.free_tail);
334
335    /* Print the core state */
336    for (core=0; core<num_cores; core++)
337    {
338        const int bit_rev = 1;
339        const int bit_cur = 2;
340        const int bit_wqp = 4;
341        printf("Core %d State:  tag=%s,0x%08x", core,
342               OCT_TAG_TYPE_STRING(dump->sstatus[core][bit_cur].s_sstatus2.tag_type),
343               dump->sstatus[core][bit_cur].s_sstatus2.tag);
344        if (dump->sstatus[core][bit_cur].s_sstatus2.tag_type != CVMX_POW_TAG_TYPE_NULL_NULL)
345        {
346            __cvmx_pow_entry_mark_list(dump->sstatus[core][bit_cur].s_sstatus2.index, CVMX_POW_LIST_CORE + core, entry_list);
347            printf(" grp=%d",                   dump->sstatus[core][bit_cur].s_sstatus2.grp);
348            printf(" wqp=0x%016llx",            CAST64(dump->sstatus[core][bit_cur|bit_wqp].s_sstatus4.wqp));
349            printf(" index=%d",                 dump->sstatus[core][bit_cur].s_sstatus2.index);
350            if (dump->sstatus[core][bit_cur].s_sstatus2.head)
351                printf(" head");
352            else
353                printf(" prev=%d", dump->sstatus[core][bit_cur|bit_rev].s_sstatus3.revlink_index);
354            if (dump->sstatus[core][bit_cur].s_sstatus2.tail)
355                printf(" tail");
356            else
357                printf(" next=%d", dump->sstatus[core][bit_cur].s_sstatus2.link_index);
358        }
359
360        if (dump->sstatus[core][0].s_sstatus0.pend_switch)
361        {
362            printf(" pend_switch=%d",           dump->sstatus[core][0].s_sstatus0.pend_switch);
363            printf(" pend_switch_full=%d",      dump->sstatus[core][0].s_sstatus0.pend_switch_full);
364            printf(" pend_switch_null=%d",      dump->sstatus[core][0].s_sstatus0.pend_switch_null);
365        }
366
367        if (dump->sstatus[core][0].s_sstatus0.pend_desched)
368        {
369            printf(" pend_desched=%d",          dump->sstatus[core][0].s_sstatus0.pend_desched);
370            printf(" pend_desched_switch=%d",   dump->sstatus[core][0].s_sstatus0.pend_desched_switch);
371            printf(" pend_nosched=%d",          dump->sstatus[core][0].s_sstatus0.pend_nosched);
372            if (dump->sstatus[core][0].s_sstatus0.pend_desched_switch)
373                printf(" pend_grp=%d",              dump->sstatus[core][0].s_sstatus0.pend_grp);
374        }
375
376        if (dump->sstatus[core][0].s_sstatus0.pend_new_work)
377        {
378            if (dump->sstatus[core][0].s_sstatus0.pend_new_work_wait)
379                printf(" (Waiting for work)");
380            else
381                printf(" (Getting work)");
382        }
383        if (dump->sstatus[core][0].s_sstatus0.pend_null_rd)
384            printf(" pend_null_rd=%d",          dump->sstatus[core][0].s_sstatus0.pend_null_rd);
385        if (dump->sstatus[core][0].s_sstatus0.pend_nosched_clr)
386        {
387            printf(" pend_nosched_clr=%d",      dump->sstatus[core][0].s_sstatus0.pend_nosched_clr);
388            printf(" pend_index=%d",            dump->sstatus[core][0].s_sstatus0.pend_index);
389        }
390        if (dump->sstatus[core][0].s_sstatus0.pend_switch ||
391            (dump->sstatus[core][0].s_sstatus0.pend_desched &&
392            dump->sstatus[core][0].s_sstatus0.pend_desched_switch))
393        {
394            printf(" pending tag=%s,0x%08x",
395                   OCT_TAG_TYPE_STRING(dump->sstatus[core][0].s_sstatus0.pend_type),
396                   dump->sstatus[core][0].s_sstatus0.pend_tag);
397        }
398        if (dump->sstatus[core][0].s_sstatus0.pend_nosched_clr)
399            printf(" pend_wqp=0x%016llx\n",     CAST64(dump->sstatus[core][bit_wqp].s_sstatus1.pend_wqp));
400        printf("\n");
401    }
402
403    /* Print out the state of the nosched list and the 16 deschedule lists. */
404    __cvmx_pow_display_list_and_walk(CVMX_POW_LIST_NOSCHED, dump, entry_list,
405                            dump->sindexload[0][2].sindexload1.nosched_val,
406                            dump->sindexload[0][2].sindexload1.nosched_one,
407                            dump->sindexload[0][2].sindexload1.nosched_head,
408                            dump->sindexload[0][2].sindexload1.nosched_tail);
409    for (index=0; index<16; index++)
410    {
411        __cvmx_pow_display_list_and_walk(CVMX_POW_LIST_DESCHED + index, dump, entry_list,
412                                dump->sindexload[index][2].sindexload1.des_val,
413                                dump->sindexload[index][2].sindexload1.des_one,
414                                dump->sindexload[index][2].sindexload1.des_head,
415                                dump->sindexload[index][2].sindexload1.des_tail);
416    }
417
418    /* Print out the state of the 8 internal input queues */
419    for (index=0; index<8; index++)
420    {
421        __cvmx_pow_display_list_and_walk(CVMX_POW_LIST_INPUT + index, dump, entry_list,
422                                dump->sindexload[index][0].sindexload0.loc_val,
423                                dump->sindexload[index][0].sindexload0.loc_one,
424                                dump->sindexload[index][0].sindexload0.loc_head,
425                                dump->sindexload[index][0].sindexload0.loc_tail);
426    }
427
428    /* Print out the state of the 16 memory queues */
429    for (index=0; index<8; index++)
430    {
431        const char *name;
432        if (dump->sindexload[index][1].sindexload2.rmt_is_head)
433            name = "Queue %da Memory (is head)";
434        else
435            name = "Queue %da Memory";
436        __cvmx_pow_display_list(name, index,
437                                dump->sindexload[index][1].sindexload2.rmt_val,
438                                dump->sindexload[index][1].sindexload2.rmt_one,
439                                dump->sindexload[index][1].sindexload2.rmt_head,
440                                dump->sindexload[index][3].sindexload3.rmt_tail);
441        if (dump->sindexload[index+8][1].sindexload2.rmt_is_head)
442            name = "Queue %db Memory (is head)";
443        else
444            name = "Queue %db Memory";
445        __cvmx_pow_display_list(name, index,
446                                dump->sindexload[index+8][1].sindexload2.rmt_val,
447                                dump->sindexload[index+8][1].sindexload2.rmt_one,
448                                dump->sindexload[index+8][1].sindexload2.rmt_head,
449                                dump->sindexload[index+8][3].sindexload3.rmt_tail);
450    }
451
452    /* Print out each of the internal POW entries. Each entry has a tag, group,
453        wqe, and possibly a next pointer. The next pointer is only valid if this
454        entry isn't make as a tail */
455    for (index=0; index<num_pow_entries; index++)
456    {
457        printf("Entry %d(%-10s): tag=%s,0x%08x grp=%d wqp=0x%016llx", index,
458               __cvmx_pow_list_names[entry_list[index]],
459               OCT_TAG_TYPE_STRING(dump->smemload[index][0].s_smemload0.tag_type),
460               dump->smemload[index][0].s_smemload0.tag,
461               dump->smemload[index][0].s_smemload0.grp,
462               CAST64(dump->smemload[index][2].s_smemload1.wqp));
463        if (dump->smemload[index][0].s_smemload0.tail)
464            printf(" tail");
465        else
466            printf(" next=%d", dump->smemload[index][0].s_smemload0.next_index);
467        if (entry_list[index] >= CVMX_POW_LIST_DESCHED)
468        {
469            printf(" prev=%d", dump->smemload[index][1].s_smemload2.fwd_index);
470            printf(" nosched=%d", dump->smemload[index][1].s_smemload2.nosched);
471            if (dump->smemload[index][1].s_smemload2.pend_switch)
472            {
473                printf(" pending tag=%s,0x%08x",
474                       OCT_TAG_TYPE_STRING(dump->smemload[index][1].s_smemload2.pend_type),
475                       dump->smemload[index][1].s_smemload2.pend_tag);
476            }
477        }
478        printf("\n");
479    }
480
481    printf("POW Display End\n");
482}
483
484