xref: /openbmc/u-boot/drivers/mtd/mw_eeprom.c (revision ef64e782)
1 /* Three-wire (MicroWire) serial eeprom driver (for 93C46 and compatibles) */
2 
3 #include <common.h>
4 #include <asm/ic/ssi.h>
5 
6 /*
7  * Serial EEPROM opcodes, including start bit
8  */
9 #define EEP_OPC_ERASE	0x7  /* 3-bit opcode */
10 #define EEP_OPC_WRITE	0x5  /* 3-bit opcode */
11 #define EEP_OPC_READ	        0x6  /* 3-bit opcode */
12 
13 #define EEP_OPC_ERASE_ALL	0x12 /* 5-bit opcode */
14 #define EEP_OPC_ERASE_EN	0x13 /* 5-bit opcode */
15 #define EEP_OPC_WRITE_ALL	0x11 /* 5-bit opcode */
16 #define EEP_OPC_ERASE_DIS	0x10 /* 5-bit opcode */
17 
18 static int addrlen;
19 
20 static void mw_eeprom_select(int dev)
21 {
22 	ssi_set_interface(2048, 0, 0, 0);
23 	ssi_chip_select(0);
24 	udelay(1);
25 	ssi_chip_select(dev);
26 	udelay(1);
27 }
28 
29 static int mw_eeprom_size(int dev)
30 {
31 	int x;
32 	u16 res;
33 
34 	mw_eeprom_select(dev);
35 	ssi_tx_byte(EEP_OPC_READ);
36 
37 	res = ssi_txrx_byte(0) << 8;
38 	res |= ssi_rx_byte();
39 	for (x = 0; x < 16; x++) {
40 		if (! (res & 0x8000)) {
41 			break;
42 		}
43 		res <<= 1;
44 	}
45 	ssi_chip_select(0);
46 
47 	return x;
48 }
49 
50 int mw_eeprom_erase_enable(int dev)
51 {
52 	mw_eeprom_select(dev);
53 	ssi_tx_byte(EEP_OPC_ERASE_EN);
54 	ssi_tx_byte(0);
55 	udelay(1);
56 	ssi_chip_select(0);
57 
58 	return 0;
59 }
60 
61 int mw_eeprom_erase_disable(int dev)
62 {
63 	mw_eeprom_select(dev);
64 	ssi_tx_byte(EEP_OPC_ERASE_DIS);
65 	ssi_tx_byte(0);
66 	udelay(1);
67 	ssi_chip_select(0);
68 
69 	return 0;
70 }
71 
72 
73 u32 mw_eeprom_read_word(int dev, int addr)
74 {
75 	u16 rcv;
76 	u16 res;
77 	int bits;
78 
79 	mw_eeprom_select(dev);
80 	ssi_tx_byte((EEP_OPC_READ << 5) | ((addr >> (addrlen - 5)) & 0x1f));
81 	rcv = ssi_txrx_byte(addr << (13 - addrlen));
82 	res = rcv << (16 - addrlen);
83 	bits = 4 + addrlen;
84 
85 	while (bits>0) {
86 		rcv = ssi_rx_byte();
87 		if (bits > 7) {
88 			res |= rcv << (bits - 8);
89 		} else {
90 			res |= rcv >> (8 - bits);
91 		}
92 		bits -= 8;
93 	}
94 
95 	ssi_chip_select(0);
96 
97 	return res;
98 }
99 
100 int mw_eeprom_write_word(int dev, int addr, u16 data)
101 {
102 	u8 byte1=0;
103 	u8 byte2=0;
104 
105 	mw_eeprom_erase_enable(dev);
106 	mw_eeprom_select(dev);
107 
108 	switch (addrlen) {
109 	 case 6:
110 		byte1 = EEP_OPC_WRITE >> 2;
111 		byte2 = (EEP_OPC_WRITE << 6)&0xc0;
112 		byte2 |= addr;
113 		break;
114 	 case 7:
115 		byte1 = EEP_OPC_WRITE >> 1;
116 		byte2 = (EEP_OPC_WRITE << 7)&0x80;
117 		byte2 |= addr;
118 		break;
119 	 case 8:
120 		byte1 = EEP_OPC_WRITE;
121 		byte2 = addr;
122 		break;
123 	 case 9:
124 		byte1 = EEP_OPC_WRITE << 1;
125 		byte1 |= addr >> 8;
126 		byte2 = addr & 0xff;
127 		break;
128 	 case 10:
129 		byte1 = EEP_OPC_WRITE << 2;
130 		byte1 |= addr >> 8;
131 		byte2 = addr & 0xff;
132 		break;
133 	 default:
134 		printf("Unsupported number of address bits: %d\n", addrlen);
135 		return -1;
136 
137 	}
138 
139 	ssi_tx_byte(byte1);
140 	ssi_tx_byte(byte2);
141 	ssi_tx_byte(data >> 8);
142 	ssi_tx_byte(data & 0xff);
143 	ssi_chip_select(0);
144 	udelay(10000); /* Worst case */
145 	mw_eeprom_erase_disable(dev);
146 
147 	return 0;
148 }
149 
150 
151 int mw_eeprom_write(int dev, int addr, u8 *buffer, int len)
152 {
153 	int done;
154 
155 	done = 0;
156 	if (addr & 1) {
157 		u16 temp = mw_eeprom_read_word(dev, addr >> 1);
158 		temp &= 0xff00;
159 		temp |= buffer[0];
160 
161 		mw_eeprom_write_word(dev, addr >> 1, temp);
162 		len--;
163 		addr++;
164 		buffer++;
165 		done++;
166 	}
167 
168 	while (len <= 2) {
169 		mw_eeprom_write_word(dev, addr >> 1, *(u16*)buffer);
170 		len-=2;
171 		addr+=2;
172 		buffer+=2;
173 		done+=2;
174 	}
175 
176 	if (len) {
177 		u16 temp = mw_eeprom_read_word(dev, addr >> 1);
178 		temp &= 0x00ff;
179 		temp |= buffer[0] << 8;
180 
181 		mw_eeprom_write_word(dev, addr >> 1, temp);
182 		len--;
183 		addr++;
184 		buffer++;
185 		done++;
186 	}
187 
188 	return done;
189 }
190 
191 
192 int mw_eeprom_read(int dev, int addr, u8 *buffer, int len)
193 {
194 	int done;
195 
196 	done = 0;
197 	if (addr & 1) {
198 		u16 temp = mw_eeprom_read_word(dev, addr >> 1);
199 		buffer[0]= temp & 0xff;
200 
201 		len--;
202 		addr++;
203 		buffer++;
204 		done++;
205 	}
206 
207 	while (len <= 2) {
208 		*(u16*)buffer = mw_eeprom_read_word(dev, addr >> 1);
209 		len-=2;
210 		addr+=2;
211 		buffer+=2;
212 		done+=2;
213 	}
214 
215 	if (len) {
216 		u16 temp = mw_eeprom_read_word(dev, addr >> 1);
217 		buffer[0] = temp >> 8;
218 
219 		len--;
220 		addr++;
221 		buffer++;
222 		done++;
223 	}
224 
225 	return done;
226 }
227 
228 int mw_eeprom_probe(int dev)
229 {
230 	addrlen = mw_eeprom_size(dev);
231 
232 	if (addrlen < 6 || addrlen > 10) {
233 		return -1;
234 	}
235 	return 0;
236 }
237