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