/****************************************************************************** ** ** FILE NAME : amazon_s_led.c ** PROJECT : Amazon_s ** MODULES : LED ** ** DATE : 22 JUL 2005 ** AUTHOR : Xu Liang ** DESCRIPTION : LED Controller Driver ** COPYRIGHT : Copyright (c) 2006 ** Infineon Technologies AG ** Am Campeon 1-12, 85579 Neubiberg, Germany ** ** 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. ** ** HISTORY ** $Date $Author $Comment ** 22 JUL 2005 Xu Liang Initiate Version ** 28 AUG 2006 Xu Liang Fix one bug in function "set_blink_in_batch". ** 2 OCT 2006 Xu Liang Enable USB on OUT11 (LED 5). ** 23 OCT 2006 Xu Liang Add GPL header. ** 6 MAR 2007 Xu Liang Add GPT 2B interrupt handler to simulate update ** clock output from GPT 2B. ** 30 JAN 2008 Lei Chuanhua Cleanup source code, verified on Amazon_S *******************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include /* Project header */ #include #include #include #include #define IFX_LED_DRV_VERSION "1.0.0" /* GPIO Pin used by led in the global view */ /* DIR=1, SEL0=1, SEL1=0, OD=1 */ #define LED_ST_PIN 4 /* DIR=1, SEL0=1, SEL1=0, OD=1 */ #define LED_D_PIN 5 /* DIR=1, SEL0=1, SEL1=0, OD=1 */ #define LED_SH_PIN 6 /* DIR=1, SEL0=0, SEL1=1, OD=1 */ #define LED_ADSL0_PIN 4 /* DIR=1, SEL0=1, SEL1=1, OD=1 */ #define LED_ADSL1_PIN 5 #define MAX_PIN_PER_PORT 16 /* Every port has 16 pins, up to 4 ports from 0~3 */ #define PIN2PORT(pin) ((((pin) >> 4) & 0x3)) #define PIN2PORTPIN(pin) ((pin) % (MAX_PIN_PER_PORT)) #define IFX_LED_PIN_RESERVE(pin) \ bsp_port_reserve_pin((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_LED)) #define IFX_LED_PIN_FREE(pin) \ bsp_port_free_pin((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_LED)) #define IFX_LED_DIR_OUT(pin) \ bsp_port_set_dir_out((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_LED)) #define IFX_LED_DIR_IN(pin) \ bsp_port_set_dir_in((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_LED)) #define IFX_LED_OUTPUT_SET(pin) \ bsp_port_set_output((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_LED)) #define IFX_LED_OUTPUT_CLR(pin) \ bsp_port_clear_output((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_LED)) #define IFX_LED_ALTSEL0_SET(pin) \ bsp_port_set_altsel0((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_LED)) #define IFX_LED_ALTSEL0_CLR(pin) \ bsp_port_clear_altsel0((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_LED)) #define IFX_LED_OD_SET(pin) \ bsp_port_set_open_drain((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_LED)) #define IFX_LED_OD_CLR(pin) \ bsp_port_clear_open_drain((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_LED)) #define IFX_LED_ALTSEL1_SET(pin) \ bsp_port_set_altsel1((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_LED)) #define IFX_LED_ALTSEL1_CLR(pin) \ bsp_port_clear_altsel1((PIN2PORT(pin)), (PIN2PORTPIN(pin)), (PORT_MODULE_LED)) /* 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))) static struct semaphore led_sem; 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; /** * Software updates LEDs with data stored in register. * * \return 0 OK * \return -EBUSY System resource is not available */ 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 -= i / 16 ) { #if !defined(GPT2_SOFTWARE_EMULATION) || !GPT2_SOFTWARE_EMULATION down(&led_sem); if ( !LED_CON0_SWU ) { *AMAZON_S_LED_CON0 |= 1 << 31; up(&led_sem); return 0; } else up(&led_sem); #else unsigned long sys_flag; local_irq_save(sys_flag); if ( !LED_CON0_SWU ) { *AMAZON_S_LED_CON0 |= 1 << 31; local_irq_restore(sys_flag); return 0; } local_irq_restore(sys_flag); #endif for ( j = 0; j < 1000 * 16; j++ ){ ; /* Do nothing */ } } return -EBUSY; } /** * Select update source for LED bit 0 and bit 1 for ADSL LED control * * \param reg Original register value to be modified * \param led BIT0 stands for LED0, and BIT1 stands for LED1. * If the bit is set, the source value is valid, else * the source value is invalid. * \param src BIT0 stands for LED0, and BIT1 stands for LED1. If the * corresponding is cleared, LED is updated with value in * data register, else LED is updated with ARC module. * \return The updated register value. */ static inline u32 set_update_source(u32 reg, unsigned long led, unsigned long src) { return (reg & ~((led & 0x03) << 24)) | ((src & 0x03) << 24); } /** * Define which of the LEDs should change their value based on the update source pulse. * * \param reg Original register value to be modified. * \param mask If the corresponding bit is set, the blink value is valid, * else the blink value is invalid. * \param blink if the corresponding bit is set, the LED should change its * value based on the US pulse. * \return 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); } /** * Define if the data output is clocked at clock rising or falling edge. * * \param reg Original register value to be modified. * \param edge If equal to zero, the fallig edge, if non-zero, the rising edge * \return The updated register value. */ static inline u32 set_data_clock_edge(u32 reg, unsigned long edge) { return edge ? (reg & ~(1 << 26)) : (reg | (1 << 26)); } /** * Select the clock source for update source pulse. * * \param reg Original register value to be modified. * \param clock Clock source. * 0 - use software update bit (SWU) as source. * 1 - use GPT2 as clock source. * 2 - use FPI as clock source. * \param fpid If FPI is selected as clock source, this field * specifies the divider. * \return 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; #if !defined(GPT2_SOFTWARE_EMULATION) || !GPT2_SOFTWARE_EMULATION case 1: reg = (reg & ~0xC0000000) | 0x40000000; break; #else case 1: reg &= ~0xC0000000; break; #endif case 2: reg = (reg & ~0xCF800000) | 0x80000000 | ((fpid & 0x1F) << 23); break; } return reg; } /** * Set the behavior of the LED_ST (Store register) signal. * * \param reg Original register value to be modified. * \param mode There are two available values: * Zero - LED controller generate single pulse. * Non-zero - LED controller generate inverted shift clock. * \return The updated register value. */ static inline u32 set_store_mode(u32 reg, unsigned long mode) { return mode ? (reg | (1 << 28)) : (reg & ~(1 << 28)); } /** * Select the clock source for shift clock LED_SH. * * \param reg Original register value to be modified. * \param fpis If FPI is selected as clock source, this field * specify the divider. * \param The updated register value. */ static inline u32 set_shift_clock(u32 reg, unsigned long fpis) { return SET_BITS(reg, 21, 20, fpis); } /** * Set the clock cycle offset before data is transmitted to LED_D pin. * * \param reg Original register value to be modified. * \param offset The number of clock cycles would be inserted before data * is transmitted to LED_D pin. Zero means no cycle inserted. * \return The updated register value. */ static inline u32 set_data_offset(u32 reg, unsigned long offset) { return SET_BITS(reg, 19, 18, offset); } /** * Enable or disable LED group * * \param reg Original register value to be modified. * \param num The number of LEDs to be enabled. This field * could 0, 8, 16 or 24. Zero means disable all LEDs. * \return The updated register value. */ static inline u32 set_number_of_enabled_led(u32 reg, unsigned long num) { u32 bit_mask; bit_mask = num > 16 ? 0x07 : (num > 8 ? 0x03 : (num ? 0x01 : 0x00)); return (reg & ~0x07) | bit_mask; } /** * Turn on/off batch of LEDs. * * \param reg Original register value to be modified. * \param mask If the corresponding bit is set, the data value * is valid, else the data value is invalid. * \param data If the corresponding bit is set, the LED should * be on, else be off. * \return 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); } /** * configures the LED outputs 23 to 0 to be controlled by which CPU/VPE. * * \param reg Original register value to be modified. * \param mask If the corresponding bit is set, the data value * is valid, else the data value is invalid. * \param ar If the corresponding bit is set, CPU0/VPE0 configure LED outputs. * else CPU1/VPE1 configure LED outputs. * \return The updated register value. */ static inline u32 set_access_right(u32 reg, unsigned long mask, unsigned long ar) { /* XXX, ~ar is not clear, if CPU1/VPE is used, needs to be changed */ return (reg & ~(mask & 0x00FFFFFF)) | (~ar & mask); } /** * Activate LED control module from PMU */ static inline void enable_led(void) { LEDC_PMU_SETUP(PMU_ENABLE); } /** * Disactivate LED control module from PMU */ static inline void disable_led(void) { LEDC_PMU_SETUP(PMU_DISABLE); } /** * If all LEDs are disabled, GPIO must be released so that other modules * could reuse it. * * \param adsl If adsl > 0, ADSL will uses LEDC pin. */ static inline void led_gpio_release(unsigned long adsl) { if ( adsl ) { IFX_LED_PIN_FREE(LED_ADSL0_PIN); IFX_LED_PIN_FREE(LED_ADSL1_PIN); } else { IFX_LED_PIN_FREE(LED_ST_PIN); IFX_LED_PIN_FREE(LED_D_PIN); IFX_LED_PIN_FREE(LED_SH_PIN); } } /** * If LEDs are enabled, GPIO must be setup to enable LED pins. * * \param adsl Configure different GPIO settings depending on using ADSL LED or not * \return 0 OK * \return -EBUSY GPIO is not reserved by other modules */ static inline int led_gpio_init(unsigned long adsl) { int ret = 0; /* Reserve all pins before config them. */ if ( adsl ) { ret |= IFX_LED_PIN_RESERVE(LED_ADSL0_PIN); ret |= IFX_LED_PIN_RESERVE(LED_ADSL1_PIN); } else { ret |= IFX_LED_PIN_RESERVE(LED_ST_PIN); ret |= IFX_LED_PIN_RESERVE(LED_D_PIN); ret |= IFX_LED_PIN_RESERVE(LED_SH_PIN); } if ( ret ) { led_gpio_release(adsl); return -EBUSY; } if ( adsl ) { IFX_LED_ALTSEL0_CLR(LED_ADSL0_PIN); IFX_LED_ALTSEL1_SET(LED_ADSL0_PIN); IFX_LED_DIR_OUT(LED_ADSL0_PIN); IFX_LED_OD_SET(LED_ADSL0_PIN); IFX_LED_ALTSEL0_SET(LED_ADSL1_PIN); IFX_LED_ALTSEL1_SET(LED_ADSL1_PIN); IFX_LED_DIR_OUT(LED_ADSL1_PIN); IFX_LED_OD_SET(LED_ADSL1_PIN); } else { /* Set LED_ST */ IFX_LED_ALTSEL0_SET(LED_ST_PIN); IFX_LED_ALTSEL1_CLR(LED_ST_PIN); IFX_LED_DIR_OUT(LED_ST_PIN); IFX_LED_OD_SET(LED_ST_PIN); /* Set LED_D */ IFX_LED_ALTSEL0_SET(LED_D_PIN); IFX_LED_ALTSEL1_CLR(LED_D_PIN); IFX_LED_DIR_OUT(LED_D_PIN); IFX_LED_OD_SET(LED_D_PIN); /* Set LED_SH */ IFX_LED_ALTSEL0_SET(LED_SH_PIN); IFX_LED_ALTSEL1_CLR(LED_SH_PIN); IFX_LED_DIR_OUT(LED_SH_PIN); IFX_LED_OD_SET(LED_SH_PIN); } return 0; } #if defined(GPT2_SOFTWARE_EMULATION) && GPT2_SOFTWARE_EMULATION static void fake_timer_callback(unsigned long arg) { int i, j; /* Check the status to prevent conflict of two consecutive update */ for ( i = 100000; i != 0; i -= i / 16 ) { if ( !LED_CON0_SWU ) { *AMAZON_S_LED_CON0 |= 1 << 31; return; } printk("update bit is set\n"); for ( j = 0; j < 1000 * 16; j++ ) { ; /* Do nothing */ } } } #endif /** * If shifter or update selected GPT2 as clock source, this function would be * invoked to setup corresponding GPT module. Depending on GPTU module * * \param timer Index of timer. * \param freq Frequency of timer (0.001Hz). This value will be rounded up * to nearest possible value. * \return 0 OK * \return < 0 Error number */ static inline int setup_gpt(int timer, unsigned long freq) { int ret; #if 0 timer = TIMER(timer, 0); #else timer = TIMER(timer, 1); #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_RISE_EDGE // org: | TIMER_FLAG_ANY_EDGE | TIMER_FLAG_CALLBACK_IN_IRQ, // org: | TIMER_FLAG_NO_HANDLE, 8000000 / freq, #if !defined(GPT2_SOFTWARE_EMULATION) || !GPT2_SOFTWARE_EMULATION 0, #else (unsigned long)fake_timer_callback, #endif 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; } /** * Release GPT2 so that other module could reuse it * * \param timer Timer index */ 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 = led_gpio_init(adsl); if ( ret ) return ret; enable_led(); return 0; } static inline void turn_off_led(unsigned long adsl) { led_gpio_release(adsl); disable_led(); } /** * Define which of the LEDs should change its value based on the US pulse. * * \param pin Index of the LED to be set. * \param blink Zero means normal mode, and non-zero means blink mode. * \return 0 OK * \return < 0 Error Code */ int bsp_led_set_blink(unsigned int pin, unsigned int blink) { u32 bit_mask; if ( pin > MAX_LED_PIN ) return -EINVAL; bit_mask = 1 << pin; #if !defined(GPT2_SOFTWARE_EMULATION) || !GPT2_SOFTWARE_EMULATION down(&led_sem); if ( blink ) *AMAZON_S_LED_CON0 |= bit_mask; else *AMAZON_S_LED_CON0 &= ~bit_mask; up(&led_sem); #else { unsigned long sys_flag; local_irq_save(sys_flag); if ( blink ) *AMAZON_S_LED_CON0 |= bit_mask; else *AMAZON_S_LED_CON0 &= ~bit_mask; local_irq_restore(sys_flag); } #endif return (pin == 0 && LED_CON0_AD0) || (pin == 1 && LED_CON0_AD1) ? -EINVAL : 0; } /** * Turn on/off LED. * * \param pin Index of the LED to be set. * \param on Zero means off, and non-zero means on. * \return 0 OK * \return < 0 Error Code */ int bsp_led_set_data(unsigned int pin, unsigned int on) { unsigned long f_update; u32 bit_mask; if ( pin > MAX_LED_PIN ) return -EINVAL; bit_mask = 1 << pin; down(&led_sem); if ( on ) *AMAZON_S_LED_CPU0 |= bit_mask; else *AMAZON_S_LED_CPU0 &= ~bit_mask; f_update = !(*AMAZON_S_LED_AR & bit_mask); up(&led_sem); return f_update ? update_led() : 0; } /** * Config LED controller. * * \param param Pointer to 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 * * \return 0 OK * \return <0 Error Code */ int bsp_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_update_clock; u32 f_setup_gpt2; u32 f_software_update; u32 new_led_on, new_adsl_on; if ( !param ) return -EINVAL; down(&led_sem); reg_con0 = *AMAZON_S_LED_CON0; reg_con1 = *AMAZON_S_LED_CON1; reg_cpu0 = *AMAZON_S_LED_CPU0; reg_ar = *AMAZON_S_LED_AR; clean_reg_con0 = 1; clean_reg_con1 = 1; clean_reg_cpu0 = 1; clean_reg_ar = 1; f_setup_update_clock = 0; 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; f_setup_update_clock = 1; 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 > (MAX_LED_PIN + 1) ) 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 which control on/off */ 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_update_clock ) { /* If ADSL led is on, GPT is disabled. */ if ( f_setup_gpt2 && !new_adsl_on ) { ret = 0; if ( gpt_on ) { if ( gpt_freq != param->fpid ) { release_gpt(2); gpt_on = 0; ret = setup_gpt(2, param->fpid); gpt_freq = param->fpid; } } else ret = setup_gpt(2, param->fpid); if ( ret ){ printk("Setup GPT error!\n"); goto SETUP_GPT_ERROR; } else { gpt_on = 1; } } else if ( gpt_on ) { /* Any other cases should shutdown gpt2 */ 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 ) { printk("Setup GPIO error!\n"); 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 ) *AMAZON_S_LED_AR = reg_ar; if ( !clean_reg_cpu0 ) *AMAZON_S_LED_CPU0 = reg_cpu0; if ( !clean_reg_con1 ) *AMAZON_S_LED_CON1 = reg_con1; if ( !clean_reg_con0 ) *AMAZON_S_LED_CON0 = reg_con0; if ( !f_led_on ) disable_led(); #if 0 printk("*AMAZON_S_LED_CON0 = 0x%08X\n", *AMAZON_S_LED_CON0); printk("*AMAZON_S_LED_CON1 = 0x%08X\n", *AMAZON_S_LED_CON1); printk("*AMAZON_S_LED_CPU0 = 0x%08X\n", *AMAZON_S_LED_CPU0); printk("*AMAZON_S_LED_CPU1 = 0x%08X\n", *AMAZON_S_LED_CPU1); printk("*AMAZON_S_LED_AR = 0x%08X\n", *AMAZON_S_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; } /** * LED IOCTL. * The following IOCTls are supported for the SSC device: * - #AMAZON_S_LED_IOC_SET_CONFIG * * \param inode Inode of device * \param filp File structure of device * \param cmd IOCTL command * \param arg Argument for some IOCTL commands * \return 0 OK * \return -EFAULT Copying data from user space to kernel space failed * \return -ENOIOCTLCMD invalid IOCTL command */ static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int ret = -EINVAL; struct led_config_param param = {0}; switch ( cmd ) { case AMAZON_S_LED_IOC_SET_CONFIG: if (copy_from_user(¶m, (char*)arg, sizeof(param))) return -EFAULT; ret = bsp_led_config(¶m); break; default: ret = -ENOIOCTLCMD; break; } return ret; } /** * LED Open. * * \param inode Inode of device or 0/1 for calls from kernel mode * \param filp File pointer of device * \return 0 OK */ static int led_open(struct inode *inode, struct file *file) { return 0; } /** * LED Release. * * \param inode Inode of device or 0/1 for calls from kernel mode * \param filp File pointer of device * \return 0 OK */ static int led_release(struct inode *inode, struct file *file) { return 0; } static void led_default_config(void) { struct led_config_param param = {0}; #if BOARD_TYPE == REFERENCE_BOARD /* Map for LED on reference board * Function LED SOUT SR bit number * -------------------------------------------- * WLAN_READ D7 OUT1 15 * WARNING D8 OUT2 14 * FXS1_LINK D9 OUT3 13 * FXS2_LINK D10 OUT4 12 * FXO_ACT D11 OUT5 11 * USB1_LINK D12 OUT6 10 * SD_LINK D13 OUT7 9 * RESEVE1 D14 OUT8 8 * USB2_LINK D15 OUT9 7 * RESERVE2 D16 OUT10 6 * RESERVE3/PW_EN D17 OUT11 5 * RESERVE4 D18 OUT12 4 * RESERVE5 D19 OUT13 3 * RESERVE6 D20 OUT14 2 * ADSL_LINK D21 OUT15 1 * ADSL_DATA D22 OUT16 0 */ param.operation_mask = CONFIG_OPERATION_NUMBER_OF_LED; param.number_of_enabled_led = 16; bsp_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 */ bsp_led_config(¶m); /* * By default, all functional LEDs should be off. * WAR, to shut down ADSL 2 LEDs, must change source to * CPU0, and turn it on:), which will make it turn off actually * Check with hw engineer */ param.operation_mask = CONFIG_OPERATION_UPDATE_SOURCE; param.led = 3; /* LED 0, 1 */ param.source = 0; /* CPU0 */ bsp_led_config(¶m); /* ADSL LED 0, ADSL_DATA */ param.operation_mask = CONFIG_OPERATION_DATA; param.data_mask = 1 << 0; param.data = 1 << 0; bsp_led_config(¶m); /* ADSL LED 1, ADSL_LINK */ param.operation_mask = CONFIG_OPERATION_DATA; param.data_mask = 1 << 1; param.data = 1 << 1; bsp_led_config(¶m); /* D8 Warning OUT2 */ param.operation_mask = CONFIG_OPERATION_DATA; param.data_mask = 1 << 14; param.data = 1 << 14; bsp_led_config(¶m); } /** * Display LED driver version after initilazation succeeds */ static inline void bsp_led_version(void) { printk(KERN_INFO "Infineon Technologies LED driver version %s \n", IFX_LED_DRV_VERSION); } static struct file_operations led_fops = { .owner = THIS_MODULE, .ioctl = led_ioctl, .open = led_open, .release = led_release }; /* LED Device Minor Number */ #if !defined(LED_MINOR) #define LED_MINOR 151 #endif static struct miscdevice led_miscdev = { .minor = LED_MINOR, .name = "led", .fops = &led_fops, }; /** * Register LED device * * \return 0 OK * \return < 0 Error Code */ static int __init bsp_led_init(void) { int ret; enable_led(); /* Set default value to registers to turn off all LED light. */ *AMAZON_S_LED_AR = LED_AR_DEFAULT_VALUE; *AMAZON_S_LED_CPU0 = LED_LED_CPU0_DEFAULT_VALUE; *AMAZON_S_LED_CPU1 = LED_LED_CPU1_DEFAULT_VALUE; *AMAZON_S_LED_CON1 = LED_CON1_DEFAULT_VALUE; *AMAZON_S_LED_CON0 = LED_CON0_DEFAULT_VALUE; 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; } up(&led_sem); bsp_led_version(); led_default_config(); return 0; } /** * Unregister device */ static void __exit bsp_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(bsp_led_set_blink); EXPORT_SYMBOL(bsp_led_set_data); EXPORT_SYMBOL(bsp_led_config); module_init(bsp_led_init); module_exit(bsp_led_exit);