/* <: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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 ); } //<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; } //<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);