source: rtems/bsps/i386/pc386/ata/ide.c

Last change on this file was 3293a21, checked in by Christian Mauderer <christian.mauderer@…>, on 03/07/22 at 12:53:06

bsps: Automated IMD header file clean up

Use the same form of IMD in all copyright lines

Update #4625.

  • Property mode set to 100644
File size: 22.8 KB
Line 
1/*
2 * RTEMS PC386 IDE harddisc driver
3 *
4 * This file contains the BSP layer for IDE access below the
5 * libchip IDE harddisc driver.
6 * based on a board specific driver from
7 * Eugeny S. Mints, Oktet
8 */
9
10/*
11 * Copyright (c) 2003 IMD Ingenieurbuero fuer Microcomputertechnik
12 * All rights reserved.
13 *
14 * The license and distribution terms for this file may be
15 * found in the file LICENSE in this distribution or at
16 * http://www.rtems.org/license/LICENSE.
17 */
18
19#include <inttypes.h>
20
21#include <rtems.h>
22
23#include <bsp.h>
24#include <libchip/ide_ctrl.h>
25#include <libchip/ide_ctrl_cfg.h>
26#include <libchip/ide_ctrl_io.h>
27
28#define ATA_SECTOR_SIZE (512)
29
30/*
31 * Use during initialisation.
32 */
33extern void Wait_X_ms(unsigned int msecs);
34
35bool pc386_ide_show;
36uint32_t pc386_ide_timeout;
37
38#define PC386_IDE_DEBUG_OUT 0
39
40#if PC386_IDE_DEBUG_OUT
41bool pc386_ide_trace;
42#define pc386_ide_printk if (pc386_ide_trace) printk
43#endif
44
45#define PC386_IDE_PROBE_TIMEOUT    (500)
46#define PC386_IDE_PRESTART_TIMEOUT (1000)
47#define PC386_IDE_TASKING_TIMEOUT  (2000)
48
49/*
50 * Prestart sleep using a calibrated timing loop.
51 */
52static void pc386_ide_prestart_sleep (void)
53{
54  Wait_X_ms (10);
55}
56
57/*
58 * Once tasking has started we use a task sleep.
59 */
60static void pc386_ide_tasking_sleep (void)
61{
62  rtems_task_wake_after (RTEMS_MICROSECONDS_TO_TICKS (10000) ?
63                         RTEMS_MICROSECONDS_TO_TICKS (10000) : 1);
64}
65
66typedef void (*pc386_ide_sleeper)(void);
67
68static void pc386_ide_sleep (pc386_ide_sleeper sleeper)
69{
70  sleeper ();
71}
72
73static void wait(volatile uint32_t loops)
74{
75  while (loops)
76    loops--;
77}
78
79static bool pc386_ide_status_busy (uint32_t          port,
80                                   volatile uint32_t timeout,
81                                   uint8_t*          status_val,
82                                   pc386_ide_sleeper sleeper)
83{
84  volatile uint8_t status;
85  int              polls;
86
87  do
88  {
89    polls = 500;
90    while (polls)
91    {
92      inport_byte (port + IDE_REGISTER_STATUS, status);
93      if ((status & IDE_REGISTER_STATUS_BSY) == 0)
94      {
95        *status_val = status;
96        return true;
97      }
98      polls--;
99    }
100
101    if (timeout)
102    {
103      timeout--;
104      pc386_ide_sleep (sleeper);
105    }
106  }
107  while (timeout);
108
109  *status_val = status;
110  return false;
111}
112
113static bool pc386_ide_status_data_ready (uint32_t          port,
114                                         volatile uint32_t timeout,
115                                         uint8_t*          status_val,
116                                         pc386_ide_sleeper sleeper)
117{
118  volatile uint8_t status;
119  int              polls;
120
121  do
122  {
123    polls = 1000;
124    while (polls)
125    {
126      inport_byte (port + IDE_REGISTER_STATUS, status);
127
128      if (((status & IDE_REGISTER_STATUS_BSY) == 0) &&
129          (status & IDE_REGISTER_STATUS_DRQ))
130      {
131        *status_val = status;
132        return true;
133      }
134
135      polls--;
136    }
137
138    if (timeout)
139    {
140      timeout--;
141      pc386_ide_sleep (sleeper);
142    }
143  }
144  while (timeout);
145
146  *status_val = status;
147  return false;
148}
149
150/*
151 * support functions for IDE harddisk IF
152 */
153/*=========================================================================*\
154| Function:                                                                 |
155\*-------------------------------------------------------------------------*/
156static bool pc386_ide_probe
157(
158/*-------------------------------------------------------------------------*\
159| Purpose:                                                                  |
160|  This function should probe, whether a IDE disk is available              |
161+---------------------------------------------------------------------------+
162| Input Parameters:                                                         |
163\*-------------------------------------------------------------------------*/
164 int minor
165 )
166/*-------------------------------------------------------------------------*\
167| Return Value:                                                             |
168|    true, when flash disk available                                        |
169\*=========================================================================*/
170{
171  bool ide_card_plugged = true; /* assume: we have a disk here */
172
173  return ide_card_plugged;
174}
175
176/*=========================================================================*\
177| Function:                                                                 |
178\*-------------------------------------------------------------------------*/
179static void pc386_ide_initialize
180(
181/*-------------------------------------------------------------------------*\
182  | Purpose:                                                                  |
183  |  initialize IDE access                                                    |
184  +---------------------------------------------------------------------------+
185  | Input Parameters:                                                         |
186  \*-------------------------------------------------------------------------*/
187  int  minor                              /* controller minor number       */
188 )
189/*-------------------------------------------------------------------------*\
190  | Return Value:                                                             |
191  |    <none>                                                                 |
192  \*=========================================================================*/
193{
194  uint32_t port = IDE_Controller_Table[minor].port1;
195  uint8_t  dev = 0;
196
197  if (pc386_ide_show)
198    printk("IDE%d: port base: %04" PRIu32 "\n", minor, port);
199
200  outport_byte(port+IDE_REGISTER_DEVICE_HEAD,
201               (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS) | 0xE0);
202  wait(10000);
203  outport_byte(port+IDE_REGISTER_DEVICE_CONTROL,
204               IDE_REGISTER_DEVICE_CONTROL_SRST | IDE_REGISTER_DEVICE_CONTROL_nIEN);
205  wait(10000);
206  outport_byte(port+IDE_REGISTER_DEVICE_CONTROL,
207               IDE_REGISTER_DEVICE_CONTROL_nIEN);
208  wait(10000);
209
210  for (dev = 0; dev < 2; dev++)
211  {
212    uint16_t    capabilities = 0;
213    uint32_t    byte;
214    uint8_t     status;
215    uint8_t     error;
216    uint8_t     cyllsb;
217    uint8_t     cylmsb;
218    const char* label = dev ? " slave" : "master";
219    int         max_multiple_sectors = 0;
220    int         cur_multiple_sectors = 0;
221    uint32_t    cylinders = 0;
222    uint32_t    heads = 0;
223    uint32_t    sectors = 0;
224    uint32_t    lba_sectors = 0;
225    char        model_number[41];
226    char*       p = &model_number[0];
227    bool        data_ready;
228
229    (void) cur_multiple_sectors; /* avoid set but not used warning */
230
231    memset(model_number, 0, sizeof(model_number));
232
233    outport_byte(port+IDE_REGISTER_DEVICE_HEAD,
234                 (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS) | 0xE0);
235    /*
236      outport_byte(port+IDE_REGISTER_SECTOR_NUMBER,
237      (dev << IDE_REGISTER_DEVICE_HEAD_DEV_POS) | IDE_REGISTER_LBA3_L);
238    */
239
240    outport_byte(port+IDE_REGISTER_COMMAND, 0x00);
241
242    if (!pc386_ide_status_busy (port, PC386_IDE_PROBE_TIMEOUT,
243                                &status, pc386_ide_prestart_sleep))
244      continue;
245
246    inport_byte(port+IDE_REGISTER_STATUS,        status);
247    inport_byte(port+IDE_REGISTER_ERROR,         error);
248    inport_byte(port+IDE_REGISTER_CYLINDER_LOW,  cyllsb);
249    inport_byte(port+IDE_REGISTER_CYLINDER_HIGH, cylmsb);
250
251    if (pc386_ide_show)
252    {
253      printk("IDE%d:%s: status=%02x\n", minor, label, status);
254      printk("IDE%d:%s: error=%02x\n", minor, label, error);
255      printk("IDE%d:%s: cylinder-low=%02x\n", minor, label, cyllsb);
256      printk("IDE%d:%s: cylinder-high=%02x\n", minor, label, cylmsb);
257    }
258
259    outport_byte(port+IDE_REGISTER_COMMAND, 0xec);
260
261    if (!pc386_ide_status_busy (port, PC386_IDE_PRESTART_TIMEOUT,
262                                &status, pc386_ide_prestart_sleep))
263    {
264      if (pc386_ide_show)
265        printk("IDE%d:%s: device busy: %02x\n", minor, label, status);
266      continue;
267    }
268
269    data_ready = pc386_ide_status_data_ready (port,
270                                              250,
271                                              &status,
272                                              pc386_ide_prestart_sleep);
273
274    if (status & IDE_REGISTER_STATUS_ERR)
275    {
276      inport_byte(port+IDE_REGISTER_ERROR, error);
277      if (error != 4)
278      {
279        if (pc386_ide_show)
280          printk("IDE%d:%s: error=%04x\n", minor, label, error);
281        continue;
282      }
283      /*
284       * The device is an ATAPI device.
285       */
286      outport_byte(port+IDE_REGISTER_COMMAND, 0xa1);
287      data_ready = pc386_ide_status_data_ready (port,
288                                                250,
289                                                &status,
290                                                pc386_ide_prestart_sleep);
291    }
292
293    if (!data_ready)
294      continue;
295
296    byte = 0;
297    while (byte < 512)
298    {
299      uint16_t word;
300
301      if (pc386_ide_show && ((byte % 16) == 0))
302        printk("\n %04" PRIx32 " : ", byte);
303
304      inport_word(port+IDE_REGISTER_DATA, word);
305
306      if (pc386_ide_show)
307        printk ("%04x ", word);
308
309      if (byte == 2)
310        cylinders = word;
311      if (byte == 6)
312        heads = word;
313      if (byte == 12)
314        sectors = word;
315
316      if (byte >= 54 && byte < (54 + 40))
317      {
318        *p = word >> 8;
319        p++;
320        *p = word;
321        p++;
322      }
323
324      if (byte == (47 * 2))
325        max_multiple_sectors = word & 0xff;
326
327      if (byte == (49 * 2))
328        capabilities = word;
329
330      if (byte == (59 * 2))
331      {
332        if (word & (1 << 8))
333          cur_multiple_sectors = word & 0xff;
334      }
335
336      if (byte == (60 * 2))
337        lba_sectors = word;
338      if (byte == (61 * 2))
339        lba_sectors |= word << 16;
340
341      byte += 2;
342    }
343
344    if (pc386_ide_show)
345      printk("\nbytes read = %" PRIu32 "\n", byte);
346
347    if (p != &model_number[0])
348    {
349      uint32_t size;
350      uint32_t left;
351      uint32_t right;
352      char     units;
353
354      if (capabilities & (1 << 9))
355        size = lba_sectors;
356      else
357        size = cylinders * heads * sectors;
358
359      size /= 2;
360
361      if (size > (1024 * 1024))
362      {
363        size = (size * 10) / (1000 * 1000);
364        units = 'G';
365      }
366      else if (size > 1024)
367      {
368        size = (size * 10) / 1000;
369        units = 'M';
370      }
371      else
372      {
373        size = size * 10;
374        units = 'K';
375      }
376
377      left = size / 10;
378      right = size % 10;
379
380      p--;
381      while (*p == ' ')
382      {
383        *p = '\0';
384        p--;
385      }
386
387      printk("IDE%d:%s:%s, %" PRIu32 ".%" PRIu32 "%c (%" PRIu32 "/%" PRIu32 "/%" PRIu32 "), max blk size:%d\n",
388             minor, label, model_number, left, right, units,
389             heads, cylinders, sectors, max_multiple_sectors * 512);
390    }
391
392#if IDE_CLEAR_MULTI_SECTOR_COUNT
393    if (max_multiple_sectors)
394    {
395      outport_byte(port+IDE_REGISTER_SECTOR_COUNT, 0);
396      outport_byte(port+IDE_REGISTER_COMMAND, 0xc6);
397
398      if (!pc386_ide_status_busy (port, PC386_IDE_PRESTART_TIMEOUT,
399                                  &status, pc386_ide_prestart_sleep))
400      {
401        if (pc386_ide_show)
402          printk("IDE%d:%s: device busy: %02x\n", minor, label, status);
403        continue;
404      }
405
406      inport_byte(port+IDE_REGISTER_STATUS, status);
407      if (status & IDE_REGISTER_STATUS_ERR)
408      {
409        inport_byte(port+IDE_REGISTER_ERROR, error);
410        if (error & IDE_REGISTER_ERROR_ABRT)
411          printk("IDE%d:%s: disable multiple failed\n", minor, label);
412        else
413          printk("IDE%d:%s: unknown error on disable multiple: %02x\n",
414                 minor, label, error);
415      }
416    }
417#endif
418
419    outport_byte(port+IDE_REGISTER_DEVICE_CONTROL,
420                 IDE_REGISTER_DEVICE_CONTROL_nIEN);
421    wait(10000);
422  }
423
424  pc386_ide_timeout = PC386_IDE_TASKING_TIMEOUT;
425
426  /*
427   * FIXME: enable interrupts, if needed
428   */
429}
430
431/*=========================================================================*\
432| Function:                                                                 |
433\*-------------------------------------------------------------------------*/
434static void pc386_ide_read_reg
435(
436/*-------------------------------------------------------------------------*\
437| Purpose:                                                                  |
438|  read a IDE controller register                                           |
439+---------------------------------------------------------------------------+
440| Input Parameters:                                                         |
441\*-------------------------------------------------------------------------*/
442 int                        minor,  /* controller minor number       */
443 int                        reg,    /* register index to access      */
444 uint16_t                  *value   /* ptr to return value location  */
445 )
446/*-------------------------------------------------------------------------*\
447| Return Value:                                                             |
448|    <none>                                                                 |
449\*=========================================================================*/
450{
451  uint32_t    port = IDE_Controller_Table[minor].port1;
452  uint8_t   bval1,bval2;
453
454  if (reg == IDE_REGISTER_DATA_WORD) {
455    inport_byte(port+reg, bval1);
456    inport_byte(port+reg+1, bval2);
457    *value = bval1 + (bval2 << 8);
458  }
459  else {
460    inport_byte(port+reg, bval1);
461    *value = bval1;
462  }
463#if PC386_IDE_DEBUG_OUT
464  pc386_ide_printk("pc386_ide_read_reg (0x%x)=0x%x\r\n",reg,*value & 0xff);
465#endif
466}
467
468/*=========================================================================*\
469| Function:                                                                 |
470\*-------------------------------------------------------------------------*/
471static void pc386_ide_write_reg
472(
473/*-------------------------------------------------------------------------*\
474| Purpose:                                                                  |
475|  write a IDE controller register                                          |
476+---------------------------------------------------------------------------+
477| Input Parameters:                                                         |
478\*-------------------------------------------------------------------------*/
479 int                        minor,  /* controller minor number       */
480 int                        reg,    /* register index to access      */
481 uint16_t                   value   /* value to write                */
482 )
483/*-------------------------------------------------------------------------*\
484| Return Value:                                                             |
485|    <none>                                                                 |
486\*=========================================================================*/
487{
488  uint32_t    port = IDE_Controller_Table[minor].port1;
489
490#if PC386_IDE_DEBUG_OUT
491  pc386_ide_printk("pc386_ide_write_reg(0x%x,0x%x)\r\n",reg,value & 0xff);
492#endif
493  if (reg == IDE_REGISTER_DATA_WORD) {
494    outport_word(port+reg,value);
495  }
496  else {
497    outport_byte(port+reg,value);
498  }
499}
500
501/*=========================================================================*\
502| Function:                                                                 |
503\*-------------------------------------------------------------------------*/
504static void pc386_ide_read_block
505(
506/*-------------------------------------------------------------------------*\
507| Purpose:                                                                  |
508|  read a IDE controller data block                                         |
509+---------------------------------------------------------------------------+
510| Input Parameters:                                                         |
511\*-------------------------------------------------------------------------*/
512 int                     minor,
513 uint32_t                block_size,
514 rtems_blkdev_sg_buffer *bufs,
515 uint32_t               *cbuf,
516 uint32_t               *pos
517 )
518/*-------------------------------------------------------------------------*\
519| Return Value:                                                             |
520|    <none>                                                                 |
521\*=========================================================================*/
522{
523  uint32_t port = IDE_Controller_Table[minor].port1;
524  uint32_t cnt = 0;
525#if PC386_IDE_DEBUG_OUT
526  int i32 = 0;
527  pc386_ide_printk("pc386_ide_read_block(bs=%u,bn=%u,bl=%u,cb=%d,p=%d)\n",
528                   block_size, bufs[(*cbuf)].block, llength, *cbuf, *pos);
529#endif
530
531  while (cnt < block_size)
532  {
533    uint16_t *lbuf;
534    uint8_t  status_val;
535    int      b;
536
537    if (!pc386_ide_status_data_ready (port, pc386_ide_timeout,
538                                      &status_val, pc386_ide_tasking_sleep))
539    {
540      printk ("pc386_ide_read_block: block=%" PRIu32 \
541              " cbuf=%" PRIu32 " status=%02x, cnt=%" PRIu32 " bs=%" PRIu32 "\n",
542              bufs[*cbuf].block, *cbuf, status_val, cnt, block_size);
543      /* FIXME: add an error here. */
544      return;
545    }
546
547    if (status_val & IDE_REGISTER_STATUS_ERR)
548    {
549      inport_byte(port+IDE_REGISTER_ERROR, status_val);
550      printk("pc386_ide_read_block: error: %02x\n", status_val);
551      return;
552    }
553
554    lbuf = (uint16_t*)((uint8_t*)(bufs[(*cbuf)].buffer) + (*pos));
555
556    for (b = 0; b < (ATA_SECTOR_SIZE / 2); b++)
557    {
558      inport_word(port+IDE_REGISTER_DATA,*lbuf);
559
560#if PC386_IDE_DEBUG_OUT
561      pc386_ide_printk("%04x ",*lbuf);
562      i32++;
563      if (i32 >= 16)
564      {
565        pc386_ide_printk("\n");
566        i32 = 0;
567      }
568#endif
569      lbuf++;
570    }
571    cnt    += ATA_SECTOR_SIZE;
572    (*pos) += ATA_SECTOR_SIZE;
573    if ((*pos) == bufs[(*cbuf)].length) {
574      (*pos) = 0;
575      (*cbuf)++;
576      lbuf = bufs[(*cbuf)].buffer;
577    }
578  }
579}
580
581/*=========================================================================*\
582| Function:                                                                 |
583\*-------------------------------------------------------------------------*/
584static void pc386_ide_write_block
585(
586/*-------------------------------------------------------------------------*\
587| Purpose:                                                                  |
588|  write a IDE controller data block                                        |
589+---------------------------------------------------------------------------+
590| Input Parameters:                                                         |
591\*-------------------------------------------------------------------------*/
592 int minor,
593 uint32_t                block_size,
594 rtems_blkdev_sg_buffer *bufs,
595 uint32_t               *cbuf,
596 uint32_t               *pos
597 )
598/*-------------------------------------------------------------------------*\
599| Return Value:                                                             |
600|    <none>                                                                 |
601\*=========================================================================*/
602{
603  uint32_t port = IDE_Controller_Table[minor].port1;
604  uint32_t cnt = 0;
605#if PC386_IDE_DEBUG_OUT
606  int i32 = 0;
607  pc386_ide_printk("pc386_ide_write_block(bs=%u,bn=%u,bl=%u,cb=%d,p=%d)\n",
608                   block_size, bufs[(*cbuf)].block, llength, *cbuf, *pos);
609#endif
610
611  while (cnt < block_size)
612  {
613    uint16_t *lbuf;
614    uint8_t  status_val;
615    int      b;
616
617    if (!pc386_ide_status_data_ready (port, pc386_ide_timeout,
618                                      &status_val, pc386_ide_tasking_sleep))
619    {
620      printk ("pc386_ide_write_block: block=%" PRIu32 " status=%02x, cnt=%" PRIu32 " bs=%" PRIu32 "\n",
621              bufs[*cbuf].block, status_val, cnt, block_size);
622      /* FIXME: add an error here. */
623      return;
624    }
625
626    if (status_val & IDE_REGISTER_STATUS_ERR)
627    {
628      inport_byte(port+IDE_REGISTER_ERROR, status_val);
629      printk ("pc386_ide_write_block: error: %02x\n", status_val);
630      return;
631    }
632
633    lbuf = (uint16_t*)(((uint8_t*)bufs[*cbuf].buffer) + (*pos));
634
635    for (b = 0; b < (ATA_SECTOR_SIZE / 2); b++)
636    {
637#if PC386_IDE_DEBUG_OUT
638      pc386_ide_printk("%04x ",*lbuf);
639      i32++;
640      if (i32 >= 16)
641      {
642        pc386_ide_printk("\n");
643        i32 = 0;
644      }
645#endif
646      outport_word(port+IDE_REGISTER_DATA,*lbuf);
647      lbuf++;
648    }
649    cnt    += ATA_SECTOR_SIZE;
650    (*pos) += ATA_SECTOR_SIZE;
651    if ((*pos) == bufs[(*cbuf)].length) {
652      (*pos) = 0;
653      (*cbuf)++;
654      lbuf = bufs[(*cbuf)].buffer;
655    }
656  }
657}
658
659/*=========================================================================*\
660| Function:                                                                 |
661\*-------------------------------------------------------------------------*/
662static int pc386_ide_control
663(
664/*-------------------------------------------------------------------------*\
665| Purpose:                                                                  |
666|  control interface for controller                                         |
667+---------------------------------------------------------------------------+
668| Input Parameters:                                                         |
669\*-------------------------------------------------------------------------*/
670 int  minor,                        /* controller minor number       */
671 uint32_t   cmd,                    /* command to send               */
672 void * arg                         /* optional argument             */
673 )
674/*-------------------------------------------------------------------------*\
675| Return Value:                                                             |
676|    <none>                                                                 |
677\*=========================================================================*/
678{
679  return 0;
680}
681
682/*=========================================================================*\
683| Function:                                                                 |
684\*-------------------------------------------------------------------------*/
685static rtems_status_code pc386_ide_config_io_speed
686(
687/*-------------------------------------------------------------------------*\
688| Purpose:                                                                  |
689|  set up transfer speed, if possible                                       |
690+---------------------------------------------------------------------------+
691| Input Parameters:                                                         |
692\*-------------------------------------------------------------------------*/
693 int        minor,                   /* controller minor number       */
694 uint16_t   modes_avail              /* optional argument             */
695 )
696/*-------------------------------------------------------------------------*\
697| Return Value:                                                             |
698|    rtems_status_code                                                      |
699\*=========================================================================*/
700{
701  return RTEMS_SUCCESSFUL;
702}
703
704/*
705 * The following table configures the functions used for IDE drivers
706 * in this BSP.
707 */
708
709ide_ctrl_fns_t pc386_ide_ctrl_fns = {
710  pc386_ide_probe,
711  pc386_ide_initialize,
712  pc386_ide_control,
713  pc386_ide_read_reg,
714  pc386_ide_write_reg,
715  pc386_ide_read_block,
716  pc386_ide_write_block,
717  pc386_ide_config_io_speed
718};
Note: See TracBrowser for help on using the repository browser.