u-boot/drivers/mtd/mw_eeprom.c
Simon Glass c05ed00afb common: Drop linux/delay.h from common header
Move this uncommon header out of the common header.

Signed-off-by: Simon Glass <sjg@chromium.org>
2020-05-18 21:19:23 -04:00

239 lines
3.9 KiB
C

/* Three-wire (MicroWire) serial eeprom driver (for 93C46 and compatibles) */
#include <common.h>
#include <eeprom.h>
#include <asm/ic/ssi.h>
#include <linux/delay.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;
}