cvmx-pow.c revision 267654
117680Spst/***********************license start***************
217680Spst * Copyright (c) 2003-2010  Cavium Networks (support@cavium.com). All rights
317680Spst * reserved.
417680Spst *
517680Spst *
617680Spst * Redistribution and use in source and binary forms, with or without
717680Spst * modification, are permitted provided that the following conditions are
817680Spst * met:
917680Spst *
1017680Spst *   * Redistributions of source code must retain the above copyright
1117680Spst *     notice, this list of conditions and the following disclaimer.
1217680Spst *
1317680Spst *   * Redistributions in binary form must reproduce the above
1417680Spst *     copyright notice, this list of conditions and the following
1517680Spst *     disclaimer in the documentation and/or other materials provided
1617680Spst *     with the distribution.
1717680Spst
1817680Spst *   * Neither the name of Cavium Networks nor the names of
1917680Spst *     its contributors may be used to endorse or promote products
2017680Spst *     derived from this software without specific prior written
2117680Spst *     permission.
2217680Spst
2317680Spst * This Software, including technical data, may be subject to U.S. export  control
2417680Spst * laws, including the U.S. Export Administration Act and its  associated
2517680Spst * regulations, and may be subject to export or import  regulations in other
2617680Spst * countries.
2717680Spst
2817680Spst * TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED "AS IS"
2917680Spst * AND WITH ALL FAULTS AND CAVIUM  NETWORKS MAKES NO PROMISES, REPRESENTATIONS OR
3017680Spst * WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY, OR OTHERWISE, WITH RESPECT TO
3117680Spst * THE SOFTWARE, INCLUDING ITS CONDITION, ITS CONFORMITY TO ANY REPRESENTATION OR
3217680Spst * DESCRIPTION, OR THE EXISTENCE OF ANY LATENT OR PATENT DEFECTS, AND CAVIUM
3317680Spst * SPECIFICALLY DISCLAIMS ALL IMPLIED (IF ANY) WARRANTIES OF TITLE,
3417680Spst * MERCHANTABILITY, NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF
3517680Spst * VIRUSES, ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
3617680Spst * CORRESPONDENCE TO DESCRIPTION. THE ENTIRE  RISK ARISING OUT OF USE OR
3717680Spst * PERFORMANCE OF THE SOFTWARE LIES WITH YOU.
3817680Spst ***********************license end**************************************/
3917680Spst
4017680Spst
4117680Spst
4217680Spst
4317680Spst
4417680Spst
4517680Spst
4617680Spst/**
4717680Spst * @file
4817680Spst *
4917680Spst * Interface to the hardware Packet Order / Work unit.
5017680Spst *
5117680Spst * <hr>$Revision: 29727 $<hr>
5217680Spst */
5317680Spst
5417680Spst#include "cvmx.h"
5517680Spst#include "cvmx-pow.h"
5617680Spst
5717680Spst/**
5817680Spst * @INTERNAL
5917680Spst * This structure stores the internal POW state captured by
6017680Spst * cvmx_pow_capture(). It is purposely not exposed to the user
6117680Spst * since the format may change without notice.
6217680Spst */
6317680Spsttypedef struct
6417680Spst{
6517680Spst    cvmx_pow_tag_load_resp_t sstatus[16][8];
6617680Spst    cvmx_pow_tag_load_resp_t smemload[2048][3];
6717680Spst    cvmx_pow_tag_load_resp_t sindexload[16][4];
6817680Spst} __cvmx_pow_dump_t;
6917680Spst
7017680Spsttypedef enum
7117680Spst{
7217680Spst    CVMX_POW_LIST_UNKNOWN=0,
7317680Spst    CVMX_POW_LIST_FREE=1,
7417680Spst    CVMX_POW_LIST_INPUT=2,
7517680Spst    CVMX_POW_LIST_CORE=CVMX_POW_LIST_INPUT+8,
7617680Spst    CVMX_POW_LIST_DESCHED=CVMX_POW_LIST_CORE+16,
7717680Spst    CVMX_POW_LIST_NOSCHED=CVMX_POW_LIST_DESCHED+16,
7817680Spst} __cvmx_pow_list_types_t;
7917680Spst
8017680Spststatic const char *__cvmx_pow_list_names[] = {
8117680Spst    "Unknown",
8217680Spst    "Free List",
8317680Spst    "Queue 0", "Queue 1", "Queue 2", "Queue 3",
8417680Spst    "Queue 4", "Queue 5", "Queue 6", "Queue 7",
8517680Spst    "Core 0", "Core 1", "Core 2", "Core 3",
8617680Spst    "Core 4", "Core 5", "Core 6", "Core 7",
8717680Spst    "Core 8", "Core 9", "Core 10", "Core 11",
8817680Spst    "Core 12", "Core 13", "Core 14", "Core 15",
8917680Spst    "Desched 0", "Desched 1", "Desched 2", "Desched 3",
9017680Spst    "Desched 4", "Desched 5", "Desched 6", "Desched 7",
9117680Spst    "Desched 8", "Desched 9", "Desched 10", "Desched 11",
9217680Spst    "Desched 12", "Desched 13", "Desched 14", "Desched 15",
9317680Spst    "Nosched 0", "Nosched 1", "Nosched 2", "Nosched 3",
9417680Spst    "Nosched 4", "Nosched 5", "Nosched 6", "Nosched 7",
9517680Spst    "Nosched 8", "Nosched 9", "Nosched 10", "Nosched 11",
9617680Spst    "Nosched 12", "Nosched 13", "Nosched 14", "Nosched 15"
9717680Spst};
9817680Spst
9917680Spst
10017680Spst/**
10117680Spst * Return the number of POW entries supported by this chip
10217680Spst *
10317680Spst * @return Number of POW entries
10417680Spst */
10517680Spstint cvmx_pow_get_num_entries(void)
10617680Spst{
10717680Spst    if (OCTEON_IS_MODEL(OCTEON_CN30XX))
10817680Spst        return 64;
10917680Spst    else if (OCTEON_IS_MODEL(OCTEON_CN31XX) || OCTEON_IS_MODEL(OCTEON_CN50XX))
11017680Spst        return 256;
11117680Spst    else if (OCTEON_IS_MODEL(OCTEON_CN52XX))
11217680Spst        return 512;
11317680Spst    else if (OCTEON_IS_MODEL(OCTEON_CN63XX))
11417680Spst	return 1024;
11517680Spst    else
11617680Spst        return 2048;
11717680Spst}
11817680Spst
11917680Spst
12017680Spst/**
12117680Spst * Store the current POW internal state into the supplied
12217680Spst * buffer. It is recommended that you pass a buffer of at least
12317680Spst * 128KB. The format of the capture may change based on SDK
12417680Spst * version and Octeon chip.
12517680Spst *
12617680Spst * @param buffer Buffer to store capture into
12717680Spst * @param buffer_size
12817680Spst *               The size of the supplied buffer
12917680Spst *
13017680Spst * @return Zero on sucess, negative on failure
13117680Spst */
13217680Spstint cvmx_pow_capture(void *buffer, int buffer_size)
13317680Spst{
13417680Spst    __cvmx_pow_dump_t *dump = (__cvmx_pow_dump_t*)buffer;
13517680Spst    int num_cores;
13617680Spst    int num_pow_entries = cvmx_pow_get_num_entries();
13717680Spst    int core;
13817680Spst    int index;
13917680Spst    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