openwrtv4/target/linux/brcm63xx-2.6/files/arch/mips/bcm963xx/bcm63xx_led.c
Florian Fainelli 3ce8de018e Split up brcm63xx into files/
SVN-Revision: 6848
2007-04-03 11:26:12 +00:00

582 lines
19 KiB
C

/*
<: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.
:>
*/
/***************************************************************************
* File Name : bcm63xx_led.c
*
* Description:
*
* This file contains bcm963xx board led control API functions.
*
* To use it, do the following
*
* 1). define in the board.c the following led mappping (this is for 6345GW board):
* const LED_MAP_PAIR cLedMapping45GW[] =
* { // led name Initial state physical pin (ledMask)
* {kLedUsb, kLedStateOff, GPIO_LED_PIN_7},
* {kLedAdsl, kLedStateOff, GPIO_LED_PIN_8},
* {kLedPPP, kLedStateOff, GPIO_LED_PIN_9}, // PPP and WanData share PIN_9
* {kLedWanData, kLedStateOff, GPIO_LED_PIN_9},
* {kLedWireless, kLedStateOff, GPIO_LED_PIN_10},
* {kLedEnd, kLedStateOff, 0 } // NOTE: kLedEnd has to be at the end.
*
* 2). };To initialize led API and initial state of the leds, call the following function with the mapping
* pointer from the above struct
*
* boardLedInit((PLED_MAP_PAIR) &cLedMapping45R);
*
* 3). Sample call for kernel mode:
*
* kerSysLedCtrl(kLedAdsl, kLedStateBlinkOnce); // kLedxxx defines in board.h
*
* 4). Sample call for user mode
*
* sysLedCtrl(kLedAdsl, kLedStateBlinkOnce); // kLedxxx defines in board_api.h
*
*
* Created on : 10/28/2002 seanl
*
***************************************************************************/
/* Includes. */
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/capability.h>
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/module.h>
#include <linux/netdevice.h>
#include <asm/uaccess.h>
#include <bcm_map_part.h>
#include <board.h>
#define k100ms (HZ / 10) // ~100 ms
#define kFastBlinkCount 0 // ~100ms
#define kSlowBlinkCount 5 // ~600ms
#define MAX_VIRT_LEDS 12
// uncomment // for debug led
//#define DEBUG_LED
// global variables:
struct timer_list gLedTimer;
int gTimerOn = FALSE;
int gLedCount = 0;
typedef struct ledinfo
{
unsigned short ledMask; // mask for led: ie. giop 10 = 0x0400
unsigned short ledActiveLow; // GPIO bit reset to turn on LED
unsigned short ledMaskFail; // mask for led: ie. giop 10 = 0x0400
unsigned short ledActiveLowFail;// GPIO bit reset to turn on LED
BOARD_LED_STATE ledState; // current led state
BOARD_LED_STATE savedLedState; // used in blink once for restore to the orignal ledState
int blinkCountDown; // if == 0, do blink (toggle). Is assgined value and dec by 1 at each timer.
} LED_INFO, *PLED_INFO;
static PLED_INFO gLed = NULL;
static PLED_INFO gpVirtLeds[MAX_VIRT_LEDS];
static HANDLE_LED_FUNC gLedHwFunc[MAX_VIRT_LEDS];
static HANDLE_LED_FUNC gLedHwFailFunc[MAX_VIRT_LEDS];
#if 0 /* BROKEN */
#if defined(CONFIG_BCM96348) || defined(CONFIG_BCM96338)
static int gLedOffInBridgeMode = 1;
#elif defined(CONFIG_BCM96345)
static int gLedOffInBridgeMode = 0;
#endif
#endif
void ledTimerExpire(void);
int initLedInfo( PLED_MAP_PAIR pCurMap, PLED_INFO pCurLed );
//**************************************************************************************
// LED operations
//**************************************************************************************
// turn led on and set the ledState
void ledOn(PLED_INFO pLed)
{
if( pLed->ledMask )
{
GPIO->GPIODir |= pLed->ledMask; // turn on the direction bit in case was turned off by some one
if( pLed->ledActiveLow )
GPIO->GPIOio &= ~pLed->ledMask; // turn on the led
else
GPIO->GPIOio |= pLed->ledMask; // turn on the led
pLed->ledState = pLed->savedLedState = kLedStateOn;
}
}
// turn led off and set the ledState
void ledOff(PLED_INFO pLed)
{
if( pLed->ledMask )
{
GPIO->GPIODir |= pLed->ledMask; // turn on the direction bit in case was turned off by some one
if( pLed->ledActiveLow )
GPIO->GPIOio |= pLed->ledMask; // turn off the led
else
GPIO->GPIOio &= ~pLed->ledMask; // turn off the led
pLed->ledState = pLed->savedLedState = kLedStateOff;
}
}
// turn led on and set the ledState
void ledOnFail(PLED_INFO pLed)
{
if( pLed->ledMaskFail )
{
GPIO->GPIODir |= pLed->ledMaskFail; // turn on the direction bit in case was turned off by some one
if( pLed->ledActiveLowFail )
GPIO->GPIOio &= ~pLed->ledMaskFail;// turn on the led
else
GPIO->GPIOio |= pLed->ledMaskFail; // turn on the led
pLed->ledState = pLed->savedLedState = kLedStateFail;
}
}
// turn led off and set the ledState
void ledOffFail(PLED_INFO pLed)
{
if( pLed->ledMaskFail )
{
GPIO->GPIODir |= pLed->ledMaskFail; // turn on the direction bit in case was turned off by some one
if( pLed->ledActiveLowFail )
GPIO->GPIOio |= pLed->ledMaskFail; // turn off the led
else
GPIO->GPIOio &= ~pLed->ledMaskFail;// turn off the led
pLed->ledState = pLed->savedLedState = kLedStateOff;
}
}
// toggle the led and return the current ledState
BOARD_LED_STATE ledToggle(PLED_INFO pLed)
{
GPIO->GPIODir |= pLed->ledMask; // turn on the direction bit in case was turned off by some one
if (GPIO->GPIOio & pLed->ledMask)
{
GPIO->GPIOio &= ~(pLed->ledMask);
return( (pLed->ledActiveLow) ? kLedStateOn : kLedStateOff );
}
else
{
GPIO->GPIOio |= pLed->ledMask;
return( (pLed->ledActiveLow) ? kLedStateOff : kLedStateOn );
}
}
// led timer. Will return if timer is already on
void ledTimerStart(void)
{
if (gTimerOn)
return;
#if defined(DEBUG_LED)
printk("led: add_timer\n");
#endif
init_timer(&gLedTimer);
gLedTimer.function = (void*)ledTimerExpire;
gLedTimer.expires = jiffies + k100ms; // timer expires in ~100ms
add_timer (&gLedTimer);
gTimerOn = TRUE;
}
// led timer expire kicks in about ~100ms and perform the led operation according to the ledState and
// restart the timer according to ledState
void ledTimerExpire(void)
{
int i;
PLED_INFO pCurLed;
gTimerOn = FALSE;
for (i = 0, pCurLed = gLed; i < gLedCount; i++, pCurLed++)
{
#if defined(DEBUG_LED)
printk("led[%d]: Mask=0x%04x, State = %d, blcd=%d\n", i, pCurLed->ledMask, pCurLed->ledState, pCurLed->blinkCountDown);
#endif
switch (pCurLed->ledState)
{
case kLedStateOn:
case kLedStateOff:
case kLedStateFail:
pCurLed->blinkCountDown = 0; // reset the blink count down
break;
case kLedStateBlinkOnce:
ledToggle(pCurLed);
pCurLed->blinkCountDown = 0; // reset to 0
pCurLed->ledState = pCurLed->savedLedState;
if (pCurLed->ledState == kLedStateSlowBlinkContinues ||
pCurLed->ledState == kLedStateFastBlinkContinues)
ledTimerStart(); // start timer if in blinkContinues stats
break;
case kLedStateSlowBlinkContinues:
if (pCurLed->blinkCountDown-- == 0)
{
pCurLed->blinkCountDown = kSlowBlinkCount;
ledToggle(pCurLed);
}
ledTimerStart();
break;
case kLedStateFastBlinkContinues:
if (pCurLed->blinkCountDown-- == 0)
{
pCurLed->blinkCountDown = kFastBlinkCount;
ledToggle(pCurLed);
}
ledTimerStart();
break;
default:
printk("Invalid state = %d\n", pCurLed->ledState);
}
}
}
// initialize the gLedCount and allocate and fill gLed struct
void __init boardLedInit(PLED_MAP_PAIR cLedMapping)
{
PLED_MAP_PAIR p1, p2;
PLED_INFO pCurLed;
int needTimer = FALSE;
int alreadyUsed = 0;
#if defined(CONFIG_BCM96348) || defined(CONFIG_BCM96338)
/* Set blink rate for BCM6348/BCM6338 hardware LEDs. */
GPIO->LEDCtrl &= ~LED_INTERVAL_SET_MASK;
GPIO->LEDCtrl |= LED_INTERVAL_SET_80MS;
#endif
memset( gpVirtLeds, 0x00, sizeof(gpVirtLeds) );
memset( gLedHwFunc, 0x00, sizeof(gLedHwFunc) );
memset( gLedHwFailFunc, 0x00, sizeof(gLedHwFailFunc) );
gLedCount = 0;
// Check for multiple LED names and multiple LED GPIO pins that share the
// same physical board LED.
for( p1 = cLedMapping; p1->ledName != kLedEnd; p1++ )
{
alreadyUsed = 0;
for( p2 = cLedMapping; p2 != p1; p2++ )
{
if( (p1->ledMask && p1->ledMask == p2->ledMask) ||
(p1->ledMaskFail && p1->ledMaskFail == p2->ledMaskFail) )
{
alreadyUsed = 1;
break;
}
}
if( alreadyUsed == 0 )
gLedCount++;
}
gLed = (PLED_INFO) kmalloc((gLedCount * sizeof(LED_INFO)), GFP_KERNEL);
if( gLed == NULL )
{
printk( "LED memory allocation error.\n" );
return;
}
memset( gLed, 0x00, gLedCount * sizeof(LED_INFO) );
// initial the gLed with unique ledMask and initial state. If more than 1 ledNames share the physical led
// (ledMask) the first defined led's ledInitState will be used.
pCurLed = gLed;
for( p1 = cLedMapping; p1->ledName != kLedEnd; p1++ )
{
if( (int) p1->ledName > MAX_VIRT_LEDS )
continue;
alreadyUsed = 0;
for( p2 = cLedMapping; p2 != p1; p2++ )
{
if( (p1->ledMask && p1->ledMask == p2->ledMask) ||
(p1->ledMaskFail && p1->ledMaskFail == p2->ledMaskFail) )
{
alreadyUsed = 1;
break;
}
}
if( alreadyUsed == 0 )
{
// Initialize the board LED for the first time.
needTimer = initLedInfo( p1, pCurLed );
gpVirtLeds[(int) p1->ledName] = pCurLed;
pCurLed++;
}
else
{
PLED_INFO pLed;
for( pLed = gLed; pLed != pCurLed; pLed++ )
{
// Find the LED_INFO structure that has already been initialized.
if((pLed->ledMask && pLed->ledMask == p1->ledMask) ||
(pLed->ledMaskFail && pLed->ledMaskFail==p1->ledMaskFail))
{
// The board LED has already been initialized but possibly
// not completely initialized.
if( p1->ledMask )
{
pLed->ledMask = p1->ledMask;
pLed->ledActiveLow = p1->ledActiveLow;
}
if( p1->ledMaskFail )
{
pLed->ledMaskFail = p1->ledMaskFail;
pLed->ledActiveLowFail = p1->ledActiveLowFail;
}
gpVirtLeds[(int) p1->ledName] = pLed;
break;
}
}
}
}
if (needTimer)
ledTimerStart();
#if defined(DEBUG_LED)
int i;
for (i=0; i < gLedCount; i++)
printk("initLed: led[%d]: mask=0x%04x, state=%d\n", i,(gLed+i)->ledMask, (gLed+i)->ledState);
#endif
}
// Initialize a structure that contains information about a physical board LED
// control. The board LED may contain more than one GPIO pin to control a
// normal condition (green) or a failure condition (red).
int initLedInfo( PLED_MAP_PAIR pCurMap, PLED_INFO pCurLed )
{
int needTimer = FALSE;
pCurLed->ledState = pCurLed->savedLedState = pCurMap->ledInitState;
pCurLed->ledMask = pCurMap->ledMask;
pCurLed->ledActiveLow = pCurMap->ledActiveLow;
pCurLed->ledMaskFail = pCurMap->ledMaskFail;
pCurLed->ledActiveLowFail = pCurMap->ledActiveLowFail;
switch (pCurLed->ledState)
{
case kLedStateOn:
pCurLed->blinkCountDown = 0; // reset the blink count down
ledOn(pCurLed);
break;
case kLedStateOff:
pCurLed->blinkCountDown = 0; // reset the blink count down
ledOff(pCurLed);
break;
case kLedStateFail:
pCurLed->blinkCountDown = 0; // reset the blink count down
ledOnFail(pCurLed);
break;
case kLedStateBlinkOnce:
pCurLed->blinkCountDown = 1;
needTimer = TRUE;
break;
case kLedStateSlowBlinkContinues:
pCurLed->blinkCountDown = kSlowBlinkCount;
needTimer = TRUE;
break;
case kLedStateFastBlinkContinues:
pCurLed->blinkCountDown = kFastBlinkCount;
needTimer = TRUE;
break;
default:
printk("Invalid state = %d\n", pCurLed->ledState);
}
return( needTimer );
}
#if 0 /* BROKEN */
// Determines if there is at least one interface in bridge mode. Bridge mode
// is determined by the cfm convention of naming bridge interfaces nas17
// through nas24.
static int isBridgedProtocol(void)
{
extern int dev_get(const char *name);
const int firstBridgeId = 17;
const int lastBridgeId = 24;
int i;
int ret = FALSE;
char name[16];
for( i = firstBridgeId; i <= lastBridgeId; i++ )
{
sprintf( name, "nas%d", i );
if( dev_get(name) )
{
ret = TRUE;
break;
}
}
return(ret);
}
#endif
// led ctrl. Maps the ledName to the corresponding ledInfoPtr and perform the led operation
void boardLedCtrl(BOARD_LED_NAME ledName, BOARD_LED_STATE ledState)
{
PLED_INFO ledInfoPtr;
// do the mapping from virtual to physical led
if( (int) ledName < MAX_VIRT_LEDS )
ledInfoPtr = gpVirtLeds[(int) ledName];
else
ledInfoPtr = NULL;
if (ledInfoPtr == NULL)
return;
if( ledState != kLedStateFail && gLedHwFunc[(int) ledName] )
{
(*gLedHwFunc[(int) ledName]) (ledName, ledState);
ledOffFail(ledInfoPtr);
return;
}
else
if( ledState == kLedStateFail && gLedHwFailFunc[(int) ledName] )
{
(*gLedHwFailFunc[(int) ledName]) (ledName, ledState);
ledOff(ledInfoPtr);
return;
}
#if 0 /* BROKEN */
// Do not blink the WAN Data LED if at least one interface is in bridge mode.
if(gLedOffInBridgeMode == 1 && (ledName == kLedWanData || ledName == kLedPPP))
{
static int BridgedProtocol = -1;
if( BridgedProtocol == -1 )
BridgedProtocol = isBridgedProtocol();
if( BridgedProtocol == TRUE )
return;
}
#endif
// If the state is kLedStateFail and there is not a failure LED defined
// in the board parameters, change the state to kLedStateFastBlinkContinues.
if( ledState == kLedStateFail && ledInfoPtr->ledMaskFail == 0 )
ledState = kLedStateFastBlinkContinues;
switch (ledState)
{
case kLedStateOn:
// First, turn off the complimentary (failure) LED GPIO.
if( ledInfoPtr->ledMaskFail )
ledOffFail(ledInfoPtr);
else
if( gLedHwFailFunc[(int) ledName] )
(*gLedHwFailFunc[(int) ledName]) (ledName, kLedStateOff);
// Next, turn on the specified LED GPIO.
ledOn(ledInfoPtr);
break;
case kLedStateOff:
// First, turn off the complimentary (failure) LED GPIO.
if( ledInfoPtr->ledMaskFail )
ledOffFail(ledInfoPtr);
else
if( gLedHwFailFunc[(int) ledName] )
(*gLedHwFailFunc[(int) ledName]) (ledName, kLedStateOff);
// Next, turn off the specified LED GPIO.
ledOff(ledInfoPtr);
break;
case kLedStateFail:
// First, turn off the complimentary (normal) LED GPIO.
if( ledInfoPtr->ledMask )
ledOff(ledInfoPtr);
else
if( gLedHwFunc[(int) ledName] )
(*gLedHwFunc[(int) ledName]) (ledName, kLedStateOff);
// Next, turn on (red) the specified LED GPIO.
ledOnFail(ledInfoPtr);
break;
case kLedStateBlinkOnce:
// skip blinkOnce if it is already in Slow/Fast blink continues state
if (ledInfoPtr->savedLedState == kLedStateSlowBlinkContinues ||
ledInfoPtr->savedLedState == kLedStateFastBlinkContinues)
;
else
{
if (ledInfoPtr->blinkCountDown == 0) // skip the call if it is 1
{
ledToggle(ledInfoPtr);
ledInfoPtr->blinkCountDown = 1; // it will be reset to 0 when timer expires
ledInfoPtr->ledState = kLedStateBlinkOnce;
ledTimerStart();
}
}
break;
case kLedStateSlowBlinkContinues:
ledInfoPtr->blinkCountDown = kSlowBlinkCount;
ledInfoPtr->ledState = kLedStateSlowBlinkContinues;
ledInfoPtr->savedLedState = kLedStateSlowBlinkContinues;
ledTimerStart();
break;
case kLedStateFastBlinkContinues:
ledInfoPtr->blinkCountDown = kFastBlinkCount;
ledInfoPtr->ledState = kLedStateFastBlinkContinues;
ledInfoPtr->savedLedState = kLedStateFastBlinkContinues;
ledTimerStart();
break;
default:
printk("Invalid led state\n");
}
}
// This function is called for an LED that is controlled by hardware.
void kerSysLedRegisterHwHandler( BOARD_LED_NAME ledName,
HANDLE_LED_FUNC ledHwFunc, int ledFailType )
{
if( (int) ledName < MAX_VIRT_LEDS )
{
if( ledFailType == 1 )
gLedHwFailFunc[(int) ledName] = ledHwFunc;
else
gLedHwFunc[(int) ledName] = ledHwFunc;
}
}