blob: f7791b51a046d663082934dcd360bfdd160b09b8 [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>
Graeme Russ3ef96de2008-09-07 07:08:42 +10004#include <asm/ic/ssi.h>
Jean-Christophe PLAGNIOL-VILLARD58b74b02007-10-19 00:09:05 +02005
wdenk7a8e9bed2003-05-31 18:35:21 +00006/*
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
18static int addrlen;
19
20static 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
29static int mw_eeprom_size(int dev)
30{
31 int x;
32 u16 res;
wdenk8bde7f72003-06-27 21:31:46 +000033
wdenk7a8e9bed2003-05-31 18:35:21 +000034 mw_eeprom_select(dev);
35 ssi_tx_byte(EEP_OPC_READ);
wdenk8bde7f72003-06-27 21:31:46 +000036
wdenk7a8e9bed2003-05-31 18:35:21 +000037 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);
wdenk8bde7f72003-06-27 21:31:46 +000046
wdenk7a8e9bed2003-05-31 18:35:21 +000047 return x;
48}
49
50int 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);
wdenk8bde7f72003-06-27 21:31:46 +000057
wdenk7a8e9bed2003-05-31 18:35:21 +000058 return 0;
59}
60
61int mw_eeprom_erase_disable(int dev)
wdenk8bde7f72003-06-27 21:31:46 +000062{
wdenk7a8e9bed2003-05-31 18:35:21 +000063 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);
wdenk8bde7f72003-06-27 21:31:46 +000068
wdenk7a8e9bed2003-05-31 18:35:21 +000069 return 0;
70}
71
72
73u32 mw_eeprom_read_word(int dev, int addr)
74{
75 u16 rcv;
76 u16 res;
77 int bits;
wdenk8bde7f72003-06-27 21:31:46 +000078
wdenk7a8e9bed2003-05-31 18:35:21 +000079 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;
wdenk8bde7f72003-06-27 21:31:46 +000084
wdenk7a8e9bed2003-05-31 18:35:21 +000085 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 }
wdenk8bde7f72003-06-27 21:31:46 +000094
wdenk7a8e9bed2003-05-31 18:35:21 +000095 ssi_chip_select(0);
wdenk8bde7f72003-06-27 21:31:46 +000096
wdenk7a8e9bed2003-05-31 18:35:21 +000097 return res;
98}
99
100int mw_eeprom_write_word(int dev, int addr, u16 data)
101{
102 u8 byte1=0;
103 u8 byte2=0;
wdenk8bde7f72003-06-27 21:31:46 +0000104
wdenk7a8e9bed2003-05-31 18:35:21 +0000105 mw_eeprom_erase_enable(dev);
106 mw_eeprom_select(dev);
wdenk8bde7f72003-06-27 21:31:46 +0000107
wdenk7a8e9bed2003-05-31 18:35:21 +0000108 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;
wdenk8bde7f72003-06-27 21:31:46 +0000136
wdenk7a8e9bed2003-05-31 18:35:21 +0000137 }
wdenk8bde7f72003-06-27 21:31:46 +0000138
wdenk7a8e9bed2003-05-31 18:35:21 +0000139 ssi_tx_byte(byte1);
140 ssi_tx_byte(byte2);
wdenk8bde7f72003-06-27 21:31:46 +0000141 ssi_tx_byte(data >> 8);
wdenk7a8e9bed2003-05-31 18:35:21 +0000142 ssi_tx_byte(data & 0xff);
wdenk8bde7f72003-06-27 21:31:46 +0000143 ssi_chip_select(0);
wdenk7a8e9bed2003-05-31 18:35:21 +0000144 udelay(10000); /* Worst case */
145 mw_eeprom_erase_disable(dev);
146
147 return 0;
148}
149
150
151int mw_eeprom_write(int dev, int addr, u8 *buffer, int len)
152{
153 int done;
wdenk8bde7f72003-06-27 21:31:46 +0000154
wdenk7a8e9bed2003-05-31 18:35:21 +0000155 done = 0;
156 if (addr & 1) {
157 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
158 temp &= 0xff00;
159 temp |= buffer[0];
wdenk8bde7f72003-06-27 21:31:46 +0000160
wdenk7a8e9bed2003-05-31 18:35:21 +0000161 mw_eeprom_write_word(dev, addr >> 1, temp);
162 len--;
163 addr++;
164 buffer++;
165 done++;
166 }
wdenk8bde7f72003-06-27 21:31:46 +0000167
wdenk7a8e9bed2003-05-31 18:35:21 +0000168 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;
wdenk8bde7f72003-06-27 21:31:46 +0000180
wdenk7a8e9bed2003-05-31 18:35:21 +0000181 mw_eeprom_write_word(dev, addr >> 1, temp);
182 len--;
183 addr++;
184 buffer++;
185 done++;
186 }
187
188 return done;
189}
190
191
wdenk7a8e9bed2003-05-31 18:35:21 +0000192int mw_eeprom_read(int dev, int addr, u8 *buffer, int len)
193{
194 int done;
wdenk8bde7f72003-06-27 21:31:46 +0000195
wdenk7a8e9bed2003-05-31 18:35:21 +0000196 done = 0;
197 if (addr & 1) {
198 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
199 buffer[0]= temp & 0xff;
wdenk8bde7f72003-06-27 21:31:46 +0000200
wdenk7a8e9bed2003-05-31 18:35:21 +0000201 len--;
202 addr++;
203 buffer++;
204 done++;
205 }
wdenk8bde7f72003-06-27 21:31:46 +0000206
wdenk7a8e9bed2003-05-31 18:35:21 +0000207 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;
wdenk8bde7f72003-06-27 21:31:46 +0000218
wdenk7a8e9bed2003-05-31 18:35:21 +0000219 len--;
220 addr++;
221 buffer++;
222 done++;
223 }
224
225 return done;
226}
227
228int mw_eeprom_probe(int dev)
229{
230 addrlen = mw_eeprom_size(dev);
wdenk8bde7f72003-06-27 21:31:46 +0000231
wdenk7a8e9bed2003-05-31 18:35:21 +0000232 if (addrlen < 6 || addrlen > 10) {
233 return -1;
234 }
235 return 0;
236}