root/kernel/blk_drv/hd.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CMOS_READ
  2. read_timer
  3. controller_ready
  4. win_result
  5. hd_out
  6. drive_busy
  7. reset_controller
  8. reset_hd
  9. unexpected_hd_interrupt
  10. bad_rw_intr
  11. wait_DRQ
  12. read_intr
  13. write_intr
  14. recal_intr
  15. hd_times_out
  16. do_hd_request
  17. hd_ioctl
  18. hd_release
  19. hd_geninit
  20. hd_interrupt
  21. hd_init

   1 /*
   2  *  linux/kernel/hd.c
   3  *
   4  *  Copyright (C) 1991, 1992  Linus Torvalds
   5  */
   6 
   7 /*
   8  * This is the low-level hd interrupt support. It traverses the
   9  * request-list, using interrupts to jump between functions. As
  10  * all the functions are called within interrupts, we may not
  11  * sleep. Special care is recommended.
  12  * 
  13  *  modified by Drew Eckhardt to check nr of hd's from the CMOS.
  14  *
  15  *  Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
  16  *  in the early extended-partition checks and added DM partitions
  17  */
  18 
  19 #include <linux/config.h>
  20 #ifdef CONFIG_BLK_DEV_HD
  21 
  22 #define HD_IRQ 14
  23 
  24 #include <linux/errno.h>
  25 #include <linux/signal.h>
  26 #include <linux/sched.h>
  27 #include <linux/timer.h>
  28 #include <linux/fs.h>
  29 #include <linux/kernel.h>
  30 #include <linux/hdreg.h>
  31 #include <linux/genhd.h>
  32 
  33 #define REALLY_SLOW_IO
  34 #include <asm/system.h>
  35 #include <asm/io.h>
  36 #include <asm/segment.h>
  37 
  38 #define MAJOR_NR 3
  39 #include "blk.h"
  40 
  41 static inline unsigned char CMOS_READ(unsigned char addr)
     /* [previous][next][first][last][top][bottom][index][help] */
  42 {
  43         outb_p(0x80|addr,0x70);
  44         return inb_p(0x71);
  45 }
  46 
  47 #define HD_DELAY        0
  48 
  49 /* Max read/write errors/sector */
  50 #define MAX_ERRORS      7
  51 #define MAX_HD          2
  52 
  53 static void recal_intr(void);
  54 static void bad_rw_intr(void);
  55 
  56 static char recalibrate[ MAX_HD ] = { 0, };
  57 
  58 static int reset = 0;
  59 
  60 #if (HD_DELAY > 0)
  61 unsigned long last_req, read_timer();
  62 #endif
  63 
  64 /*
  65  *  This struct defines the HD's and their types.
  66  */
  67 struct hd_i_struct {
  68         unsigned int head,sect,cyl,wpcom,lzone,ctl;
  69         };
  70 #ifdef HD_TYPE
  71 struct hd_i_struct hd_info[] = { HD_TYPE };
  72 #define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct)))
  73 #else
  74 struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
  75 static int NR_HD = 0;
  76 #endif
  77 
  78 static struct hd_struct hd[MAX_HD<<6]={{0,0},};
  79 static int hd_sizes[MAX_HD<<6] = {0, };
  80 
  81 #define port_read(port,buf,nr) \
  82 __asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
  83 
  84 #define port_write(port,buf,nr) \
  85 __asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
  86 
  87 #if (HD_DELAY > 0)
  88 unsigned long read_timer(void)
     /* [previous][next][first][last][top][bottom][index][help] */
  89 {
  90         unsigned long t;
  91         int i;
  92 
  93         cli();
  94         outb_p(0xc2, 0x43);
  95         t = jiffies * 11931 + (inb_p(0x40) & 0x80 ? 5966 : 11932);
  96         i = inb_p(0x40);
  97         i |= inb(0x40) << 8;
  98         sti();
  99         return(t - i / 2);
 100 }
 101 #endif
 102 
 103 static int controller_ready(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 104 {
 105         int retries = 100000;
 106 
 107         while (--retries && (inb_p(HD_STATUS)&0x80))
 108                 /* nothing */;
 109         if (!retries)
 110                 printk("controller_ready: status = %02x\n\r",
 111                         (unsigned char) inb_p(HD_STATUS));
 112         return (retries);
 113 }
 114 
 115 static int win_result(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 116 {
 117         int i=inb_p(HD_STATUS);
 118 
 119         if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT))
 120                 == (READY_STAT | SEEK_STAT))
 121                 return 0; /* ok */
 122         printk("HD: win_result: status = 0x%02x\n",i);
 123         if (i&1) {
 124                 i=inb(HD_ERROR);
 125                 printk("HD: win_result: error = 0x%02x\n",i);
 126         }       
 127         return 1;
 128 }
 129 
 130 static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
     /* [previous][next][first][last][top][bottom][index][help] */
 131                 unsigned int head,unsigned int cyl,unsigned int cmd,
 132                 void (*intr_addr)(void))
 133 {
 134         unsigned short port;
 135 
 136         if (drive>1 || head>15)
 137                 panic("Trying to write bad sector");
 138 #if (HD_DELAY > 0)
 139         while (read_timer() - last_req < HD_DELAY)
 140                 /* nothing */;
 141 #endif
 142         if (reset || !controller_ready()) {
 143                 reset = 1;
 144                 return;
 145         }
 146         SET_INTR(intr_addr);
 147         outb_p(hd_info[drive].ctl,HD_CMD);
 148         port=HD_DATA;
 149         outb_p(hd_info[drive].wpcom>>2,++port);
 150         outb_p(nsect,++port);
 151         outb_p(sect,++port);
 152         outb_p(cyl,++port);
 153         outb_p(cyl>>8,++port);
 154         outb_p(0xA0|(drive<<4)|head,++port);
 155         outb_p(cmd,++port);
 156 }
 157 
 158 static int drive_busy(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 159 {
 160         unsigned int i;
 161         unsigned char c;
 162 
 163         for (i = 0; i < 500000 ; i++) {
 164                 c = inb_p(HD_STATUS);
 165                 c &= (BUSY_STAT | READY_STAT | SEEK_STAT);
 166                 if (c == (READY_STAT | SEEK_STAT))
 167                         return 0;
 168         }
 169         printk("HD controller times out, status = 0x%02x\n\r",c);
 170         return(1);
 171 }
 172 
 173 static void reset_controller(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 174 {
 175         int     i;
 176 
 177         printk("HD-controller reset\r\n");
 178         outb(4,HD_CMD);
 179         for(i = 0; i < 1000; i++) nop();
 180         outb(hd_info[0].ctl & 0x0f ,HD_CMD);
 181         if (drive_busy())
 182                 printk("HD-controller still busy\n\r");
 183         if ((i = inb(HD_ERROR)) != 1)
 184                 printk("HD-controller reset failed: %02x\n\r",i);
 185 }
 186 
 187 static void reset_hd(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 188 {
 189         static int i;
 190 
 191 repeat:
 192         if (reset) {
 193                 reset = 0;
 194                 i = -1;
 195                 reset_controller();
 196         } else if (win_result()) {
 197                 bad_rw_intr();
 198                 if (reset)
 199                         goto repeat;
 200         }
 201         i++;
 202         if (i < NR_HD) {
 203                 hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
 204                         hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
 205                 if (reset)
 206                         goto repeat;
 207         } else
 208                 do_hd_request();
 209 }
 210 
 211 /*
 212  * Ok, don't know what to do with the unexpected interrupts: on some machines
 213  * doing a reset and a retry seems to result in an eternal loop. Right now I
 214  * ignore it, and just set the timeout.
 215  */
 216 void unexpected_hd_interrupt(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 217 {
 218         sti();
 219         printk("Unexpected HD interrupt\n\r");
 220         SET_TIMER;
 221 }
 222 
 223 static void bad_rw_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 224 {
 225         int i;
 226 
 227         if (!CURRENT)
 228                 return;
 229         if (++CURRENT->errors >= MAX_ERRORS)
 230                 end_request(0);
 231         else if (CURRENT->errors > MAX_ERRORS/2)
 232                 reset = 1;
 233         else
 234                 for (i=0; i < NR_HD; i++)
 235                         recalibrate[i] = 1;
 236 }
 237 
 238 static inline int wait_DRQ(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 239 {
 240         int retries = 100000;
 241 
 242         while (--retries > 0)
 243                 if (inb_p(HD_STATUS) & DRQ_STAT)
 244                         return 0;
 245         return -1;
 246 }
 247 
 248 #define STAT_MASK (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT)
 249 #define STAT_OK (READY_STAT | SEEK_STAT)
 250 
 251 static void read_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 252 {
 253         int i;
 254 
 255         i = (unsigned) inb_p(HD_STATUS);
 256         if ((i & STAT_MASK) != STAT_OK) {
 257                 printk("HD: read_intr: status = 0x%02x\n",i);
 258                 goto bad_read;
 259         }
 260         if (wait_DRQ()) {
 261                 printk("HD: read_intr: no DRQ\n");
 262                 goto bad_read;
 263         }
 264         port_read(HD_DATA,CURRENT->buffer,256);
 265         i = (unsigned) inb_p(HD_STATUS);
 266         if (!(i & BUSY_STAT))
 267                 if ((i & STAT_MASK) != STAT_OK) {
 268                         printk("HD: read_intr: second status = 0x%02x\n",i);
 269                         goto bad_read;
 270                 }
 271         CURRENT->errors = 0;
 272         CURRENT->buffer += 512;
 273         CURRENT->sector++;
 274         i = --CURRENT->nr_sectors;
 275         --CURRENT->current_nr_sectors;
 276 #ifdef DEBUG
 277         printk("hd%d : sector = %d, %d remaining to buffer = %08x\n",
 278                 MINOR(CURRENT->dev), CURRENT->sector, i, CURRENT-> 
 279                 buffer);
 280 #endif
 281         if (!i || (CURRENT->bh && !SUBSECTOR(i)))
 282                 end_request(1);
 283         if (i > 0) {
 284                 SET_INTR(&read_intr);
 285                 sti();
 286                 return;
 287         }
 288 #if (HD_DELAY > 0)
 289         last_req = read_timer();
 290 #endif
 291         do_hd_request();
 292         return;
 293 bad_read:
 294         if (i & ERR_STAT) {
 295                 i = (unsigned) inb(HD_ERROR);
 296                 printk("HD: read_intr: error = 0x%02x\n",i);
 297         }
 298         bad_rw_intr();
 299         do_hd_request();
 300         return;
 301 }
 302 
 303 static void write_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 304 {
 305         int i;
 306 
 307         i = (unsigned) inb_p(HD_STATUS);
 308         if ((i & STAT_MASK) != STAT_OK) {
 309                 printk("HD: write_intr: status = 0x%02x\n",i);
 310                 goto bad_write;
 311         }
 312         if (CURRENT->nr_sectors > 1 && wait_DRQ()) {
 313                 printk("HD: write_intr: no DRQ\n");
 314                 goto bad_write;
 315         }
 316         CURRENT->sector++;
 317         i = --CURRENT->nr_sectors;
 318         --CURRENT->current_nr_sectors;
 319         CURRENT->buffer += 512;
 320         if (!i || (CURRENT->bh && !SUBSECTOR(i)))
 321                 end_request(1);
 322         if (i > 0) {
 323                 SET_INTR(&write_intr);
 324                 port_write(HD_DATA,CURRENT->buffer,256);
 325                 sti();
 326         } else {
 327 #if (HD_DELAY > 0)
 328                 last_req = read_timer();
 329 #endif
 330                 do_hd_request();
 331         }
 332         return;
 333 bad_write:
 334         sti();
 335         if (i & ERR_STAT) {
 336                 i = (unsigned) inb(HD_ERROR);
 337                 printk("HD: write_intr: error = 0x%02x\n",i);
 338         }
 339         bad_rw_intr();
 340         cli();
 341         do_hd_request();
 342         return;
 343 }
 344 
 345 static void recal_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 346 {
 347         if (win_result())
 348                 bad_rw_intr();
 349         do_hd_request();
 350 }
 351 
 352 /*
 353  * This is another of the error-routines I don't know what to do with. The
 354  * best idea seems to just set reset, and start all over again.
 355  */
 356 static void hd_times_out(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 357 {
 358         DEVICE_INTR = NULL;
 359         sti();
 360         reset = 1;
 361         if (!CURRENT)
 362                 return;
 363         printk("HD timeout\n\r");
 364         cli();
 365         if (++CURRENT->errors >= MAX_ERRORS) {
 366 #ifdef DEBUG
 367                 printk("hd : too many errors.\n");
 368 #endif
 369                 end_request(0);
 370         }
 371 
 372         do_hd_request();
 373 }
 374 
 375 /*
 376  * The driver has been modified to enable interrupts a bit more: in order to
 377  * do this we first (a) disable the timeout-interrupt and (b) clear the
 378  * device-interrupt. This way the interrupts won't mess with out code (the
 379  * worst that can happen is that an unexpected HD-interrupt comes in and
 380  * sets the "reset" variable and starts the timer)
 381  */
 382 static void do_hd_request(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 383 {
 384         unsigned int block,dev;
 385         unsigned int sec,head,cyl,track;
 386         unsigned int nsect;
 387 
 388         if (DEVICE_INTR)
 389                 return;
 390 repeat:
 391         timer_active &= ~(1<<HD_TIMER);
 392         sti();
 393         INIT_REQUEST;
 394         dev = MINOR(CURRENT->dev);
 395         block = CURRENT->sector;
 396         nsect = CURRENT->nr_sectors;
 397         if (dev >= (NR_HD<<6) || block >= hd[dev].nr_sects) {
 398 #ifdef DEBUG
 399                 printk("hd%d : attempted read for sector %d past end of device at sector %d.\n",
 400                         block, hd[dev].nr_sects);
 401 #endif
 402                 end_request(0);
 403                 goto repeat;
 404         }
 405         block += hd[dev].start_sect;
 406         dev >>= 6;
 407         sec = block % hd_info[dev].sect + 1;
 408         track = block / hd_info[dev].sect;
 409         head = track % hd_info[dev].head;
 410         cyl = track / hd_info[dev].head;
 411 #ifdef DEBUG
 412         printk("hd%d : cyl = %d, head = %d, sector = %d, buffer = %08x\n",
 413                 dev, cyl, head, sec, CURRENT->buffer);
 414 #endif
 415         cli();
 416         if (reset) {
 417                 int i;
 418 
 419                 for (i=0; i < NR_HD; i++)
 420                         recalibrate[i] = 1;
 421                 reset_hd();
 422                 sti();
 423                 return;
 424         }
 425         if (recalibrate[dev]) {
 426                 recalibrate[dev] = 0;
 427                 hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr);
 428                 if (reset)
 429                         goto repeat;
 430                 sti();
 431                 return;
 432         }       
 433         if (CURRENT->cmd == WRITE) {
 434                 hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
 435                 if (reset)
 436                         goto repeat;
 437                 if (wait_DRQ()) {
 438                         printk("HD: do_hd_request: no DRQ\n");
 439                         bad_rw_intr();
 440                         goto repeat;
 441                 }
 442                 port_write(HD_DATA,CURRENT->buffer,256);
 443                 sti();
 444                 return;
 445         }
 446         if (CURRENT->cmd == READ) {
 447                 hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
 448                 if (reset)
 449                         goto repeat;
 450                 sti();
 451                 return;
 452         }
 453         panic("unknown hd-command");
 454 }
 455 
 456 static int hd_ioctl(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 457         unsigned int cmd, unsigned int arg)
 458 {
 459         struct hd_geometry *loc = (void *) arg;
 460         int dev;
 461 
 462         if (!loc || !inode)
 463                 return -EINVAL;
 464         dev = MINOR(inode->i_rdev) >> 6;
 465         if (dev >= NR_HD)
 466                 return -EINVAL;
 467         switch (cmd) {
 468                 case HDIO_REQ:
 469                         verify_area(loc, sizeof(*loc));
 470                         put_fs_byte(hd_info[dev].head,
 471                                 (char *) &loc->heads);
 472                         put_fs_byte(hd_info[dev].sect,
 473                                 (char *) &loc->sectors);
 474                         put_fs_word(hd_info[dev].cyl,
 475                                 (short *) &loc->cylinders);
 476                         put_fs_long(hd[MINOR(inode->i_rdev)].start_sect,
 477                                 (long *) &loc->start);
 478                         return 0;
 479                 RO_IOCTLS(inode->i_rdev,arg);
 480                 default:
 481                         return -EINVAL;
 482         }
 483 }
 484 
 485 /*
 486  * Releasing a block device means we sync() it, so that it can safely
 487  * be forgotten about...
 488  */
 489 static void hd_release(struct inode * inode, struct file * file)
     /* [previous][next][first][last][top][bottom][index][help] */
 490 {
 491         sync_dev(inode->i_rdev);
 492 }
 493 
 494 static void hd_geninit();
 495 
 496 static struct gendisk hd_gendisk = {
 497         MAJOR_NR,       /* Major number */      
 498         "hd",           /* Major name */
 499         6,              /* Bits to shift to get real from partition */
 500         1 << 6,         /* Number of partitions per real */
 501         MAX_HD,         /* maximum number of real */
 502         hd_geninit,     /* init function */
 503         hd,             /* hd struct */
 504         hd_sizes,       /* block sizes */
 505         0,              /* number */
 506         (void *) hd_info,       /* internal */
 507         NULL            /* next */
 508 };
 509         
 510 static void hd_geninit(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 511 {
 512         int drive, i;
 513 #ifndef HD_TYPE
 514         extern struct drive_info drive_info;
 515         void *BIOS = (void *) &drive_info;
 516         int cmos_disks;
 517            
 518         for (drive=0 ; drive<2 ; drive++) {
 519                 hd_info[drive].cyl = *(unsigned short *) BIOS;
 520                 hd_info[drive].head = *(unsigned char *) (2+BIOS);
 521                 hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
 522                 hd_info[drive].ctl = *(unsigned char *) (8+BIOS);
 523                 hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
 524                 hd_info[drive].sect = *(unsigned char *) (14+BIOS);
 525                 BIOS += 16;
 526         }
 527 
 528         /*
 529                 We querry CMOS about hard disks : it could be that 
 530                 we have a SCSI/ESDI/etc controller that is BIOS
 531                 compatable with ST-506, and thus showing up in our
 532                 BIOS table, but not register compatable, and therefore
 533                 not present in CMOS.
 534 
 535                 Furthurmore, we will assume that our ST-506 drives
 536                 <if any> are the primary drives in the system, and 
 537                 the ones reflected as drive 1 or 2.
 538 
 539                 The first drive is stored in the high nibble of CMOS
 540                 byte 0x12, the second in the low nibble.  This will be
 541                 either a 4 bit drive type or 0xf indicating use byte 0x19 
 542                 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.
 543 
 544                 Needless to say, a non-zero value means we have 
 545                 an AT controller hard disk for that drive.
 546 
 547                 
 548         */
 549 
 550         if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
 551                 if (cmos_disks & 0x0f)
 552                         NR_HD = 2;
 553                 else
 554                         NR_HD = 1;
 555         else
 556                 NR_HD = 0;
 557 #endif
 558 
 559         for (i = 0 ; i < NR_HD ; i++)
 560                 hd[i<<6].nr_sects = hd_info[i].head*
 561                                 hd_info[i].sect*hd_info[i].cyl;
 562 
 563         hd_gendisk.nr_real = NR_HD;
 564 }
 565 
 566 static struct file_operations hd_fops = {
 567         NULL,                   /* lseek - default */
 568         block_read,             /* read - general block-dev read */
 569         block_write,            /* write - general block-dev write */
 570         NULL,                   /* readdir - bad */
 571         NULL,                   /* select */
 572         hd_ioctl,               /* ioctl */
 573         NULL,                   /* no special open code */
 574         hd_release              /* release */
 575 };
 576 
 577 static void hd_interrupt(int unused)
     /* [previous][next][first][last][top][bottom][index][help] */
 578 {
 579         void (*handler)(void) = DEVICE_INTR;
 580 
 581         DEVICE_INTR = NULL;
 582         timer_active &= ~(1<<HD_TIMER);
 583         if (!handler)
 584                 handler = unexpected_hd_interrupt;
 585         handler();
 586         sti();
 587 }
 588 
 589 /*
 590  * This is the harddisk IRQ descruption. The SA_INTERRUPT in sa_flags
 591  * means we run the IRQ-handler with interrupts disabled: this is bad for
 592  * interrupt latency, but anything else has led to problems on some
 593  * machines...
 594  *
 595  * We enable interrupts in some of the routines after making sure it's
 596  * safe.
 597  */
 598 static struct sigaction hd_sigaction = {
 599         hd_interrupt,
 600         0,
 601         SA_INTERRUPT,
 602         NULL
 603 };
 604 
 605 unsigned long hd_init(unsigned long mem_start, unsigned long mem_end)
     /* [previous][next][first][last][top][bottom][index][help] */
 606 {
 607         blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
 608         blkdev_fops[MAJOR_NR] = &hd_fops;
 609         hd_gendisk.next = gendisk_head;
 610         gendisk_head = &hd_gendisk;
 611         if (irqaction(HD_IRQ,&hd_sigaction))
 612                 printk("Unable to get IRQ%d for the harddisk driver\n",HD_IRQ);
 613         timer_table[HD_TIMER].fn = hd_times_out;
 614         return mem_start;
 615 }
 616 
 617 #endif

/* [previous][next][first][last][top][bottom][index][help] */