/* D11 macdbg functions for Broadcom 802.11abgn
|
* Networking Adapter Device Drivers.
|
*
|
* Broadcom Proprietary and Confidential. Copyright (C) 2020,
|
* All Rights Reserved.
|
*
|
* This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom;
|
* the contents of this file may not be disclosed to third parties,
|
* copied or duplicated in any form, in whole or in part, without
|
* the prior written permission of Broadcom.
|
*
|
*
|
* <<Broadcom-WL-IPTag/Proprietary:>>
|
*
|
* $Id: dhd_macdbg.c 670412 2016-11-15 20:01:18Z shinuk $
|
*/
|
|
#ifdef BCMDBG
|
#include <typedefs.h>
|
#include <osl.h>
|
|
#include <bcmutils.h>
|
#include <dhd_dbg.h>
|
#include <dhd_macdbg.h>
|
#include "d11reglist_proto.h"
|
#include "dhdioctl.h"
|
#include <sdiovar.h>
|
|
#ifdef BCMDBUS
|
#include <dbus.h>
|
#define BUS_IOVAR_OP(a, b, c, d, e, f, g) dbus_iovar_op(a->dbus, b, c, d, e, f, g)
|
#else
|
#include <dhd_bus.h>
|
#define BUS_IOVAR_OP dhd_bus_iovar_op
|
#endif
|
|
typedef struct _macdbg_info_t {
|
dhd_pub_t *dhdp;
|
d11regs_list_t *pd11regs;
|
uint16 d11regs_sz;
|
d11regs_list_t *pd11regs_x;
|
uint16 d11regsx_sz;
|
svmp_list_t *psvmpmems;
|
uint16 svmpmems_sz;
|
} macdbg_info_t;
|
|
#define SVMPLIST_HARDCODE
|
|
int
|
dhd_macdbg_attach(dhd_pub_t *dhdp)
|
{
|
macdbg_info_t *macdbg_info = MALLOCZ(dhdp->osh, sizeof(*macdbg_info));
|
#ifdef SVMPLIST_HARDCODE
|
svmp_list_t svmpmems[] = {
|
{0x20000, 256},
|
{0x21e10, 16},
|
{0x20300, 16},
|
{0x20700, 16},
|
{0x20b00, 16},
|
{0x20be0, 16},
|
{0x20bff, 16},
|
{0xc000, 32},
|
{0xe000, 32},
|
{0x10000, 0x8000},
|
{0x18000, 0x8000}
|
};
|
#endif /* SVMPLIST_HARDCODE */
|
|
if (macdbg_info == NULL) {
|
return BCME_NOMEM;
|
}
|
dhdp->macdbg_info = macdbg_info;
|
macdbg_info->dhdp = dhdp;
|
|
#ifdef SVMPLIST_HARDCODE
|
macdbg_info->psvmpmems = MALLOCZ(dhdp->osh, sizeof(svmpmems));
|
if (macdbg_info->psvmpmems == NULL) {
|
return BCME_NOMEM;
|
}
|
|
macdbg_info->svmpmems_sz = ARRAYSIZE(svmpmems);
|
memcpy(macdbg_info->psvmpmems, svmpmems, sizeof(svmpmems));
|
|
DHD_ERROR(("%s: psvmpmems %p svmpmems_sz %d\n",
|
__FUNCTION__, macdbg_info->psvmpmems, macdbg_info->svmpmems_sz));
|
#endif
|
return BCME_OK;
|
}
|
|
void
|
dhd_macdbg_detach(dhd_pub_t *dhdp)
|
{
|
macdbg_info_t *macdbg_info = dhdp->macdbg_info;
|
ASSERT(macdbg_info);
|
|
if (macdbg_info->pd11regs) {
|
ASSERT(macdbg_info->d11regs_sz > 0);
|
MFREE(dhdp->osh, macdbg_info->pd11regs,
|
(macdbg_info->d11regs_sz * sizeof(macdbg_info->pd11regs[0])));
|
macdbg_info->d11regs_sz = 0;
|
}
|
if (macdbg_info->pd11regs_x) {
|
ASSERT(macdbg_info->d11regsx_sz > 0);
|
MFREE(dhdp->osh, macdbg_info->pd11regs_x,
|
(macdbg_info->d11regsx_sz * sizeof(macdbg_info->pd11regs_x[0])));
|
macdbg_info->d11regsx_sz = 0;
|
}
|
if (macdbg_info->psvmpmems) {
|
ASSERT(macdbg_info->svmpmems_sz > 0);
|
MFREE(dhdp->osh, macdbg_info->psvmpmems,
|
(macdbg_info->svmpmems_sz * sizeof(macdbg_info->psvmpmems[0])));
|
macdbg_info->svmpmems_sz = 0;
|
}
|
MFREE(dhdp->osh, macdbg_info, sizeof(*macdbg_info));
|
}
|
|
void
|
dhd_macdbg_event_handler(dhd_pub_t *dhdp, uint32 reason,
|
uint8 *event_data, uint32 datalen)
|
{
|
d11regs_list_t *pd11regs;
|
macdbg_info_t *macdbg_info = dhdp->macdbg_info;
|
uint d11regs_sz;
|
|
DHD_TRACE(("%s: reason %d datalen %d\n", __FUNCTION__, reason, datalen));
|
switch (reason) {
|
case WLC_E_MACDBG_LIST_PSMX:
|
/* Fall through */
|
case WLC_E_MACDBG_LIST_PSM:
|
pd11regs = MALLOCZ(dhdp->osh, datalen);
|
if (pd11regs == NULL) {
|
DHD_ERROR(("%s: NOMEM for len %d\n", __FUNCTION__, datalen));
|
return;
|
}
|
memcpy(pd11regs, event_data, datalen);
|
d11regs_sz = datalen / sizeof(pd11regs[0]);
|
DHD_ERROR(("%s: d11regs %p d11regs_sz %d\n",
|
__FUNCTION__, pd11regs, d11regs_sz));
|
if (reason == WLC_E_MACDBG_LIST_PSM) {
|
macdbg_info->pd11regs = pd11regs;
|
macdbg_info->d11regs_sz = (uint16)d11regs_sz;
|
} else {
|
macdbg_info->pd11regs_x = pd11regs;
|
macdbg_info->d11regsx_sz = (uint16)d11regs_sz;
|
}
|
break;
|
case WLC_E_MACDBG_REGALL:
|
#ifdef LINUX
|
/* Schedule to work queue as this context could be ISR */
|
dhd_schedule_macdbg_dump(dhdp);
|
#else
|
/* Dump PSMr */
|
(void) dhd_macdbg_dumpmac(dhdp, NULL, 0, NULL, FALSE);
|
/* Dump PSMx */
|
(void) dhd_macdbg_dumpmac(dhdp, NULL, 0, NULL, TRUE);
|
/* Dump SVMP mems */
|
(void) dhd_macdbg_dumpsvmp(dhdp, NULL, 0, NULL);
|
#endif
|
break;
|
default:
|
DHD_ERROR(("%s: Unknown reason %d\n",
|
__FUNCTION__, reason));
|
}
|
return;
|
}
|
|
static uint16
|
_dhd_get_ihr16(macdbg_info_t *macdbg_info, uint16 addr, struct bcmstrbuf *b, bool verbose)
|
{
|
sdreg_t sdreg;
|
uint16 val;
|
|
sdreg.func = 2;
|
sdreg.offset = (0x1000 | addr);
|
BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg",
|
&sdreg, sizeof(sdreg), &val, sizeof(val), IOV_GET);
|
if (verbose) {
|
if (b) {
|
bcm_bprintf(b, "DEBUG: IHR16: read 0x%08x, size 2, value 0x%04x\n",
|
(addr + 0x18001000), val);
|
} else {
|
printf("DEBUG: IHR16: read 0x%08x, size 2, value 0x%04x\n",
|
(addr + 0x18001000), val);
|
}
|
}
|
return val;
|
}
|
|
static uint32
|
_dhd_get_ihr32(macdbg_info_t *macdbg_info, uint16 addr, struct bcmstrbuf *b, bool verbose)
|
{
|
sdreg_t sdreg;
|
uint32 val;
|
|
sdreg.func = 4;
|
sdreg.offset = (0x1000 | addr);
|
BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg",
|
&sdreg, sizeof(sdreg), &val, sizeof(val), IOV_GET);
|
if (verbose) {
|
if (b) {
|
bcm_bprintf(b, "DEBUG: IHR32: read 0x%08x, size 4, value 0x%08x\n",
|
(addr + 0x18001000), val);
|
} else {
|
printf("DEBUG: IHR32: read 0x%08x, size 4, value 0x%08x\n",
|
(addr + 0x18001000), val);
|
}
|
}
|
return val;
|
}
|
|
static void
|
_dhd_set_ihr16(macdbg_info_t *macdbg_info, uint16 addr, uint16 val,
|
struct bcmstrbuf *b, bool verbose)
|
{
|
sdreg_t sdreg;
|
|
sdreg.func = 2;
|
sdreg.offset = (0x1000 | addr);
|
sdreg.value = val;
|
|
if (verbose) {
|
if (b) {
|
bcm_bprintf(b, "DEBUG: IHR16: write 0x%08x, size 2, value 0x%04x\n",
|
(addr + 0x18001000), val);
|
} else {
|
printf("DEBUG: IHR16: write 0x%08x, size 2, value 0x%04x\n",
|
(addr + 0x18001000), val);
|
}
|
}
|
BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg",
|
NULL, 0, &sdreg, sizeof(sdreg), IOV_SET);
|
}
|
|
static void
|
_dhd_set_ihr32(macdbg_info_t *macdbg_info, uint16 addr, uint32 val,
|
struct bcmstrbuf *b, bool verbose)
|
{
|
sdreg_t sdreg;
|
|
sdreg.func = 4;
|
sdreg.offset = (0x1000 | addr);
|
sdreg.value = val;
|
|
if (verbose) {
|
if (b) {
|
bcm_bprintf(b, "DEBUG: IHR32: write 0x%08x, size 4, value 0x%08x\n",
|
(addr + 0x18001000), val);
|
} else {
|
printf("DEBUG: IHR32: write 0x%08x, size 4, value 0x%08x\n",
|
(addr + 0x18001000), val);
|
}
|
}
|
BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg",
|
NULL, 0, &sdreg, sizeof(sdreg), IOV_SET);
|
}
|
|
static uint32
|
_dhd_get_d11obj32(macdbg_info_t *macdbg_info, uint16 objaddr, uint32 sel,
|
struct bcmstrbuf *b, bool verbose)
|
{
|
uint32 val;
|
sdreg_t sdreg;
|
sdreg.func = 4; // 4bytes by default.
|
sdreg.offset = 0x1160;
|
|
if (objaddr == 0xffff) {
|
if (verbose) {
|
goto objaddr_read;
|
} else {
|
goto objdata_read;
|
}
|
}
|
|
if (objaddr & 0x3) {
|
printf("%s: ERROR! Invalid addr 0x%x\n", __FUNCTION__, objaddr);
|
}
|
|
sdreg.value = (sel | (objaddr >> 2));
|
|
if (verbose) {
|
if (b) {
|
bcm_bprintf(b, "DEBUG: %s: Indirect: write 0x%08x, size %d, value 0x%08x\n",
|
(sel & 0x00020000) ? "SCR":"SHM",
|
(sdreg.offset + 0x18000000), sdreg.func, sdreg.value);
|
} else {
|
printf("DEBUG: %s: Indirect: write 0x%08x, size %d, value 0x%08x\n",
|
(sel & 0x00020000) ? "SCR":"SHM",
|
(sdreg.offset + 0x18000000), sdreg.func, sdreg.value);
|
}
|
}
|
BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg",
|
NULL, 0, &sdreg, sizeof(sdreg), IOV_SET);
|
|
objaddr_read:
|
/* Give some time to obj addr register */
|
BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg",
|
&sdreg, sizeof(sdreg), &val, sizeof(val), IOV_GET);
|
if (verbose) {
|
if (b) {
|
bcm_bprintf(b, "DEBUG: %s: Indirect: Read 0x%08x, size %d, value 0x%08x\n",
|
(sel & 0x00020000) ? "SCR":"SHM",
|
(sdreg.offset + 0x18000000), sdreg.func, val);
|
} else {
|
printf("DEBUG: %s: Indirect: Read 0x%08x, size %d, value 0x%08x\n",
|
(sel & 0x00020000) ? "SCR":"SHM",
|
(sdreg.offset + 0x18000000), sdreg.func, val);
|
}
|
}
|
|
objdata_read:
|
sdreg.offset = 0x1164;
|
BUS_IOVAR_OP(macdbg_info->dhdp, "sbreg",
|
&sdreg, sizeof(sdreg), &val, sizeof(val), IOV_GET);
|
if (verbose) {
|
if (b) {
|
bcm_bprintf(b, "DEBUG: %s: Indirect: Read 0x%08x, size %d, value 0x%04x\n",
|
(sel & 0x00020000) ? "SCR":"SHM",
|
(sdreg.offset + 0x18000000), sdreg.func, val);
|
} else {
|
printf("DEBUG: %s: Indirect: Read 0x%08x, size %d, value 0x%04x\n",
|
(sel & 0x00020000) ? "SCR":"SHM",
|
(sdreg.offset + 0x18000000), sdreg.func, val);
|
}
|
}
|
return val;
|
}
|
|
static uint16
|
_dhd_get_d11obj16(macdbg_info_t *macdbg_info, uint16 objaddr,
|
uint32 sel, d11obj_cache_t *obj_cache, struct bcmstrbuf *b, bool verbose)
|
{
|
uint32 val;
|
if (obj_cache && obj_cache->cache_valid && ((obj_cache->sel ^ sel) & (0xffffff)) == 0) {
|
if (obj_cache->addr32 == (objaddr & ~0x3)) {
|
/* XXX: Same objaddr read as the previous one */
|
if (verbose) {
|
if (b) {
|
bcm_bprintf(b, "DEBUG: %s: Read cache value: "
|
"addr32 0x%04x, sel 0x%08x, value 0x%08x\n",
|
(sel & 0x00020000) ? "SCR":"SHM",
|
obj_cache->addr32, obj_cache->sel, obj_cache->val);
|
} else {
|
printf("DEBUG: %s: Read cache value: "
|
"addr32 0x%04x, sel 0x%08x, value 0x%08x\n",
|
(sel & 0x00020000) ? "SCR":"SHM",
|
obj_cache->addr32, obj_cache->sel, obj_cache->val);
|
}
|
}
|
val = obj_cache->val;
|
goto exit;
|
} else if ((obj_cache->sel & 0x02000000) &&
|
(obj_cache->addr32 + 4 == (objaddr & ~0x3))) {
|
/* XXX: objaddr is auto incrementing, so just read objdata */
|
if (verbose) {
|
if (b) {
|
bcm_bprintf(b, "DEBUG: %s: Read objdata only: "
|
"addr32 0x%04x, sel 0x%08x, value 0x%08x\n",
|
(sel & 0x00020000) ? "SCR":"SHM",
|
obj_cache->addr32, obj_cache->sel, obj_cache->val);
|
} else {
|
printf("DEBUG: %s: Read objdata only: "
|
"addr32 0x%04x, sel 0x%08x, value 0x%08x\n",
|
(sel & 0x00020000) ? "SCR":"SHM",
|
obj_cache->addr32, obj_cache->sel, obj_cache->val);
|
}
|
}
|
val = _dhd_get_d11obj32(macdbg_info, 0xffff, sel, b, verbose);
|
goto exit;
|
}
|
}
|
val = _dhd_get_d11obj32(macdbg_info, (objaddr & ~0x2), sel, b, verbose);
|
exit:
|
if (obj_cache) {
|
obj_cache->addr32 = (objaddr & ~0x3);
|
obj_cache->sel = sel;
|
obj_cache->val = val;
|
obj_cache->cache_valid = TRUE;
|
}
|
return (uint16)((objaddr & 0x2) ? (val >> 16) : val);
|
}
|
|
static int
|
_dhd_print_d11reg(macdbg_info_t *macdbg_info, int idx, int type, uint16 addr, struct bcmstrbuf *b,
|
d11obj_cache_t *obj_cache, bool verbose)
|
{
|
const char *regname[D11REG_TYPE_MAX] = D11REGTYPENAME;
|
uint32 val;
|
|
if (type == D11REG_TYPE_IHR32) {
|
if ((addr & 0x3)) {
|
printf("%s: ERROR! Invalid addr 0x%x\n", __FUNCTION__, addr);
|
addr &= ~0x3;
|
}
|
val = _dhd_get_ihr32(macdbg_info, addr, b, verbose);
|
if (b) {
|
bcm_bprintf(b, "%-3d %s 0x%-4x = 0x%-8x\n",
|
idx, regname[type], addr, val);
|
} else {
|
printf("%-3d %s 0x%-4x = 0x%-8x\n",
|
idx, regname[type], addr, val);
|
}
|
} else {
|
switch (type) {
|
case D11REG_TYPE_IHR16: {
|
if ((addr & 0x1)) {
|
printf("%s: ERROR! Invalid addr 0x%x\n", __FUNCTION__, addr);
|
addr &= ~0x1;
|
}
|
val = _dhd_get_ihr16(macdbg_info, addr, b, verbose);
|
break;
|
}
|
case D11REG_TYPE_IHRX16:
|
val = _dhd_get_d11obj16(macdbg_info, (addr - 0x400) << 1, 0x020b0000,
|
obj_cache, b, verbose);
|
break;
|
case D11REG_TYPE_SCR:
|
val = _dhd_get_d11obj16(macdbg_info, addr << 2, 0x02020000,
|
obj_cache, b, verbose);
|
break;
|
case D11REG_TYPE_SCRX:
|
val = _dhd_get_d11obj16(macdbg_info, addr << 2, 0x020a0000,
|
obj_cache, b, verbose);
|
break;
|
case D11REG_TYPE_SHM:
|
val = _dhd_get_d11obj16(macdbg_info, addr, 0x02010000,
|
obj_cache, b, verbose);
|
break;
|
case D11REG_TYPE_SHMX:
|
val = _dhd_get_d11obj16(macdbg_info, addr, 0x02090000,
|
obj_cache, b, verbose);
|
break;
|
default:
|
printf("Unrecognized type %d!\n", type);
|
return 0;
|
}
|
if (b) {
|
bcm_bprintf(b, "%-3d %s 0x%-4x = 0x%-4x\n",
|
idx, regname[type], addr, val);
|
} else {
|
printf("%-3d %s 0x%-4x = 0x%-4x\n",
|
idx, regname[type], addr, val);
|
}
|
}
|
return 1;
|
}
|
|
static int
|
_dhd_print_d11regs(macdbg_info_t *macdbg_info, d11regs_list_t *pregs,
|
int start_idx, struct bcmstrbuf *b, bool verbose)
|
{
|
uint16 addr;
|
int idx = 0;
|
d11obj_cache_t obj_cache = {0, 0, 0, FALSE};
|
|
addr = pregs->addr;
|
if (pregs->type >= D11REG_TYPE_MAX) {
|
printf("%s: wrong type %d\n", __FUNCTION__, pregs->type);
|
return 0;
|
}
|
if (pregs->bitmap) {
|
while (pregs->bitmap) {
|
if (pregs->bitmap && (pregs->bitmap & 0x1)) {
|
_dhd_print_d11reg(macdbg_info, (idx + start_idx), pregs->type,
|
addr, b, &obj_cache, verbose);
|
idx++;
|
}
|
pregs->bitmap = pregs->bitmap >> 1;
|
addr += pregs->step;
|
}
|
} else {
|
for (; idx < pregs->cnt; idx++) {
|
_dhd_print_d11reg(macdbg_info, (idx + start_idx), pregs->type,
|
addr, b, &obj_cache, verbose);
|
addr += pregs->step;
|
}
|
}
|
return idx;
|
}
|
|
static int
|
_dhd_pd11regs_bylist(macdbg_info_t *macdbg_info, d11regs_list_t *reglist,
|
uint16 reglist_sz, struct bcmstrbuf *b)
|
{
|
uint i, idx = 0;
|
|
if (reglist != NULL && reglist_sz > 0) {
|
for (i = 0; i < reglist_sz; i++) {
|
DHD_TRACE(("%s %d %p %d\n", __FUNCTION__, __LINE__,
|
®list[i], reglist_sz));
|
idx += _dhd_print_d11regs(macdbg_info, ®list[i], idx, b, FALSE);
|
}
|
}
|
return idx;
|
}
|
|
int
|
dhd_macdbg_dumpmac(dhd_pub_t *dhdp, char *buf, int buflen,
|
int *outbuflen, bool dump_x)
|
{
|
macdbg_info_t *macdbg_info = dhdp->macdbg_info;
|
struct bcmstrbuf *b = NULL;
|
struct bcmstrbuf bcmstrbuf;
|
uint cnt = 0;
|
|
DHD_TRACE(("%s %d %p %d %p %d %p %d\n", __FUNCTION__, __LINE__,
|
buf, buflen, macdbg_info->pd11regs, macdbg_info->d11regs_sz,
|
macdbg_info->pd11regs_x, macdbg_info->d11regsx_sz));
|
|
if (buf && buflen > 0) {
|
bcm_binit(&bcmstrbuf, buf, buflen);
|
b = &bcmstrbuf;
|
}
|
if (!dump_x) {
|
/* Dump PSMr */
|
cnt += _dhd_pd11regs_bylist(macdbg_info, macdbg_info->pd11regs,
|
macdbg_info->d11regs_sz, b);
|
} else {
|
/* Dump PSMx */
|
cnt += _dhd_pd11regs_bylist(macdbg_info, macdbg_info->pd11regs_x,
|
macdbg_info->d11regsx_sz, b);
|
}
|
|
if (b && outbuflen) {
|
if ((uint)buflen > BCMSTRBUF_LEN(b)) {
|
*outbuflen = buflen - BCMSTRBUF_LEN(b);
|
} else {
|
DHD_ERROR(("%s: buflen insufficient!\n", __FUNCTION__));
|
*outbuflen = buflen;
|
/* Do not return buftooshort to allow printing macregs we have got */
|
}
|
}
|
|
return ((cnt > 0) ? BCME_OK : BCME_UNSUPPORTED);
|
}
|
|
int
|
dhd_macdbg_pd11regs(dhd_pub_t *dhdp, char *params, int plen, char *buf, int buflen)
|
{
|
macdbg_info_t *macdbg_info = dhdp->macdbg_info;
|
dhd_pd11regs_param *pd11regs = (void *)params;
|
dhd_pd11regs_buf *pd11regs_buf = (void *)buf;
|
uint16 start_idx;
|
bool verbose;
|
d11regs_list_t reglist;
|
struct bcmstrbuf *b = NULL;
|
struct bcmstrbuf bcmstrbuf;
|
|
start_idx = pd11regs->start_idx;
|
verbose = pd11regs->verbose;
|
memcpy(®list, pd11regs->plist, sizeof(reglist));
|
memset(buf, '\0', buflen);
|
bcm_binit(&bcmstrbuf, (char *)(pd11regs_buf->pbuf),
|
(buflen - OFFSETOF(dhd_pd11regs_buf, pbuf)));
|
b = &bcmstrbuf;
|
pd11regs_buf->idx = (uint16)_dhd_print_d11regs(macdbg_info, ®list,
|
start_idx, b, verbose);
|
|
return ((pd11regs_buf->idx > 0) ? BCME_OK : BCME_ERROR);
|
}
|
|
int
|
dhd_macdbg_reglist(dhd_pub_t *dhdp, char *buf, int buflen)
|
{
|
int err, desc_idx = 0;
|
dhd_maclist_t *maclist = (dhd_maclist_t *)buf;
|
macdbg_info_t *macdbg_info = dhdp->macdbg_info;
|
void *xtlvbuf_p = maclist->plist;
|
uint16 xtlvbuflen = (uint16)buflen;
|
xtlv_desc_t xtlv_desc[] = {
|
{0, 0, NULL},
|
{0, 0, NULL},
|
{0, 0, NULL},
|
{0, 0, NULL}
|
};
|
|
if (!macdbg_info->pd11regs) {
|
err = BCME_NOTFOUND;
|
goto exit;
|
}
|
ASSERT(macdbg_info->d11regs_sz > 0);
|
xtlv_desc[desc_idx].type = DHD_MACLIST_XTLV_R;
|
xtlv_desc[desc_idx].len =
|
macdbg_info->d11regs_sz * (uint16)sizeof(*(macdbg_info->pd11regs));
|
xtlv_desc[desc_idx].ptr = macdbg_info->pd11regs;
|
desc_idx++;
|
|
if (macdbg_info->pd11regs_x) {
|
ASSERT(macdbg_info->d11regsx_sz);
|
xtlv_desc[desc_idx].type = DHD_MACLIST_XTLV_X;
|
xtlv_desc[desc_idx].len = macdbg_info->d11regsx_sz *
|
(uint16)sizeof(*(macdbg_info->pd11regs_x));
|
xtlv_desc[desc_idx].ptr = macdbg_info->pd11regs_x;
|
desc_idx++;
|
}
|
|
if (macdbg_info->psvmpmems) {
|
ASSERT(macdbg_info->svmpmems_sz);
|
xtlv_desc[desc_idx].type = DHD_SVMPLIST_XTLV;
|
xtlv_desc[desc_idx].len = macdbg_info->svmpmems_sz *
|
(uint16)sizeof(*(macdbg_info->psvmpmems));
|
xtlv_desc[desc_idx].ptr = macdbg_info->psvmpmems;
|
desc_idx++;
|
}
|
|
err = bcm_pack_xtlv_buf_from_mem((uint8 **)&xtlvbuf_p, &xtlvbuflen,
|
xtlv_desc, BCM_XTLV_OPTION_ALIGN32);
|
|
maclist->version = 0; /* No version control for now anyway */
|
maclist->bytes_len = (buflen - xtlvbuflen);
|
|
exit:
|
return err;
|
}
|
|
static int
|
_dhd_print_svmps(macdbg_info_t *macdbg_info, svmp_list_t *psvmp,
|
int start_idx, struct bcmstrbuf *b, bool verbose)
|
{
|
int idx;
|
uint32 addr, mem_id, offset, prev_mem_id, prev_offset;
|
uint16 cnt, val;
|
|
BCM_REFERENCE(start_idx);
|
|
/* Set tbl ID and tbl offset. */
|
_dhd_set_ihr32(macdbg_info, 0x3fc, 0x30000d, b, verbose);
|
_dhd_set_ihr32(macdbg_info, 0x3fc, 0x8000000e, b, verbose);
|
|
addr = psvmp->addr;
|
cnt = psvmp->cnt;
|
|
/* In validate previous mem_id and offset */
|
prev_mem_id = (uint32)(-1);
|
prev_offset = (uint32)(-1);
|
|
for (idx = 0; idx < cnt; idx++, addr++) {
|
mem_id = (addr >> 15);
|
offset = (addr & 0x7fff) >> 1;
|
|
if (mem_id != prev_mem_id) {
|
/* Set mem_id */
|
_dhd_set_ihr32(macdbg_info, 0x3fc, ((mem_id & 0xffff0000) | 0x10),
|
b, verbose);
|
_dhd_set_ihr32(macdbg_info, 0x3fc, ((mem_id << 16) | 0xf),
|
b, verbose);
|
}
|
|
if (offset != prev_offset) {
|
/* XXX: Is this needed?
|
* _dhd_set_ihr32(macdbg_info, 0x3fc, 0x30000d, b, verbose);
|
*/
|
/* svmp offset */
|
_dhd_set_ihr32(macdbg_info, 0x3fc, ((offset << 16) | 0xe),
|
b, verbose);
|
}
|
/* Read hi or lo */
|
_dhd_set_ihr16(macdbg_info, 0x3fc, ((addr & 0x1) ? 0x10 : 0xf), b, verbose);
|
val = _dhd_get_ihr16(macdbg_info, 0x3fe, b, verbose);
|
if (b) {
|
bcm_bprintf(b, "0x%-4x 0x%-4x\n",
|
addr, val);
|
|
} else {
|
printf("0x%-4x 0x%-4x\n",
|
addr, val);
|
}
|
prev_mem_id = mem_id;
|
prev_offset = offset;
|
}
|
return idx;
|
}
|
|
static int
|
_dhd_psvmps_bylist(macdbg_info_t *macdbg_info, svmp_list_t *svmplist,
|
uint16 svmplist_sz, struct bcmstrbuf *b)
|
{
|
uint i, idx = 0;
|
|
if (svmplist != NULL && svmplist_sz > 0) {
|
for (i = 0; i < svmplist_sz; i++) {
|
DHD_TRACE(("%s %d %p %d\n", __FUNCTION__, __LINE__,
|
&svmplist[i], svmplist_sz));
|
idx += _dhd_print_svmps(macdbg_info, &svmplist[i], idx, b, FALSE);
|
}
|
}
|
return idx;
|
}
|
|
int
|
dhd_macdbg_dumpsvmp(dhd_pub_t *dhdp, char *buf, int buflen,
|
int *outbuflen)
|
{
|
macdbg_info_t *macdbg_info = dhdp->macdbg_info;
|
struct bcmstrbuf *b = NULL;
|
struct bcmstrbuf bcmstrbuf;
|
uint cnt = 0;
|
|
DHD_TRACE(("%s %d %p %d %p %d\n", __FUNCTION__, __LINE__,
|
buf, buflen, macdbg_info->psvmpmems, macdbg_info->svmpmems_sz));
|
|
if (buf && buflen > 0) {
|
bcm_binit(&bcmstrbuf, buf, buflen);
|
b = &bcmstrbuf;
|
}
|
cnt = _dhd_psvmps_bylist(macdbg_info, macdbg_info->psvmpmems,
|
macdbg_info->svmpmems_sz, b);
|
|
if (b && outbuflen) {
|
if ((uint)buflen > BCMSTRBUF_LEN(b)) {
|
*outbuflen = buflen - BCMSTRBUF_LEN(b);
|
} else {
|
DHD_ERROR(("%s: buflen insufficient!\n", __FUNCTION__));
|
*outbuflen = buflen;
|
/* Do not return buftooshort to allow printing macregs we have got */
|
}
|
}
|
|
return ((cnt > 0) ? BCME_OK : BCME_UNSUPPORTED);
|
}
|
|
int
|
dhd_macdbg_psvmpmems(dhd_pub_t *dhdp, char *params, int plen, char *buf, int buflen)
|
{
|
macdbg_info_t *macdbg_info = dhdp->macdbg_info;
|
dhd_pd11regs_param *pd11regs = (void *)params;
|
dhd_pd11regs_buf *pd11regs_buf = (void *)buf;
|
uint16 start_idx;
|
bool verbose;
|
svmp_list_t reglist;
|
struct bcmstrbuf *b = NULL;
|
struct bcmstrbuf bcmstrbuf;
|
|
start_idx = pd11regs->start_idx;
|
verbose = pd11regs->verbose;
|
memcpy(®list, pd11regs->plist, sizeof(reglist));
|
memset(buf, '\0', buflen);
|
bcm_binit(&bcmstrbuf, (char *)(pd11regs_buf->pbuf),
|
(buflen - OFFSETOF(dhd_pd11regs_buf, pbuf)));
|
b = &bcmstrbuf;
|
pd11regs_buf->idx = (uint16)_dhd_print_svmps(macdbg_info, ®list,
|
start_idx, b, verbose);
|
|
return ((pd11regs_buf->idx > 0) ? BCME_OK : BCME_ERROR);
|
}
|
|
#endif /* BCMDBG */
|