diff options
| -rw-r--r-- | target/linux/danube/config-2.6.23 | 3 | ||||
| -rw-r--r-- | target/linux/danube/files/arch/mips/danube/Kconfig | 4 | ||||
| -rw-r--r-- | target/linux/danube/files/drivers/char/danube_led.c | 1378 | 
3 files changed, 1384 insertions, 1 deletions
| diff --git a/target/linux/danube/config-2.6.23 b/target/linux/danube/config-2.6.23 index f575f6f13..b6f46d696 100644 --- a/target/linux/danube/config-2.6.23 +++ b/target/linux/danube/config-2.6.23 @@ -41,8 +41,10 @@ CONFIG_CPU_SUPPORTS_HIGHMEM=y  # CONFIG_CRYPTO_HW is not set  CONFIG_DANUBE=y  CONFIG_DANUBE_ASC_UART=y +# CONFIG_DANUBE_LED is not set  CONFIG_DANUBE_MII0=y  CONFIG_DANUBE_MII1=y +CONFIG_DANUBE_WDT=y  CONFIG_DEVPORT=y  # CONFIG_DM9000 is not set  CONFIG_DMA_NEED_PCI_MAP_STATE=y @@ -183,4 +185,3 @@ CONFIG_TRAD_SIGNALS=y  # CONFIG_VGASTATE is not set  # CONFIG_VIA_RHINE is not set  CONFIG_ZONE_DMA_FLAG=0 -CONFIG_DANUBE_WDT=y diff --git a/target/linux/danube/files/arch/mips/danube/Kconfig b/target/linux/danube/files/arch/mips/danube/Kconfig index 135b640b4..08e5fd4c1 100644 --- a/target/linux/danube/files/arch/mips/danube/Kconfig +++ b/target/linux/danube/files/arch/mips/danube/Kconfig @@ -16,5 +16,9 @@ config DANUBE_WDT  	bool "Danube watchdog"  	default y +config DANUBE_LED +	bool "Danube led" +	default y +  endmenu 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 index 000000000..531c7ed0c --- /dev/null +++ b/target/linux/danube/files/drivers/char/danube_led.c @@ -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(¶m, (char*)arg, sizeof(param)); +        ret = danube_led_config(¶m); +        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(¶m); +    param.operation_mask = CONFIG_OPERATION_DATA; +    param.data_mask = 1 << 4; +    param.data = 1 << 4; +    danube_led_config(¶m); +#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(¶m); + +    //  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(¶m); + +    //  turn on USB +    param.operation_mask = CONFIG_OPERATION_DATA; +    param.data_mask = 1 << 5; +    param.data = 1 << 5; +    danube_led_config(¶m); + +    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); + | 
