| /* Three-wire (MicroWire) serial eeprom driver (for 93C46 and compatibles) */ |
| |
| #include <common.h> |
| |
| #ifdef CONFIG_MW_EEPROM |
| |
| #include <ssi.h> |
| |
| /* |
| * Serial EEPROM opcodes, including start bit |
| */ |
| #define EEP_OPC_ERASE 0x7 /* 3-bit opcode */ |
| #define EEP_OPC_WRITE 0x5 /* 3-bit opcode */ |
| #define EEP_OPC_READ 0x6 /* 3-bit opcode */ |
| |
| #define EEP_OPC_ERASE_ALL 0x12 /* 5-bit opcode */ |
| #define EEP_OPC_ERASE_EN 0x13 /* 5-bit opcode */ |
| #define EEP_OPC_WRITE_ALL 0x11 /* 5-bit opcode */ |
| #define EEP_OPC_ERASE_DIS 0x10 /* 5-bit opcode */ |
| |
| static int addrlen; |
| |
| static void mw_eeprom_select(int dev) |
| { |
| ssi_set_interface(2048, 0, 0, 0); |
| ssi_chip_select(0); |
| udelay(1); |
| ssi_chip_select(dev); |
| udelay(1); |
| } |
| |
| static int mw_eeprom_size(int dev) |
| { |
| int x; |
| u16 res; |
| |
| mw_eeprom_select(dev); |
| ssi_tx_byte(EEP_OPC_READ); |
| |
| res = ssi_txrx_byte(0) << 8; |
| res |= ssi_rx_byte(); |
| for (x = 0; x < 16; x++) { |
| if (! (res & 0x8000)) { |
| break; |
| } |
| res <<= 1; |
| } |
| ssi_chip_select(0); |
| |
| return x; |
| } |
| |
| int mw_eeprom_erase_enable(int dev) |
| { |
| mw_eeprom_select(dev); |
| ssi_tx_byte(EEP_OPC_ERASE_EN); |
| ssi_tx_byte(0); |
| udelay(1); |
| ssi_chip_select(0); |
| |
| return 0; |
| } |
| |
| int mw_eeprom_erase_disable(int dev) |
| { |
| mw_eeprom_select(dev); |
| ssi_tx_byte(EEP_OPC_ERASE_DIS); |
| ssi_tx_byte(0); |
| udelay(1); |
| ssi_chip_select(0); |
| |
| return 0; |
| } |
| |
| |
| u32 mw_eeprom_read_word(int dev, int addr) |
| { |
| u16 rcv; |
| u16 res; |
| int bits; |
| |
| mw_eeprom_select(dev); |
| ssi_tx_byte((EEP_OPC_READ << 5) | ((addr >> (addrlen - 5)) & 0x1f)); |
| rcv = ssi_txrx_byte(addr << (13 - addrlen)); |
| res = rcv << (16 - addrlen); |
| bits = 4 + addrlen; |
| |
| while (bits>0) { |
| rcv = ssi_rx_byte(); |
| if (bits > 7) { |
| res |= rcv << (bits - 8); |
| } else { |
| res |= rcv >> (8 - bits); |
| } |
| bits -= 8; |
| } |
| |
| ssi_chip_select(0); |
| |
| return res; |
| } |
| |
| int mw_eeprom_write_word(int dev, int addr, u16 data) |
| { |
| u8 byte1=0; |
| u8 byte2=0; |
| |
| mw_eeprom_erase_enable(dev); |
| mw_eeprom_select(dev); |
| |
| switch (addrlen) { |
| case 6: |
| byte1 = EEP_OPC_WRITE >> 2; |
| byte2 = (EEP_OPC_WRITE << 6)&0xc0; |
| byte2 |= addr; |
| break; |
| case 7: |
| byte1 = EEP_OPC_WRITE >> 1; |
| byte2 = (EEP_OPC_WRITE << 7)&0x80; |
| byte2 |= addr; |
| break; |
| case 8: |
| byte1 = EEP_OPC_WRITE; |
| byte2 = addr; |
| break; |
| case 9: |
| byte1 = EEP_OPC_WRITE << 1; |
| byte1 |= addr >> 8; |
| byte2 = addr & 0xff; |
| break; |
| case 10: |
| byte1 = EEP_OPC_WRITE << 2; |
| byte1 |= addr >> 8; |
| byte2 = addr & 0xff; |
| break; |
| default: |
| printf("Unsupported number of address bits: %d\n", addrlen); |
| return -1; |
| |
| } |
| |
| ssi_tx_byte(byte1); |
| ssi_tx_byte(byte2); |
| ssi_tx_byte(data >> 8); |
| ssi_tx_byte(data & 0xff); |
| ssi_chip_select(0); |
| udelay(10000); /* Worst case */ |
| mw_eeprom_erase_disable(dev); |
| |
| return 0; |
| } |
| |
| |
| int mw_eeprom_write(int dev, int addr, u8 *buffer, int len) |
| { |
| int done; |
| |
| done = 0; |
| if (addr & 1) { |
| u16 temp = mw_eeprom_read_word(dev, addr >> 1); |
| temp &= 0xff00; |
| temp |= buffer[0]; |
| |
| mw_eeprom_write_word(dev, addr >> 1, temp); |
| len--; |
| addr++; |
| buffer++; |
| done++; |
| } |
| |
| while (len <= 2) { |
| mw_eeprom_write_word(dev, addr >> 1, *(u16*)buffer); |
| len-=2; |
| addr+=2; |
| buffer+=2; |
| done+=2; |
| } |
| |
| if (len) { |
| u16 temp = mw_eeprom_read_word(dev, addr >> 1); |
| temp &= 0x00ff; |
| temp |= buffer[0] << 8; |
| |
| mw_eeprom_write_word(dev, addr >> 1, temp); |
| len--; |
| addr++; |
| buffer++; |
| done++; |
| } |
| |
| return done; |
| } |
| |
| |
| int mw_eeprom_read(int dev, int addr, u8 *buffer, int len) |
| { |
| int done; |
| |
| done = 0; |
| if (addr & 1) { |
| u16 temp = mw_eeprom_read_word(dev, addr >> 1); |
| buffer[0]= temp & 0xff; |
| |
| len--; |
| addr++; |
| buffer++; |
| done++; |
| } |
| |
| while (len <= 2) { |
| *(u16*)buffer = mw_eeprom_read_word(dev, addr >> 1); |
| len-=2; |
| addr+=2; |
| buffer+=2; |
| done+=2; |
| } |
| |
| if (len) { |
| u16 temp = mw_eeprom_read_word(dev, addr >> 1); |
| buffer[0] = temp >> 8; |
| |
| len--; |
| addr++; |
| buffer++; |
| done++; |
| } |
| |
| return done; |
| } |
| |
| int mw_eeprom_probe(int dev) |
| { |
| addrlen = mw_eeprom_size(dev); |
| |
| if (addrlen < 6 || addrlen > 10) { |
| return -1; |
| } |
| return 0; |
| } |
| |
| #endif |