/* 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 */
/* Revised: 05/07/94 by Arindam Banerji (axb@cse.nd.edu) */
/* Revised: 09/08/94 by Peter De Schrijver (stud11@cc4.kuleuven.ac.be)
Thanks to Arindam Banerij for sending me the docs of the adapter */
/* BA Modified for ThinkPad 720 by Boris Ashkinazi */
/* (bash@vnet.ibm.com) 08/08/95 */
/* Modified further for ThinkPad-720C by Uri Blumenthal */
/* (uri@watson.ibm.com) Sep 11, 1995 */
/* TODO :
+ Timeouts
+ Get disk parameters
+ DMA above 16MB
+ reset after read/write error
*/
#define DEVICE_NAME "PS/2 ESDI"
#include <linux/config.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <linux/fs.h>
#include <linux/kernel.h>
#include <linux/genhd.h>
#include <linux/ps2esdi.h>
#include <linux/blkdev.h>
#include <linux/mca-legacy.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/hdreg.h>
#include <asm/system.h>
#include <asm/io.h>
#include <asm/dma.h>
#include <asm/mca_dma.h>
#include <asm/uaccess.h>
#define PS2ESDI_IRQ 14
#define MAX_HD 2
#define MAX_RETRIES 5
#define MAX_16BIT 65536
#define ESDI_TIMEOUT 0xf000
#define ESDI_STAT_TIMEOUT 4
#define TYPE_0_CMD_BLK_LENGTH 2
#define TYPE_1_CMD_BLK_LENGTH 4
static void reset_ctrl(void);
static int ps2esdi_geninit(void);
static void do_ps2esdi_request(request_queue_t * q);
static void ps2esdi_readwrite(int cmd, struct request *req);
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 void ps2esdi_prep_dma(char *buffer, u_short length, u_char dma_xmode);
static irqreturn_t ps2esdi_interrupt_handler(int irq, void *dev_id,
struct pt_regs *regs);
static void (*current_int_handler) (u_int) = NULL;
static void ps2esdi_normal_interrupt_handler(u_int);
static void ps2esdi_initial_reset_int_handler(u_int);
static void ps2esdi_geometry_int_handler(u_int);
static int ps2esdi_getgeo(struct block_device *bdev, struct hd_geometry *geo);
static int ps2esdi_read_status_words(int num_words, int max_words, u_short * buffer);
static void dump_cmd_complete_status(u_int int_ret_code);
static void ps2esdi_get_device_cfg(void);
static void ps2esdi_reset_timer(unsigned long unused);
static u_int dma_arb_level; /* DMA arbitration level */
static DECLARE_WAIT_QUEUE_HEAD(ps2esdi_int);
static int no_int_yet;
static int ps2esdi_drives;
static u_short io_base;
static DEFINE_TIMER(esdi_timer, ps2esdi_reset_timer, 0, 0);
static int reset_status;
static int ps2esdi_slot = -1;
static int tp720esdi = 0; /* Is it Integrated ESDI of ThinkPad-720? */
static int intg_esdi = 0; /* If integrated adapter */
struct ps2esdi_i_struct {
unsigned int head, sect, cyl, wpcom, lzone, ctl;
};
static DEFINE_SPINLOCK(ps2esdi_lock);
static struct request_queue *ps2esdi_queue;
static struct request *current_req;
#if 0
#if 0 /* try both - I don't know which one is better... UB */
static struct ps2esdi_i_struct ps2esdi_info[MAX_HD] =
{
{4, 48, 1553, 0, 0, 0},
{0, 0, 0, 0, 0, 0}};
#else
static struct ps2esdi_i_struct ps2esdi_info[MAX_HD] =
{
{64, 32, 161, 0, 0, 0},
{0, 0, 0, 0, 0, 0}};
#endif
#endif
static struct ps2esdi_i_struct ps2esdi_info[MAX_HD] =
{
{0, 0, 0, 0, 0, 0},
{0, 0, 0, 0, 0, 0}};
static struct block_device_operations ps2esdi_fops =
{
.owner = THIS_MODULE,
.getgeo = ps2esdi_getgeo,
};
static struct gendisk *ps2esdi_gendisk[2];
/* initialization routine called by ll_rw_blk.c */
static int __init ps2esdi_init(void)
{
int error = 0;
/* register the device - pass the name and major number */
if (register_blkdev(PS2ESDI_MAJOR, "ed"))
return -EBUSY;
/* set up some global information - indicating device specific info */
ps2esdi_queue = blk_init_queue(do_ps2esdi_request, &ps2esdi_lock);
if (!ps2esdi_queue) {
unregister_blkdev(PS2ESDI_MAJOR, "ed");