unplugged-kernel/drivers/misc/mediatek/debug_latch/lastbus/mt6779/plat_lastbus.c

396 lines
8.8 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020 MediaTek Inc.
*/
#include "./../lastbus.h"
#include "plat_lastbus.h"
#include <linux/seqlock.h>
#include <linux/irqflags.h>
static DEFINE_SPINLOCK(lastbus_spin_lock);
int infra_set_event(const struct plt_cfg_bus_latch *self, const char *buf)
{
int i;
char *arg;
char *p = (char *)buf;
unsigned long input;
unsigned int settings[PLAT_NUM_INFRA_EVENT_REG];
for (i = 0; i < self->num_infra_event_reg; i++) {
arg = strsep(&p, " ");
if (arg == NULL)
return -EINVAL;
if (kstrtoul(arg, 16, &input) != 0) {
pr_info("%s:%d: kstrtoul fail for %s\n",
__func__, __LINE__, p);
return -1;
}
settings[i] = (unsigned int)input;
}
writel(settings[0],
self->infra_base + self->infrasys_offsets.bus_infra_mask_l);
writel(settings[1],
self->infra_base + self->infrasys_offsets.bus_infra_mask_h);
return 0;
}
int infra_get_event(const struct plt_cfg_bus_latch *self, char *buf)
{
int ret;
ret = snprintf(buf, PAGE_SIZE,
"INFRA_MASKL = 0x%x, INFRA_MASKH = 0x%x\n",
readl(self->infra_base +
self->infrasys_offsets.bus_infra_mask_l),
readl(self->infra_base +
self->infrasys_offsets.bus_infra_mask_h));
return ret;
}
int infra_set_timeout(const struct plt_cfg_bus_latch *self, const char *buf)
{
int i;
char *arg;
char *p = (char *)buf;
unsigned long input;
unsigned int settings[4];
unsigned int infra_timeout;
unsigned int peri_timeout;
for (i = 0; i < 4; i++) {
arg = strsep(&p, " ");
if (arg == NULL)
return -EINVAL;
if (kstrtoul(arg, 16, &input) != 0) {
pr_info("%s:%d: kstrtoul fail for %s\n",
__func__, __LINE__, p);
return -1;
}
settings[i] = (unsigned int)input;
}
infra_timeout = readl(self->infra_base +
self->infrasys_offsets.bus_infra_ctrl) & ~(0xffff);
infra_timeout = infra_timeout | settings[2];
writel(infra_timeout, self->infra_base +
self->infrasys_offsets.bus_infra_ctrl);
peri_timeout = readl(self->peri_base +
self->perisys_offsets.bus_peri_r0) & ~0xffff;
peri_timeout = peri_timeout | settings[3];
writel(peri_timeout, self->peri_base +
self->perisys_offsets.bus_peri_r0);
return 0;
}
int infra_get_timeout(const struct plt_cfg_bus_latch *self, char *buf)
{
return snprintf(buf, PAGE_SIZE,
"infra_config=0x%x, peri_config=0x%x\n",
readl(self->infra_base +
self->infrasys_offsets.bus_infra_ctrl),
readl(self->peri_base +
self->perisys_offsets.bus_peri_r0));
}
int peri_set_event(const struct plt_cfg_bus_latch *self, const char *buf)
{
char *p = (char *)buf;
unsigned long input;
if (kstrtoul(p, 16, &input) != 0) {
pr_info("%s:%d: kstrtoul fail for %s\n",
__func__, __LINE__, p);
return -1;
}
writel(input, self->peri_base +
self->perisys_offsets.bus_peri_r1);
return 0;
}
int peri_get_event(const struct plt_cfg_bus_latch *self, char *buf)
{
int ret;
ret = snprintf(buf, PAGE_SIZE, "PERI_MASK = 0x%x\n",
readl(self->peri_base +
self->perisys_offsets.bus_peri_r1));
return ret;
}
int infra_dump(const struct plt_cfg_bus_latch *self, char *buf, int *wp)
{
unsigned int i;
void __iomem *infra_base = self->infra_base;
unsigned int ctrl;
if (self->num_infrasys_mon != 0) {
ctrl = readl(infra_base +
self->infrasys_offsets.bus_infra_ctrl);
if (ctrl & 0xFF000000) {
*wp += snprintf(buf + *wp, LATCH_BUF_LENGTH - *wp,
"[LAST BUS] INFRASYS TIMEOUT:\n");
*wp += snprintf(buf + *wp, LATCH_BUF_LENGTH - *wp,
"[LAST BUS] INFRASYS_CTRL = %04x\n", ctrl);
*wp += snprintf(buf + *wp, LATCH_BUF_LENGTH - *wp,
"[LAST BUS] INFRASYS_MASKL = %04x\n",
readl(infra_base +
self->infrasys_offsets.bus_infra_ctrl));
*wp += snprintf(buf + *wp, LATCH_BUF_LENGTH - *wp,
"[LAST BUS] INFRASYS_MASKH = %04x\n",
readl(infra_base +
self->infrasys_offsets.bus_infra_ctrl));
for (i = 0; i <= self->num_infrasys_mon-1; ++i)
*wp += snprintf(buf + *wp,
LATCH_BUF_LENGTH - *wp,
"INFRA SNAPSHOT%d = %04x\n",
i, readl(infra_base +
self->infrasys_offsets.bus_infra_snapshot
+ 4*i));
} else {
*wp += snprintf(buf + *wp,
LATCH_BUF_LENGTH - *wp,
"[LAST BUS] INFRASYS NO TIMEOUT:\n");
}
}
return 0;
}
static unsigned long preisys_dump_offset[] = {
0x508, /* PERIBUS_DBG0 */
0x50c, /* PERIBUS_DBG1 */
0x510, /* PERIBUS_DBG2 */
0x514, /* PERIBUS_DBG3 */
0x518, /* PERIBUS_DBG4 */
0x51c, /* PERIBUS_DBG5 */
0x520, /* PERIBUS_DBG6 */
0x524, /* PERIBUS_DBG7 */
0x528, /* PERIBUS_DBG8 */
0x52c, /* PERIBUS_DBG9 */
0x530, /* PERIBUS_DBG10 */
0x534, /* PERIBUS_DBG11 */
0x538, /* PERIBUS_DBG12 */
0x53c, /* PERIBUS_DBG13 */
0x580, /* PERIBUS_DBG14 */
0x584, /* PERIBUS_DBG15 */
0x588, /* PERIBUS_DBG16 */
0x58c, /* PERIBUS_DBG17 */
0x590, /* PERIBUS_DBG18 */
0x594, /* PERIBUS_DBG19 */
0x598, /* PERIBUS_DBG20 */
};
int peri_dump(const struct plt_cfg_bus_latch *self, char *buf, int *wp)
{
unsigned int i;
void __iomem *peri_base = self->peri_base;
unsigned long dump_size =
sizeof(preisys_dump_offset) / sizeof(unsigned long);
unsigned __iomem *reg;
unsigned int data;
*wp += snprintf(buf + *wp, LATCH_BUF_LENGTH - *wp,
"[LAST BUS] PERISYS BUS DUMP:\n");
for (i = 0; i < dump_size; ++i) {
reg = peri_base + preisys_dump_offset[i];
data = readl(reg);
*wp += snprintf(buf + *wp, LATCH_BUF_LENGTH - *wp,
"PERIBUS_DBG%d(%p) = %04x\n",
i, reg, data);
}
return 0;
}
struct plt_cfg_bus_latch cfg_bus_latch = {
.supported = 1,
.num_perisys_mon = 21,
.num_infrasys_mon = 48,
.num_infra_event_reg = PLAT_NUM_INFRA_EVENT_REG,
.num_peri_event_reg = PLAT_NUM_PERI_EVENT_REG,
.perisys_offsets = {
.bus_peri_r0 = 0x0500,
.bus_peri_r1 = 0x0504,
.bus_peri_mon = 0x0508,
},
.infrasys_offsets = {
.bus_infra_ctrl = 0x0d04,
.bus_infra_mask_l = 0x0d00,
.bus_infra_mask_h = 0x0df0,
.bus_infra_snapshot = 0x0d08,
},
.perisys_ops = {
.dump = peri_dump,
.set_event = peri_set_event,
.get_event = peri_get_event,
},
.infrasys_ops = {
.dump = infra_dump,
.set_event = infra_set_event,
.get_event = infra_get_event,
.set_timeout = infra_set_timeout,
.get_timeout = infra_get_timeout,
},
};
struct plt_cfg_bus_latch *lb = &cfg_bus_latch;
int infra_timeout_dump(void)
{
unsigned int i;
void __iomem *infra_base = lb->infra_base;
pr_info("[LAST BUS] PERISYS BUS & INFRA BUS DUMP:\n");
/* infrabus_dbg_in_0 */
pr_info("%08x\n", readl(infra_base +
lb->infrasys_offsets.bus_infra_mask_l));
/* infrabus_dbg_mask_2 */
pr_info("%08x\n", readl(infra_base +
lb->infrasys_offsets.bus_infra_mask_h));
/* infrabus_dbg_in_1 */
pr_info("%08x\n", readl(infra_base +
lb->infrasys_offsets.bus_infra_ctrl));
if (lb->num_infrasys_mon != 0) {
for (i = 0; i <= lb->num_infrasys_mon-1; ++i)
pr_info("%08x\n",
readl(infra_base +
lb->infrasys_offsets.bus_infra_snapshot
+ 4*i));
}
return 0;
}
int peri_timeout_dump(void)
{
unsigned int i;
void __iomem *peri_base = lb->peri_base;
unsigned long dump_size =
sizeof(preisys_dump_offset) / sizeof(unsigned long);
unsigned __iomem *reg;
unsigned int data;
/* peribus_dbg_in_0 */
pr_info("%08x\n", readl(peri_base +
lb->perisys_offsets.bus_peri_r0));
/* peribus_dbg_in_1 */
pr_info("%08x\n", readl(peri_base +
lb->perisys_offsets.bus_peri_r1));
for (i = 0; i < dump_size; ++i) {
reg = peri_base + preisys_dump_offset[i];
data = readl(reg);
pr_info("%08x\n", data);
}
return 0;
}
int is_infra_timeout(void)
{
int ctrl = 0;
if (!lb->infra_base) {
pr_info("%s:%d: not ready\n", __func__, __LINE__);
return 0;
}
ctrl = readl(lb->infra_base +
lb->infrasys_offsets.bus_infra_ctrl);
return ctrl & 0x1;
}
int is_peri_timeout(void)
{
int ctrl = 0;
if (!lb->peri_base) {
pr_info("%s:%d: not ready\n", __func__, __LINE__);
return 0;
}
ctrl = readl(lb->peri_base +
lb->perisys_offsets.bus_peri_r1);
return ctrl & 0x1;
}
void reset_infra(void)
{
writel(0x8, lb->infra_base + lb->infrasys_offsets.bus_infra_ctrl);
writel(0xffff000c,
lb->infra_base + lb->infrasys_offsets.bus_infra_ctrl);
}
void reset_peri(void)
{
writel(0x0, lb->peri_base + lb->perisys_offsets.bus_peri_r0);
writel(0x8, lb->peri_base + lb->perisys_offsets.bus_peri_r1);
writel(0x3fff, lb->peri_base + lb->perisys_offsets.bus_peri_r0);
writel(0xc, lb->peri_base + lb->perisys_offsets.bus_peri_r1);
}
int lastbus_timeout_dump(void)
{
int infra = 0, peri = 0;
unsigned long flags;
spin_lock_irqsave(&lastbus_spin_lock, flags);
if (!lb->peri_base || !lb->infra_base) {
pr_info("%s:%d: not ready\n", __func__, __LINE__);
spin_unlock_irqrestore(&lastbus_spin_lock, flags);
return -1;
}
infra = is_infra_timeout();
peri = is_peri_timeout();
if (infra | peri) {
infra_timeout_dump();
peri_timeout_dump();
reset_infra();
reset_peri();
}
spin_unlock_irqrestore(&lastbus_spin_lock, flags);
return infra | peri;
}
static int __init plt_lastbus_init(void)
{
lastbus_setup(&cfg_bus_latch);
return 0;
}
late_initcall(plt_lastbus_init);