add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
obj-y := cfe.o
obj-$(CONFIG_SIBYTE_CFE_CONSOLE) += cfe_console.o
obj-$(CONFIG_SIBYTE_TBPROF) += sb_tbprof.o
EXTRA_CFLAGS += -Werror

View File

@@ -0,0 +1,344 @@
/*
* Copyright (C) 2000, 2001, 2002, 2003 Broadcom Corporation
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/linkage.h>
#include <linux/mm.h>
#include <linux/blkdev.h>
#include <linux/bootmem.h>
#include <linux/pm.h>
#include <linux/smp.h>
#include <asm/bootinfo.h>
#include <asm/reboot.h>
#include <asm/sibyte/board.h>
#include <asm/smp-ops.h>
#include <asm/fw/cfe/cfe_api.h>
#include <asm/fw/cfe/cfe_error.h>
/* Max ram addressable in 32-bit segments */
#ifdef CONFIG_64BIT
#define MAX_RAM_SIZE (~0ULL)
#else
#ifdef CONFIG_HIGHMEM
#ifdef CONFIG_64BIT_PHYS_ADDR
#define MAX_RAM_SIZE (~0ULL)
#else
#define MAX_RAM_SIZE (0xffffffffULL)
#endif
#else
#define MAX_RAM_SIZE (0x1fffffffULL)
#endif
#endif
#define SIBYTE_MAX_MEM_REGIONS 8
phys_t board_mem_region_addrs[SIBYTE_MAX_MEM_REGIONS];
phys_t board_mem_region_sizes[SIBYTE_MAX_MEM_REGIONS];
unsigned int board_mem_region_count;
int cfe_cons_handle;
#ifdef CONFIG_BLK_DEV_INITRD
extern unsigned long initrd_start, initrd_end;
#endif
static void __noreturn cfe_linux_exit(void *arg)
{
int warm = *(int *)arg;
if (smp_processor_id()) {
static int reboot_smp;
/* Don't repeat the process from another CPU */
if (!reboot_smp) {
/* Get CPU 0 to do the cfe_exit */
reboot_smp = 1;
smp_call_function(cfe_linux_exit, arg, 0);
}
} else {
printk("Passing control back to CFE...\n");
cfe_exit(warm, 0);
printk("cfe_exit returned??\n");
}
while (1);
}
static void __noreturn cfe_linux_restart(char *command)
{
static const int zero;
cfe_linux_exit((void *)&zero);
}
static void __noreturn cfe_linux_halt(void)
{
static const int one = 1;
cfe_linux_exit((void *)&one);
}
static __init void prom_meminit(void)
{
u64 addr, size, type; /* regardless of 64BIT_PHYS_ADDR */
int mem_flags = 0;
unsigned int idx;
int rd_flag;
#ifdef CONFIG_BLK_DEV_INITRD
unsigned long initrd_pstart;
unsigned long initrd_pend;
initrd_pstart = CPHYSADDR(initrd_start);
initrd_pend = CPHYSADDR(initrd_end);
if (initrd_start &&
((initrd_pstart > MAX_RAM_SIZE)
|| (initrd_pend > MAX_RAM_SIZE))) {
panic("initrd out of addressable memory");
}
#endif /* INITRD */
for (idx = 0; cfe_enummem(idx, mem_flags, &addr, &size, &type) != CFE_ERR_NOMORE;
idx++) {
rd_flag = 0;
if (type == CFE_MI_AVAILABLE) {
/*
* See if this block contains (any portion of) the
* ramdisk
*/
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start) {
if ((initrd_pstart > addr) &&
(initrd_pstart < (addr + size))) {
add_memory_region(addr,
initrd_pstart - addr,
BOOT_MEM_RAM);
rd_flag = 1;
}
if ((initrd_pend > addr) &&
(initrd_pend < (addr + size))) {
add_memory_region(initrd_pend,
(addr + size) - initrd_pend,
BOOT_MEM_RAM);
rd_flag = 1;
}
}
#endif
if (!rd_flag) {
if (addr > MAX_RAM_SIZE)
continue;
if (addr+size > MAX_RAM_SIZE)
size = MAX_RAM_SIZE - (addr+size) + 1;
/*
* memcpy/__copy_user prefetch, which
* will cause a bus error for
* KSEG/KUSEG addrs not backed by RAM.
* Hence, reserve some padding for the
* prefetch distance.
*/
if (size > 512)
size -= 512;
add_memory_region(addr, size, BOOT_MEM_RAM);
}
board_mem_region_addrs[board_mem_region_count] = addr;
board_mem_region_sizes[board_mem_region_count] = size;
board_mem_region_count++;
if (board_mem_region_count ==
SIBYTE_MAX_MEM_REGIONS) {
/*
* Too many regions. Need to configure more
*/
while(1);
}
}
}
#ifdef CONFIG_BLK_DEV_INITRD
if (initrd_start) {
add_memory_region(initrd_pstart, initrd_pend - initrd_pstart,
BOOT_MEM_RESERVED);
}
#endif
}
#ifdef CONFIG_BLK_DEV_INITRD
static int __init initrd_setup(char *str)
{
char rdarg[64];
int idx;
char *tmp, *endptr;
unsigned long initrd_size;
/* Make a copy of the initrd argument so we can smash it up here */
for (idx = 0; idx < sizeof(rdarg)-1; idx++) {
if (!str[idx] || (str[idx] == ' ')) break;
rdarg[idx] = str[idx];
}
rdarg[idx] = 0;
str = rdarg;
/*
*Initrd location comes in the form "<hex size of ramdisk in bytes>@<location in memory>"
* e.g. initrd=3abfd@80010000. This is set up by the loader.
*/
for (tmp = str; *tmp != '@'; tmp++) {
if (!*tmp) {
goto fail;
}
}
*tmp = 0;
tmp++;
if (!*tmp) {
goto fail;
}
initrd_size = simple_strtoul(str, &endptr, 16);
if (*endptr) {
*(tmp-1) = '@';
goto fail;
}
*(tmp-1) = '@';
initrd_start = simple_strtoul(tmp, &endptr, 16);
if (*endptr) {
goto fail;
}
initrd_end = initrd_start + initrd_size;
printk("Found initrd of %lx@%lx\n", initrd_size, initrd_start);
return 1;
fail:
printk("Bad initrd argument. Disabling initrd\n");
initrd_start = 0;
initrd_end = 0;
return 1;
}
#endif
extern struct plat_smp_ops sb_smp_ops;
extern struct plat_smp_ops bcm1480_smp_ops;
/*
* prom_init is called just after the cpu type is determined, from setup_arch()
*/
void __init prom_init(void)
{
uint64_t cfe_ept, cfe_handle;
unsigned int cfe_eptseal;
int argc = fw_arg0;
char **envp = (char **) fw_arg2;
int *prom_vec = (int *) fw_arg3;
_machine_restart = cfe_linux_restart;
_machine_halt = cfe_linux_halt;
pm_power_off = cfe_linux_halt;
/*
* Check if a loader was used; if NOT, the 4 arguments are
* what CFE gives us (handle, 0, EPT and EPTSEAL)
*/
if (argc < 0) {
cfe_handle = (uint64_t)(long)argc;
cfe_ept = (long)envp;
cfe_eptseal = (uint32_t)(unsigned long)prom_vec;
} else {
if ((int32_t)(long)prom_vec < 0) {
/*
* Old loader; all it gives us is the handle,
* so use the "known" entrypoint and assume
* the seal.
*/
cfe_handle = (uint64_t)(long)prom_vec;
cfe_ept = (uint64_t)((int32_t)0x9fc00500);
cfe_eptseal = CFE_EPTSEAL;
} else {
/*
* Newer loaders bundle the handle/ept/eptseal
* Note: prom_vec is in the loader's useg
* which is still alive in the TLB.
*/
cfe_handle = (uint64_t)((int32_t *)prom_vec)[0];
cfe_ept = (uint64_t)((int32_t *)prom_vec)[2];
cfe_eptseal = (unsigned int)((uint32_t *)prom_vec)[3];
}
}
if (cfe_eptseal != CFE_EPTSEAL) {
/* too early for panic to do any good */
printk("CFE's entrypoint seal doesn't match. Spinning.");
while (1) ;
}
cfe_init(cfe_handle, cfe_ept);
/*
* Get the handle for (at least) prom_putchar, possibly for
* boot console
*/
cfe_cons_handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE);
if (cfe_getenv("LINUX_CMDLINE", arcs_cmdline, CL_SIZE) < 0) {
if (argc >= 0) {
/* The loader should have set the command line */
/* too early for panic to do any good */
printk("LINUX_CMDLINE not defined in cfe.");
while (1) ;
}
}
#ifdef CONFIG_BLK_DEV_INITRD
{
char *ptr;
/* Need to find out early whether we've got an initrd. So scan
the list looking now */
for (ptr = arcs_cmdline; *ptr; ptr++) {
while (*ptr == ' ') {
ptr++;
}
if (!strncmp(ptr, "initrd=", 7)) {
initrd_setup(ptr+7);
break;
} else {
while (*ptr && (*ptr != ' ')) {
ptr++;
}
}
}
}
#endif /* CONFIG_BLK_DEV_INITRD */
/* Not sure this is needed, but it's the safe way. */
arcs_cmdline[CL_SIZE-1] = 0;
prom_meminit();
#if defined(CONFIG_SIBYTE_BCM112X) || defined(CONFIG_SIBYTE_SB1250)
register_smp_ops(&sb_smp_ops);
#endif
#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
register_smp_ops(&bcm1480_smp_ops);
#endif
}
void __init prom_free_prom_memory(void)
{
/* Not sure what I'm supposed to do here. Nothing, I think */
}
void prom_putchar(char c)
{
int ret;
while ((ret = cfe_write(cfe_cons_handle, &c, 1)) == 0)
;
}

View File

@@ -0,0 +1,80 @@
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/console.h>
#include <asm/sibyte/board.h>
#include <asm/fw/cfe/cfe_api.h>
#include <asm/fw/cfe/cfe_error.h>
extern int cfe_cons_handle;
static void cfe_console_write(struct console *cons, const char *str,
unsigned int count)
{
int i, last, written;
for (i=0, last=0; i<count; i++) {
if (!str[i])
/* XXXKW can/should this ever happen? */
return;
if (str[i] == '\n') {
do {
written = cfe_write(cfe_cons_handle, &str[last], i-last);
if (written < 0)
;
last += written;
} while (last < i);
while (cfe_write(cfe_cons_handle, "\r", 1) <= 0)
;
}
}
if (last != count) {
do {
written = cfe_write(cfe_cons_handle, &str[last], count-last);
if (written < 0)
;
last += written;
} while (last < count);
}
}
static int cfe_console_setup(struct console *cons, char *str)
{
char consdev[32];
/* XXXKW think about interaction with 'console=' cmdline arg */
/* If none of the console options are configured, the build will break. */
if (cfe_getenv("BOOT_CONSOLE", consdev, 32) >= 0) {
#ifdef CONFIG_SERIAL_SB1250_DUART
if (!strcmp(consdev, "uart0")) {
setleds("u0cn");
} else if (!strcmp(consdev, "uart1")) {
setleds("u1cn");
} else
#endif
#ifdef CONFIG_VGA_CONSOLE
if (!strcmp(consdev, "pcconsole0")) {
setleds("pccn");
} else
#endif
return -ENODEV;
}
return 0;
}
static struct console sb1250_cfe_cons = {
.name = "cfe",
.write = cfe_console_write,
.setup = cfe_console_setup,
.flags = CON_PRINTBUFFER,
.index = -1,
};
static int __init sb1250_cfe_console_init(void)
{
register_console(&sb1250_cfe_cons);
return 0;
}
console_initcall(sb1250_cfe_console_init);

View File

@@ -0,0 +1,609 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* Copyright (C) 2001, 2002, 2003 Broadcom Corporation
* Copyright (C) 2007 Ralf Baechle <ralf@linux-mips.org>
* Copyright (C) 2007 MIPS Technologies, Inc.
* written by Ralf Baechle <ralf@linux-mips.org>
*/
#undef DEBUG
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/vmalloc.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <asm/io.h>
#include <asm/sibyte/sb1250.h>
#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
#include <asm/sibyte/bcm1480_regs.h>
#include <asm/sibyte/bcm1480_scd.h>
#include <asm/sibyte/bcm1480_int.h>
#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_scd.h>
#include <asm/sibyte/sb1250_int.h>
#else
#error invalid SiByte UART configuation
#endif
#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
#undef K_INT_TRACE_FREEZE
#define K_INT_TRACE_FREEZE K_BCM1480_INT_TRACE_FREEZE
#undef K_INT_PERF_CNT
#define K_INT_PERF_CNT K_BCM1480_INT_PERF_CNT
#endif
#include <asm/system.h>
#include <asm/uaccess.h>
#define SBPROF_TB_MAJOR 240
typedef u64 tb_sample_t[6*256];
enum open_status {
SB_CLOSED,
SB_OPENING,
SB_OPEN
};
struct sbprof_tb {
wait_queue_head_t tb_sync;
wait_queue_head_t tb_read;
struct mutex lock;
enum open_status open;
tb_sample_t *sbprof_tbbuf;
int next_tb_sample;
volatile int tb_enable;
volatile int tb_armed;
};
static struct sbprof_tb sbp;
#define MAX_SAMPLE_BYTES (24*1024*1024)
#define MAX_TBSAMPLE_BYTES (12*1024*1024)
#define MAX_SAMPLES (MAX_SAMPLE_BYTES/sizeof(u_int32_t))
#define TB_SAMPLE_SIZE (sizeof(tb_sample_t))
#define MAX_TB_SAMPLES (MAX_TBSAMPLE_BYTES/TB_SAMPLE_SIZE)
/* ioctls */
#define SBPROF_ZBSTART _IOW('s', 0, int)
#define SBPROF_ZBSTOP _IOW('s', 1, int)
#define SBPROF_ZBWAITFULL _IOW('s', 2, int)
/*
* Routines for using 40-bit SCD cycle counter
*
* Client responsible for either handling interrupts or making sure
* the cycles counter never saturates, e.g., by doing
* zclk_timer_init(0) at least every 2^40 - 1 ZCLKs.
*/
/*
* Configures SCD counter 0 to count ZCLKs starting from val;
* Configures SCD counters1,2,3 to count nothing.
* Must not be called while gathering ZBbus profiles.
*/
#define zclk_timer_init(val) \
__asm__ __volatile__ (".set push;" \
".set mips64;" \
"la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \
"sd %0, 0x10($8);" /* write val to counter0 */ \
"sd %1, 0($8);" /* config counter0 for zclks*/ \
".set pop" \
: /* no outputs */ \
/* enable, counter0 */ \
: /* inputs */ "r"(val), "r" ((1ULL << 33) | 1ULL) \
: /* modifies */ "$8" )
/* Reads SCD counter 0 and puts result in value
unsigned long long val; */
#define zclk_get(val) \
__asm__ __volatile__ (".set push;" \
".set mips64;" \
"la $8, 0xb00204c0;" /* SCD perf_cnt_cfg */ \
"ld %0, 0x10($8);" /* write val to counter0 */ \
".set pop" \
: /* outputs */ "=r"(val) \
: /* inputs */ \
: /* modifies */ "$8" )
#define DEVNAME "sb_tbprof"
#define TB_FULL (sbp.next_tb_sample == MAX_TB_SAMPLES)
/*
* Support for ZBbus sampling using the trace buffer
*
* We use the SCD performance counter interrupt, caused by a Zclk counter
* overflow, to trigger the start of tracing.
*
* We set the trace buffer to sample everything and freeze on
* overflow.
*
* We map the interrupt for trace_buffer_freeze to handle it on CPU 0.
*
*/
static u64 tb_period;
static void arm_tb(void)
{
u64 scdperfcnt;
u64 next = (1ULL << 40) - tb_period;
u64 tb_options = M_SCD_TRACE_CFG_FREEZE_FULL;
/*
* Generate an SCD_PERFCNT interrupt in TB_PERIOD Zclks to
* trigger start of trace. XXX vary sampling period
*/
__raw_writeq(0, IOADDR(A_SCD_PERF_CNT_1));
scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG));
/*
* Unfortunately, in Pass 2 we must clear all counters to knock down
* a previous interrupt request. This means that bus profiling
* requires ALL of the SCD perf counters.
*/
#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
__raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) |
/* keep counters 0,2,3,4,5,6,7 as is */
V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */
IOADDR(A_BCM1480_SCD_PERF_CNT_CFG0));
__raw_writeq(
M_SPC_CFG_ENABLE | /* enable counting */
M_SPC_CFG_CLEAR | /* clear all counters */
V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */
IOADDR(A_BCM1480_SCD_PERF_CNT_CFG1));
#else
__raw_writeq((scdperfcnt & ~M_SPC_CFG_SRC1) |
/* keep counters 0,2,3 as is */
M_SPC_CFG_ENABLE | /* enable counting */
M_SPC_CFG_CLEAR | /* clear all counters */
V_SPC_CFG_SRC1(1), /* counter 1 counts cycles */
IOADDR(A_SCD_PERF_CNT_CFG));
#endif
__raw_writeq(next, IOADDR(A_SCD_PERF_CNT_1));
/* Reset the trace buffer */
__raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
#if 0 && defined(M_SCD_TRACE_CFG_FORCECNT)
/* XXXKW may want to expose control to the data-collector */
tb_options |= M_SCD_TRACE_CFG_FORCECNT;
#endif
__raw_writeq(tb_options, IOADDR(A_SCD_TRACE_CFG));
sbp.tb_armed = 1;
}
static irqreturn_t sbprof_tb_intr(int irq, void *dev_id)
{
int i;
pr_debug(DEVNAME ": tb_intr\n");
if (sbp.next_tb_sample < MAX_TB_SAMPLES) {
/* XXX should use XKPHYS to make writes bypass L2 */
u64 *p = sbp.sbprof_tbbuf[sbp.next_tb_sample++];
/* Read out trace */
__raw_writeq(M_SCD_TRACE_CFG_START_READ,
IOADDR(A_SCD_TRACE_CFG));
__asm__ __volatile__ ("sync" : : : "memory");
/* Loop runs backwards because bundles are read out in reverse order */
for (i = 256 * 6; i > 0; i -= 6) {
/* Subscripts decrease to put bundle in the order */
/* t0 lo, t0 hi, t1 lo, t1 hi, t2 lo, t2 hi */
p[i - 1] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
/* read t2 hi */
p[i - 2] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
/* read t2 lo */
p[i - 3] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
/* read t1 hi */
p[i - 4] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
/* read t1 lo */
p[i - 5] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
/* read t0 hi */
p[i - 6] = __raw_readq(IOADDR(A_SCD_TRACE_READ));
/* read t0 lo */
}
if (!sbp.tb_enable) {
pr_debug(DEVNAME ": tb_intr shutdown\n");
__raw_writeq(M_SCD_TRACE_CFG_RESET,
IOADDR(A_SCD_TRACE_CFG));
sbp.tb_armed = 0;
wake_up_interruptible(&sbp.tb_sync);
} else {
/* knock down current interrupt and get another one later */
arm_tb();
}
} else {
/* No more trace buffer samples */
pr_debug(DEVNAME ": tb_intr full\n");
__raw_writeq(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
sbp.tb_armed = 0;
if (!sbp.tb_enable)
wake_up_interruptible(&sbp.tb_sync);
wake_up_interruptible(&sbp.tb_read);
}
return IRQ_HANDLED;
}
static irqreturn_t sbprof_pc_intr(int irq, void *dev_id)
{
printk(DEVNAME ": unexpected pc_intr");
return IRQ_NONE;
}
/*
* Requires: Already called zclk_timer_init with a value that won't
* saturate 40 bits. No subsequent use of SCD performance counters
* or trace buffer.
*/
static int sbprof_zbprof_start(struct file *filp)
{
u64 scdperfcnt;
int err;
if (xchg(&sbp.tb_enable, 1))
return -EBUSY;
pr_debug(DEVNAME ": starting\n");
sbp.next_tb_sample = 0;
filp->f_pos = 0;
err = request_irq(K_INT_TRACE_FREEZE, sbprof_tb_intr, 0,
DEVNAME " trace freeze", &sbp);
if (err)
return -EBUSY;
/* Make sure there isn't a perf-cnt interrupt waiting */
scdperfcnt = __raw_readq(IOADDR(A_SCD_PERF_CNT_CFG));
/* Disable and clear counters, override SRC_1 */
__raw_writeq((scdperfcnt & ~(M_SPC_CFG_SRC1 | M_SPC_CFG_ENABLE)) |
M_SPC_CFG_ENABLE | M_SPC_CFG_CLEAR | V_SPC_CFG_SRC1(1),
IOADDR(A_SCD_PERF_CNT_CFG));
/*
* We grab this interrupt to prevent others from trying to use
* it, even though we don't want to service the interrupts
* (they only feed into the trace-on-interrupt mechanism)
*/
if (request_irq(K_INT_PERF_CNT, sbprof_pc_intr, 0, DEVNAME " scd perfcnt", &sbp)) {
free_irq(K_INT_TRACE_FREEZE, &sbp);
return -EBUSY;
}
/*
* I need the core to mask these, but the interrupt mapper to
* pass them through. I am exploiting my knowledge that
* cp0_status masks out IP[5]. krw
*/
#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
__raw_writeq(K_BCM1480_INT_MAP_I3,
IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_INTERRUPT_MAP_BASE_L) +
((K_BCM1480_INT_PERF_CNT & 0x3f) << 3)));
#else
__raw_writeq(K_INT_MAP_I3,
IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
(K_INT_PERF_CNT << 3)));
#endif
/* Initialize address traps */
__raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_0));
__raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_1));
__raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_2));
__raw_writeq(0, IOADDR(A_ADDR_TRAP_UP_3));
__raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_0));
__raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_1));
__raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_2));
__raw_writeq(0, IOADDR(A_ADDR_TRAP_DOWN_3));
__raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_0));
__raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_1));
__raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_2));
__raw_writeq(0, IOADDR(A_ADDR_TRAP_CFG_3));
/* Initialize Trace Event 0-7 */
/* when interrupt */
__raw_writeq(M_SCD_TREVT_INTERRUPT, IOADDR(A_SCD_TRACE_EVENT_0));
__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_1));
__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_2));
__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_3));
__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_4));
__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_5));
__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_6));
__raw_writeq(0, IOADDR(A_SCD_TRACE_EVENT_7));
/* Initialize Trace Sequence 0-7 */
/* Start on event 0 (interrupt) */
__raw_writeq(V_SCD_TRSEQ_FUNC_START | 0x0fff,
IOADDR(A_SCD_TRACE_SEQUENCE_0));
/* dsamp when d used | asamp when a used */
__raw_writeq(M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE |
K_SCD_TRSEQ_TRIGGER_ALL,
IOADDR(A_SCD_TRACE_SEQUENCE_1));
__raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_2));
__raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_3));
__raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_4));
__raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_5));
__raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_6));
__raw_writeq(0, IOADDR(A_SCD_TRACE_SEQUENCE_7));
/* Now indicate the PERF_CNT interrupt as a trace-relevant interrupt */
#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
__raw_writeq(1ULL << (K_BCM1480_INT_PERF_CNT & 0x3f),
IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_INTERRUPT_TRACE_L)));
#else
__raw_writeq(1ULL << K_INT_PERF_CNT,
IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_TRACE)));
#endif
arm_tb();
pr_debug(DEVNAME ": done starting\n");
return 0;
}
static int sbprof_zbprof_stop(void)
{
int err = 0;
pr_debug(DEVNAME ": stopping\n");
if (sbp.tb_enable) {
/*
* XXXKW there is a window here where the intr handler may run,
* see the disable, and do the wake_up before this sleep
* happens.
*/
pr_debug(DEVNAME ": wait for disarm\n");
err = wait_event_interruptible(sbp.tb_sync, !sbp.tb_armed);
pr_debug(DEVNAME ": disarm complete, stat %d\n", err);
if (err)
return err;
sbp.tb_enable = 0;
free_irq(K_INT_TRACE_FREEZE, &sbp);
free_irq(K_INT_PERF_CNT, &sbp);
}
pr_debug(DEVNAME ": done stopping\n");
return err;
}
static int sbprof_tb_open(struct inode *inode, struct file *filp)
{
int minor;
minor = iminor(inode);
if (minor != 0)
return -ENODEV;
if (xchg(&sbp.open, SB_OPENING) != SB_CLOSED)
return -EBUSY;
memset(&sbp, 0, sizeof(struct sbprof_tb));
sbp.sbprof_tbbuf = vmalloc(MAX_TBSAMPLE_BYTES);
if (!sbp.sbprof_tbbuf) {
sbp.open = SB_CLOSED;
wmb();
return -ENOMEM;
}
memset(sbp.sbprof_tbbuf, 0, MAX_TBSAMPLE_BYTES);
init_waitqueue_head(&sbp.tb_sync);
init_waitqueue_head(&sbp.tb_read);
mutex_init(&sbp.lock);
sbp.open = SB_OPEN;
wmb();
return 0;
}
static int sbprof_tb_release(struct inode *inode, struct file *filp)
{
int minor;
minor = iminor(inode);
if (minor != 0 || sbp.open != SB_CLOSED)
return -ENODEV;
mutex_lock(&sbp.lock);
if (sbp.tb_armed || sbp.tb_enable)
sbprof_zbprof_stop();
vfree(sbp.sbprof_tbbuf);
sbp.open = SB_CLOSED;
wmb();
mutex_unlock(&sbp.lock);
return 0;
}
static ssize_t sbprof_tb_read(struct file *filp, char *buf,
size_t size, loff_t *offp)
{
int cur_sample, sample_off, cur_count, sample_left;
char *src;
int count = 0;
char *dest = buf;
long cur_off = *offp;
if (!access_ok(VERIFY_WRITE, buf, size))
return -EFAULT;
mutex_lock(&sbp.lock);
count = 0;
cur_sample = cur_off / TB_SAMPLE_SIZE;
sample_off = cur_off % TB_SAMPLE_SIZE;
sample_left = TB_SAMPLE_SIZE - sample_off;
while (size && (cur_sample < sbp.next_tb_sample)) {
int err;
cur_count = size < sample_left ? size : sample_left;
src = (char *)(((long)sbp.sbprof_tbbuf[cur_sample])+sample_off);
err = __copy_to_user(dest, src, cur_count);
if (err) {
*offp = cur_off + cur_count - err;
mutex_unlock(&sbp.lock);
return err;
}
pr_debug(DEVNAME ": read from sample %d, %d bytes\n",
cur_sample, cur_count);
size -= cur_count;
sample_left -= cur_count;
if (!sample_left) {
cur_sample++;
sample_off = 0;
sample_left = TB_SAMPLE_SIZE;
} else {
sample_off += cur_count;
}
cur_off += cur_count;
dest += cur_count;
count += cur_count;
}
*offp = cur_off;
mutex_unlock(&sbp.lock);
return count;
}
static long sbprof_tb_ioctl(struct file *filp,
unsigned int command,
unsigned long arg)
{
int err = 0;
switch (command) {
case SBPROF_ZBSTART:
mutex_lock(&sbp.lock);
err = sbprof_zbprof_start(filp);
mutex_unlock(&sbp.lock);
break;
case SBPROF_ZBSTOP:
mutex_lock(&sbp.lock);
err = sbprof_zbprof_stop();
mutex_unlock(&sbp.lock);
break;
case SBPROF_ZBWAITFULL: {
err = wait_event_interruptible(sbp.tb_read, TB_FULL);
if (err)
break;
err = put_user(TB_FULL, (int *) arg);
break;
}
default:
err = -EINVAL;
break;
}
return err;
}
static const struct file_operations sbprof_tb_fops = {
.owner = THIS_MODULE,
.open = sbprof_tb_open,
.release = sbprof_tb_release,
.read = sbprof_tb_read,
.unlocked_ioctl = sbprof_tb_ioctl,
.compat_ioctl = sbprof_tb_ioctl,
.mmap = NULL,
};
static struct class *tb_class;
static struct device *tb_dev;
static int __init sbprof_tb_init(void)
{
struct device *dev;
struct class *tbc;
int err;
if (register_chrdev(SBPROF_TB_MAJOR, DEVNAME, &sbprof_tb_fops)) {
printk(KERN_WARNING DEVNAME ": initialization failed (dev %d)\n",
SBPROF_TB_MAJOR);
return -EIO;
}
tbc = class_create(THIS_MODULE, "sb_tracebuffer");
if (IS_ERR(tbc)) {
err = PTR_ERR(tbc);
goto out_chrdev;
}
tb_class = tbc;
dev = device_create(tbc, NULL, MKDEV(SBPROF_TB_MAJOR, 0), NULL, "tb");
if (IS_ERR(dev)) {
err = PTR_ERR(dev);
goto out_class;
}
tb_dev = dev;
sbp.open = SB_CLOSED;
wmb();
tb_period = zbbus_mhz * 10000LL;
pr_info(DEVNAME ": initialized - tb_period = %lld\n",
(long long) tb_period);
return 0;
out_class:
class_destroy(tb_class);
out_chrdev:
unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME);
return err;
}
static void __exit sbprof_tb_cleanup(void)
{
device_destroy(tb_class, MKDEV(SBPROF_TB_MAJOR, 0));
unregister_chrdev(SBPROF_TB_MAJOR, DEVNAME);
class_destroy(tb_class);
}
module_init(sbprof_tb_init);
module_exit(sbprof_tb_cleanup);
MODULE_ALIAS_CHARDEV_MAJOR(SBPROF_TB_MAJOR);
MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
MODULE_LICENSE("GPL");