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);  | 
