/* * LED interface for WP3200 * * Copyright (C) 2002, by Allen Hung * * 2005.06.22 Joco: fixed some type conversion warnings */ #include #include #include #include #include #include #include #include #include #include #include #include #include "led.h" #include "asm/am5120/adm5120.h" #define BUF_LEN 30 //------------------------------------------------------------ static long atoh(char *p) { long v = 0, c; while ( (c = *p++) ) { if ( c >= '0' && c <= '9' ) v = (v << 4) + c - '0'; else if ( c >= 'a' && c <= 'f' ) v = (v << 4) + c - 'a' + 0xA; else if ( c >= 'A' && c <= 'F' ) v = (v << 4) + c - 'A' + 0xA; else break; } return v; } #define REG_BASE (0xb2000000) #define GPIO_VAL (*(unsigned long *)0xb20000b8) #define GPIO_SEL (*(unsigned long *)0xb20000bc) #define GPIO_SEL_I_O (*(unsigned long *)0xb20000b8) #define GPIO_O_EN (*(unsigned long *)0xb20000b8) #define INIT_WATCHDOG_REGISTER 0x20 #define PORT0_LED (*(unsigned long *)0xb2000100) #define PORT1_LED (*(unsigned long *)0xb2000104) #define PORT2_LED (*(unsigned long *)0xb2000108) #define PORT3_LED (*(unsigned long *)0xb200010c) #define PORT4_LED (*(unsigned long *)0xb2000110) // sam 1-30-2004 LED status // bit map as following // BIT 4:0 Link status -->PHY Link ->1 = up, 0 = down #define LINK_STATUS (*(unsigned long *)0xb2000014) #define WATCHDOG_VAL (*(unsigned long *)0xb20000c0) #define WATCHDOG_PERIOD 2000 // unit ms #define EXPIRE_TIME 300 // unit 10 ms #define CLEAR_TIMEER 0xffffa000l // bit 14:0 -> count up timer, write 0 to clear #define ENABLE_WATCHDOG 0x80000000l // bit 31 -> 1 enable , 0 disable watchdog #define WATCHDOG_SET_TMR_SHIFT 16 // bit 16:30 -> watchdog timer set // end sam //------------------------------------------------------------ typedef struct { u32 reg_offset; u32 bit_no; int f_inverted; } led_config_struct; static led_config_struct led_config[] = { {GPIO_conf0_REG, 24, 0}, {GPIO_conf0_REG, 25, 1}, {Port0_LED_REG, 0, 0}, {Port1_LED_REG, 0, 0}, {Port2_LED_REG, 0, 0}, {Port3_LED_REG, 0, 0}, {Port4_LED_REG, 0, 0}, {GPIO_conf0_REG, 27, 1}, {Port0_LED_REG, 4, 0}, {Port1_LED_REG, 4, 0}, {Port2_LED_REG, 4, 0}, {Port3_LED_REG, 4, 0}, {Port4_LED_REG, 4, 0}, {Port0_LED_REG, 8, 0}, {Port1_LED_REG, 8, 0}, {Port2_LED_REG, 8, 0}, {Port3_LED_REG, 8, 0}, {Port4_LED_REG, 8, 0}, }; struct LED_DATA { char sts_buf[BUF_LEN+1]; unsigned long sts; }; struct LED_DATA led_data[ARRAY_SIZE(led_config)]; // sam 01-30-2004 for watchdog static struct timer_list watchdog; // end sam static struct timer_list blink_timer[ARRAY_SIZE(led_config)]; static char cmd_buf[BUF_LEN+1]; static void turn_led(int id, int on) { unsigned long led_bit,led_bit_val; if(id <= 7 && id >= 0) { led_bit = 1 << (id); led_bit_val = led_bit << 24; switch ( on ) { case 0: GPIO_VAL |= led_bit_val; break; // LED OFF case 1: GPIO_VAL &= ~led_bit_val; break; // LED ON case 2: GPIO_VAL ^= led_bit_val; break; // LED inverse } } } static void blink_wrapper(u_long id) { u_long sts = led_data[id].sts; if ( (sts & LED_BLINK_CMD) == LED_BLINK_CMD ) { int period = sts & LED_BLINK_PERIOD; blink_timer[id].expires = jiffies + (period * HZ / 1000); turn_led(id, 2); add_timer(&blink_timer[id]); } else if ( sts == LED_ON || sts == LED_OFF ) turn_led(id, sts==LED_ON ? 1 : 0); } //------------------------------------------------------------ static void get_token_str(char *str, char token[][21], int token_num) { int t, i; for ( t = 0 ; t < token_num ; t++ ) { memset(token[t], 0, 21); while ( *str == ' ' ) str++; for ( i = 0 ; str[i] ; i++ ) { if ( str[i] == '\t' || str[i] == ' ' || str[i] == '\n' ) break; if ( i < 20 ) token[t][i] = str[i]; } str += i; } } //------------------------------------------------------------ static void set_led_status_by_str(int id) { char token[3][21], *p; get_token_str(led_data[id].sts_buf, token, 3); if ( strcmp(token[0], "LED") ) goto set_led_off; if ( !strcmp(token[1], "ON") ) { turn_led(id, 1); led_data[id].sts = LED_ON; } else if ( !strcmp(token[1], "OFF") ) { goto set_led_off; } else if ( !strcmp(token[1], "BLINK") ) { int period = 0; p = token[2]; if ( !strcmp(p, "FAST") ) period = LED_BLINK_FAST & LED_BLINK_PERIOD; else if ( !strcmp(p, "SLOW") ) period = LED_BLINK_SLOW & LED_BLINK_PERIOD; else if ( !strcmp(p, "EXTRA_SLOW") ) period = LED_BLINK_EXTRA_SLOW & LED_BLINK_PERIOD; else if ( !strcmp(p, "OFF") ) goto set_led_off; else if ( *p >= '0' && *p <= '9' ) { while ( *p >= '0' && *p <= '9' ) period = period * 10 + (*p++) - '0'; if ( period > 10000 ) period = 10000; } else period = LED_BLINK & LED_BLINK_PERIOD; if ( period == 0 ) goto set_led_off; sprintf(led_data[id].sts_buf, "LED BLINK %d\n", period); led_data[id].sts = LED_BLINK_CMD + period; turn_led(id, 2); // Set timer for next blink del_timer(&blink_timer[id]); blink_timer[id].function = blink_wrapper; blink_timer[id].data = id; init_timer(&blink_timer[id]); blink_timer[id].expires = jiffies + (period * HZ / 1000); add_timer(&blink_timer[id]); } else goto set_led_off; return; set_led_off: strcpy(led_data[id].sts_buf, "LED OFF\n"); led_data[id].sts = LED_OFF; turn_led(id, 0); } void get_led ( int id, int* pf_on) { u32* p_reg = (u32*)(led_config[id].reg_offset + REG_BASE); *pf_on = *p_reg & (1 << (led_config[id].bit_no)); if (( *pf_on && !led_config[id].f_inverted) || (!(*pf_on) && led_config[id].f_inverted )) { *pf_on = 1; } else { *pf_on = 0; } } void set_led ( int id, int f_on) { u32* p_reg = (u32*)(led_config[id].reg_offset + REG_BASE); if (( f_on && !led_config[id].f_inverted) || (!f_on && led_config[id].f_inverted )) { *p_reg |= (1 << (led_config[id].bit_no)); } else { *p_reg &= ~(1 << (led_config[id].bit_no)); } } //---------------------------------------------------------------------- static int led_read_proc(char *buf, char **start, off_t fpos, int length, int *eof, void *data) { int len, dev; for ( len = dev = 0 ; dev < ARRAY_SIZE(led_config) ; dev++ ) { len += sprintf(buf+len, "%d: %s", dev, led_data[dev].sts_buf); } len = strlen(buf) - fpos; if ( len <= 0 ) { *start = buf; *eof = 1; return 0; } *start = buf + fpos; if ( len <= length ) *eof = 1; return len < length ? len : length; } //---------------------------------------------------------------------- static int led_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { int id = (int)file->private_data; int* p_leds = (int*)arg; int f_on; int i; if (arg < ARRAY_SIZE(led_config)) { id = arg; } switch ( cmd ) { case 0: // set OFF set_led(id, 0); break; case 1: // set ON set_led(id, 1); break; case 2: // get leds state *p_leds = 0; for (i = 0; i < 13; i++) { get_led(i, &f_on); *p_leds |= (f_on << i); } break; default: if ( (cmd & LED_BLINK_CMD) != LED_BLINK_CMD ) { break; } case LED_BLINK: case LED_BLINK_FAST: case LED_BLINK_SLOW: case LED_BLINK_EXTRA_SLOW: sprintf(led_data[id].sts_buf, "LED BLINK %d\n", (int)(cmd & LED_BLINK_PERIOD)); set_led_status_by_str(id); break; } return 0; } static int led_open(struct inode *inode, struct file *file) { int led_id = MINOR(inode->i_rdev); unsigned long led_bit = 1 << (led_id); if ( led_id >= ARRAY_SIZE(led_config) ) { printk ("led open %d error ENODEV\n", led_id); return -ENODEV; } GPIO_SEL_I_O &= ~led_bit; // 0 to GPIO GPIO_O_EN |= (led_bit << 16); // 0 to Output file->private_data = (void*)led_id; return 0; } static ssize_t led_read(struct file *file, char *buf, size_t count, loff_t *fpos) { int rem, len; int id = (int)file->private_data; char *p = led_data[id].sts_buf; len = strlen(p); rem = len - *fpos; if ( rem <= 0 ) { *fpos = len; return 0; } if ( rem > count ) rem = count; memcpy(buf, p+(*fpos), rem); *fpos += rem; return rem; } static ssize_t led_write(struct file *file, const char *buf, size_t count, loff_t *fpos) { int len; int id = (int)file->private_data; char *p = id == REG_MINOR ? cmd_buf : led_data[id].sts_buf; memset(p, 0, BUF_LEN); p += *fpos; len = 0; while ( count > 0 ) { if ( *fpos < BUF_LEN ) { int c = *buf++; p[len] = c>='a' && c<='z' ? c-'a'+'A' : c; } (*fpos)++; len++; count--; } return len; } static int led_flush(struct file *file) { int id = (int)file->private_data; if ( file->f_mode & FMODE_WRITE ) set_led_status_by_str(id); return 0; } static struct file_operations led_fops = { read: led_read, write: led_write, flush: led_flush, ioctl: led_ioctl, open: led_open, }; //---------------------------------------------- static unsigned long *reg_addr; static int dump_len; static int dump_content(char *buf) { int i=0, j, len=0; unsigned long *p = reg_addr; j = dump_len/4 + ((dump_len&3) ? 1 : 0); len = sprintf(buf, "Reg Addr = %08lX, Value = ", (unsigned long)p); for ( i = 0 ; i < j ; i++, p++ ) len += sprintf(buf+len,"%08lX%c", *p, (i&7)==7||i==j-1?'\n':' '); return len; } static ssize_t gpio_read(struct file *file, char *buf, size_t count, loff_t *fpos) { int rem, len; int id = (int)file->private_data; char temp[80*10]; if ( id < GPIO_DEV_NUM ) { int gpio_bit = 1 << id; len = sprintf(temp, "%d\n", ((GPIO_VAL >> 8)&gpio_bit) ? 1 : 0); } else // REG device len = dump_content(temp); rem = len - *fpos; if ( rem <= 0 ) { *fpos = len; return 0; } if ( rem > count ) rem = count; memcpy(buf, temp+(*fpos), rem); *fpos += rem; return rem; } static int gpio_flush(struct file *file) { long v, addr; int id = (int)file->private_data; if ( id == REG_MINOR && (file->f_mode & FMODE_WRITE) ) { char token[3][21], *p; get_token_str(cmd_buf, token, 3); // get reg address p = token[0]; if ( *p == 0 ) return 0; addr = atoh(p); //--------------------- p = token[1]; if ( *p == 'W' ) { int width = 0; if ( !strcmp(p, "W") || !strcmp(p, "WW") ) width = 4; else if ( !strcmp(p, "WH") ) width = 2; else if ( !strcmp(p, "WB") ) width = 1; else return 0; p = token[2]; if ( *p == 0 ) return 0; v = atoh(p); switch ( width ) { case 1: *((char *)addr) = (v & 0xFF); break; case 2: *((short*)addr) = (v & 0xFFFF); break; case 4: *((long *)addr) = v; break; } } else { // get dump len char temp[80*10]; reg_addr = (unsigned long *)(addr & ~3); dump_len = 4; if ( *p ) { dump_len = atoh(p); dump_len = dump_len < 4 ? 4 : dump_len > 32*10 ? 32*10 : dump_len; } dump_content(temp); //printk( KERN_INFO "%s", temp); } cmd_buf[0] = 0; } return 0; } static int gpio_open(struct inode *inode, struct file *file) { int id = MINOR(inode->i_rdev); if ( id >= GPIO_DEV_NUM && id != REG_MINOR ) return -ENODEV; if ( id < GPIO_DEV_NUM ) { int gpio_bit = 1 << id; GPIO_SEL = 0; GPIO_SEL |= gpio_bit; // bit=0 for GPIO } file->private_data = (void*)id; return 0; } static ssize_t gpio_write(struct file *file, const char *buf, size_t count, loff_t *fpos) { int len; int id = (int)file->private_data; char *p = id == REG_MINOR ? cmd_buf : led_data[id].sts_buf; memset(p, 0, BUF_LEN); p += *fpos; len = 0; while ( count > 0 ) { if ( *fpos < BUF_LEN ) { int c = *buf++; p[len] = c>='a' && c<='z' ? c-'a'+'A' : c; } (*fpos)++; len++; count--; } return len; } static struct file_operations gpio_fops = { read: gpio_read, open: gpio_open, flush: gpio_flush, write: gpio_write, }; //---------------------------------------------- static void watchdog_wrapper(unsigned long period) { // clear timer count WATCHDOG_VAL &= CLEAR_TIMEER; watchdog.expires = jiffies + (period * HZ / 1000); add_timer(&watchdog); } //---------------------------------------------- static int init_status; #define INIT_REGION 0x01 #define INIT_LED_REGISTER 0x02 #define INIT_LED_PROC_READ 0x04 #define INIT_GPIO_REGISTER 0x08 static void led_exit(void) { int id; for ( id = 0 ; id < ARRAY_SIZE(led_config) ; id++ ) { del_timer(&blink_timer[id]); turn_led(id, 0); } if ( init_status & INIT_LED_PROC_READ ) remove_proc_entry("driver/led", NULL); if ( init_status & INIT_LED_REGISTER ) unregister_chrdev(LED_MAJOR, "led"); if ( init_status & INIT_GPIO_REGISTER ) unregister_chrdev(GPIO_MAJOR, "gpio"); if ( init_status & INIT_REGION ) release_region(GPIO_IO_BASE, GPIO_IO_EXTENT); } #define GPIO_MODE_INPUT 0 #define GPIO_MODE_OUTPUT 1 /* default configuration for Edimax BR-6104K */ static int adm5120gpio_init_data[] __initdata = { GPIO_MODE_OUTPUT, /* 0 - Power LED */ GPIO_MODE_OUTPUT, /* 1 */ GPIO_MODE_INPUT, /* 2 - Reset switch */ GPIO_MODE_OUTPUT, /* 3 */ GPIO_MODE_OUTPUT, /* 4 */ GPIO_MODE_OUTPUT, /* 5 */ GPIO_MODE_OUTPUT, /* 6 */ GPIO_MODE_OUTPUT, /* 7 */ }; static int __init led_init(void) { int result, id; int init_status = 0; unsigned long mode_bit = (1 << (id+16)); //---- request region -------------------------- /* if ( check_region(GPIO_IO_BASE, GPIO_IO_EXTENT) ) { printk(KERN_ERR "gpio: I/O port %lX is not free.\n", GPIO_IO_BASE); return -EIO; } request_region(GPIO_IO_BASE, GPIO_IO_EXTENT, "gpio"); init_status |= INIT_REGION; */ //----- register device (LED)------------------------- result = register_chrdev(LED_MAJOR, "led", &led_fops); if ( result < 0 ) { printk(KERN_ERR "led: can't register char device\n" ); led_exit(); return result; } init_status |= INIT_LED_REGISTER; //----- register device (GPIO)------------------------- result = register_chrdev(GPIO_MAJOR, "gpio", &gpio_fops); if ( result < 0 ) { printk(KERN_ERR "gpio: can't register char device\n" ); led_exit(); return result; } init_status |= INIT_GPIO_REGISTER; // sam 1-30-2004 LAN Status // ----- register device (LAN_STATUS)------------------- /* result = register_chrdev(LAN_STATUS_MAJOR, "lanSt", &lanSt_fops); if ( result < 0 ) { printk(KERN_ERR "lanSt: can't register char device\n" ); led_exit(); return result; } init_status |= INIT_LAN_STATUS_REGISTER; */ // -----------init watchdog timer------------------------- //del_timer(&blink_timer[id]); WATCHDOG_VAL = ENABLE_WATCHDOG | ( EXPIRE_TIME << WATCHDOG_SET_TMR_SHIFT); watchdog.function = watchdog_wrapper; watchdog.data = WATCHDOG_PERIOD; init_timer(&watchdog); watchdog.expires = jiffies + (WATCHDOG_PERIOD * HZ / 1000); add_timer(&watchdog); init_status |= INIT_WATCHDOG_REGISTER; // end sam //------ read proc ------------------- if ( !create_proc_read_entry("driver/led", 0, 0, led_read_proc, NULL) ) { printk(KERN_ERR "led: can't create /proc/driver/led\n"); led_exit(); return -ENOMEM; } init_status |= INIT_LED_PROC_READ; //------------------------------ // reg_addr = (unsigned long *)0xB4000000; reg_addr = (unsigned long *)0xB2000000; dump_len = 4; printk(KERN_INFO "LED & GPIO Driver " LED_VERSION ", regval %08x, %08x\n", GPIO_VAL, GPIO_SEL); for ( id = 0 ; id < ARRAY_SIZE(led_config) ; id++ ) { GPIO_VAL &= ~mode_bit; if (adm5120gpio_init_data[id] == GPIO_MODE_OUTPUT) GPIO_VAL |= mode_bit; strcpy(led_data[id].sts_buf, "LED ON\n" ); set_led_status_by_str(id); mode_bit = mode_bit << 1; } printk(KERN_INFO "LED & GPIO Driver " LED_VERSION ", regval %08x, %08x\n", GPIO_VAL, GPIO_SEL); #if 1 //initialize port led registers PORT0_LED = 0x222; PORT1_LED = 0x222; PORT2_LED = 0x222; PORT3_LED = 0x222; PORT4_LED = 0x222; for ( id = 0 ; id < ARRAY_SIZE(led_config) ; id++ ) { set_led(id, 1); } #endif return 0; } module_init(led_init); module_exit(led_exit); EXPORT_NO_SYMBOLS;