xref: /openbmc/u-boot/drivers/mtd/mw_eeprom.c (revision 624656314f5684995fb9f499d38ad18d378802a5)
159829cc1SJean-Christophe PLAGNIOL-VILLARD /* Three-wire (MicroWire) serial eeprom driver (for 93C46 and compatibles) */
259829cc1SJean-Christophe PLAGNIOL-VILLARD 
359829cc1SJean-Christophe PLAGNIOL-VILLARD #include <common.h>
4*62465631SSimon Glass #include <eeprom.h>
53ef96dedSGraeme Russ #include <asm/ic/ssi.h>
659829cc1SJean-Christophe PLAGNIOL-VILLARD 
759829cc1SJean-Christophe PLAGNIOL-VILLARD /*
859829cc1SJean-Christophe PLAGNIOL-VILLARD  * Serial EEPROM opcodes, including start bit
959829cc1SJean-Christophe PLAGNIOL-VILLARD  */
1059829cc1SJean-Christophe PLAGNIOL-VILLARD #define EEP_OPC_ERASE	0x7  /* 3-bit opcode */
1159829cc1SJean-Christophe PLAGNIOL-VILLARD #define EEP_OPC_WRITE	0x5  /* 3-bit opcode */
1259829cc1SJean-Christophe PLAGNIOL-VILLARD #define EEP_OPC_READ	        0x6  /* 3-bit opcode */
1359829cc1SJean-Christophe PLAGNIOL-VILLARD 
1459829cc1SJean-Christophe PLAGNIOL-VILLARD #define EEP_OPC_ERASE_ALL	0x12 /* 5-bit opcode */
1559829cc1SJean-Christophe PLAGNIOL-VILLARD #define EEP_OPC_ERASE_EN	0x13 /* 5-bit opcode */
1659829cc1SJean-Christophe PLAGNIOL-VILLARD #define EEP_OPC_WRITE_ALL	0x11 /* 5-bit opcode */
1759829cc1SJean-Christophe PLAGNIOL-VILLARD #define EEP_OPC_ERASE_DIS	0x10 /* 5-bit opcode */
1859829cc1SJean-Christophe PLAGNIOL-VILLARD 
1959829cc1SJean-Christophe PLAGNIOL-VILLARD static int addrlen;
2059829cc1SJean-Christophe PLAGNIOL-VILLARD 
mw_eeprom_select(int dev)2159829cc1SJean-Christophe PLAGNIOL-VILLARD static void mw_eeprom_select(int dev)
2259829cc1SJean-Christophe PLAGNIOL-VILLARD {
2359829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_set_interface(2048, 0, 0, 0);
2459829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_chip_select(0);
2559829cc1SJean-Christophe PLAGNIOL-VILLARD 	udelay(1);
2659829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_chip_select(dev);
2759829cc1SJean-Christophe PLAGNIOL-VILLARD 	udelay(1);
2859829cc1SJean-Christophe PLAGNIOL-VILLARD }
2959829cc1SJean-Christophe PLAGNIOL-VILLARD 
mw_eeprom_size(int dev)3059829cc1SJean-Christophe PLAGNIOL-VILLARD static int mw_eeprom_size(int dev)
3159829cc1SJean-Christophe PLAGNIOL-VILLARD {
3259829cc1SJean-Christophe PLAGNIOL-VILLARD 	int x;
3359829cc1SJean-Christophe PLAGNIOL-VILLARD 	u16 res;
3459829cc1SJean-Christophe PLAGNIOL-VILLARD 
3559829cc1SJean-Christophe PLAGNIOL-VILLARD 	mw_eeprom_select(dev);
3659829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_tx_byte(EEP_OPC_READ);
3759829cc1SJean-Christophe PLAGNIOL-VILLARD 
3859829cc1SJean-Christophe PLAGNIOL-VILLARD 	res = ssi_txrx_byte(0) << 8;
3959829cc1SJean-Christophe PLAGNIOL-VILLARD 	res |= ssi_rx_byte();
4059829cc1SJean-Christophe PLAGNIOL-VILLARD 	for (x = 0; x < 16; x++) {
4159829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (! (res & 0x8000)) {
4259829cc1SJean-Christophe PLAGNIOL-VILLARD 			break;
4359829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
4459829cc1SJean-Christophe PLAGNIOL-VILLARD 		res <<= 1;
4559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
4659829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_chip_select(0);
4759829cc1SJean-Christophe PLAGNIOL-VILLARD 
4859829cc1SJean-Christophe PLAGNIOL-VILLARD 	return x;
4959829cc1SJean-Christophe PLAGNIOL-VILLARD }
5059829cc1SJean-Christophe PLAGNIOL-VILLARD 
mw_eeprom_erase_enable(int dev)5159829cc1SJean-Christophe PLAGNIOL-VILLARD int mw_eeprom_erase_enable(int dev)
5259829cc1SJean-Christophe PLAGNIOL-VILLARD {
5359829cc1SJean-Christophe PLAGNIOL-VILLARD 	mw_eeprom_select(dev);
5459829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_tx_byte(EEP_OPC_ERASE_EN);
5559829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_tx_byte(0);
5659829cc1SJean-Christophe PLAGNIOL-VILLARD 	udelay(1);
5759829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_chip_select(0);
5859829cc1SJean-Christophe PLAGNIOL-VILLARD 
5959829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
6059829cc1SJean-Christophe PLAGNIOL-VILLARD }
6159829cc1SJean-Christophe PLAGNIOL-VILLARD 
mw_eeprom_erase_disable(int dev)6259829cc1SJean-Christophe PLAGNIOL-VILLARD int mw_eeprom_erase_disable(int dev)
6359829cc1SJean-Christophe PLAGNIOL-VILLARD {
6459829cc1SJean-Christophe PLAGNIOL-VILLARD 	mw_eeprom_select(dev);
6559829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_tx_byte(EEP_OPC_ERASE_DIS);
6659829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_tx_byte(0);
6759829cc1SJean-Christophe PLAGNIOL-VILLARD 	udelay(1);
6859829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_chip_select(0);
6959829cc1SJean-Christophe PLAGNIOL-VILLARD 
7059829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
7159829cc1SJean-Christophe PLAGNIOL-VILLARD }
7259829cc1SJean-Christophe PLAGNIOL-VILLARD 
7359829cc1SJean-Christophe PLAGNIOL-VILLARD 
mw_eeprom_read_word(int dev,int addr)7459829cc1SJean-Christophe PLAGNIOL-VILLARD u32 mw_eeprom_read_word(int dev, int addr)
7559829cc1SJean-Christophe PLAGNIOL-VILLARD {
7659829cc1SJean-Christophe PLAGNIOL-VILLARD 	u16 rcv;
7759829cc1SJean-Christophe PLAGNIOL-VILLARD 	u16 res;
7859829cc1SJean-Christophe PLAGNIOL-VILLARD 	int bits;
7959829cc1SJean-Christophe PLAGNIOL-VILLARD 
8059829cc1SJean-Christophe PLAGNIOL-VILLARD 	mw_eeprom_select(dev);
8159829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_tx_byte((EEP_OPC_READ << 5) | ((addr >> (addrlen - 5)) & 0x1f));
8259829cc1SJean-Christophe PLAGNIOL-VILLARD 	rcv = ssi_txrx_byte(addr << (13 - addrlen));
8359829cc1SJean-Christophe PLAGNIOL-VILLARD 	res = rcv << (16 - addrlen);
8459829cc1SJean-Christophe PLAGNIOL-VILLARD 	bits = 4 + addrlen;
8559829cc1SJean-Christophe PLAGNIOL-VILLARD 
8659829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (bits>0) {
8759829cc1SJean-Christophe PLAGNIOL-VILLARD 		rcv = ssi_rx_byte();
8859829cc1SJean-Christophe PLAGNIOL-VILLARD 		if (bits > 7) {
8959829cc1SJean-Christophe PLAGNIOL-VILLARD 			res |= rcv << (bits - 8);
9059829cc1SJean-Christophe PLAGNIOL-VILLARD 		} else {
9159829cc1SJean-Christophe PLAGNIOL-VILLARD 			res |= rcv >> (8 - bits);
9259829cc1SJean-Christophe PLAGNIOL-VILLARD 		}
9359829cc1SJean-Christophe PLAGNIOL-VILLARD 		bits -= 8;
9459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
9559829cc1SJean-Christophe PLAGNIOL-VILLARD 
9659829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_chip_select(0);
9759829cc1SJean-Christophe PLAGNIOL-VILLARD 
9859829cc1SJean-Christophe PLAGNIOL-VILLARD 	return res;
9959829cc1SJean-Christophe PLAGNIOL-VILLARD }
10059829cc1SJean-Christophe PLAGNIOL-VILLARD 
mw_eeprom_write_word(int dev,int addr,u16 data)10159829cc1SJean-Christophe PLAGNIOL-VILLARD int mw_eeprom_write_word(int dev, int addr, u16 data)
10259829cc1SJean-Christophe PLAGNIOL-VILLARD {
10359829cc1SJean-Christophe PLAGNIOL-VILLARD 	u8 byte1=0;
10459829cc1SJean-Christophe PLAGNIOL-VILLARD 	u8 byte2=0;
10559829cc1SJean-Christophe PLAGNIOL-VILLARD 
10659829cc1SJean-Christophe PLAGNIOL-VILLARD 	mw_eeprom_erase_enable(dev);
10759829cc1SJean-Christophe PLAGNIOL-VILLARD 	mw_eeprom_select(dev);
10859829cc1SJean-Christophe PLAGNIOL-VILLARD 
10959829cc1SJean-Christophe PLAGNIOL-VILLARD 	switch (addrlen) {
11059829cc1SJean-Christophe PLAGNIOL-VILLARD 	 case 6:
11159829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte1 = EEP_OPC_WRITE >> 2;
11259829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte2 = (EEP_OPC_WRITE << 6)&0xc0;
11359829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte2 |= addr;
11459829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
11559829cc1SJean-Christophe PLAGNIOL-VILLARD 	 case 7:
11659829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte1 = EEP_OPC_WRITE >> 1;
11759829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte2 = (EEP_OPC_WRITE << 7)&0x80;
11859829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte2 |= addr;
11959829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
12059829cc1SJean-Christophe PLAGNIOL-VILLARD 	 case 8:
12159829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte1 = EEP_OPC_WRITE;
12259829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte2 = addr;
12359829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
12459829cc1SJean-Christophe PLAGNIOL-VILLARD 	 case 9:
12559829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte1 = EEP_OPC_WRITE << 1;
12659829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte1 |= addr >> 8;
12759829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte2 = addr & 0xff;
12859829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
12959829cc1SJean-Christophe PLAGNIOL-VILLARD 	 case 10:
13059829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte1 = EEP_OPC_WRITE << 2;
13159829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte1 |= addr >> 8;
13259829cc1SJean-Christophe PLAGNIOL-VILLARD 		byte2 = addr & 0xff;
13359829cc1SJean-Christophe PLAGNIOL-VILLARD 		break;
13459829cc1SJean-Christophe PLAGNIOL-VILLARD 	 default:
13559829cc1SJean-Christophe PLAGNIOL-VILLARD 		printf("Unsupported number of address bits: %d\n", addrlen);
13659829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -1;
13759829cc1SJean-Christophe PLAGNIOL-VILLARD 
13859829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
13959829cc1SJean-Christophe PLAGNIOL-VILLARD 
14059829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_tx_byte(byte1);
14159829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_tx_byte(byte2);
14259829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_tx_byte(data >> 8);
14359829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_tx_byte(data & 0xff);
14459829cc1SJean-Christophe PLAGNIOL-VILLARD 	ssi_chip_select(0);
14559829cc1SJean-Christophe PLAGNIOL-VILLARD 	udelay(10000); /* Worst case */
14659829cc1SJean-Christophe PLAGNIOL-VILLARD 	mw_eeprom_erase_disable(dev);
14759829cc1SJean-Christophe PLAGNIOL-VILLARD 
14859829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
14959829cc1SJean-Christophe PLAGNIOL-VILLARD }
15059829cc1SJean-Christophe PLAGNIOL-VILLARD 
15159829cc1SJean-Christophe PLAGNIOL-VILLARD 
mw_eeprom_write(int dev,int addr,u8 * buffer,int len)15259829cc1SJean-Christophe PLAGNIOL-VILLARD int mw_eeprom_write(int dev, int addr, u8 *buffer, int len)
15359829cc1SJean-Christophe PLAGNIOL-VILLARD {
15459829cc1SJean-Christophe PLAGNIOL-VILLARD 	int done;
15559829cc1SJean-Christophe PLAGNIOL-VILLARD 
15659829cc1SJean-Christophe PLAGNIOL-VILLARD 	done = 0;
15759829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (addr & 1) {
15859829cc1SJean-Christophe PLAGNIOL-VILLARD 		u16 temp = mw_eeprom_read_word(dev, addr >> 1);
15959829cc1SJean-Christophe PLAGNIOL-VILLARD 		temp &= 0xff00;
16059829cc1SJean-Christophe PLAGNIOL-VILLARD 		temp |= buffer[0];
16159829cc1SJean-Christophe PLAGNIOL-VILLARD 
16259829cc1SJean-Christophe PLAGNIOL-VILLARD 		mw_eeprom_write_word(dev, addr >> 1, temp);
16359829cc1SJean-Christophe PLAGNIOL-VILLARD 		len--;
16459829cc1SJean-Christophe PLAGNIOL-VILLARD 		addr++;
16559829cc1SJean-Christophe PLAGNIOL-VILLARD 		buffer++;
16659829cc1SJean-Christophe PLAGNIOL-VILLARD 		done++;
16759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
16859829cc1SJean-Christophe PLAGNIOL-VILLARD 
16959829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (len <= 2) {
17059829cc1SJean-Christophe PLAGNIOL-VILLARD 		mw_eeprom_write_word(dev, addr >> 1, *(u16*)buffer);
17159829cc1SJean-Christophe PLAGNIOL-VILLARD 		len-=2;
17259829cc1SJean-Christophe PLAGNIOL-VILLARD 		addr+=2;
17359829cc1SJean-Christophe PLAGNIOL-VILLARD 		buffer+=2;
17459829cc1SJean-Christophe PLAGNIOL-VILLARD 		done+=2;
17559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
17659829cc1SJean-Christophe PLAGNIOL-VILLARD 
17759829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (len) {
17859829cc1SJean-Christophe PLAGNIOL-VILLARD 		u16 temp = mw_eeprom_read_word(dev, addr >> 1);
17959829cc1SJean-Christophe PLAGNIOL-VILLARD 		temp &= 0x00ff;
18059829cc1SJean-Christophe PLAGNIOL-VILLARD 		temp |= buffer[0] << 8;
18159829cc1SJean-Christophe PLAGNIOL-VILLARD 
18259829cc1SJean-Christophe PLAGNIOL-VILLARD 		mw_eeprom_write_word(dev, addr >> 1, temp);
18359829cc1SJean-Christophe PLAGNIOL-VILLARD 		len--;
18459829cc1SJean-Christophe PLAGNIOL-VILLARD 		addr++;
18559829cc1SJean-Christophe PLAGNIOL-VILLARD 		buffer++;
18659829cc1SJean-Christophe PLAGNIOL-VILLARD 		done++;
18759829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
18859829cc1SJean-Christophe PLAGNIOL-VILLARD 
18959829cc1SJean-Christophe PLAGNIOL-VILLARD 	return done;
19059829cc1SJean-Christophe PLAGNIOL-VILLARD }
19159829cc1SJean-Christophe PLAGNIOL-VILLARD 
19259829cc1SJean-Christophe PLAGNIOL-VILLARD 
mw_eeprom_read(int dev,int addr,u8 * buffer,int len)19359829cc1SJean-Christophe PLAGNIOL-VILLARD int mw_eeprom_read(int dev, int addr, u8 *buffer, int len)
19459829cc1SJean-Christophe PLAGNIOL-VILLARD {
19559829cc1SJean-Christophe PLAGNIOL-VILLARD 	int done;
19659829cc1SJean-Christophe PLAGNIOL-VILLARD 
19759829cc1SJean-Christophe PLAGNIOL-VILLARD 	done = 0;
19859829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (addr & 1) {
19959829cc1SJean-Christophe PLAGNIOL-VILLARD 		u16 temp = mw_eeprom_read_word(dev, addr >> 1);
20059829cc1SJean-Christophe PLAGNIOL-VILLARD 		buffer[0]= temp & 0xff;
20159829cc1SJean-Christophe PLAGNIOL-VILLARD 
20259829cc1SJean-Christophe PLAGNIOL-VILLARD 		len--;
20359829cc1SJean-Christophe PLAGNIOL-VILLARD 		addr++;
20459829cc1SJean-Christophe PLAGNIOL-VILLARD 		buffer++;
20559829cc1SJean-Christophe PLAGNIOL-VILLARD 		done++;
20659829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
20759829cc1SJean-Christophe PLAGNIOL-VILLARD 
20859829cc1SJean-Christophe PLAGNIOL-VILLARD 	while (len <= 2) {
20959829cc1SJean-Christophe PLAGNIOL-VILLARD 		*(u16*)buffer = mw_eeprom_read_word(dev, addr >> 1);
21059829cc1SJean-Christophe PLAGNIOL-VILLARD 		len-=2;
21159829cc1SJean-Christophe PLAGNIOL-VILLARD 		addr+=2;
21259829cc1SJean-Christophe PLAGNIOL-VILLARD 		buffer+=2;
21359829cc1SJean-Christophe PLAGNIOL-VILLARD 		done+=2;
21459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
21559829cc1SJean-Christophe PLAGNIOL-VILLARD 
21659829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (len) {
21759829cc1SJean-Christophe PLAGNIOL-VILLARD 		u16 temp = mw_eeprom_read_word(dev, addr >> 1);
21859829cc1SJean-Christophe PLAGNIOL-VILLARD 		buffer[0] = temp >> 8;
21959829cc1SJean-Christophe PLAGNIOL-VILLARD 
22059829cc1SJean-Christophe PLAGNIOL-VILLARD 		len--;
22159829cc1SJean-Christophe PLAGNIOL-VILLARD 		addr++;
22259829cc1SJean-Christophe PLAGNIOL-VILLARD 		buffer++;
22359829cc1SJean-Christophe PLAGNIOL-VILLARD 		done++;
22459829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
22559829cc1SJean-Christophe PLAGNIOL-VILLARD 
22659829cc1SJean-Christophe PLAGNIOL-VILLARD 	return done;
22759829cc1SJean-Christophe PLAGNIOL-VILLARD }
22859829cc1SJean-Christophe PLAGNIOL-VILLARD 
mw_eeprom_probe(int dev)22959829cc1SJean-Christophe PLAGNIOL-VILLARD int mw_eeprom_probe(int dev)
23059829cc1SJean-Christophe PLAGNIOL-VILLARD {
23159829cc1SJean-Christophe PLAGNIOL-VILLARD 	addrlen = mw_eeprom_size(dev);
23259829cc1SJean-Christophe PLAGNIOL-VILLARD 
23359829cc1SJean-Christophe PLAGNIOL-VILLARD 	if (addrlen < 6 || addrlen > 10) {
23459829cc1SJean-Christophe PLAGNIOL-VILLARD 		return -1;
23559829cc1SJean-Christophe PLAGNIOL-VILLARD 	}
23659829cc1SJean-Christophe PLAGNIOL-VILLARD 	return 0;
23759829cc1SJean-Christophe PLAGNIOL-VILLARD }
238