6a3a6f67518b52b8432a599dcb02b2d35f03162b
[oweals/u-boot.git] / drivers / mtd / mw_eeprom.c
1 /* Three-wire (MicroWire) serial eeprom driver (for 93C46 and compatibles) */
2
3 #include <common.h>
4 #include <eeprom.h>
5 #include <asm/ic/ssi.h>
6
7 /*
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
19 static int addrlen;
20
21 static 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
30 static int mw_eeprom_size(int dev)
31 {
32         int x;
33         u16 res;
34
35         mw_eeprom_select(dev);
36         ssi_tx_byte(EEP_OPC_READ);
37
38         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);
47
48         return x;
49 }
50
51 int 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);
58
59         return 0;
60 }
61
62 int mw_eeprom_erase_disable(int dev)
63 {
64         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);
69
70         return 0;
71 }
72
73
74 u32 mw_eeprom_read_word(int dev, int addr)
75 {
76         u16 rcv;
77         u16 res;
78         int bits;
79
80         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;
85
86         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         }
95
96         ssi_chip_select(0);
97
98         return res;
99 }
100
101 int mw_eeprom_write_word(int dev, int addr, u16 data)
102 {
103         u8 byte1=0;
104         u8 byte2=0;
105
106         mw_eeprom_erase_enable(dev);
107         mw_eeprom_select(dev);
108
109         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;
137
138         }
139
140         ssi_tx_byte(byte1);
141         ssi_tx_byte(byte2);
142         ssi_tx_byte(data >> 8);
143         ssi_tx_byte(data & 0xff);
144         ssi_chip_select(0);
145         udelay(10000); /* Worst case */
146         mw_eeprom_erase_disable(dev);
147
148         return 0;
149 }
150
151
152 int mw_eeprom_write(int dev, int addr, u8 *buffer, int len)
153 {
154         int done;
155
156         done = 0;
157         if (addr & 1) {
158                 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
159                 temp &= 0xff00;
160                 temp |= buffer[0];
161
162                 mw_eeprom_write_word(dev, addr >> 1, temp);
163                 len--;
164                 addr++;
165                 buffer++;
166                 done++;
167         }
168
169         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;
181
182                 mw_eeprom_write_word(dev, addr >> 1, temp);
183                 len--;
184                 addr++;
185                 buffer++;
186                 done++;
187         }
188
189         return done;
190 }
191
192
193 int mw_eeprom_read(int dev, int addr, u8 *buffer, int len)
194 {
195         int done;
196
197         done = 0;
198         if (addr & 1) {
199                 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
200                 buffer[0]= temp & 0xff;
201
202                 len--;
203                 addr++;
204                 buffer++;
205                 done++;
206         }
207
208         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;
219
220                 len--;
221                 addr++;
222                 buffer++;
223                 done++;
224         }
225
226         return done;
227 }
228
229 int mw_eeprom_probe(int dev)
230 {
231         addrlen = mw_eeprom_size(dev);
232
233         if (addrlen < 6 || addrlen > 10) {
234                 return -1;
235         }
236         return 0;
237 }