diff options
| author | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2007-09-06 16:27:37 +0000 | 
|---|---|---|
| committer | nbd <nbd@3c298f89-4303-0410-b956-a3cf2f4a3e73> | 2007-09-06 16:27:37 +0000 | 
| commit | 17c7b6c3fdc48301e50d22cc6138ede16bd1be24 (patch) | |
| tree | a5d41b991a151e72663527a96fbc6c494565d65c /target/linux/brcm63xx/files/arch/mips/bcm963xx | |
| parent | 5389989abaa52926b22f9f030d1481df1e73d745 (diff) | |
strip the kernel version suffix from target directories, except for brcm-2.4 (the -2.4 will be included in the board name here). CONFIG_LINUX_<ver>_<board> becomes CONFIG_TARGET_<board>, same for profiles.
git-svn-id: svn://svn.openwrt.org/openwrt/trunk@8653 3c298f89-4303-0410-b956-a3cf2f4a3e73
Diffstat (limited to 'target/linux/brcm63xx/files/arch/mips/bcm963xx')
9 files changed, 1516 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"); | 
