add original danube led driver
[openwrt/openwrt.git] / target / linux / danube / files / drivers / char / danube_led.c
diff --git a/target/linux/danube/files/drivers/char/danube_led.c b/target/linux/danube/files/drivers/char/danube_led.c
new file mode 100644 (file)
index 0000000..531c7ed
--- /dev/null
@@ -0,0 +1,1378 @@
+/*
+ *   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.
+ *
+ *   This program is distributed in the hope that 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.
+ *
+ *   Copyright (C) 2006 infineon
+ *   Copyright (C) 2007 John Crispin <blogic@openwrt.org> 
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/unistd.h>
+#include <linux/errno.h>
+
+/*
+ *  Chip Specific Head File
+ */
+#include <asm/danube/port.h>
+
+#include <asm/danube/danube_led.h>
+#include <asm/danube/danube_gptu.h>
+
+
+/*
+ * ####################################
+ *              Definition
+ * ####################################
+ */
+
+#define DEBUG_ON_AMAZON                 0
+
+#define DATA_CLOCKING_EDGE              FALLING_EDGE
+
+#define BOARD_TYPE                      REFERENCE_BOARD
+
+#define DEBUG_WRITE_REGISTER            0
+
+#define RISING_EDGE                     0
+#define FALLING_EDGE                    1
+
+#define EVALUATION_BOARD                0
+#define REFERENCE_BOARD                 1
+
+/*
+ *  GPIO Driver Function Wrapping
+ */
+#define port_reserve_pin                danube_port_reserve_pin
+#define port_free_pin                   danube_port_free_pin
+#define port_set_altsel0                danube_port_set_altsel0
+#define port_clear_altsel0              danube_port_clear_altsel0
+#define port_set_altsel1                danube_port_set_altsel1
+#define port_clear_altsel1              danube_port_clear_altsel1
+#define port_set_dir_out                danube_port_set_dir_out
+#define port_clear_dir_out              danube_port_clear_dir_out
+#define port_set_open_drain             danube_port_set_open_drain
+#define port_clear_open_drain           danube_port_clear_open_drain
+
+/*
+ *  GPIO Port Used By LED
+ */
+#define LED_SH_PORT                     0
+#define LED_SH_PIN                      4
+#define LED_SH_DIR                      1
+#define LED_SH_ALTSEL0                  1
+#define LED_SH_ALTSEL1                  0
+#define LED_SH_OPENDRAIN                1
+#define LED_D_PORT                      0
+#define LED_D_PIN                       5
+#define LED_D_DIR                       1
+#define LED_D_ALTSEL0                   1
+#define LED_D_ALTSEL1                   0
+#define LED_D_OPENDRAIN                 1
+#define LED_ST_PORT                     0
+#define LED_ST_PIN                      6
+#define LED_ST_DIR                      1
+#define LED_ST_ALTSEL0                  1
+#define LED_ST_ALTSEL1                  0
+#define LED_ST_OPENDRAIN                1
+
+#define LED_ADSL0_PORT                  0
+#define LED_ADSL0_PIN                   4
+#define LED_ADSL0_DIR                   1
+#define LED_ADSL0_ALTSEL0               0
+#define LED_ADSL0_ALTSEL1               1
+#define LED_ADSL0_OPENDRAIN             1
+#define LED_ADSL1_PORT                  0
+#define LED_ADSL1_PIN                   5
+#define LED_ADSL1_DIR                   1
+#define LED_ADSL1_ALTSEL0               1
+#define LED_ADSL1_ALTSEL1               1
+#define LED_ADSL1_OPENDRAIN             1
+
+#if (LED_SH_PORT == LED_ADSL0_PORT && LED_SH_PIN == LED_ADSL0_PIN)      \
+    || (LED_D_PORT == LED_ADSL0_PORT && LED_D_PIN == LED_ADSL0_PIN)     \
+    || (LED_ST_PORT == LED_ADSL0_PORT && LED_ST_PIN == LED_ADSL0_PIN)   \
+    || (LED_SH_PORT == LED_ADSL1_PORT && LED_SH_PIN == LED_ADSL1_PIN)   \
+    || (LED_D_PORT == LED_ADSL1_PORT && LED_D_PIN == LED_ADSL1_PIN)     \
+    || (LED_ST_PORT == LED_ADSL1_PORT && LED_ST_PIN == LED_ADSL1_PIN)
+  #define ADSL_LED_IS_EXCLUSIVE         1
+#else
+  #define ADSL_LED_IS_EXCLUSIVE         0
+#endif
+
+/*
+ *  Define GPIO Functions
+ */
+#if LED_SH_DIR
+  #define LED_SH_DIR_SETUP              port_set_dir_out
+#else
+  #define LED_SH_DIR_SETUP              port_clear_dir_out
+#endif
+#if LED_SH_ALTSEL0
+  #define LED_SH_ALTSEL0_SETUP          port_set_altsel0
+#else
+  #define LED_SH_ALTSEL0_SETUP          port_clear_altsel0
+#endif
+#if LED_SH_ALTSEL1
+  #define LED_SH_ALTSEL1_SETUP          port_set_altsel1
+#else
+  #define LED_SH_ALTSEL1_SETUP          port_clear_altsel1
+#endif
+#if LED_SH_OPENDRAIN
+  #define LED_SH_OPENDRAIN_SETUP        port_set_open_drain
+#else
+  #define LED_SH_OPENDRAIN_SETUP        port_clear_open_drain
+#endif
+
+#if LED_D_DIR
+  #define LED_D_DIR_SETUP               port_set_dir_out
+#else
+  #define LED_D_DIR_SETUP               port_clear_dir_out
+#endif
+#if LED_D_ALTSEL0
+  #define LED_D_ALTSEL0_SETUP           port_set_altsel0
+#else
+  #define LED_D_ALTSEL0_SETUP           port_clear_altsel0
+#endif
+#if LED_D_ALTSEL1
+  #define LED_D_ALTSEL1_SETUP           port_set_altsel1
+#else
+  #define LED_D_ALTSEL1_SETUP           port_clear_altsel1
+#endif
+#if LED_D_OPENDRAIN
+  #define LED_D_OPENDRAIN_SETUP         port_set_open_drain
+#else
+  #define LED_D_OPENDRAIN_SETUP         port_clear_open_drain
+#endif
+
+#if LED_ST_DIR
+  #define LED_ST_DIR_SETUP              port_set_dir_out
+#else
+  #define LED_ST_DIR_SETUP              port_clear_dir_out
+#endif
+#if LED_ST_ALTSEL0
+  #define LED_ST_ALTSEL0_SETUP          port_set_altsel0
+#else
+  #define LED_ST_ALTSEL0_SETUP          port_clear_altsel0
+#endif
+#if LED_ST_ALTSEL1
+  #define LED_ST_ALTSEL1_SETUP          port_set_altsel1
+#else
+  #define LED_ST_ALTSEL1_SETUP          port_clear_altsel1
+#endif
+#if LED_ST_OPENDRAIN
+  #define LED_ST_OPENDRAIN_SETUP        port_set_open_drain
+#else
+  #define LED_ST_OPENDRAIN_SETUP        port_clear_open_drain
+#endif
+
+#if LED_ADSL0_DIR
+  #define LED_ADSL0_DIR_SETUP           port_set_dir_out
+#else
+  #define LED_ADSL0_DIR_SETUP           port_clear_dir_out
+#endif
+#if LED_ADSL0_ALTSEL0
+  #define LED_ADSL0_ALTSEL0_SETUP       port_set_altsel0
+#else
+  #define LED_ADSL0_ALTSEL0_SETUP       port_clear_altsel0
+#endif
+#if LED_ADSL0_ALTSEL1
+  #define LED_ADSL0_ALTSEL1_SETUP       port_set_altsel1
+#else
+  #define LED_ADSL0_ALTSEL1_SETUP       port_clear_altsel1
+#endif
+#if LED_ADSL0_OPENDRAIN
+  #define LED_ADSL0_OPENDRAIN_SETUP     port_set_open_drain
+#else
+  #define LED_ADSL0_OPENDRAIN_SETUP     port_clear_open_drain
+#endif
+
+#if LED_ADSL1_DIR
+  #define LED_ADSL1_DIR_SETUP           port_set_dir_out
+#else
+  #define LED_ADSL1_DIR_SETUP           port_clear_dir_out
+#endif
+#if LED_ADSL1_ALTSEL0
+  #define LED_ADSL1_ALTSEL0_SETUP       port_set_altsel0
+#else
+  #define LED_ADSL1_ALTSEL0_SETUP       port_clear_altsel0
+#endif
+#if LED_ADSL1_ALTSEL1
+  #define LED_ADSL1_ALTSEL1_SETUP       port_set_altsel1
+#else
+  #define LED_ADSL1_ALTSEL1_SETUP       port_clear_altsel1
+#endif
+#if LED_ADSL1_OPENDRAIN
+  #define LED_ADSL1_OPENDRAIN_SETUP     port_set_open_drain
+#else
+  #define LED_ADSL1_OPENDRAIN_SETUP     port_clear_open_drain
+#endif
+
+/*
+ *  LED Device Minor Number
+ */
+#if !defined(LED_MINOR)
+    #define LED_MINOR                   151 //  This number is written in Linux kernel document "devices.txt"
+#endif  //  !defined(LED_MINOR)
+
+/*
+ *  Bits Operation
+ */
+#define GET_BITS(x, msb, lsb)           (((x) & ((1 << ((msb) + 1)) - 1)) >> (lsb))
+#define SET_BITS(x, msb, lsb, value)    (((x) & ~(((1 << ((msb) + 1)) - 1) ^ ((1 << (lsb)) - 1))) | (((value) & ((1 << (1 + (msb) - (lsb))) - 1)) << (lsb)))
+
+/*
+ *  LED Registers Mapping
+ */
+#define DANUBE_LED                      (KSEG1 + 0x1E100BB0)
+#define DANUBE_LED_CON0                 ((volatile u32*)(DANUBE_LED + 0x0000))
+#define DANUBE_LED_CON1                 ((volatile u32*)(DANUBE_LED + 0x0004))
+#define DANUBE_LED_CPU0                 ((volatile u32*)(DANUBE_LED + 0x0008))
+#define DANUBE_LED_CPU1                 ((volatile u32*)(DANUBE_LED + 0x000C))
+#define DANUBE_LED_AR                   ((volatile u32*)(DANUBE_LED + 0x0010))
+
+/*
+ *  LED Control 0 Register
+ */
+#define LED_CON0_SWU                    (*DANUBE_LED_CON0 & (1 << 31))
+#define LED_CON0_FALLING_EDGE           (*DANUBE_LED_CON0 & (1 << 26))
+#define LED_CON0_AD1                    (*DANUBE_LED_CON0 & (1 << 25))
+#define LED_CON0_AD0                    (*DANUBE_LED_CON0 & (1 << 24))
+#define LED_CON0_LBn(n)                 (*DANUBE_LED_CON0 & (1 << n))
+#define LED_CON0_DEFAULT_VALUE          (0x80000000 | (DATA_CLOCKING_EDGE << 26))
+
+/*
+ *  LED Control 1 Register
+ */
+#define LED_CON1_US                     (*DANUBE_LED_CON1 >> 30)
+#define LED_CON1_SCS                    (*DANUBE_LED_CON1 & (1 << 28))
+#define LED_CON1_FPID                   GET_BITS(*DANUBE_LED_CON1, 27, 23)
+#define LED_CON1_FPIS                   GET_BITS(*DANUBE_LED_CON1, 21, 20)
+#define LED_CON1_DO                     GET_BITS(*DANUBE_LED_CON1, 19, 18)
+#define LED_CON1_G2                     (*DANUBE_LED_CON1 & (1 << 2))
+#define LED_CON1_G1                     (*DANUBE_LED_CON1 & (1 << 1))
+#define LED_CON1_G0                     (*DANUBE_LED_CON1 & 0x01)
+#define LED_CON1_G                      (*DANUBE_LED_CON1 & 0x07)
+#define LED_CON1_DEFAULT_VALUE          0x00000000
+
+/*
+ *  LED Data Output CPU 0 Register
+ */
+#define LED_CPU0_Ln(n)                  (*DANUBE_LED_CPU0 & (1 << n))
+#define LED_LED_CPU0_DEFAULT_VALUE      0x00000000
+
+/*
+ *  LED Data Output CPU 1 Register
+ */
+#define LED_CPU1_Ln(n)                  (*DANUBE_LED_CPU1 & (1 << n))
+#define LED_LED_CPU1_DEFAULT_VALUE      0x00000000
+
+/*
+ *  LED Data Output Access Rights Register
+ */
+#define LED_AR_Ln(n)                    (*DANUBE_LED_AR & (1 << n))
+#define LED_AR_DEFAULT_VALUE            0x00000000
+
+
+/*
+ * ####################################
+ * Preparation of Debug on Amazon Chip
+ * ####################################
+ */
+
+/*
+ *  If try module on Amazon chip, prepare some tricks to prevent invalid memory write.
+ */
+#if defined(DEBUG_ON_AMAZON) && DEBUG_ON_AMAZON
+    char g_pFakeRegisters[0x50];
+
+    #undef  DEBUG_WRITE_REGISTER
+
+    #undef  DANUBE_LED
+    #define DANUBE_LED                  g_pFakeRegisters
+
+    #undef  port_reserve_pin
+    #undef  port_free_pin
+    #undef  port_set_altsel0
+    #undef  port_clear_altsel0
+    #undef  port_set_altsel1
+    #undef  port_clear_altsel1
+    #undef  port_set_dir_out
+
+    #define port_reserve_pin            amazon_port_reserve_pin
+    #define port_free_pin               amazon_port_free_pin
+    #define port_set_altsel0            amazon_port_set_altsel0
+    #define port_clear_altsel0          amazon_port_clear_altsel0
+    #define port_set_altsel1            amazon_port_set_altsel1
+    #define port_clear_altsel1          amazon_port_clear_altsel1
+    #define port_set_dir_out            amazon_port_set_dir_out
+#endif  //  defined(DEBUG_ON_AMAZON) && DEBUG_ON_AMAZON
+
+
+/*
+ * ####################################
+ *             Declaration
+ * ####################################
+ */
+
+/*
+ *  File Operations
+ */
+static int led_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+static int led_open(struct inode *, struct file *);
+static int led_release(struct inode *, struct file *);
+
+/*
+ *  Software Update LED
+ */
+static inline int update_led(void);
+
+/*
+ *  LED Configuration Functions
+ */
+static inline u32 set_update_source(u32, unsigned long, unsigned long);
+static inline u32 set_blink_in_batch(u32, unsigned long, unsigned long);
+static inline u32 set_data_clock_edge(u32, unsigned long);
+static inline u32 set_update_clock(u32, unsigned long, unsigned long);
+static inline u32 set_store_mode(u32, unsigned long);
+static inline u32 set_shift_clock(u32, unsigned long);
+static inline u32 set_data_offset(u32, unsigned long);
+static inline u32 set_number_of_enabled_led(u32, unsigned long);
+static inline u32 set_data_in_batch(u32, unsigned long, unsigned long);
+static inline u32 set_access_right(u32, unsigned long, unsigned long);
+
+/*
+ *  PMU Operation
+ */
+static inline void enable_led(void);
+static inline void disable_led(void);
+
+/*
+ *  GPIO Setup & Release
+ */
+static inline int setup_gpio_port(unsigned long);
+static inline void release_gpio_port(unsigned long);
+
+/*
+ *  GPT Setup & Release
+ */
+static inline int setup_gpt(int, unsigned long);
+static inline void release_gpt(int);
+
+/*
+ *  Turn On/Off LED
+ */
+static inline int turn_on_led(unsigned long);
+static inline void turn_off_led(unsigned long);
+
+
+/*
+ * ####################################
+ *            Local Variable
+ * ####################################
+ */
+
+static struct semaphore led_sem;
+
+static struct file_operations led_fops = {
+    owner:      THIS_MODULE,
+    ioctl:      led_ioctl,
+    open:       led_open,
+    release:    led_release
+};
+
+static struct miscdevice led_miscdev = {
+    LED_MINOR,
+    "led",
+    &led_fops,
+    NULL,
+    NULL,
+    NULL
+};
+
+static unsigned long gpt_on = 0;
+static unsigned long gpt_freq = 0;
+
+static unsigned long adsl_on = 0;
+static unsigned long f_led_on = 0;
+
+static int module_id;
+
+
+/*
+ * ####################################
+ *           Global Variable
+ * ####################################
+ */
+
+
+/*
+ * ####################################
+ *            Local Function
+ * ####################################
+ */
+
+static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+    int ret = -EINVAL;
+    struct led_config_param param;
+
+    switch ( cmd )
+    {
+    case LED_CONFIG:
+        copy_from_user(&param, (char*)arg, sizeof(param));
+        ret = danube_led_config(&param);
+        break;
+    }
+
+    return ret;
+}
+
+static int led_open(struct inode *inode, struct file *file)
+{
+    return 0;
+}
+
+static int led_release(struct inode *inode, struct file *file)
+{
+    return 0;
+}
+
+/*
+ *  Description:
+ *    Update LEDs with data stored in register.
+ *  Input:
+ *    none
+ *  Output:
+ *    int --- 0:    Success
+ *            else: Error Code
+ */
+static inline int update_led(void)
+{
+    int i, j;
+
+    /*
+     *  GPT2 or FPID is the clock to update LEDs automatically.
+     */
+    if ( LED_CON1_US != 0 )
+        return 0;
+
+    /*
+     *  Check the status to prevent conflict of two consecutive update
+     */
+    for ( i = 100000; i != 0; i -= j / 16 )
+    {
+        down(&led_sem);
+        if ( !LED_CON0_SWU )
+        {
+            *DANUBE_LED_CON0 |= 1 << 31;
+            up(&led_sem);
+            return 0;
+        }
+        else
+            up(&led_sem);
+        for ( j = 0; j < 1000 * 16; j++ );
+    }
+
+    return -EBUSY;
+}
+
+/*
+ *  Description:
+ *    Select update source for LED bit 0 and bit 1.
+ *  Input:
+ *    reg    --- u32, the original register value going to be modified.
+ *    led    --- unsigned long, bit 0 stands for LED 0, and bit 1 stands for
+ *               LED 1. If the bit is set, the source value is valid, else
+ *               the source value is invalid.
+ *    source --- unsigned long, bit 0 stands for LED 0, and bit 1 stands for
+ *               LED 1. If the corresponding is cleared, LED is updated with
+ *               value in data register, else LED is updated with ARC module.
+ *  Output:
+ *    u32    --- The updated register value.
+ */
+static inline u32 set_update_source(u32 reg, unsigned long led, unsigned long source)
+{
+    return (reg & ~((led & 0x03) << 24)) | ((source & 0x03) << 24);
+}
+
+/*
+ *  Description:
+ *    Define which of the LEDs should change their value based on the US pulse.
+ *  Input:
+ *    reg    --- u32, the original register value going to be modified.
+ *    mask   --- unsigned long, if the corresponding bit is set, the blink value
+ *               is valid, else the blink value is invalid.
+ *    blink  --- unsigned long, if the corresponding bit is set, the LED should
+ *               change its value based on the US pulse.
+ *  Output:
+ *    u32    --- The updated register value.
+ */
+static inline u32 set_blink_in_batch(u32 reg, unsigned long mask, unsigned long blink)
+{
+    return (reg & (~(mask & 0x00FFFFFF) & 0x87FFFFFF)) | (blink & 0x00FFFFFF);
+}
+
+static inline u32 set_data_clock_edge(u32 reg, unsigned long f_on_rising_edge)
+{
+    return f_on_rising_edge ? (reg & ~(1 << 26)) : (reg | (1 << 26));
+}
+
+/*
+ *  Description:
+ *    Select the clock source for US pulse.
+ *  Input:
+ *    reg    --- u32, the original register value going to be modified.
+ *    clock  --- unsigned long, there 3 available values:
+ *               0x00 - use software update bit (SWU) as source.
+ *               0x01 - use GPT2 as clock source.
+ *               0x02 - use FPI as clock source.
+ *    fpid   --- unsigned long, if FPI is selected as clock source, this field
+ *               specify the divider. Please refer to specification for detail
+ *               description.
+ *  Output:
+ *    u32    --- The updated register value.
+ */
+static inline u32 set_update_clock(u32 reg, unsigned long clock, unsigned long fpid)
+{
+    switch ( clock )
+    {
+    case 0: reg &= ~0xC0000000; break;
+    case 1: reg = (reg & ~0xC0000000) | 0x40000000; break;
+    case 2: reg = (reg & ~0xCF800000) | 0x80000000 | ((fpid & 0x1F) << 23); break;
+    }
+    return reg;
+}
+
+/*
+ *  Description:
+ *    Set the behavior of the LED_ST (shift register) signal.
+ *  Input:
+ *    reg    --- u32, the original register value going to be modified.
+ *    mode   --- unsigned long, there 2 available values:
+ *               zero     - LED controller generate single pulse.
+ *               non-zero - LED controller generate inverted shift clock.
+ *  Output:
+ *    u32    --- The updated register value.
+ */
+static inline u32 set_store_mode(u32 reg, unsigned long mode)
+{
+    return mode ? (reg | (1 << 28)) : (reg & ~(1 << 28));
+}
+
+/*
+ *  Description:
+ *    Select the clock source for shift clock LED_SH.
+ *  Input:
+ *    reg    --- u32, the original register value going to be modified.
+ *    fpis   --- unsigned long, if FPI is selected as clock source, this field
+ *               specify the divider. Please refer to specification for detail
+ *               description.
+ *  Output:
+ *    u32    --- The updated register value.
+ */
+static inline u32 set_shift_clock(u32 reg, unsigned long fpis)
+{
+    return SET_BITS(reg, 21, 20, fpis);
+}
+
+/*
+ *  Description:
+ *    Set the clock cycle offset before data is transmitted to LED_D pin.
+ *  Input:
+ *    reg    --- u32, the original register value going to be modified.
+ *    offset --- unsigned long, the number of clock cycles would be inserted
+ *               before data is transmitted to LED_D pin. Zero means no cycle
+ *               inserted.
+ *  Output:
+ *    u32    --- The updated register value.
+ */
+static inline u32 set_data_offset(u32 reg, unsigned long offset)
+{
+    return SET_BITS(reg, 19, 18, offset);
+}
+
+/*
+ *  Description:
+ *    Enable or disable LEDs.
+ *  Input:
+ *    reg    --- u32, the original register value going to be modified.
+ *    number --- unsigned long, the number of LED to be enabled. This field
+ *               could 0, 8, 16 or 24. Zero means disable all LEDs.
+ *  Output:
+ *    u32    --- The updated register value.
+ */
+static inline u32 set_number_of_enabled_led(u32 reg, unsigned long number)
+{
+    u32 bit_mask;
+
+    bit_mask = number > 16 ? 0x07 : (number > 8 ? 0x03 : (number ? 0x01 : 0x00));
+    return (reg & ~0x07) | bit_mask;
+}
+
+/*
+ *  Description:
+ *    Turn on/off LEDs.
+ *  Input:
+ *    reg    --- u32, the original register value going to be modified.
+ *    mask   --- unsigned long, if the corresponding bit is set, the data value
+ *               is valid, else the data value is invalid.
+ *    data   --- unsigned long, if the corresponding bit is set, the LED should
+ *               be on, else be off.
+ *  Output:
+ *    u32    --- The updated register value.
+ */
+static inline u32 set_data_in_batch(u32 reg, unsigned long mask, unsigned long data)
+{
+    return (reg & ~(mask & 0x00FFFFFF)) | (data & 0x00FFFFFF);
+}
+
+static inline u32 set_access_right(u32 reg, unsigned long mask, unsigned long ar)
+{
+    return (reg & ~(mask & 0x00FFFFFF)) | (~ar & mask);
+}
+
+/*
+ *  Description:
+ *    Enable LED control module.
+ *  Input:
+ *    none
+ *  Output:
+ *    none
+ */
+static inline void enable_led(void)
+{
+#if !defined(DEBUG_ON_AMAZON) || !DEBUG_ON_AMAZON
+    /*  Activate LED module in PMU. */
+    int i = 1000000;
+
+    *(unsigned long *)0xBF10201C &= ~(1 << 11);
+    while ( --i && (*(unsigned long *)0xBF102020 & (1 << 11)) );
+    if ( !i )
+        panic("Activating LED in PMU failed!");
+#endif
+}
+
+/*
+ *  Description:
+ *    Disable LED control module.
+ *  Input:
+ *    none
+ *  Output:
+ *    none
+ */
+static inline void disable_led(void)
+{
+#if !defined(DEBUG_ON_AMAZON) || !DEBUG_ON_AMAZON
+    /*  Inactivating LED module in PMU.    */
+    *(unsigned long *)0xBF10201C |= 1 << 11;
+#endif
+}
+
+/*
+ *  Description:
+ *    If LEDs are enabled, GPIO must be setup to enable LED pins.
+ *  Input:
+ *    none
+ *  Output:
+ *    int --- 0:    Success
+ *            else: Error Code
+ */
+static inline int setup_gpio_port(unsigned long adsl)
+{
+#if !defined(DEBUG_ON_AMAZON) || !DEBUG_ON_AMAZON
+    int ret = 0;
+
+  #if defined(DEBUG_WRITE_REGISTER) && DEBUG_WRITE_REGISTER
+    if ( adsl )
+    {
+        *(unsigned long *)0xBE100B18 |=  0x30;
+        *(unsigned long *)0xBE100B1C |=  0x20;
+        *(unsigned long *)0xBE100B1C &= ~0x10;
+        *(unsigned long *)0xBE100B20 |=  0x30;
+        *(unsigned long *)0xBE100B24 |=  0x30;
+    }
+    else
+    {
+        *(unsigned long *)0xBE100B18 |=  0x70;
+        *(unsigned long *)0xBE100B1C |=  0x70;
+        *(unsigned long *)0xBE100B20 &= ~0x70;
+        *(unsigned long *)0xBE100B24 |=  0x70;
+    }
+  #else
+
+    /*
+     *  Reserve all pins before config them.
+     */
+    if ( adsl )
+    {
+        ret |= port_reserve_pin(LED_ADSL0_PORT, LED_ADSL0_PIN, module_id);
+        ret |= port_reserve_pin(LED_ADSL1_PORT, LED_ADSL1_PIN, module_id);
+    }
+    else
+    {
+        ret |= port_reserve_pin(LED_ST_PORT, LED_ST_PIN, module_id);
+        ret |= port_reserve_pin(LED_D_PORT, LED_D_PIN, module_id);
+        ret |= port_reserve_pin(LED_SH_PORT, LED_SH_PIN, module_id);
+    }
+    if ( ret )
+    {
+        release_gpio_port(adsl);
+        return ret; //  Should be -EBUSY
+    }
+
+    if ( adsl )
+    {
+        LED_ADSL0_ALTSEL0_SETUP(LED_ADSL0_PORT, LED_ADSL0_PIN, module_id);
+        LED_ADSL0_ALTSEL1_SETUP(LED_ADSL0_PORT, LED_ADSL0_PIN, module_id);
+        LED_ADSL0_DIR_SETUP(LED_ADSL0_PORT, LED_ADSL0_PIN, module_id);
+        LED_ADSL0_OPENDRAIN_SETUP(LED_ADSL0_PORT, LED_ADSL0_PIN, module_id);
+
+        LED_ADSL1_ALTSEL0_SETUP(LED_ADSL1_PORT, LED_ADSL1_PIN, module_id);
+        LED_ADSL1_ALTSEL1_SETUP(LED_ADSL1_PORT, LED_ADSL1_PIN, module_id);
+        LED_ADSL1_DIR_SETUP(LED_ADSL1_PORT, LED_ADSL1_PIN, module_id);
+        LED_ADSL1_OPENDRAIN_SETUP(LED_ADSL1_PORT, LED_ADSL1_PIN, module_id);
+    }
+    else
+    {
+        /*
+         *  Set LED_ST
+         *    I don't check the return value, because I'm sure the value is valid
+         *    and the pins are reserved already.
+         */
+        LED_ST_ALTSEL0_SETUP(LED_ST_PORT, LED_ST_PIN, module_id);
+        LED_ST_ALTSEL1_SETUP(LED_ST_PORT, LED_ST_PIN, module_id);
+        LED_ST_DIR_SETUP(LED_ST_PORT, LED_ST_PIN, module_id);
+        LED_ST_OPENDRAIN_SETUP(LED_ST_PORT, LED_ST_PIN, module_id);
+
+        /*
+         *  Set LED_D
+         */
+        LED_D_ALTSEL0_SETUP(LED_D_PORT, LED_D_PIN, module_id);
+        LED_D_ALTSEL1_SETUP(LED_D_PORT, LED_D_PIN, module_id);
+        LED_D_DIR_SETUP(LED_D_PORT, LED_D_PIN, module_id);
+        LED_D_OPENDRAIN_SETUP(LED_D_PORT, LED_D_PIN, module_id);
+
+        /*
+         *  Set LED_SH
+         */
+        LED_SH_ALTSEL0_SETUP(LED_SH_PORT, LED_SH_PIN, module_id);
+        LED_SH_ALTSEL1_SETUP(LED_SH_PORT, LED_SH_PIN, module_id);
+        LED_SH_DIR_SETUP(LED_SH_PORT, LED_SH_PIN, module_id);
+        LED_SH_OPENDRAIN_SETUP(LED_SH_PORT, LED_SH_PIN, module_id);
+    }
+  #endif
+#endif
+
+    return 0;
+}
+
+/*
+ *  Description:
+ *    If LEDs are all disabled, GPIO must be released so that other application
+ *    could reuse it.
+ *  Input:
+ *    none
+ *  Output:
+ *    none
+ */
+static inline void release_gpio_port(unsigned long adsl)
+{
+#if !defined(DEBUG_ON_AMAZON) || !DEBUG_ON_AMAZON
+  #if !defined(DEBUG_WRITE_REGISTER) || !DEBUG_WRITE_REGISTER
+    if ( adsl )
+    {
+        port_free_pin(LED_ADSL0_PORT, LED_ADSL0_PIN, module_id);
+        port_free_pin(LED_ADSL1_PORT, LED_ADSL1_PIN, module_id);
+    }
+    else
+    {
+        port_free_pin(LED_ST_PORT, LED_ST_PIN, module_id);
+        port_free_pin(LED_D_PORT, LED_D_PIN, module_id);
+        port_free_pin(LED_SH_PORT, LED_SH_PIN, module_id);
+    }
+  #endif
+#endif
+}
+
+/*
+ *  Description:
+ *    If shifter or update select GPT as clock source, this function would be
+ *    invoked to setup corresponding GPT module.
+ *    Attention please, this function is not working since the GPTU driver is
+ *    not ready.
+ *  Input:
+ *    timer  --- int, index of timer.
+ *    freq   --- unsigned long, frequency of timer (0.001Hz). This value will be
+ *               rounded off to nearest possible value.
+ *  Output:
+ *    int --- 0:    Success
+ *            else: Error Code
+ */
+static inline int setup_gpt(int timer, unsigned long freq)
+{
+    int ret;
+
+#if 0
+    timer = TIMER(timer, 0);
+#else
+    timer = TIMER(timer, 1);    //  2B
+#endif
+
+#if 0
+    ret  = set_timer(timer, freq, 1, 0, TIMER_FLAG_NO_HANDLE, 0, 0);
+#else
+    ret  = request_timer(timer,
+                           TIMER_FLAG_SYNC
+                         | TIMER_FLAG_16BIT
+                         | TIMER_FLAG_INT_SRC
+                         | TIMER_FLAG_CYCLIC | TIMER_FLAG_COUNTER | TIMER_FLAG_DOWN
+                         | TIMER_FLAG_ANY_EDGE
+                         | TIMER_FLAG_NO_HANDLE,
+                         8000000 / freq,
+                         0,
+                         0);
+
+#endif
+//    printk("setup_gpt: timer = %d, freq = %d, return = %d\n", timer, freq, ret);
+    if ( !ret )
+    {
+        ret = start_timer(timer, 0);
+        if ( ret )
+            free_timer(timer);
+    }
+
+    return ret;
+}
+
+/*
+ *  Description:
+ *    If shifter or update select other clock source, allocated GPT must be
+ *    released so that other application can use it.
+ *    Attention please, this function is not working since the GPTU driver is
+ *    not ready.
+ *  Input:
+ *    none
+ *  Output:
+ *    none
+ */
+static inline void release_gpt(int timer)
+{
+#if 0
+    timer = TIMER(timer, 0);
+#else
+    timer = TIMER(timer, 1);
+#endif
+    stop_timer(timer);
+    free_timer(timer);
+}
+
+static inline int turn_on_led(unsigned long adsl)
+{
+    int ret;
+
+    ret = setup_gpio_port(adsl);
+    if ( ret )
+        return ret;
+
+    enable_led();
+
+    return 0;
+}
+
+static inline void turn_off_led(unsigned long adsl)
+{
+    release_gpio_port(adsl);
+    disable_led();
+}
+
+
+/*
+ * ####################################
+ *           Global Function
+ * ####################################
+ */
+
+/*
+ *  Description:
+ *    Define which of the LEDs should change its value based on the US pulse.
+ *  Input:
+ *    led    --- unsigned int, index of the LED to be set.
+ *    blink  --- unsigned int, zero means normal mode, and non-zero means blink
+ *               mode.
+ *  Output:
+ *    int    --- 0:    Success
+ *               else: Error Code
+ */
+int danube_led_set_blink(unsigned int led, unsigned int blink)
+{
+    u32 bit_mask;
+
+    if ( led > 23 )
+        return -EINVAL;
+
+    bit_mask = 1 << led;
+    down(&led_sem);
+    if ( blink )
+        *DANUBE_LED_CON0 |= bit_mask;
+    else
+        *DANUBE_LED_CON0 &= ~bit_mask;
+    up(&led_sem);
+
+    return (led == 0 && LED_CON0_AD0) || (led == 1 && LED_CON0_AD1) ? -EINVAL : 0;
+}
+
+/*
+ *  Description:
+ *    Turn on/off LED.
+ *  Input:
+ *    led    --- unsigned int, index of the LED to be set.
+ *    data   --- unsigned int, zero means off, and non-zero means on.
+ *  Output:
+ *    int    --- 0:    Success
+ *               else: Error Code
+ */
+int danube_led_set_data(unsigned int led, unsigned int data)
+{
+    unsigned long f_update;
+    u32 bit_mask;
+
+    if ( led > 23 )
+        return -EINVAL;
+
+    bit_mask = 1 << led;
+    down(&led_sem);
+    if ( data )
+        *DANUBE_LED_CPU0 |= bit_mask;
+    else
+        *DANUBE_LED_CPU0 &= ~bit_mask;
+    f_update = !(*DANUBE_LED_AR & bit_mask);
+    up(&led_sem);
+
+    return f_update ? update_led() : 0;
+}
+
+/*
+ *  Description:
+ *    Config LED controller.
+ *  Input:
+ *    param   --- struct led_config_param*, the members are listed below:
+ *                  operation_mask         - Select operations to be performed
+ *                  led                    - LED to change update source
+ *                  source                 - Corresponding update source
+ *                  blink_mask             - LEDs to set blink mode
+ *                  blink                  - Set to blink mode or normal mode
+ *                  update_clock           - Select the source of update clock
+ *                  fpid                   - If FPI is the source of update clock, set the divider
+ *                  store_mode             - Set clock mode or single pulse mode for store signal
+ *                  fpis                   - If FPI is the source of shift clock, set the divider
+ *                  data_offset            - Set cycles to be inserted before data is transmitted
+ *                  number_of_enabled_led  - Total number of LED to be enabled
+ *                  data_mask              - LEDs to set value
+ *                  data                   - Corresponding value
+ *                  mips0_access_mask      - LEDs to set access right
+ *                  mips0_access;          - 1: the corresponding data is output from MIPS0, 0: MIPS1
+ *                  f_data_clock_on_rising - 1: data clock on rising edge, 0: data clock on falling edge
+ *  Output:
+ *    int    --- 0:    Success
+ *               else: Error Code
+ */
+int danube_led_config(struct led_config_param* param)
+{
+    int ret;
+    u32 reg_con0, reg_con1, reg_cpu0, reg_ar;
+    u32 clean_reg_con0, clean_reg_con1, clean_reg_cpu0, clean_reg_ar;
+    u32 f_setup_gpt2;
+    u32 f_software_update;
+    u32 new_led_on, new_adsl_on;
+
+    if ( !param )
+        return -EINVAL;
+
+    down(&led_sem);
+
+    reg_con0 = *DANUBE_LED_CON0;
+    reg_con1 = *DANUBE_LED_CON1;
+    reg_cpu0 = *DANUBE_LED_CPU0;
+    reg_ar   = *DANUBE_LED_AR;
+
+    clean_reg_con0 = 1;
+    clean_reg_con1 = 1;
+    clean_reg_cpu0 = 1;
+    clean_reg_ar   = 1;
+
+    f_setup_gpt2 = 0;
+
+    f_software_update = LED_CON0_SWU ? 0 : 1;
+
+    new_led_on = f_led_on;
+    new_adsl_on = adsl_on;
+
+    /*  ADSL or LED */
+    if ( (param->operation_mask & CONFIG_OPERATION_UPDATE_SOURCE) )
+    {
+        if ( param->led > 0x03 || param->source > 0x03 )
+            goto INVALID_PARAM;
+        clean_reg_con0 = 0;
+        reg_con0 = set_update_source(reg_con0, param->led, param->source);
+#if 0   //  ADSL0,1 is source for bit 0, 1 in shift register
+        new_adsl_on = param->source;
+#endif
+    }
+
+    /*  Blink   */
+    if ( (param->operation_mask & CONFIG_OPERATION_BLINK) )
+    {
+        if ( (param->blink_mask & 0xFF000000) || (param->blink & 0xFF000000) )
+            goto INVALID_PARAM;
+        clean_reg_con0 = 0;
+        reg_con0 = set_blink_in_batch(reg_con0, param->blink_mask, param->blink);
+    }
+
+    /*  Edge    */
+    if ( (param->operation_mask & CONFIG_DATA_CLOCK_EDGE) )
+    {
+        clean_reg_con0 = 0;
+        reg_con0 = set_data_clock_edge(reg_con0, param->f_data_clock_on_rising);
+    }
+
+    /*  Update Clock    */
+    if ( (param->operation_mask & CONFIG_OPERATION_UPDATE_CLOCK) )
+    {
+        if ( param->update_clock > 0x02 || (param->update_clock == 0x02 && param->fpid > 0x3) )
+            goto INVALID_PARAM;
+        clean_reg_con1 = 0;
+        f_software_update = param->update_clock == 0 ? 1 : 0;
+        if ( param->update_clock == 0x01 )
+            f_setup_gpt2 = 1;
+        reg_con1 = set_update_clock(reg_con1, param->update_clock, param->fpid);
+    }
+
+    /*  Store Mode  */
+    if ( (param->operation_mask & CONFIG_OPERATION_STORE_MODE) )
+    {
+        clean_reg_con1 = 0;
+        reg_con1 = set_store_mode(reg_con1, param->store_mode);
+    }
+
+    /*  Shift Clock */
+    if ( (param->operation_mask & CONFIG_OPERATION_SHIFT_CLOCK) )
+    {
+        if ( param->fpis > 0x03 )
+            goto INVALID_PARAM;
+        clean_reg_con1 = 0;
+        reg_con1 = set_shift_clock(reg_con1, param->fpis);
+    }
+
+    /*  Data Offset */
+    if ( (param->operation_mask & CONFIG_OPERATION_DATA_OFFSET) )
+    {
+        if ( param->data_offset > 0x03 )
+            goto INVALID_PARAM;
+        clean_reg_con1 = 0;
+        reg_con1 = set_data_offset(reg_con1, param->data_offset);
+    }
+
+    /*  Number of LED   */
+    if ( (param->operation_mask & CONFIG_OPERATION_NUMBER_OF_LED) )
+    {
+        if ( param->number_of_enabled_led > 0x24 )
+            goto INVALID_PARAM;
+
+        /*
+         *  If there is at lease one LED enabled, the GPIO pin must be setup.
+         */
+        new_led_on = param->number_of_enabled_led ? 1 : 0;
+
+        clean_reg_con1 = 0;
+        reg_con1 = set_number_of_enabled_led(reg_con1, param->number_of_enabled_led);
+    }
+
+    /*  LED Data    */
+    if ( (param->operation_mask & CONFIG_OPERATION_DATA) )
+    {
+        if ( (param->data_mask & 0xFF000000) || (param->data & 0xFF000000) )
+            goto INVALID_PARAM;
+        clean_reg_cpu0 = 0;
+        reg_cpu0 = set_data_in_batch(reg_cpu0, param->data_mask, param->data);
+        if ( f_software_update )
+        {
+            clean_reg_con0 = 0;
+            reg_con0 |= 0x80000000;
+        }
+    }
+
+    /*  Access Right    */
+    if ( (param->operation_mask & CONFIG_OPERATION_MIPS0_ACCESS) )
+    {
+        if ( (param->mips0_access_mask & 0xFF000000) || (param->mips0_access & 0xFF000000) )
+            goto INVALID_PARAM;
+        clean_reg_ar = 0;
+        reg_ar = set_access_right(reg_ar, param->mips0_access_mask, param->mips0_access);
+    }
+
+    /*  Setup GPT   */
+    if ( f_setup_gpt2 && !new_adsl_on )     //  If ADSL led is on, GPT is disabled.
+    {
+        ret = 0;
+
+        if ( gpt_on )
+        {
+            if ( gpt_freq != param->fpid )
+            {
+                release_gpt(2);
+                gpt_on = 0;
+                ret = setup_gpt(2, param->fpid);
+            }
+        }
+        else
+            ret = setup_gpt(2, param->fpid);
+
+        if ( ret )
+        {
+#if 1
+            printk("Setup GPT error!\n");
+#endif
+            goto SETUP_GPT_ERROR;
+        }
+        else
+        {
+#if 0
+            printk("Setup GPT successfully!\n");
+#endif
+            gpt_on = 1;
+        }
+    }
+    else
+        if ( gpt_on )
+        {
+            release_gpt(2);
+            gpt_on = 0;
+        }
+
+    /*  Turn on LED */
+    if ( new_adsl_on )
+        new_led_on = 1;
+    if ( !new_led_on || adsl_on != new_adsl_on )
+    {
+        turn_off_led(adsl_on);
+        f_led_on = 0;
+        adsl_on = 0;
+    }
+    if ( !f_led_on && new_led_on )
+    {
+        ret = turn_on_led(new_adsl_on);
+        if ( ret )
+        {
+#if 1
+            printk("Setup GPIO error!\n");
+#endif
+            goto SETUP_GPIO_ERROR;
+        }
+        adsl_on = new_adsl_on;
+        f_led_on = 1;
+    }
+
+#if 0
+    if ( (reg_con0 & 0x80000000) )
+        printk("software update\n");
+#endif
+
+    /*  Write Register  */
+    if ( !f_led_on )
+        enable_led();
+    if ( !clean_reg_ar )
+        *DANUBE_LED_AR   = reg_ar;
+    if ( !clean_reg_cpu0 )
+        *DANUBE_LED_CPU0 = reg_cpu0;
+    if ( !clean_reg_con1 )
+        *DANUBE_LED_CON1 = reg_con1;
+    if ( !clean_reg_con0 )
+        *DANUBE_LED_CON0 = reg_con0;
+    if ( !f_led_on )
+        disable_led();
+
+#if defined(DEBUG_ON_AMAZON) && DEBUG_ON_AMAZON
+    *DANUBE_LED_CON0 &= 0x7FFFFFFF;
+#endif
+
+#if 0
+  #if !defined(DEBUG_ON_AMAZON) || !DEBUG_ON_AMAZON
+    printk("*0xBF10201C      = 0x%08lX\n", *(unsigned long *)0xBF10201C);
+    printk("*0xBE100B18      = 0x%08lX\n", *(unsigned long *)0xBE100B18);
+    printk("*0xBE100B1C      = 0x%08lX\n", *(unsigned long *)0xBE100B1C);
+    printk("*0xBE100B20      = 0x%08lX\n", *(unsigned long *)0xBE100B20);
+    printk("*0xBE100B24      = 0x%08lX\n", *(unsigned long *)0xBE100B24);
+  #endif
+    printk("*DANUBE_LED_CON0 = 0x%08X\n", *DANUBE_LED_CON0);
+    printk("*DANUBE_LED_CON1 = 0x%08X\n", *DANUBE_LED_CON1);
+    printk("*DANUBE_LED_CPU0 = 0x%08X\n", *DANUBE_LED_CPU0);
+    printk("*DANUBE_LED_CPU1 = 0x%08X\n", *DANUBE_LED_CPU1);
+    printk("*DANUBE_LED_AR   = 0x%08X\n", *DANUBE_LED_AR);
+#endif
+
+    up(&led_sem);
+    return 0;
+
+SETUP_GPIO_ERROR:
+    release_gpt(2);
+    gpt_on = 0;
+SETUP_GPT_ERROR:
+    up(&led_sem);
+    return ret;
+
+INVALID_PARAM:
+    up(&led_sem);
+    return -EINVAL;
+}
+
+
+/*
+ * ####################################
+ *           Init/Cleanup API
+ * ####################################
+ */
+
+/*
+ *  Description:
+ *    register device
+ *  Input:
+ *    none
+ *  Output:
+ *    0    --- successful
+ *    else --- failure, usually it is negative value of error code
+ */
+int __init danube_led_init(void)
+{
+    int ret;
+    struct led_config_param param = {0};
+
+    enable_led();
+
+    /*
+     *  Set default value to registers to turn off all LED light.
+     */
+    *DANUBE_LED_AR   = LED_AR_DEFAULT_VALUE;
+    *DANUBE_LED_CPU0 = LED_LED_CPU0_DEFAULT_VALUE;
+    *DANUBE_LED_CPU1 = LED_LED_CPU1_DEFAULT_VALUE;
+    *DANUBE_LED_CON1 = LED_CON1_DEFAULT_VALUE;
+    *DANUBE_LED_CON0 = LED_CON0_DEFAULT_VALUE;
+
+#if defined(DEBUG_ON_AMAZON) && DEBUG_ON_AMAZON
+    *DANUBE_LED_CON0 &= 0x7FFFFFFF;
+#endif
+
+    disable_led();
+
+    sema_init(&led_sem, 0);
+
+    ret = misc_register(&led_miscdev);
+    if ( ret == -EBUSY )
+    {
+        led_miscdev.minor = MISC_DYNAMIC_MINOR;
+        ret = misc_register(&led_miscdev);
+    }
+    if ( ret )
+    {
+        printk(KERN_ERR "led: can't misc_register\n");
+        return ret;
+    }
+    else
+        printk(KERN_INFO "led: misc_register on minor = %d\n", led_miscdev.minor);
+
+    module_id = THIS_MODULE ? (int)THIS_MODULE : ((MISC_MAJOR << 8) | led_miscdev.minor);
+
+    up(&led_sem);
+
+#if BOARD_TYPE == REFERENCE_BOARD
+    /*  Add to enable hardware relay    */
+        /*  Map for LED on reference board
+              WLAN_READ     LED11   OUT1    15
+              WARNING       LED12   OUT2    14
+              FXS1_LINK     LED13   OUT3    13
+              FXS2_LINK     LED14   OUT4    12
+              FXO_ACT       LED15   OUT5    11
+              USB_LINK      LED16   OUT6    10
+              ADSL2_LINK    LED19   OUT7    9
+              BT_LINK       LED17   OUT8    8
+              SD_LINK       LED20   OUT9    7
+              ADSL2_TRAFFIC LED31   OUT16   0
+            Map for hardware relay on reference board
+              USB Power On          OUT11   5
+              RELAY                 OUT12   4
+        */
+    param.operation_mask = CONFIG_OPERATION_NUMBER_OF_LED;
+    param.number_of_enabled_led = 16;
+    danube_led_config(&param);
+    param.operation_mask = CONFIG_OPERATION_DATA;
+    param.data_mask = 1 << 4;
+    param.data = 1 << 4;
+    danube_led_config(&param);
+#endif
+
+    //  by default, update by FSC clock (FPID)
+    param.operation_mask = CONFIG_OPERATION_UPDATE_CLOCK;
+    param.update_clock   = 2;   //  FPID
+    param.fpid           = 3;   //  10Hz
+    danube_led_config(&param);
+
+    //  source of LED 0, 1 is ADSL
+    param.operation_mask = CONFIG_OPERATION_UPDATE_SOURCE;
+    param.led            = 3;   //  LED 0, 1
+    param.source         = 3;   //  ADSL
+    danube_led_config(&param);
+
+    //  turn on USB
+    param.operation_mask = CONFIG_OPERATION_DATA;
+    param.data_mask = 1 << 5;
+    param.data = 1 << 5;
+    danube_led_config(&param);
+
+    return 0;
+}
+
+/*
+ *  Description:
+ *    deregister device
+ *  Input:
+ *    none
+ *  Output:
+ *    none
+ */
+void __exit danube_led_exit(void)
+{
+    int ret;
+
+    ret = misc_deregister(&led_miscdev);
+    if ( ret )
+        printk(KERN_ERR "led: can't misc_deregister, get error number %d\n", -ret);
+    else
+        printk(KERN_INFO "led: misc_deregister successfully\n");
+}
+
+EXPORT_SYMBOL(danube_led_set_blink);
+EXPORT_SYMBOL(danube_led_set_data);
+EXPORT_SYMBOL(danube_led_config);
+
+module_init(danube_led_init);
+module_exit(danube_led_exit);
+