1
0
mirror of git://projects.qi-hardware.com/openwrt-xburst.git synced 2025-04-21 12:27:27 +03:00

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
This commit is contained in:
nbd
2007-09-06 16:27:37 +00:00
parent 5389989aba
commit 17c7b6c3fd
701 changed files with 48 additions and 105 deletions

View File

@@ -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

View File

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

View File

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

View File

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

View File

@@ -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 */
}

View File

@@ -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

View File

@@ -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();
}

View File

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

View File

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