/* ps2esdi driver based on assembler code by Arindam Banerji,
   written by Peter De Schrijver */
/* Reassuring note to IBM : This driver was NOT developed by vice-versa
   engineering the PS/2's BIOS */
/* Dedicated to Wannes, Tofke, Ykke, Godot, Killroy and all those other lovely
   fish out there... */
/* This code was written during the long and boring WINA elections 1994 */
/* Thanks to Arindam Banerij for giving me the source of his driver */
/* This code may be freely distributed and modified in any way, as long as these
   notes remain intact */

/* TODO : 
 + Timeouts
 + Get disk parameters
 + DMA above 16MB
 + reset after read/write error
*/
#include <linux/config.h>
#ifdef CONFIG_BLK_DEV_PS2

#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/genhd.h>
#include <linux/ps2esdi.h>

#include <asm/system.h>
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/dma.h>

#define MAJOR_NR 31
#define PS2ESDI_IRQ 14
#define MAX_HD 2
#define MAX_RETRIES 5
#define MAX_16BIT 65536

#define CMD_BLK_LENGTH 4
#include "blk.h"

static inline unsigned char CMOS_READ(unsigned char addr)
{
	outb_p(addr,0x70);
	return inb_p(0x71);
}

static void reset_ctrl(void);
u_long ps2esdi_init(u_long mem_start, u_long mem_end);
static void ps2esdi_geninit(void);
static void do_ps2esdi_request(void);
static int ps2esdi_readwrite(int cmd,u_char drive, char *buffer, u_int block,u_int count);
static void ps2esdi_fill_cmd_block(u_short *cmd_blk,u_short cmd,u_short cyl,u_short head,u_short sector,u_short length,u_char drive);
static int ps2esdi_out_cmd_blk(u_short *cmd_blk);
static int ps2esdi_wait_xfer(void);
static void ps2esdi_prep_dma(char *buffer,u_short length,u_char dma_xmode);
static int ps2esdi_do_dma(void);
static void ps2esdi_interrupt_handler(int unused);
static int ps2esdi_open(struct inode *inode,struct file *file);
static void ps2esdi_release (struct inode *inode, struct file *file);
static int ps2esdi_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg);
static int ps2esdi_reread_partitions(int dev);

u_int dma_arb_level;			/* DMA arbitration level */

static struct wait_queue *ps2esdi_int = NULL, *ps2esdi_wait_open=NULL;
static u_int int_ret_code=0xffff;
static int access_count[MAX_HD]={0,};
static char ps2esdi_valid[MAX_HD]={0,};
static int ps2esdi_sizes[MAX_HD<<6]={0,};
static int ps2esdi_blocksizes[MAX_HD<<6]={0,};
static int ps2esdi_drives=0;
static struct hd_struct ps2esdi[MAX_HD<<6];

struct ps2esdi_i_struct {
   unsigned int head,sect,cyl,wpcom,lzone,ctl;
   };
struct ps2esdi_i_struct ps2esdi_info[] = { {0,0,0,0,0,0},{0,0,0,0,0,0} };

static struct file_operations ps2esdi_fops = {
   NULL,       /* lseek - default */
   block_read,    /* read - general block-dev read */
   block_write,      /* write - general block-dev write */
   NULL,       /* readdir - bad */
   NULL,       /* select */
   ps2esdi_ioctl,      /* ioctl */
   NULL,       /* mmap */
   ps2esdi_open,    /* open */
   ps2esdi_release,    /* release */
   block_fsync    /* fsync */
};

static struct gendisk ps2esdi_gendisk = {
   MAJOR_NR,   /* Major number */
   "ps2esdi",    /* Major name */
   6,    /* Bits to shift to get real from partition */
   1 << 6,     /* Number of partitions per real */
   MAX_HD,  /* maximum number of real */
   ps2esdi_geninit, /* init function */
   ps2esdi,      /* hd struct */
   ps2esdi_sizes,   /* block sizes */
   0,    /* number */
   (void *) ps2esdi_info, /* internal */
   NULL     /* next */
};

u_long ps2esdi_init(u_long mem_start, u_long mem_end) {

	if(register_blkdev(MAJOR_NR,"ps2esdi",&ps2esdi_fops)) {
		printk("%s: Unable to get major number %d\n",DEVICE_NAME,MAJOR_NR);
		return mem_start;
	}

   blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
   read_ahead[MAJOR_NR] = 8;  /* 8 sector (4kB) read ahead */
   ps2esdi_gendisk.next = gendisk_head;
   gendisk_head = &ps2esdi_gendisk;

	return mem_start;
}

void ed_setup(char *str, int *ints)
{
   int hdind = 0;

   if (ints[0] != 3)
      return;

printk("%s: ints[0]=%d ints[1]=%d ints[2]=%d ints[3]=%d\n",DEVICE_NAME,ints[0],ints[1],ints[2],ints[3]);
   if (ps2esdi_info[0].head != 0)
      hdind=1;
   ps2esdi_info[hdind].head = ints[2];
   ps2esdi_info[hdind].sect = ints[3];
   ps2esdi_info[hdind].cyl = ints[1];
   ps2esdi_info[hdind].wpcom = 0;
   ps2esdi_info[hdind].lzone = ints[1];
   ps2esdi_info[hdind].ctl = (ints[2] > 8 ? 8 : 0);
   ps2esdi_drives = hdind+1;
}

static void ps2esdi_geninit(void) {

	int drive, i, cmos_disks;
	extern struct drive_info drive_info;
	u_char *BIOS = (unsigned char *) &drive_info,status;
	u_int id;

	for(i=0;i<NUMBER_OF_SLOTS;i++) {
		outb_p(i | SELECT_BIT,PORT_POS_SEL);
		id=((inb_p(PORT_POS_REG_1)<<8) + inb_p(PORT_POS_REG_0));
		if(id!=0xFFFF)
			printk("%s: found device %04X in slot %d\n",DEVICE_NAME,id,i);
		if((id==NRML_ESDI_ID) || (id==INTG_ESDI_ID))
			break;
	}

	if(i==NUMBER_OF_SLOTS) {
		printk("%s: ESDI adapter not found\n",DEVICE_NAME);
		outb(0,PORT_POS_SEL);
		return;
	}
		
	printk("%s: %s ESDI adapter found in slot %d\n",DEVICE_NAME,(id==NRML_ESDI_ID) ? "normal" : "integrated",i);

	status=inb_p(PORT_POS_REG_2);
	outb(0,PORT_POS_SEL);
	if(!(status & STATUS_ENABLED)) {
		printk("%s: ESDI adapter disabled\n",DEVICE_NAME);
		return;
	}	

	if(status & STATUS_ALTERNATE) {
		printk("%s: alternate ESDI adapter not supported\n",DEVICE_NAME);
		return;
	}	

	dma_arb_level=	(status>>2) & 0xf;

	printk("%s: DMA arbitration level : %d\n",DEVICE_NAME,dma_arb_level);

	LITE_ON;
	reset_ctrl();
	LITE_OFF; 

/* get geometry from cmos... */

printk("%s: ps2esdi_drives : %d\n",DEVICE_NAME,ps2esdi_drives);
	if (!ps2esdi_drives) {	   
		for (drive=0 ; drive<2 ; drive++) {
			ps2esdi_info[drive].cyl = *(unsigned short *) BIOS;
			ps2esdi_info[drive].head = *(2+BIOS);
			ps2esdi_info[drive].wpcom = *(unsigned short *) (5+BIOS);
			ps2esdi_info[drive].ctl = *(8+BIOS);
			ps2esdi_info[drive].lzone = *(unsigned short *) (12+BIOS);
			ps2esdi_info[drive].sect = *(14+BIOS);
			BIOS += 16;
		}

	/*
		We querry CMOS about hard disks : it could be that 
		we have a SCSI/ESDI/etc controller that is BIOS
		compatable with ST-506, and thus showing up in our
		BIOS table, but not register compatable, and therefore
		not present in CMOS.

		Furthurmore, we will assume that our ST-506 drives
		<if any> are the primary drives in the system, and 
		the ones reflected as drive 1 or 2.

		The first drive is stored in the high nibble of CMOS
		byte 0x12, the second in the low nibble.  This will be
		either a 4 bit drive type or 0xf indicating use byte 0x19 
		for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS.

		Needless to say, a non-zero value means we have 
		an AT controller hard disk for that drive.

		
	*/

		if ((cmos_disks = CMOS_READ(0x12)) & 0xf0)
			if (cmos_disks & 0x0f)
				ps2esdi_drives = 2;
			else
				ps2esdi_drives = 1;
printk("%s: cmos said ps2esdi_drives : %d. I assume 1 drive\n",DEVICE_NAME,ps2esdi_drives);
ps2esdi_info[0].cyl=115;
ps2esdi_info[0].head=64;
ps2esdi_info[0].sect=32;
printk("%s: Drive's parameters : cyl : %d sect : %d head : %d\n",DEVICE_NAME,ps2esdi_info[0].cyl,ps2esdi_info[0].sect,ps2esdi_info[0].head);
	ps2esdi_drives=1;
	}
	i = ps2esdi_drives;
	while (i-- > 0) {
		ps2esdi[i<<6].nr_sects = 0;
		if (ps2esdi_info[i].head > 64) {
			printk("ps2esdi.c: ps/2 esdi interface disk with more than 16 heads detected,\n");
			printk("  probably due to non-standard sector translation. Giving up.\n");
			printk("  (disk %d: cyl=%d, sect=%d, head=%d)\n", i,
				ps2esdi_info[i].cyl,
				ps2esdi_info[i].sect,
				ps2esdi_info[i].head);
			if (i+1 == ps2esdi_drives)
				ps2esdi_drives--;
			continue;
		}
		ps2esdi[i<<6].nr_sects = ps2esdi_info[i].head*
				ps2esdi_info[i].sect*ps2esdi_info[i].cyl;
		ps2esdi_valid[i]=1;
	}

/* end of cmos code...*/

	if(request_irq(PS2ESDI_IRQ,ps2esdi_interrupt_handler)) {
		printk("%s: Unable to get IRQ %d\n",DEVICE_NAME,PS2ESDI_IRQ);
		return;
	}

	ps2esdi_gendisk.nr_real = ps2esdi_drives;

	for(i=0;i<(MAX_HD << 6);i++) ps2esdi_blocksizes[i] = 1024;
	blksize_size[MAJOR_NR] = ps2esdi_blocksizes;
}


static void do_ps2esdi_request(void) {

	u_int block,count,retry;
	int result;

	sti();
	while(result=FAIL, CURRENT) {
		INIT_REQUEST;
		
		if((u_int)CURRENT->buffer+CURRENT->nr_sectors*512 > 16*MB) {
			printk("%s: DMA above 16MB not supported\n",DEVICE_NAME);
			end_request(FAIL);
		}

		if(CURRENT_DEV < ps2esdi_drives && CURRENT->sector + CURRENT->nr_sectors <= ps2esdi[MINOR(CURRENT->dev)].nr_sects) {
#if 0
printk("%s:got request. device : %d minor : %d command : %d  sector : %ld count : %ld\n",DEVICE_NAME,CURRENT_DEV,MINOR(CURRENT->dev),CURRENT->cmd,CURRENT->sector,CURRENT->nr_sectors);
#endif
			block=CURRENT->sector + ps2esdi[MINOR(CURRENT->dev)].start_sect;
#if 0
printk("%s: blocknumber : %d\n",DEVICE_NAME,block);
#endif
			count=CURRENT->nr_sectors;
			switch(CURRENT->cmd) {
				case READ: for(retry=0;retry < MAX_RETRIES && !result;retry++)
									result=ps2esdi_readwrite(READ,CURRENT_DEV,CURRENT->buffer,block,count); break;
				case WRITE: for(retry=0;retry < MAX_RETRIES && !result;retry++)
									result=ps2esdi_readwrite(WRITE,CURRENT_DEV,CURRENT->buffer,block,count); break;
								break;
				default: printk("%s: Unknown command\n",DEVICE_NAME);
						   break;
			}
		}
		end_request(result);
	}
}

static void reset_ctrl(void) {

	u_char status;
	u_long expire;

	if(!(inb(ESDI_STATUS) & STATUS_BUSY)) {
printk("%s: soft reset...\n",DEVICE_NAME);
		outb_p(CTRL_SOFT_RESET,ESDI_ATTN);
	}
	else {
printk("%s: hard reset...\n",DEVICE_NAME);
		outb_p(CTRL_HARD_RESET,ESDI_CONTROL);
		expire=jiffies + 10;
		while(jiffies < expire)
			;
		outb_p(0,ESDI_CONTROL);
	}

	expire= jiffies + 40*100;
	while(((inb(ESDI_STATUS) & STATUS_INTR)) && (jiffies < expire))
		;
	
	if(jiffies>=expire) {
		printk("%s: controller reset timed out\n",DEVICE_NAME);
		return;
	}

	status=inb(ESDI_INTRPT);
#if 0
	if(status!=STATUS_RESET_FAIL) {
		printk("%s: controller reset failed status=%02X\n",DEVICE_NAME,status);
		return;
	}
#endif

	outb_p(CTRL_EOI,ESDI_ATTN);
	outb_p(CTRL_ENABLE_INTR,ESDI_CONTROL);

}

static int ps2esdi_readwrite(int cmd,u_char drive, char *buffer, u_int block,	u_int count) {
	
	u_short temp,track,head,cylinder,sector;
	u_short cmd_blk[CMD_BLK_LENGTH];

	while(count) {
		temp=(count <  (2*MAX_16BIT/SECT_SIZE)) ? count : (2*MAX_16BIT/SECT_SIZE);
		
		track=block / ps2esdi_info[drive].sect;
		head=track % ps2esdi_info[drive].head;
		cylinder=track / ps2esdi_info[drive].head;
		sector=block % ps2esdi_info[drive].sect;
	
#if 0
printk("%s: cyl=%d head=%d sect=%d\n",DEVICE_NAME,cylinder,head,sector);
#endif
		ps2esdi_fill_cmd_block(cmd_blk,(cmd==READ) ? CMD_READ : CMD_WRITE,cylinder,head,sector,temp,drive);
		
		int_ret_code=0xffff;
		if(ps2esdi_out_cmd_blk(cmd_blk)) {
			printk("%s: Controller failed\n",DEVICE_NAME);
			return FAIL;
		}

#if 0
printk("%s: waiting for xfer\n",DEVICE_NAME);
#endif
LITE_ON;
		if(!ps2esdi_wait_xfer()) {
			printk("%s: Controller failed in send_cmd\n",DEVICE_NAME);
			return FAIL;
		}
		int_ret_code=0xffff;
			
#if 0 
printk("%s: preparing DMA for %s\n",DEVICE_NAME,(cmd==READ) ? "read" : "write");
#endif
		ps2esdi_prep_dma(buffer,temp,(cmd==READ) ? DMA_READ_16 : DMA_WRITE_16);

#if 0
printk("%s: doing dma...\n",DEVICE_NAME);
#endif
		if(!ps2esdi_do_dma()) {
			printk("%s: Controller failed in DMA\n",DEVICE_NAME);
			return FAIL;
		}
LITE_OFF;
		int_ret_code=0xffff;
		count-=temp; buffer+=temp*SECT_SIZE; block+=temp;
	}
	return SUCCES;
}	
				
static void ps2esdi_fill_cmd_block(u_short *cmd_blk,u_short cmd,u_short cyl,u_short head,u_short sector,u_short length,u_char drive) {

	cmd_blk[0]=(drive<<5) | cmd;
	cmd_blk[1]=length;
	cmd_blk[2]=((cyl & 0x1f) << 11) | (head << 5) | sector;
	cmd_blk[3]=(cyl & 0x3E0) >> 5;

}	
		
static int ps2esdi_out_cmd_blk(u_short *cmd_blk) {

	int i,j;
	u_char status;

	outb(CTRL_ENABLE_INTR,ESDI_CONTROL);

	for(i=65000;i && (inb(ESDI_STATUS) & STATUS_BUSY);i--);
#if 0
printk("%s: i(1)=%d\n",DEVICE_NAME,i);
#endif
	if(inb(ESDI_STATUS) & STATUS_BUSY) {
		printk("%s: ps2esdi_out_cmd timed out (1)\n",DEVICE_NAME);
		return ERROR;
	}

	outb(((*cmd_blk) & 0xE0) | 1,ESDI_ATTN);
	
#if 0 
printk("%s: sending %d words to controller\n",DEVICE_NAME,(((*cmd_blk)>>14)+1)<<1);
#endif

	for(i=(((*cmd_blk)>>14)+1)<<1;i;i--) {
		status=inb(ESDI_STATUS);
		for(j=65000;j && (status & STATUS_BUSY) && (status & STATUS_CMD_INF);j--,status=inb(ESDI_STATUS));
		if((status & (STATUS_BUSY | STATUS_CMD_INF))==STATUS_BUSY) {
#if 0
			printk("%s: sending %04X\n",DEVICE_NAME,*cmd_blk);
#endif
			outw(*cmd_blk++,ESDI_CMD_INT);
		}
		else {
			printk("%s: ps2esdi_out_cmd timed out while sending command (status=%02X)\n",DEVICE_NAME,status);
			return ERROR;
		}
	}
	return OK;
}

static int ps2esdi_wait_xfer(void) {

	
#if 0
printk("%s: int_ret_code %02X\n",DEVICE_NAME,int_ret_code);
printk("%s: wait_xfer sleeping ...\n",DEVICE_NAME);
#endif
	if(int_ret_code==0xFFff) {
		sleep_on(&ps2esdi_int);
	}

	if((int_ret_code & 0x1f) != INT_TRANSFER_REQ) 
		printk("%s: wait_xfer: unexpected int_ret_code : %02X\n",DEVICE_NAME,int_ret_code);

	return (int_ret_code & 0x1f) == INT_TRANSFER_REQ;
}
	
static void ps2esdi_prep_dma(char *buffer,u_short length,u_char dma_xmode) {

	u_int tc;

	cli();

	outb(dma_arb_level | DMA_MASK_CHAN,PORT_DMA_FN);

	outb(dma_arb_level | DMA_WRITE_ADDR,PORT_DMA_FN);
	outb((u_int) buffer & (u_int) 0xff,PORT_DMA_EX);
	outb(((u_int) buffer >> 8) & (u_int) 0xff,PORT_DMA_EX); 
	outb(((u_int) buffer >> 16) & (u_int) 0xff,PORT_DMA_EX); 

	outb(dma_arb_level | DMA_WRITE_TC,PORT_DMA_FN);
	tc=(length*SECT_SIZE/2)-1;
	outb(tc & 0xff,PORT_DMA_EX);
	outb((tc>>8) & 0xff,PORT_DMA_EX);

	outb(dma_arb_level | DMA_WRITE_MODE,PORT_DMA_FN);
	outb(dma_xmode,PORT_DMA_EX);

	outb(dma_arb_level | DMA_UNMASK_CHAN,PORT_DMA_FN);
 	
	sti();

}

static int ps2esdi_do_dma(void) {

	outb(CTRL_ENABLE_DMA | CTRL_ENABLE_INTR,ESDI_CONTROL);
#if 0
printk("%s: do_dma int_ret_code %02X\n",DEVICE_NAME,int_ret_code);
#endif
	if(int_ret_code==0xFFff) {
		sleep_on(&ps2esdi_int);
	}
	outb(CTRL_ENABLE_INTR,ESDI_CONTROL);
	
	if((int_ret_code & 0x1f) != INT_CMD_COMPLETE) 
		printk("%s: do_dma: unexpected int_ret_code : %02X\n",DEVICE_NAME,int_ret_code);

	return (int_ret_code & 0x1f) == INT_CMD_COMPLETE; 
}				
	
static void ps2esdi_interrupt_handler(int unused) {

	int i,j;
	if(inb(ESDI_STATUS) & STATUS_INTR) {
		int_ret_code=inb(ESDI_INTRPT);
#if 0
		printk("%s: esdi interrupt code : %02X\n",DEVICE_NAME,int_ret_code);
#endif
		if((int_ret_code & 0x0f) == INT_TRANSFER_REQ)  {
#if 0 
printk("%s: waking up...\n",DEVICE_NAME);
			outb(0x20,0x20);
			outb(0x20,0xa0);
#endif
			wake_up(&ps2esdi_int);
		}
		else if((int_ret_code & 0x0f) == INT_ATTN_ERROR) {
printk("%s: ATTN_ERROR interrupt...\n",DEVICE_NAME);
#if 0 
			outb(0x20,0x20);
			outb(0x20,0xa0);
#endif
			wake_up(&ps2esdi_int);
		}
		else {
			if((int_ret_code & 0x0f) != INT_CMD_COMPLETE) {
				for(i=65000;i && (inb(ESDI_STATUS) & STATUS_BUSY);i--);
				if(inb(ESDI_STATUS) & STATUS_BUSY) {
					printk("%s: ps2esdi_out_cmd timed out while getting status",DEVICE_NAME);
				}
				j=inw(ESDI_STT_INT)>>8;
				printk("%s: %d status words follow :",DEVICE_NAME,j);
				for(;j;j--) {
					for(i=65000;i && (inb(ESDI_STATUS) & STATUS_BUSY);i--);
					if(inb(ESDI_STATUS) & STATUS_BUSY) {
						printk("%s: ps2esdi_out_cmd timed out while reading status",DEVICE_NAME);
						return;
					}
					printk("%04X ",inw(ESDI_STT_INT));
				}
				printk("\n");
			}
			outb((int_ret_code & 0xe0) | 0x2,ESDI_ATTN);
			wake_up(&ps2esdi_int);
		}
	}
	else { 
		printk("%s: Spurious interrupt ?\n",DEVICE_NAME);
	}
}

static int ps2esdi_open(struct inode *inode,struct file *file) {

   int dev = DEVICE_NR(MINOR(inode->i_rdev));

#if 0
printk("%s: dev= %d\n",DEVICE_NAME,dev);
#endif

   if (dev < ps2esdi_drives) {
      while (!ps2esdi_valid[dev])
         sleep_on(&ps2esdi_wait_open);

      access_count[dev]++;

      return (0);
   }
   else
      return (-ENODEV);
}

static void ps2esdi_release (struct inode *inode, struct file *file) {

   int dev = DEVICE_NR(MINOR(inode->i_rdev));

   if (dev < ps2esdi_drives) {
      sync_dev(dev);
      access_count[dev]--;
   }
}
	
static int ps2esdi_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg) {

	struct ps2esdi_geometry *geometry = (struct ps2esdi_geometry *) arg;
	int dev = DEVICE_NR(MINOR(inode->i_rdev)),err;

	if (inode && (dev < ps2esdi_drives))
		switch (cmd) {
			case HDIO_GETGEO:
				if (arg) {
					if ((err = verify_area(VERIFY_WRITE,geometry,sizeof(*geometry))))
						return (err);
					put_fs_byte(ps2esdi_info[dev].head,(char *) &geometry->heads);
					put_fs_byte(ps2esdi_info[dev].sect,(char *) &geometry->sectors);
					put_fs_word(ps2esdi_info[dev].cyl,(short *) &geometry->cylinders);
					put_fs_long(ps2esdi[MINOR(inode->i_rdev)].start_sect,(long *) &geometry->start);

					return (0);
				}
				break;
			case BLKRASET:
			  if(!suser())  return -EACCES;
			  if(!inode->i_rdev) return -EINVAL;
			  if(arg > 0xff) return -EINVAL;
			  read_ahead[MAJOR(inode->i_rdev)] = arg;
			  return 0;
			case BLKGETSIZE:
				if (arg) {
					if ((err = verify_area(VERIFY_WRITE,(long *) arg,sizeof(long))))
						return (err);
					put_fs_long(ps2esdi[MINOR(inode->i_rdev)].nr_sects,(long *) arg);

					return (0);
				}
				break;
			case BLKFLSBUF:
				if(!suser())  return -EACCES;
				if(!inode->i_rdev) return -EINVAL;
				fsync_dev(inode->i_rdev);
				invalidate_buffers(inode->i_rdev);
				return 0;
				
			case BLKRRPART:
				return (ps2esdi_reread_partitions(inode->i_rdev));
			RO_IOCTLS(inode->i_rdev,arg);
		}
	return (-EINVAL);
}

static int ps2esdi_reread_partitions(int dev) {

	int target = DEVICE_NR(MINOR(dev)),start = target << ps2esdi_gendisk.minor_shift,partition;

	cli(); ps2esdi_valid[target] = (access_count[target] != 1); sti();
	if (ps2esdi_valid[target])
		return (-EBUSY);

	for (partition = ps2esdi_gendisk.max_p - 1; partition >= 0; partition--) {
		sync_dev(MAJOR_NR << 8 | start | partition);
		invalidate_inodes(MAJOR_NR << 8 | start | partition);
		invalidate_buffers(MAJOR_NR << 8 | start | partition);
		ps2esdi_gendisk.part[start + partition].start_sect = 0;
		ps2esdi_gendisk.part[start + partition].nr_sects = 0;
	};

	ps2esdi_gendisk.part[start].nr_sects = ps2esdi_info[target].head * ps2esdi_info[target].cyl * ps2esdi_info[target].sect;
	resetup_one_dev(&ps2esdi_gendisk,target);

	ps2esdi_valid[target] = 1;
	wake_up(&ps2esdi_wait_open);

	return (0);
}
#endif
