blob: 6a3a6f67518b52b8432a599dcb02b2d35f03162b [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>
Jean-Christophe PLAGNIOL-VILLARD58b74b02007-10-19 00:09:05 +02006
wdenk7a8e9bed2003-05-31 18:35:21 +00007/*
8 * Serial EEPROM opcodes, including start bit
9 */
10#define EEP_OPC_ERASE 0x7 /* 3-bit opcode */
11#define EEP_OPC_WRITE 0x5 /* 3-bit opcode */
12#define EEP_OPC_READ 0x6 /* 3-bit opcode */
13
14#define EEP_OPC_ERASE_ALL 0x12 /* 5-bit opcode */
15#define EEP_OPC_ERASE_EN 0x13 /* 5-bit opcode */
16#define EEP_OPC_WRITE_ALL 0x11 /* 5-bit opcode */
17#define EEP_OPC_ERASE_DIS 0x10 /* 5-bit opcode */
18
19static int addrlen;
20
21static void mw_eeprom_select(int dev)
22{
23 ssi_set_interface(2048, 0, 0, 0);
24 ssi_chip_select(0);
25 udelay(1);
26 ssi_chip_select(dev);
27 udelay(1);
28}
29
30static int mw_eeprom_size(int dev)
31{
32 int x;
33 u16 res;
wdenk8bde7f72003-06-27 21:31:46 +000034
wdenk7a8e9bed2003-05-31 18:35:21 +000035 mw_eeprom_select(dev);
36 ssi_tx_byte(EEP_OPC_READ);
wdenk8bde7f72003-06-27 21:31:46 +000037
wdenk7a8e9bed2003-05-31 18:35:21 +000038 res = ssi_txrx_byte(0) << 8;
39 res |= ssi_rx_byte();
40 for (x = 0; x < 16; x++) {
41 if (! (res & 0x8000)) {
42 break;
43 }
44 res <<= 1;
45 }
46 ssi_chip_select(0);
wdenk8bde7f72003-06-27 21:31:46 +000047
wdenk7a8e9bed2003-05-31 18:35:21 +000048 return x;
49}
50
51int mw_eeprom_erase_enable(int dev)
52{
53 mw_eeprom_select(dev);
54 ssi_tx_byte(EEP_OPC_ERASE_EN);
55 ssi_tx_byte(0);
56 udelay(1);
57 ssi_chip_select(0);
wdenk8bde7f72003-06-27 21:31:46 +000058
wdenk7a8e9bed2003-05-31 18:35:21 +000059 return 0;
60}
61
62int mw_eeprom_erase_disable(int dev)
wdenk8bde7f72003-06-27 21:31:46 +000063{
wdenk7a8e9bed2003-05-31 18:35:21 +000064 mw_eeprom_select(dev);
65 ssi_tx_byte(EEP_OPC_ERASE_DIS);
66 ssi_tx_byte(0);
67 udelay(1);
68 ssi_chip_select(0);
wdenk8bde7f72003-06-27 21:31:46 +000069
wdenk7a8e9bed2003-05-31 18:35:21 +000070 return 0;
71}
72
73
74u32 mw_eeprom_read_word(int dev, int addr)
75{
76 u16 rcv;
77 u16 res;
78 int bits;
wdenk8bde7f72003-06-27 21:31:46 +000079
wdenk7a8e9bed2003-05-31 18:35:21 +000080 mw_eeprom_select(dev);
81 ssi_tx_byte((EEP_OPC_READ << 5) | ((addr >> (addrlen - 5)) & 0x1f));
82 rcv = ssi_txrx_byte(addr << (13 - addrlen));
83 res = rcv << (16 - addrlen);
84 bits = 4 + addrlen;
wdenk8bde7f72003-06-27 21:31:46 +000085
wdenk7a8e9bed2003-05-31 18:35:21 +000086 while (bits>0) {
87 rcv = ssi_rx_byte();
88 if (bits > 7) {
89 res |= rcv << (bits - 8);
90 } else {
91 res |= rcv >> (8 - bits);
92 }
93 bits -= 8;
94 }
wdenk8bde7f72003-06-27 21:31:46 +000095
wdenk7a8e9bed2003-05-31 18:35:21 +000096 ssi_chip_select(0);
wdenk8bde7f72003-06-27 21:31:46 +000097
wdenk7a8e9bed2003-05-31 18:35:21 +000098 return res;
99}
100
101int mw_eeprom_write_word(int dev, int addr, u16 data)
102{
103 u8 byte1=0;
104 u8 byte2=0;
wdenk8bde7f72003-06-27 21:31:46 +0000105
wdenk7a8e9bed2003-05-31 18:35:21 +0000106 mw_eeprom_erase_enable(dev);
107 mw_eeprom_select(dev);
wdenk8bde7f72003-06-27 21:31:46 +0000108
wdenk7a8e9bed2003-05-31 18:35:21 +0000109 switch (addrlen) {
110 case 6:
111 byte1 = EEP_OPC_WRITE >> 2;
112 byte2 = (EEP_OPC_WRITE << 6)&0xc0;
113 byte2 |= addr;
114 break;
115 case 7:
116 byte1 = EEP_OPC_WRITE >> 1;
117 byte2 = (EEP_OPC_WRITE << 7)&0x80;
118 byte2 |= addr;
119 break;
120 case 8:
121 byte1 = EEP_OPC_WRITE;
122 byte2 = addr;
123 break;
124 case 9:
125 byte1 = EEP_OPC_WRITE << 1;
126 byte1 |= addr >> 8;
127 byte2 = addr & 0xff;
128 break;
129 case 10:
130 byte1 = EEP_OPC_WRITE << 2;
131 byte1 |= addr >> 8;
132 byte2 = addr & 0xff;
133 break;
134 default:
135 printf("Unsupported number of address bits: %d\n", addrlen);
136 return -1;
wdenk8bde7f72003-06-27 21:31:46 +0000137
wdenk7a8e9bed2003-05-31 18:35:21 +0000138 }
wdenk8bde7f72003-06-27 21:31:46 +0000139
wdenk7a8e9bed2003-05-31 18:35:21 +0000140 ssi_tx_byte(byte1);
141 ssi_tx_byte(byte2);
wdenk8bde7f72003-06-27 21:31:46 +0000142 ssi_tx_byte(data >> 8);
wdenk7a8e9bed2003-05-31 18:35:21 +0000143 ssi_tx_byte(data & 0xff);
wdenk8bde7f72003-06-27 21:31:46 +0000144 ssi_chip_select(0);
wdenk7a8e9bed2003-05-31 18:35:21 +0000145 udelay(10000); /* Worst case */
146 mw_eeprom_erase_disable(dev);
147
148 return 0;
149}
150
151
152int mw_eeprom_write(int dev, int addr, u8 *buffer, int len)
153{
154 int done;
wdenk8bde7f72003-06-27 21:31:46 +0000155
wdenk7a8e9bed2003-05-31 18:35:21 +0000156 done = 0;
157 if (addr & 1) {
158 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
159 temp &= 0xff00;
160 temp |= buffer[0];
wdenk8bde7f72003-06-27 21:31:46 +0000161
wdenk7a8e9bed2003-05-31 18:35:21 +0000162 mw_eeprom_write_word(dev, addr >> 1, temp);
163 len--;
164 addr++;
165 buffer++;
166 done++;
167 }
wdenk8bde7f72003-06-27 21:31:46 +0000168
wdenk7a8e9bed2003-05-31 18:35:21 +0000169 while (len <= 2) {
170 mw_eeprom_write_word(dev, addr >> 1, *(u16*)buffer);
171 len-=2;
172 addr+=2;
173 buffer+=2;
174 done+=2;
175 }
176
177 if (len) {
178 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
179 temp &= 0x00ff;
180 temp |= buffer[0] << 8;
wdenk8bde7f72003-06-27 21:31:46 +0000181
wdenk7a8e9bed2003-05-31 18:35:21 +0000182 mw_eeprom_write_word(dev, addr >> 1, temp);
183 len--;
184 addr++;
185 buffer++;
186 done++;
187 }
188
189 return done;
190}
191
192
wdenk7a8e9bed2003-05-31 18:35:21 +0000193int mw_eeprom_read(int dev, int addr, u8 *buffer, int len)
194{
195 int done;
wdenk8bde7f72003-06-27 21:31:46 +0000196
wdenk7a8e9bed2003-05-31 18:35:21 +0000197 done = 0;
198 if (addr & 1) {
199 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
200 buffer[0]= temp & 0xff;
wdenk8bde7f72003-06-27 21:31:46 +0000201
wdenk7a8e9bed2003-05-31 18:35:21 +0000202 len--;
203 addr++;
204 buffer++;
205 done++;
206 }
wdenk8bde7f72003-06-27 21:31:46 +0000207
wdenk7a8e9bed2003-05-31 18:35:21 +0000208 while (len <= 2) {
209 *(u16*)buffer = mw_eeprom_read_word(dev, addr >> 1);
210 len-=2;
211 addr+=2;
212 buffer+=2;
213 done+=2;
214 }
215
216 if (len) {
217 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
218 buffer[0] = temp >> 8;
wdenk8bde7f72003-06-27 21:31:46 +0000219
wdenk7a8e9bed2003-05-31 18:35:21 +0000220 len--;
221 addr++;
222 buffer++;
223 done++;
224 }
225
226 return done;
227}
228
229int mw_eeprom_probe(int dev)
230{
231 addrlen = mw_eeprom_size(dev);
wdenk8bde7f72003-06-27 21:31:46 +0000232
wdenk7a8e9bed2003-05-31 18:35:21 +0000233 if (addrlen < 6 || addrlen > 10) {
234 return -1;
235 }
236 return 0;
237}