root/kernel/blk_drv/hd.c

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

DEFINITIONS

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

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