diff options
Diffstat (limited to 'target/linux/brcm63xx/files/arch')
15 files changed, 2665 insertions, 0 deletions
diff --git a/target/linux/brcm63xx/files/arch/mips/bcm963xx/Makefile b/target/linux/brcm63xx/files/arch/mips/bcm963xx/Makefile new file mode 100644 index 000000000..a9d1e5597 --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/bcm963xx/Makefile @@ -0,0 +1,9 @@ +# +# Makefile for the Broadcom BCM963xx SoC specific parts of the kernel +# +# Copyright (C) 2004 Broadcom Corporation +# +obj-y := irq.o prom.o setup.o time.o ser_init.o int-handler.o info.o wdt.o + +SRCBASE := $(TOPDIR) +EXTRA_CFLAGS += -I$(SRCBASE)/include diff --git a/target/linux/brcm63xx/files/arch/mips/bcm963xx/info.c b/target/linux/brcm63xx/files/arch/mips/bcm963xx/info.c new file mode 100644 index 000000000..d50b601e2 --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/bcm963xx/info.c @@ -0,0 +1,102 @@ +/* + * $Id$ + * + * Copyright (C) 2007 OpenWrt.org + * Copyright (C) 2007 Gabor Juhos <juhosg at openwrt.org> + * Copyright (C) 2007 Florian Fainelli <florian@openwrt.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. + */ + +#include <linux/types.h> +#include <linux/autoconf.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> + +#include <asm/bootinfo.h> +#include <asm/addrspace.h> +#include <asm/string.h> +#include <asm/mach-bcm963xx/bootloaders.h> + +static char *boot_loader_names[BOOT_LOADER_LAST+1] = { + [BOOT_LOADER_UNKNOWN] = "Unknown", + [BOOT_LOADER_CFE] = "CFE", + [BOOT_LOADER_REDBOOT] = "RedBoot", + [BOOT_LOADER_CFE2] = "CFEv2" +}; + +/* boot loaders specific definitions */ +#define CFE_EPTSEAL 0x43464531 /* CFE1 is the magic number to recognize CFE from other bootloaders */ + +int boot_loader_type; +/* + * Boot loader detection routines + */ +static int __init detect_cfe(void) +{ + /* + * This method only works, when we are booted directly from the CFE. + */ + uint32_t cfe_handle = (uint32_t) fw_arg0; + uint32_t cfe_a1_val = (uint32_t) fw_arg1; + uint32_t cfe_entry = (uint32_t) fw_arg2; + uint32_t cfe_seal = (uint32_t) fw_arg3; + + /* Check for CFE by finding the CFE magic number */ + if (cfe_seal != CFE_EPTSEAL) + /* We are not booted from CFE */ + return 0; + + /* cfe_a1_val must be 0, because only one CPU present in the ADM5120 SoC */ + if (cfe_a1_val != 0) + return 0; + + /* The cfe_handle, and the cfe_entry must be kernel mode addresses */ + if ((cfe_handle < KSEG0) || (cfe_entry < KSEG0)) + return 0; + + return 1; +} + +static int __init detect_redboot(void) +{ + /* On Inventel Livebox, the boot loader is passed as a command line argument, check for it */ + if (!strncmp(arcs_cmdline, "boot_loader=RedBoot", 19)) + return 1; + return 0; +} + +void __init detect_bootloader(void) +{ + if (detect_cfe()) { + boot_loader_type = BOOT_LOADER_CFE; + } + + if (detect_redboot()) { + boot_loader_type = BOOT_LOADER_REDBOOT; + } + else { + /* Some devices are using CFE, but it is not detected as is */ + boot_loader_type = BOOT_LOADER_CFE2; + } + printk("Boot loader is : %s\n", boot_loader_names[boot_loader_type]); +} + +void __init detect_board(void) +{ + switch (boot_loader_type) + { + case BOOT_LOADER_CFE: + break; + case BOOT_LOADER_REDBOOT: + break; + default: + break; + } +} + +EXPORT_SYMBOL(boot_loader_type); diff --git a/target/linux/brcm63xx/files/arch/mips/bcm963xx/int-handler.S b/target/linux/brcm63xx/files/arch/mips/bcm963xx/int-handler.S new file mode 100644 index 000000000..a7a9c9d20 --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/bcm963xx/int-handler.S @@ -0,0 +1,59 @@ +/* +<:copyright-gpl + Copyright 2002 Broadcom Corp. All Rights Reserved. + + This program is free software; you can distribute it and/or modify it + under the terms of the GNU General Public License (Version 2) as + published by the Free Software Foundation. + + This program is distributed in the hope 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. +:> +*/ +/* + * Generic interrupt handler for Broadcom MIPS boards + */ + +#include <linux/autoconf.h> + +#include <asm/asm.h> +#include <asm/mipsregs.h> +#include <asm/regdef.h> +#include <asm/stackframe.h> + +/* + * MIPS IRQ Source + * -------- ------ + * 0 Software (ignored) + * 1 Software (ignored) + * 2 Combined hardware interrupt (hw0) + * 3 Hardware + * 4 Hardware + * 5 Hardware + * 6 Hardware + * 7 R4k timer + */ + + .text + .set noreorder + .set noat + .align 5 + NESTED(brcmIRQ, PT_SIZE, sp) + SAVE_ALL + CLI + .set noreorder + .set at + + jal plat_irq_dispatch + move a0, sp + + j ret_from_irq + nop + + END(brcmIRQ) diff --git a/target/linux/brcm63xx/files/arch/mips/bcm963xx/irq.c b/target/linux/brcm63xx/files/arch/mips/bcm963xx/irq.c new file mode 100644 index 000000000..962cd374d --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/bcm963xx/irq.c @@ -0,0 +1,258 @@ +/* +<:copyright-gpl + Copyright 2002 Broadcom Corp. All Rights Reserved. + + This program is free software; you can distribute it and/or modify it + under the terms of the GNU General Public License (Version 2) as + published by the Free Software Foundation. + + This program is distributed in the hope 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. +:> +*/ +/* + * Interrupt control functions for Broadcom 963xx MIPS boards + */ + +#include <asm/atomic.h> + +#include <linux/delay.h> +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/irq.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> + +#include <asm/irq.h> +#include <asm/mipsregs.h> +#include <asm/addrspace.h> +#include <asm/signal.h> +#include <6348_map_part.h> +#include <6348_intr.h> +#include <bcm_map_part.h> +#include <bcm_intr.h> + +static void irq_dispatch_int(struct pt_regs *regs) +{ + unsigned int pendingIrqs; + static unsigned int irqBit; + static unsigned int isrNumber = 31; + + pendingIrqs = PERF->IrqStatus & PERF->IrqMask; + if (!pendingIrqs) { + return; + } + + while (1) { + irqBit <<= 1; + isrNumber++; + if (isrNumber == 32) { + isrNumber = 0; + irqBit = 0x1; + } + if (pendingIrqs & irqBit) { + PERF->IrqMask &= ~irqBit; // mask + do_IRQ(isrNumber + INTERNAL_ISR_TABLE_OFFSET); + break; + } + } +} + +static void irq_dispatch_ext(uint32 irq) +{ + if (!(PERF->ExtIrqCfg & (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)))) { + printk("**** Ext IRQ mask. Should not dispatch ****\n"); + } + /* disable and clear interrupt in the controller */ + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); + PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); + do_IRQ(irq); +} + + +extern void brcm_timer_interrupt(struct pt_regs *regs); + +asmlinkage void plat_irq_dispatch(struct pt_regs *regs) +{ + u32 cause; + while((cause = (read_c0_cause()& CAUSEF_IP))) { + if (cause & CAUSEF_IP7) + brcm_timer_interrupt(regs); + else if (cause & CAUSEF_IP2) + irq_dispatch_int(regs); + else if (cause & CAUSEF_IP3) + irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_0); + else if (cause & CAUSEF_IP4) + irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_1); + else if (cause & CAUSEF_IP5) + irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_2); + else if (cause & CAUSEF_IP6) + irq_dispatch_ext(INTERRUPT_ID_EXTERNAL_3); + local_irq_disable(); + } +} + + +void enable_brcm_irq(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + if( irq >= INTERNAL_ISR_TABLE_OFFSET ) { + PERF->IrqMask |= (1 << (irq - INTERNAL_ISR_TABLE_OFFSET)); + } + else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) { + /* enable and clear interrupt in the controller */ + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); + } + local_irq_restore(flags); +} + +void disable_brcm_irq(unsigned int irq) +{ + unsigned long flags; + + local_irq_save(flags); + if( irq >= INTERNAL_ISR_TABLE_OFFSET ) { + PERF->IrqMask &= ~(1 << (irq - INTERNAL_ISR_TABLE_OFFSET)); + } + else if (irq >= INTERRUPT_ID_EXTERNAL_0 && irq <= INTERRUPT_ID_EXTERNAL_3) { + /* disable interrupt in the controller */ + PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); + } + local_irq_restore(flags); +} + +void ack_brcm_irq(unsigned int irq) +{ + /* Already done in brcm_irq_dispatch */ +} + +unsigned int startup_brcm_irq(unsigned int irq) +{ + enable_brcm_irq(irq); + + return 0; /* never anything pending */ +} + +unsigned int startup_brcm_none(unsigned int irq) +{ + return 0; +} + +void end_brcm_irq(unsigned int irq) +{ + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + enable_brcm_irq(irq); +} + +void end_brcm_none(unsigned int irq) +{ +} + +static struct hw_interrupt_type brcm_irq_type = { + .typename = "MIPS", + .startup = startup_brcm_irq, + .shutdown = disable_brcm_irq, + .enable = enable_brcm_irq, + .disable = disable_brcm_irq, + .ack = ack_brcm_irq, + .end = end_brcm_irq, + .set_affinity = NULL +}; + +static struct hw_interrupt_type brcm_irq_no_end_type = { + .typename = "MIPS", + .startup = startup_brcm_none, + .shutdown = disable_brcm_irq, + .enable = enable_brcm_irq, + .disable = disable_brcm_irq, + .ack = ack_brcm_irq, + .end = end_brcm_none, + .set_affinity = NULL +}; + +void __init arch_init_irq(void) +{ + int i; + + clear_c0_status(ST0_BEV); + change_c0_status(ST0_IM, (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4)); + + for (i = 0; i < NR_IRQS; i++) { + irq_desc[i].status = IRQ_DISABLED; + irq_desc[i].action = 0; + irq_desc[i].depth = 1; + irq_desc[i].chip = &brcm_irq_type; + } +} + +int request_external_irq(unsigned int irq, + FN_HANDLER handler, + unsigned long irqflags, + const char * devname, + void *dev_id) +{ + unsigned long flags; + + local_irq_save(flags); + + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_CLEAR_SHFT)); // Clear + PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_MASK_SHFT)); // Mask + PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_INSENS_SHFT)); // Edge insesnsitive + PERF->ExtIrqCfg |= (1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_LEVEL_SHFT)); // Level triggered + PERF->ExtIrqCfg &= ~(1 << (irq - INTERRUPT_ID_EXTERNAL_0 + EI_SENSE_SHFT)); // Low level + + local_irq_restore(flags); + + return( request_irq(irq, handler, irqflags, devname, dev_id) ); +} + +/* VxWorks compatibility function(s). */ + +unsigned int BcmHalMapInterrupt(FN_HANDLER pfunc, unsigned int param, + unsigned int interruptId) +{ + int nRet = -1; + char *devname; + + devname = kmalloc(16, GFP_KERNEL); + if (devname) + sprintf( devname, "brcm_%d", interruptId ); + + /* Set the IRQ description to not automatically enable the interrupt at + * the end of an ISR. The driver that handles the interrupt must + * explicitly call BcmHalInterruptEnable or enable_brcm_irq. This behavior + * is consistent with interrupt handling on VxWorks. + */ + irq_desc[interruptId].chip = &brcm_irq_no_end_type; + + if( interruptId >= INTERNAL_ISR_TABLE_OFFSET ) + { + printk("BcmHalMapInterrupt : internal IRQ\n"); + nRet = request_irq( interruptId, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT, devname, (void *) param ); + } + else if (interruptId >= INTERRUPT_ID_EXTERNAL_0 && interruptId <= INTERRUPT_ID_EXTERNAL_3) + { + printk("BcmHalMapInterrupt : external IRQ\n"); + nRet = request_external_irq( interruptId, pfunc, SA_SAMPLE_RANDOM | SA_INTERRUPT, devname, (void *) param ); + } + + return( nRet ); +} + + +EXPORT_SYMBOL(enable_brcm_irq); +EXPORT_SYMBOL(disable_brcm_irq); +EXPORT_SYMBOL(request_external_irq); +EXPORT_SYMBOL(BcmHalMapInterrupt); + diff --git a/target/linux/brcm63xx/files/arch/mips/bcm963xx/prom.c b/target/linux/brcm63xx/files/arch/mips/bcm963xx/prom.c new file mode 100644 index 000000000..e02d31c9e --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/bcm963xx/prom.c @@ -0,0 +1,73 @@ +/* + Copyright 2004 Broadcom Corp. All Rights Reserved. + Copyright 2007 OpenWrt,org, Florian Fainelli <florian@openwrt.org> + + This program is free software; you can distribute it and/or modify it + under the terms of the GNU General Public License (Version 2) as + published by the Free Software Foundation. + + This program is distributed in the hope 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. +*/ +/* + * prom.c: PROM library initialization code. + * + */ +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/bootmem.h> +#include <linux/blkdev.h> + +#include <asm/addrspace.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> +#include <asm/time.h> +#include <asm/mach-bcm963xx/bootloaders.h> +#include <asm/mach-bcm963xx/6348_map_part.h> + +#include "../cfe/cfe_private.h" + +extern void __init detect_bootloader(void); +extern void serial_init(void); +extern int boot_loader_type; + +#define MACH_BCM MACH_BCM96348 + +const char *get_system_type(void) +{ + return "Broadcom BCM963xx"; +} + +void __init prom_init(void) +{ + serial_init(); + + printk("%s prom init\n", get_system_type() ); + + PERF->IrqMask = 0; + + /* Detect the bootloader */ + detect_bootloader(); + + /* Do further initialisations depending on the bootloader */ + if (boot_loader_type == BOOT_LOADER_CFE || boot_loader_type == BOOT_LOADER_CFE2) { + cfe_setup(fw_arg0, fw_arg1, fw_arg2, fw_arg3); + } + /* Register 16MB RAM minus the ADSL SDRAM by default */ + add_memory_region(0, (0x01000000 - ADSL_SDRAM_IMAGE_SIZE), BOOT_MEM_RAM); + + mips_machgroup = MACH_GROUP_BRCM; + mips_machtype = MACH_BCM; +} + +void __init prom_free_prom_memory(void) +{ + /* We do not have any memory to free */ +} diff --git a/target/linux/brcm63xx/files/arch/mips/bcm963xx/ser_init.c b/target/linux/brcm63xx/files/arch/mips/bcm963xx/ser_init.c new file mode 100644 index 000000000..bb745aec6 --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/bcm963xx/ser_init.c @@ -0,0 +1,181 @@ +/* +<:copyright-gpl + Copyright 2004 Broadcom Corp. All Rights Reserved. + + This program is free software; you can distribute it and/or modify it + under the terms of the GNU General Public License (Version 2) as + published by the Free Software Foundation. + + This program is distributed in the hope 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. +:> +*/ +/* + * Broadcom bcm63xx serial port initialization, also prepare for printk + * by registering with console_init + * + */ + +#include <linux/autoconf.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/types.h> +#include <linux/console.h> +#include <linux/sched.h> + +#include <asm/addrspace.h> +#include <asm/irq.h> +#include <asm/reboot.h> +#include <asm/gdb-stub.h> +#include <asm/mc146818rtc.h> + +#include <bcm_map_part.h> +#include <6348_map_part.h> +#include <board.h> + +#define SER63XX_DEFAULT_BAUD 115200 +#define BD_BCM63XX_TIMER_CLOCK_INPUT (FPERIPH) +#define stUart ((volatile Uart * const) UART_BASE) + +// Transmit interrupts +#define TXINT (TXFIFOEMT | TXUNDERR | TXOVFERR) +// Receive interrupts +#define RXINT (RXFIFONE | RXOVFERR) + +/* -------------------------------------------------------------------------- + Name: serial_init + Purpose: Initalize the UART +-------------------------------------------------------------------------- */ +void __init serial_init(void) +{ + u32 tmpVal = SER63XX_DEFAULT_BAUD; + ULONG clockFreqHz; + +#if defined(CONFIG_BCM96345) + // Make sure clock is ticking + PERF->blkEnables |= UART_CLK_EN; +#endif + + /* Dissable channel's receiver and transmitter. */ + stUart->control &= ~(BRGEN|TXEN|RXEN); + + /*--------------------------------------------------------------------*/ + /* Write the table value to the clock select register. */ + /* DPullen - this is the equation to use: */ + /* value = clockFreqHz / baud / 32-1; */ + /* (snmod) Actually you should also take into account any necessary */ + /* rounding. Divide by 16, look at lsb, if 0, divide by 2 */ + /* and subtract 1. If 1, just divide by 2 */ + /*--------------------------------------------------------------------*/ + clockFreqHz = BD_BCM63XX_TIMER_CLOCK_INPUT; + tmpVal = (clockFreqHz / tmpVal) / 16; + if( tmpVal & 0x01 ) + tmpVal /= 2; //Rounding up, so sub is already accounted for + else + tmpVal = (tmpVal / 2) - 1; // Rounding down so we must sub 1 + stUart->baudword = tmpVal; + + /* Finally, re-enable the transmitter and receiver. */ + stUart->control |= (BRGEN|TXEN|RXEN); + + stUart->config = (BITS8SYM | ONESTOP); + // Set the FIFO interrupt depth ... stUart->fifocfg = 0xAA; + stUart->fifoctl = RSTTXFIFOS | RSTRXFIFOS; + stUart->intMask = 0; + stUart->intMask = RXINT | TXINT; +} + + +/* prom_putc() + * Output a character to the UART + */ +void prom_putc(char c) +{ + /* Wait for Tx uffer to empty */ + while (! (READ16(stUart->intStatus) & TXFIFOEMT)); + /* Send character */ + stUart->Data = c; +} + +/* prom_puts() + * Write a string to the UART + */ +void prom_puts(const char *s) +{ + while (*s) { + if (*s == '\n') { + prom_putc('\r'); + } + prom_putc(*s++); + } +} + + +/* prom_getc_nowait() + * Returns a character from the UART + * Returns -1 if no characters available or corrupted + */ +int prom_getc_nowait(void) +{ + uint16 uStatus; + int cData = -1; + + uStatus = READ16(stUart->intStatus); + + if (uStatus & RXFIFONE) { /* Do we have a character? */ + cData = READ16(stUart->Data) & 0xff; /* Read character */ + if (uStatus & (RXFRAMERR | RXPARERR)) { /* If we got an error, throw it away */ + cData = -1; + } + } + + return cData; +} + +/* prom_getc() + * Returns a charcter from the serial port + * Will block until it receives a valid character +*/ +char prom_getc(void) +{ + int cData = -1; + + /* Loop until we get a valid character */ + while(cData == -1) { + cData = prom_getc_nowait(); + } + return (char) cData; +} + +/* prom_testc() + * Returns 0 if no characters available + */ +int prom_testc(void) +{ + uint16 uStatus; + + uStatus = READ16(stUart->intStatus); + + return (uStatus & RXFIFONE); +} + +#if defined (CONFIG_REMOTE_DEBUG) +/* Prevent other code from writing to the serial port */ +void _putc(char c) { } +void _puts(const char *ptr) { } +#else +/* Low level outputs call prom routines */ +void _putc(char c) { + prom_putc(c); +} +void _puts(const char *ptr) { + prom_puts(ptr); +} +#endif diff --git a/target/linux/brcm63xx/files/arch/mips/bcm963xx/setup.c b/target/linux/brcm63xx/files/arch/mips/bcm963xx/setup.c new file mode 100644 index 000000000..70c6ebe60 --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/bcm963xx/setup.c @@ -0,0 +1,472 @@ +/* +<:copyright-gpl + Copyright 2002 Broadcom Corp. All Rights Reserved. + + This program is free software; you can distribute it and/or modify it + under the terms of the GNU General Public License (Version 2) as + published by the Free Software Foundation. + + This program is distributed in the hope 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. +:> +*/ +/* + * Generic setup routines for Broadcom 963xx MIPS boards + */ + +#include <linux/autoconf.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/kdev_t.h> +#include <linux/types.h> +#include <linux/console.h> +#include <linux/sched.h> +#include <linux/mm.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/bootmem.h> + +#include <asm/addrspace.h> +#include <asm/bcache.h> +#include <asm/irq.h> +#include <asm/time.h> +#include <asm/reboot.h> +#include <asm/gdb-stub.h> +#include <asm/bootinfo.h> +#include <asm/cpu.h> +#include <asm/mach-bcm963xx/bootloaders.h> + +extern void brcm_time_init(void); +extern int boot_loader_type; + +#include <linux/pci.h> +#include <linux/delay.h> +#include <bcm_map_part.h> +#include <6348_map_part.h> +#include <bcmpci.h> + +static volatile MpiRegisters * mpi = (MpiRegisters *)(MPI_BASE); + +/* This function should be in a board specific directory. For now, + * assume that all boards that include this file use a Broadcom chip + * with a soft reset bit in the PLL control register. + */ +static void brcm_machine_restart(char *command) +{ + const unsigned long ulSoftReset = 0x00000001; + unsigned long *pulPllCtrl = (unsigned long *) 0xfffe0008; + *pulPllCtrl |= ulSoftReset; +} + +static void brcm_machine_halt(void) +{ + printk("System halted\n"); + while (1); +} + +static void mpi_SetLocalPciConfigReg(uint32 reg, uint32 value) +{ + /* write index then value */ + mpi->pcicfgcntrl = PCI_CFG_REG_WRITE_EN + reg;; + mpi->pcicfgdata = value; +} + +static uint32 mpi_GetLocalPciConfigReg(uint32 reg) +{ + /* write index then get value */ + mpi->pcicfgcntrl = PCI_CFG_REG_WRITE_EN + reg;; + return mpi->pcicfgdata; +} + +/* + * mpi_ResetPcCard: Set/Reset the PcCard + */ +static void mpi_ResetPcCard(int cardtype, BOOL bReset) +{ + if (cardtype == MPI_CARDTYPE_NONE) { + return; + } + + if (cardtype == MPI_CARDTYPE_CARDBUS) { + bReset = ! bReset; + } + + if (bReset) { + mpi->pcmcia_cntl1 = (mpi->pcmcia_cntl1 & ~PCCARD_CARD_RESET); + } else { + mpi->pcmcia_cntl1 = (mpi->pcmcia_cntl1 | PCCARD_CARD_RESET); + } +} + +/* + * mpi_ConfigCs: Configure an MPI/EBI chip select + */ +static void mpi_ConfigCs(uint32 cs, uint32 base, uint32 size, uint32 flags) +{ + mpi->cs[cs].base = ((base & 0x1FFFFFFF) | size); + mpi->cs[cs].config = flags; +} + +/* + * mpi_InitPcmciaSpace + */ +static void mpi_InitPcmciaSpace(void) +{ + // ChipSelect 4 controls PCMCIA Memory accesses + mpi_ConfigCs(PCMCIA_COMMON_BASE, pcmciaMem, EBI_SIZE_1M, (EBI_WORD_WIDE|EBI_ENABLE)); + // ChipSelect 5 controls PCMCIA Attribute accesses + mpi_ConfigCs(PCMCIA_ATTRIBUTE_BASE, pcmciaAttr, EBI_SIZE_1M, (EBI_WORD_WIDE|EBI_ENABLE)); + // ChipSelect 6 controls PCMCIA I/O accesses + mpi_ConfigCs(PCMCIA_IO_BASE, pcmciaIo, EBI_SIZE_64K, (EBI_WORD_WIDE|EBI_ENABLE)); + + mpi->pcmcia_cntl2 = ((PCMCIA_ATTR_ACTIVE << RW_ACTIVE_CNT_BIT) | + (PCMCIA_ATTR_INACTIVE << INACTIVE_CNT_BIT) | + (PCMCIA_ATTR_CE_SETUP << CE_SETUP_CNT_BIT) | + (PCMCIA_ATTR_CE_HOLD << CE_HOLD_CNT_BIT)); + + mpi->pcmcia_cntl2 |= (PCMCIA_HALFWORD_EN | PCMCIA_BYTESWAP_DIS); +} + +/* + * cardtype_vcc_detect: PC Card's card detect and voltage sense connection + * + * CD1#/ CD2#/ VS1#/ VS2#/ Card Initial Vcc + * CCD1# CCD2# CVS1 CVS2 Type + * + * GND GND open open 16-bit 5 vdc + * + * GND GND GND open 16-bit 3.3 vdc + * + * GND GND open GND 16-bit x.x vdc + * + * GND GND GND GND 16-bit 3.3 & x.x vdc + * + *==================================================================== + * + * CVS1 GND CCD1# open CardBus 3.3 vdc + * + * GND CVS2 open CCD2# CardBus x.x vdc + * + * GND CVS1 CCD2# open CardBus y.y vdc + * + * GND CVS2 GND CCD2# CardBus 3.3 & x.x vdc + * + * CVS2 GND open CCD1# CardBus x.x & y.y vdc + * + * GND CVS1 CCD2# open CardBus 3.3, x.x & y.y vdc + * + */ +static int cardtype_vcc_detect(void) +{ + uint32 data32; + int cardtype; + + cardtype = MPI_CARDTYPE_NONE; + mpi->pcmcia_cntl1 = 0x0000A000; // Turn on the output enables and drive + // the CVS pins to 0. + data32 = mpi->pcmcia_cntl1; + switch (data32 & 0x00000003) // Test CD1# and CD2#, see if card is plugged in. + { + case 0x00000003: // No Card is in the slot. + printk("mpi: No Card is in the PCMCIA slot\n"); + break; + + case 0x00000002: // Partial insertion, No CD2#. + printk("mpi: Card in the PCMCIA slot partial insertion, no CD2 signal\n"); + break; + + case 0x00000001: // Partial insertion, No CD1#. + printk("mpi: Card in the PCMCIA slot partial insertion, no CD1 signal\n"); + break; + + case 0x00000000: + mpi->pcmcia_cntl1 = 0x0000A0C0; // Turn off the CVS output enables and + // float the CVS pins. + mdelay(1); + data32 = mpi->pcmcia_cntl1; + // Read the Register. + switch (data32 & 0x0000000C) // See what is on the CVS pins. + { + case 0x00000000: // CVS1 and CVS2 are tied to ground, only 1 option. + printk("mpi: Detected 3.3 & x.x 16-bit PCMCIA card\n"); + cardtype = MPI_CARDTYPE_PCMCIA; + break; + + case 0x00000004: // CVS1 is open or tied to CCD1/CCD2 and CVS2 is tied to ground. + // 2 valid voltage options. + switch (data32 & 0x00000003) // Test the values of CCD1 and CCD2. + { + case 0x00000003: // CCD1 and CCD2 are tied to 1 of the CVS pins. + // This is not a valid combination. + printk("mpi: Unknown card plugged into slot\n"); + break; + + case 0x00000002: // CCD2 is tied to either CVS1 or CVS2. + mpi->pcmcia_cntl1 = 0x0000A080; // Drive CVS1 to a 0. + mdelay(1); + data32 = mpi->pcmcia_cntl1; + if (data32 & 0x00000002) { // CCD2 is tied to CVS2, not valid. + printk("mpi: Unknown card plugged into slot\n"); + } else { // CCD2 is tied to CVS1. + printk("mpi: Detected 3.3, x.x and y.y Cardbus card\n"); + cardtype = MPI_CARDTYPE_CARDBUS; + } + break; + + case 0x00000001: // CCD1 is tied to either CVS1 or CVS2. + // This is not a valid combination. + printk("mpi: Unknown card plugged into slot\n"); + break; + + case 0x00000000: // CCD1 and CCD2 are tied to ground. + printk("mpi: Detected x.x vdc 16-bit PCMCIA card\n"); + cardtype = MPI_CARDTYPE_PCMCIA; + break; + } + break; + + case 0x00000008: // CVS2 is open or tied to CCD1/CCD2 and CVS1 is tied to ground. + // 2 valid voltage options. + switch (data32 & 0x00000003) // Test the values of CCD1 and CCD2. + { + case 0x00000003: // CCD1 and CCD2 are tied to 1 of the CVS pins. + // This is not a valid combination. + printk("mpi: Unknown card plugged into slot\n"); + break; + + case 0x00000002: // CCD2 is tied to either CVS1 or CVS2. + mpi->pcmcia_cntl1 = 0x0000A040; // Drive CVS2 to a 0. + mdelay(1); + data32 = mpi->pcmcia_cntl1; + if (data32 & 0x00000002) { // CCD2 is tied to CVS1, not valid. + printk("mpi: Unknown card plugged into slot\n"); + } else {// CCD2 is tied to CVS2. + printk("mpi: Detected 3.3 and x.x Cardbus card\n"); + cardtype = MPI_CARDTYPE_CARDBUS; + } + break; + + case 0x00000001: // CCD1 is tied to either CVS1 or CVS2. + // This is not a valid combination. + printk("mpi: Unknown card plugged into slot\n"); + break; + + case 0x00000000: // CCD1 and CCD2 are tied to ground. + cardtype = MPI_CARDTYPE_PCMCIA; + printk("mpi: Detected 3.3 vdc 16-bit PCMCIA card\n"); + break; + } + break; + + case 0x0000000C: // CVS1 and CVS2 are open or tied to CCD1/CCD2. + // 5 valid voltage options. + + switch (data32 & 0x00000003) // Test the values of CCD1 and CCD2. + { + case 0x00000003: // CCD1 and CCD2 are tied to 1 of the CVS pins. + // This is not a valid combination. + printk("mpi: Unknown card plugged into slot\n"); + break; + + case 0x00000002: // CCD2 is tied to either CVS1 or CVS2. + // CCD1 is tied to ground. + mpi->pcmcia_cntl1 = 0x0000A040; // Drive CVS2 to a 0. + mdelay(1); + data32 = mpi->pcmcia_cntl1; + if (data32 & 0x00000002) { // CCD2 is tied to CVS1. + printk("mpi: Detected y.y vdc Cardbus card\n"); + } else { // CCD2 is tied to CVS2. + printk("mpi: Detected x.x vdc Cardbus card\n"); + } + cardtype = MPI_CARDTYPE_CARDBUS; + break; + + case 0x00000001: // CCD1 is tied to either CVS1 or CVS2. + // CCD2 is tied to ground. + + mpi->pcmcia_cntl1 = 0x0000A040; // Drive CVS2 to a 0. + mdelay(1); + data32 = mpi->pcmcia_cntl1; + if (data32 & 0x00000001) {// CCD1 is tied to CVS1. + printk("mpi: Detected 3.3 vdc Cardbus card\n"); + } else { // CCD1 is tied to CVS2. + printk("mpi: Detected x.x and y.y Cardbus card\n"); + } + cardtype = MPI_CARDTYPE_CARDBUS; + break; + + case 0x00000000: // CCD1 and CCD2 are tied to ground. + cardtype = MPI_CARDTYPE_PCMCIA; + printk("mpi: Detected 5 vdc 16-bit PCMCIA card\n"); + break; + } + break; + + default: + printk("mpi: Unknown card plugged into slot\n"); + break; + + } + } + return cardtype; +} + +/* + * mpi_DetectPcCard: Detect the plugged in PC-Card + * Return: < 0 => Unknown card detected + * 0 => No card detected + * 1 => 16-bit card detected + * 2 => 32-bit CardBus card detected + */ +static int mpi_DetectPcCard(void) +{ + int cardtype; + + cardtype = cardtype_vcc_detect(); + switch(cardtype) { + case MPI_CARDTYPE_PCMCIA: + mpi->pcmcia_cntl1 &= ~0x0000e000; // disable enable bits + //mpi->pcmcia_cntl1 = (mpi->pcmcia_cntl1 & ~PCCARD_CARD_RESET); + mpi->pcmcia_cntl1 |= (PCMCIA_ENABLE | PCMCIA_GPIO_ENABLE); + mpi_InitPcmciaSpace(); + mpi_ResetPcCard(cardtype, FALSE); + // Hold card in reset for 10ms + mdelay(10); + mpi_ResetPcCard(cardtype, TRUE); + // Let card come out of reset + mdelay(100); + break; + case MPI_CARDTYPE_CARDBUS: + // 8 => CardBus Enable + // 1 => PCI Slot Number + // C => Float VS1 & VS2 + mpi->pcmcia_cntl1 = (mpi->pcmcia_cntl1 & 0xFFFF0000) | + CARDBUS_ENABLE | + (CARDBUS_SLOT << 8)| + VS2_OEN | + VS1_OEN; + /* access to this memory window will be to/from CardBus */ + mpi->l2pmremap1 |= CARDBUS_MEM; + + // Need to reset the Cardbus Card. There's no CardManager to do this, + // and we need to be ready for PCI configuration. + mpi_ResetPcCard(cardtype, FALSE); + // Hold card in reset for 10ms + mdelay(10); + mpi_ResetPcCard(cardtype, TRUE); + // Let card come out of reset + mdelay(100); + break; + default: + break; + } + return cardtype; +} + +static int mpi_init(void) +{ + unsigned long data; + unsigned int chipid, chiprev, sdramsize; + + printk("Broadcom BCM963xx MPI\n"); + chipid = (PERF->RevID & 0xFFFF0000) >> 16; + chiprev = (PERF->RevID & 0xFF); + + if (boot_loader_type == BOOT_LOADER_CFE) + sdramsize = boot_mem_map.map[0].size; + else + sdramsize = 0x01000000; + /* + * Init the pci interface + */ + data = GPIO->GPIOMode; // GPIO mode register + data |= GROUP2_PCI | GROUP1_MII_PCCARD; // PCI internal arbiter + Cardbus + GPIO->GPIOMode = data; // PCI internal arbiter + + /* + * In the BCM6348 CardBus support is defaulted to Slot 0 + * because there is no external IDSEL for CardBus. To disable + * the CardBus and allow a standard PCI card in Slot 0 + * set the cbus_idsel field to 0x1f. + */ + /* + uData = mpi->pcmcia_cntl1; + uData |= CARDBUS_IDSEL; + mpi->pcmcia_cntl1 = uData; + */ + // Setup PCI I/O Window range. Give 64K to PCI I/O + mpi->l2piorange = ~(BCM_PCI_IO_SIZE_64KB-1); + // UBUS to PCI I/O base address + mpi->l2piobase = BCM_PCI_IO_BASE & BCM_PCI_ADDR_MASK; + // UBUS to PCI I/O Window remap + mpi->l2pioremap = (BCM_PCI_IO_BASE | MEM_WINDOW_EN); + + // enable PCI related GPIO pins and data swap between system and PCI bus + mpi->locbuscntrl = (EN_PCI_GPIO | DIR_U2P_NOSWAP); + + /* Enable 6348 BusMaster and Memory access mode */ + data = mpi_GetLocalPciConfigReg(PCI_COMMAND); + data |= (PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER); + mpi_SetLocalPciConfigReg(PCI_COMMAND, data); + + /* Configure two 16 MByte PCI to System memory regions. */ + /* These memory regions are used when PCI device is a bus master */ + /* Accesses to the SDRAM from PCI bus will be "byte swapped" for this region */ + mpi_SetLocalPciConfigReg(PCI_BASE_ADDRESS_3, BCM_HOST_MEM_SPACE1); + mpi->sp0remap = 0x0; + + /* Accesses to the SDRAM from PCI bus will not be "byte swapped" for this region */ + mpi_SetLocalPciConfigReg(PCI_BASE_ADDRESS_4, BCM_HOST_MEM_SPACE2); + mpi->sp1remap = 0x0; + mpi->pcimodesel |= (PCI_BAR2_NOSWAP | 0x40); + + if ((chipid == 0x6348) && (chiprev == 0xb0)) { + mpi->sp0range = ~(sdramsize-1); + mpi->sp1range = ~(sdramsize-1); + } + /* + * Change 6348 PCI Cfg Reg. offset 0x40 to PCI memory read retry count infinity + * by set 0 in bit 8~15. This resolve read Bcm4306 srom return 0xffff in + * first read. + */ + data = mpi_GetLocalPciConfigReg(BRCM_PCI_CONFIG_TIMER); + data &= ~BRCM_PCI_CONFIG_TIMER_RETRY_MASK; + data |= 0x00000080; + mpi_SetLocalPciConfigReg(BRCM_PCI_CONFIG_TIMER, data); + + /* enable pci interrupt */ + mpi->locintstat |= (EXT_PCI_INT << 16); + + mpi_DetectPcCard(); + + ioport_resource.start = BCM_PCI_IO_BASE; + ioport_resource.end = BCM_PCI_IO_BASE + BCM_PCI_IO_SIZE_64KB; + +#if defined(CONFIG_USB) + PERF->blkEnables |= USBH_CLK_EN; + mdelay(100); + *USBH_NON_OHCI = NON_OHCI_BYTE_SWAP; +#endif + + return 0; +} + +void __init plat_mem_setup(void) +{ + _machine_restart = brcm_machine_restart; + _machine_halt = brcm_machine_halt; + pm_power_off = brcm_machine_halt; + + board_time_init = brcm_time_init; + + /* mpi initialization */ + mpi_init(); +} diff --git a/target/linux/brcm63xx/files/arch/mips/bcm963xx/time.c b/target/linux/brcm63xx/files/arch/mips/bcm963xx/time.c new file mode 100644 index 000000000..8b82ada37 --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/bcm963xx/time.c @@ -0,0 +1,116 @@ +/* +<:copyright-gpl + Copyright 2004 Broadcom Corp. All Rights Reserved. + + This program is free software; you can distribute it and/or modify it + under the terms of the GNU General Public License (Version 2) as + published by the Free Software Foundation. + + This program is distributed in the hope 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 time for Broadcom 963xx MIPS boards + */ + +#include <linux/autoconf.h> +#include <linux/init.h> +#include <linux/kernel_stat.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/time.h> +#include <linux/timex.h> + +#include <asm/mipsregs.h> +#include <asm/ptrace.h> +#include <asm/div64.h> +#include <asm/time.h> + +#include <6348_map_part.h> +#include <6348_intr.h> +#include <bcm_map_part.h> +#include <bcm_intr.h> + +static unsigned long r4k_offset; /* Amount to increment compare reg each time */ +static unsigned long r4k_cur; /* What counter should be at next timer irq */ + +/* ********************************************************************* + * calculateCpuSpeed() + * Calculate the BCM6348 CPU speed by reading the PLL strap register + * and applying the following formula: + * cpu_clk = (.25 * 64MHz freq) * (N1 + 1) * (N2 + 2) / (M1_CPU + 1) + * Input parameters: + * none + * Return value: + * none + ********************************************************************* */ + +static inline unsigned long __init calculateCpuSpeed(void) +{ + u32 pllStrap = PERF->PllStrap; + int n1 = (pllStrap & PLL_N1_MASK) >> PLL_N1_SHFT; + int n2 = (pllStrap & PLL_N2_MASK) >> PLL_N2_SHFT; + int m1cpu = (pllStrap & PLL_M1_CPU_MASK) >> PLL_M1_CPU_SHFT; + + return (16 * (n1 + 1) * (n2 + 2) / (m1cpu + 1)) * 1000000; +} + + +static inline unsigned long __init cal_r4koff(void) +{ + mips_hpt_frequency = calculateCpuSpeed() / 2; + return (mips_hpt_frequency / HZ); +} + + +/* + * There are a lot of conceptually broken versions of the MIPS timer interrupt + * handler floating around. This one is rather different, but the algorithm + * is provably more robust. + */ +irqreturn_t brcm_timer_interrupt(struct pt_regs *regs) +{ + int irq = MIPS_TIMER_INT; + + irq_enter(); + kstat_this_cpu.irqs[irq]++; + + timer_interrupt(irq, regs); + irq_exit(); + return IRQ_HANDLED; +} + + +void __init brcm_time_init(void) +{ + unsigned int est_freq, flags; + local_irq_save(flags); + + printk("calculating r4koff... "); + r4k_offset = cal_r4koff(); + printk("%08lx(%d)\n", r4k_offset, (int)r4k_offset); + + est_freq = 2 * r4k_offset * HZ; + est_freq += 5000; /* round */ + est_freq -= est_freq % 10000; + printk("CPU frequency %d.%02d MHz\n", est_freq / 1000000, + (est_freq % 1000000) * 100 / 1000000); + local_irq_restore(flags); +} + + +void __init plat_timer_setup(struct irqaction *irq) +{ + r4k_cur = (read_c0_count() + r4k_offset); + write_c0_compare(r4k_cur); + set_c0_status(IE_IRQ5); +} diff --git a/target/linux/brcm63xx/files/arch/mips/bcm963xx/wdt.c b/target/linux/brcm63xx/files/arch/mips/bcm963xx/wdt.c new file mode 100644 index 000000000..0ea36a67f --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/bcm963xx/wdt.c @@ -0,0 +1,246 @@ +/* + * Watchdog driver for the BCM963xx devices + * + * Copyright (C) 2007 OpenWrt.org + * Florian Fainelli <florian@openwrt.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. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/miscdevice.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/notifier.h> +#include <linux/watchdog.h> +#include <linux/timer.h> +#include <linux/jiffies.h> +#include <linux/completion.h> +#include <linux/ioport.h> + +typedef struct bcm963xx_timer { + unsigned short unused0; + unsigned char timer_mask; +#define TIMER0EN 0x01 +#define TIMER1EN 0x02 +#define TIMER2EN 0x04 + unsigned char timer_ints; +#define TIMER0 0x01 +#define TIMER1 0x02 +#define TIMER2 0x04 +#define WATCHDOG 0x08 + unsigned long timer_ctl0; + unsigned long timer_ctl1; + unsigned long timer_ctl2; +#define TIMERENABLE 0x80000000 +#define RSTCNTCLR 0x40000000 + unsigned long timer_cnt0; + unsigned long timer_cnt1; + unsigned long timer_cnt2; + unsigned long wdt_def_count; + + /* Write 0xff00 0x00ff to Start timer + * Write 0xee00 0x00ee to Stop and re-load default count + * Read from this register returns current watch dog count + */ + unsigned long wdt_ctl; + + /* Number of 40-MHz ticks for WD Reset pulse to last */ + unsigned long wdt_rst_count; +} bcm963xx_timer; + +static struct bcm963xx_wdt_device { + struct completion stop; + volatile int running; + struct timer_list timer; + volatile int queue; + int default_ticks; + unsigned long inuse; +} bcm963xx_wdt_device; + +static int ticks = 1000; + +#define WDT_BASE 0xfffe0200 +#define WDT ((volatile bcm963xx_timer * const) WDT_BASE) + +#define BCM963XX_INTERVAL (HZ/10+1) + +static void bcm963xx_wdt_trigger(unsigned long unused) +{ + if (bcm963xx_wdt_device.running) + ticks--; + + /* Load the default ticking value into the reset counter register */ + WDT->wdt_rst_count = bcm963xx_wdt_device.default_ticks; + + if (bcm963xx_wdt_device.queue && ticks) { + bcm963xx_wdt_device.timer.expires = jiffies + BCM963XX_INTERVAL; + add_timer(&bcm963xx_wdt_device.timer); + } + else { + complete(&bcm963xx_wdt_device.stop); + } +} + +static void bcm963xx_wdt_reset(void) +{ + ticks = bcm963xx_wdt_device.default_ticks; + /* Also reload default count */ + WDT->wdt_def_count = ticks; + WDT->wdt_ctl = 0xee00; + WDT->wdt_ctl = 0x00ee; +} + +static void bcm963xx_wdt_start(void) +{ + if (!bcm963xx_wdt_device.queue) { + bcm963xx_wdt_device.queue; + /* Enable the watchdog by writing 0xff00 ,then 0x00ff to the control register */ + WDT->wdt_ctl = 0xff00; + WDT->wdt_ctl = 0x00ff; + bcm963xx_wdt_device.timer.expires = jiffies + BCM963XX_INTERVAL; + add_timer(&bcm963xx_wdt_device.timer); + } + bcm963xx_wdt_device.running++; +} + +static int bcm963xx_wdt_stop(void) +{ + if (bcm963xx_wdt_device.running) + bcm963xx_wdt_device.running = 0; + + ticks = bcm963xx_wdt_device.default_ticks; + + /* Stop the watchdog by writing 0xee00 then 0x00ee to the control register */ + WDT->wdt_ctl = 0xee00; + WDT->wdt_ctl = 0x00ee; + + return -EIO; +} + +static int bcm963xx_wdt_open(struct inode *inode, struct file *file) +{ + if (test_and_set_bit(0, &bcm963xx_wdt_device.inuse)) + return -EBUSY; + return nonseekable_open(inode, file); +} + +static int bcm963xx_wdt_release(struct inode *inode, struct file *file) +{ + clear_bit(0, &bcm963xx_wdt_device.inuse); + return 0; +} + +static int bcm963xx_wdt_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + void __user *argp = (void __user *)arg; + unsigned int value; + + static struct watchdog_info ident = { + .options = WDIOF_CARDRESET, + .identity = "BCM963xx WDT", + }; + + switch (cmd) { + case WDIOC_KEEPALIVE: + bcm963xx_wdt_reset(); + break; + case WDIOC_GETSTATUS: + /* Reading from the control register will return the current value */ + value = WDT->wdt_ctl; + if ( copy_to_user(argp, &value, sizeof(int)) ) + return -EFAULT; + break; + case WDIOC_GETSUPPORT: + if ( copy_to_user(argp, &ident, sizeof(ident)) ) + return -EFAULT; + break; + case WDIOC_SETOPTIONS: + if ( copy_from_user(&value, argp, sizeof(int)) ) + return -EFAULT; + switch(value) { + case WDIOS_ENABLECARD: + bcm963xx_wdt_start(); + break; + case WDIOS_DISABLECARD: + bcm963xx_wdt_stop(); + break; + default: + return -EINVAL; + } + break; + default: + return -ENOTTY; + } + return 0; +} + +static int bcm963xx_wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) +{ + if (!count) + return -EIO; + bcm963xx_wdt_reset(); + return count; +} + +static const struct file_operations bcm963xx_wdt_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = bcm963xx_wdt_write, + .ioctl = bcm963xx_wdt_ioctl, + .open = bcm963xx_wdt_open, + .release = bcm963xx_wdt_release, +}; + +static struct miscdevice bcm963xx_wdt_miscdev = { + .minor = WATCHDOG_MINOR, + .name = "watchdog", + .fops = &bcm963xx_wdt_fops, +}; + +static void __exit bcm963xx_wdt_exit(void) +{ + if (bcm963xx_wdt_device.queue ){ + bcm963xx_wdt_device.queue = 0; + wait_for_completion(&bcm963xx_wdt_device.stop); + } + misc_deregister(&bcm963xx_wdt_miscdev); +} + +static int __init bcm963xx_wdt_init(void) +{ + int ret = 0; + + printk("Broadcom BCM963xx Watchdog timer\n"); + + ret = misc_register(&bcm963xx_wdt_miscdev); + if (ret) { + printk(KERN_CRIT "Cannot register miscdev on minor=%d (err=%d)\n", WATCHDOG_MINOR, ret); + return ret; + } + init_completion(&bcm963xx_wdt_device.stop); + bcm963xx_wdt_device.queue = 0; + + clear_bit(0, &bcm963xx_wdt_device.inuse); + + init_timer(&bcm963xx_wdt_device.timer); + bcm963xx_wdt_device.timer.function = bcm963xx_wdt_trigger; + bcm963xx_wdt_device.timer.data = 0; + + bcm963xx_wdt_device.default_ticks = ticks; + return ret; +} + + +module_init(bcm963xx_wdt_init); +module_exit(bcm963xx_wdt_exit); + +MODULE_AUTHOR("Florian Fainelli <florian@openwrt.org>"); +MODULE_DESCRIPTION("Broadcom BCM963xx Watchdog driver"); +MODULE_LICENSE("GPL"); diff --git a/target/linux/brcm63xx/files/arch/mips/cfe/Makefile b/target/linux/brcm63xx/files/arch/mips/cfe/Makefile new file mode 100644 index 000000000..d9f046adf --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/cfe/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the Broadcom Common Firmware Environment support +# + +obj-y += cfe.o diff --git a/target/linux/brcm63xx/files/arch/mips/cfe/cfe.c b/target/linux/brcm63xx/files/arch/mips/cfe/cfe.c new file mode 100644 index 000000000..6d16111e1 --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/cfe/cfe.c @@ -0,0 +1,533 @@ +/* + * Broadcom Common Firmware Environment (CFE) support + * + * Copyright 2000, 2001, 2002 + * Broadcom Corporation. All rights reserved. + * + * Copyright (C) 2006 Michael Buesch + * + * Original Authors: Mitch Lichtenberg, Chris Demetriou + * + * This software is furnished under license and may be used and copied only + * in accordance with the following terms and conditions. Subject to these + * conditions, you may download, copy, install, use, modify and distribute + * modified or unmodified copies of this software in source and/or binary + * form. No title or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce and + * retain this copyright notice and list of conditions as they appear in + * the source file. + * + * 2) No right is granted to use any trade name, trademark, or logo of + * Broadcom Corporation. The "Broadcom Corporation" name may not be + * used to endorse or promote products derived from this software + * without the prior written permission of Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM BE LIABLE + * FOR ANY DAMAGES WHATSOEVER, AND IN PARTICULAR, BROADCOM SHALL NOT BE + * LIABLE FOR DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE), EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <linux/init.h> +#include <linux/string.h> +#include <linux/errno.h> +#include <linux/spinlock.h> +#include <asm/cfe.h> + +#include "cfe_private.h" + + +static cfe_uint_t cfe_handle; +static int (*cfe_trampoline)(long handle, long iocb); + + +#include <linux/kernel.h> + +void __init cfe_setup(unsigned long fwarg0, unsigned long fwarg1, + unsigned long fwarg2, unsigned long fwarg3) +{ + if (fwarg3 == 0x80300000) { + /* WRT54G workaround */ + fwarg3 = CFE_EPTSEAL; + fwarg2 = 0xBFC00500; + } + if (fwarg3 != CFE_EPTSEAL) { + /* We are not booted from CFE */ + return; + } + if (fwarg1 == 0) { + /* We are on the boot CPU */ + cfe_handle = (cfe_uint_t)fwarg0; + cfe_trampoline = CFE_TO_PTR(fwarg2); + } +} + +int cfe_vprintk(const char *fmt, va_list args) +{ + static char buffer[1024]; + static DEFINE_SPINLOCK(lock); + static const char pfx[] = "CFE-console: "; + static const size_t pfx_len = sizeof(pfx) - 1; + unsigned long flags; + int len, cnt, pos; + int handle; + int res; + + if (!cfe_present()) + return -ENODEV; + + spin_lock_irqsave(&lock, flags); + handle = cfe_getstdhandle(CFE_STDHANDLE_CONSOLE); + if (CFE_ISERR(handle)) { + len = -EIO; + goto out; + } + strcpy(buffer, pfx); + len = vscnprintf(buffer + pfx_len, + sizeof(buffer) - pfx_len - 2, + fmt, args); + len += pfx_len; + /* The CFE console requires CR-LF line-ends. + * Add a CR, if we only terminate lines with a LF. + * This does only fix CR-LF at the end of the string. + * So for multiple lines, use multiple cfe_vprintk calls. + */ + if (len > 1 && + buffer[len - 1] == '\n' && buffer[len - 2] != '\r') { + buffer[len - 1] = '\r'; + buffer[len] = '\n'; + len += 1; + } + cnt = len; + pos = 0; + while (cnt > 0) { + res = cfe_write(handle, buffer + pos, len - pos); + if (CFE_ISERR(res)) { + len = -EIO; + goto out; + } + cnt -= res; + pos += res; + } +out: + spin_unlock_irqrestore(&lock, flags); + + return len; +} + +int cfe_printk(const char *fmt, ...) +{ + va_list args; + int res; + + va_start(args, fmt); + res = cfe_vprintk(fmt, args); + va_end(args); + + return res; +} + +static int cfe_iocb_dispatch(struct cfe_iocb *iocb) +{ + if (!cfe_present()) + return CFE_ERR_UNSUPPORTED; + return cfe_trampoline((long)cfe_handle, (long)iocb); +} + +int cfe_present(void) +{ + return (cfe_trampoline != NULL); +} + +int cfe_close(int handle) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_DEV_CLOSE; + iocb.handle = handle; + + err = cfe_iocb_dispatch(&iocb); + + return (CFE_ISERR(err)) ? err : iocb.status; +} + +int cfe_cpu_start(int cpu, void (*fn)(void), long sp, long gp, long a1) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_FW_CPUCTL; + iocb.psize = sizeof(struct cfe_iocb_cpuctl); + iocb.cpuctl.number = cpu; + iocb.cpuctl.command = CFE_CPU_CMD_START; + iocb.cpuctl.gp = gp; + iocb.cpuctl.sp = sp; + iocb.cpuctl.a1 = a1; + iocb.cpuctl.start_addr = (long)fn; + + err = cfe_iocb_dispatch(&iocb); + + return (CFE_ISERR(err)) ? err : iocb.status; +} + +int cfe_cpu_stop(int cpu) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_FW_CPUCTL; + iocb.psize = sizeof(struct cfe_iocb_cpuctl); + iocb.cpuctl.number = cpu; + iocb.cpuctl.command = CFE_CPU_CMD_STOP; + + err = cfe_iocb_dispatch(&iocb); + + return (CFE_ISERR(err)) ? err : iocb.status; +} + +int cfe_enumenv(int idx, char *name, int namelen, char *val, int vallen) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_ENV_ENUM; + iocb.psize = sizeof(struct cfe_iocb_envbuf); + iocb.envbuf.index = idx; + iocb.envbuf.name = PTR_TO_CFE(name); + iocb.envbuf.name_len = namelen; + iocb.envbuf.val = PTR_TO_CFE(val); + iocb.envbuf.val_len = vallen; + + err = cfe_iocb_dispatch(&iocb); + + return (CFE_ISERR(err)) ? err : iocb.status; +} + +int cfe_enumdev(int idx, char *name, int namelen) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + + iocb.fcode = CFE_CMD_DEV_ENUM; + iocb.psize = sizeof(struct cfe_iocb_envbuf); + iocb.envbuf.index = idx; + iocb.envbuf.name = PTR_TO_CFE(name); + iocb.envbuf.name_len = namelen; + + err = cfe_iocb_dispatch(&iocb); + + return (CFE_ISERR(err)) ? err : iocb.status; +} + +int cfe_enummem(int idx, int flags, u64 *start, u64 *length, + u64 *type) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + + iocb.fcode = CFE_CMD_FW_MEMENUM; + iocb.flags = flags; + iocb.psize = sizeof(struct cfe_iocb_meminfo); + iocb.meminfo.index = idx; + + err = cfe_iocb_dispatch(&iocb); + if (CFE_ISERR(err)) + return err; + if (!CFE_ISERR(iocb.status)) { + *start = iocb.meminfo.addr; + *length = iocb.meminfo.size; + *type = iocb.meminfo.type; + } + + return iocb.status; +} + +int cfe_exit(int warm, int status) +{ + struct cfe_iocb iocb; + int err; + +printk("CFE REBOOT\n"); + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_FW_RESTART; + if (warm) + iocb.flags = CFE_FLG_WARMSTART; + iocb.psize = sizeof(struct cfe_iocb_exitstat); + iocb.exitstat.status = status; + +printk("CALL\n"); + err = cfe_iocb_dispatch(&iocb); +printk("DONE\n"); + + return (CFE_ISERR(err)) ? err : iocb.status; +} + +int cfe_flushcache(int flags) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_FW_FLUSHCACHE; + iocb.flags = flags; + + err = cfe_iocb_dispatch(&iocb); + + return (CFE_ISERR(err)) ? err : iocb.status; +} + +int cfe_getdevinfo(char *name) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_DEV_GETINFO; + iocb.psize = sizeof(struct cfe_iocb_buf); + iocb.buffer.ptr = PTR_TO_CFE(name); + iocb.buffer.length = strlen(name); + + err = cfe_iocb_dispatch(&iocb); + if (CFE_ISERR(err)) + return err; + if (CFE_ISERR(iocb.status)) + return iocb.status; + + return iocb.buffer.devflags; +} + +int cfe_getenv(char *name, char *dest, int destlen) +{ + struct cfe_iocb iocb; + int err; + + dest[0] = '\0'; + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_ENV_GET; + iocb.psize = sizeof(struct cfe_iocb_envbuf); + iocb.envbuf.name = PTR_TO_CFE(name); + iocb.envbuf.name_len = strlen(name); + iocb.envbuf.val = PTR_TO_CFE(dest); + iocb.envbuf.val_len = destlen; + + err = cfe_iocb_dispatch(&iocb); + + return (CFE_ISERR(err)) ? err : iocb.status; +} + +int cfe_getfwinfo(struct cfe_fwinfo *info) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_FW_GETINFO; + iocb.psize = sizeof(struct cfe_iocb_fwinfo); + + err = cfe_iocb_dispatch(&iocb); + if (CFE_ISERR(err)) + return err; + if (CFE_ISERR(iocb.status)) + return err; + + info->version = iocb.fwinfo.version; + info->totalmem = iocb.fwinfo.totalmem; + info->flags = iocb.fwinfo.flags; + info->boardid = iocb.fwinfo.boardid; + info->bootarea_va = iocb.fwinfo.bootarea_va; + info->bootarea_pa = iocb.fwinfo.bootarea_pa; + info->bootarea_size = iocb.fwinfo.bootarea_size; + + return iocb.status; +} + +int cfe_getstdhandle(int handletype) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_DEV_GETHANDLE; + iocb.flags = handletype; + + err = cfe_iocb_dispatch(&iocb); + if (CFE_ISERR(err)) + return err; + if (CFE_ISERR(iocb.status)) + return iocb.status; + + return iocb.handle; +} + +int cfe_getticks(s64 *ticks) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_FW_GETTIME; + iocb.psize = sizeof(struct cfe_iocb_time); + + err = cfe_iocb_dispatch(&iocb); + if (CFE_ISERR(err)) + return err; + if (!CFE_ISERR(iocb.status)) + *ticks = iocb.time.ticks; + + return iocb.status; +} + +int cfe_inpstat(int handle) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_DEV_INPSTAT; + iocb.handle = handle; + iocb.psize = sizeof(struct cfe_iocb_inpstat); + + err = cfe_iocb_dispatch(&iocb); + if (CFE_ISERR(err)) + return err; + if (CFE_ISERR(iocb.status)) + return iocb.status; + + return iocb.inpstat.status; +} + +int cfe_ioctl(int handle, unsigned int ioctlnum, + unsigned char *buffer, int length, + int *retlen, u64 offset) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_DEV_IOCTL; + iocb.handle = handle; + iocb.psize = sizeof(struct cfe_iocb_buf); + iocb.buffer.offset = offset; + iocb.buffer.ioctlcmd = ioctlnum; + iocb.buffer.ptr = PTR_TO_CFE(buffer); + iocb.buffer.length = length; + + err = cfe_iocb_dispatch(&iocb); + if (CFE_ISERR(err)) + return err; + if (CFE_ISERR(iocb.status)) + return iocb.status; + if (retlen) + *retlen = iocb.buffer.retlen; + + return iocb.status; +} + +int cfe_open(char *name) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_DEV_OPEN; + iocb.psize = sizeof(struct cfe_iocb_buf); + iocb.buffer.ptr = PTR_TO_CFE(name); + iocb.buffer.length = strlen(name); + + err = cfe_iocb_dispatch(&iocb); + if (CFE_ISERR(err)) + return err; + if (CFE_ISERR(iocb.status)) + return iocb.status; + + return iocb.handle; +} + +int cfe_read(int handle, unsigned char *buffer, int length) +{ + return cfe_readblk(handle, 0, buffer, length); +} + +int cfe_readblk(int handle, s64 offset, unsigned char *buffer, int length) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_DEV_READ; + iocb.handle = handle; + iocb.psize = sizeof(struct cfe_iocb_buf); + iocb.buffer.offset = offset; + iocb.buffer.ptr = PTR_TO_CFE(buffer); + iocb.buffer.length = length; + + err = cfe_iocb_dispatch(&iocb); + if (CFE_ISERR(err)) + return err; + if (CFE_ISERR(iocb.status)) + return iocb.status; + + return iocb.buffer.retlen; +} + +int cfe_setenv(char *name, char *val) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_ENV_SET; + iocb.psize = sizeof(struct cfe_iocb_envbuf); + iocb.envbuf.name = PTR_TO_CFE(name); + iocb.envbuf.name_len = strlen(name); + iocb.envbuf.val = PTR_TO_CFE(val); + iocb.envbuf.val_len = strlen(val); + + err = cfe_iocb_dispatch(&iocb); + + return (CFE_ISERR(err)) ? err : iocb.status; +} + +int cfe_write(int handle, unsigned char *buffer, int length) +{ + return cfe_writeblk(handle, 0, buffer, length); +} + +int cfe_writeblk(int handle, s64 offset, unsigned char *buffer, int length) +{ + struct cfe_iocb iocb; + int err; + + memset(&iocb, 0, sizeof(iocb)); + iocb.fcode = CFE_CMD_DEV_WRITE; + iocb.handle = handle; + iocb.psize = sizeof(struct cfe_iocb_buf); + iocb.buffer.offset = offset; + iocb.buffer.ptr = PTR_TO_CFE(buffer); + iocb.buffer.length = length; + + err = cfe_iocb_dispatch(&iocb); + if (CFE_ISERR(err)) + return err; + if (CFE_ISERR(iocb.status)) + return iocb.status; + + return iocb.buffer.retlen; +} diff --git a/target/linux/brcm63xx/files/arch/mips/cfe/cfe_private.h b/target/linux/brcm63xx/files/arch/mips/cfe/cfe_private.h new file mode 100644 index 000000000..0a604d3bb --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/cfe/cfe_private.h @@ -0,0 +1,176 @@ +/* + * Broadcom Common Firmware Environment (CFE) support + * + * Copyright 2000, 2001, 2002 + * Broadcom Corporation. All rights reserved. + * + * Copyright (C) 2006 Michael Buesch + * + * Original Authors: Mitch Lichtenberg, Chris Demetriou + * + * This software is furnished under license and may be used and copied only + * in accordance with the following terms and conditions. Subject to these + * conditions, you may download, copy, install, use, modify and distribute + * modified or unmodified copies of this software in source and/or binary + * form. No title or ownership is transferred hereby. + * + * 1) Any source code used, modified or distributed must reproduce and + * retain this copyright notice and list of conditions as they appear in + * the source file. + * + * 2) No right is granted to use any trade name, trademark, or logo of + * Broadcom Corporation. The "Broadcom Corporation" name may not be + * used to endorse or promote products derived from this software + * without the prior written permission of Broadcom Corporation. + * + * 3) THIS SOFTWARE IS PROVIDED "AS-IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING BUT NOT LIMITED TO, ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR + * NON-INFRINGEMENT ARE DISCLAIMED. IN NO EVENT SHALL BROADCOM BE LIABLE + * FOR ANY DAMAGES WHATSOEVER, AND IN PARTICULAR, BROADCOM SHALL NOT BE + * LIABLE FOR DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE), EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LINUX_CFE_PRIVATE_H_ +#define LINUX_CFE_PRIVATE_H_ + +#ifndef __ASSEMBLY__ + +/* Seal indicating CFE's presence, passed to the kernel. */ +#define CFE_EPTSEAL 0x43464531 + +#define CFE_CMD_FW_GETINFO 0 +#define CFE_CMD_FW_RESTART 1 +#define CFE_CMD_FW_BOOT 2 +#define CFE_CMD_FW_CPUCTL 3 +#define CFE_CMD_FW_GETTIME 4 +#define CFE_CMD_FW_MEMENUM 5 +#define CFE_CMD_FW_FLUSHCACHE 6 + +#define CFE_CMD_DEV_GETHANDLE 9 +#define CFE_CMD_DEV_ENUM 10 +#define CFE_CMD_DEV_OPEN 11 +#define CFE_CMD_DEV_INPSTAT 12 +#define CFE_CMD_DEV_READ 13 +#define CFE_CMD_DEV_WRITE 14 +#define CFE_CMD_DEV_IOCTL 15 +#define CFE_CMD_DEV_CLOSE 16 +#define CFE_CMD_DEV_GETINFO 17 + +#define CFE_CMD_ENV_ENUM 20 +#define CFE_CMD_ENV_GET 22 +#define CFE_CMD_ENV_SET 23 +#define CFE_CMD_ENV_DEL 24 + +#define CFE_CMD_MAX 32 + +#define CFE_CMD_VENDOR_USE 0x8000 /* codes above this are for customer use */ + +typedef u64 cfe_uint_t; +typedef s64 cfe_int_t; +typedef s64 cfe_ptr_t; + +/* Cast a pointer from native to CFE-API pointer and back */ +#define CFE_TO_PTR(p) ((void *)(unsigned long)(p)) +#define PTR_TO_CFE(p) ((cfe_ptr_t)(unsigned long)(p)) + +struct cfe_iocb_buf { + cfe_uint_t offset; /* offset on device (bytes) */ + cfe_ptr_t ptr; /* pointer to a buffer */ + cfe_uint_t length; /* length of this buffer */ + cfe_uint_t retlen; /* returned length (for read ops) */ + union { + cfe_uint_t ioctlcmd; /* IOCTL command (used only for IOCTLs) */ + cfe_uint_t devflags; /* Returned device info flags */ + }; +}; + +struct cfe_iocb_inpstat { + cfe_uint_t status; /* 1 means input available */ +}; + +struct cfe_iocb_envbuf { + cfe_int_t index; /* 0-based enumeration index */ + cfe_ptr_t name; /* name string buffer */ + cfe_int_t name_len; /* size of name buffer */ + cfe_ptr_t val; /* value string buffer */ + cfe_int_t val_len; /* size of value string buffer */ +}; + +struct cfe_iocb_cpuctl { + cfe_uint_t number; /* cpu number to control */ + cfe_uint_t command; /* command to issue to CPU */ + cfe_uint_t start_addr; /* CPU start address */ + cfe_uint_t gp; /* starting GP value */ + cfe_uint_t sp; /* starting SP value */ + cfe_uint_t a1; /* starting A1 value */ +}; + +struct cfe_iocb_time { + cfe_int_t ticks; /* current time in ticks */ +}; + +struct cfe_iocb_exitstat { + cfe_int_t status; +}; + +struct cfe_iocb_meminfo { + cfe_int_t index; /* 0-based enumeration index */ + cfe_int_t type; /* type of memory block */ + cfe_uint_t addr; /* physical start address */ + cfe_uint_t size; /* block size */ +}; + +struct cfe_iocb_fwinfo { + cfe_int_t version; /* major, minor, eco version */ + cfe_int_t totalmem; /* total installed mem */ + cfe_int_t flags; /* various flags */ + cfe_int_t boardid; /* board ID */ + cfe_int_t bootarea_va; /* VA of boot area */ + cfe_int_t bootarea_pa; /* PA of boot area */ + cfe_int_t bootarea_size; /* size of boot area */ + cfe_int_t reserved1; + cfe_int_t reserved2; + cfe_int_t reserved3; +}; + +/* CFE I/O Control Block */ +struct cfe_iocb { + cfe_uint_t fcode; /* IOCB function code */ + cfe_int_t status; /* return status */ + cfe_int_t handle; /* file/device handle */ + cfe_uint_t flags; /* flags for this IOCB */ + cfe_uint_t psize; /* size of parameter list */ + union { + struct cfe_iocb_buf buffer; /* buffer parameters */ + struct cfe_iocb_inpstat inpstat; /* input status parameters */ + struct cfe_iocb_envbuf envbuf; /* environment function parameters */ + struct cfe_iocb_cpuctl cpuctl; /* CPU control parameters */ + struct cfe_iocb_time time; /* timer parameters */ + struct cfe_iocb_meminfo meminfo; /* memory arena info parameters */ + struct cfe_iocb_fwinfo fwinfo; /* firmware information */ + struct cfe_iocb_exitstat exitstat; /* Exit Status */ + }; +}; + + +#include <linux/init.h> + +void __init cfe_setup(unsigned long fwarg0, unsigned long fwarg1, + unsigned long fwarg2, unsigned long fwarg3); + +#else /* __ASSEMBLY__ */ + + .macro cfe_early_init +#ifdef CONFIG_CFE + jal cfe_setup +#endif + .endm + +#endif /* __ASSEMBLY__ */ +#endif /* LINUX_CFE_PRIVATE_H_ */ diff --git a/target/linux/brcm63xx/files/arch/mips/pci/fixup-bcm96348.c b/target/linux/brcm63xx/files/arch/mips/pci/fixup-bcm96348.c new file mode 100644 index 000000000..ae71ebc73 --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/pci/fixup-bcm96348.c @@ -0,0 +1,95 @@ +/* +<:copyright-gpl + Copyright 2002 Broadcom Corp. All Rights Reserved. + + This program is free software; you can distribute it and/or modify it + under the terms of the GNU General Public License (Version 2) as + published by the Free Software Foundation. + + This program is distributed in the hope 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/types.h> +#include <linux/pci.h> + +#include <bcmpci.h> +#include <bcm_intr.h> +#include <bcm_map_part.h> +#include <6348_intr.h> +#include <6348_map_part.h> + +static volatile MpiRegisters * mpi = (MpiRegisters *)(MPI_BASE); + +static char irq_tab_bcm96348[] __initdata = { + [0] = INTERRUPT_ID_MPI, + [1] = INTERRUPT_ID_MPI, +#if defined(CONFIG_USB) + [USB_HOST_SLOT] = INTERRUPT_ID_USBH +#endif +}; + +int __init pcibios_map_irq(struct pci_dev *dev, u8 slot, u8 pin) +{ + return irq_tab_bcm96348[slot]; +} + +static void bcm96348_fixup(struct pci_dev *dev) +{ + uint32 memaddr; + uint32 size; + + memaddr = pci_resource_start(dev, 0); + size = pci_resource_len(dev, 0); + + switch (PCI_SLOT(dev->devfn)) { + case 0: + // UBUS to PCI address range + // Memory Window 1. Mask determines which bits are decoded. + mpi->l2pmrange1 = ~(size-1); + // UBUS to PCI Memory base address. This is akin to the ChipSelect base + // register. + mpi->l2pmbase1 = memaddr & BCM_PCI_ADDR_MASK; + // UBUS to PCI Remap Address. Replaces the masked address bits in the + // range register with this setting. + // Also, enable direct I/O and direct Memory accesses + mpi->l2pmremap1 = (memaddr | MEM_WINDOW_EN); + break; + + case 1: + // Memory Window 2 + mpi->l2pmrange2 = ~(size-1); + // UBUS to PCI Memory base address. + mpi->l2pmbase2 = memaddr & BCM_PCI_ADDR_MASK; + // UBUS to PCI Remap Address + mpi->l2pmremap2 = (memaddr | MEM_WINDOW_EN); + break; + +#if defined(CONFIG_USB) + case USB_HOST_SLOT: + dev->resource[0].start = USB_HOST_BASE; + dev->resource[0].end = USB_HOST_BASE+USB_BAR0_MEM_SIZE-1; + break; +#endif + } +} + +int pcibios_plat_dev_init(struct pci_dev *dev) +{ + return 0; +} + +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_BROADCOM, PCI_ANY_ID, + bcm96348_fixup); + +/*struct pci_fixup pcibios_fixups[] = { + { PCI_FIXUP_FINAL, PCI_ANY_ID, PCI_ANY_ID, bcm96348_fixup }, + {0} +};*/ diff --git a/target/linux/brcm63xx/files/arch/mips/pci/ops-bcm96348.c b/target/linux/brcm63xx/files/arch/mips/pci/ops-bcm96348.c new file mode 100644 index 000000000..ee1647121 --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/pci/ops-bcm96348.c @@ -0,0 +1,278 @@ +/* +<:copyright-gpl + Copyright 2002 Broadcom Corp. All Rights Reserved. + + This program is free software; you can distribute it and/or modify it + under the terms of the GNU General Public License (Version 2) as + published by the Free Software Foundation. + + This program is distributed in the hope 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/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/addrspace.h> + +#include <bcm_intr.h> +#include <bcm_map_part.h> +#include <6348_intr.h> +#include <6348_map_part.h> +#include <bcmpci.h> + +#include <linux/delay.h> + +#if defined(CONFIG_USB) +#if 0 +#define DPRINT(x...) printk(x) +#else +#define DPRINT(x...) +#endif + +static int +pci63xx_int_read(unsigned int devfn, int where, u32 * value, int size); +static int +pci63xx_int_write(unsigned int devfn, int where, u32 * value, int size); + +static bool usb_mem_size_rd = FALSE; +static uint32 usb_mem_base = 0; +static uint32 usb_cfg_space_cmd_reg = 0; +#endif +static bool pci_mem_size_rd = FALSE; + +static volatile MpiRegisters * mpi = (MpiRegisters *)(MPI_BASE); + +static void mpi_SetupPciConfigAccess(uint32 addr) +{ + mpi->l2pcfgctl = (DIR_CFG_SEL | DIR_CFG_USEREG | addr) & ~CONFIG_TYPE; +} + +static void mpi_ClearPciConfigAccess(void) +{ + mpi->l2pcfgctl = 0x00000000; +} + +#if defined(CONFIG_USB) +/* -------------------------------------------------------------------------- + Name: pci63xx_int_write +Abstract: PCI Config write on internal device(s) + -------------------------------------------------------------------------- */ +static int +pci63xx_int_write(unsigned int devfn, int where, u32 * value, int size) +{ + if (PCI_SLOT(devfn) != USB_HOST_SLOT) { + return PCIBIOS_SUCCESSFUL; + } + + switch (size) { + case 1: + DPRINT("W => Slot: %d Where: %2X Len: %d Data: %02X\n", + PCI_SLOT(devfn), where, size, *value); + break; + case 2: + DPRINT("W => Slot: %d Where: %2X Len: %d Data: %04X\n", + PCI_SLOT(devfn), where, size, *value); + switch (where) { + case PCI_COMMAND: + usb_cfg_space_cmd_reg = *value; + break; + default: + break; + } + break; + case 4: + DPRINT("W => Slot: %d Where: %2X Len: %d Data: %08lX\n", + PCI_SLOT(devfn), where, size, *value); + switch (where) { + case PCI_BASE_ADDRESS_0: + if (*value == 0xffffffff) { + usb_mem_size_rd = TRUE; + } else { + usb_mem_base = *value; + } + break; + default: + break; + } + break; + default: + break; + } + + return PCIBIOS_SUCCESSFUL; +} + +/* -------------------------------------------------------------------------- + Name: pci63xx_int_read +Abstract: PCI Config read on internal device(s) + -------------------------------------------------------------------------- */ +static int +pci63xx_int_read(unsigned int devfn, int where, u32 * value, int size) +{ + uint32 retValue = 0xFFFFFFFF; + + if (PCI_SLOT(devfn) != USB_HOST_SLOT) { + return PCIBIOS_SUCCESSFUL; + } + + // For now, this is specific to the USB Host controller. We can + // make it more general if we have to... + // Emulate PCI Config accesses + switch (where) { + case PCI_VENDOR_ID: + case PCI_DEVICE_ID: + retValue = PCI_VENDOR_ID_BROADCOM | 0x63000000; + break; + case PCI_COMMAND: + case PCI_STATUS: + retValue = (0x0006 << 16) | usb_cfg_space_cmd_reg; + break; + case PCI_CLASS_REVISION: + case PCI_CLASS_DEVICE: + retValue = (PCI_CLASS_SERIAL_USB << 16) | (0x10 << 8) | 0x01; + break; + case PCI_BASE_ADDRESS_0: + if (usb_mem_size_rd) { + retValue = USB_BAR0_MEM_SIZE; + } else { + if (usb_mem_base != 0) + retValue = usb_mem_base; + else + retValue = USB_HOST_BASE; + } + usb_mem_size_rd = FALSE; + break; + case PCI_CACHE_LINE_SIZE: + case PCI_LATENCY_TIMER: + retValue = 0; + break; + case PCI_HEADER_TYPE: + retValue = PCI_HEADER_TYPE_NORMAL; + break; + case PCI_SUBSYSTEM_VENDOR_ID: + retValue = PCI_VENDOR_ID_BROADCOM; + break; + case PCI_SUBSYSTEM_ID: + retValue = 0x6300; + break; + case PCI_INTERRUPT_LINE: + retValue = INTERRUPT_ID_USBH; + break; + default: + break; + } + + switch (size) { + case 1: + *value = (retValue >> ((where & 3) << 3)) & 0xff; + DPRINT("R <= Slot: %d Where: %2X Len: %d Data: %02X\n", + PCI_SLOT(devfn), where, size, *value); + break; + case 2: + *value = (retValue >> ((where & 3) << 3)) & 0xffff; + DPRINT("R <= Slot: %d Where: %2X Len: %d Data: %04X\n", + PCI_SLOT(devfn), where, size, *value); + break; + case 4: + *value = retValue; + DPRINT("R <= Slot: %d Where: %2X Len: %d Data: %08lX\n", + PCI_SLOT(devfn), where, size, *value); + break; + default: + break; + } + + return PCIBIOS_SUCCESSFUL; +} +#endif + +static int bcm96348_pcibios_read(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 * val) +{ + volatile unsigned char *ioBase = (unsigned char *)(mpi->l2piobase | KSEG1); + uint32 data; + +#if defined(CONFIG_USB) + if (PCI_SLOT(devfn) == USB_HOST_SLOT) + return pci63xx_int_read(devfn, where, val, size); +#endif + + mpi_SetupPciConfigAccess(BCM_PCI_CFG(PCI_SLOT(devfn), PCI_FUNC(devfn), where)); + data = *(uint32 *)ioBase; + switch(size) { + case 1: + *val = (data >> ((where & 3) << 3)) & 0xff; + break; + case 2: + *val = (data >> ((where & 3) << 3)) & 0xffff; + break; + case 4: + *val = data; + /* Special case for reading PCI device range */ + if ((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_5)) { + if (pci_mem_size_rd) { + /* bcm6348 PCI memory window minimum size is 64K */ + *val &= PCI_SIZE_64K; + } + } + break; + default: + break; + } + pci_mem_size_rd = FALSE; + mpi_ClearPciConfigAccess(); + + return PCIBIOS_SUCCESSFUL; +} + +static int bcm96348_pcibios_write(struct pci_bus *bus, unsigned int devfn, + int where, int size, u32 val) +{ + volatile unsigned char *ioBase = (unsigned char *)(mpi->l2piobase | KSEG1); + uint32 data; + +#if defined(CONFIG_USB) + if (PCI_SLOT(devfn) == USB_HOST_SLOT) + return pci63xx_int_write(devfn, where, &val, size); +#endif + mpi_SetupPciConfigAccess(BCM_PCI_CFG(PCI_SLOT(devfn), PCI_FUNC(devfn), where)); + data = *(uint32 *)ioBase; + switch(size) { + case 1: + data = (data & ~(0xff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + break; + case 2: + data = (data & ~(0xffff << ((where & 3) << 3))) | + (val << ((where & 3) << 3)); + break; + case 4: + data = val; + /* Special case for reading PCI device range */ + if ((where >= PCI_BASE_ADDRESS_0) && (where <= PCI_BASE_ADDRESS_5)) { + if (val == 0xffffffff) + pci_mem_size_rd = TRUE; + } + break; + default: + break; + } + *(uint32 *)ioBase = data; + udelay(500); + mpi_ClearPciConfigAccess(); + + return PCIBIOS_SUCCESSFUL; +} + +struct pci_ops bcm96348_pci_ops = { + .read = bcm96348_pcibios_read, + .write = bcm96348_pcibios_write +}; diff --git a/target/linux/brcm63xx/files/arch/mips/pci/pci-bcm96348.c b/target/linux/brcm63xx/files/arch/mips/pci/pci-bcm96348.c new file mode 100644 index 000000000..bea3b7ba0 --- /dev/null +++ b/target/linux/brcm63xx/files/arch/mips/pci/pci-bcm96348.c @@ -0,0 +1,62 @@ +/* +<:copyright-gpl + Copyright 2002 Broadcom Corp. All Rights Reserved. + + This program is free software; you can distribute it and/or modify it + under the terms of the GNU General Public License (Version 2) as + published by the Free Software Foundation. + + This program is distributed in the hope 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/types.h> +#include <linux/pci.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <bcmpci.h> + +static struct resource bcm_pci_io_resource = { + .name = "bcm96348 pci IO space", + .start = BCM_PCI_IO_BASE, + .end = BCM_PCI_IO_BASE + BCM_PCI_IO_SIZE_64KB - 1, + .flags = IORESOURCE_IO +}; + +static struct resource bcm_pci_mem_resource = { + .name = "bcm96348 pci memory space", + .start = BCM_PCI_MEM_BASE, + .end = BCM_PCI_MEM_BASE + BCM_PCI_MEM_SIZE_16MB - 1, + .flags = IORESOURCE_MEM +}; + +extern struct pci_ops bcm96348_pci_ops; + +struct pci_controller bcm96348_controller = { + .pci_ops = &bcm96348_pci_ops, + .io_resource = &bcm_pci_io_resource, + .mem_resource = &bcm_pci_mem_resource, +}; + +static __init int bcm96348_pci_init(void) +{ + /* Avoid ISA compat ranges. */ + PCIBIOS_MIN_IO = 0x00000000; + PCIBIOS_MIN_MEM = 0x00000000; + + /* Set I/O resource limits. */ + ioport_resource.end = 0x1fffffff; + iomem_resource.end = 0xffffffff; + + register_pci_controller(&bcm96348_controller); + return 0; +} + +arch_initcall(bcm96348_pci_init); |