blob: 9837733beeed0ce4aa2641c44c061063b6060a92 [file] [log] [blame]
wdenk7a8e9bed2003-05-31 18:35:21 +00001/* Three-wire (MicroWire) serial eeprom driver (for 93C46 and compatibles) */
2
3#include <common.h>
Simon Glasscb3ef682019-11-14 12:57:50 -07004#include <eeprom.h>
Graeme Russ3ef96de2008-09-07 07:08:42 +10005#include <asm/ic/ssi.h>
Simon Glassc05ed002020-05-10 11:40:11 -06006#include <linux/delay.h>
Jean-Christophe PLAGNIOL-VILLARD58b74b02007-10-19 00:09:05 +02007
wdenk7a8e9bed2003-05-31 18:35:21 +00008/*
9 * Serial EEPROM opcodes, including start bit
10 */
11#define EEP_OPC_ERASE 0x7 /* 3-bit opcode */
12#define EEP_OPC_WRITE 0x5 /* 3-bit opcode */
13#define EEP_OPC_READ 0x6 /* 3-bit opcode */
14
15#define EEP_OPC_ERASE_ALL 0x12 /* 5-bit opcode */
16#define EEP_OPC_ERASE_EN 0x13 /* 5-bit opcode */
17#define EEP_OPC_WRITE_ALL 0x11 /* 5-bit opcode */
18#define EEP_OPC_ERASE_DIS 0x10 /* 5-bit opcode */
19
20static int addrlen;
21
22static void mw_eeprom_select(int dev)
23{
24 ssi_set_interface(2048, 0, 0, 0);
25 ssi_chip_select(0);
26 udelay(1);
27 ssi_chip_select(dev);
28 udelay(1);
29}
30
31static int mw_eeprom_size(int dev)
32{
33 int x;
34 u16 res;
wdenk8bde7f72003-06-27 21:31:46 +000035
wdenk7a8e9bed2003-05-31 18:35:21 +000036 mw_eeprom_select(dev);
37 ssi_tx_byte(EEP_OPC_READ);
wdenk8bde7f72003-06-27 21:31:46 +000038
wdenk7a8e9bed2003-05-31 18:35:21 +000039 res = ssi_txrx_byte(0) << 8;
40 res |= ssi_rx_byte();
41 for (x = 0; x < 16; x++) {
42 if (! (res & 0x8000)) {
43 break;
44 }
45 res <<= 1;
46 }
47 ssi_chip_select(0);
wdenk8bde7f72003-06-27 21:31:46 +000048
wdenk7a8e9bed2003-05-31 18:35:21 +000049 return x;
50}
51
52int mw_eeprom_erase_enable(int dev)
53{
54 mw_eeprom_select(dev);
55 ssi_tx_byte(EEP_OPC_ERASE_EN);
56 ssi_tx_byte(0);
57 udelay(1);
58 ssi_chip_select(0);
wdenk8bde7f72003-06-27 21:31:46 +000059
wdenk7a8e9bed2003-05-31 18:35:21 +000060 return 0;
61}
62
63int mw_eeprom_erase_disable(int dev)
wdenk8bde7f72003-06-27 21:31:46 +000064{
wdenk7a8e9bed2003-05-31 18:35:21 +000065 mw_eeprom_select(dev);
66 ssi_tx_byte(EEP_OPC_ERASE_DIS);
67 ssi_tx_byte(0);
68 udelay(1);
69 ssi_chip_select(0);
wdenk8bde7f72003-06-27 21:31:46 +000070
wdenk7a8e9bed2003-05-31 18:35:21 +000071 return 0;
72}
73
74
75u32 mw_eeprom_read_word(int dev, int addr)
76{
77 u16 rcv;
78 u16 res;
79 int bits;
wdenk8bde7f72003-06-27 21:31:46 +000080
wdenk7a8e9bed2003-05-31 18:35:21 +000081 mw_eeprom_select(dev);
82 ssi_tx_byte((EEP_OPC_READ << 5) | ((addr >> (addrlen - 5)) & 0x1f));
83 rcv = ssi_txrx_byte(addr << (13 - addrlen));
84 res = rcv << (16 - addrlen);
85 bits = 4 + addrlen;
wdenk8bde7f72003-06-27 21:31:46 +000086
wdenk7a8e9bed2003-05-31 18:35:21 +000087 while (bits>0) {
88 rcv = ssi_rx_byte();
89 if (bits > 7) {
90 res |= rcv << (bits - 8);
91 } else {
92 res |= rcv >> (8 - bits);
93 }
94 bits -= 8;
95 }
wdenk8bde7f72003-06-27 21:31:46 +000096
wdenk7a8e9bed2003-05-31 18:35:21 +000097 ssi_chip_select(0);
wdenk8bde7f72003-06-27 21:31:46 +000098
wdenk7a8e9bed2003-05-31 18:35:21 +000099 return res;
100}
101
102int mw_eeprom_write_word(int dev, int addr, u16 data)
103{
104 u8 byte1=0;
105 u8 byte2=0;
wdenk8bde7f72003-06-27 21:31:46 +0000106
wdenk7a8e9bed2003-05-31 18:35:21 +0000107 mw_eeprom_erase_enable(dev);
108 mw_eeprom_select(dev);
wdenk8bde7f72003-06-27 21:31:46 +0000109
wdenk7a8e9bed2003-05-31 18:35:21 +0000110 switch (addrlen) {
111 case 6:
112 byte1 = EEP_OPC_WRITE >> 2;
113 byte2 = (EEP_OPC_WRITE << 6)&0xc0;
114 byte2 |= addr;
115 break;
116 case 7:
117 byte1 = EEP_OPC_WRITE >> 1;
118 byte2 = (EEP_OPC_WRITE << 7)&0x80;
119 byte2 |= addr;
120 break;
121 case 8:
122 byte1 = EEP_OPC_WRITE;
123 byte2 = addr;
124 break;
125 case 9:
126 byte1 = EEP_OPC_WRITE << 1;
127 byte1 |= addr >> 8;
128 byte2 = addr & 0xff;
129 break;
130 case 10:
131 byte1 = EEP_OPC_WRITE << 2;
132 byte1 |= addr >> 8;
133 byte2 = addr & 0xff;
134 break;
135 default:
136 printf("Unsupported number of address bits: %d\n", addrlen);
137 return -1;
wdenk8bde7f72003-06-27 21:31:46 +0000138
wdenk7a8e9bed2003-05-31 18:35:21 +0000139 }
wdenk8bde7f72003-06-27 21:31:46 +0000140
wdenk7a8e9bed2003-05-31 18:35:21 +0000141 ssi_tx_byte(byte1);
142 ssi_tx_byte(byte2);
wdenk8bde7f72003-06-27 21:31:46 +0000143 ssi_tx_byte(data >> 8);
wdenk7a8e9bed2003-05-31 18:35:21 +0000144 ssi_tx_byte(data & 0xff);
wdenk8bde7f72003-06-27 21:31:46 +0000145 ssi_chip_select(0);
wdenk7a8e9bed2003-05-31 18:35:21 +0000146 udelay(10000); /* Worst case */
147 mw_eeprom_erase_disable(dev);
148
149 return 0;
150}
151
152
153int mw_eeprom_write(int dev, int addr, u8 *buffer, int len)
154{
155 int done;
wdenk8bde7f72003-06-27 21:31:46 +0000156
wdenk7a8e9bed2003-05-31 18:35:21 +0000157 done = 0;
158 if (addr & 1) {
159 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
160 temp &= 0xff00;
161 temp |= buffer[0];
wdenk8bde7f72003-06-27 21:31:46 +0000162
wdenk7a8e9bed2003-05-31 18:35:21 +0000163 mw_eeprom_write_word(dev, addr >> 1, temp);
164 len--;
165 addr++;
166 buffer++;
167 done++;
168 }
wdenk8bde7f72003-06-27 21:31:46 +0000169
wdenk7a8e9bed2003-05-31 18:35:21 +0000170 while (len <= 2) {
171 mw_eeprom_write_word(dev, addr >> 1, *(u16*)buffer);
172 len-=2;
173 addr+=2;
174 buffer+=2;
175 done+=2;
176 }
177
178 if (len) {
179 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
180 temp &= 0x00ff;
181 temp |= buffer[0] << 8;
wdenk8bde7f72003-06-27 21:31:46 +0000182
wdenk7a8e9bed2003-05-31 18:35:21 +0000183 mw_eeprom_write_word(dev, addr >> 1, temp);
184 len--;
185 addr++;
186 buffer++;
187 done++;
188 }
189
190 return done;
191}
192
193
wdenk7a8e9bed2003-05-31 18:35:21 +0000194int mw_eeprom_read(int dev, int addr, u8 *buffer, int len)
195{
196 int done;
wdenk8bde7f72003-06-27 21:31:46 +0000197
wdenk7a8e9bed2003-05-31 18:35:21 +0000198 done = 0;
199 if (addr & 1) {
200 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
201 buffer[0]= temp & 0xff;
wdenk8bde7f72003-06-27 21:31:46 +0000202
wdenk7a8e9bed2003-05-31 18:35:21 +0000203 len--;
204 addr++;
205 buffer++;
206 done++;
207 }
wdenk8bde7f72003-06-27 21:31:46 +0000208
wdenk7a8e9bed2003-05-31 18:35:21 +0000209 while (len <= 2) {
210 *(u16*)buffer = mw_eeprom_read_word(dev, addr >> 1);
211 len-=2;
212 addr+=2;
213 buffer+=2;
214 done+=2;
215 }
216
217 if (len) {
218 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
219 buffer[0] = temp >> 8;
wdenk8bde7f72003-06-27 21:31:46 +0000220
wdenk7a8e9bed2003-05-31 18:35:21 +0000221 len--;
222 addr++;
223 buffer++;
224 done++;
225 }
226
227 return done;
228}
229
230int mw_eeprom_probe(int dev)
231{
232 addrlen = mw_eeprom_size(dev);
wdenk8bde7f72003-06-27 21:31:46 +0000233
wdenk7a8e9bed2003-05-31 18:35:21 +0000234 if (addrlen < 6 || addrlen > 10) {
235 return -1;
236 }
237 return 0;
238}