sf: Add Kconfig menu entry
[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 <asm/ic/ssi.h>
5
6 /*
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
18 static int addrlen;
19
20 static 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
29 static int mw_eeprom_size(int dev)
30 {
31         int x;
32         u16 res;
33
34         mw_eeprom_select(dev);
35         ssi_tx_byte(EEP_OPC_READ);
36
37         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);
46
47         return x;
48 }
49
50 int 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);
57
58         return 0;
59 }
60
61 int mw_eeprom_erase_disable(int dev)
62 {
63         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);
68
69         return 0;
70 }
71
72
73 u32 mw_eeprom_read_word(int dev, int addr)
74 {
75         u16 rcv;
76         u16 res;
77         int bits;
78
79         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;
84
85         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         }
94
95         ssi_chip_select(0);
96
97         return res;
98 }
99
100 int mw_eeprom_write_word(int dev, int addr, u16 data)
101 {
102         u8 byte1=0;
103         u8 byte2=0;
104
105         mw_eeprom_erase_enable(dev);
106         mw_eeprom_select(dev);
107
108         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;
136
137         }
138
139         ssi_tx_byte(byte1);
140         ssi_tx_byte(byte2);
141         ssi_tx_byte(data >> 8);
142         ssi_tx_byte(data & 0xff);
143         ssi_chip_select(0);
144         udelay(10000); /* Worst case */
145         mw_eeprom_erase_disable(dev);
146
147         return 0;
148 }
149
150
151 int mw_eeprom_write(int dev, int addr, u8 *buffer, int len)
152 {
153         int done;
154
155         done = 0;
156         if (addr & 1) {
157                 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
158                 temp &= 0xff00;
159                 temp |= buffer[0];
160
161                 mw_eeprom_write_word(dev, addr >> 1, temp);
162                 len--;
163                 addr++;
164                 buffer++;
165                 done++;
166         }
167
168         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;
180
181                 mw_eeprom_write_word(dev, addr >> 1, temp);
182                 len--;
183                 addr++;
184                 buffer++;
185                 done++;
186         }
187
188         return done;
189 }
190
191
192 int mw_eeprom_read(int dev, int addr, u8 *buffer, int len)
193 {
194         int done;
195
196         done = 0;
197         if (addr & 1) {
198                 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
199                 buffer[0]= temp & 0xff;
200
201                 len--;
202                 addr++;
203                 buffer++;
204                 done++;
205         }
206
207         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;
218
219                 len--;
220                 addr++;
221                 buffer++;
222                 done++;
223         }
224
225         return done;
226 }
227
228 int mw_eeprom_probe(int dev)
229 {
230         addrlen = mw_eeprom_size(dev);
231
232         if (addrlen < 6 || addrlen > 10) {
233                 return -1;
234         }
235         return 0;
236 }