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