Wrt54g
From ByteWiki
Contents |
WRT54G and OpenWRT
Jnewbigin 16:15, 22 December 2005 (EST)
GPIO on the WRT54G version 3.1
Like all WRT54G, the version 3.1 has 8 GPIO pins.
| PIN | Usage | Comments |
|---|---|---|
| 0 | Unknown | Not WLAN like other versions |
| 1 | Power LED | 0 = Flashing 1 = On |
| 2 | White LED | 0 = On 1 = Off |
| 3 | Orange LED | 0 = On 1 = Off (Or better, set to input) |
| 4 | Cisco Button | Pressed/Grounded = 0, Break = 1 |
| 5 | Unknown | ADM_EEDI/robo_reset |
| 6 | Reset Button | Pressed/Grounded = 0, Break = 1 |
| 7 | DMZ LED | 0 = On 1 = Off |
The GPIO pins run of 3.3 volts so it is handy to have a 3.3 volt power source. There are a few places you can get power from the WRT54G. At first I used the serial port pins 1 & 10, but then I neede to attach a serial port so I switched to the JTAG plug. JTAG howerver does NOT supply power, so that did not work and I switched back to the serial port. I recommend that you solder in the header pins for the serial port on top, and then take power from that. If however you want to run serial as well, you can solder onto the underside of the header pins. You can solder directly onto the board, but it becomes a pain if you want to remove them and attach a serial port.
3.3v = Serial 1 & 2 (JP1 PIN 1 & 2) GND = Serial 9 & 10 (JP1 PIN 9 & 10)
You can also take power from the power regulator circut. See the section on Power below.
Before you Start
While pokeing round with the multimeter, I managed to blow up the BCM5325 ethernet chip on my router. This makes remote connections a bit of a problem so I suggest that the first thing you do is connnect a serial port. That has lots of advantages such as watching boot time messages, editing the CFE to set boot_wait etc. Perhaps even logging into the Linksys firmware? I built my own ttl -> RS232 converter based on the MAX232. This is a 5v chip but it works on the 3.3v available on the WRT54G. There is a MAX3232 which is designed for 3.3v if you can get it. And remember to connect the ground... I spent ages trying to figure out why I could transmit but not receive till I discovered that I forgot to connect the ground to the IC :-(
GPIO
By default there are 2 GPIO inputs. This can be changed via software but these inputs have buttons on them which make it easy to test. The GPIO pins are an open drain arrangement with pull up resistors alredy supplied, so short to ground = 0 and break = 1. To use GPIO 6 the control flag also needs to be cleared. What is the control flag for? I don't know.
GPIO Solder Points
Cisco Button (View from under side of motherboard) * *=GPIO 4 * * Reset Button (View from under side of motherboard) * *=GPIO 6 * *
Adding i2c bus to the WRT54G
NOTE: This is still work in progress. Some people have has success, some have had problems with timing and line levels. I'll post more information as it becomes available. If you don't have access to a digital oscilloscope then you might have trouble tracking down the problem.
It turned out be to quite simple to build a bit bashing interface for the GPIO outputs. I took GPIO code from mmc.c which is the WRT54G SD card hack driver. This directly manipulates the GPIO registers via memory mapped IO. This may well conflict with other drivers using GPIO and might change in the future.
I then based my driver on the i2c_philips_par.c but there is an even closer sample I could have used. scx200_i2c.c looks like it is the same idea but for a different CPU. Anyway, I had my module already working, but not tested. I was planning to use the Orange and White LEDS because they don't get used by default in openwrt, but they are tricky to solder onto, and have +5v on the other side, don't ask me how that works. So in the end I went with the 2 buttons because they are easy to solder onto from the bottom of the motherboard.
i2c-mips-gpio.c (version 2)
/* ------------------------------------------------------------------------- */ /* i2c-mips-gpio.c i2c-hw access for WRT54G */ /* ------------------------------------------------------------------------- */ /* Based on i2c-philips-par.c by Simon G. Vogl It seems that this is very simalar to scx200_i2c.c Perhaps there should be a generic GPIO interface in the kernel 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* ------------------------------------------------------------------------- */ /* $Id: $ */ #include <linux/kernel.h> #include <linux/ioport.h> #include <linux/module.h> #include <linux/init.h> #include <linux/stddef.h> #include <linux/i2c.h> #include <linux/i2c-algo-bit.h> #ifndef __exit #define __exit __init #endif typedef unsigned int uint32; /* GPIO Defines taken from mmc.c */ static volatile uint32 *gpioaddr_input = (uint32 *)0xb8000060; static volatile uint32 *gpioaddr_output = (uint32 *)0xb8000064; static volatile uint32 *gpioaddr_enable = (uint32 *)0xb8000068; static volatile uint32 *gpioaddr_control = (uint32 *)0xb800006c; // should this use the sb_gpio* functions from sbutils.h (like gpio.c)? #define GPIO_WHITE 2 #define GPIO_ORANGE 3 #define GPIO_CISCO 4 #define GPIO_RESET 6 #define GPIO_CLOCK (1 << GPIO_CISCO) #define GPIO_DATA (1 << GPIO_RESET) /* ----- local functions ---------------------------------------------- */ // we probably also need to invert all the signals... // we need to change gpioaddr_enable to change from Input to Output static void bit_gpio_set(unsigned int mask) { unsigned int port_state; port_state = *gpioaddr_enable; port_state |= mask; *gpioaddr_enable = port_state; port_state = *gpioaddr_input; port_state |= mask; *gpioaddr_output = port_state; } static void bit_gpio_clear(unsigned int mask) { unsigned int port_state; port_state = *gpioaddr_enable; port_state |= mask; *gpioaddr_enable = port_state; port_state = *gpioaddr_input; port_state &= ~mask; *gpioaddr_output = port_state; } static int bit_gpio_get(int mask) { unsigned char port_state; port_state = *gpioaddr_enable; /* Read current config */ port_state &= ~mask; /* Set to input */ *gpioaddr_enable = port_state; port_state = *gpioaddr_input; return port_state & mask; } static void bit_gpio_setscl(void *data, int state) { if (state) { bit_gpio_set(GPIO_CLOCK); } else { bit_gpio_clear(GPIO_CLOCK); } } static void bit_gpio_setsda(void *data, int state) { if (state) { bit_gpio_set(GPIO_DATA); } else { bit_gpio_clear(GPIO_DATA); } } static int bit_gpio_getscl(void *data) { return bit_gpio_get(GPIO_CLOCK); } static int bit_gpio_getsda(void *data) { return bit_gpio_get(GPIO_DATA); } /* */ static int bit_gpio_reg(struct i2c_client *client) { return 0; } static int bit_gpio_unreg(struct i2c_client *client) { return 0; } static void bit_gpio_inc_use(struct i2c_adapter *adap) { MOD_INC_USE_COUNT; } static void bit_gpio_dec_use(struct i2c_adapter *adap) { MOD_DEC_USE_COUNT; } /* ------------------------------------------------------------------------ * Encapsulate the above functions in the correct operations structure. * This is only done when more than one hardware adapter is supported. */ static struct i2c_algo_bit_data bit_gpio_data = { NULL, bit_gpio_setsda, bit_gpio_setscl, bit_gpio_getsda, bit_gpio_getscl, 80, 80, 100, /* waits, timeout */ }; static struct i2c_adapter bit_gpio_ops = { "WRT54G GPIO", I2C_HW_B_LP, // what to use here ??? NULL, &bit_gpio_data, bit_gpio_inc_use, bit_gpio_dec_use, bit_gpio_reg, bit_gpio_unreg, }; int __init i2c_bitgpio_init(void) { unsigned char gpio_outen; printk(KERN_INFO "i2c-mpis-gpio.o: i2c WRT54G GPIO module version %s (%s)\n", I2C_VERSION, I2C_DATE); /* do some init */ bit_gpio_set(GPIO_CLOCK); bit_gpio_clear(GPIO_DATA); /* I don't know what it does, but if we use the reset button we need to set control to 0 so I assume that is a good thing to do */ gpio_outen = *gpioaddr_control; gpio_outen = gpio_outen & ~(GPIO_CLOCK | GPIO_DATA); *gpioaddr_control = gpio_outen; if(i2c_bit_add_bus(&bit_gpio_ops) < 0) return -ENODEV; return 0; }void __exit i2c_bitgpio_exit(void) { /* do any clean up. we set them back to input */ bit_gpio_get(GPIO_CLOCK); bit_gpio_get(GPIO_DATA); /* we should restore the control mode */ i2c_bit_del_bus(&bit_gpio_ops); } EXPORT_NO_SYMBOLS; MODULE_AUTHOR("John Newbigin <jn@it.swin.edu.au>"); MODULE_DESCRIPTION("I2C-Bus adapter routines for WRT54G GPIO"); MODULE_LICENSE("GPL"); #ifdef MODULE int init_module(void) { return i2c_bitgpio_init(); } void cleanup_module(void) { i2c_bitgpio_exit(); } #endif
Testing i2c
I used i2cdetect and i2cdump from the lm_sensors packages to test the i2c bus. When i2cdetect was run, the LEDs flicker which seems like a good thing but I did't have any test equipment to see if it was correct. I also didn't have any i2c devices to test with so I found a SIMM which had a serial EEPROM on it. It is an AT24C01 and I found the specs and got the pin out. It runs off 3.3 volts which makes things easy. After some fancy solder work I connected the Vcc, GND, CLOCK and DATA and hooked then up to the WRT54G using the Cisco and Reset buttons (because they are easy to solder onto).
I was not expecting much but when I ran i2cdetect it found a device at 0x50, the correct address for an EEPROM! (Note that this is a slightly modified version of i2cdetect to make it simple to run)
root@OpenWrt:~# ./i2cdetect
Error: No i2c-bus specified, using bus 0
I will probe file /dev/i2c/0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
10: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
20: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
30: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
40: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
50: 50 XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
60: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
70: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
By shorting powering the address pins on the EEPROM I can change the address
root@OpenWrt:~# ./i2cdetect
Error: No i2c-bus specified, using bus 0
I will probe file /dev/i2c/0
0 1 2 3 4 5 6 7 8 9 a b c d e f
00: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
10: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
20: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
30: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
40: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
50: XX XX XX XX XX XX XX 57 XX XX XX XX XX XX XX XX
60: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
70: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX
Then I ran i2cdump and it worked too.
root@OpenWrt:~# ./i2cdump 0 0x50
No size specified (using byte-data access)
WARNING! This program can confuse your I2C bus, cause data loss and worse!
I will probe file /dev/i2c/0, address 0x50, mode byte
You have five seconds to reconsider and press CTRL-C!
0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef
00: 80 08 04 0c 09 01 40 00 01 f0 90 00 80 08 08 01 ??????@.???.????
10: 8f 04 07 01 01 00 0f f0 09 78 6c 2d 1e 1e 3c 10 ?????.???xl-??<?
20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
30: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
40: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 66 06 ..............f?
80: 80 08 04 0c 09 01 40 00 01 f0 90 00 80 08 08 01 ??????@.???.????
90: 8f 04 07 01 01 00 0f f0 09 78 6c 2d 1e 1e 3c 10 ?????.???xl-??<?
a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
f0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 66 06 ..............f?
You can see that the data is repeated but I assume that is just the size of the EEPROM. The data in the EEPROM should be Jedec Standard, but as you would expect, their web site is down. Using the source to decode-dimms.pl I manually checked some values and they all seem correct so I assume that this is indeed working correctly.
Now I have to get my hands on a PCF8574 to test the extra IO.
Hardware
There are a number of different WRT54G[S] models. You can see them all here http://www.linksysinfo.org/modules.php?name=Content&pa=showpage&pid=6
Opening the case
Start by removing the rubber feet from the front of the case. They can be hard to get started but I have found the easiest way is to insert a small screwdriver into the hole in the middle (there are actually 4 small holes) and pry it up.
Once the feet are off, remove the screws. You can re-install the feet, with the screw loose inside so you don't loose the bits.
At this stage, I like to attach the header pins for the serial port. To remove the board from the base, remove the screw near the ethernet ports and slide the board forward about 5mm. It will then lift cleanly off.
Power
The WRT54G supplies +12v, +5v, +3.3v, +2.5v & ground/0v. The +12v is the unregulated input from the transformer and will no doubt fluctuate. Unloaded mine measuers around 13.5 volts.
It is easy to solder power takeof wires on the underside of the board. The coils and capacitors from the power regulators are mounted through the board and are easy enough to solder onto. Check *carefully* with a multimeter to find what you need. According to the specs for the regulator chips, the + side of the capacitor is probably the easiest identifiable location. You can use the coil, but it does not have polarity so you have to check which end. One end is connected directly to the capacitor + so if you can find that end you can use it. +12v and ground can be taken from the underside of the power jack.
I also rigged up a power over ethernet which does not require a splitter. It is not really PoE, just power over CAT5. Follow http://www.nycwireless.net/poe/ for building the +12v power injector but rather than building a splitter on the WRT end, solder a wire from the ethernet plug pins 4&5 to the ground pin on the side or front leg of the power jack (check with multimeter first). Solder a wire from pins 7&8 to the back leg of the power jack (check with multimeter, you don't want to use the front). This is also handy because the WRT54G does not have a power switch and it is easier to plug & unplug ethernet than the power jack.