--- /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.
+:>
+*/
+/***************************************************************************
+ * 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;
+ }
+}
+