Add support for MicroSys PM856 board
[platform/kernel/u-boot.git] / drivers / mw_eeprom.c
1 /* Three-wire (MicroWire) serial eeprom driver (for 93C46 and compatibles) */
2
3 #include <common.h>
4 #include <ssi.h>
5
6
7 #ifdef CONFIG_MW_EEPROM
8
9 /*
10  * Serial EEPROM opcodes, including start bit
11  */
12 #define EEP_OPC_ERASE   0x7  /* 3-bit opcode */
13 #define EEP_OPC_WRITE   0x5  /* 3-bit opcode */
14 #define EEP_OPC_READ            0x6  /* 3-bit opcode */
15
16 #define EEP_OPC_ERASE_ALL       0x12 /* 5-bit opcode */
17 #define EEP_OPC_ERASE_EN        0x13 /* 5-bit opcode */
18 #define EEP_OPC_WRITE_ALL       0x11 /* 5-bit opcode */
19 #define EEP_OPC_ERASE_DIS       0x10 /* 5-bit opcode */
20
21 static int addrlen;
22
23 static void mw_eeprom_select(int dev)
24 {
25         ssi_set_interface(2048, 0, 0, 0);
26         ssi_chip_select(0);
27         udelay(1);
28         ssi_chip_select(dev);
29         udelay(1);
30 }
31
32 static int mw_eeprom_size(int dev)
33 {
34         int x;
35         u16 res;
36
37         mw_eeprom_select(dev);
38         ssi_tx_byte(EEP_OPC_READ);
39
40         res = ssi_txrx_byte(0) << 8;
41         res |= ssi_rx_byte();
42         for (x = 0; x < 16; x++) {
43                 if (! (res & 0x8000)) {
44                         break;
45                 }
46                 res <<= 1;
47         }
48         ssi_chip_select(0);
49
50         return x;
51 }
52
53 int mw_eeprom_erase_enable(int dev)
54 {
55         mw_eeprom_select(dev);
56         ssi_tx_byte(EEP_OPC_ERASE_EN);
57         ssi_tx_byte(0);
58         udelay(1);
59         ssi_chip_select(0);
60
61         return 0;
62 }
63
64 int mw_eeprom_erase_disable(int dev)
65 {
66         mw_eeprom_select(dev);
67         ssi_tx_byte(EEP_OPC_ERASE_DIS);
68         ssi_tx_byte(0);
69         udelay(1);
70         ssi_chip_select(0);
71
72         return 0;
73 }
74
75
76 u32 mw_eeprom_read_word(int dev, int addr)
77 {
78         u16 rcv;
79         u16 res;
80         int bits;
81
82         mw_eeprom_select(dev);
83         ssi_tx_byte((EEP_OPC_READ << 5) | ((addr >> (addrlen - 5)) & 0x1f));
84         rcv = ssi_txrx_byte(addr << (13 - addrlen));
85         res = rcv << (16 - addrlen);
86         bits = 4 + addrlen;
87
88         while (bits>0) {
89                 rcv = ssi_rx_byte();
90                 if (bits > 7) {
91                         res |= rcv << (bits - 8);
92                 } else {
93                         res |= rcv >> (8 - bits);
94                 }
95                 bits -= 8;
96         }
97
98         ssi_chip_select(0);
99
100         return res;
101 }
102
103 int mw_eeprom_write_word(int dev, int addr, u16 data)
104 {
105         u8 byte1=0;
106         u8 byte2=0;
107
108         mw_eeprom_erase_enable(dev);
109         mw_eeprom_select(dev);
110
111         switch (addrlen) {
112          case 6:
113                 byte1 = EEP_OPC_WRITE >> 2;
114                 byte2 = (EEP_OPC_WRITE << 6)&0xc0;
115                 byte2 |= addr;
116                 break;
117          case 7:
118                 byte1 = EEP_OPC_WRITE >> 1;
119                 byte2 = (EEP_OPC_WRITE << 7)&0x80;
120                 byte2 |= addr;
121                 break;
122          case 8:
123                 byte1 = EEP_OPC_WRITE;
124                 byte2 = addr;
125                 break;
126          case 9:
127                 byte1 = EEP_OPC_WRITE << 1;
128                 byte1 |= addr >> 8;
129                 byte2 = addr & 0xff;
130                 break;
131          case 10:
132                 byte1 = EEP_OPC_WRITE << 2;
133                 byte1 |= addr >> 8;
134                 byte2 = addr & 0xff;
135                 break;
136          default:
137                 printf("Unsupported number of address bits: %d\n", addrlen);
138                 return -1;
139
140         }
141
142         ssi_tx_byte(byte1);
143         ssi_tx_byte(byte2);
144         ssi_tx_byte(data >> 8);
145         ssi_tx_byte(data & 0xff);
146         ssi_chip_select(0);
147         udelay(10000); /* Worst case */
148         mw_eeprom_erase_disable(dev);
149
150         return 0;
151 }
152
153
154 int mw_eeprom_write(int dev, int addr, u8 *buffer, int len)
155 {
156         int done;
157
158         done = 0;
159         if (addr & 1) {
160                 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
161                 temp &= 0xff00;
162                 temp |= buffer[0];
163
164                 mw_eeprom_write_word(dev, addr >> 1, temp);
165                 len--;
166                 addr++;
167                 buffer++;
168                 done++;
169         }
170
171         while (len <= 2) {
172                 mw_eeprom_write_word(dev, addr >> 1, *(u16*)buffer);
173                 len-=2;
174                 addr+=2;
175                 buffer+=2;
176                 done+=2;
177         }
178
179         if (len) {
180                 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
181                 temp &= 0x00ff;
182                 temp |= buffer[0] << 8;
183
184                 mw_eeprom_write_word(dev, addr >> 1, temp);
185                 len--;
186                 addr++;
187                 buffer++;
188                 done++;
189         }
190
191         return done;
192 }
193
194
195 int mw_eeprom_read(int dev, int addr, u8 *buffer, int len)
196 {
197         int done;
198
199         done = 0;
200         if (addr & 1) {
201                 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
202                 buffer[0]= temp & 0xff;
203
204                 len--;
205                 addr++;
206                 buffer++;
207                 done++;
208         }
209
210         while (len <= 2) {
211                 *(u16*)buffer = mw_eeprom_read_word(dev, addr >> 1);
212                 len-=2;
213                 addr+=2;
214                 buffer+=2;
215                 done+=2;
216         }
217
218         if (len) {
219                 u16 temp = mw_eeprom_read_word(dev, addr >> 1);
220                 buffer[0] = temp >> 8;
221
222                 len--;
223                 addr++;
224                 buffer++;
225                 done++;
226         }
227
228         return done;
229 }
230
231 int mw_eeprom_probe(int dev)
232 {
233         addrlen = mw_eeprom_size(dev);
234
235         if (addrlen < 6 || addrlen > 10) {
236                 return -1;
237         }
238         return 0;
239 }
240
241 #endif