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. check_partition
  3. sys_setup
  4. controller_ready
  5. win_result
  6. hd_out
  7. drive_busy
  8. reset_controller
  9. reset_hd
  10. unexpected_hd_interrupt
  11. bad_rw_intr
  12. read_intr
  13. write_intr
  14. recal_intr
  15. hd_times_out
  16. do_hd_request
  17. hd_init

   1 /*
   2  *  linux/kernel/hd.c
   3  *
   4  *  (C) 1991  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 #include <linux/sched.h>
  21 #include <linux/timer.h>
  22 #include <linux/fs.h>
  23 #include <linux/kernel.h>
  24 #include <linux/hdreg.h>
  25 #include <asm/system.h>
  26 #include <asm/io.h>
  27 #include <asm/segment.h>
  28 
  29 #define MAJOR_NR 3
  30 #include "blk.h"
  31 
  32 static inline unsigned char CMOS_READ(unsigned char addr)
     /* [previous][next][first][last][top][bottom][index][help] */
  33 {
  34         outb_p(0x80|addr,0x70);
  35         return inb_p(0x71);
  36 }
  37 
  38 /* Max read/write errors/sector */
  39 #define MAX_ERRORS      7
  40 #define MAX_HD          2
  41 
  42 static void recal_intr(void);
  43 static void bad_rw_intr(void);
  44 
  45 static int recalibrate = 0;
  46 static int reset = 1;
  47 
  48 /*
  49  *  This struct defines the HD's and their types.
  50  */
  51 struct hd_i_struct {
  52         unsigned int head,sect,cyl,wpcom,lzone,ctl;
  53         };
  54 #ifdef HD_TYPE
  55 struct hd_i_struct hd_info[] = { HD_TYPE };
  56 #define NR_HD ((sizeof (hd_info))/(sizeof (struct hd_i_struct)))
  57 #else
  58 struct hd_i_struct hd_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };
  59 static int NR_HD = 0;
  60 #endif
  61 
  62 static struct hd_struct {
  63         long start_sect;
  64         long nr_sects;
  65 } hd[MAX_HD<<6]={{0,0},};
  66 
  67 static int hd_sizes[MAX_HD<<6] = {0, };
  68 
  69 #define port_read(port,buf,nr) \
  70 __asm__("cld;rep;insw"::"d" (port),"D" (buf),"c" (nr):"cx","di")
  71 
  72 #define port_write(port,buf,nr) \
  73 __asm__("cld;rep;outsw"::"d" (port),"S" (buf),"c" (nr):"cx","si")
  74 
  75 extern void hd_interrupt(void);
  76 extern void rd_load(void);
  77 
  78 static unsigned int current_minor;
  79 
  80 static void check_partition(unsigned int dev)
     /* [previous][next][first][last][top][bottom][index][help] */
  81 {
  82         int minor, i;
  83         struct buffer_head *bh;
  84         struct partition *p;
  85 
  86         if (!(bh = bread(dev,0))) {
  87                 printk("Unable to read partition table of device %04x\n",dev);
  88                 return;
  89         }
  90         minor = current_minor;
  91         if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
  92                 p = 0x1BE + (void *)bh->b_data;
  93                 for (i=0 ; i<4 ; i++,p++) {
  94                         if (!(hd[i+minor].nr_sects = p->nr_sects))
  95                                 continue;
  96                         hd[i+minor].start_sect = p->start_sect;
  97                         if ((current_minor & 0x3f) >= 60)
  98                                 continue;
  99                         if (p->sys_ind == EXTENDED_PARTITION) {
 100                                 current_minor += 4;
 101                                 check_partition(0x0300 | (i+minor));
 102                         }
 103                 }
 104                 /*
 105                  * check for Disk Manager partition table
 106                  */
 107                 if (*(unsigned short *) (bh->b_data+0xfc) == 0x55AA) {
 108                         p = 0x1BE + (void *)bh->b_data;
 109                         for (i=4; i<16; i++) {
 110                                 p--;
 111                                 if ((current_minor & 0x3f) >= 60)
 112                                         break;
 113                                 if (!(hd[current_minor+4].start_sect = p->start_sect))
 114                                         continue;
 115                                 hd[current_minor+4].nr_sects = p->nr_sects;
 116                                 current_minor++;
 117                         }
 118                 }
 119         } else
 120                 printk("Bad partition table on dev %04x\n",dev);
 121         brelse(bh);
 122 }
 123 
 124 /* This may be used only once, enforced by 'static int callable' */
 125 int sys_setup(void * BIOS)
     /* [previous][next][first][last][top][bottom][index][help] */
 126 {
 127         static int callable = 1;
 128         int i,drive;
 129         unsigned char cmos_disks;
 130 
 131         if (!callable)
 132                 return -1;
 133         callable = 0;
 134 #ifndef HD_TYPE
 135         for (drive=0 ; drive<2 ; drive++) {
 136                 hd_info[drive].cyl = *(unsigned short *) BIOS;
 137                 hd_info[drive].head = *(unsigned char *) (2+BIOS);
 138                 hd_info[drive].wpcom = *(unsigned short *) (5+BIOS);
 139                 hd_info[drive].ctl = *(unsigned char *) (8+BIOS);
 140                 hd_info[drive].lzone = *(unsigned short *) (12+BIOS);
 141                 hd_info[drive].sect = *(unsigned char *) (14+BIOS);
 142                 BIOS += 16;
 143         }
 144         if (hd_info[1].cyl)
 145                 NR_HD=2;
 146         else
 147                 NR_HD=1;
 148 #endif
 149         for (i=0 ; i<NR_HD ; i++) {
 150                 hd[i<<6].start_sect = 0;
 151                 hd[i<<6].nr_sects = hd_info[i].head*
 152                                 hd_info[i].sect*hd_info[i].cyl;
 153         }
 154 
 155         /*
 156                 We querry CMOS about hard disks : it could be that 
 157                 we have a SCSI/ESDI/etc controller that is BIOS
 158                 compatable with ST-506, and thus showing up in our
 159                 BIOS table, but not register compatable, and therefore
 160                 not present in CMOS.
 161 
 162                 Furthurmore, we will assume that our ST-506 drives
 163                 <if any> are the primary drives in the system, and 
 164                 the ones reflected as drive 1 or 2.
 165 
 166                 The first drive is stored in the high nibble of CMOS
 167                 byte 0x12, the second in the low nibble.  This will be
 168                 either a 4 bit drive type or 0xf indicating use byte 0x19 
 169                 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.
 170 
 171                 Needless to say, a non-zero value means we have 
 172                 an AT controller hard disk for that drive.
 173 
 174                 
 175         */
 176 
 177         if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
 178                 if (cmos_disks & 0x0f)
 179                         NR_HD = 2;
 180                 else
 181                         NR_HD = 1;
 182         else
 183                 NR_HD = 0;
 184         for (i = NR_HD ; i < 2 ; i++) {
 185                 hd[i<<6].start_sect = 0;
 186                 hd[i<<6].nr_sects = 0;
 187         }
 188         for (drive=0 ; drive<NR_HD ; drive++) {
 189                 current_minor = 1+(drive<<6);
 190                 check_partition(0x0300+(drive<<6));
 191         }
 192         for (i=0 ; i<(MAX_HD<<6) ; i++)
 193                 hd_sizes[i] = hd[i].nr_sects>>1 ;
 194         blk_size[MAJOR_NR] = hd_sizes;
 195         if (NR_HD)
 196                 printk("Partition table%s ok.\n\r",(NR_HD>1)?"s":"");
 197         rd_load();
 198         mount_root();
 199         return (0);
 200 }
 201 
 202 static int controller_ready(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 203 {
 204         int retries = 100000;
 205 
 206         while (--retries && (inb_p(HD_STATUS)&0x80))
 207                 /* nothing */;
 208         if (!retries)
 209                 printk("controller_ready: status = %02x\n\r",
 210                         (unsigned char) inb_p(HD_STATUS));
 211         return (retries);
 212 }
 213 
 214 static int win_result(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 215 {
 216         int i=inb_p(HD_STATUS);
 217 
 218         if ((i & (BUSY_STAT | READY_STAT | WRERR_STAT | SEEK_STAT | ERR_STAT))
 219                 == (READY_STAT | SEEK_STAT))
 220                 return(0); /* ok */
 221         if (i&1)
 222                 i=inb(HD_ERROR);
 223         return (1);
 224 }
 225 
 226 static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect,
     /* [previous][next][first][last][top][bottom][index][help] */
 227                 unsigned int head,unsigned int cyl,unsigned int cmd,
 228                 void (*intr_addr)(void))
 229 {
 230         unsigned short port;
 231 
 232         if (drive>1 || head>15)
 233                 panic("Trying to write bad sector");
 234         if (reset || !controller_ready()) {
 235                 reset = 1;
 236                 return;
 237         }
 238         SET_INTR(intr_addr);
 239         outb_p(hd_info[drive].ctl,HD_CMD);
 240         port=HD_DATA;
 241         outb_p(hd_info[drive].wpcom>>2,++port);
 242         outb_p(nsect,++port);
 243         outb_p(sect,++port);
 244         outb_p(cyl,++port);
 245         outb_p(cyl>>8,++port);
 246         outb_p(0xA0|(drive<<4)|head,++port);
 247         outb(cmd,++port);
 248 }
 249 
 250 static int drive_busy(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 251 {
 252         unsigned int i;
 253         unsigned char c;
 254 
 255         for (i = 0; i < 50000; i++) {
 256                 c = inb_p(HD_STATUS);
 257                 c &= (BUSY_STAT | READY_STAT | SEEK_STAT);
 258                 if (c == (READY_STAT | SEEK_STAT))
 259                         return 0;
 260         }
 261         printk("HD controller times out, c=%02x\n\r",c);
 262         return(1);
 263 }
 264 
 265 static void reset_controller(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 266 {
 267         int     i;
 268 
 269         outb(4,HD_CMD);
 270         for(i = 0; i < 1000; i++) nop();
 271         outb(hd_info[0].ctl & 0x0f ,HD_CMD);
 272         if (drive_busy())
 273                 printk("HD-controller still busy\n\r");
 274         if ((i = inb(HD_ERROR)) != 1)
 275                 printk("HD-controller reset failed: %02x\n\r",i);
 276 }
 277 
 278 static void reset_hd(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 279 {
 280         static int i;
 281 
 282 repeat:
 283         if (reset) {
 284                 reset = 0;
 285                 i = -1;
 286                 reset_controller();
 287         } else if (win_result()) {
 288                 bad_rw_intr();
 289                 if (reset)
 290                         goto repeat;
 291         }
 292         i++;
 293         if (i < NR_HD) {
 294                 hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1,
 295                         hd_info[i].cyl,WIN_SPECIFY,&reset_hd);
 296                 if (reset)
 297                         goto repeat;
 298         } else
 299                 do_hd_request();
 300 }
 301 
 302 void unexpected_hd_interrupt(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 303 {
 304         printk("Unexpected HD interrupt\n\r");
 305         reset = 1;
 306         do_hd_request();
 307 }
 308 
 309 static void bad_rw_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 310 {
 311         if (++CURRENT->errors >= MAX_ERRORS)
 312                 end_request(0);
 313         if (CURRENT->errors > MAX_ERRORS/2)
 314                 reset = 1;
 315         else
 316                 recalibrate = 1;
 317 }
 318 
 319 static void read_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 320 {
 321         SET_INTR(&read_intr);
 322         if (win_result()) {
 323                 SET_INTR(NULL);
 324                 bad_rw_intr();
 325                 do_hd_request();
 326                 return;
 327         }
 328         port_read(HD_DATA,CURRENT->buffer,256);
 329         CURRENT->errors = 0;
 330         CURRENT->buffer += 512;
 331         CURRENT->sector++;
 332         if (--CURRENT->nr_sectors)
 333                 return;
 334         SET_INTR(NULL);
 335         end_request(1);
 336         do_hd_request();
 337 }
 338 
 339 static void write_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 340 {
 341         if (win_result()) {
 342                 bad_rw_intr();
 343                 do_hd_request();
 344                 return;
 345         }
 346         if (--CURRENT->nr_sectors) {
 347                 CURRENT->sector++;
 348                 CURRENT->buffer += 512;
 349                 SET_INTR(&write_intr);
 350                 port_write(HD_DATA,CURRENT->buffer,256);
 351                 return;
 352         }
 353         end_request(1);
 354         do_hd_request();
 355 }
 356 
 357 static void recal_intr(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 358 {
 359         if (win_result())
 360                 bad_rw_intr();
 361         do_hd_request();
 362 }
 363 
 364 static void hd_times_out(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 365 {       
 366         do_hd = NULL;
 367         reset = 1;
 368         if (!CURRENT)
 369                 return;
 370         printk("HD timeout");
 371         if (++CURRENT->errors >= MAX_ERRORS)
 372                 end_request(0);
 373         do_hd_request();
 374 }
 375 
 376 void do_hd_request(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 377 {
 378         int i,r;
 379         unsigned int block,dev;
 380         unsigned int sec,head,cyl;
 381         unsigned int nsect;
 382 
 383         INIT_REQUEST;
 384         dev = MINOR(CURRENT->dev);
 385         block = CURRENT->sector;
 386         nsect = CURRENT->nr_sectors;
 387         if (dev >= (NR_HD<<6) || block+nsect > hd[dev].nr_sects) {
 388                 end_request(0);
 389                 goto repeat;
 390         }
 391         block += hd[dev].start_sect;
 392         dev >>= 6;
 393         sec = block % hd_info[dev].sect;
 394         block /= hd_info[dev].sect;
 395         head = block % hd_info[dev].head;
 396         cyl = block / hd_info[dev].head;
 397         sec++;
 398         if (reset) {
 399                 recalibrate = 1;
 400                 reset_hd();
 401                 return;
 402         }
 403         if (recalibrate) {
 404                 recalibrate = 0;
 405                 hd_out(dev,hd_info[dev].sect,0,0,0,
 406                         WIN_RESTORE,&recal_intr);
 407                 if (reset)
 408                         goto repeat;
 409                 return;
 410         }       
 411         if (CURRENT->cmd == WRITE) {
 412                 hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr);
 413                 if (reset)
 414                         goto repeat;
 415                 for(i=0 ; i<10000 && !(r=inb_p(HD_STATUS)&DRQ_STAT) ; i++)
 416                         /* nothing */ ;
 417                 if (!r) {
 418                         bad_rw_intr();
 419                         goto repeat;
 420                 }
 421                 port_write(HD_DATA,CURRENT->buffer,256);
 422         } else if (CURRENT->cmd == READ) {
 423                 hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr);
 424                 if (reset)
 425                         goto repeat;
 426         } else
 427                 panic("unknown hd-command");
 428 }
 429 
 430 void hd_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 431 {
 432         blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
 433         set_intr_gate(0x2E,&hd_interrupt);
 434         outb_p(inb_p(0x21)&0xfb,0x21);
 435         outb(inb_p(0xA1)&0xbf,0xA1);
 436         timer_table[HD_TIMER].fn = hd_times_out;
 437 }

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