--- /dev/null
+/*
+<: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.
+:>
+*/
+
+/* Includes. */
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/capability.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <asm/uaccess.h>
+#include <linux/wait.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/if.h>
+#include <linux/spinlock.h>
+
+#include <bcm_map_part.h>
+#include <board.h>
+#include <bcmTag.h>
+#include "boardparms.h"
+#include "bcm_intr.h"
+#include "board.h"
+#include "bcm_map_part.h"
+
+static DEFINE_SPINLOCK(board_lock);
+
+/* Typedefs. */
+#if defined (NON_CONSECUTIVE_MAC)
+// used to be the last octet. Now changed to the first 5 bits of the the forth octet
+// to reduced the duplicated MAC addresses.
+#define CHANGED_OCTET 3
+#define SHIFT_BITS 3
+#else
+#define CHANGED_OCTET 1
+#define SHIFT_BITS 0
+#endif
+
+typedef struct
+{
+ unsigned long ulId;
+ char chInUse;
+ char chReserved[3];
+} MAC_ADDR_INFO, *PMAC_ADDR_INFO;
+
+typedef struct
+{
+ unsigned long ulSdramSize;
+ unsigned long ulPsiSize;
+ unsigned long ulNumMacAddrs;
+ unsigned long ucaBaseMacAddr[NVRAM_MAC_ADDRESS_LEN];
+ MAC_ADDR_INFO MacAddrs[1];
+} NVRAM_INFO, *PNVRAM_INFO;
+
+typedef struct
+{
+ unsigned long eventmask;
+} BOARD_IOC, *PBOARD_IOC;
+
+
+/*Dyinggasp callback*/
+typedef void (*cb_dgasp_t)(void *arg);
+typedef struct _CB_DGASP__LIST
+{
+ struct list_head list;
+ char name[IFNAMSIZ];
+ cb_dgasp_t cb_dgasp_fn;
+ void *context;
+}CB_DGASP_LIST , *PCB_DGASP_LIST;
+
+
+static LED_MAP_PAIR LedMapping[] =
+{ // led name Initial state physical pin (ledMask)
+ {kLedEnd, kLedStateOff, 0, 0, 0, 0},
+ {kLedEnd, kLedStateOff, 0, 0, 0, 0},
+ {kLedEnd, kLedStateOff, 0, 0, 0, 0},
+ {kLedEnd, kLedStateOff, 0, 0, 0, 0},
+ {kLedEnd, kLedStateOff, 0, 0, 0, 0},
+ {kLedEnd, kLedStateOff, 0, 0, 0, 0},
+ {kLedEnd, kLedStateOff, 0, 0, 0, 0},
+ {kLedEnd, kLedStateOff, 0, 0, 0, 0},
+ {kLedEnd, kLedStateOff, 0, 0, 0, 0} // NOTE: kLedEnd has to be at the end.
+};
+
+/* Externs. */
+extern struct file fastcall *fget_light(unsigned int fd, int *fput_needed);
+extern unsigned int nr_free_pages (void);
+extern const char *get_system_type(void);
+extern void kerSysFlashInit(void);
+extern unsigned long get_nvram_start_addr(void);
+extern unsigned long get_scratch_pad_start_addr(void);
+extern unsigned long getMemorySize(void);
+extern void __init boardLedInit(PLED_MAP_PAIR);
+extern void boardLedCtrl(BOARD_LED_NAME, BOARD_LED_STATE);
+extern void kerSysLedRegisterHandler( BOARD_LED_NAME ledName,
+ HANDLE_LED_FUNC ledHwFunc, int ledFailType );
+
+/* Prototypes. */
+void __init InitNvramInfo( void );
+
+/* DyingGasp function prototype */
+static void __init kerSysDyingGaspMapIntr(void);
+static irqreturn_t kerSysDyingGaspIsr(int irq, void * dev_id);
+static void __init kerSysInitDyingGaspHandler( void );
+static void __exit kerSysDeinitDyingGaspHandler( void );
+/* -DyingGasp function prototype - */
+
+static PNVRAM_INFO g_pNvramInfo = NULL;
+static int g_ledInitialized = 0;
+static CB_DGASP_LIST *g_cb_dgasp_list_head = NULL;
+
+static int g_wakeup_monitor = 0;
+static struct file *g_monitor_file = NULL;
+static struct task_struct *g_monitor_task = NULL;
+static unsigned int (*g_orig_fop_poll)
+ (struct file *, struct poll_table_struct *) = NULL;
+
+void kerSysMipsSoftReset(void)
+{
+ if (PERF->RevID == 0x634800A1) {
+ typedef void (*FNPTR) (void);
+ FNPTR bootaddr = (FNPTR) FLASH_BASE;
+ int i;
+
+ /* Disable interrupts. */
+ //cli();
+ spin_lock_irq(&board_lock);
+
+ /* Reset all blocks. */
+ PERF->BlockSoftReset &= ~BSR_ALL_BLOCKS;
+ for( i = 0; i < 1000000; i++ )
+ ;
+ PERF->BlockSoftReset |= BSR_ALL_BLOCKS;
+ /* Jump to the power on address. */
+ (*bootaddr) ();
+ }
+ else
+ PERF->pll_control |= SOFT_RESET; // soft reset mips
+}
+
+
+int kerSysGetMacAddress( unsigned char *pucaMacAddr, unsigned long ulId )
+{
+ int nRet = 0;
+ PMAC_ADDR_INFO pMai = NULL;
+ PMAC_ADDR_INFO pMaiFreeNoId = NULL;
+ PMAC_ADDR_INFO pMaiFreeId = NULL;
+ unsigned long i = 0, ulIdxNoId = 0, ulIdxId = 0, shiftedIdx = 0;
+
+ /* CMO -- Fix le problème avec les adresses mac que l'on n'arrive pas
+ * * à relire plusieurs fois */
+ /* inv_xde */
+#if 0
+ if (boot_loader_type == BOOT_CFE)
+ memcpy( pucaMacAddr, g_pNvramInfo->ucaBaseMacAddr,
+ NVRAM_MAC_ADDRESS_LEN );
+ else {
+#endif
+ pucaMacAddr[0] = 0x00;
+ pucaMacAddr[1] = 0x07;
+ pucaMacAddr[2] = 0x3A;
+ pucaMacAddr[3] = 0xFF;
+ pucaMacAddr[4] = 0xFF;
+ pucaMacAddr[5] = 0xFF;
+#if 0
+ }
+#endif
+
+ return nRet;
+} /* kerSysGetMacAddr */
+
+int kerSysReleaseMacAddress( unsigned char *pucaMacAddr )
+{
+ int nRet = -EINVAL;
+ unsigned long ulIdx = 0;
+ int idx = (pucaMacAddr[NVRAM_MAC_ADDRESS_LEN - CHANGED_OCTET] -
+ g_pNvramInfo->ucaBaseMacAddr[NVRAM_MAC_ADDRESS_LEN - CHANGED_OCTET]);
+
+ // if overflow 255 (negitive), add 256 to have the correct index
+ if (idx < 0)
+ idx += 256;
+ ulIdx = (unsigned long) (idx >> SHIFT_BITS);
+
+ if( ulIdx < g_pNvramInfo->ulNumMacAddrs )
+ {
+ PMAC_ADDR_INFO pMai = &g_pNvramInfo->MacAddrs[ulIdx];
+ if( pMai->chInUse == 1 )
+ {
+ pMai->chInUse = 0;
+ nRet = 0;
+ }
+ }
+
+ return( nRet );
+} /* kerSysReleaseMacAddr */
+
+int kerSysGetSdramSize( void )
+{
+ if (boot_loader_type == BOOT_CFE) {
+ return( (int) g_pNvramInfo->ulSdramSize );
+ }
+ else {
+ printk("kerSysGetSdramSize : 0x%08X\n", (int)getMemorySize() + 0x00040000);
+ return((int)getMemorySize() + 0x00040000);
+ }
+} /* kerSysGetSdramSize */
+
+
+void kerSysLedCtrl(BOARD_LED_NAME ledName, BOARD_LED_STATE ledState)
+{
+ if (g_ledInitialized)
+ boardLedCtrl(ledName, ledState);
+}
+
+unsigned int kerSysMonitorPollHook( struct file *f, struct poll_table_struct *t)
+{
+ int mask = (*g_orig_fop_poll) (f, t);
+
+ if( g_wakeup_monitor == 1 && g_monitor_file == f )
+ {
+ /* If g_wakeup_monitor is non-0, the user mode application needs to
+ * return from a blocking select function. Return POLLPRI which will
+ * cause the select to return with the exception descriptor set.
+ */
+ mask |= POLLPRI;
+ g_wakeup_monitor = 0;
+ }
+
+ return( mask );
+}
+
+/* Put the user mode application that monitors link state on a run queue. */
+void kerSysWakeupMonitorTask( void )
+{
+ g_wakeup_monitor = 1;
+ if( g_monitor_task )
+ wake_up_process( g_monitor_task );
+}
+
+//<<JUNHON, 2004/09/15, get reset button status , tim hou , 05/04/12
+int kerSysGetResetHold(void)
+{
+ unsigned short gpio;
+
+ if( BpGetPressAndHoldResetGpio( &gpio ) == BP_SUCCESS )
+ {
+ unsigned long gpio_mask = GPIO_NUM_TO_MASK(gpio);
+ volatile unsigned long *gpio_reg = &GPIO->GPIOio;
+
+ if( (gpio & ~BP_ACTIVE_MASK) >= 32 )
+ {
+ gpio_mask = GPIO_NUM_TO_MASK_HIGH(gpio);
+ gpio_reg = &GPIO->GPIOio_high;
+ }
+ //printk("gpio=%04x,gpio_mask=%04x,gpio_reg=%04x\n",gpio,gpio_mask,*gpio_reg);
+ if(*gpio_reg & gpio_mask) //press down
+ return RESET_BUTTON_UP;
+ }
+ return RESET_BUTTON_PRESSDOWN;
+}
+//<<JUNHON, 2004/09/15
+
+/***************************************************************************
+ * Dying gasp ISR and functions.
+ ***************************************************************************/
+#define KERSYS_DBG printk
+
+#if defined(CONFIG_BCM96345)
+#define CYCLE_PER_US 70
+#elif defined(CONFIG_BCM96348) || defined(CONFIG_BCM96338)
+/* The BCM6348 cycles per microsecond is really variable since the BCM6348
+ * MIPS speed can vary depending on the PLL settings. However, an appoximate
+ * value of 120 will still work OK for the test being done.
+ */
+#define CYCLE_PER_US 120
+#endif
+#define DG_GLITCH_TO (100*CYCLE_PER_US)
+
+static void __init kerSysDyingGaspMapIntr()
+{
+ unsigned long ulIntr;
+
+#if defined(CONFIG_BCM96348) || defined(_BCM96348_) || defined(CONFIG_BCM96338) || defined(_BCM96338_)
+ if( BpGetAdslDyingGaspExtIntr( &ulIntr ) == BP_SUCCESS ) {
+ BcmHalMapInterrupt((FN_HANDLER)kerSysDyingGaspIsr, 0, INTERRUPT_ID_DG);
+ BcmHalInterruptEnable( INTERRUPT_ID_DG );
+ }
+#elif defined(CONFIG_BCM96345) || defined(_BCM96345_)
+ if( BpGetAdslDyingGaspExtIntr( &ulIntr ) == BP_SUCCESS ) {
+ ulIntr += INTERRUPT_ID_EXTERNAL_0;
+ BcmHalMapInterrupt((FN_HANDLER)kerSysDyingGaspIsr, 0, ulIntr);
+ BcmHalInterruptEnable( ulIntr );
+ }
+#endif
+
+}
+
+void kerSysSetWdTimer(ulong timeUs)
+{
+ TIMER->WatchDogDefCount = timeUs * (FPERIPH/1000000);
+ TIMER->WatchDogCtl = 0xFF00;
+ TIMER->WatchDogCtl = 0x00FF;
+}
+
+ulong kerSysGetCycleCount(void)
+{
+ ulong cnt;
+#ifdef _WIN32_WCE
+ cnt = 0;
+#else
+ __asm volatile("mfc0 %0, $9":"=d"(cnt));
+#endif
+ return(cnt);
+}
+
+static Bool kerSysDyingGaspCheckPowerLoss(void)
+{
+ ulong clk0;
+ ulong ulIntr;
+
+ ulIntr = 0;
+ clk0 = kerSysGetCycleCount();
+
+ UART->Data = 'D';
+ UART->Data = '%';
+ UART->Data = 'G';
+
+#if defined(CONFIG_BCM96345)
+ BpGetAdslDyingGaspExtIntr( &ulIntr );
+
+ do {
+ ulong clk1;
+
+ clk1 = kerSysGetCycleCount(); /* time cleared */
+ /* wait a little to get new reading */
+ while ((kerSysGetCycleCount()-clk1) < CYCLE_PER_US*2)
+ ;
+ } while ((0 == (PERF->ExtIrqCfg & (1 << (ulIntr + EI_STATUS_SHFT)))) && ((kerSysGetCycleCount() - clk0) < DG_GLITCH_TO));
+
+ if (PERF->ExtIrqCfg & (1 << (ulIntr + EI_STATUS_SHFT))) { /* power glitch */
+ BcmHalInterruptEnable( ulIntr + INTERRUPT_ID_EXTERNAL_0);
+ KERSYS_DBG(" - Power glitch detected. Duration: %ld us\n", (kerSysGetCycleCount() - clk0)/CYCLE_PER_US);
+ return 0;
+ }
+#elif (defined(CONFIG_BCM96348) || defined(CONFIG_BCM96338)) && !defined(VXWORKS)
+ do {
+ ulong clk1;
+
+ clk1 = kerSysGetCycleCount(); /* time cleared */
+ /* wait a little to get new reading */
+ while ((kerSysGetCycleCount()-clk1) < CYCLE_PER_US*2)
+ ;
+ } while ((PERF->IrqStatus & (1 << (INTERRUPT_ID_DG - INTERNAL_ISR_TABLE_OFFSET))) && ((kerSysGetCycleCount() - clk0) < DG_GLITCH_TO));
+
+ if (!(PERF->IrqStatus & (1 << (INTERRUPT_ID_DG - INTERNAL_ISR_TABLE_OFFSET)))) {
+ BcmHalInterruptEnable( INTERRUPT_ID_DG );
+ KERSYS_DBG(" - Power glitch detected. Duration: %ld us\n", (kerSysGetCycleCount() - clk0)/CYCLE_PER_US);
+ return 0;
+ }
+#endif
+ return 1;
+}
+
+static void kerSysDyingGaspShutdown( void )
+{
+ kerSysSetWdTimer(1000000);
+#if defined(CONFIG_BCM96345)
+ PERF->blkEnables &= ~(EMAC_CLK_EN | USB_CLK_EN | CPU_CLK_EN);
+#elif defined(CONFIG_BCM96348)
+ PERF->blkEnables &= ~(EMAC_CLK_EN | USBS_CLK_EN | USBH_CLK_EN | SAR_CLK_EN);
+#endif
+}
+
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+static irqreturn_t kerSysDyingGaspIsr(int irq, void * dev_id)
+#else
+static unsigned int kerSysDyingGaspIsr(void)
+#endif
+{
+ struct list_head *pos;
+ CB_DGASP_LIST *tmp, *dsl = NULL;
+
+ if (kerSysDyingGaspCheckPowerLoss()) {
+
+ /* first to turn off everything other than dsl */
+ list_for_each(pos, &g_cb_dgasp_list_head->list) {
+ tmp = list_entry(pos, CB_DGASP_LIST, list);
+ if(strncmp(tmp->name, "dsl", 3)) {
+ (tmp->cb_dgasp_fn)(tmp->context);
+ }else {
+ dsl = tmp;
+ }
+ }
+
+ /* now send dgasp */
+ if(dsl)
+ (dsl->cb_dgasp_fn)(dsl->context);
+
+ /* reset and shutdown system */
+ kerSysDyingGaspShutdown();
+ }
+#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0))
+return( IRQ_HANDLED );
+#else
+ return( 1 );
+#endif
+}
+
+static void __init kerSysInitDyingGaspHandler( void )
+{
+ CB_DGASP_LIST *new_node;
+
+ if( g_cb_dgasp_list_head != NULL) {
+ printk("Error: kerSysInitDyingGaspHandler: list head is not null\n");
+ return;
+ }
+ new_node= (CB_DGASP_LIST *)kmalloc(sizeof(CB_DGASP_LIST), GFP_KERNEL);
+ memset(new_node, 0x00, sizeof(CB_DGASP_LIST));
+ INIT_LIST_HEAD(&new_node->list);
+ g_cb_dgasp_list_head = new_node;
+
+} /* kerSysInitDyingGaspHandler */
+
+static void __exit kerSysDeinitDyingGaspHandler( void )
+{
+ struct list_head *pos;
+ CB_DGASP_LIST *tmp;
+
+ if(g_cb_dgasp_list_head == NULL)
+ return;
+
+ list_for_each(pos, &g_cb_dgasp_list_head->list) {
+ tmp = list_entry(pos, CB_DGASP_LIST, list);
+ list_del(pos);
+ kfree(tmp);
+ }
+
+ kfree(g_cb_dgasp_list_head);
+ g_cb_dgasp_list_head = NULL;
+
+} /* kerSysDeinitDyingGaspHandler */
+
+void kerSysRegisterDyingGaspHandler(char *devname, void *cbfn, void *context)
+{
+ CB_DGASP_LIST *new_node;
+
+ if( g_cb_dgasp_list_head == NULL) {
+ printk("Error: kerSysRegisterDyingGaspHandler: list head is null\n");
+ return;
+ }
+
+ if( devname == NULL || cbfn == NULL ) {
+ printk("Error: kerSysRegisterDyingGaspHandler: register info not enough (%s,%x,%x)\n", devname, (unsigned int)cbfn, (unsigned int)context);
+ return;
+ }
+
+ new_node= (CB_DGASP_LIST *)kmalloc(sizeof(CB_DGASP_LIST), GFP_KERNEL);
+ memset(new_node, 0x00, sizeof(CB_DGASP_LIST));
+ INIT_LIST_HEAD(&new_node->list);
+ strncpy(new_node->name, devname, IFNAMSIZ);
+ new_node->cb_dgasp_fn = (cb_dgasp_t)cbfn;
+ new_node->context = context;
+ list_add(&new_node->list, &g_cb_dgasp_list_head->list);
+
+ printk("dgasp: kerSysRegisterDyingGaspHandler: %s registered \n", devname);
+
+} /* kerSysRegisterDyingGaspHandler */
+
+void kerSysDeregisterDyingGaspHandler(char *devname)
+{
+ struct list_head *pos;
+ CB_DGASP_LIST *tmp;
+
+ if(g_cb_dgasp_list_head == NULL) {
+ printk("Error: kerSysDeregisterDyingGaspHandler: list head is null\n");
+ return;
+ }
+
+ if(devname == NULL) {
+ printk("Error: kerSysDeregisterDyingGaspHandler: devname is null\n");
+ return;
+ }
+
+ printk("kerSysDeregisterDyingGaspHandler: %s is deregistering\n", devname);
+
+ list_for_each(pos, &g_cb_dgasp_list_head->list) {
+ tmp = list_entry(pos, CB_DGASP_LIST, list);
+ if(!strcmp(tmp->name, devname)) {
+ list_del(pos);
+ kfree(tmp);
+ printk("kerSysDeregisterDyingGaspHandler: %s is deregistered\n", devname);
+ return;
+ }
+ }
+ printk("kerSysDeregisterDyingGaspHandler: %s not (de)registered\n", devname);
+
+} /* kerSysDeregisterDyingGaspHandler */
+
+//EXPORT_SYMBOL(kerSysNvRamGet);
+EXPORT_SYMBOL(kerSysGetMacAddress);
+EXPORT_SYMBOL(kerSysReleaseMacAddress);
+EXPORT_SYMBOL(kerSysGetSdramSize);
+EXPORT_SYMBOL(kerSysLedCtrl);
+EXPORT_SYMBOL(kerSysGetResetHold);
+EXPORT_SYMBOL(kerSysLedRegisterHwHandler);
+EXPORT_SYMBOL(BpGetBoardIds);
+EXPORT_SYMBOL(BpGetSdramSize);
+EXPORT_SYMBOL(BpGetPsiSize);
+EXPORT_SYMBOL(BpGetEthernetMacInfo);
+EXPORT_SYMBOL(BpGetRj11InnerOuterPairGpios);
+EXPORT_SYMBOL(BpGetPressAndHoldResetGpio);
+EXPORT_SYMBOL(BpGetVoipResetGpio);
+EXPORT_SYMBOL(BpGetVoipIntrGpio);
+EXPORT_SYMBOL(BpGetPcmciaResetGpio);
+EXPORT_SYMBOL(BpGetRtsCtsUartGpios);
+EXPORT_SYMBOL(BpGetAdslLedGpio);
+EXPORT_SYMBOL(BpGetAdslFailLedGpio);
+EXPORT_SYMBOL(BpGetWirelessLedGpio);
+EXPORT_SYMBOL(BpGetUsbLedGpio);
+EXPORT_SYMBOL(BpGetHpnaLedGpio);
+EXPORT_SYMBOL(BpGetWanDataLedGpio);
+EXPORT_SYMBOL(BpGetPppLedGpio);
+EXPORT_SYMBOL(BpGetPppFailLedGpio);
+EXPORT_SYMBOL(BpGetVoipLedGpio);
+EXPORT_SYMBOL(BpGetWirelessExtIntr);
+EXPORT_SYMBOL(BpGetAdslDyingGaspExtIntr);
+EXPORT_SYMBOL(BpGetVoipExtIntr);
+EXPORT_SYMBOL(BpGetHpnaExtIntr);
+EXPORT_SYMBOL(BpGetHpnaChipSelect);
+EXPORT_SYMBOL(BpGetVoipChipSelect);
+EXPORT_SYMBOL(BpGetWirelessSesBtnGpio);
+EXPORT_SYMBOL(BpGetWirelessSesExtIntr);
+EXPORT_SYMBOL(BpGetWirelessSesLedGpio);
+EXPORT_SYMBOL(kerSysRegisterDyingGaspHandler);
+EXPORT_SYMBOL(kerSysDeregisterDyingGaspHandler);
+EXPORT_SYMBOL(kerSysGetCycleCount);
+EXPORT_SYMBOL(kerSysSetWdTimer);
+EXPORT_SYMBOL(kerSysWakeupMonitorTask);
+