M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions

View File

@ -0,0 +1,170 @@
config SIBYTE_SB1250
bool
select CEVT_SB1250
select CSRC_SB1250
select HW_HAS_PCI
select IRQ_CPU
select SIBYTE_ENABLE_LDT_IF_PCI
select SIBYTE_HAS_ZBUS_PROFILING
select SIBYTE_SB1xxx_SOC
select SYS_SUPPORTS_SMP
config SIBYTE_BCM1120
bool
select CEVT_SB1250
select CSRC_SB1250
select IRQ_CPU
select SIBYTE_BCM112X
select SIBYTE_HAS_ZBUS_PROFILING
select SIBYTE_SB1xxx_SOC
config SIBYTE_BCM1125
bool
select CEVT_SB1250
select CSRC_SB1250
select HW_HAS_PCI
select IRQ_CPU
select SIBYTE_BCM112X
select SIBYTE_HAS_ZBUS_PROFILING
select SIBYTE_SB1xxx_SOC
config SIBYTE_BCM1125H
bool
select CEVT_SB1250
select CSRC_SB1250
select HW_HAS_PCI
select IRQ_CPU
select SIBYTE_BCM112X
select SIBYTE_ENABLE_LDT_IF_PCI
select SIBYTE_HAS_ZBUS_PROFILING
select SIBYTE_SB1xxx_SOC
config SIBYTE_BCM112X
bool
select CEVT_SB1250
select CSRC_SB1250
select IRQ_CPU
select SIBYTE_SB1xxx_SOC
select SIBYTE_HAS_ZBUS_PROFILING
config SIBYTE_BCM1x80
bool
select CEVT_BCM1480
select CSRC_BCM1480
select HW_HAS_PCI
select IRQ_CPU
select SIBYTE_HAS_ZBUS_PROFILING
select SIBYTE_SB1xxx_SOC
select SYS_SUPPORTS_SMP
config SIBYTE_BCM1x55
bool
select CEVT_BCM1480
select CSRC_BCM1480
select HW_HAS_PCI
select IRQ_CPU
select SIBYTE_SB1xxx_SOC
select SIBYTE_HAS_ZBUS_PROFILING
select SYS_SUPPORTS_SMP
config SIBYTE_SB1xxx_SOC
bool
select DMA_COHERENT
select IRQ_CPU
select SWAP_IO_SPACE
select SYS_SUPPORTS_32BIT_KERNEL
select SYS_SUPPORTS_64BIT_KERNEL
select CFE
select SYS_HAS_EARLY_PRINTK
choice
prompt "SiByte SOC Stepping"
depends on SIBYTE_SB1xxx_SOC
config CPU_SB1_PASS_1
bool "1250 Pass1"
depends on SIBYTE_SB1250
select CPU_HAS_PREFETCH
config CPU_SB1_PASS_2_1250
bool "1250 An"
depends on SIBYTE_SB1250
select CPU_SB1_PASS_2
help
Also called BCM1250 Pass 2
config CPU_SB1_PASS_2_2
bool "1250 Bn"
depends on SIBYTE_SB1250
select CPU_HAS_PREFETCH
help
Also called BCM1250 Pass 2.2
config CPU_SB1_PASS_4
bool "1250 Cn"
depends on SIBYTE_SB1250
select CPU_HAS_PREFETCH
help
Also called BCM1250 Pass 3
config CPU_SB1_PASS_2_112x
bool "112x Hybrid"
depends on SIBYTE_BCM112X
select CPU_SB1_PASS_2
config CPU_SB1_PASS_3
bool "112x An"
depends on SIBYTE_BCM112X
select CPU_HAS_PREFETCH
endchoice
config CPU_SB1_PASS_2
bool
config SIBYTE_HAS_LDT
bool
config SIBYTE_ENABLE_LDT_IF_PCI
bool
select SIBYTE_HAS_LDT if PCI
config SB1_CEX_ALWAYS_FATAL
bool "All cache exceptions considered fatal (no recovery attempted)"
depends on SIBYTE_SB1xxx_SOC
config SB1_CERR_STALL
bool "Stall (rather than panic) on fatal cache error"
depends on SIBYTE_SB1xxx_SOC
config SIBYTE_CFE_CONSOLE
bool "Use firmware console"
depends on SIBYTE_SB1xxx_SOC
help
Use the CFE API's console write routines during boot. Other console
options (VT console, sb1250 duart console, etc.) should not be
configured.
config SIBYTE_BUS_WATCHER
bool "Support for Bus Watcher statistics"
depends on SIBYTE_SB1xxx_SOC
help
Handle and keep statistics on the bus error interrupts (COR_ECC,
BAD_ECC, IO_BUS).
config SIBYTE_BW_TRACE
bool "Capture bus trace before bus error"
depends on SIBYTE_BUS_WATCHER
help
Run a continuous bus trace, dumping the raw data as soon as
a ZBbus error is detected. Cannot work if ZBbus profiling
is turned on, and also will interfere with JTAG-based trace
buffer activity. Raw buffer data is dumped to console, and
must be processed off-line.
config SIBYTE_TBPROF
tristate "Support for ZBbus profiling"
depends on SIBYTE_HAS_ZBUS_PROFILING
config SIBYTE_HAS_ZBUS_PROFILING
bool

View File

@ -0,0 +1,27 @@
#
# Sibyte SB1250 / BCM1480 family of SOCs
#
obj-$(CONFIG_SIBYTE_BCM112X) += sb1250/
obj-$(CONFIG_SIBYTE_BCM112X) += common/
obj-$(CONFIG_SIBYTE_SB1250) += sb1250/
obj-$(CONFIG_SIBYTE_SB1250) += common/
obj-$(CONFIG_SIBYTE_BCM1x55) += bcm1480/
obj-$(CONFIG_SIBYTE_BCM1x55) += common/
obj-$(CONFIG_SIBYTE_BCM1x80) += bcm1480/
obj-$(CONFIG_SIBYTE_BCM1x80) += common/
#
# Sibyte BCM91120x (Carmel) board
# Sibyte BCM91120C (CRhine) board
# Sibyte BCM91125C (CRhone) board
# Sibyte BCM91125E (Rhone) board
# Sibyte SWARM board
# Sibyte BCM91x80 (BigSur) board
#
obj-$(CONFIG_SIBYTE_CARMEL) += swarm/
obj-$(CONFIG_SIBYTE_CRHINE) += swarm/
obj-$(CONFIG_SIBYTE_CRHONE) += swarm/
obj-$(CONFIG_SIBYTE_RHONE) += swarm/
obj-$(CONFIG_SIBYTE_SENTOSA) += swarm/
obj-$(CONFIG_SIBYTE_SWARM) += swarm/
obj-$(CONFIG_SIBYTE_BIGSUR) += swarm/

View File

@ -0,0 +1,43 @@
#
# These are all rather similar so we consider them a single platform
#
platform-$(CONFIG_SIBYTE_BCM112X) += sibyte/
platform-$(CONFIG_SIBYTE_SB1250) += sibyte/
platform-$(CONFIG_SIBYTE_BCM1x55) += sibyte/
platform-$(CONFIG_SIBYTE_BCM1x80) += sibyte/
#
# Sibyte SB1250 / BCM1480 family of SOCs
#
cflags-$(CONFIG_SIBYTE_BCM112X) += \
-I$(srctree)/arch/mips/include/asm/mach-sibyte \
-DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1250_112x_ALL
platform-$(CONFIG_SIBYTE_SB1250) += sibyte/
cflags-$(CONFIG_SIBYTE_SB1250) += \
-I$(srctree)/arch/mips/include/asm/mach-sibyte \
-DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1250_112x_ALL
cflags-$(CONFIG_SIBYTE_BCM1x55) += \
-I$(srctree)/arch/mips/include/asm/mach-sibyte \
-DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1480_ALL
cflags-$(CONFIG_SIBYTE_BCM1x80) += \
-I$(srctree)/arch/mips/include/asm/mach-sibyte \
-DSIBYTE_HDR_FEATURES=SIBYTE_HDR_FMASK_1480_ALL
#
# Sibyte BCM91120x (Carmel) board
# Sibyte BCM91120C (CRhine) board
# Sibyte BCM91125C (CRhone) board
# Sibyte BCM91125E (Rhone) board
# Sibyte SWARM board
# Sibyte BCM91x80 (BigSur) board
#
load-$(CONFIG_SIBYTE_CARMEL) := 0xffffffff80100000
load-$(CONFIG_SIBYTE_CRHINE) := 0xffffffff80100000
load-$(CONFIG_SIBYTE_CRHONE) := 0xffffffff80100000
load-$(CONFIG_SIBYTE_RHONE) := 0xffffffff80100000
load-$(CONFIG_SIBYTE_SENTOSA) := 0xffffffff80100000
load-$(CONFIG_SIBYTE_SWARM) := 0xffffffff80100000
load-$(CONFIG_SIBYTE_BIGSUR) := 0xffffffff80100000

View File

@ -0,0 +1,3 @@
obj-y := setup.o irq.o time.o
obj-$(CONFIG_SMP) += smp.o

View File

@ -0,0 +1,372 @@
/*
* Copyright (C) 2000,2001,2002,2003,2004 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/kernel.h>
#include <linux/init.h>
#include <linux/linkage.h>
#include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/kernel_stat.h>
#include <asm/errno.h>
#include <asm/irq_regs.h>
#include <asm/signal.h>
#include <asm/io.h>
#include <asm/sibyte/bcm1480_regs.h>
#include <asm/sibyte/bcm1480_int.h>
#include <asm/sibyte/bcm1480_scd.h>
#include <asm/sibyte/sb1250_uart.h>
#include <asm/sibyte/sb1250.h>
/*
* These are the routines that handle all the low level interrupt stuff.
* Actions handled here are: initialization of the interrupt map, requesting of
* interrupt lines by handlers, dispatching if interrupts to handlers, probing
* for interrupt lines
*/
#ifdef CONFIG_PCI
extern unsigned long ht_eoi_space;
#endif
/* Store the CPU id (not the logical number) */
int bcm1480_irq_owner[BCM1480_NR_IRQS];
static DEFINE_RAW_SPINLOCK(bcm1480_imr_lock);
void bcm1480_mask_irq(int cpu, int irq)
{
unsigned long flags, hl_spacing;
u64 cur_ints;
raw_spin_lock_irqsave(&bcm1480_imr_lock, flags);
hl_spacing = 0;
if ((irq >= BCM1480_NR_IRQS_HALF) && (irq <= BCM1480_NR_IRQS)) {
hl_spacing = BCM1480_IMR_HL_SPACING;
irq -= BCM1480_NR_IRQS_HALF;
}
cur_ints = ____raw_readq(IOADDR(A_BCM1480_IMR_MAPPER(cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + hl_spacing));
cur_ints |= (((u64) 1) << irq);
____raw_writeq(cur_ints, IOADDR(A_BCM1480_IMR_MAPPER(cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + hl_spacing));
raw_spin_unlock_irqrestore(&bcm1480_imr_lock, flags);
}
void bcm1480_unmask_irq(int cpu, int irq)
{
unsigned long flags, hl_spacing;
u64 cur_ints;
raw_spin_lock_irqsave(&bcm1480_imr_lock, flags);
hl_spacing = 0;
if ((irq >= BCM1480_NR_IRQS_HALF) && (irq <= BCM1480_NR_IRQS)) {
hl_spacing = BCM1480_IMR_HL_SPACING;
irq -= BCM1480_NR_IRQS_HALF;
}
cur_ints = ____raw_readq(IOADDR(A_BCM1480_IMR_MAPPER(cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + hl_spacing));
cur_ints &= ~(((u64) 1) << irq);
____raw_writeq(cur_ints, IOADDR(A_BCM1480_IMR_MAPPER(cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + hl_spacing));
raw_spin_unlock_irqrestore(&bcm1480_imr_lock, flags);
}
#ifdef CONFIG_SMP
static int bcm1480_set_affinity(struct irq_data *d, const struct cpumask *mask,
bool force)
{
unsigned int irq_dirty, irq = d->irq;
int i = 0, old_cpu, cpu, int_on, k;
u64 cur_ints;
unsigned long flags;
i = cpumask_first(mask);
/* Convert logical CPU to physical CPU */
cpu = cpu_logical_map(i);
/* Protect against other affinity changers and IMR manipulation */
raw_spin_lock_irqsave(&bcm1480_imr_lock, flags);
/* Swizzle each CPU's IMR (but leave the IP selection alone) */
old_cpu = bcm1480_irq_owner[irq];
irq_dirty = irq;
if ((irq_dirty >= BCM1480_NR_IRQS_HALF) && (irq_dirty <= BCM1480_NR_IRQS)) {
irq_dirty -= BCM1480_NR_IRQS_HALF;
}
for (k=0; k<2; k++) { /* Loop through high and low interrupt mask register */
cur_ints = ____raw_readq(IOADDR(A_BCM1480_IMR_MAPPER(old_cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + (k*BCM1480_IMR_HL_SPACING)));
int_on = !(cur_ints & (((u64) 1) << irq_dirty));
if (int_on) {
/* If it was on, mask it */
cur_ints |= (((u64) 1) << irq_dirty);
____raw_writeq(cur_ints, IOADDR(A_BCM1480_IMR_MAPPER(old_cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + (k*BCM1480_IMR_HL_SPACING)));
}
bcm1480_irq_owner[irq] = cpu;
if (int_on) {
/* unmask for the new CPU */
cur_ints = ____raw_readq(IOADDR(A_BCM1480_IMR_MAPPER(cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + (k*BCM1480_IMR_HL_SPACING)));
cur_ints &= ~(((u64) 1) << irq_dirty);
____raw_writeq(cur_ints, IOADDR(A_BCM1480_IMR_MAPPER(cpu) + R_BCM1480_IMR_INTERRUPT_MASK_H + (k*BCM1480_IMR_HL_SPACING)));
}
}
raw_spin_unlock_irqrestore(&bcm1480_imr_lock, flags);
return 0;
}
#endif
/*****************************************************************************/
static void disable_bcm1480_irq(struct irq_data *d)
{
unsigned int irq = d->irq;
bcm1480_mask_irq(bcm1480_irq_owner[irq], irq);
}
static void enable_bcm1480_irq(struct irq_data *d)
{
unsigned int irq = d->irq;
bcm1480_unmask_irq(bcm1480_irq_owner[irq], irq);
}
static void ack_bcm1480_irq(struct irq_data *d)
{
unsigned int irq_dirty, irq = d->irq;
u64 pending;
int k;
/*
* If the interrupt was an HT interrupt, now is the time to
* clear it. NOTE: we assume the HT bridge was set up to
* deliver the interrupts to all CPUs (which makes affinity
* changing easier for us)
*/
irq_dirty = irq;
if ((irq_dirty >= BCM1480_NR_IRQS_HALF) && (irq_dirty <= BCM1480_NR_IRQS)) {
irq_dirty -= BCM1480_NR_IRQS_HALF;
}
for (k=0; k<2; k++) { /* Loop through high and low LDT interrupts */
pending = __raw_readq(IOADDR(A_BCM1480_IMR_REGISTER(bcm1480_irq_owner[irq],
R_BCM1480_IMR_LDT_INTERRUPT_H + (k*BCM1480_IMR_HL_SPACING))));
pending &= ((u64)1 << (irq_dirty));
if (pending) {
#ifdef CONFIG_SMP
int i;
for (i=0; i<NR_CPUS; i++) {
/*
* Clear for all CPUs so an affinity switch
* doesn't find an old status
*/
__raw_writeq(pending, IOADDR(A_BCM1480_IMR_REGISTER(cpu_logical_map(i),
R_BCM1480_IMR_LDT_INTERRUPT_CLR_H + (k*BCM1480_IMR_HL_SPACING))));
}
#else
__raw_writeq(pending, IOADDR(A_BCM1480_IMR_REGISTER(0, R_BCM1480_IMR_LDT_INTERRUPT_CLR_H + (k*BCM1480_IMR_HL_SPACING))));
#endif
/*
* Generate EOI. For Pass 1 parts, EOI is a nop. For
* Pass 2, the LDT world may be edge-triggered, but
* this EOI shouldn't hurt. If they are
* level-sensitive, the EOI is required.
*/
#ifdef CONFIG_PCI
if (ht_eoi_space)
*(uint32_t *)(ht_eoi_space+(irq<<16)+(7<<2)) = 0;
#endif
}
}
bcm1480_mask_irq(bcm1480_irq_owner[irq], irq);
}
static struct irq_chip bcm1480_irq_type = {
.name = "BCM1480-IMR",
.irq_mask_ack = ack_bcm1480_irq,
.irq_mask = disable_bcm1480_irq,
.irq_unmask = enable_bcm1480_irq,
#ifdef CONFIG_SMP
.irq_set_affinity = bcm1480_set_affinity
#endif
};
void __init init_bcm1480_irqs(void)
{
int i;
for (i = 0; i < BCM1480_NR_IRQS; i++) {
irq_set_chip_and_handler(i, &bcm1480_irq_type,
handle_level_irq);
bcm1480_irq_owner[i] = 0;
}
}
/*
* init_IRQ is called early in the boot sequence from init/main.c. It
* is responsible for setting up the interrupt mapper and installing the
* handler that will be responsible for dispatching interrupts to the
* "right" place.
*/
/*
* For now, map all interrupts to IP[2]. We could save
* some cycles by parceling out system interrupts to different
* IP lines, but keep it simple for bringup. We'll also direct
* all interrupts to a single CPU; we should probably route
* PCI and LDT to one cpu and everything else to the other
* to balance the load a bit.
*
* On the second cpu, everything is set to IP5, which is
* ignored, EXCEPT the mailbox interrupt. That one is
* set to IP[2] so it is handled. This is needed so we
* can do cross-cpu function calls, as required by SMP
*/
#define IMR_IP2_VAL K_BCM1480_INT_MAP_I0
#define IMR_IP3_VAL K_BCM1480_INT_MAP_I1
#define IMR_IP4_VAL K_BCM1480_INT_MAP_I2
#define IMR_IP5_VAL K_BCM1480_INT_MAP_I3
#define IMR_IP6_VAL K_BCM1480_INT_MAP_I4
void __init arch_init_irq(void)
{
unsigned int i, cpu;
u64 tmp;
unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
STATUSF_IP1 | STATUSF_IP0;
/* Default everything to IP2 */
/* Start with _high registers which has no bit 0 interrupt source */
for (i = 1; i < BCM1480_NR_IRQS_HALF; i++) { /* was I0 */
for (cpu = 0; cpu < 4; cpu++) {
__raw_writeq(IMR_IP2_VAL,
IOADDR(A_BCM1480_IMR_REGISTER(cpu,
R_BCM1480_IMR_INTERRUPT_MAP_BASE_H) + (i << 3)));
}
}
/* Now do _low registers */
for (i = 0; i < BCM1480_NR_IRQS_HALF; i++) {
for (cpu = 0; cpu < 4; cpu++) {
__raw_writeq(IMR_IP2_VAL,
IOADDR(A_BCM1480_IMR_REGISTER(cpu,
R_BCM1480_IMR_INTERRUPT_MAP_BASE_L) + (i << 3)));
}
}
init_bcm1480_irqs();
/*
* Map the high 16 bits of mailbox_0 registers to IP[3], for
* inter-cpu messages
*/
/* Was I1 */
for (cpu = 0; cpu < 4; cpu++) {
__raw_writeq(IMR_IP3_VAL, IOADDR(A_BCM1480_IMR_REGISTER(cpu, R_BCM1480_IMR_INTERRUPT_MAP_BASE_H) +
(K_BCM1480_INT_MBOX_0_0 << 3)));
}
/* Clear the mailboxes. The firmware may leave them dirty */
for (cpu = 0; cpu < 4; cpu++) {
__raw_writeq(0xffffffffffffffffULL,
IOADDR(A_BCM1480_IMR_REGISTER(cpu, R_BCM1480_IMR_MAILBOX_0_CLR_CPU)));
__raw_writeq(0xffffffffffffffffULL,
IOADDR(A_BCM1480_IMR_REGISTER(cpu, R_BCM1480_IMR_MAILBOX_1_CLR_CPU)));
}
/* Mask everything except the high 16 bit of mailbox_0 registers for all cpus */
tmp = ~((u64) 0) ^ ( (((u64) 1) << K_BCM1480_INT_MBOX_0_0));
for (cpu = 0; cpu < 4; cpu++) {
__raw_writeq(tmp, IOADDR(A_BCM1480_IMR_REGISTER(cpu, R_BCM1480_IMR_INTERRUPT_MASK_H)));
}
tmp = ~((u64) 0);
for (cpu = 0; cpu < 4; cpu++) {
__raw_writeq(tmp, IOADDR(A_BCM1480_IMR_REGISTER(cpu, R_BCM1480_IMR_INTERRUPT_MASK_L)));
}
/*
* Note that the timer interrupts are also mapped, but this is
* done in bcm1480_time_init(). Also, the profiling driver
* does its own management of IP7.
*/
/* Enable necessary IPs, disable the rest */
change_c0_status(ST0_IM, imask);
}
extern void bcm1480_mailbox_interrupt(void);
static inline void dispatch_ip2(void)
{
unsigned long long mask_h, mask_l;
unsigned int cpu = smp_processor_id();
unsigned long base;
/*
* Default...we've hit an IP[2] interrupt, which means we've got to
* check the 1480 interrupt registers to figure out what to do. Need
* to detect which CPU we're on, now that smp_affinity is supported.
*/
base = A_BCM1480_IMR_MAPPER(cpu);
mask_h = __raw_readq(
IOADDR(base + R_BCM1480_IMR_INTERRUPT_STATUS_BASE_H));
mask_l = __raw_readq(
IOADDR(base + R_BCM1480_IMR_INTERRUPT_STATUS_BASE_L));
if (mask_h) {
if (mask_h ^ 1)
do_IRQ(fls64(mask_h) - 1);
else if (mask_l)
do_IRQ(63 + fls64(mask_l));
}
}
asmlinkage void plat_irq_dispatch(void)
{
unsigned int cpu = smp_processor_id();
unsigned int pending;
#ifdef CONFIG_SIBYTE_BCM1480_PROF
/* Set compare to count to silence count/compare timer interrupts */
write_c0_compare(read_c0_count());
#endif
pending = read_c0_cause() & read_c0_status();
#ifdef CONFIG_SIBYTE_BCM1480_PROF
if (pending & CAUSEF_IP7) /* Cpu performance counter interrupt */
sbprof_cpu_intr();
else
#endif
if (pending & CAUSEF_IP4)
do_IRQ(K_BCM1480_INT_TIMER_0 + cpu);
#ifdef CONFIG_SMP
else if (pending & CAUSEF_IP3)
bcm1480_mailbox_interrupt();
#endif
else if (pending & CAUSEF_IP2)
dispatch_ip2();
}

View File

@ -0,0 +1,139 @@
/*
* Copyright (C) 2000,2001,2002,2003,2004 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/module.h>
#include <linux/reboot.h>
#include <linux/string.h>
#include <asm/bootinfo.h>
#include <asm/mipsregs.h>
#include <asm/io.h>
#include <asm/sibyte/sb1250.h>
#include <asm/sibyte/bcm1480_regs.h>
#include <asm/sibyte/bcm1480_scd.h>
#include <asm/sibyte/sb1250_scd.h>
unsigned int sb1_pass;
unsigned int soc_pass;
unsigned int soc_type;
EXPORT_SYMBOL(soc_type);
unsigned int periph_rev;
unsigned int zbbus_mhz;
EXPORT_SYMBOL(zbbus_mhz);
static unsigned int part_type;
static char *soc_str;
static char *pass_str;
static int __init setup_bcm1x80_bcm1x55(void)
{
int ret = 0;
switch (soc_pass) {
case K_SYS_REVISION_BCM1480_S0:
periph_rev = 1;
pass_str = "S0 (pass1)";
break;
case K_SYS_REVISION_BCM1480_A1:
periph_rev = 1;
pass_str = "A1 (pass1)";
break;
case K_SYS_REVISION_BCM1480_A2:
periph_rev = 1;
pass_str = "A2 (pass1)";
break;
case K_SYS_REVISION_BCM1480_A3:
periph_rev = 1;
pass_str = "A3 (pass1)";
break;
case K_SYS_REVISION_BCM1480_B0:
periph_rev = 1;
pass_str = "B0 (pass2)";
break;
default:
printk("Unknown %s rev %x\n", soc_str, soc_pass);
periph_rev = 1;
pass_str = "Unknown Revision";
break;
}
return ret;
}
/* Setup code likely to be common to all SiByte platforms */
static int __init sys_rev_decode(void)
{
int ret = 0;
switch (soc_type) {
case K_SYS_SOC_TYPE_BCM1x80:
if (part_type == K_SYS_PART_BCM1480)
soc_str = "BCM1480";
else if (part_type == K_SYS_PART_BCM1280)
soc_str = "BCM1280";
else
soc_str = "BCM1x80";
ret = setup_bcm1x80_bcm1x55();
break;
case K_SYS_SOC_TYPE_BCM1x55:
if (part_type == K_SYS_PART_BCM1455)
soc_str = "BCM1455";
else if (part_type == K_SYS_PART_BCM1255)
soc_str = "BCM1255";
else
soc_str = "BCM1x55";
ret = setup_bcm1x80_bcm1x55();
break;
default:
printk("Unknown part type %x\n", part_type);
ret = 1;
break;
}
return ret;
}
void __init bcm1480_setup(void)
{
uint64_t sys_rev;
int plldiv;
sb1_pass = read_c0_prid() & 0xff;
sys_rev = __raw_readq(IOADDR(A_SCD_SYSTEM_REVISION));
soc_type = SYS_SOC_TYPE(sys_rev);
part_type = G_SYS_PART(sys_rev);
soc_pass = G_SYS_REVISION(sys_rev);
if (sys_rev_decode()) {
printk("Restart after failure to identify SiByte chip\n");
machine_restart(NULL);
}
plldiv = G_BCM1480_SYS_PLL_DIV(__raw_readq(IOADDR(A_SCD_SYSTEM_CFG)));
zbbus_mhz = ((plldiv >> 1) * 50) + ((plldiv & 1) * 25);
printk("Broadcom SiByte %s %s @ %d MHz (SB-1A rev %d)\n",
soc_str, pass_str, zbbus_mhz * 2, sb1_pass);
printk("Board type: %s\n", get_system_type());
}

View File

@ -0,0 +1,197 @@
/*
* Copyright (C) 2001,2002,2004 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/delay.h>
#include <linux/smp.h>
#include <linux/kernel_stat.h>
#include <linux/sched.h>
#include <asm/mmu_context.h>
#include <asm/io.h>
#include <asm/fw/cfe/cfe_api.h>
#include <asm/sibyte/sb1250.h>
#include <asm/sibyte/bcm1480_regs.h>
#include <asm/sibyte/bcm1480_int.h>
extern void smp_call_function_interrupt(void);
/*
* These are routines for dealing with the bcm1480 smp capabilities
* independent of board/firmware
*/
static void *mailbox_0_set_regs[] = {
IOADDR(A_BCM1480_IMR_CPU0_BASE + R_BCM1480_IMR_MAILBOX_0_SET_CPU),
IOADDR(A_BCM1480_IMR_CPU1_BASE + R_BCM1480_IMR_MAILBOX_0_SET_CPU),
IOADDR(A_BCM1480_IMR_CPU2_BASE + R_BCM1480_IMR_MAILBOX_0_SET_CPU),
IOADDR(A_BCM1480_IMR_CPU3_BASE + R_BCM1480_IMR_MAILBOX_0_SET_CPU),
};
static void *mailbox_0_clear_regs[] = {
IOADDR(A_BCM1480_IMR_CPU0_BASE + R_BCM1480_IMR_MAILBOX_0_CLR_CPU),
IOADDR(A_BCM1480_IMR_CPU1_BASE + R_BCM1480_IMR_MAILBOX_0_CLR_CPU),
IOADDR(A_BCM1480_IMR_CPU2_BASE + R_BCM1480_IMR_MAILBOX_0_CLR_CPU),
IOADDR(A_BCM1480_IMR_CPU3_BASE + R_BCM1480_IMR_MAILBOX_0_CLR_CPU),
};
static void *mailbox_0_regs[] = {
IOADDR(A_BCM1480_IMR_CPU0_BASE + R_BCM1480_IMR_MAILBOX_0_CPU),
IOADDR(A_BCM1480_IMR_CPU1_BASE + R_BCM1480_IMR_MAILBOX_0_CPU),
IOADDR(A_BCM1480_IMR_CPU2_BASE + R_BCM1480_IMR_MAILBOX_0_CPU),
IOADDR(A_BCM1480_IMR_CPU3_BASE + R_BCM1480_IMR_MAILBOX_0_CPU),
};
/*
* SMP init and finish on secondary CPUs
*/
void __cpuinit bcm1480_smp_init(void)
{
unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
STATUSF_IP1 | STATUSF_IP0;
/* Set interrupt mask, but don't enable */
change_c0_status(ST0_IM, imask);
}
/*
* These are routines for dealing with the sb1250 smp capabilities
* independent of board/firmware
*/
/*
* Simple enough; everything is set up, so just poke the appropriate mailbox
* register, and we should be set
*/
static void bcm1480_send_ipi_single(int cpu, unsigned int action)
{
__raw_writeq((((u64)action)<< 48), mailbox_0_set_regs[cpu]);
}
static void bcm1480_send_ipi_mask(const struct cpumask *mask,
unsigned int action)
{
unsigned int i;
for_each_cpu(i, mask)
bcm1480_send_ipi_single(i, action);
}
/*
* Code to run on secondary just after probing the CPU
*/
static void __cpuinit bcm1480_init_secondary(void)
{
extern void bcm1480_smp_init(void);
bcm1480_smp_init();
}
/*
* Do any tidying up before marking online and running the idle
* loop
*/
static void __cpuinit bcm1480_smp_finish(void)
{
extern void sb1480_clockevent_init(void);
sb1480_clockevent_init();
local_irq_enable();
}
/*
* Final cleanup after all secondaries booted
*/
static void bcm1480_cpus_done(void)
{
}
/*
* Setup the PC, SP, and GP of a secondary processor and start it
* running!
*/
static void __cpuinit bcm1480_boot_secondary(int cpu, struct task_struct *idle)
{
int retval;
retval = cfe_cpu_start(cpu_logical_map(cpu), &smp_bootstrap,
__KSTK_TOS(idle),
(unsigned long)task_thread_info(idle), 0);
if (retval != 0)
printk("cfe_start_cpu(%i) returned %i\n" , cpu, retval);
}
/*
* Use CFE to find out how many CPUs are available, setting up
* cpu_possible_mask and the logical/physical mappings.
* XXXKW will the boot CPU ever not be physical 0?
*
* Common setup before any secondaries are started
*/
static void __init bcm1480_smp_setup(void)
{
int i, num;
init_cpu_possible(cpumask_of(0));
__cpu_number_map[0] = 0;
__cpu_logical_map[0] = 0;
for (i = 1, num = 0; i < NR_CPUS; i++) {
if (cfe_cpu_stop(i) == 0) {
set_cpu_possible(i, true);
__cpu_number_map[i] = ++num;
__cpu_logical_map[num] = i;
}
}
printk(KERN_INFO "Detected %i available secondary CPU(s)\n", num);
}
static void __init bcm1480_prepare_cpus(unsigned int max_cpus)
{
}
struct plat_smp_ops bcm1480_smp_ops = {
.send_ipi_single = bcm1480_send_ipi_single,
.send_ipi_mask = bcm1480_send_ipi_mask,
.init_secondary = bcm1480_init_secondary,
.smp_finish = bcm1480_smp_finish,
.cpus_done = bcm1480_cpus_done,
.boot_secondary = bcm1480_boot_secondary,
.smp_setup = bcm1480_smp_setup,
.prepare_cpus = bcm1480_prepare_cpus,
};
void bcm1480_mailbox_interrupt(void)
{
int cpu = smp_processor_id();
int irq = K_BCM1480_INT_MBOX_0_0;
unsigned int action;
kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq));
/* Load the mailbox register to figure out what we're supposed to do */
action = (__raw_readq(mailbox_0_regs[cpu]) >> 48) & 0xffff;
/* Clear the mailbox to clear the interrupt */
__raw_writeq(((u64)action)<<48, mailbox_0_clear_regs[cpu]);
if (action & SMP_RESCHEDULE_YOURSELF)
scheduler_ipi();
if (action & SMP_CALL_FUNCTION)
smp_call_function_interrupt();
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2000,2001,2004 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>
extern void sb1480_clockevent_init(void);
extern void sb1480_clocksource_init(void);
void __init plat_time_init(void)
{
sb1480_clocksource_init();
sb1480_clockevent_init();
}

View File

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

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, COMMAND_LINE_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[COMMAND_LINE_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,606 @@
/*
* 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/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 configuration
#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/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 = vzalloc(MAX_TBSAMPLE_BYTES);
if (!sbp.sbprof_tbbuf) {
sbp.open = SB_CLOSED;
wmb();
return -ENOMEM;
}
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,
.llseek = default_llseek,
};
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");

View File

@ -0,0 +1,4 @@
obj-y := setup.o irq.o time.o
obj-$(CONFIG_SMP) += smp.o
obj-$(CONFIG_SIBYTE_BUS_WATCHER) += bus_watcher.o

View File

@ -0,0 +1,257 @@
/*
* Copyright (C) 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.
*/
/*
* The Bus Watcher monitors internal bus transactions and maintains
* counts of transactions with error status, logging details and
* causing one of several interrupts. This driver provides a handler
* for those interrupts which aggregates the counts (to avoid
* saturating the 8-bit counters) and provides a presence in
* /proc/bus_watcher if PROC_FS is on.
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include <linux/proc_fs.h>
#include <asm/io.h>
#include <asm/sibyte/sb1250.h>
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_int.h>
#include <asm/sibyte/sb1250_scd.h>
struct bw_stats_struct {
uint64_t status;
uint32_t l2_err;
uint32_t memio_err;
int status_printed;
unsigned long l2_cor_d;
unsigned long l2_bad_d;
unsigned long l2_cor_t;
unsigned long l2_bad_t;
unsigned long mem_cor_d;
unsigned long mem_bad_d;
unsigned long bus_error;
} bw_stats;
static void print_summary(uint32_t status, uint32_t l2_err,
uint32_t memio_err)
{
printk("Bus watcher error counters: %08x %08x\n", l2_err, memio_err);
printk("\nLast recorded signature:\n");
printk("Request %02x from %d, answered by %d with Dcode %d\n",
(unsigned int)(G_SCD_BERR_TID(status) & 0x3f),
(int)(G_SCD_BERR_TID(status) >> 6),
(int)G_SCD_BERR_RID(status),
(int)G_SCD_BERR_DCODE(status));
}
/*
* check_bus_watcher is exported for use in situations where we want
* to see the most recent status of the bus watcher, which might have
* already been destructively read out of the registers.
*
* notes: this is currently used by the cache error handler
* should provide locking against the interrupt handler
*/
void check_bus_watcher(void)
{
u32 status, l2_err, memio_err;
#ifdef CONFIG_SB1_PASS_1_WORKAROUNDS
/* Destructive read, clears register and interrupt */
status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS));
#else
/* Use non-destructive register */
status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS_DEBUG));
#endif
if (!(status & 0x7fffffff)) {
printk("Using last values reaped by bus watcher driver\n");
status = bw_stats.status;
l2_err = bw_stats.l2_err;
memio_err = bw_stats.memio_err;
} else {
l2_err = csr_in32(IOADDR(A_BUS_L2_ERRORS));
memio_err = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
}
if (status & ~(1UL << 31))
print_summary(status, l2_err, memio_err);
else
printk("Bus watcher indicates no error\n");
}
static int bw_print_buffer(char *page, struct bw_stats_struct *stats)
{
int len;
len = sprintf(page, "SiByte Bus Watcher statistics\n");
len += sprintf(page+len, "-----------------------------\n");
len += sprintf(page+len, "L2-d-cor %8ld\nL2-d-bad %8ld\n",
stats->l2_cor_d, stats->l2_bad_d);
len += sprintf(page+len, "L2-t-cor %8ld\nL2-t-bad %8ld\n",
stats->l2_cor_t, stats->l2_bad_t);
len += sprintf(page+len, "MC-d-cor %8ld\nMC-d-bad %8ld\n",
stats->mem_cor_d, stats->mem_bad_d);
len += sprintf(page+len, "IO-err %8ld\n", stats->bus_error);
len += sprintf(page+len, "\nLast recorded signature:\n");
len += sprintf(page+len, "Request %02x from %d, answered by %d with Dcode %d\n",
(unsigned int)(G_SCD_BERR_TID(stats->status) & 0x3f),
(int)(G_SCD_BERR_TID(stats->status) >> 6),
(int)G_SCD_BERR_RID(stats->status),
(int)G_SCD_BERR_DCODE(stats->status));
/* XXXKW indicate multiple errors between printings, or stats
collection (or both)? */
if (stats->status & M_SCD_BERR_MULTERRS)
len += sprintf(page+len, "Multiple errors observed since last check.\n");
if (stats->status_printed) {
len += sprintf(page+len, "(no change since last printing)\n");
} else {
stats->status_printed = 1;
}
return len;
}
#ifdef CONFIG_PROC_FS
/* For simplicity, I want to assume a single read is required each
time */
static int bw_read_proc(char *page, char **start, off_t off,
int count, int *eof, void *data)
{
int len;
if (off == 0) {
len = bw_print_buffer(page, data);
*start = page;
} else {
len = 0;
*eof = 1;
}
return len;
}
static void create_proc_decoder(struct bw_stats_struct *stats)
{
struct proc_dir_entry *ent;
ent = create_proc_read_entry("bus_watcher", S_IWUSR | S_IRUGO, NULL,
bw_read_proc, stats);
if (!ent) {
printk(KERN_INFO "Unable to initialize bus_watcher /proc entry\n");
return;
}
}
#endif /* CONFIG_PROC_FS */
/*
* sibyte_bw_int - handle bus watcher interrupts and accumulate counts
*
* notes: possible re-entry due to multiple sources
* should check/indicate saturation
*/
static irqreturn_t sibyte_bw_int(int irq, void *data)
{
struct bw_stats_struct *stats = data;
unsigned long cntr;
#ifdef CONFIG_SIBYTE_BW_TRACE
int i;
#endif
#ifndef CONFIG_PROC_FS
char bw_buf[1024];
#endif
#ifdef CONFIG_SIBYTE_BW_TRACE
csr_out32(M_SCD_TRACE_CFG_FREEZE, IOADDR(A_SCD_TRACE_CFG));
csr_out32(M_SCD_TRACE_CFG_START_READ, IOADDR(A_SCD_TRACE_CFG));
for (i=0; i<256*6; i++)
printk("%016llx\n",
(long long)__raw_readq(IOADDR(A_SCD_TRACE_READ)));
csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG));
#endif
/* Destructive read, clears register and interrupt */
stats->status = csr_in32(IOADDR(A_SCD_BUS_ERR_STATUS));
stats->status_printed = 0;
stats->l2_err = cntr = csr_in32(IOADDR(A_BUS_L2_ERRORS));
stats->l2_cor_d += G_SCD_L2ECC_CORR_D(cntr);
stats->l2_bad_d += G_SCD_L2ECC_BAD_D(cntr);
stats->l2_cor_t += G_SCD_L2ECC_CORR_T(cntr);
stats->l2_bad_t += G_SCD_L2ECC_BAD_T(cntr);
csr_out32(0, IOADDR(A_BUS_L2_ERRORS));
stats->memio_err = cntr = csr_in32(IOADDR(A_BUS_MEM_IO_ERRORS));
stats->mem_cor_d += G_SCD_MEM_ECC_CORR(cntr);
stats->mem_bad_d += G_SCD_MEM_ECC_BAD(cntr);
stats->bus_error += G_SCD_MEM_BUSERR(cntr);
csr_out32(0, IOADDR(A_BUS_MEM_IO_ERRORS));
#ifndef CONFIG_PROC_FS
bw_print_buffer(bw_buf, stats);
printk(bw_buf);
#endif
return IRQ_HANDLED;
}
int __init sibyte_bus_watcher(void)
{
memset(&bw_stats, 0, sizeof(struct bw_stats_struct));
bw_stats.status_printed = 1;
if (request_irq(K_INT_BAD_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
printk("Failed to register bus watcher BAD_ECC irq\n");
return -1;
}
if (request_irq(K_INT_COR_ECC, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
free_irq(K_INT_BAD_ECC, &bw_stats);
printk("Failed to register bus watcher COR_ECC irq\n");
return -1;
}
if (request_irq(K_INT_IO_BUS, sibyte_bw_int, 0, "Bus watcher", &bw_stats)) {
free_irq(K_INT_BAD_ECC, &bw_stats);
free_irq(K_INT_COR_ECC, &bw_stats);
printk("Failed to register bus watcher IO_BUS irq\n");
return -1;
}
#ifdef CONFIG_PROC_FS
create_proc_decoder(&bw_stats);
#endif
#ifdef CONFIG_SIBYTE_BW_TRACE
csr_out32((M_SCD_TRSEQ_ASAMPLE | M_SCD_TRSEQ_DSAMPLE |
K_SCD_TRSEQ_TRIGGER_ALL),
IOADDR(A_SCD_TRACE_SEQUENCE_0));
csr_out32(M_SCD_TRACE_CFG_RESET, IOADDR(A_SCD_TRACE_CFG));
csr_out32(M_SCD_TRACE_CFG_START, IOADDR(A_SCD_TRACE_CFG));
#endif
return 0;
}
__initcall(sibyte_bus_watcher);

View File

@ -0,0 +1,337 @@
/*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/linkage.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/smp.h>
#include <linux/mm.h>
#include <linux/kernel_stat.h>
#include <asm/errno.h>
#include <asm/signal.h>
#include <asm/time.h>
#include <asm/io.h>
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_int.h>
#include <asm/sibyte/sb1250_uart.h>
#include <asm/sibyte/sb1250_scd.h>
#include <asm/sibyte/sb1250.h>
/*
* These are the routines that handle all the low level interrupt stuff.
* Actions handled here are: initialization of the interrupt map, requesting of
* interrupt lines by handlers, dispatching if interrupts to handlers, probing
* for interrupt lines
*/
#ifdef CONFIG_SIBYTE_HAS_LDT
extern unsigned long ldt_eoi_space;
#endif
/* Store the CPU id (not the logical number) */
int sb1250_irq_owner[SB1250_NR_IRQS];
static DEFINE_RAW_SPINLOCK(sb1250_imr_lock);
void sb1250_mask_irq(int cpu, int irq)
{
unsigned long flags;
u64 cur_ints;
raw_spin_lock_irqsave(&sb1250_imr_lock, flags);
cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) +
R_IMR_INTERRUPT_MASK));
cur_ints |= (((u64) 1) << irq);
____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
R_IMR_INTERRUPT_MASK));
raw_spin_unlock_irqrestore(&sb1250_imr_lock, flags);
}
void sb1250_unmask_irq(int cpu, int irq)
{
unsigned long flags;
u64 cur_ints;
raw_spin_lock_irqsave(&sb1250_imr_lock, flags);
cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) +
R_IMR_INTERRUPT_MASK));
cur_ints &= ~(((u64) 1) << irq);
____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
R_IMR_INTERRUPT_MASK));
raw_spin_unlock_irqrestore(&sb1250_imr_lock, flags);
}
#ifdef CONFIG_SMP
static int sb1250_set_affinity(struct irq_data *d, const struct cpumask *mask,
bool force)
{
int i = 0, old_cpu, cpu, int_on;
unsigned int irq = d->irq;
u64 cur_ints;
unsigned long flags;
i = cpumask_first(mask);
/* Convert logical CPU to physical CPU */
cpu = cpu_logical_map(i);
/* Protect against other affinity changers and IMR manipulation */
raw_spin_lock_irqsave(&sb1250_imr_lock, flags);
/* Swizzle each CPU's IMR (but leave the IP selection alone) */
old_cpu = sb1250_irq_owner[irq];
cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(old_cpu) +
R_IMR_INTERRUPT_MASK));
int_on = !(cur_ints & (((u64) 1) << irq));
if (int_on) {
/* If it was on, mask it */
cur_ints |= (((u64) 1) << irq);
____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(old_cpu) +
R_IMR_INTERRUPT_MASK));
}
sb1250_irq_owner[irq] = cpu;
if (int_on) {
/* unmask for the new CPU */
cur_ints = ____raw_readq(IOADDR(A_IMR_MAPPER(cpu) +
R_IMR_INTERRUPT_MASK));
cur_ints &= ~(((u64) 1) << irq);
____raw_writeq(cur_ints, IOADDR(A_IMR_MAPPER(cpu) +
R_IMR_INTERRUPT_MASK));
}
raw_spin_unlock_irqrestore(&sb1250_imr_lock, flags);
return 0;
}
#endif
static void disable_sb1250_irq(struct irq_data *d)
{
unsigned int irq = d->irq;
sb1250_mask_irq(sb1250_irq_owner[irq], irq);
}
static void enable_sb1250_irq(struct irq_data *d)
{
unsigned int irq = d->irq;
sb1250_unmask_irq(sb1250_irq_owner[irq], irq);
}
static void ack_sb1250_irq(struct irq_data *d)
{
unsigned int irq = d->irq;
#ifdef CONFIG_SIBYTE_HAS_LDT
u64 pending;
/*
* If the interrupt was an HT interrupt, now is the time to
* clear it. NOTE: we assume the HT bridge was set up to
* deliver the interrupts to all CPUs (which makes affinity
* changing easier for us)
*/
pending = __raw_readq(IOADDR(A_IMR_REGISTER(sb1250_irq_owner[irq],
R_IMR_LDT_INTERRUPT)));
pending &= ((u64)1 << (irq));
if (pending) {
int i;
for (i=0; i<NR_CPUS; i++) {
int cpu;
#ifdef CONFIG_SMP
cpu = cpu_logical_map(i);
#else
cpu = i;
#endif
/*
* Clear for all CPUs so an affinity switch
* doesn't find an old status
*/
__raw_writeq(pending,
IOADDR(A_IMR_REGISTER(cpu,
R_IMR_LDT_INTERRUPT_CLR)));
}
/*
* Generate EOI. For Pass 1 parts, EOI is a nop. For
* Pass 2, the LDT world may be edge-triggered, but
* this EOI shouldn't hurt. If they are
* level-sensitive, the EOI is required.
*/
*(uint32_t *)(ldt_eoi_space+(irq<<16)+(7<<2)) = 0;
}
#endif
sb1250_mask_irq(sb1250_irq_owner[irq], irq);
}
static struct irq_chip sb1250_irq_type = {
.name = "SB1250-IMR",
.irq_mask_ack = ack_sb1250_irq,
.irq_unmask = enable_sb1250_irq,
.irq_mask = disable_sb1250_irq,
#ifdef CONFIG_SMP
.irq_set_affinity = sb1250_set_affinity
#endif
};
void __init init_sb1250_irqs(void)
{
int i;
for (i = 0; i < SB1250_NR_IRQS; i++) {
irq_set_chip_and_handler(i, &sb1250_irq_type,
handle_level_irq);
sb1250_irq_owner[i] = 0;
}
}
/*
* arch_init_irq is called early in the boot sequence from init/main.c via
* init_IRQ. It is responsible for setting up the interrupt mapper and
* installing the handler that will be responsible for dispatching interrupts
* to the "right" place.
*/
/*
* For now, map all interrupts to IP[2]. We could save
* some cycles by parceling out system interrupts to different
* IP lines, but keep it simple for bringup. We'll also direct
* all interrupts to a single CPU; we should probably route
* PCI and LDT to one cpu and everything else to the other
* to balance the load a bit.
*
* On the second cpu, everything is set to IP5, which is
* ignored, EXCEPT the mailbox interrupt. That one is
* set to IP[2] so it is handled. This is needed so we
* can do cross-cpu function calls, as required by SMP
*/
#define IMR_IP2_VAL K_INT_MAP_I0
#define IMR_IP3_VAL K_INT_MAP_I1
#define IMR_IP4_VAL K_INT_MAP_I2
#define IMR_IP5_VAL K_INT_MAP_I3
#define IMR_IP6_VAL K_INT_MAP_I4
void __init arch_init_irq(void)
{
unsigned int i;
u64 tmp;
unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
STATUSF_IP1 | STATUSF_IP0;
/* Default everything to IP2 */
for (i = 0; i < SB1250_NR_IRQS; i++) { /* was I0 */
__raw_writeq(IMR_IP2_VAL,
IOADDR(A_IMR_REGISTER(0,
R_IMR_INTERRUPT_MAP_BASE) +
(i << 3)));
__raw_writeq(IMR_IP2_VAL,
IOADDR(A_IMR_REGISTER(1,
R_IMR_INTERRUPT_MAP_BASE) +
(i << 3)));
}
init_sb1250_irqs();
/*
* Map the high 16 bits of the mailbox registers to IP[3], for
* inter-cpu messages
*/
/* Was I1 */
__raw_writeq(IMR_IP3_VAL,
IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MAP_BASE) +
(K_INT_MBOX_0 << 3)));
__raw_writeq(IMR_IP3_VAL,
IOADDR(A_IMR_REGISTER(1, R_IMR_INTERRUPT_MAP_BASE) +
(K_INT_MBOX_0 << 3)));
/* Clear the mailboxes. The firmware may leave them dirty */
__raw_writeq(0xffffffffffffffffULL,
IOADDR(A_IMR_REGISTER(0, R_IMR_MAILBOX_CLR_CPU)));
__raw_writeq(0xffffffffffffffffULL,
IOADDR(A_IMR_REGISTER(1, R_IMR_MAILBOX_CLR_CPU)));
/* Mask everything except the mailbox registers for both cpus */
tmp = ~((u64) 0) ^ (((u64) 1) << K_INT_MBOX_0);
__raw_writeq(tmp, IOADDR(A_IMR_REGISTER(0, R_IMR_INTERRUPT_MASK)));
__raw_writeq(tmp, IOADDR(A_IMR_REGISTER(1, R_IMR_INTERRUPT_MASK)));
/*
* Note that the timer interrupts are also mapped, but this is
* done in sb1250_time_init(). Also, the profiling driver
* does its own management of IP7.
*/
/* Enable necessary IPs, disable the rest */
change_c0_status(ST0_IM, imask);
}
extern void sb1250_mailbox_interrupt(void);
static inline void dispatch_ip2(void)
{
unsigned int cpu = smp_processor_id();
unsigned long long mask;
/*
* Default...we've hit an IP[2] interrupt, which means we've got to
* check the 1250 interrupt registers to figure out what to do. Need
* to detect which CPU we're on, now that smp_affinity is supported.
*/
mask = __raw_readq(IOADDR(A_IMR_REGISTER(cpu,
R_IMR_INTERRUPT_STATUS_BASE)));
if (mask)
do_IRQ(fls64(mask) - 1);
}
asmlinkage void plat_irq_dispatch(void)
{
unsigned int cpu = smp_processor_id();
unsigned int pending;
/*
* What a pain. We have to be really careful saving the upper 32 bits
* of any * register across function calls if we don't want them
* trashed--since were running in -o32, the calling routing never saves
* the full 64 bits of a register across a function call. Being the
* interrupt handler, we're guaranteed that interrupts are disabled
* during this code so we don't have to worry about random interrupts
* blasting the high 32 bits.
*/
pending = read_c0_cause() & read_c0_status() & ST0_IM;
if (pending & CAUSEF_IP7) /* CPU performance counter interrupt */
do_IRQ(MIPS_CPU_IRQ_BASE + 7);
else if (pending & CAUSEF_IP4)
do_IRQ(K_INT_TIMER_0 + cpu); /* sb1250_timer_interrupt() */
#ifdef CONFIG_SMP
else if (pending & CAUSEF_IP3)
sb1250_mailbox_interrupt();
#endif
else if (pending & CAUSEF_IP2)
dispatch_ip2();
else
spurious_interrupt();
}

View File

@ -0,0 +1,247 @@
/*
* 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/module.h>
#include <linux/kernel.h>
#include <linux/reboot.h>
#include <linux/string.h>
#include <asm/bootinfo.h>
#include <asm/mipsregs.h>
#include <asm/io.h>
#include <asm/sibyte/sb1250.h>
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_scd.h>
unsigned int sb1_pass;
unsigned int soc_pass;
unsigned int soc_type;
EXPORT_SYMBOL(soc_type);
unsigned int periph_rev;
unsigned int zbbus_mhz;
EXPORT_SYMBOL(zbbus_mhz);
static char *soc_str;
static char *pass_str;
static unsigned int war_pass; /* XXXKW don't overload PASS defines? */
static int __init setup_bcm1250(void)
{
int ret = 0;
switch (soc_pass) {
case K_SYS_REVISION_BCM1250_PASS1:
periph_rev = 1;
pass_str = "Pass 1";
break;
case K_SYS_REVISION_BCM1250_A10:
periph_rev = 2;
pass_str = "A8/A10";
/* XXXKW different war_pass? */
war_pass = K_SYS_REVISION_BCM1250_PASS2;
break;
case K_SYS_REVISION_BCM1250_PASS2_2:
periph_rev = 2;
pass_str = "B1";
break;
case K_SYS_REVISION_BCM1250_B2:
periph_rev = 2;
pass_str = "B2";
war_pass = K_SYS_REVISION_BCM1250_PASS2_2;
break;
case K_SYS_REVISION_BCM1250_PASS3:
periph_rev = 3;
pass_str = "C0";
break;
case K_SYS_REVISION_BCM1250_C1:
periph_rev = 3;
pass_str = "C1";
break;
default:
if (soc_pass < K_SYS_REVISION_BCM1250_PASS2_2) {
periph_rev = 2;
pass_str = "A0-A6";
war_pass = K_SYS_REVISION_BCM1250_PASS2;
} else {
printk("Unknown BCM1250 rev %x\n", soc_pass);
ret = 1;
}
break;
}
return ret;
}
int sb1250_m3_workaround_needed(void)
{
switch (soc_type) {
case K_SYS_SOC_TYPE_BCM1250:
case K_SYS_SOC_TYPE_BCM1250_ALT:
case K_SYS_SOC_TYPE_BCM1250_ALT2:
case K_SYS_SOC_TYPE_BCM1125:
case K_SYS_SOC_TYPE_BCM1125H:
return soc_pass < K_SYS_REVISION_BCM1250_C0;
default:
return 0;
}
}
static int __init setup_bcm112x(void)
{
int ret = 0;
switch (soc_pass) {
case 0:
/* Early build didn't have revid set */
periph_rev = 3;
pass_str = "A1";
war_pass = K_SYS_REVISION_BCM112x_A1;
break;
case K_SYS_REVISION_BCM112x_A1:
periph_rev = 3;
pass_str = "A1";
break;
case K_SYS_REVISION_BCM112x_A2:
periph_rev = 3;
pass_str = "A2";
break;
case K_SYS_REVISION_BCM112x_A3:
periph_rev = 3;
pass_str = "A3";
break;
case K_SYS_REVISION_BCM112x_A4:
periph_rev = 3;
pass_str = "A4";
break;
case K_SYS_REVISION_BCM112x_B0:
periph_rev = 3;
pass_str = "B0";
break;
default:
printk("Unknown %s rev %x\n", soc_str, soc_pass);
ret = 1;
}
return ret;
}
/* Setup code likely to be common to all SiByte platforms */
static int __init sys_rev_decode(void)
{
int ret = 0;
war_pass = soc_pass;
switch (soc_type) {
case K_SYS_SOC_TYPE_BCM1250:
case K_SYS_SOC_TYPE_BCM1250_ALT:
case K_SYS_SOC_TYPE_BCM1250_ALT2:
soc_str = "BCM1250";
ret = setup_bcm1250();
break;
case K_SYS_SOC_TYPE_BCM1120:
soc_str = "BCM1120";
ret = setup_bcm112x();
break;
case K_SYS_SOC_TYPE_BCM1125:
soc_str = "BCM1125";
ret = setup_bcm112x();
break;
case K_SYS_SOC_TYPE_BCM1125H:
soc_str = "BCM1125H";
ret = setup_bcm112x();
break;
default:
printk("Unknown SOC type %x\n", soc_type);
ret = 1;
break;
}
return ret;
}
void __init sb1250_setup(void)
{
uint64_t sys_rev;
int plldiv;
int bad_config = 0;
sb1_pass = read_c0_prid() & 0xff;
sys_rev = __raw_readq(IOADDR(A_SCD_SYSTEM_REVISION));
soc_type = SYS_SOC_TYPE(sys_rev);
soc_pass = G_SYS_REVISION(sys_rev);
if (sys_rev_decode()) {
printk("Restart after failure to identify SiByte chip\n");
machine_restart(NULL);
}
plldiv = G_SYS_PLL_DIV(__raw_readq(IOADDR(A_SCD_SYSTEM_CFG)));
zbbus_mhz = ((plldiv >> 1) * 50) + ((plldiv & 1) * 25);
printk("Broadcom SiByte %s %s @ %d MHz (SB1 rev %d)\n",
soc_str, pass_str, zbbus_mhz * 2, sb1_pass);
printk("Board type: %s\n", get_system_type());
switch (war_pass) {
case K_SYS_REVISION_BCM1250_PASS1:
#ifndef CONFIG_SB1_PASS_1_WORKAROUNDS
printk("@@@@ This is a BCM1250 A0-A2 (Pass 1) board, "
"and the kernel doesn't have the proper "
"workarounds compiled in. @@@@\n");
bad_config = 1;
#endif
break;
case K_SYS_REVISION_BCM1250_PASS2:
/* Pass 2 - easiest as default for now - so many numbers */
#if !defined(CONFIG_SB1_PASS_2_WORKAROUNDS) || \
!defined(CONFIG_SB1_PASS_2_1_WORKAROUNDS)
printk("@@@@ This is a BCM1250 A3-A10 board, and the "
"kernel doesn't have the proper workarounds "
"compiled in. @@@@\n");
bad_config = 1;
#endif
#ifdef CONFIG_CPU_HAS_PREFETCH
printk("@@@@ Prefetches may be enabled in this kernel, "
"but are buggy on this board. @@@@\n");
bad_config = 1;
#endif
break;
case K_SYS_REVISION_BCM1250_PASS2_2:
#ifndef CONFIG_SB1_PASS_2_WORKAROUNDS
printk("@@@@ This is a BCM1250 B1/B2. board, and the "
"kernel doesn't have the proper workarounds "
"compiled in. @@@@\n");
bad_config = 1;
#endif
#if defined(CONFIG_SB1_PASS_2_1_WORKAROUNDS) || \
!defined(CONFIG_CPU_HAS_PREFETCH)
printk("@@@@ This is a BCM1250 B1/B2, but the kernel is "
"conservatively configured for an 'A' stepping. "
"@@@@\n");
#endif
break;
default:
break;
}
if (bad_config) {
printk("Invalid configuration for this chip.\n");
machine_restart(NULL);
}
}

View File

@ -0,0 +1,185 @@
/*
* Copyright (C) 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/delay.h>
#include <linux/interrupt.h>
#include <linux/smp.h>
#include <linux/kernel_stat.h>
#include <linux/sched.h>
#include <asm/mmu_context.h>
#include <asm/io.h>
#include <asm/fw/cfe/cfe_api.h>
#include <asm/sibyte/sb1250.h>
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_int.h>
static void *mailbox_set_regs[] = {
IOADDR(A_IMR_CPU0_BASE + R_IMR_MAILBOX_SET_CPU),
IOADDR(A_IMR_CPU1_BASE + R_IMR_MAILBOX_SET_CPU)
};
static void *mailbox_clear_regs[] = {
IOADDR(A_IMR_CPU0_BASE + R_IMR_MAILBOX_CLR_CPU),
IOADDR(A_IMR_CPU1_BASE + R_IMR_MAILBOX_CLR_CPU)
};
static void *mailbox_regs[] = {
IOADDR(A_IMR_CPU0_BASE + R_IMR_MAILBOX_CPU),
IOADDR(A_IMR_CPU1_BASE + R_IMR_MAILBOX_CPU)
};
/*
* SMP init and finish on secondary CPUs
*/
void __cpuinit sb1250_smp_init(void)
{
unsigned int imask = STATUSF_IP4 | STATUSF_IP3 | STATUSF_IP2 |
STATUSF_IP1 | STATUSF_IP0;
/* Set interrupt mask, but don't enable */
change_c0_status(ST0_IM, imask);
}
/*
* These are routines for dealing with the sb1250 smp capabilities
* independent of board/firmware
*/
/*
* Simple enough; everything is set up, so just poke the appropriate mailbox
* register, and we should be set
*/
static void sb1250_send_ipi_single(int cpu, unsigned int action)
{
__raw_writeq((((u64)action) << 48), mailbox_set_regs[cpu]);
}
static inline void sb1250_send_ipi_mask(const struct cpumask *mask,
unsigned int action)
{
unsigned int i;
for_each_cpu(i, mask)
sb1250_send_ipi_single(i, action);
}
/*
* Code to run on secondary just after probing the CPU
*/
static void __cpuinit sb1250_init_secondary(void)
{
extern void sb1250_smp_init(void);
sb1250_smp_init();
}
/*
* Do any tidying up before marking online and running the idle
* loop
*/
static void __cpuinit sb1250_smp_finish(void)
{
extern void sb1250_clockevent_init(void);
sb1250_clockevent_init();
local_irq_enable();
}
/*
* Final cleanup after all secondaries booted
*/
static void sb1250_cpus_done(void)
{
}
/*
* Setup the PC, SP, and GP of a secondary processor and start it
* running!
*/
static void __cpuinit sb1250_boot_secondary(int cpu, struct task_struct *idle)
{
int retval;
retval = cfe_cpu_start(cpu_logical_map(cpu), &smp_bootstrap,
__KSTK_TOS(idle),
(unsigned long)task_thread_info(idle), 0);
if (retval != 0)
printk("cfe_start_cpu(%i) returned %i\n" , cpu, retval);
}
/*
* Use CFE to find out how many CPUs are available, setting up
* cpu_possible_mask and the logical/physical mappings.
* XXXKW will the boot CPU ever not be physical 0?
*
* Common setup before any secondaries are started
*/
static void __init sb1250_smp_setup(void)
{
int i, num;
init_cpu_possible(cpumask_of(0));
__cpu_number_map[0] = 0;
__cpu_logical_map[0] = 0;
for (i = 1, num = 0; i < NR_CPUS; i++) {
if (cfe_cpu_stop(i) == 0) {
set_cpu_possible(i, true);
__cpu_number_map[i] = ++num;
__cpu_logical_map[num] = i;
}
}
printk(KERN_INFO "Detected %i available secondary CPU(s)\n", num);
}
static void __init sb1250_prepare_cpus(unsigned int max_cpus)
{
}
struct plat_smp_ops sb_smp_ops = {
.send_ipi_single = sb1250_send_ipi_single,
.send_ipi_mask = sb1250_send_ipi_mask,
.init_secondary = sb1250_init_secondary,
.smp_finish = sb1250_smp_finish,
.cpus_done = sb1250_cpus_done,
.boot_secondary = sb1250_boot_secondary,
.smp_setup = sb1250_smp_setup,
.prepare_cpus = sb1250_prepare_cpus,
};
void sb1250_mailbox_interrupt(void)
{
int cpu = smp_processor_id();
int irq = K_INT_MBOX_0;
unsigned int action;
kstat_incr_irqs_this_cpu(irq, irq_to_desc(irq));
/* Load the mailbox register to figure out what we're supposed to do */
action = (____raw_readq(mailbox_regs[cpu]) >> 48) & 0xffff;
/* Clear the mailbox to clear the interrupt */
____raw_writeq(((u64)action) << 48, mailbox_clear_regs[cpu]);
if (action & SMP_RESCHEDULE_YOURSELF)
scheduler_ipi();
if (action & SMP_CALL_FUNCTION)
smp_call_function_interrupt();
}

View File

@ -0,0 +1,27 @@
/*
* Copyright (C) 2000, 2001 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>
extern void sb1250_clocksource_init(void);
extern void sb1250_clockevent_init(void);
void __init plat_time_init(void)
{
sb1250_clocksource_init();
sb1250_clockevent_init();
}

View File

@ -0,0 +1,4 @@
obj-y := platform.o setup.o rtc_xicor1241.o \
rtc_m41t81.o
obj-$(CONFIG_I2C_BOARDINFO) += swarm-i2c.o

View File

@ -0,0 +1,139 @@
#include <linux/err.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/platform_device.h>
#include <linux/ata_platform.h>
#include <asm/sibyte/board.h>
#include <asm/sibyte/sb1250_genbus.h>
#include <asm/sibyte/sb1250_regs.h>
#if defined(CONFIG_SIBYTE_SWARM) || defined(CONFIG_SIBYTE_LITTLESUR)
#define DRV_NAME "pata-swarm"
#define SWARM_IDE_SHIFT 5
#define SWARM_IDE_BASE 0x1f0
#define SWARM_IDE_CTRL 0x3f6
static struct resource swarm_pata_resource[] = {
{
.name = "Swarm GenBus IDE",
.flags = IORESOURCE_MEM,
}, {
.name = "Swarm GenBus IDE",
.flags = IORESOURCE_MEM,
}, {
.name = "Swarm GenBus IDE",
.flags = IORESOURCE_IRQ,
.start = K_INT_GB_IDE,
.end = K_INT_GB_IDE,
},
};
static struct pata_platform_info pata_platform_data = {
.ioport_shift = SWARM_IDE_SHIFT,
};
static struct platform_device swarm_pata_device = {
.name = "pata_platform",
.id = -1,
.resource = swarm_pata_resource,
.num_resources = ARRAY_SIZE(swarm_pata_resource),
.dev = {
.platform_data = &pata_platform_data,
.coherent_dma_mask = ~0, /* grumble */
},
};
static int __init swarm_pata_init(void)
{
u8 __iomem *base;
phys_t offset, size;
struct resource *r;
if (!SIBYTE_HAVE_IDE)
return -ENODEV;
base = ioremap(A_IO_EXT_BASE, 0x800);
offset = __raw_readq(base + R_IO_EXT_REG(R_IO_EXT_START_ADDR, IDE_CS));
size = __raw_readq(base + R_IO_EXT_REG(R_IO_EXT_MULT_SIZE, IDE_CS));
iounmap(base);
offset = G_IO_START_ADDR(offset) << S_IO_ADDRBASE;
size = (G_IO_MULT_SIZE(size) + 1) << S_IO_REGSIZE;
if (offset < A_PHYS_GENBUS || offset >= A_PHYS_GENBUS_END) {
pr_info(DRV_NAME ": PATA interface at GenBus disabled\n");
return -EBUSY;
}
pr_info(DRV_NAME ": PATA interface at GenBus slot %i\n", IDE_CS);
r = swarm_pata_resource;
r[0].start = offset + (SWARM_IDE_BASE << SWARM_IDE_SHIFT);
r[0].end = offset + ((SWARM_IDE_BASE + 8) << SWARM_IDE_SHIFT) - 1;
r[1].start = offset + (SWARM_IDE_CTRL << SWARM_IDE_SHIFT);
r[1].end = offset + ((SWARM_IDE_CTRL + 1) << SWARM_IDE_SHIFT) - 1;
return platform_device_register(&swarm_pata_device);
}
device_initcall(swarm_pata_init);
#endif /* defined(CONFIG_SIBYTE_SWARM) || defined(CONFIG_SIBYTE_LITTLESUR) */
#define sb1250_dev_struct(num) \
static struct resource sb1250_res##num = { \
.name = "SB1250 MAC " __stringify(num), \
.flags = IORESOURCE_MEM, \
.start = A_MAC_CHANNEL_BASE(num), \
.end = A_MAC_CHANNEL_BASE(num + 1) -1, \
};\
static struct platform_device sb1250_dev##num = { \
.name = "sb1250-mac", \
.id = num, \
.resource = &sb1250_res##num, \
.num_resources = 1, \
}
sb1250_dev_struct(0);
sb1250_dev_struct(1);
sb1250_dev_struct(2);
sb1250_dev_struct(3);
static struct platform_device *sb1250_devs[] __initdata = {
&sb1250_dev0,
&sb1250_dev1,
&sb1250_dev2,
&sb1250_dev3,
};
static int __init sb1250_device_init(void)
{
int ret;
/* Set the number of available units based on the SOC type. */
switch (soc_type) {
case K_SYS_SOC_TYPE_BCM1250:
case K_SYS_SOC_TYPE_BCM1250_ALT:
ret = platform_add_devices(sb1250_devs, 3);
break;
case K_SYS_SOC_TYPE_BCM1120:
case K_SYS_SOC_TYPE_BCM1125:
case K_SYS_SOC_TYPE_BCM1125H:
case K_SYS_SOC_TYPE_BCM1250_ALT2: /* Hybrid */
ret = platform_add_devices(sb1250_devs, 2);
break;
case K_SYS_SOC_TYPE_BCM1x55:
case K_SYS_SOC_TYPE_BCM1x80:
ret = platform_add_devices(sb1250_devs, 4);
break;
default:
ret = -ENODEV;
break;
}
return ret;
}
device_initcall(sb1250_device_init);

View File

@ -0,0 +1,233 @@
/*
* Copyright (C) 2000, 2001 Broadcom Corporation
*
* Copyright (C) 2002 MontaVista Software Inc.
* Author: jsun@mvista.com or jsun@junsun.net
*
* 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.
*
*/
#include <linux/bcd.h>
#include <linux/types.h>
#include <linux/time.h>
#include <asm/time.h>
#include <asm/addrspace.h>
#include <asm/io.h>
#include <asm/sibyte/sb1250.h>
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_smbus.h>
/* M41T81 definitions */
/*
* Register bits
*/
#define M41T81REG_SC_ST 0x80 /* stop bit */
#define M41T81REG_HR_CB 0x40 /* century bit */
#define M41T81REG_HR_CEB 0x80 /* century enable bit */
#define M41T81REG_CTL_S 0x20 /* sign bit */
#define M41T81REG_CTL_FT 0x40 /* frequency test bit */
#define M41T81REG_CTL_OUT 0x80 /* output level */
#define M41T81REG_WD_RB0 0x01 /* watchdog resolution bit 0 */
#define M41T81REG_WD_RB1 0x02 /* watchdog resolution bit 1 */
#define M41T81REG_WD_BMB0 0x04 /* watchdog multiplier bit 0 */
#define M41T81REG_WD_BMB1 0x08 /* watchdog multiplier bit 1 */
#define M41T81REG_WD_BMB2 0x10 /* watchdog multiplier bit 2 */
#define M41T81REG_WD_BMB3 0x20 /* watchdog multiplier bit 3 */
#define M41T81REG_WD_BMB4 0x40 /* watchdog multiplier bit 4 */
#define M41T81REG_AMO_ABE 0x20 /* alarm in "battery back-up mode" enable bit */
#define M41T81REG_AMO_SQWE 0x40 /* square wave enable */
#define M41T81REG_AMO_AFE 0x80 /* alarm flag enable flag */
#define M41T81REG_ADT_RPT5 0x40 /* alarm repeat mode bit 5 */
#define M41T81REG_ADT_RPT4 0x80 /* alarm repeat mode bit 4 */
#define M41T81REG_AHR_RPT3 0x80 /* alarm repeat mode bit 3 */
#define M41T81REG_AHR_HT 0x40 /* halt update bit */
#define M41T81REG_AMN_RPT2 0x80 /* alarm repeat mode bit 2 */
#define M41T81REG_ASC_RPT1 0x80 /* alarm repeat mode bit 1 */
#define M41T81REG_FLG_AF 0x40 /* alarm flag (read only) */
#define M41T81REG_FLG_WDF 0x80 /* watchdog flag (read only) */
#define M41T81REG_SQW_RS0 0x10 /* sqw frequency bit 0 */
#define M41T81REG_SQW_RS1 0x20 /* sqw frequency bit 1 */
#define M41T81REG_SQW_RS2 0x40 /* sqw frequency bit 2 */
#define M41T81REG_SQW_RS3 0x80 /* sqw frequency bit 3 */
/*
* Register numbers
*/
#define M41T81REG_TSC 0x00 /* tenths/hundredths of second */
#define M41T81REG_SC 0x01 /* seconds */
#define M41T81REG_MN 0x02 /* minute */
#define M41T81REG_HR 0x03 /* hour/century */
#define M41T81REG_DY 0x04 /* day of week */
#define M41T81REG_DT 0x05 /* date of month */
#define M41T81REG_MO 0x06 /* month */
#define M41T81REG_YR 0x07 /* year */
#define M41T81REG_CTL 0x08 /* control */
#define M41T81REG_WD 0x09 /* watchdog */
#define M41T81REG_AMO 0x0A /* alarm: month */
#define M41T81REG_ADT 0x0B /* alarm: date */
#define M41T81REG_AHR 0x0C /* alarm: hour */
#define M41T81REG_AMN 0x0D /* alarm: minute */
#define M41T81REG_ASC 0x0E /* alarm: second */
#define M41T81REG_FLG 0x0F /* flags */
#define M41T81REG_SQW 0x13 /* square wave register */
#define M41T81_CCR_ADDRESS 0x68
#define SMB_CSR(reg) IOADDR(A_SMB_REGISTER(1, reg))
static int m41t81_read(uint8_t addr)
{
while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
;
__raw_writeq(addr & 0xff, SMB_CSR(R_SMB_CMD));
__raw_writeq(V_SMB_ADDR(M41T81_CCR_ADDRESS) | V_SMB_TT_WR1BYTE,
SMB_CSR(R_SMB_START));
while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
;
__raw_writeq(V_SMB_ADDR(M41T81_CCR_ADDRESS) | V_SMB_TT_RD1BYTE,
SMB_CSR(R_SMB_START));
while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
;
if (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_ERROR) {
/* Clear error bit by writing a 1 */
__raw_writeq(M_SMB_ERROR, SMB_CSR(R_SMB_STATUS));
return -1;
}
return (__raw_readq(SMB_CSR(R_SMB_DATA)) & 0xff);
}
static int m41t81_write(uint8_t addr, int b)
{
while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
;
__raw_writeq(addr & 0xff, SMB_CSR(R_SMB_CMD));
__raw_writeq(b & 0xff, SMB_CSR(R_SMB_DATA));
__raw_writeq(V_SMB_ADDR(M41T81_CCR_ADDRESS) | V_SMB_TT_WR2BYTE,
SMB_CSR(R_SMB_START));
while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
;
if (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_ERROR) {
/* Clear error bit by writing a 1 */
__raw_writeq(M_SMB_ERROR, SMB_CSR(R_SMB_STATUS));
return -1;
}
/* read the same byte again to make sure it is written */
__raw_writeq(V_SMB_ADDR(M41T81_CCR_ADDRESS) | V_SMB_TT_RD1BYTE,
SMB_CSR(R_SMB_START));
while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
;
return 0;
}
int m41t81_set_time(unsigned long t)
{
struct rtc_time tm;
unsigned long flags;
/* Note we don't care about the century */
rtc_time_to_tm(t, &tm);
/*
* Note the write order matters as it ensures the correctness.
* When we write sec, 10th sec is clear. It is reasonable to
* believe we should finish writing min within a second.
*/
spin_lock_irqsave(&rtc_lock, flags);
tm.tm_sec = bin2bcd(tm.tm_sec);
m41t81_write(M41T81REG_SC, tm.tm_sec);
tm.tm_min = bin2bcd(tm.tm_min);
m41t81_write(M41T81REG_MN, tm.tm_min);
tm.tm_hour = bin2bcd(tm.tm_hour);
tm.tm_hour = (tm.tm_hour & 0x3f) | (m41t81_read(M41T81REG_HR) & 0xc0);
m41t81_write(M41T81REG_HR, tm.tm_hour);
/* tm_wday starts from 0 to 6 */
if (tm.tm_wday == 0) tm.tm_wday = 7;
tm.tm_wday = bin2bcd(tm.tm_wday);
m41t81_write(M41T81REG_DY, tm.tm_wday);
tm.tm_mday = bin2bcd(tm.tm_mday);
m41t81_write(M41T81REG_DT, tm.tm_mday);
/* tm_mon starts from 0, *ick* */
tm.tm_mon ++;
tm.tm_mon = bin2bcd(tm.tm_mon);
m41t81_write(M41T81REG_MO, tm.tm_mon);
/* we don't do century, everything is beyond 2000 */
tm.tm_year %= 100;
tm.tm_year = bin2bcd(tm.tm_year);
m41t81_write(M41T81REG_YR, tm.tm_year);
spin_unlock_irqrestore(&rtc_lock, flags);
return 0;
}
unsigned long m41t81_get_time(void)
{
unsigned int year, mon, day, hour, min, sec;
unsigned long flags;
/*
* min is valid if two reads of sec are the same.
*/
for (;;) {
spin_lock_irqsave(&rtc_lock, flags);
sec = m41t81_read(M41T81REG_SC);
min = m41t81_read(M41T81REG_MN);
if (sec == m41t81_read(M41T81REG_SC)) break;
spin_unlock_irqrestore(&rtc_lock, flags);
}
hour = m41t81_read(M41T81REG_HR) & 0x3f;
day = m41t81_read(M41T81REG_DT);
mon = m41t81_read(M41T81REG_MO);
year = m41t81_read(M41T81REG_YR);
spin_unlock_irqrestore(&rtc_lock, flags);
sec = bcd2bin(sec);
min = bcd2bin(min);
hour = bcd2bin(hour);
day = bcd2bin(day);
mon = bcd2bin(mon);
year = bcd2bin(year);
year += 2000;
return mktime(year, mon, day, hour, min, sec);
}
int m41t81_probe(void)
{
unsigned int tmp;
/* enable chip if it is not enabled yet */
tmp = m41t81_read(M41T81REG_SC);
m41t81_write(M41T81REG_SC, tmp & 0x7f);
return (m41t81_read(M41T81REG_SC) != -1);
}

View File

@ -0,0 +1,210 @@
/*
* Copyright (C) 2000, 2001 Broadcom Corporation
*
* Copyright (C) 2002 MontaVista Software Inc.
* Author: jsun@mvista.com or jsun@junsun.net
*
* 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.
*/
#include <linux/bcd.h>
#include <linux/types.h>
#include <linux/time.h>
#include <asm/time.h>
#include <asm/addrspace.h>
#include <asm/io.h>
#include <asm/sibyte/sb1250.h>
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_smbus.h>
/* Xicor 1241 definitions */
/*
* Register bits
*/
#define X1241REG_SR_BAT 0x80 /* currently on battery power */
#define X1241REG_SR_RWEL 0x04 /* r/w latch is enabled, can write RTC */
#define X1241REG_SR_WEL 0x02 /* r/w latch is unlocked, can enable r/w now */
#define X1241REG_SR_RTCF 0x01 /* clock failed */
#define X1241REG_BL_BP2 0x80 /* block protect 2 */
#define X1241REG_BL_BP1 0x40 /* block protect 1 */
#define X1241REG_BL_BP0 0x20 /* block protect 0 */
#define X1241REG_BL_WD1 0x10
#define X1241REG_BL_WD0 0x08
#define X1241REG_HR_MIL 0x80 /* military time format */
/*
* Register numbers
*/
#define X1241REG_BL 0x10 /* block protect bits */
#define X1241REG_INT 0x11 /* */
#define X1241REG_SC 0x30 /* Seconds */
#define X1241REG_MN 0x31 /* Minutes */
#define X1241REG_HR 0x32 /* Hours */
#define X1241REG_DT 0x33 /* Day of month */
#define X1241REG_MO 0x34 /* Month */
#define X1241REG_YR 0x35 /* Year */
#define X1241REG_DW 0x36 /* Day of Week */
#define X1241REG_Y2K 0x37 /* Year 2K */
#define X1241REG_SR 0x3F /* Status register */
#define X1241_CCR_ADDRESS 0x6F
#define SMB_CSR(reg) IOADDR(A_SMB_REGISTER(1, reg))
static int xicor_read(uint8_t addr)
{
while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
;
__raw_writeq((addr >> 8) & 0x7, SMB_CSR(R_SMB_CMD));
__raw_writeq(addr & 0xff, SMB_CSR(R_SMB_DATA));
__raw_writeq(V_SMB_ADDR(X1241_CCR_ADDRESS) | V_SMB_TT_WR2BYTE,
SMB_CSR(R_SMB_START));
while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
;
__raw_writeq(V_SMB_ADDR(X1241_CCR_ADDRESS) | V_SMB_TT_RD1BYTE,
SMB_CSR(R_SMB_START));
while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
;
if (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_ERROR) {
/* Clear error bit by writing a 1 */
__raw_writeq(M_SMB_ERROR, SMB_CSR(R_SMB_STATUS));
return -1;
}
return (__raw_readq(SMB_CSR(R_SMB_DATA)) & 0xff);
}
static int xicor_write(uint8_t addr, int b)
{
while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
;
__raw_writeq(addr, SMB_CSR(R_SMB_CMD));
__raw_writeq((addr & 0xff) | ((b & 0xff) << 8), SMB_CSR(R_SMB_DATA));
__raw_writeq(V_SMB_ADDR(X1241_CCR_ADDRESS) | V_SMB_TT_WR3BYTE,
SMB_CSR(R_SMB_START));
while (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_BUSY)
;
if (__raw_readq(SMB_CSR(R_SMB_STATUS)) & M_SMB_ERROR) {
/* Clear error bit by writing a 1 */
__raw_writeq(M_SMB_ERROR, SMB_CSR(R_SMB_STATUS));
return -1;
} else {
return 0;
}
}
int xicor_set_time(unsigned long t)
{
struct rtc_time tm;
int tmp;
unsigned long flags;
rtc_time_to_tm(t, &tm);
tm.tm_year += 1900;
spin_lock_irqsave(&rtc_lock, flags);
/* unlock writes to the CCR */
xicor_write(X1241REG_SR, X1241REG_SR_WEL);
xicor_write(X1241REG_SR, X1241REG_SR_WEL | X1241REG_SR_RWEL);
/* trivial ones */
tm.tm_sec = bin2bcd(tm.tm_sec);
xicor_write(X1241REG_SC, tm.tm_sec);
tm.tm_min = bin2bcd(tm.tm_min);
xicor_write(X1241REG_MN, tm.tm_min);
tm.tm_mday = bin2bcd(tm.tm_mday);
xicor_write(X1241REG_DT, tm.tm_mday);
/* tm_mon starts from 0, *ick* */
tm.tm_mon ++;
tm.tm_mon = bin2bcd(tm.tm_mon);
xicor_write(X1241REG_MO, tm.tm_mon);
/* year is split */
tmp = tm.tm_year / 100;
tm.tm_year %= 100;
xicor_write(X1241REG_YR, tm.tm_year);
xicor_write(X1241REG_Y2K, tmp);
/* hour is the most tricky one */
tmp = xicor_read(X1241REG_HR);
if (tmp & X1241REG_HR_MIL) {
/* 24 hour format */
tm.tm_hour = bin2bcd(tm.tm_hour);
tmp = (tmp & ~0x3f) | (tm.tm_hour & 0x3f);
} else {
/* 12 hour format, with 0x2 for pm */
tmp = tmp & ~0x3f;
if (tm.tm_hour >= 12) {
tmp |= 0x20;
tm.tm_hour -= 12;
}
tm.tm_hour = bin2bcd(tm.tm_hour);
tmp |= tm.tm_hour;
}
xicor_write(X1241REG_HR, tmp);
xicor_write(X1241REG_SR, 0);
spin_unlock_irqrestore(&rtc_lock, flags);
return 0;
}
unsigned long xicor_get_time(void)
{
unsigned int year, mon, day, hour, min, sec, y2k;
unsigned long flags;
spin_lock_irqsave(&rtc_lock, flags);
sec = xicor_read(X1241REG_SC);
min = xicor_read(X1241REG_MN);
hour = xicor_read(X1241REG_HR);
if (hour & X1241REG_HR_MIL) {
hour &= 0x3f;
} else {
if (hour & 0x20)
hour = (hour & 0xf) + 0x12;
}
day = xicor_read(X1241REG_DT);
mon = xicor_read(X1241REG_MO);
year = xicor_read(X1241REG_YR);
y2k = xicor_read(X1241REG_Y2K);
spin_unlock_irqrestore(&rtc_lock, flags);
sec = bcd2bin(sec);
min = bcd2bin(min);
hour = bcd2bin(hour);
day = bcd2bin(day);
mon = bcd2bin(mon);
year = bcd2bin(year);
y2k = bcd2bin(y2k);
year += (y2k * 100);
return mktime(year, mon, day, hour, min, sec);
}
int xicor_probe(void)
{
return (xicor_read(X1241REG_SC) != -1);
}

View File

@ -0,0 +1,184 @@
/*
* Copyright (C) 2000, 2001, 2002, 2003, 2004 Broadcom Corporation
* Copyright (C) 2004 by Ralf Baechle (ralf@linux-mips.org)
*
* 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.
*/
/*
* Setup code for the SWARM board
*/
#include <linux/spinlock.h>
#include <linux/mm.h>
#include <linux/bootmem.h>
#include <linux/blkdev.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/screen_info.h>
#include <linux/initrd.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/bootinfo.h>
#include <asm/mipsregs.h>
#include <asm/reboot.h>
#include <asm/time.h>
#include <asm/traps.h>
#include <asm/sibyte/sb1250.h>
#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
#include <asm/sibyte/bcm1480_regs.h>
#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
#include <asm/sibyte/sb1250_regs.h>
#else
#error invalid SiByte board configuration
#endif
#include <asm/sibyte/sb1250_genbus.h>
#include <asm/sibyte/board.h>
#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
extern void bcm1480_setup(void);
#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
extern void sb1250_setup(void);
#else
#error invalid SiByte board configuration
#endif
extern int xicor_probe(void);
extern int xicor_set_time(unsigned long);
extern unsigned long xicor_get_time(void);
extern int m41t81_probe(void);
extern int m41t81_set_time(unsigned long);
extern unsigned long m41t81_get_time(void);
const char *get_system_type(void)
{
return "SiByte " SIBYTE_BOARD_NAME;
}
int swarm_be_handler(struct pt_regs *regs, int is_fixup)
{
if (!is_fixup && (regs->cp0_cause & 4)) {
/* Data bus error - print PA */
printk("DBE physical address: %010Lx\n",
__read_64bit_c0_register($26, 1));
}
return (is_fixup ? MIPS_BE_FIXUP : MIPS_BE_FATAL);
}
enum swarm_rtc_type {
RTC_NONE,
RTC_XICOR,
RTC_M41T81,
};
enum swarm_rtc_type swarm_rtc_type;
void read_persistent_clock(struct timespec *ts)
{
unsigned long sec;
switch (swarm_rtc_type) {
case RTC_XICOR:
sec = xicor_get_time();
break;
case RTC_M41T81:
sec = m41t81_get_time();
break;
case RTC_NONE:
default:
sec = mktime(2000, 1, 1, 0, 0, 0);
break;
}
ts->tv_sec = sec;
ts->tv_nsec = 0;
}
int rtc_mips_set_time(unsigned long sec)
{
switch (swarm_rtc_type) {
case RTC_XICOR:
return xicor_set_time(sec);
case RTC_M41T81:
return m41t81_set_time(sec);
case RTC_NONE:
default:
return -1;
}
}
void __init plat_mem_setup(void)
{
#if defined(CONFIG_SIBYTE_BCM1x55) || defined(CONFIG_SIBYTE_BCM1x80)
bcm1480_setup();
#elif defined(CONFIG_SIBYTE_SB1250) || defined(CONFIG_SIBYTE_BCM112X)
sb1250_setup();
#else
#error invalid SiByte board configuration
#endif
panic_timeout = 5; /* For debug. */
board_be_handler = swarm_be_handler;
if (xicor_probe())
swarm_rtc_type = RTC_XICOR;
if (m41t81_probe())
swarm_rtc_type = RTC_M41T81;
#ifdef CONFIG_VT
screen_info = (struct screen_info) {
.orig_video_page = 52,
.orig_video_mode = 3,
.orig_video_cols = 80,
.flags = 12,
.orig_video_ega_bx = 3,
.orig_video_lines = 25,
.orig_video_isVGA = 0x22,
.orig_video_points = 16,
};
/* XXXKW for CFE, get lines/cols from environment */
#endif
}
#ifdef LEDS_PHYS
#ifdef CONFIG_SIBYTE_CARMEL
/* XXXKW need to detect Monterey/LittleSur/etc */
#undef LEDS_PHYS
#define LEDS_PHYS MLEDS_PHYS
#endif
void setleds(char *str)
{
void *reg;
int i;
for (i = 0; i < 4; i++) {
reg = IOADDR(LEDS_PHYS) + 0x20 + ((3 - i) << 3);
if (!str[i])
writeb(' ', reg);
else
writeb(str[i], reg);
}
}
#endif /* LEDS_PHYS */

View File

@ -0,0 +1,35 @@
/*
* Broadcom BCM91250A (SWARM), etc. I2C platform setup.
*
* Copyright (c) 2008 Maciej W. Rozycki
*
* 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.
*/
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
static struct i2c_board_info swarm_i2c_info1[] __initdata = {
{
I2C_BOARD_INFO("m41t81", 0x68),
},
};
static int __init swarm_i2c_init(void)
{
int err;
err = i2c_register_board_info(1, swarm_i2c_info1,
ARRAY_SIZE(swarm_i2c_info1));
if (err < 0)
printk(KERN_ERR
"swarm-i2c: cannot register board I2C devices\n");
return err;
}
arch_initcall(swarm_i2c_init);