|
1814
|
1 //========================================================================== |
|
|
2 // |
|
|
3 // ide_disk.c |
|
|
4 // |
|
|
5 // IDE polled mode disk driver |
|
|
6 // |
|
|
7 //========================================================================== |
|
|
8 //####ECOSGPLCOPYRIGHTBEGIN#### |
|
|
9 // ------------------------------------------- |
|
|
10 // This file is part of eCos, the Embedded Configurable Operating System. |
|
|
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004 Red Hat, Inc. |
|
|
12 // Copyright (C) 2004 eCosCentric, Ltd. |
|
|
13 // |
|
|
14 // eCos is free software; you can redistribute it and/or modify it under |
|
|
15 // the terms of the GNU General Public License as published by the Free |
|
|
16 // Software Foundation; either version 2 or (at your option) any later version. |
|
|
17 // |
|
|
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY |
|
|
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
|
20 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
|
21 // for more details. |
|
|
22 // |
|
|
23 // You should have received a copy of the GNU General Public License along |
|
|
24 // with eCos; if not, write to the Free Software Foundation, Inc., |
|
|
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. |
|
|
26 // |
|
|
27 // As a special exception, if other files instantiate templates or use macros |
|
|
28 // or inline functions from this file, or you compile this file and link it |
|
|
29 // with other works to produce a work based on this file, this file does not |
|
|
30 // by itself cause the resulting work to be covered by the GNU General Public |
|
|
31 // License. However the source code for this file must still be made available |
|
|
32 // in accordance with section (3) of the GNU General Public License. |
|
|
33 // |
|
|
34 // This exception does not invalidate any other reasons why a work based on |
|
|
35 // this file might be covered by the GNU General Public License. |
|
|
36 // |
|
|
37 // ------------------------------------------- |
|
|
38 //####ECOSGPLCOPYRIGHTEND#### |
|
|
39 //========================================================================== |
|
|
40 //#####DESCRIPTIONBEGIN#### |
|
|
41 // |
|
|
42 // Author(s): iz |
|
|
43 // Contributors: |
|
|
44 // Date: 2004-10-16 |
|
|
45 // |
|
|
46 //####DESCRIPTIONEND#### |
|
|
47 // |
|
|
48 //========================================================================== |
|
|
49 |
|
|
50 #include <pkgconf/devs_disk_ide.h> |
|
|
51 |
|
|
52 #include <cyg/infra/cyg_type.h> |
|
|
53 #include <cyg/infra/cyg_ass.h> |
|
|
54 #include <cyg/infra/diag.h> |
|
|
55 #include <cyg/hal/hal_arch.h> |
|
|
56 #include <cyg/hal/hal_io.h> |
|
|
57 #include <cyg/hal/hal_if.h> // delays |
|
|
58 #include <cyg/hal/drv_api.h> |
|
|
59 #include <cyg/io/io.h> |
|
|
60 #include <cyg/io/devtab.h> |
|
|
61 #include <cyg/io/disk.h> |
|
|
62 |
|
|
63 #include "ide_disk.h" |
|
|
64 |
|
|
65 // ---------------------------------------------------------------------------- |
|
|
66 |
|
|
67 //#define DEBUG 1 |
|
|
68 |
|
|
69 #ifdef DEBUG |
|
|
70 # define D(fmt,args...) diag_printf(fmt, ## args) |
|
|
71 #else |
|
|
72 # define D(fmt,args...) |
|
|
73 #endif |
|
|
74 |
|
|
75 // ---------------------------------------------------------------------------- |
|
|
76 |
|
|
77 #ifdef CYGVAR_DEVS_DISK_IDE_DISK0 |
|
|
78 IDE_DISK_INSTANCE(0, 0, 0, true, CYGDAT_IO_DISK_IDE_DISK0_NAME); |
|
|
79 #endif |
|
|
80 |
|
|
81 #ifdef CYGVAR_DEVS_DISK_IDE_DISK1 |
|
|
82 IDE_DISK_INSTANCE(1, 0, 1, true, CYGDAT_IO_DISK_IDE_DISK1_NAME); |
|
|
83 #endif |
|
|
84 |
|
|
85 #ifdef CYGVAR_DEVS_DISK_IDE_DISK2 |
|
|
86 IDE_DISK_INSTANCE(2, 1, 0, true, CYGDAT_IO_DISK_IDE_DISK2_NAME); |
|
|
87 #endif |
|
|
88 |
|
|
89 #ifdef CYGVAR_DEVS_DISK_IDE_DISK3 |
|
|
90 IDE_DISK_INSTANCE(3, 1, 1, true, CYGDAT_IO_DISK_IDE_DISK3_NAME); |
|
|
91 #endif |
|
|
92 |
|
|
93 // ---------------------------------------------------------------------------- |
|
|
94 |
|
|
95 static void |
|
|
96 id_strcpy(char *dest, cyg_uint16 *src, cyg_uint16 size) |
|
|
97 { |
|
|
98 int i; |
|
|
99 |
|
|
100 for (i = 0; i < size; i+=2) |
|
|
101 { |
|
|
102 *dest++ = (char)(*src >> 8); |
|
|
103 *dest++ = (char)(*src & 0x00FF); |
|
|
104 src++; |
|
|
105 } |
|
|
106 *dest = '\0'; |
|
|
107 } |
|
|
108 |
|
|
109 // ---------------------------------------------------------------------------- |
|
|
110 |
|
|
111 static inline void |
|
|
112 __wait_for_ready(int ctlr) |
|
|
113 { |
|
|
114 cyg_uint8 status; |
|
|
115 do { |
|
|
116 HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status); |
|
|
117 } while (status & (IDE_STAT_BSY | IDE_STAT_DRQ)); |
|
|
118 } |
|
|
119 |
|
|
120 static inline int |
|
|
121 __wait_for_drq(int ctlr) |
|
|
122 { |
|
|
123 cyg_uint8 status; |
|
|
124 cyg_ucount32 tries; |
|
|
125 |
|
|
126 CYGACC_CALL_IF_DELAY_US(10); |
|
|
127 for (tries=0; tries<1000000; tries++) { |
|
|
128 HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status); |
|
|
129 if (!(status & IDE_STAT_BSY)) { |
|
|
130 if (status & IDE_STAT_DRQ) |
|
|
131 return 1; |
|
|
132 else |
|
|
133 return 0; |
|
|
134 } |
|
|
135 } |
|
|
136 } |
|
|
137 |
|
|
138 // Return true if any devices attached to controller |
|
|
139 static int |
|
|
140 ide_presence_detect(int ctlr) |
|
|
141 { |
|
|
142 cyg_uint8 sel, val; |
|
|
143 int i; |
|
|
144 |
|
|
145 for (i = 0; i < 2; i++) { |
|
|
146 sel = (i << 4) | 0xA0; |
|
|
147 CYGACC_CALL_IF_DELAY_US((cyg_uint32)50000); |
|
|
148 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE, sel); |
|
|
149 CYGACC_CALL_IF_DELAY_US((cyg_uint32)50000); |
|
|
150 HAL_IDE_READ_UINT8(ctlr, IDE_REG_DEVICE, val); |
|
|
151 if (val == sel) { |
|
|
152 #ifndef CYGSEM_DEVS_DISK_IDE_VMWARE |
|
|
153 if (i) |
|
|
154 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE, 0); |
|
|
155 #endif |
|
|
156 return 1; |
|
|
157 } |
|
|
158 } |
|
|
159 return 0; |
|
|
160 } |
|
|
161 |
|
|
162 static int |
|
|
163 ide_reset(int ctlr) |
|
|
164 { |
|
|
165 cyg_uint8 status; |
|
|
166 int delay; |
|
|
167 // |
|
|
168 // VMware note: |
|
|
169 // VMware virtual IDE device handler obviously expects that |
|
|
170 // the reset and setup functions were already done |
|
|
171 // by it's bios and complains if one uses reset here... |
|
|
172 // |
|
|
173 #ifndef CYGSEM_DEVS_DISK_IDE_VMWARE |
|
|
174 HAL_IDE_WRITE_CONTROL(ctlr, 6); // polled mode, reset asserted |
|
|
175 CYGACC_CALL_IF_DELAY_US(5000); |
|
|
176 HAL_IDE_WRITE_CONTROL(ctlr, 2); // polled mode, reset cleared |
|
|
177 CYGACC_CALL_IF_DELAY_US((cyg_uint32)50000); |
|
|
178 #endif |
|
|
179 |
|
|
180 // wait 30 seconds max for not busy and drive ready |
|
|
181 for (delay = 0; delay < 300; ++delay) { |
|
|
182 CYGACC_CALL_IF_DELAY_US((cyg_uint32)100000); |
|
|
183 HAL_IDE_READ_UINT8(ctlr, IDE_REG_STATUS, status); |
|
|
184 if (!(status & IDE_STAT_BSY)) { |
|
|
185 if (status & IDE_STAT_DRDY) { |
|
|
186 return 1; |
|
|
187 } |
|
|
188 } |
|
|
189 } |
|
|
190 return 0; |
|
|
191 } |
|
|
192 |
|
|
193 static int |
|
|
194 ide_ident(int ctlr, int dev, cyg_uint16 *buf) |
|
|
195 { |
|
|
196 int i; |
|
|
197 |
|
|
198 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE, dev << 4); |
|
|
199 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COMMAND, 0xEC); |
|
|
200 CYGACC_CALL_IF_DELAY_US((cyg_uint32)50000); |
|
|
201 |
|
|
202 if (!__wait_for_drq(ctlr)) |
|
|
203 return 0; |
|
|
204 |
|
|
205 for (i = 0; i < (CYGDAT_DEVS_DISK_IDE_SECTOR_SIZE / sizeof(cyg_uint16)); |
|
|
206 i++, buf++) |
|
|
207 HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, *buf); |
|
|
208 |
|
|
209 return 1; |
|
|
210 } |
|
|
211 |
|
|
212 static int |
|
|
213 ide_read_sector(int ctlr, int dev, cyg_uint32 start, |
|
|
214 cyg_uint8 *buf, cyg_uint32 len) |
|
|
215 { |
|
|
216 int j, c; |
|
|
217 cyg_uint16 p; |
|
|
218 cyg_uint8 * b=buf; |
|
|
219 |
|
|
220 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COUNT, 1); // count =1 |
|
|
221 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBALOW, start & 0xff); |
|
|
222 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAMID, (start >> 8) & 0xff); |
|
|
223 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAHI, (start >> 16) & 0xff); |
|
|
224 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE, |
|
|
225 ((start >> 24) & 0xf) | (dev << 4) | 0x40); |
|
|
226 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COMMAND, 0x20); |
|
|
227 |
|
|
228 if (!__wait_for_drq(ctlr)) |
|
|
229 return 0; |
|
|
230 // |
|
|
231 // It would be fine if all buffers were word aligned, |
|
|
232 // but who knows |
|
|
233 // |
|
|
234 for (j = 0, c=0 ; j < (CYGDAT_DEVS_DISK_IDE_SECTOR_SIZE / sizeof(cyg_uint16)); |
|
|
235 j++) { |
|
|
236 HAL_IDE_READ_UINT16(ctlr, IDE_REG_DATA, p); |
|
|
237 if (c++<len) *b++=p&0xff; |
|
|
238 if (c++<len) *b++=(p>>8)&0xff; |
|
|
239 } |
|
|
240 return 1; |
|
|
241 } |
|
|
242 |
|
|
243 static int |
|
|
244 ide_write_sector(int ctlr, int dev, cyg_uint32 start, |
|
|
245 cyg_uint8 *buf, cyg_uint32 len) |
|
|
246 { |
|
|
247 int j, c; |
|
|
248 cyg_uint16 p; |
|
|
249 cyg_uint8 * b=buf; |
|
|
250 |
|
|
251 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COUNT, 1); // count =1 |
|
|
252 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBALOW, start & 0xff); |
|
|
253 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAMID, (start >> 8) & 0xff); |
|
|
254 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_LBAHI, (start >> 16) & 0xff); |
|
|
255 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_DEVICE, |
|
|
256 ((start >> 24) & 0xf) | (dev << 4) | 0x40); |
|
|
257 HAL_IDE_WRITE_UINT8(ctlr, IDE_REG_COMMAND, 0x30); |
|
|
258 |
|
|
259 if (!__wait_for_drq(ctlr)) |
|
|
260 return 0; |
|
|
261 // |
|
|
262 // It would be fine if all buffers were word aligned, |
|
|
263 // but who knows |
|
|
264 // |
|
|
265 for (j = 0, c=0 ; j < (CYGDAT_DEVS_DISK_IDE_SECTOR_SIZE / sizeof(cyg_uint16)); |
|
|
266 j++) { |
|
|
267 p = (c++<len) ? *b++ : 0; |
|
|
268 p |= (c++<len) ? (*b++<<8) : 0; |
|
|
269 HAL_IDE_WRITE_UINT16(ctlr, IDE_REG_DATA, p); |
|
|
270 } |
|
|
271 return 1; |
|
|
272 } |
|
|
273 |
|
|
274 // ---------------------------------------------------------------------------- |
|
|
275 |
|
|
276 static cyg_bool |
|
|
277 ide_disk_init(struct cyg_devtab_entry *tab) |
|
|
278 { |
|
|
279 disk_channel *chan = (disk_channel *) tab->priv; |
|
|
280 ide_disk_info_t *info = (ide_disk_info_t *) chan->dev_priv; |
|
|
281 cyg_uint32 id_buf[CYGDAT_DEVS_DISK_IDE_SECTOR_SIZE/sizeof(cyg_uint32)]; |
|
|
282 static int num_controllers; |
|
|
283 static int ide_present[4], ide_reset_done[4]; |
|
|
284 cyg_disk_identify_t ident; |
|
|
285 ide_identify_data_t *ide_idData=(ide_identify_data_t*)id_buf; |
|
|
286 |
|
|
287 if (chan->init) |
|
|
288 return true; |
|
|
289 |
|
|
290 D("IDE(%d:%d) hw init\n", info->port, info->chan); |
|
|
291 |
|
|
292 if (!num_controllers) num_controllers=HAL_IDE_INIT(); |
|
|
293 if (info->chan>=num_controllers) { |
|
|
294 D("No IDE controller for channel %d:%d\n", info->port, info->chan); |
|
|
295 return false; |
|
|
296 } |
|
|
297 if (!ide_present[info->port]) { |
|
|
298 ide_present[info->port]=ide_presence_detect(info->port); |
|
|
299 if (!ide_present[info->port]) { |
|
|
300 diag_printf("No devices on IDE controller #%d\n",info->port); |
|
|
301 return false; |
|
|
302 } |
|
|
303 } |
|
|
304 if (!ide_reset_done[info->port]) { |
|
|
305 ide_reset_done[info->port]=ide_reset(info->port); |
|
|
306 if (!ide_reset_done[info->port]) { |
|
|
307 D("Controller #%d reset failure\n",info->port); |
|
|
308 return false; |
|
|
309 } |
|
|
310 } |
|
|
311 |
|
|
312 D("IDE %d:%d identify drive\n", info->port, info->chan); |
|
|
313 |
|
|
314 if (!ide_ident(info->port, info->chan, (cyg_uint16 *)id_buf)) { |
|
|
315 diag_printf("IDE %d:%d ident DRQ error\n", info->port, info->chan); |
|
|
316 return false; |
|
|
317 } |
|
|
318 |
|
|
319 id_strcpy(ident.serial, ide_idData->serial, 20); |
|
|
320 id_strcpy(ident.firmware_rev, ide_idData->firmware_rev, 8); |
|
|
321 id_strcpy(ident.model_num, ide_idData->model_num, 40); |
|
|
322 |
|
|
323 ident.cylinders_num = ide_idData->num_cylinders; |
|
|
324 ident.heads_num = ide_idData->num_heads; |
|
|
325 ident.sectors_num = ide_idData->num_sectors; |
|
|
326 ident.lba_sectors_num = ide_idData->lba_total_sectors[1] << 16 | |
|
|
327 ide_idData->lba_total_sectors[0]; |
|
|
328 |
|
|
329 D("\tSerial : %s\n", ident.serial); |
|
|
330 D("\tFirmware rev. : %s\n", ident.firmware_rev); |
|
|
331 D("\tModel : %s\n", ident.model_num); |
|
|
332 D("\tC/H/S : %d/%d/%d\n", ident.cylinders_num, |
|
|
333 ident.heads_num, ident.sectors_num); |
|
|
334 D("\tKind : %x\n", (ide_idData->general_conf>>8)&0x1f); |
|
|
335 |
|
|
336 if (((ide_idData->general_conf>>8)&0x1f)!=2) { |
|
|
337 diag_printf("IDE device %d:%d is not a hard disk!\n", |
|
|
338 info->port, info->chan); |
|
|
339 return false; |
|
|
340 } |
|
|
341 if (!(chan->callbacks->disk_init)(tab)) |
|
|
342 return false; |
|
|
343 |
|
|
344 if (ENOERR != (chan->callbacks->disk_connected)(tab, &ident)) |
|
|
345 return false; |
|
|
346 |
|
|
347 return true; |
|
|
348 } |
|
|
349 |
|
|
350 // ---------------------------------------------------------------------------- |
|
|
351 |
|
|
352 static Cyg_ErrNo |
|
|
353 ide_disk_lookup(struct cyg_devtab_entry **tab, |
|
|
354 struct cyg_devtab_entry *sub_tab, |
|
|
355 const char *name) |
|
|
356 { |
|
|
357 disk_channel *chan = (disk_channel *) (*tab)->priv; |
|
|
358 return (chan->callbacks->disk_lookup)(tab, sub_tab, name); |
|
|
359 } |
|
|
360 |
|
|
361 // ---------------------------------------------------------------------------- |
|
|
362 |
|
|
363 static Cyg_ErrNo |
|
|
364 ide_disk_read(disk_channel *chan, |
|
|
365 void *buf, |
|
|
366 cyg_uint32 len, |
|
|
367 cyg_uint32 block_num) |
|
|
368 { |
|
|
369 ide_disk_info_t *info = (ide_disk_info_t *)chan->dev_priv; |
|
|
370 |
|
|
371 D("IDE %d:%d read block %d\n", info->port, info->chan, block_num); |
|
|
372 |
|
|
373 if (!ide_read_sector(info->port, info->chan, block_num, |
|
|
374 (cyg_uint8 *)buf, len)) { |
|
|
375 diag_printf("IDE %d:%d read DRQ error\n", info->port, info->chan); |
|
|
376 return -EIO; |
|
|
377 } |
|
|
378 |
|
|
379 return ENOERR; |
|
|
380 } |
|
|
381 |
|
|
382 // ---------------------------------------------------------------------------- |
|
|
383 |
|
|
384 static Cyg_ErrNo |
|
|
385 ide_disk_write(disk_channel *chan, |
|
|
386 const void *buf, |
|
|
387 cyg_uint32 len, |
|
|
388 cyg_uint32 block_num) |
|
|
389 { |
|
|
390 ide_disk_info_t *info = (ide_disk_info_t *)chan->dev_priv; |
|
|
391 |
|
|
392 D("IDE %d:%d write block %d\n", info->port, info->chan, block_num); |
|
|
393 |
|
|
394 if (!ide_write_sector(info->port, info->chan, block_num, |
|
|
395 (cyg_uint8 *)buf, len)) { |
|
|
396 diag_printf("IDE %d:%d read DRQ error\n", info->port, info->chan); |
|
|
397 return -EIO; |
|
|
398 } |
|
|
399 |
|
|
400 return ENOERR; |
|
|
401 } |
|
|
402 |
|
|
403 // ---------------------------------------------------------------------------- |
|
|
404 |
|
|
405 static Cyg_ErrNo |
|
|
406 ide_disk_get_config(disk_channel *chan, |
|
|
407 cyg_uint32 key, |
|
|
408 const void *xbuf, |
|
|
409 cyg_uint32 *len) |
|
|
410 { |
|
|
411 return -EINVAL; |
|
|
412 } |
|
|
413 |
|
|
414 // ---------------------------------------------------------------------------- |
|
|
415 |
|
|
416 static Cyg_ErrNo |
|
|
417 ide_disk_set_config(disk_channel *chan, |
|
|
418 cyg_uint32 key, |
|
|
419 const void *xbuf, |
|
|
420 cyg_uint32 *len) |
|
|
421 { |
|
|
422 return -EINVAL; |
|
|
423 } |
|
|
424 |
|
|
425 //EOF ide_disk.c |