diff -Nru a/Documentation/DocBook/kernel-api.tmpl b/Documentation/DocBook/kernel-api.tmpl --- a/Documentation/DocBook/kernel-api.tmpl Fri Aug 16 14:34:51 2002 +++ b/Documentation/DocBook/kernel-api.tmpl Fri Aug 16 14:34:51 2002 @@ -299,6 +299,8 @@ EHCI, OHCI, or UHCI. !Edrivers/usb/core/hcd.c +!Edrivers/usb/core/hcd-pci.c +!Edrivers/usb/core/buffer.c diff -Nru a/Documentation/filesystems/driverfs.txt b/Documentation/filesystems/driverfs.txt --- a/Documentation/filesystems/driverfs.txt Fri Aug 16 14:34:59 2002 +++ b/Documentation/filesystems/driverfs.txt Fri Aug 16 14:34:59 2002 @@ -165,9 +165,9 @@ order to relieve pain in declaring attributes, the subsystem should also define a macro, like: -#define DEVICE_ATTR(_name,_str,_mode,_show,_store) \ +#define DEVICE_ATTR(_name,_mode,_show,_store) \ struct device_attribute dev_attr_##_name = { \ - .attr = {.name = _str, .mode = _mode }, \ + .attr = {.name = __stringify(_name) , .mode = _mode }, \ .show = _show, \ .store = _store, \ }; @@ -252,7 +252,7 @@ Declaring: -BUS_ATTR(_name,_str,_mode,_show,_store) +BUS_ATTR(_name,_mode,_show,_store) Creation/Removal: @@ -273,7 +273,7 @@ Declaring: -DRIVER_ATTR(_name,_str,_mode,_show,_store) +DRIVER_ATTR(_name,_mode,_show,_store) Creation/Removal: diff -Nru a/Documentation/filesystems/ntfs.txt b/Documentation/filesystems/ntfs.txt --- a/Documentation/filesystems/ntfs.txt Fri Aug 16 14:34:58 2002 +++ b/Documentation/filesystems/ntfs.txt Fri Aug 16 14:34:58 2002 @@ -247,6 +247,8 @@ Note, a technical ChangeLog aimed at kernel hackers is in fs/ntfs/ChangeLog. +2.0.25: + - Minor bugfixes in error code paths and small cleanups. 2.0.24: - Small internal cleanups. - Support for sendfile system call. (Christoph Hellwig) diff -Nru a/Documentation/i2c/dev-interface b/Documentation/i2c/dev-interface --- a/Documentation/i2c/dev-interface Fri Aug 16 14:34:53 2002 +++ b/Documentation/i2c/dev-interface Fri Aug 16 14:34:53 2002 @@ -87,7 +87,12 @@ ioctl(file,I2C_TENBIT,long select) Selects ten bit addresses if select not equals 0, selects normal 7 bit - addresses if select equals 0. + addresses if select equals 0. Default 0. + +ioctl(file,I2C_PEC,long select) + Selects SMBus PEC (packet error checking) generation and verification + if select not equals 0, disables if select equals 0. Default 0. + Used only for SMBus transactions. ioctl(file,I2C_FUNCS,unsigned long *funcs) Gets the adapter functionality and puts it in *funcs. diff -Nru a/Documentation/i2c/i2c-old-porting b/Documentation/i2c/i2c-old-porting --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/i2c/i2c-old-porting Fri Aug 16 14:35:01 2002 @@ -0,0 +1,626 @@ +I2C Conversion Guide for I2C-old to the current I2C API +July 2002 +For Linux Kernel v2.5.x +Frank Davis +------------------------------------------------------- + +There exists several kernel drivers that are using an old version of the I2C +API. These drivers need to be converted to the current (kernel 2.5.x) version. +The following document provides a guideline to make the appropriate changes to +the affected drivers. There maybe slight modifications to this guide that are +specific to the driver you are working on. If you see {driver_name}, replace +that with the respective name of the driver, such as saa7110.c , {driver_name} += saa7110. + +------------------------------------------------------- + +Step 1: Include the right header file + +Perform the following change within the driver + +#include --> #include + +Step 2: Add and set the i2c modes + +Add the following code near the top of the driver + +static unsigned short normal_i2c[] = {34>>1, I2C_CLIENT_END }; +static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; +static unsigned short probe[2] = { I2C_CLIENT_END , I2C_CLIENT_END }; +static unsigned short probe_range[2] = { I2C_CLIENT_END , I2C_CLIENT_END }; +static unsigned short ignore[2] = { I2C_CLIENT_END , I2C_CLIENT_END }; +static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; +static unsigned short force[2] = { I2C_CLIENT_END , I2C_CLIENT_END }; + +static struct i2c_client_address_data addr_data = { + normal_i2c , normal_i2c_range, + probe , probe_range, + ignore , ignore_range, + force +}; + +static struct i2c_client client_template; + +Step 3: Modify the driver info struct + +Within the struct for the driver , such as struct {driver_name} , make the +following change , +struct i2c_bus *bus --> struct i2c_client *client + +Make changes where this change affects references within the file. + +Add a semaphore to the driver struct (as above) + +struct semaphore lock + +Step 5: Remove specific read and write functions + +Remove the driver specific write and read functions, usually in the form: +{driver_name}_write , {driver_name}_read , {driver_name}_write_block , etc. + +Step 6: Update the write and read functions for the current I2C API + +Replace all references of {driver_name}_write with i2c_smbus_write_byte_data +Replace all references of {driver_name}_read with i2c_smbus_read_byte_data or +i2c_smbus_read_byte , depending on args passed in. + +** Ensure that these functions pass in the i2c_client *client , NOT the +decoder/encoder that was passed in the driver specific write and read +functions. + +Step 7: Modify the driver's attach function + +Change the driver attach function prototype : +{driver_name}_attach(struct i2c_device *device) --> {driver_name}_attach(struct +i2c_adapter *adap, int addr , unsigned short flags, int kind) + +Create a i2c_client client... +Add the following (where "decoder" is a reference to a struct for the driver +info: + +struct i2c_client *client; +client = kmalloc(sizeof(*client), GFP_KERNEL); +if(client == NULL) + return -ENOMEM; +client_template.adapter = adap; +client_template.addr = addr; +memcpy(client, &client_template, sizeof(*client)); +strcpy(client->name , "{driver_name}"); +decoder->client = client; +client->data = decoder; +decoder->addr = addr; + +Towards the end of the function, add: + +init_MUTEX(&decoder->lock); +i2c_attach_client(client); + + +Step 8: Modify the driver's detach function + +Change the driver detach function prototype : +{driver_name}_detach(struct i2c_device *device) --> {driver_name}_detach(struct +i2c_client *client) + +In the beginning of the detach function, add: +i2c_detach_client(client); + +Towards the end of the detach function, add: +kfree(client->data); +kfree(client); + +Step 9: Modify the driver's command function + +Change the driver command function prototype : + +Step 10: Add the probe function after the driver's attach function. + +Add the following code: + +static int {driver_name}_probe(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &addr_data, {driver_name}_attach); + +} + +Step 11: Modify the driver's i2c_driver + +Find the i2c_driver , such as +static struct i2c_driver i2c_driver_saa7110 +It is usually located towards the end of the driver +Replace the values from I2C_DRIVERID_{something} to {driver_name}_attach, and +add the following +I2C_DRIVERID_{driver_name} , // verify by looking in include/linux/i2c-id.h +I2C_DF_NOTIFY, +{driver_name}_probe, +.... + +Step 12: Adding the i2c_client + +Add the i2c_client to the driver. Add the following code: + +static struct i2c_client client_template = { + "{driver_name}_client", + -1, + 0, + 0, + NULL, + {i2c_driver reference} +}; + +Step 13: Registering and Unregistering + +Replace i2c_register_driver with i2c_add_driver +Replace i2c_unregister_driver with i2c_del_driver + +------------------------------------------------------- + +Example: + +The following patch provides the i2c coversion patch for the saa7110 driver +based on the above guide (for clarity). + + +--- drivers/media/video/saa7110.c.old Fri Jun 28 10:22:52 2002 ++++ drivers/media/video/saa7110.c Thu Jul 4 16:51:08 2002 +@@ -26,7 +26,7 @@ + #include + #include + +-#include ++#include + #include + #include "linux/video_decoder.h" + +@@ -37,13 +37,31 @@ + + #define I2C_SAA7110 0x9C /* or 0x9E */ + ++#define IF_NAME "saa7110" + #define I2C_DELAY 10 /* 10 us or 100khz */ + ++static unsigned short normal_i2c[] = {34>>1, I2C_CLIENT_END }; ++static unsigned short normal_i2c_range[] = { I2C_CLIENT_END }; ++static unsigned short probe[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; ++static unsigned short probe_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; ++static unsigned short ignore[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; ++static unsigned short ignore_range[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; ++static unsigned short force[2] = { I2C_CLIENT_END, I2C_CLIENT_END }; ++ ++static struct i2c_client_address_data addr_data = { ++ normal_i2c, normal_i2c_range, ++ probe, probe_range, ++ ignore, ignore_range, ++ force ++}; ++ ++static struct i2c_client client_template; ++ + struct saa7110 { +- struct i2c_bus *bus; ++ struct i2c_client *client; + int addr; + unsigned char reg[36]; +- ++ struct semaphore lock; + int norm; + int input; + int enable; +@@ -54,67 +72,10 @@ + }; + + /* ----------------------------------------------------------------------- */ +-/* I2C support functions */ +-/* ----------------------------------------------------------------------- */ +-static +-int saa7110_write(struct saa7110 *decoder, unsigned char subaddr, unsigned char data) +-{ +- int ack; +- +- LOCK_I2C_BUS(decoder->bus); +- i2c_start(decoder->bus); +- i2c_sendbyte(decoder->bus, decoder->addr, I2C_DELAY); +- i2c_sendbyte(decoder->bus, subaddr, I2C_DELAY); +- ack = i2c_sendbyte(decoder->bus, data, I2C_DELAY); +- i2c_stop(decoder->bus); +- decoder->reg[subaddr] = data; +- UNLOCK_I2C_BUS(decoder->bus); +- return ack; +-} +- +-static +-int saa7110_write_block(struct saa7110* decoder, unsigned const char *data, unsigned int len) +-{ +- unsigned subaddr = *data; +- +- LOCK_I2C_BUS(decoder->bus); +- i2c_start(decoder->bus); +- i2c_sendbyte(decoder->bus,decoder->addr,I2C_DELAY); +- while (len-- > 0) { +- if (i2c_sendbyte(decoder->bus,*data,0)) { +- i2c_stop(decoder->bus); +- UNLOCK_I2C_BUS(decoder->bus); +- return -EAGAIN; +- } +- decoder->reg[subaddr++] = *data++; +- } +- i2c_stop(decoder->bus); +- UNLOCK_I2C_BUS(decoder->bus); +- +- return 0; +-} +- +-static +-int saa7110_read(struct saa7110* decoder) +-{ +- int data; +- +- LOCK_I2C_BUS(decoder->bus); +- i2c_start(decoder->bus); +- i2c_sendbyte(decoder->bus, decoder->addr, I2C_DELAY); +- i2c_start(decoder->bus); +- i2c_sendbyte(decoder->bus, decoder->addr | 1, I2C_DELAY); +- data = i2c_readbyte(decoder->bus, 1); +- i2c_stop(decoder->bus); +- UNLOCK_I2C_BUS(decoder->bus); +- return data; +-} +- +-/* ----------------------------------------------------------------------- */ + /* SAA7110 functions */ + /* ----------------------------------------------------------------------- */ + static +-int saa7110_selmux(struct i2c_device *device, int chan) ++int saa7110_selmux(struct i2c_client *client, int chan) + { + static const unsigned char modes[9][8] = { + /* mode 0 */ { 0x00, 0xD9, 0x17, 0x40, 0x03, 0x44, 0x75, 0x16 }, +@@ -126,61 +87,59 @@ + /* mode 6 */ { 0x80, 0x59, 0x17, 0x42, 0xA3, 0x44, 0x75, 0x12 }, + /* mode 7 */ { 0x80, 0x9A, 0x17, 0xB1, 0x13, 0x60, 0xB5, 0x14 }, + /* mode 8 */ { 0x80, 0x3C, 0x27, 0xC1, 0x23, 0x44, 0x75, 0x21 } }; +- struct saa7110* decoder = device->data; + const unsigned char* ptr = modes[chan]; + +- saa7110_write(decoder,0x06,ptr[0]); /* Luminance control */ +- saa7110_write(decoder,0x20,ptr[1]); /* Analog Control #1 */ +- saa7110_write(decoder,0x21,ptr[2]); /* Analog Control #2 */ +- saa7110_write(decoder,0x22,ptr[3]); /* Mixer Control #1 */ +- saa7110_write(decoder,0x2C,ptr[4]); /* Mixer Control #2 */ +- saa7110_write(decoder,0x30,ptr[5]); /* ADCs gain control */ +- saa7110_write(decoder,0x31,ptr[6]); /* Mixer Control #3 */ +- saa7110_write(decoder,0x21,ptr[7]); /* Analog Control #2 */ ++ i2c_smbus_write_byte_data(client,0x06,ptr[0]); /* Luminance control */ ++ i2c_smbus_write_byte_data(client,0x20,ptr[1]); /* Analog Control #1 */ ++ i2c_smbus_write_byte_data(client,0x21,ptr[2]); /* Analog Control #2 */ ++ i2c_smbus_write_byte_data(client,0x22,ptr[3]); /* Mixer Control #1 */ ++ i2c_smbus_write_byte_data(client,0x2C,ptr[4]); /* Mixer Control #2 */ ++ i2c_smbus_write_byte_data(client,0x30,ptr[5]); /* ADCs gain control */ ++ i2c_smbus_write_byte_data(client,0x31,ptr[6]); /* Mixer Control #3 */ ++ i2c_smbus_write_byte_data(client,0x21,ptr[7]); /* Analog Control #2 */ + + return 0; + } + + static +-int determine_norm(struct i2c_device* dev) ++int determine_norm(struct i2c_client* client) + { +- struct saa7110* decoder = dev->data; + int status; + + /* mode changed, start automatic detection */ +- status = saa7110_read(decoder); ++ status = i2c_smbus_read_byte(client); + if ((status & 3) == 0) { +- saa7110_write(decoder,0x06,0x80); ++ i2c_smbus_write_byte_data(client,0x06,0x80); + if (status & 0x20) { +- DEBUG(printk(KERN_INFO "%s: norm=bw60\n",dev->name)); +- saa7110_write(decoder,0x2E,0x81); ++ DEBUG(printk(KERN_INFO "%s: norm=bw60\n",adp->name)); ++ i2c_smbus_write_byte_data(client,0x2E,0x81); + return VIDEO_MODE_NTSC; + } +- DEBUG(printk(KERN_INFO "%s: norm=bw50\n",dev->name)); +- saa7110_write(decoder,0x2E,0x9A); ++ DEBUG(printk(KERN_INFO "%s: norm=bw50\n",adp->name)); ++ i2c_smbus_write_byte_data(client,0x2E,0x9A); + return VIDEO_MODE_PAL; + } + +- saa7110_write(decoder,0x06,0x00); ++ i2c_smbus_write_byte_data(client,0x06,0x00); + if (status & 0x20) { /* 60Hz */ +- DEBUG(printk(KERN_INFO "%s: norm=ntsc\n",dev->name)); +- saa7110_write(decoder,0x0D,0x06); +- saa7110_write(decoder,0x11,0x2C); +- saa7110_write(decoder,0x2E,0x81); ++ DEBUG(printk(KERN_INFO "%s: norm=ntsc\n",adp->name)); ++ i2c_smbus_write_byte_data(client,0x0D,0x06); ++ i2c_smbus_write_byte_data(client,0x11,0x2C); ++ i2c_smbus_write_byte_data(client,0x2E,0x81); + return VIDEO_MODE_NTSC; + } + + /* 50Hz -> PAL/SECAM */ +- saa7110_write(decoder,0x0D,0x06); +- saa7110_write(decoder,0x11,0x59); +- saa7110_write(decoder,0x2E,0x9A); ++ i2c_smbus_write_byte_data(client,0x0D,0x06); ++ i2c_smbus_write_byte_data(client,0x11,0x59); ++ i2c_smbus_write_byte_data(client,0x2E,0x9A); + + mdelay(150); /* pause 150 ms */ + +- status = saa7110_read(decoder); ++ status = i2c_smbus_read_byte(client); + if ((status & 0x03) == 0x01) { + DEBUG(printk(KERN_INFO "%s: norm=secam\n",dev->name)); +- saa7110_write(decoder,0x0D,0x07); ++ i2c_smbus_write_byte_data(client,0x0D,0x07); + return VIDEO_MODE_SECAM; + } + DEBUG(printk(KERN_INFO "%s: norm=pal\n",dev->name)); +@@ -188,7 +147,7 @@ + } + + static +-int saa7110_attach(struct i2c_device *device) ++int saa7110_attach(struct i2c_adapter *adap, int addr, unsigned short flags, int kind) + { + static const unsigned char initseq[] = { + 0, 0x4C, 0x3C, 0x0D, 0xEF, 0xBD, 0xF0, 0x00, 0x00, +@@ -198,20 +157,28 @@ + 0xD9, 0x17, 0x40, 0x41, 0x80, 0x41, 0x80, 0x4F, + 0xFE, 0x01, 0xCF, 0x0F, 0x03, 0x01, 0x81, 0x03, + 0x40, 0x75, 0x01, 0x8C, 0x03}; +- struct saa7110* decoder; ++ struct saa7110 *decoder; ++ struct i2c_client *client; + int rv; +- +- device->data = decoder = kmalloc(sizeof(struct saa7110), GFP_KERNEL); +- if (device->data == 0) ++ client=kmalloc(sizeof(*client), GFP_KERNEL); ++ if(client == NULL) + return -ENOMEM; +- +- MOD_INC_USE_COUNT; ++ client_template.adapter = adap; ++ client_template.addr = addr; ++ memcpy(client, &client_template, sizeof(*client)); ++ ++ decoder = kmalloc(sizeof(*decoder), GFP_KERNEL); ++ if (decoder == NULL) { ++ kfree(client); ++ return -ENOMEM; ++ } + + /* clear our private data */ +- memset(decoder, 0, sizeof(struct saa7110)); +- strcpy(device->name, "saa7110"); +- decoder->bus = device->bus; +- decoder->addr = device->addr; ++ memset(decoder, 0, sizeof(*decoder)); ++ strcpy(client->name, IF_NAME); ++ decoder->client = client; ++ client->data = decoder; ++ decoder->addr = addr; + decoder->norm = VIDEO_MODE_PAL; + decoder->input = 0; + decoder->enable = 1; +@@ -220,40 +187,52 @@ + decoder->hue = 32768; + decoder->sat = 32768; + +- rv = saa7110_write_block(decoder, initseq, sizeof(initseq)); ++ rv = i2c_master_send(client, initseq, sizeof(initseq)); + if (rv < 0) +- printk(KERN_ERR "%s_attach: init status %d\n", device->name, rv); ++ printk(KERN_ERR "%s_attach: init status %d\n", client->name, rv); + else { +- saa7110_write(decoder,0x21,0x16); +- saa7110_write(decoder,0x0D,0x04); +- DEBUG(printk(KERN_INFO "%s_attach: chip version %x\n", device->name, saa7110_read(decoder))); +- saa7110_write(decoder,0x0D,0x06); ++ i2c_smbus_write_byte_data(client,0x21,0x16); ++ i2c_smbus_write_byte_data(client,0x0D,0x04); ++ DEBUG(printk(KERN_INFO "%s_attach: chip version %x\n", client->name, i2c_smbus_read_byte(client))); ++ i2c_smbus_write_byte_data(client,0x0D,0x06); + } + ++ init_MUTEX(&decoder->lock); ++ i2c_attach_client(client); ++ MOD_INC_USE_COUNT; + /* setup and implicit mode 0 select has been performed */ + return 0; + } + ++static ++int saa7110_probe(struct i2c_adapter *adap) ++{ ++ return i2c_probe(adap, &addr_data, saa7110_attach); ++} ++ + static +-int saa7110_detach(struct i2c_device *device) ++int saa7110_detach(struct i2c_client *client) + { +- struct saa7110* decoder = device->data; ++ struct saa7110* decoder = client->data; + +- DEBUG(printk(KERN_INFO "%s_detach\n",device->name)); ++ i2c_detach_client(client); ++ ++ DEBUG(printk(KERN_INFO "%s_detach\n",client->name)); + + /* stop further output */ +- saa7110_write(decoder,0x0E,0x00); ++ i2c_smbus_write_byte_data(client,0x0E,0x00); + +- kfree(device->data); ++ kfree(decoder); ++ kfree(client); + + MOD_DEC_USE_COUNT; + return 0; + } + + static +-int saa7110_command(struct i2c_device *device, unsigned int cmd, void *arg) ++int saa7110_command(struct i2c_client *client, unsigned int cmd, void *arg) + { +- struct saa7110* decoder = device->data; ++ struct saa7110* decoder = client->data; + int v; + + switch (cmd) { +@@ -272,11 +251,11 @@ + + case DECODER_GET_STATUS: + { +- struct saa7110* decoder = device->data; ++ struct saa7110* decoder = client->data; + int status; + int res = 0; + +- status = i2c_read(device->bus,device->addr|1); ++ status = i2c_smbus_read_byte(client); + if (status & 0x40) + res |= DECODER_STATUS_GOOD; + if (status & 0x03) +@@ -301,26 +280,26 @@ + v = *(int*)arg; + if (decoder->norm != v) { + decoder->norm = v; +- saa7110_write(decoder, 0x06, 0x00); ++ i2c_smbus_write_byte_data(client, 0x06, 0x00); + switch (v) { + case VIDEO_MODE_NTSC: +- saa7110_write(decoder, 0x0D, 0x06); +- saa7110_write(decoder, 0x11, 0x2C); +- saa7110_write(decoder, 0x30, 0x81); +- saa7110_write(decoder, 0x2A, 0xDF); ++ i2c_smbus_write_byte_data(client, 0x0D, 0x06); ++ i2c_smbus_write_byte_data(client, 0x11, 0x2C); ++ i2c_smbus_write_byte_data(client, 0x30, 0x81); ++ i2c_smbus_write_byte_data(client, 0x2A, 0xDF); + break; + case VIDEO_MODE_PAL: +- saa7110_write(decoder, 0x0D, 0x06); +- saa7110_write(decoder, 0x11, 0x59); +- saa7110_write(decoder, 0x2E, 0x9A); ++ i2c_smbus_write_byte_data(client, 0x0D, 0x06); ++ i2c_smbus_write_byte_data(client, 0x11, 0x59); ++ i2c_smbus_write_byte_data(client, 0x2E, 0x9A); + break; + case VIDEO_MODE_SECAM: +- saa7110_write(decoder, 0x0D, 0x07); +- saa7110_write(decoder, 0x11, 0x59); +- saa7110_write(decoder, 0x2E, 0x9A); ++ i2c_smbus_write_byte_data(client, 0x0D, 0x07); ++ i2c_smbus_write_byte_data(client, 0x11, 0x59); ++ i2c_smbus_write_byte_data(client, 0x2E, 0x9A); + break; + case VIDEO_MODE_AUTO: +- *(int*)arg = determine_norm(device); ++ *(int*)arg = determine_norm(client); + break; + default: + return -EPERM; +@@ -334,7 +313,7 @@ + return -EINVAL; + if (decoder->input != v) { + decoder->input = v; +- saa7110_selmux(device, v); ++ saa7110_selmux(client, v); + } + break; + +@@ -349,7 +328,7 @@ + v = *(int*)arg; + if (decoder->enable != v) { + decoder->enable = v; +- saa7110_write(decoder,0x0E, v ? 0x18 : 0x00); ++ i2c_smbus_write_byte_data(client,0x0E, v ? 0x18 : 0x00); + } + break; + +@@ -360,22 +339,22 @@ + if (decoder->bright != pic->brightness) { + /* We want 0 to 255 we get 0-65535 */ + decoder->bright = pic->brightness; +- saa7110_write(decoder, 0x19, decoder->bright >> 8); ++ i2c_smbus_write_byte_data(client, 0x19, decoder->bright >> 8); + } + if (decoder->contrast != pic->contrast) { + /* We want 0 to 127 we get 0-65535 */ + decoder->contrast = pic->contrast; +- saa7110_write(decoder, 0x13, decoder->contrast >> 9); ++ i2c_smbus_write_byte_data(client, 0x13, decoder->contrast >> 9); + } + if (decoder->sat != pic->colour) { + /* We want 0 to 127 we get 0-65535 */ + decoder->sat = pic->colour; +- saa7110_write(decoder, 0x12, decoder->sat >> 9); ++ i2c_smbus_write_byte_data(client, 0x12, decoder->sat >> 9); + } + if (decoder->hue != pic->hue) { + /* We want -128 to 127 we get 0-65535 */ + decoder->hue = pic->hue; +- saa7110_write(decoder, 0x07, (decoder->hue>>8)-128); ++ i2c_smbus_write_byte_data(client, 0x07, (decoder->hue>>8)-128); + } + } + break; +@@ -383,7 +362,7 @@ + case DECODER_DUMP: + for (v=0; v<34; v+=16) { + int j; +- DEBUG(printk(KERN_INFO "%s: %03x\n",device->name,v)); ++ DEBUG(printk(KERN_INFO "%s: %03x\n",client->name,v)); + for (j=0; j<16; j++) { + DEBUG(printk(KERN_INFO " %02x",decoder->reg[v+j])); + } +@@ -402,24 +381,30 @@ + + static struct i2c_driver i2c_driver_saa7110 = + { +- "saa7110", /* name */ +- +- I2C_DRIVERID_VIDEODECODER, /* in i2c.h */ +- I2C_SAA7110, I2C_SAA7110+1, /* Addr range */ +- +- saa7110_attach, ++ IF_NAME, /* name */ ++ I2C_DRIVERID_SAA7110, /* in i2c.h */ ++ I2C_DF_NOTIFY, /* Addr range */ ++ saa7110_probe, + saa7110_detach, + saa7110_command + }; ++static struct i2c_client client_template = { ++ "saa7110_client", ++ -1, ++ 0, ++ 0, ++ NULL, ++ &i2c_driver_saa7110 ++}; + + static int saa7110_init(void) + { +- return i2c_register_driver(&i2c_driver_saa7110); ++ return i2c_add_driver(&i2c_driver_saa7110); + } + + static void saa7110_exit(void) + { +- i2c_unregister_driver(&i2c_driver_saa7110); ++ i2c_del_driver(&i2c_driver_saa7110); + } + + + + diff -Nru a/Documentation/i2c/i2c-pport b/Documentation/i2c/i2c-pport --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/i2c/i2c-pport Fri Aug 16 14:35:01 2002 @@ -0,0 +1,45 @@ +Primitive parallel port is driver for i2c bus, which exploits +features of modern bidirectional parallel ports. + +Bidirectional ports have particular bits connected in following way: + + | + /-----| R + --o| |-----| + read \-----| /------- Out pin + |/ + - -|\ + write V + | + --- + + +It means when output is set to 1 we can read the port. Therefore +we can use 2 pins of parallel port as SDA and SCL for i2c bus. It +is not necessary to add any external - additional parts, we can +read and write the same port simultaneously. + I only use register base+2 so it is possible to use all +8 data bits of parallel port for other applications (I have +connected EEPROM and LCD display). I do not use bit Enable Bi-directional + Port. The only disadvantage is we can only support 5V chips. + +Layout: + +Cannon 25 pin + +SDA - connect to pin 14 (Auto Linefeed) +SCL - connect to pin 16 (Initialize Printer) +GND - connect to pin 18-25 ++5V - use external supply (I use 5V from 3.5" floppy connector) + +no pullups requied + +Module parameters: + +base = 0xXXX +XXX - 278 or 378 + +That's all. + +Daniel Smolik +marvin@sitour.cz diff -Nru a/Documentation/i2c/i2c-velleman b/Documentation/i2c/i2c-velleman --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/Documentation/i2c/i2c-velleman Fri Aug 16 14:35:01 2002 @@ -0,0 +1,27 @@ +i2c-velleman driver +------------------- +This is a driver for i2c-hw access for Velleman K9000 and other adapters. + +Useful links +------------ +Velleman: + http://www.velleman.be/ + +Velleman K8000 Howto: + http://howto.htlw16.ac.at/k8000-howto.html + + +K8000 and K8005 libraries +------------------------- +The project has lead to new libs for the Velleman K8000 and K8005.. +LIBK8000 v1.99.1 and LIBK8005 v0.21 + +With these libs you can control the K8000 and K8005 with the original +simple commands which are in the original Velleman software. +Like SetIOchannel, ReadADchannel, SendStepCCWFull and many more. +Via i2c kernel device /dev/velleman + +The libs can be found on http://groups.yahoo.com/group/k8000/files/linux/ + +The Velleman K8000 interface card on http://www.velleman.be/kits/k8000.htm +The Velleman K8005 steppermotorcard on http://www.velleman.be/kits/k8005.htm diff -Nru a/Documentation/i2c/smbus-protocol b/Documentation/i2c/smbus-protocol --- a/Documentation/i2c/smbus-protocol Fri Aug 16 14:34:57 2002 +++ b/Documentation/i2c/smbus-protocol Fri Aug 16 14:34:57 2002 @@ -1,3 +1,10 @@ +SMBus Protocol Summary +====================== +The following is a summary of the SMBus protocol. It applies to +all revisions of the protocol (1.0, 1.1, and 2.0). +Certain protocol features which are not supported by +this package are briefly described at the end of this document. + Some adapters understand only the SMBus (System Management Bus) protocol, which is a subset from the I2C protocol. Fortunately, many devices use only the same subset, which makes it possible to put them on an SMBus. @@ -6,7 +13,7 @@ I2C protocol). This makes it possible to use the device driver on both SMBus adapters and I2C adapters (the SMBus command set is automatically translated to I2C on I2C adapters, but plain I2C commands can not be -handled at all on a pure SMBus adapter). +handled at all on most pure SMBus adapters). Below is a list of SMBus commands. @@ -109,7 +116,7 @@ SMBus Block Read ================ -This command reads a block of upto 32 bytes from a device, from a +This command reads a block of up to 32 bytes from a device, from a designated register that is specified through the Comm byte. The amount of data is specified by the device in the Count byte. @@ -120,8 +127,90 @@ SMBus Block Write ================= -The opposite of the Block Read command, this writes upto 32 bytes to +The opposite of the Block Read command, this writes up to 32 bytes to a device, to a designated register that is specified through the Comm byte. The amount of data is specified in the Count byte. S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P + + +SMBus Block Process Call +======================== + +SMBus Block Process Call was introduced in Revision 2.0 of the specification. + +This command selects a device register (through the Comm byte), sends +1 to 31 bytes of data to it, and reads 1 to 31 bytes of data in return. + +S Addr Wr [A] Comm [A] Count [A] Data [A] ... + S Addr Rd [A] [Count] A [Data] ... A P + + +SMBus Host Notify +================= + +This command is sent from a SMBus device acting as a master to the +SMBus host acting as a slave. +It is the same form as Write Word, with the command code replaced by the +alerting device's address. + +[S] [HostAddr] [Wr] A [DevAddr] A [DataLow] A [DataHigh] A [P] + + +Packet Error Checking (PEC) +=========================== +Packet Error Checking was introduced in Revision 1.1 of the specification. + +PEC adds a CRC-8 error-checking byte to all transfers. + + +Address Resolution Protocol (ARP) +================================= +The Address Resolution Protocol was introduced in Revision 2.0 of +the specification. It is a higher-layer protocol which uses the +messages above. + +ARP adds device enumeration and dynamic address assignment to +the protocol. All ARP communications use slave address 0x61 and +require PEC checksums. + + +I2C Block Transactions +====================== +The following I2C block transactions are supported by the +SMBus layer and are described here for completeness. +I2C block transactions do not limit the number of bytes transferred +but the SMBus layer places a limit of 32 bytes. + + +I2C Block Read +============== + +This command reads a block of bytes from a device, from a +designated register that is specified through the Comm byte. + +S Addr Wr [A] Comm [A] + S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P + + +I2C Block Read (2 Comm bytes) +============================= + +This command reads a block of bytes from a device, from a +designated register that is specified through the two Comm bytes. + +S Addr Wr [A] Comm1 [A] Comm2 [A] + S Addr Rd [A] [Data] A [Data] A ... A [Data] NA P + + +I2C Block Write +=============== + +The opposite of the Block Read command, this writes bytes to +a device, to a designated register that is specified through the +Comm byte. Note that command lengths of 0, 2, or more bytes are +supported as they are indistinguishable from data. + +S Addr Wr [A] Comm [A] Data [A] Data [A] ... [A] Data [A] P + + diff -Nru a/Documentation/i2c/summary b/Documentation/i2c/summary --- a/Documentation/i2c/summary Fri Aug 16 14:34:53 2002 +++ b/Documentation/i2c/summary Fri Aug 16 14:34:53 2002 @@ -59,16 +59,16 @@ i2c-algo-8xx: An algorithm for CPM's I2C device in Motorola 8xx processors (NOT BUILT BY DEFAULT) i2c-algo-bit: A bit-banging algorithm i2c-algo-pcf: A PCF 8584 style algorithm -i2c-algo-ppc405: An algorithm for the I2C device in IBM 405xx processors (NOT BUILT BY DEFAULT) +i2c-algo-ibmocp: An algorithm for the I2C device in IBM 4xx processors (NOT BUILT BY DEFAULT) Adapter drivers --------------- i2c-elektor: Elektor ISA card (uses i2c-algo-pcf) i2c-elv: ELV parallel port adapter (uses i2c-algo-bit) -i2c-pcf-epp: PCF8584 on a EPP parallel port (uses i2c-algo-pcf) (BROKEN - missing i2c-pcf-epp.h) +i2c-pcf-epp: PCF8584 on a EPP parallel port (uses i2c-algo-pcf) (NOT mkpatched) i2c-philips-par: Philips style parallel port adapter (uses i2c-algo-bit) -i2c-ppc405: IBM 405xx processor I2C device (uses i2c-algo-ppc405) (NOT BUILT BY DEFAULT) +i2c-adap_ibmocp: IBM 4xx processor I2C device (uses i2c-algo-ibmocp) (NOT BUILT BY DEFAULT) i2c-pport: Primitive parallel port adapter (uses i2c-algo-bit) i2c-rpx: RPX board Motorola 8xx I2C device (uses i2c-algo-8xx) (NOT BUILT BY DEFAULT) i2c-velleman: Velleman K9000 parallel port adapter (uses i2c-algo-bit) diff -Nru a/Documentation/i2c/writing-clients b/Documentation/i2c/writing-clients --- a/Documentation/i2c/writing-clients Fri Aug 16 14:34:54 2002 +++ b/Documentation/i2c/writing-clients Fri Aug 16 14:34:54 2002 @@ -365,7 +365,7 @@ The detect client function is called by i2c_probe or i2c_detect. The `kind' parameter contains 0 if this call is due to a `force' -parameter, and 0 otherwise (for i2c_detect, it contains 0 if +parameter, and -1 otherwise (for i2c_detect, it contains 0 if this call is due to the generic `force' parameter, and the chip type number if it is due to a specific `force' parameter). diff -Nru a/arch/alpha/config.in b/arch/alpha/config.in --- a/arch/alpha/config.in Fri Aug 16 14:34:55 2002 +++ b/arch/alpha/config.in Fri Aug 16 14:34:55 2002 @@ -283,10 +283,6 @@ source drivers/md/Config.in -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - mainmenu_option next_comment comment 'ATA/ATAPI/MFM/RLL support' @@ -314,6 +310,8 @@ fi if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in + mainmenu_option next_comment comment 'Network device support' diff -Nru a/arch/cris/config.in b/arch/cris/config.in --- a/arch/cris/config.in Fri Aug 16 14:34:58 2002 +++ b/arch/cris/config.in Fri Aug 16 14:34:58 2002 @@ -135,12 +135,6 @@ source drivers/md/Config.in -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - -source drivers/telephony/Config.in - mainmenu_option next_comment comment 'ATA/IDE/MFM/RLL support' @@ -168,6 +162,8 @@ source drivers/message/i2o/Config.in if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in + mainmenu_option next_comment comment 'Network device support' @@ -186,6 +182,8 @@ source net/irda/Config.in source drivers/isdn/Config.in + +source drivers/telephony/Config.in mainmenu_option next_comment comment 'Old CD-ROM drivers (not SCSI, not IDE)' diff -Nru a/arch/i386/boot/setup.S b/arch/i386/boot/setup.S --- a/arch/i386/boot/setup.S Fri Aug 16 14:34:51 2002 +++ b/arch/i386/boot/setup.S Fri Aug 16 14:34:51 2002 @@ -1005,9 +1005,14 @@ ret # Descriptor tables +# +# NOTE: if you think the GDT is large, you can make it smaller by just +# defining the KERNEL_CS and KERNEL_DS entries and shifting the gdt +# address down by GDT_ENTRY_KERNEL_CS*8. This puts bogus entries into +# the GDT, but those wont be used so it's not a problem. +# gdt: - .word 0, 0, 0, 0 # dummy - .word 0, 0, 0, 0 # unused + .fill GDT_ENTRY_KERNEL_CS,8,0 .word 0xFFFF # 4Gb - (0x100000*0x1000 = 4Gb) .word 0 # base address = 0 diff -Nru a/arch/i386/config.in b/arch/i386/config.in --- a/arch/i386/config.in Fri Aug 16 14:34:56 2002 +++ b/arch/i386/config.in Fri Aug 16 14:34:56 2002 @@ -332,12 +332,6 @@ source drivers/md/Config.in -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - -source drivers/telephony/Config.in - source drivers/message/fusion/Config.in source drivers/ieee1394/Config.in @@ -345,6 +339,8 @@ source drivers/message/i2o/Config.in if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in + mainmenu_option next_comment comment 'Network device support' @@ -363,6 +359,8 @@ source net/irda/Config.in source drivers/isdn/Config.in + +source drivers/telephony/Config.in # # input before char - char/joystick depends on it. As does USB. diff -Nru a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile --- a/arch/i386/kernel/Makefile Fri Aug 16 14:34:54 2002 +++ b/arch/i386/kernel/Makefile Fri Aug 16 14:34:54 2002 @@ -6,7 +6,7 @@ O_TARGET := kernel.o -export-objs := mca.o mtrr.o i386_ksyms.o time.o +export-objs := mca.o msr.o i386_ksyms.o time.o obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \ ptrace.o i8259.o ioport.o ldt.o setup.o time.o sys_i386.o \ @@ -15,7 +15,6 @@ obj-y += cpu/ obj-$(CONFIG_MCA) += mca.o -obj-$(CONFIG_MTRR) += mtrr.o obj-$(CONFIG_X86_MSR) += msr.o obj-$(CONFIG_X86_CPUID) += cpuid.o obj-$(CONFIG_MICROCODE) += microcode.o diff -Nru a/arch/i386/kernel/acpi.c b/arch/i386/kernel/acpi.c --- a/arch/i386/kernel/acpi.c Fri Aug 16 14:35:00 2002 +++ b/arch/i386/kernel/acpi.c Fri Aug 16 14:35:00 2002 @@ -32,7 +32,6 @@ #include #include #include -#include #include #include #include @@ -53,11 +52,9 @@ Boot-time Configuration -------------------------------------------------------------------------- */ -#ifdef CONFIG_ACPI_BOOT - enum acpi_irq_model_id acpi_irq_model; - +#ifdef CONFIG_ACPI_BOOT /* * Use reserved fixmap pages for physical-to-virtual mappings of ACPI tables. * Note that the same range is used for each table, so tables that need to diff -Nru a/arch/i386/kernel/apm.c b/arch/i386/kernel/apm.c --- a/arch/i386/kernel/apm.c Fri Aug 16 14:34:52 2002 +++ b/arch/i386/kernel/apm.c Fri Aug 16 14:34:52 2002 @@ -214,6 +214,7 @@ #include #include #include +#include #include #include @@ -419,6 +420,7 @@ static DECLARE_WAIT_QUEUE_HEAD(apm_suspend_waitqueue); static struct apm_user * user_list; static spinlock_t user_list_lock = SPIN_LOCK_UNLOCKED; +static struct desc_struct bad_bios_desc = { 0, 0x00409200 }; static char driver_version[] = "1.16"; /* no spaces */ @@ -568,7 +570,12 @@ u32 *eax, u32 *ebx, u32 *ecx, u32 *edx, u32 *esi) { APM_DECL_SEGS - unsigned long flags; + unsigned long flags; + int cpu = smp_processor_id(); + struct desc_struct save_desc_40; + + save_desc_40 = cpu_gdt_table[cpu][0x40 / 8]; + cpu_gdt_table[cpu][0x40 / 8] = bad_bios_desc; local_save_flags(flags); APM_DO_CLI; @@ -591,6 +598,7 @@ : "memory", "cc"); APM_DO_RESTORE_SEGS; local_irq_restore(flags); + cpu_gdt_table[cpu][0x40 / 8] = save_desc_40; return *eax & 0xff; } @@ -613,6 +621,11 @@ u8 error; APM_DECL_SEGS unsigned long flags; + int cpu = smp_processor_id(); + struct desc_struct save_desc_40; + + save_desc_40 = cpu_gdt_table[cpu][0x40 / 8]; + cpu_gdt_table[cpu][0x40 / 8] = bad_bios_desc; local_save_flags(flags); APM_DO_CLI; @@ -639,6 +652,7 @@ } APM_DO_RESTORE_SEGS; local_irq_restore(flags); + cpu_gdt_table[smp_processor_id()][0x40 / 8] = save_desc_40; return error; } @@ -931,9 +945,9 @@ } static struct sysrq_key_op sysrq_poweroff_op = { - handler: handle_poweroff, - help_msg: "Off", - action_msg: "Power Off\n" + .handler = handle_poweroff, + .help_msg = "Off", + .action_msg = "Power Off\n" }; @@ -1826,12 +1840,12 @@ #endif static struct file_operations apm_bios_fops = { - owner: THIS_MODULE, - read: do_read, - poll: do_poll, - ioctl: do_ioctl, - open: do_open, - release: do_release, + .owner = THIS_MODULE, + .read = do_read, + .poll = do_poll, + .ioctl = do_ioctl, + .open = do_open, + .release = do_release, }; static struct miscdevice apm_device = { @@ -1927,13 +1941,13 @@ * NOTE: on SMP we call into the APM BIOS only on CPU#0, so it's * enough to modify CPU#0's GDT. */ - for (i = 0; i < NR_CPUS; i++) { - set_base(cpu_gdt_table[i][APM_40 >> 3], - __va((unsigned long)0x40 << 4)); - _set_limit((char *)&cpu_gdt_table[i][APM_40 >> 3], 4095 - (0x40 << 4)); + set_base(bad_bios_desc, __va((unsigned long)0x40 << 4)); + _set_limit((char *)&bad_bios_desc, 4095 - (0x40 << 4)); + + apm_bios_entry.offset = apm_info.bios.offset; + apm_bios_entry.segment = APM_CS; - apm_bios_entry.offset = apm_info.bios.offset; - apm_bios_entry.segment = APM_CS; + for (i = 0; i < NR_CPUS; i++) { set_base(cpu_gdt_table[i][APM_CS >> 3], __va((unsigned long)apm_info.bios.cseg << 4)); set_base(cpu_gdt_table[i][APM_CS_16 >> 3], diff -Nru a/arch/i386/kernel/bluesmoke.c b/arch/i386/kernel/bluesmoke.c --- a/arch/i386/kernel/bluesmoke.c Fri Aug 16 14:34:56 2002 +++ b/arch/i386/kernel/bluesmoke.c Fri Aug 16 14:34:56 2002 @@ -307,7 +307,7 @@ } static struct tq_struct mce_task = { - routine: do_mce_timer + .routine = do_mce_timer }; static void mce_timerfunc (unsigned long data) diff -Nru a/arch/i386/kernel/cpu/Makefile b/arch/i386/kernel/cpu/Makefile --- a/arch/i386/kernel/cpu/Makefile Fri Aug 16 14:34:58 2002 +++ b/arch/i386/kernel/cpu/Makefile Fri Aug 16 14:34:58 2002 @@ -13,4 +13,6 @@ obj-y += nexgen.o obj-y += umc.o +obj-$(CONFIG_MTRR) += mtrr/ + include $(TOPDIR)/Rules.make diff -Nru a/arch/i386/kernel/cpu/amd.c b/arch/i386/kernel/cpu/amd.c --- a/arch/i386/kernel/cpu/amd.c Fri Aug 16 14:35:01 2002 +++ b/arch/i386/kernel/cpu/amd.c Fri Aug 16 14:35:01 2002 @@ -189,8 +189,8 @@ } static struct cpu_dev amd_cpu_dev __initdata = { - c_vendor: "AMD", - c_ident: { "AuthenticAMD" }, + .c_vendor = "AMD", + .c_ident = { "AuthenticAMD" }, c_models: { { X86_VENDOR_AMD, 4, { @@ -203,9 +203,9 @@ } }, }, - c_init: init_amd, - c_identify: amd_identify, - c_size_cache: amd_size_cache, + .c_init = init_amd, + .c_identify = amd_identify, + .c_size_cache = amd_size_cache, }; int __init amd_init_cpu(void) diff -Nru a/arch/i386/kernel/cpu/centaur.c b/arch/i386/kernel/cpu/centaur.c --- a/arch/i386/kernel/cpu/centaur.c Fri Aug 16 14:34:52 2002 +++ b/arch/i386/kernel/cpu/centaur.c Fri Aug 16 14:34:52 2002 @@ -411,10 +411,10 @@ } static struct cpu_dev centaur_cpu_dev __initdata = { - c_vendor: "Centaur", - c_ident: { "CentaurHauls" }, - c_init: init_centaur, - c_size_cache: centaur_size_cache, + .c_vendor = "Centaur", + .c_ident = { "CentaurHauls" }, + .c_init = init_centaur, + .c_size_cache = centaur_size_cache, }; int __init centaur_init_cpu(void) diff -Nru a/arch/i386/kernel/cpu/common.c b/arch/i386/kernel/cpu/common.c --- a/arch/i386/kernel/cpu/common.c Fri Aug 16 14:34:52 2002 +++ b/arch/i386/kernel/cpu/common.c Fri Aug 16 14:34:52 2002 @@ -31,7 +31,7 @@ } static struct cpu_dev default_cpu = { - c_init: default_init, + .c_init = default_init, }; static struct cpu_dev * this_cpu = &default_cpu; @@ -423,6 +423,7 @@ { int cpu = smp_processor_id(); struct tss_struct * t = init_tss + cpu; + struct thread_struct *thread = ¤t->thread; if (test_and_set_bit(cpu, &cpu_initialized)) { printk(KERN_WARNING "CPU#%d already initialized!\n", cpu); @@ -447,9 +448,13 @@ */ if (cpu) { memcpy(cpu_gdt_table[cpu], cpu_gdt_table[0], GDT_SIZE); - cpu_gdt_descr[cpu].size = GDT_SIZE; + cpu_gdt_descr[cpu].size = GDT_SIZE - 1; cpu_gdt_descr[cpu].address = (unsigned long)cpu_gdt_table[cpu]; } + /* + * Set up the per-thread TLS descriptor cache: + */ + memcpy(thread->tls_array, cpu_gdt_table[cpu], GDT_ENTRY_TLS_MAX * 8); __asm__ __volatile__("lgdt %0": "=m" (cpu_gdt_descr[cpu])); __asm__ __volatile__("lidt %0": "=m" (idt_descr)); @@ -468,9 +473,9 @@ BUG(); enter_lazy_tlb(&init_mm, current, cpu); - t->esp0 = current->thread.esp0; + t->esp0 = thread->esp0; set_tss_desc(cpu,t); - cpu_gdt_table[cpu][TSS_ENTRY].b &= 0xfffffdff; + cpu_gdt_table[cpu][GDT_ENTRY_TSS].b &= 0xfffffdff; load_TR_desc(); load_LDT(&init_mm.context); diff -Nru a/arch/i386/kernel/cpu/cyrix.c b/arch/i386/kernel/cpu/cyrix.c --- a/arch/i386/kernel/cpu/cyrix.c Fri Aug 16 14:34:54 2002 +++ b/arch/i386/kernel/cpu/cyrix.c Fri Aug 16 14:34:54 2002 @@ -322,10 +322,10 @@ } static struct cpu_dev cyrix_cpu_dev __initdata = { - c_vendor: "Cyrix", - c_ident: { "CyrixInstead" }, - c_init: init_cyrix, - c_identify: cyrix_identify, + .c_vendor = "Cyrix", + .c_ident = { "CyrixInstead" }, + .c_init = init_cyrix, + .c_identify = cyrix_identify, }; int __init cyrix_init_cpu(void) @@ -337,10 +337,10 @@ //early_arch_initcall(cyrix_init_cpu); static struct cpu_dev nsc_cpu_dev __initdata = { - c_vendor: "NSC", - c_ident: { "Geode by NSC" }, - c_init: init_cyrix, - c_identify: generic_identify, + .c_vendor = "NSC", + .c_ident = { "Geode by NSC" }, + .c_init = init_cyrix, + .c_identify = generic_identify, }; int __init nsc_init_cpu(void) diff -Nru a/arch/i386/kernel/cpu/intel.c b/arch/i386/kernel/cpu/intel.c --- a/arch/i386/kernel/cpu/intel.c Fri Aug 16 14:34:55 2002 +++ b/arch/i386/kernel/cpu/intel.c Fri Aug 16 14:34:55 2002 @@ -327,8 +327,8 @@ } static struct cpu_dev intel_cpu_dev __initdata = { - c_vendor: "Intel", - c_ident: { "GenuineIntel" }, + .c_vendor = "Intel", + .c_ident = { "GenuineIntel" }, c_models: { { X86_VENDOR_INTEL, 4, { @@ -375,9 +375,9 @@ } }, }, - c_init: init_intel, - c_identify: generic_identify, - c_size_cache: intel_size_cache, + .c_init = init_intel, + .c_identify = generic_identify, + .c_size_cache = intel_size_cache, }; __init int intel_cpu_init(void) diff -Nru a/arch/i386/kernel/cpu/mtrr/Makefile b/arch/i386/kernel/cpu/mtrr/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/cpu/mtrr/Makefile Fri Aug 16 14:35:01 2002 @@ -0,0 +1,8 @@ +obj-y := main.o if.o generic.o state.o +obj-y += amd.o +obj-y += cyrix.o +obj-y += centaur.o + +export-objs := main.o + +include $(TOPDIR)/Rules.make diff -Nru a/arch/i386/kernel/cpu/mtrr/amd.c b/arch/i386/kernel/cpu/mtrr/amd.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/cpu/mtrr/amd.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,121 @@ +#include +#include +#include +#include + +#include "mtrr.h" + +static void +amd_get_mtrr(unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type * type) +{ + unsigned long low, high; + + rdmsr(MSR_K6_UWCCR, low, high); + /* Upper dword is region 1, lower is region 0 */ + if (reg == 1) + low = high; + /* The base masks off on the right alignment */ + *base = (low & 0xFFFE0000) >> PAGE_SHIFT; + *type = 0; + if (low & 1) + *type = MTRR_TYPE_UNCACHABLE; + if (low & 2) + *type = MTRR_TYPE_WRCOMB; + if (!(low & 3)) { + *size = 0; + return; + } + /* + * This needs a little explaining. The size is stored as an + * inverted mask of bits of 128K granularity 15 bits long offset + * 2 bits + * + * So to get a size we do invert the mask and add 1 to the lowest + * mask bit (4 as its 2 bits in). This gives us a size we then shift + * to turn into 128K blocks + * + * eg 111 1111 1111 1100 is 512K + * + * invert 000 0000 0000 0011 + * +1 000 0000 0000 0100 + * *128K ... + */ + low = (~low) & 0x1FFFC; + *size = (low + 4) << (15 - PAGE_SHIFT); + return; +} + +static void amd_set_mtrr(unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type) +/* [SUMMARY] Set variable MTRR register on the local CPU. + The register to set. + The base address of the region. + The size of the region. If this is 0 the region is disabled. + The type of the region. + If TRUE, do the change safely. If FALSE, safety measures should + be done externally. + [RETURNS] Nothing. +*/ +{ + u32 regs[2]; + + /* + * Low is MTRR0 , High MTRR 1 + */ + rdmsr(MSR_K6_UWCCR, regs[0], regs[1]); + /* + * Blank to disable + */ + if (size == 0) + regs[reg] = 0; + else + /* Set the register to the base, the type (off by one) and an + inverted bitmask of the size The size is the only odd + bit. We are fed say 512K We invert this and we get 111 1111 + 1111 1011 but if you subtract one and invert you get the + desired 111 1111 1111 1100 mask + + But ~(x - 1) == ~x + 1 == -x. Two's complement rocks! */ + regs[reg] = (-size >> (15 - PAGE_SHIFT) & 0x0001FFFC) + | (base << PAGE_SHIFT) | (type + 1); + + /* + * The writeback rule is quite specific. See the manual. Its + * disable local interrupts, write back the cache, set the mtrr + */ + wbinvd(); + wrmsr(MSR_K6_UWCCR, regs[0], regs[1]); +} + +static int amd_validate_add_page(unsigned long base, unsigned long size, unsigned int type) +{ + /* Apply the K6 block alignment and size rules + In order + o Uncached or gathering only + o 128K or bigger block + o Power of 2 block + o base suitably aligned to the power + */ + if (type > MTRR_TYPE_WRCOMB || size < (1 << (17 - PAGE_SHIFT)) + || (size & ~(size - 1)) - size || (base & (size - 1))) + return -EINVAL; + return 0; +} + +static struct mtrr_ops amd_mtrr_ops = { + .vendor = X86_VENDOR_AMD, + .set = amd_set_mtrr, + .get = amd_get_mtrr, + .get_free_region = generic_get_free_region, + .validate_add_page = amd_validate_add_page, + .have_wrcomb = positive_have_wrcomb, +}; + +int __init amd_init_mtrr(void) +{ + set_mtrr_ops(&amd_mtrr_ops); + return 0; +} + +//arch_initcall(amd_mtrr_init); diff -Nru a/arch/i386/kernel/cpu/mtrr/centaur.c b/arch/i386/kernel/cpu/mtrr/centaur.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/cpu/mtrr/centaur.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,219 @@ +#include +#include +#include +#include +#include "mtrr.h" + +static struct { + unsigned long high; + unsigned long low; +} centaur_mcr[8]; + +static u8 centaur_mcr_reserved; +static u8 centaur_mcr_type; /* 0 for winchip, 1 for winchip2 */ + +/* + * Report boot time MCR setups + */ + +static int +centaur_get_free_region(unsigned long base, unsigned long size) +/* [SUMMARY] Get a free MTRR. + The starting (base) address of the region. + The size (in bytes) of the region. + [RETURNS] The index of the region on success, else -1 on error. +*/ +{ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize; + + max = num_var_ranges; + for (i = 0; i < max; ++i) { + if (centaur_mcr_reserved & (1 << i)) + continue; + mtrr_if->get(i, &lbase, &lsize, <ype); + if (lsize == 0) + return i; + } + return -ENOSPC; +} + +void +mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) +{ + centaur_mcr[mcr].low = lo; + centaur_mcr[mcr].high = hi; +} + +static void +centaur_get_mcr(unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type * type) +{ + *base = centaur_mcr[reg].high >> PAGE_SHIFT; + *size = -(centaur_mcr[reg].low & 0xfffff000) >> PAGE_SHIFT; + *type = MTRR_TYPE_WRCOMB; /* If it is there, it is write-combining */ + if (centaur_mcr_type == 1 && ((centaur_mcr[reg].low & 31) & 2)) + *type = MTRR_TYPE_UNCACHABLE; + if (centaur_mcr_type == 1 && (centaur_mcr[reg].low & 31) == 25) + *type = MTRR_TYPE_WRBACK; + if (centaur_mcr_type == 0 && (centaur_mcr[reg].low & 31) == 31) + *type = MTRR_TYPE_WRBACK; + +} + +static void centaur_set_mcr(unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type) +{ + unsigned long low, high; + + if (size == 0) { + /* Disable */ + high = low = 0; + } else { + high = base << PAGE_SHIFT; + if (centaur_mcr_type == 0) + low = -size << PAGE_SHIFT | 0x1f; /* only support write-combining... */ + else { + if (type == MTRR_TYPE_UNCACHABLE) + low = -size << PAGE_SHIFT | 0x02; /* NC */ + else + low = -size << PAGE_SHIFT | 0x09; /* WWO,WC */ + } + } + centaur_mcr[reg].high = high; + centaur_mcr[reg].low = low; + wrmsr(MSR_IDT_MCR0 + reg, low, high); +} +/* + * Initialise the later (saner) Winchip MCR variant. In this version + * the BIOS can pass us the registers it has used (but not their values) + * and the control register is read/write + */ + +static void __init +centaur_mcr1_init(void) +{ + unsigned i; + u32 lo, hi; + + /* Unfortunately, MCR's are read-only, so there is no way to + * find out what the bios might have done. + */ + + rdmsr(MSR_IDT_MCR_CTRL, lo, hi); + if (((lo >> 17) & 7) == 1) { /* Type 1 Winchip2 MCR */ + lo &= ~0x1C0; /* clear key */ + lo |= 0x040; /* set key to 1 */ + wrmsr(MSR_IDT_MCR_CTRL, lo, hi); /* unlock MCR */ + } + + centaur_mcr_type = 1; + + /* + * Clear any unconfigured MCR's. + */ + + for (i = 0; i < 8; ++i) { + if (centaur_mcr[i].high == 0 && centaur_mcr[i].low == 0) { + if (!(lo & (1 << (9 + i)))) + wrmsr(MSR_IDT_MCR0 + i, 0, 0); + else + /* + * If the BIOS set up an MCR we cannot see it + * but we don't wish to obliterate it + */ + centaur_mcr_reserved |= (1 << i); + } + } + /* + * Throw the main write-combining switch... + * However if OOSTORE is enabled then people have already done far + * cleverer things and we should behave. + */ + + lo |= 15; /* Write combine enables */ + wrmsr(MSR_IDT_MCR_CTRL, lo, hi); +} + +/* + * Initialise the original winchip with read only MCR registers + * no used bitmask for the BIOS to pass on and write only control + */ + +static void __init +centaur_mcr0_init(void) +{ + unsigned i; + + /* Unfortunately, MCR's are read-only, so there is no way to + * find out what the bios might have done. + */ + + /* Clear any unconfigured MCR's. + * This way we are sure that the centaur_mcr array contains the actual + * values. The disadvantage is that any BIOS tweaks are thus undone. + * + */ + for (i = 0; i < 8; ++i) { + if (centaur_mcr[i].high == 0 && centaur_mcr[i].low == 0) + wrmsr(MSR_IDT_MCR0 + i, 0, 0); + } + + wrmsr(MSR_IDT_MCR_CTRL, 0x01F0001F, 0); /* Write only */ +} + +/* + * Initialise Winchip series MCR registers + */ + +static void __init +centaur_mcr_init(void) +{ + struct set_mtrr_context ctxt; + + set_mtrr_prepare_save(&ctxt); + set_mtrr_cache_disable(&ctxt); + + if (boot_cpu_data.x86_model == 4) + centaur_mcr0_init(); + else if (boot_cpu_data.x86_model == 8 || boot_cpu_data.x86_model == 9) + centaur_mcr1_init(); + + set_mtrr_done(&ctxt); +} + +static int centaur_validate_add_page(unsigned long base, + unsigned long size, unsigned int type) +{ + /* + * FIXME: Winchip2 supports uncached + */ + if (type != MTRR_TYPE_WRCOMB && + (centaur_mcr_type == 0 || type != MTRR_TYPE_UNCACHABLE)) { + printk(KERN_WARNING + "mtrr: only write-combining%s supported\n", + centaur_mcr_type ? " and uncacheable are" + : " is"); + return -EINVAL; + } + return 0; +} + +static struct mtrr_ops centaur_mtrr_ops = { + .vendor = X86_VENDOR_CENTAUR, + .init = centaur_mcr_init, + .set = centaur_set_mcr, + .get = centaur_get_mcr, + .get_free_region = centaur_get_free_region, + .validate_add_page = centaur_validate_add_page, + .have_wrcomb = positive_have_wrcomb, +}; + +int __init centaur_init_mtrr(void) +{ + set_mtrr_ops(¢aur_mtrr_ops); + return 0; +} + +//arch_initcall(centaur_init_mtrr); diff -Nru a/arch/i386/kernel/cpu/mtrr/changelog b/arch/i386/kernel/cpu/mtrr/changelog --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/cpu/mtrr/changelog Fri Aug 16 14:35:01 2002 @@ -0,0 +1,229 @@ + ChangeLog + + Prehistory Martin Tischhäuser + Initial register-setting code (from proform-1.0). + 19971216 Richard Gooch + Original version for /proc/mtrr interface, SMP-safe. + v1.0 + 19971217 Richard Gooch + Bug fix for ioctls()'s. + Added sample code in Documentation/mtrr.txt + v1.1 + 19971218 Richard Gooch + Disallow overlapping regions. + 19971219 Jens Maurer + Register-setting fixups. + v1.2 + 19971222 Richard Gooch + Fixups for kernel 2.1.75. + v1.3 + 19971229 David Wragg + Register-setting fixups and conformity with Intel conventions. + 19971229 Richard Gooch + Cosmetic changes and wrote this ChangeLog ;-) + 19980106 Richard Gooch + Fixups for kernel 2.1.78. + v1.4 + 19980119 David Wragg + Included passive-release enable code (elsewhere in PCI setup). + v1.5 + 19980131 Richard Gooch + Replaced global kernel lock with private spinlock. + v1.6 + 19980201 Richard Gooch + Added wait for other CPUs to complete changes. + v1.7 + 19980202 Richard Gooch + Bug fix in definition of for UP. + v1.8 + 19980319 Richard Gooch + Fixups for kernel 2.1.90. + 19980323 Richard Gooch + Move SMP BIOS fixup before secondary CPUs call + v1.9 + 19980325 Richard Gooch + Fixed test for overlapping regions: confused by adjacent regions + 19980326 Richard Gooch + Added wbinvd in . + 19980401 Richard Gooch + Bug fix for non-SMP compilation. + 19980418 David Wragg + Fixed-MTRR synchronisation for SMP and use atomic operations + instead of spinlocks. + 19980418 Richard Gooch + Differentiate different MTRR register classes for BIOS fixup. + v1.10 + 19980419 David Wragg + Bug fix in variable MTRR synchronisation. + v1.11 + 19980419 Richard Gooch + Fixups for kernel 2.1.97. + v1.12 + 19980421 Richard Gooch + Safer synchronisation across CPUs when changing MTRRs. + v1.13 + 19980423 Richard Gooch + Bugfix for SMP systems without MTRR support. + v1.14 + 19980427 Richard Gooch + Trap calls to and on non-MTRR machines. + v1.15 + 19980427 Richard Gooch + Use atomic bitops for setting SMP change mask. + v1.16 + 19980428 Richard Gooch + Removed spurious diagnostic message. + v1.17 + 19980429 Richard Gooch + Moved register-setting macros into this file. + Moved setup code from init/main.c to i386-specific areas. + v1.18 + 19980502 Richard Gooch + Moved MTRR detection outside conditionals in . + v1.19 + 19980502 Richard Gooch + Documentation improvement: mention Pentium II and AGP. + v1.20 + 19980521 Richard Gooch + Only manipulate interrupt enable flag on local CPU. + Allow enclosed uncachable regions. + v1.21 + 19980611 Richard Gooch + Always define . + v1.22 + 19980901 Richard Gooch + Removed module support in order to tidy up code. + Added sanity check for / before . + Created addition queue for prior to SMP commence. + v1.23 + 19980902 Richard Gooch + Ported patch to kernel 2.1.120-pre3. + v1.24 + 19980910 Richard Gooch + Removed sanity checks and addition queue: Linus prefers an OOPS. + v1.25 + 19981001 Richard Gooch + Fixed harmless compiler warning in include/asm-i386/mtrr.h + Fixed version numbering and history for v1.23 -> v1.24. + v1.26 + 19990118 Richard Gooch + Added devfs support. + v1.27 + 19990123 Richard Gooch + Changed locking to spin with reschedule. + Made use of new . + v1.28 + 19990201 Zoltán Böszörményi + Extended the driver to be able to use Cyrix style ARRs. + 19990204 Richard Gooch + Restructured Cyrix support. + v1.29 + 19990204 Zoltán Böszörményi + Refined ARR support: enable MAPEN in set_mtrr_prepare() + and disable MAPEN in set_mtrr_done(). + 19990205 Richard Gooch + Minor cleanups. + v1.30 + 19990208 Zoltán Böszörményi + Protect plain 6x86s (and other processors without the + Page Global Enable feature) against accessing CR4 in + set_mtrr_prepare() and set_mtrr_done(). + 19990210 Richard Gooch + Turned and into function pointers. + v1.31 + 19990212 Zoltán Böszörményi + Major rewrite of cyrix_arr_init(): do not touch ARRs, + leave them as the BIOS have set them up. + Enable usage of all 8 ARRs. + Avoid multiplications by 3 everywhere and other + code clean ups/speed ups. + 19990213 Zoltán Böszörményi + Set up other Cyrix processors identical to the boot cpu. + Since Cyrix don't support Intel APIC, this is l'art pour l'art. + Weigh ARRs by size: + If size <= 32M is given, set up ARR# we were given. + If size > 32M is given, set up ARR7 only if it is free, + fail otherwise. + 19990214 Zoltán Böszörményi + Also check for size >= 256K if we are to set up ARR7, + mtrr_add() returns the value it gets from set_mtrr() + 19990218 Zoltán Böszörményi + Remove Cyrix "coma bug" workaround from here. + Moved to linux/arch/i386/kernel/setup.c and + linux/include/asm-i386/bugs.h + 19990228 Richard Gooch + Added MTRRIOC_KILL_ENTRY ioctl(2) + Trap for counter underflow in . + Trap for 4 MiB aligned regions for PPro, stepping <= 7. + 19990301 Richard Gooch + Created hook. + 19990305 Richard Gooch + Temporarily disable AMD support now MTRR capability flag is set. + v1.32 + 19990308 Zoltán Böszörményi + Adjust my changes (19990212-19990218) to Richard Gooch's + latest changes. (19990228-19990305) + v1.33 + 19990309 Richard Gooch + Fixed typo in message. + 19990310 Richard Gooch + Support K6-II/III based on Alan Cox's patches. + v1.34 + 19990511 Bart Hartgers + Support Centaur C6 MCR's. + 19990512 Richard Gooch + Minor cleanups. + v1.35 + 19990707 Zoltán Böszörményi + Check whether ARR3 is protected in cyrix_get_free_region() + and mtrr_del(). The code won't attempt to delete or change it + from now on if the BIOS protected ARR3. It silently skips ARR3 + in cyrix_get_free_region() or returns with an error code from + mtrr_del(). + 19990711 Zoltán Böszörményi + Reset some bits in the CCRs in cyrix_arr_init() to disable SMM + if ARR3 isn't protected. This is needed because if SMM is active + and ARR3 isn't protected then deleting and setting ARR3 again + may lock up the processor. With SMM entirely disabled, it does + not happen. + 19990812 Zoltán Böszörményi + Rearrange switch() statements so the driver accomodates to + the fact that the AMD Athlon handles its MTRRs the same way + as Intel does. + 19990814 Zoltán Böszörményi + Double check for Intel in mtrr_add()'s big switch() because + that revision check is only valid for Intel CPUs. + 19990819 Alan Cox + Tested Zoltan's changes on a pre production Athlon - 100% + success. + 19991008 Manfred Spraul + replaced spin_lock_reschedule() with a normal semaphore. + v1.36 + 20000221 Richard Gooch + Compile fix if procfs and devfs not enabled. + Formatting changes. + v1.37 + 20001109 H. Peter Anvin + Use the new centralized CPU feature detects. + + v1.38 + 20010309 Dave Jones + Add support for Cyrix III. + + v1.39 + 20010312 Dave Jones + Ugh, I broke AMD support. + Reworked fix by Troels Walsted Hansen + + v1.40 + 20010327 Dave Jones + Adapted Cyrix III support to include VIA C3. + + v2.0 + 20020306 Patrick Mochel + Split mtrr.c -> mtrr/*.c + Converted to Linux Kernel Coding Style + Fixed several minor nits in form + Moved some SMP-only functions out, so they can be used + for power management in the future. + TODO: Fix user interface cruft. diff -Nru a/arch/i386/kernel/cpu/mtrr/cyrix.c b/arch/i386/kernel/cpu/mtrr/cyrix.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/cpu/mtrr/cyrix.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,378 @@ +#include +#include +#include +#include +#include +#include "mtrr.h" + +int arr3_protected; + +static void +cyrix_get_arr(unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type * type) +{ + unsigned long flags; + unsigned char arr, ccr3, rcr, shift; + + arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ + + /* Save flags and disable interrupts */ + local_irq_save(flags); + + ccr3 = getCx86(CX86_CCR3); + setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ + ((unsigned char *) base)[3] = getCx86(arr); + ((unsigned char *) base)[2] = getCx86(arr + 1); + ((unsigned char *) base)[1] = getCx86(arr + 2); + rcr = getCx86(CX86_RCR_BASE + reg); + setCx86(CX86_CCR3, ccr3); /* disable MAPEN */ + + /* Enable interrupts if it was enabled previously */ + local_irq_restore(flags); + shift = ((unsigned char *) base)[1] & 0x0f; + *base >>= PAGE_SHIFT; + + /* Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 + * Note: shift==0xf means 4G, this is unsupported. + */ + if (shift) + *size = (reg < 7 ? 0x1UL : 0x40UL) << (shift - 1); + else + *size = 0; + + /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */ + if (reg < 7) { + switch (rcr) { + case 1: + *type = MTRR_TYPE_UNCACHABLE; + break; + case 8: + *type = MTRR_TYPE_WRBACK; + break; + case 9: + *type = MTRR_TYPE_WRCOMB; + break; + case 24: + default: + *type = MTRR_TYPE_WRTHROUGH; + break; + } + } else { + switch (rcr) { + case 0: + *type = MTRR_TYPE_UNCACHABLE; + break; + case 8: + *type = MTRR_TYPE_WRCOMB; + break; + case 9: + *type = MTRR_TYPE_WRBACK; + break; + case 25: + default: + *type = MTRR_TYPE_WRTHROUGH; + break; + } + } +} + +static int +cyrix_get_free_region(unsigned long base, unsigned long size) +/* [SUMMARY] Get a free ARR. + The starting (base) address of the region. + The size (in bytes) of the region. + [RETURNS] The index of the region on success, else -1 on error. +*/ +{ + int i; + mtrr_type ltype; + unsigned long lbase, lsize; + + /* If we are to set up a region >32M then look at ARR7 immediately */ + if (size > 0x2000) { + cyrix_get_arr(7, &lbase, &lsize, <ype); + if (lsize == 0) + return 7; + /* Else try ARR0-ARR6 first */ + } else { + for (i = 0; i < 7; i++) { + cyrix_get_arr(i, &lbase, &lsize, <ype); + if ((i == 3) && arr3_protected) + continue; + if (lsize == 0) + return i; + } + /* ARR0-ARR6 isn't free, try ARR7 but its size must be at least 256K */ + cyrix_get_arr(i, &lbase, &lsize, <ype); + if ((lsize == 0) && (size >= 0x40)) + return i; + } + return -ENOSPC; +} + +static void cyrix_set_arr(unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type) +{ + unsigned char arr, arr_type, arr_size; + u32 cr0, ccr3; + u32 cr4 = 0; + + arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ + + /* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */ + if (reg >= 7) + size >>= 6; + + size &= 0x7fff; /* make sure arr_size <= 14 */ + for (arr_size = 0; size; arr_size++, size >>= 1) ; + + if (reg < 7) { + switch (type) { + case MTRR_TYPE_UNCACHABLE: + arr_type = 1; + break; + case MTRR_TYPE_WRCOMB: + arr_type = 9; + break; + case MTRR_TYPE_WRTHROUGH: + arr_type = 24; + break; + default: + arr_type = 8; + break; + } + } else { + switch (type) { + case MTRR_TYPE_UNCACHABLE: + arr_type = 0; + break; + case MTRR_TYPE_WRCOMB: + arr_type = 8; + break; + case MTRR_TYPE_WRTHROUGH: + arr_type = 25; + break; + default: + arr_type = 9; + break; + } + } + + /* Save value of CR4 and clear Page Global Enable (bit 7) */ + if ( cpu_has_pge ) { + cr4 = read_cr4(); + write_cr4(cr4 & (unsigned char) ~(1 << 7)); + } + + /* Disable and flush caches. Note that wbinvd flushes the TLBs as + a side-effect */ + cr0 = read_cr0() | 0x40000000; + wbinvd(); + write_cr0(cr0); + wbinvd(); + + /* Cyrix ARRs - everything else were excluded at the top */ + ccr3 = getCx86(CX86_CCR3); + + /* Cyrix ARRs - everything else were excluded at the top */ + setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); + + base <<= PAGE_SHIFT; + setCx86(arr, ((unsigned char *) &base)[3]); + setCx86(arr + 1, ((unsigned char *) &base)[2]); + setCx86(arr + 2, (((unsigned char *) &base)[1]) | arr_size); + setCx86(CX86_RCR_BASE + reg, arr_type); + + /* Flush caches and TLBs */ + wbinvd(); + + /* Cyrix ARRs - everything else was excluded at the top */ + setCx86(CX86_CCR3, ccr3); + + /* Enable caches */ + write_cr0(read_cr0() & 0xbfffffff); + + /* Restore value of CR4 */ + if ( cpu_has_pge ) + write_cr4(cr4); +} + +typedef struct { + unsigned long base; + unsigned long size; + mtrr_type type; +} arr_state_t; + +arr_state_t arr_state[8] __initdata = { + {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, + {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL} +}; + +unsigned char ccr_state[7] __initdata = { 0, 0, 0, 0, 0, 0, 0 }; + +static void __init +cyrix_arr_init_secondary(void) +{ + int i; + u32 cr0, ccr3, cr4 = 0; + + /* flush cache and enable MAPEN */ + /* Save value of CR4 and clear Page Global Enable (bit 7) */ + if ( cpu_has_pge ) { + cr4 = read_cr4(); + write_cr4(cr4 & (unsigned char) ~(1 << 7)); + } + + /* Disable and flush caches. Note that wbinvd flushes the TLBs as + a side-effect */ + cr0 = read_cr0() | 0x40000000; + wbinvd(); + write_cr0(cr0); + wbinvd(); + + /* Cyrix ARRs - everything else were excluded at the top */ + ccr3 = getCx86(CX86_CCR3); + + /* Cyrix ARRs - everything else were excluded at the top */ + setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); + + /* the CCRs are not contiguous */ + for (i = 0; i < 4; i++) + setCx86(CX86_CCR0 + i, ccr_state[i]); + for (; i < 7; i++) + setCx86(CX86_CCR4 + i, ccr_state[i]); + for (i = 0; i < 8; i++) + cyrix_set_arr(i, arr_state[i].base, + arr_state[i].size, arr_state[i].type); + + /* Flush caches and TLBs */ + wbinvd(); + + /* Cyrix ARRs - everything else was excluded at the top */ + setCx86(CX86_CCR3, ccr3); + + /* Enable caches */ + write_cr0(read_cr0() & 0xbfffffff); + + /* Restore value of CR4 */ + if ( cpu_has_pge ) + write_cr4(cr4); +} + +/* + * On Cyrix 6x86(MX) and M II the ARR3 is special: it has connection + * with the SMM (System Management Mode) mode. So we need the following: + * Check whether SMI_LOCK (CCR3 bit 0) is set + * if it is set, write a warning message: ARR3 cannot be changed! + * (it cannot be changed until the next processor reset) + * if it is reset, then we can change it, set all the needed bits: + * - disable access to SMM memory through ARR3 range (CCR1 bit 7 reset) + * - disable access to SMM memory (CCR1 bit 2 reset) + * - disable SMM mode (CCR1 bit 1 reset) + * - disable write protection of ARR3 (CCR6 bit 1 reset) + * - (maybe) disable ARR3 + * Just to be sure, we enable ARR usage by the processor (CCR5 bit 5 set) + */ +static void __init +cyrix_arr_init(void) +{ + struct set_mtrr_context ctxt; + unsigned char ccr[7]; + int ccrc[7] = { 0, 0, 0, 0, 0, 0, 0 }; +#ifdef CONFIG_SMP + int i; +#endif + + /* flush cache and enable MAPEN */ + set_mtrr_prepare_save(&ctxt); + set_mtrr_cache_disable(&ctxt); + + /* Save all CCRs locally */ + ccr[0] = getCx86(CX86_CCR0); + ccr[1] = getCx86(CX86_CCR1); + ccr[2] = getCx86(CX86_CCR2); + ccr[3] = ctxt.ccr3; + ccr[4] = getCx86(CX86_CCR4); + ccr[5] = getCx86(CX86_CCR5); + ccr[6] = getCx86(CX86_CCR6); + + if (ccr[3] & 1) { + ccrc[3] = 1; + arr3_protected = 1; + } else { + /* Disable SMM mode (bit 1), access to SMM memory (bit 2) and + * access to SMM memory through ARR3 (bit 7). + */ + if (ccr[1] & 0x80) { + ccr[1] &= 0x7f; + ccrc[1] |= 0x80; + } + if (ccr[1] & 0x04) { + ccr[1] &= 0xfb; + ccrc[1] |= 0x04; + } + if (ccr[1] & 0x02) { + ccr[1] &= 0xfd; + ccrc[1] |= 0x02; + } + arr3_protected = 0; + if (ccr[6] & 0x02) { + ccr[6] &= 0xfd; + ccrc[6] = 1; /* Disable write protection of ARR3 */ + setCx86(CX86_CCR6, ccr[6]); + } + /* Disable ARR3. This is safe now that we disabled SMM. */ + /* cyrix_set_arr_up (3, 0, 0, 0, FALSE); */ + } + /* If we changed CCR1 in memory, change it in the processor, too. */ + if (ccrc[1]) + setCx86(CX86_CCR1, ccr[1]); + + /* Enable ARR usage by the processor */ + if (!(ccr[5] & 0x20)) { + ccr[5] |= 0x20; + ccrc[5] = 1; + setCx86(CX86_CCR5, ccr[5]); + } +#ifdef CONFIG_SMP + for (i = 0; i < 7; i++) + ccr_state[i] = ccr[i]; + for (i = 0; i < 8; i++) + cyrix_get_arr(i, + &arr_state[i].base, &arr_state[i].size, + &arr_state[i].type); +#endif + + set_mtrr_done(&ctxt); /* flush cache and disable MAPEN */ + + if (ccrc[5]) + printk("mtrr: ARR usage was not enabled, enabled manually\n"); + if (ccrc[3]) + printk("mtrr: ARR3 cannot be changed\n"); +/* + if ( ccrc[1] & 0x80) printk ("mtrr: SMM memory access through ARR3 disabled\n"); + if ( ccrc[1] & 0x04) printk ("mtrr: SMM memory access disabled\n"); + if ( ccrc[1] & 0x02) printk ("mtrr: SMM mode disabled\n"); +*/ + if (ccrc[6]) + printk("mtrr: ARR3 was write protected, unprotected\n"); +} + +static struct mtrr_ops cyrix_mtrr_ops = { + .vendor = X86_VENDOR_CYRIX, + .init = cyrix_arr_init, + .init_secondary = cyrix_arr_init_secondary, + .set = cyrix_set_arr, + .get = cyrix_get_arr, + .get_free_region = cyrix_get_free_region, + .validate_add_page = generic_validate_add_page, + .have_wrcomb = positive_have_wrcomb, +}; + +int __init cyrix_init_mtrr(void) +{ + set_mtrr_ops(&cyrix_mtrr_ops); + return 0; +} + +//arch_initcall(cyrix_init_mtrr); diff -Nru a/arch/i386/kernel/cpu/mtrr/generic.c b/arch/i386/kernel/cpu/mtrr/generic.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/cpu/mtrr/generic.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,184 @@ +#include +#include +#include +#include +#include +#include +#include "mtrr.h" + + +int generic_get_free_region(unsigned long base, unsigned long size) +/* [SUMMARY] Get a free MTRR. + The starting (base) address of the region. + The size (in bytes) of the region. + [RETURNS] The index of the region on success, else -1 on error. +*/ +{ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize; + + max = num_var_ranges; + for (i = 0; i < max; ++i) { + mtrr_if->get(i, &lbase, &lsize, <ype); + if (lsize == 0) + return i; + } + return -ENOSPC; +} + + +void generic_get_mtrr(unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type * type) +{ + unsigned long mask_lo, mask_hi, base_lo, base_hi; + + rdmsr(MTRRphysMask_MSR(reg), mask_lo, mask_hi); + if ((mask_lo & 0x800) == 0) { + /* Invalid (i.e. free) range */ + *base = 0; + *size = 0; + *type = 0; + return; + } + + rdmsr(MTRRphysBase_MSR(reg), base_lo, base_hi); + + /* Work out the shifted address mask. */ + mask_lo = size_or_mask | mask_hi << (32 - PAGE_SHIFT) + | mask_lo >> PAGE_SHIFT; + + /* This works correctly if size is a power of two, i.e. a + contiguous range. */ + *size = -mask_lo; + *base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT; + *type = base_lo & 0xff; +} + +void generic_set_mtrr(unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type) +/* [SUMMARY] Set variable MTRR register on the local CPU. + The register to set. + The base address of the region. + The size of the region. If this is 0 the region is disabled. + The type of the region. + If TRUE, do the change safely. If FALSE, safety measures should + be done externally. + [RETURNS] Nothing. +*/ +{ + u32 cr0, cr4 = 0; + u32 deftype_lo, deftype_hi; + + /* Save value of CR4 and clear Page Global Enable (bit 7) */ + if ( cpu_has_pge ) { + cr4 = read_cr4(); + write_cr4(cr4 & (unsigned char) ~(1 << 7)); + } + + /* Disable and flush caches. Note that wbinvd flushes the TLBs as + a side-effect */ + cr0 = read_cr0() | 0x40000000; + wbinvd(); + write_cr0(cr0); + wbinvd(); + + /* Save MTRR state */ + rdmsr(MTRRdefType_MSR, deftype_lo, deftype_hi); + + /* Disable MTRRs, and set the default type to uncached */ + wrmsr(MTRRdefType_MSR, deftype_lo & 0xf300UL, deftype_hi); + + if (size == 0) { + /* The invalid bit is kept in the mask, so we simply clear the + relevant mask register to disable a range. */ + wrmsr(MTRRphysMask_MSR(reg), 0, 0); + } else { + wrmsr(MTRRphysBase_MSR(reg), base << PAGE_SHIFT | type, + (base & size_and_mask) >> (32 - PAGE_SHIFT)); + wrmsr(MTRRphysMask_MSR(reg), -size << PAGE_SHIFT | 0x800, + (-size & size_and_mask) >> (32 - PAGE_SHIFT)); + } + + /* Flush caches and TLBs */ + wbinvd(); + + /* Intel (P6) standard MTRRs */ + wrmsr(MTRRdefType_MSR, deftype_lo, deftype_hi); + + /* Enable caches */ + write_cr0(read_cr0() & 0xbfffffff); + + /* Restore value of CR4 */ + if ( cpu_has_pge ) + write_cr4(cr4); +} + +int generic_validate_add_page(unsigned long base, unsigned long size, unsigned int type) +{ + unsigned long lbase, last; + + /* For Intel PPro stepping <= 7, must be 4 MiB aligned + and not touch 0x70000000->0x7003FFFF */ + if (is_cpu(INTEL) && boot_cpu_data.x86 == 6 && + boot_cpu_data.x86_model == 1 && + boot_cpu_data.x86_mask <= 7) { + if (base & ((1 << (22 - PAGE_SHIFT)) - 1)) { + printk(KERN_WARNING + "mtrr: base(0x%lx000) is not 4 MiB aligned\n", + base); + return -EINVAL; + } + if (!(base + size < 0x70000000 || base > 0x7003FFFF) && + (type == MTRR_TYPE_WRCOMB + || type == MTRR_TYPE_WRBACK)) { + printk(KERN_WARNING + "mtrr: writable mtrr between 0x70000000 and 0x7003FFFF may hang the CPU.\n"); + return -EINVAL; + } + } + + if (base + size < 0x100) { + printk(KERN_WARNING + "mtrr: cannot set region below 1 MiB (0x%lx000,0x%lx000)\n", + base, size); + return -EINVAL; + } + /* Check upper bits of base and last are equal and lower bits are 0 + for base and 1 for last */ + last = base + size - 1; + for (lbase = base; !(lbase & 1) && (last & 1); + lbase = lbase >> 1, last = last >> 1) ; + if (lbase != last) { + printk(KERN_WARNING + "mtrr: base(0x%lx000) is not aligned on a size(0x%lx000) boundary\n", + base, size); + return -EINVAL; + } + return 0; +} + + +int generic_have_wrcomb(void) +{ + unsigned long config, dummy; + rdmsr(MTRRcap_MSR, config, dummy); + return (config & (1 << 10)); +} + +int positive_have_wrcomb(void) +{ + return 1; +} + +/* generic structure... + */ +struct mtrr_ops generic_mtrr_ops = { + .use_intel_if = 1, + .init_secondary = generic_init_secondary, + .get = generic_get_mtrr, + .get_free_region = generic_get_free_region, + .set = generic_set_mtrr, + .validate_add_page = generic_validate_add_page, + .have_wrcomb = generic_have_wrcomb, +}; diff -Nru a/arch/i386/kernel/cpu/mtrr/if.c b/arch/i386/kernel/cpu/mtrr/if.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/cpu/mtrr/if.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,396 @@ +#include +#include +#include +#include +#include +#include + +/* What kind of fucking hack is this? */ +#define MTRR_NEED_STRINGS + +#include +#include "mtrr.h" + +static char *ascii_buffer; +static unsigned int ascii_buf_bytes; + +extern unsigned int *usage_table; + +#define LINE_SIZE 80 + +static int +mtrr_file_add(unsigned long base, unsigned long size, + unsigned int type, char increment, struct file *file, int page) +{ + int reg, max; + unsigned int *fcount = file->private_data; + + max = num_var_ranges; + if (fcount == NULL) { + if ((fcount = + kmalloc(max * sizeof *fcount, GFP_KERNEL)) == NULL) { + printk("mtrr: could not allocate\n"); + return -ENOMEM; + } + memset(fcount, 0, max * sizeof *fcount); + file->private_data = fcount; + } + if (!page) { + if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) { + printk + ("mtrr: size and base must be multiples of 4 kiB\n"); + printk("mtrr: size: 0x%lx base: 0x%lx\n", size, base); + return -EINVAL; + } + base >>= PAGE_SHIFT; + size >>= PAGE_SHIFT; + } + reg = mtrr_add_page(base, size, type, 1); + if (reg >= 0) + ++fcount[reg]; + return reg; +} + +static int +mtrr_file_del(unsigned long base, unsigned long size, + struct file *file, int page) +{ + int reg; + unsigned int *fcount = file->private_data; + + if (!page) { + if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) { + printk + ("mtrr: size and base must be multiples of 4 kiB\n"); + printk("mtrr: size: 0x%lx base: 0x%lx\n", size, base); + return -EINVAL; + } + base >>= PAGE_SHIFT; + size >>= PAGE_SHIFT; + } + reg = mtrr_del_page(-1, base, size); + if (reg < 0) + return reg; + if (fcount == NULL) + return reg; + if (fcount[reg] < 1) + return -EINVAL; + --fcount[reg]; + return reg; +} + +static ssize_t +mtrr_read(struct file *file, char *buf, size_t len, loff_t * ppos) +{ + if (*ppos >= ascii_buf_bytes) + return 0; + if (*ppos + len > ascii_buf_bytes) + len = ascii_buf_bytes - *ppos; + if (copy_to_user(buf, ascii_buffer + *ppos, len)) + return -EFAULT; + *ppos += len; + return len; +} + +static ssize_t +mtrr_write(struct file *file, const char *buf, size_t len, loff_t * ppos) +/* Format of control line: + "base=%Lx size=%Lx type=%s" OR: + "disable=%d" +*/ +{ + int i, err; + unsigned long reg; + unsigned long long base, size; + char *ptr; + char line[LINE_SIZE]; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + /* Can't seek (pwrite) on this device */ + if (ppos != &file->f_pos) + return -ESPIPE; + memset(line, 0, LINE_SIZE); + if (len > LINE_SIZE) + len = LINE_SIZE; + if (copy_from_user(line, buf, len - 1)) + return -EFAULT; + ptr = line + strlen(line) - 1; + if (*ptr == '\n') + *ptr = '\0'; + if (!strncmp(line, "disable=", 8)) { + reg = simple_strtoul(line + 8, &ptr, 0); + err = mtrr_del_page(reg, 0, 0); + if (err < 0) + return err; + return len; + } + if (strncmp(line, "base=", 5)) { + printk("mtrr: no \"base=\" in line: \"%s\"\n", line); + return -EINVAL; + } + base = simple_strtoull(line + 5, &ptr, 0); + for (; isspace(*ptr); ++ptr) ; + if (strncmp(ptr, "size=", 5)) { + printk("mtrr: no \"size=\" in line: \"%s\"\n", line); + return -EINVAL; + } + size = simple_strtoull(ptr + 5, &ptr, 0); + if ((base & 0xfff) || (size & 0xfff)) { + printk("mtrr: size and base must be multiples of 4 kiB\n"); + printk("mtrr: size: 0x%Lx base: 0x%Lx\n", size, base); + return -EINVAL; + } + for (; isspace(*ptr); ++ptr) ; + if (strncmp(ptr, "type=", 5)) { + printk("mtrr: no \"type=\" in line: \"%s\"\n", line); + return -EINVAL; + } + ptr += 5; + for (; isspace(*ptr); ++ptr) ; + for (i = 0; i < MTRR_NUM_TYPES; ++i) { +// if (strcmp(ptr, mtrr_strings[i])) + continue; + base >>= PAGE_SHIFT; + size >>= PAGE_SHIFT; + err = + mtrr_add_page((unsigned long) base, (unsigned long) size, i, + 1); + if (err < 0) + return err; + return len; + } + printk("mtrr: illegal type: \"%s\"\n", ptr); + return -EINVAL; +} + +static int +mtrr_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err; + mtrr_type type; + struct mtrr_sentry sentry; + struct mtrr_gentry gentry; + + switch (cmd) { + default: + return -ENOIOCTLCMD; + case MTRRIOC_ADD_ENTRY: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&sentry, (void *) arg, sizeof sentry)) + return -EFAULT; + err = + mtrr_file_add(sentry.base, sentry.size, sentry.type, 1, + file, 0); + if (err < 0) + return err; + break; + case MTRRIOC_SET_ENTRY: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&sentry, (void *) arg, sizeof sentry)) + return -EFAULT; + err = mtrr_add(sentry.base, sentry.size, sentry.type, 0); + if (err < 0) + return err; + break; + case MTRRIOC_DEL_ENTRY: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&sentry, (void *) arg, sizeof sentry)) + return -EFAULT; + err = mtrr_file_del(sentry.base, sentry.size, file, 0); + if (err < 0) + return err; + break; + case MTRRIOC_KILL_ENTRY: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&sentry, (void *) arg, sizeof sentry)) + return -EFAULT; + err = mtrr_del(-1, sentry.base, sentry.size); + if (err < 0) + return err; + break; + case MTRRIOC_GET_ENTRY: + if (copy_from_user(&gentry, (void *) arg, sizeof gentry)) + return -EFAULT; + if (gentry.regnum >= num_var_ranges) + return -EINVAL; + mtrr_if->get(gentry.regnum, &gentry.base, &gentry.size, &type); + + /* Hide entries that go above 4GB */ + if (gentry.base + gentry.size > 0x100000 + || gentry.size == 0x100000) + gentry.base = gentry.size = gentry.type = 0; + else { + gentry.base <<= PAGE_SHIFT; + gentry.size <<= PAGE_SHIFT; + gentry.type = type; + } + + if (copy_to_user((void *) arg, &gentry, sizeof gentry)) + return -EFAULT; + break; + case MTRRIOC_ADD_PAGE_ENTRY: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&sentry, (void *) arg, sizeof sentry)) + return -EFAULT; + err = + mtrr_file_add(sentry.base, sentry.size, sentry.type, 1, + file, 1); + if (err < 0) + return err; + break; + case MTRRIOC_SET_PAGE_ENTRY: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&sentry, (void *) arg, sizeof sentry)) + return -EFAULT; + err = mtrr_add_page(sentry.base, sentry.size, sentry.type, 0); + if (err < 0) + return err; + break; + case MTRRIOC_DEL_PAGE_ENTRY: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&sentry, (void *) arg, sizeof sentry)) + return -EFAULT; + err = mtrr_file_del(sentry.base, sentry.size, file, 1); + if (err < 0) + return err; + break; + case MTRRIOC_KILL_PAGE_ENTRY: + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + if (copy_from_user(&sentry, (void *) arg, sizeof sentry)) + return -EFAULT; + err = mtrr_del_page(-1, sentry.base, sentry.size); + if (err < 0) + return err; + break; + case MTRRIOC_GET_PAGE_ENTRY: + if (copy_from_user(&gentry, (void *) arg, sizeof gentry)) + return -EFAULT; + if (gentry.regnum >= num_var_ranges) + return -EINVAL; + mtrr_if->get(gentry.regnum, &gentry.base, &gentry.size, &type); + gentry.type = type; + + if (copy_to_user((void *) arg, &gentry, sizeof gentry)) + return -EFAULT; + break; + } + return 0; +} + +static int +mtrr_close(struct inode *ino, struct file *file) +{ + int i, max; + unsigned int *fcount = file->private_data; + + if (fcount == NULL) + return 0; + max = num_var_ranges; + for (i = 0; i < max; ++i) { + while (fcount[i] > 0) { + if (mtrr_del(i, 0, 0) < 0) + printk("mtrr: reg %d not used\n", i); + --fcount[i]; + } + } + kfree(fcount); + file->private_data = NULL; + return 0; +} + +static struct file_operations mtrr_fops = { + .owner = THIS_MODULE, + .read = mtrr_read, + .write = mtrr_write, + .ioctl = mtrr_ioctl, + .release = mtrr_close, +}; + +# ifdef CONFIG_PROC_FS + +static struct proc_dir_entry *proc_root_mtrr; + +# endif /* CONFIG_PROC_FS */ + +static devfs_handle_t devfs_handle; + +char * attrib_to_str(int x) +{ + return (x <= 6) ? mtrr_strings[x] : "?"; +} + +void compute_ascii(void) +{ + char factor; + int i, max; + mtrr_type type; + unsigned long base, size; + + ascii_buf_bytes = 0; + max = num_var_ranges; + for (i = 0; i < max; i++) { + mtrr_if->get(i, &base, &size, &type); + if (size == 0) + usage_table[i] = 0; + else { + if (size < (0x100000 >> PAGE_SHIFT)) { + /* less than 1MB */ + factor = 'K'; + size <<= PAGE_SHIFT - 10; + } else { + factor = 'M'; + size >>= 20 - PAGE_SHIFT; + } + sprintf + (ascii_buffer + ascii_buf_bytes, + "reg%02i: base=0x%05lx000 (%4liMB), size=%4li%cB: %s, count=%d\n", + i, base, base >> (20 - PAGE_SHIFT), size, factor, + attrib_to_str(type), usage_table[i]); + ascii_buf_bytes += + strlen(ascii_buffer + ascii_buf_bytes); + } + } + devfs_set_file_size(devfs_handle, ascii_buf_bytes); +# ifdef CONFIG_PROC_FS + if (proc_root_mtrr) + proc_root_mtrr->size = ascii_buf_bytes; +# endif /* CONFIG_PROC_FS */ +} + +static int __init mtrr_if_init(void) +{ + int max = num_var_ranges; + + if ((ascii_buffer = kmalloc(max * LINE_SIZE, GFP_KERNEL)) == NULL) { + printk("mtrr: could not allocate\n"); + return -ENOMEM; + } + ascii_buf_bytes = 0; + compute_ascii(); +#ifdef CONFIG_PROC_FS + proc_root_mtrr = + create_proc_entry("mtrr", S_IWUSR | S_IRUGO, &proc_root); + if (proc_root_mtrr) { + proc_root_mtrr->owner = THIS_MODULE; + proc_root_mtrr->proc_fops = &mtrr_fops; + } +#endif +#ifdef USERSPACE_INTERFACE + devfs_handle = devfs_register(NULL, "cpu/mtrr", DEVFS_FL_DEFAULT, 0, 0, + S_IFREG | S_IRUGO | S_IWUSR, + &mtrr_fops, NULL); +#endif + return 0; +} + +arch_initcall(mtrr_if_init); diff -Nru a/arch/i386/kernel/cpu/mtrr/main.c b/arch/i386/kernel/cpu/mtrr/main.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/cpu/mtrr/main.c Fri Aug 16 14:34:52 2002 @@ -0,0 +1,628 @@ +/* Generic MTRR (Memory Type Range Register) driver. + + Copyright (C) 1997-2000 Richard Gooch + Copyright (c) 2002 Patrick Mochel + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + Richard Gooch may be reached by email at rgooch@atnf.csiro.au + The postal address is: + Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. + + Source: "Pentium Pro Family Developer's Manual, Volume 3: + Operating System Writer's Guide" (Intel document number 242692), + section 11.11.7 + + This was cleaned and made readable by Patrick Mochel + on 6-7 March 2002. + Source: Intel Architecture Software Developers Manual, Volume 3: + System Programming Guide; Section 9.11. (1997 edition - PPro). +*/ + +#include +#include +#include +#include + +#define MTRR_NEED_STRINGS +#include + +#include +#include +#include +#include "mtrr.h" + +#define MTRR_VERSION "2.0 (20020519)" + +u32 num_var_ranges = 0; + +unsigned int *usage_table; +static DECLARE_MUTEX(main_lock); + +u32 size_or_mask, size_and_mask; + +static struct mtrr_ops * mtrr_ops[X86_VENDOR_NUM] = {}; +struct mtrr_ops * mtrr_if = NULL; + +__initdata char *mtrr_if_name[] = { + "none", "Intel", "AMD K6", "Cyrix ARR", "Centaur MCR" +}; + +static void set_mtrr(unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type); + +static unsigned int arr3_protected; + +void set_mtrr_ops(struct mtrr_ops * ops) +{ + if (ops->vendor && ops->vendor < X86_VENDOR_NUM) + mtrr_ops[ops->vendor] = ops; +} + +/* Returns non-zero if we have the write-combining memory type */ +static int have_wrcomb(void) +{ + struct pci_dev *dev = NULL; + + /* WTF is this? + * Someone, please shoot me. + */ + + /* ServerWorks LE chipsets have problems with write-combining + Don't allow it and leave room for other chipsets to be tagged */ + + if ((dev = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8, NULL)) != NULL) { + if ((dev->vendor == PCI_VENDOR_ID_SERVERWORKS) && + (dev->device == PCI_DEVICE_ID_SERVERWORKS_LE)) { + printk(KERN_INFO + "mtrr: Serverworks LE detected. Write-combining disabled.\n"); + return 0; + } + } + return (mtrr_if->have_wrcomb ? mtrr_if->have_wrcomb() : 0); +} + +/* This function returns the number of variable MTRRs */ +void __init set_num_var_ranges(void) +{ + unsigned long config = 0, dummy; + + if (use_intel()) { + rdmsr(MTRRcap_MSR, config, dummy); + } else if (is_cpu(AMD)) + config = 2; + else if (is_cpu(CYRIX) || is_cpu(CENTAUR)) + config = 8; + num_var_ranges = config & 0xff; +} + +static char * attrib_to_str(int x) +{ + return (x <= 6) ? mtrr_strings[x] : "?"; +} + +static void init_table(void) +{ + int i, max; + + max = num_var_ranges; + if ((usage_table = kmalloc(max * sizeof *usage_table, GFP_KERNEL)) + == NULL) { + printk("mtrr: could not allocate\n"); + return; + } + for (i = 0; i < max; i++) + usage_table[i] = 1; +#ifdef USERSPACE_INTERFACE + if ((ascii_buffer = kmalloc(max * LINE_SIZE, GFP_KERNEL)) == NULL) { + printk("mtrr: could not allocate\n"); + return; + } + ascii_buf_bytes = 0; + compute_ascii(); +#endif +} + +struct set_mtrr_data { + atomic_t count; + atomic_t gate; + unsigned long smp_base; + unsigned long smp_size; + unsigned int smp_reg; + mtrr_type smp_type; +}; + +#ifdef CONFIG_SMP + +static void ipi_handler(void *info) +/* [SUMMARY] Synchronisation handler. Executed by "other" CPUs. + [RETURNS] Nothing. +*/ +{ + struct set_mtrr_data *data = info; + unsigned long flags; + + local_irq_save(flags); + + atomic_dec(&data->count); + while(!atomic_read(&data->gate)) { + cpu_relax(); + barrier(); + } + + /* The master has cleared me to execute */ + mtrr_if->set(data->smp_reg, data->smp_base, + data->smp_size, data->smp_type); + + atomic_dec(&data->count); + while(atomic_read(&data->gate)) { + cpu_relax(); + barrier(); + } + local_irq_restore(flags); +} + +#endif + +/** + * set_mtrr - update mtrrs on all processors + * @reg: mtrr in question + * @base: mtrr base + * @size: mtrr size + * @type: mtrr type + * + * This is kinda tricky, but fortunately, Intel spelled it out for us cleanly: + * + * 1. Send IPI to do the following: + * 2. Disable Interrupts + * 3. Wait for all procs to do so + * 4. Enter no-fill cache mode + * 5. Flush caches + * 6. Clear PGE bit + * 7. Flush all TLBs + * 8. Disable all range registers + * 9. Update the MTRRs + * 10. Enable all range registers + * 11. Flush all TLBs and caches again + * 12. Enter normal cache mode and reenable caching + * 13. Set PGE + * 14. Wait for buddies to catch up + * 15. Enable interrupts. + * + * What does that mean for us? Well, first we set data.count to the number + * of CPUs. As each CPU disables interrupts, it'll decrement it once. We wait + * until it hits 0 and proceed. We set the data.gate flag and reset data.count. + * Meanwhile, they are waiting for that flag to be set. Once it's set, each + * CPU goes through the transition of updating MTRRs. The CPU vendors may each do it + * differently, so we call mtrr_if->set() callback and let them take care of it. + * When they're done, they again decrement data->count and wait for data.gate to + * be reset. + * When we finish, we wait for data.count to hit 0 and toggle the data.gate flag. + * Everyone then enables interrupts and we all continue on. + * + * Note that the mechanism is the same for UP systems, too; all the SMP stuff + * becomes nops. + */ +static void set_mtrr(unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type) +{ + struct set_mtrr_data data; + unsigned long flags; + + data.smp_reg = reg; + data.smp_base = base; + data.smp_size = size; + data.smp_type = type; + atomic_set(&data.count, num_booting_cpus() - 1); + atomic_set(&data.gate,0); + + /* Start the ball rolling on other CPUs */ + if (smp_call_function(ipi_handler, &data, 1, 0) != 0) + panic("mtrr: timed out waiting for other CPUs\n"); + + local_irq_save(flags); + + while(atomic_read(&data.count)) { + cpu_relax(); + barrier(); + } + /* ok, reset count and toggle gate */ + atomic_set(&data.count, num_booting_cpus() - 1); + atomic_set(&data.gate,1); + + /* do our MTRR business */ + mtrr_if->set(reg,base,size,type); + + /* wait for the others */ + while(atomic_read(&data.count)) { + cpu_relax(); + barrier(); + } + local_irq_restore(flags); + atomic_set(&data.gate,0); +} + +/** + * mtrr_add_page - Add a memory type region + * @base: Physical base address of region in pages (4 KB) + * @size: Physical size of region in pages (4 KB) + * @type: Type of MTRR desired + * @increment: If this is true do usage counting on the region + * + * Memory type region registers control the caching on newer Intel and + * non Intel processors. This function allows drivers to request an + * MTRR is added. The details and hardware specifics of each processor's + * implementation are hidden from the caller, but nevertheless the + * caller should expect to need to provide a power of two size on an + * equivalent power of two boundary. + * + * If the region cannot be added either because all regions are in use + * or the CPU cannot support it a negative value is returned. On success + * the register number for this entry is returned, but should be treated + * as a cookie only. + * + * On a multiprocessor machine the changes are made to all processors. + * This is required on x86 by the Intel processors. + * + * The available types are + * + * %MTRR_TYPE_UNCACHABLE - No caching + * + * %MTRR_TYPE_WRBACK - Write data back in bursts whenever + * + * %MTRR_TYPE_WRCOMB - Write data back soon but allow bursts + * + * %MTRR_TYPE_WRTHROUGH - Cache reads but not writes + * + * BUGS: Needs a quiet flag for the cases where drivers do not mind + * failures and do not wish system log messages to be sent. + */ + +int mtrr_add_page(unsigned long base, unsigned long size, + unsigned int type, char increment) +{ + int i; + mtrr_type ltype; + unsigned long lbase, lsize; + int error; + + if (!mtrr_if) + return -ENXIO; + + if ((error = mtrr_if->validate_add_page(base,size,type))) + return error; + + if (type >= MTRR_NUM_TYPES) { + printk("mtrr: type: %u illegal\n", type); + return -EINVAL; + } + + /* If the type is WC, check that this processor supports it */ + if ((type == MTRR_TYPE_WRCOMB) && !have_wrcomb()) { + printk(KERN_WARNING + "mtrr: your processor doesn't support write-combining\n"); + return -ENOSYS; + } + + if (base & size_or_mask || size & size_or_mask) { + printk("mtrr: base or size exceeds the MTRR width\n"); + return -EINVAL; + } + + error = -EINVAL; + + /* Search for existing MTRR */ + down(&main_lock); + for (i = 0; i < num_var_ranges; ++i) { + mtrr_if->get(i, &lbase, &lsize, <ype); + if (base >= lbase + lsize) + continue; + if ((base < lbase) && (base + size <= lbase)) + continue; + /* At this point we know there is some kind of overlap/enclosure */ + if ((base < lbase) || (base + size > lbase + lsize)) { + printk(KERN_WARNING + "mtrr: 0x%lx000,0x%lx000 overlaps existing" + " 0x%lx000,0x%lx000\n", base, size, lbase, + lsize); + goto out; + } + /* New region is enclosed by an existing region */ + if (ltype != type) { + if (type == MTRR_TYPE_UNCACHABLE) + continue; + printk ("mtrr: type mismatch for %lx000,%lx000 old: %s new: %s\n", + base, size, attrib_to_str(ltype), + attrib_to_str(type)); + goto out; + } + if (increment) + ++usage_table[i]; + compute_ascii(); + error = i; + goto out; + } + /* Search for an empty MTRR */ + i = mtrr_if->get_free_region(base, size); + if (i >= 0) { + set_mtrr(i, base, size, type); + usage_table[i] = 1; + compute_ascii(); + } else + printk("mtrr: no more MTRRs available\n"); + error = i; + out: + up(&main_lock); + return error; +} + +/** + * mtrr_add - Add a memory type region + * @base: Physical base address of region + * @size: Physical size of region + * @type: Type of MTRR desired + * @increment: If this is true do usage counting on the region + * + * Memory type region registers control the caching on newer Intel and + * non Intel processors. This function allows drivers to request an + * MTRR is added. The details and hardware specifics of each processor's + * implementation are hidden from the caller, but nevertheless the + * caller should expect to need to provide a power of two size on an + * equivalent power of two boundary. + * + * If the region cannot be added either because all regions are in use + * or the CPU cannot support it a negative value is returned. On success + * the register number for this entry is returned, but should be treated + * as a cookie only. + * + * On a multiprocessor machine the changes are made to all processors. + * This is required on x86 by the Intel processors. + * + * The available types are + * + * %MTRR_TYPE_UNCACHABLE - No caching + * + * %MTRR_TYPE_WRBACK - Write data back in bursts whenever + * + * %MTRR_TYPE_WRCOMB - Write data back soon but allow bursts + * + * %MTRR_TYPE_WRTHROUGH - Cache reads but not writes + * + * BUGS: Needs a quiet flag for the cases where drivers do not mind + * failures and do not wish system log messages to be sent. + */ + +int +mtrr_add(unsigned long base, unsigned long size, unsigned int type, + char increment) +{ + if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) { + printk("mtrr: size and base must be multiples of 4 kiB\n"); + printk("mtrr: size: 0x%lx base: 0x%lx\n", size, base); + return -EINVAL; + } + return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type, + increment); +} + +/** + * mtrr_del_page - delete a memory type region + * @reg: Register returned by mtrr_add + * @base: Physical base address + * @size: Size of region + * + * If register is supplied then base and size are ignored. This is + * how drivers should call it. + * + * Releases an MTRR region. If the usage count drops to zero the + * register is freed and the region returns to default state. + * On success the register is returned, on failure a negative error + * code. + */ + +int mtrr_del_page(int reg, unsigned long base, unsigned long size) +{ + int i, max; + mtrr_type ltype; + unsigned long lbase, lsize; + int error = -EINVAL; + + if (!mtrr_if) + return -ENXIO; + + max = num_var_ranges; + down(&main_lock); + if (reg < 0) { + /* Search for existing MTRR */ + for (i = 0; i < max; ++i) { + mtrr_if->get(i, &lbase, &lsize, <ype); + if (lbase == base && lsize == size) { + reg = i; + break; + } + } + if (reg < 0) { + printk("mtrr: no MTRR for %lx000,%lx000 found\n", base, + size); + goto out; + } + } + if (reg >= max) { + printk("mtrr: register: %d too big\n", reg); + goto out; + } + if (is_cpu(CYRIX) && !use_intel()) { + if ((reg == 3) && arr3_protected) { + printk("mtrr: ARR3 cannot be changed\n"); + goto out; + } + } + mtrr_if->get(reg, &lbase, &lsize, <ype); + if (lsize < 1) { + printk("mtrr: MTRR %d not used\n", reg); + goto out; + } + if (usage_table[reg] < 1) { + printk("mtrr: reg: %d has count=0\n", reg); + goto out; + } + if (--usage_table[reg] < 1) + set_mtrr(reg, 0, 0, 0); + compute_ascii(); + error = reg; + out: + up(&main_lock); + return error; +} +/** + * mtrr_del - delete a memory type region + * @reg: Register returned by mtrr_add + * @base: Physical base address + * @size: Size of region + * + * If register is supplied then base and size are ignored. This is + * how drivers should call it. + * + * Releases an MTRR region. If the usage count drops to zero the + * register is freed and the region returns to default state. + * On success the register is returned, on failure a negative error + * code. + */ + +int +mtrr_del(int reg, unsigned long base, unsigned long size) +{ + if ((base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1))) { + printk("mtrr: size and base must be multiples of 4 kiB\n"); + printk("mtrr: size: 0x%lx base: 0x%lx\n", size, base); + return -EINVAL; + } + return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT); +} + +EXPORT_SYMBOL(mtrr_add); +EXPORT_SYMBOL(mtrr_del); + +/* HACK ALERT! + * These should be called implicitly, but we can't yet until all the initcall + * stuff is done... + */ +extern void amd_init_mtrr(void); +extern void cyrix_init_mtrr(void); +extern void centaur_init_mtrr(void); + +static void __init init_ifs(void) +{ + amd_init_mtrr(); + cyrix_init_mtrr(); + centaur_init_mtrr(); +} + +/** + * mtrr_init - initialie mtrrs on the boot CPU + * + * This needs to be called early; before any of the other CPUs are + * initialized (i.e. before smp_init()). + * + */ +int __init mtrr_init(void) +{ + init_ifs(); + + if ( cpu_has_mtrr ) { + mtrr_if = &generic_mtrr_ops; + size_or_mask = 0xff000000; /* 36 bits */ + size_and_mask = 0x00f00000; + + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + /* The original Athlon docs said that + total addressable memory is 44 bits wide. + It was not really clear whether its MTRRs + follow this or not. (Read: 44 or 36 bits). + However, "x86-64_overview.pdf" explicitly + states that "previous implementations support + 36 bit MTRRs" and also provides a way to + query the width (in bits) of the physical + addressable memory on the Hammer family. + */ + if (boot_cpu_data.x86 == 7 + && (cpuid_eax(0x80000000) >= 0x80000008)) { + u32 phys_addr; + phys_addr = cpuid_eax(0x80000008) & 0xff; + size_or_mask = + ~((1 << (phys_addr - PAGE_SHIFT)) - 1); + size_and_mask = ~size_or_mask & 0xfff00000; + } + /* Athlon MTRRs use an Intel-compatible interface for + * getting and setting */ + break; + case X86_VENDOR_CENTAUR: + if (boot_cpu_data.x86 == 6) { + /* VIA Cyrix family have Intel style MTRRs, but don't support PAE */ + size_or_mask = 0xfff00000; /* 32 bits */ + size_and_mask = 0; + } + break; + + default: + break; + } + } else { + switch (boot_cpu_data.x86_vendor) { + case X86_VENDOR_AMD: + if ( cpu_has_k6_mtrr ) { + /* Pre-Athlon (K6) AMD CPU MTRRs */ + mtrr_if = mtrr_ops[X86_VENDOR_AMD]; + size_or_mask = 0xfff00000; /* 32 bits */ + size_and_mask = 0; + } + break; + case X86_VENDOR_CENTAUR: + if ( cpu_has_centaur_mcr ) { + mtrr_if = mtrr_ops[X86_VENDOR_CENTAUR]; + size_or_mask = 0xfff00000; /* 32 bits */ + size_and_mask = 0; + } + break; + case X86_VENDOR_CYRIX: + if ( cpu_has_cyrix_arr ) { + mtrr_if = mtrr_ops[X86_VENDOR_CYRIX]; + size_or_mask = 0xfff00000; /* 32 bits */ + size_and_mask = 0; + } + break; + default: + break; + } + } + if (mtrr_if) { + set_num_var_ranges(); + if (use_intel()) { + /* Only for Intel MTRRs */ + get_mtrr_state(); + } + init_table(); + } +#if 0 + printk("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n" + "mtrr: detected mtrr type: %s\n", + MTRR_VERSION, mtrr_if_name[mtrr_if]); +#endif + return mtrr_if ? -ENXIO : 0; +} + +//subsys_initcall(mtrr_init); + diff -Nru a/arch/i386/kernel/cpu/mtrr/mtrr.h b/arch/i386/kernel/cpu/mtrr/mtrr.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/cpu/mtrr/mtrr.h Fri Aug 16 14:35:01 2002 @@ -0,0 +1,98 @@ +/* + * local mtrr defines. + */ + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + +#define MTRRcap_MSR 0x0fe +#define MTRRdefType_MSR 0x2ff + +#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg)) +#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1) + +#define NUM_FIXED_RANGES 88 +#define MTRRfix64K_00000_MSR 0x250 +#define MTRRfix16K_80000_MSR 0x258 +#define MTRRfix16K_A0000_MSR 0x259 +#define MTRRfix4K_C0000_MSR 0x268 +#define MTRRfix4K_C8000_MSR 0x269 +#define MTRRfix4K_D0000_MSR 0x26a +#define MTRRfix4K_D8000_MSR 0x26b +#define MTRRfix4K_E0000_MSR 0x26c +#define MTRRfix4K_E8000_MSR 0x26d +#define MTRRfix4K_F0000_MSR 0x26e +#define MTRRfix4K_F8000_MSR 0x26f + +#define MTRR_CHANGE_MASK_FIXED 0x01 +#define MTRR_CHANGE_MASK_VARIABLE 0x02 +#define MTRR_CHANGE_MASK_DEFTYPE 0x04 + +/* In the Intel processor's MTRR interface, the MTRR type is always held in + an 8 bit field: */ +typedef u8 mtrr_type; + +struct mtrr_ops { + u32 vendor; + u32 use_intel_if; + void (*init)(void); + void (*init_secondary)(void); + void (*set)(unsigned int reg, unsigned long base, + unsigned long size, mtrr_type type); + void (*get)(unsigned int reg, unsigned long *base, + unsigned long *size, mtrr_type * type); + int (*get_free_region) (unsigned long base, unsigned long size); + + int (*validate_add_page)(unsigned long base, unsigned long size, + unsigned int type); + int (*have_wrcomb)(void); +}; + +extern int generic_get_free_region(unsigned long base, unsigned long size); +extern void generic_init_secondary(void); +extern int generic_validate_add_page(unsigned long base, unsigned long size, + unsigned int type); + +extern struct mtrr_ops generic_mtrr_ops; + +extern int generic_have_wrcomb(void); +extern int positive_have_wrcomb(void); + +/* library functions for processor-specific routines */ +struct set_mtrr_context { + unsigned long flags; + unsigned long deftype_lo; + unsigned long deftype_hi; + unsigned long cr4val; + unsigned long ccr3; +}; + +struct mtrr_var_range { + unsigned long base_lo; + unsigned long base_hi; + unsigned long mask_lo; + unsigned long mask_hi; +}; + +void set_mtrr_done(struct set_mtrr_context *ctxt); +void set_mtrr_cache_disable(struct set_mtrr_context *ctxt); +void set_mtrr_prepare_save(struct set_mtrr_context *ctxt); + +void get_mtrr_state(void); + +extern void set_mtrr_ops(struct mtrr_ops * ops); + +/* Don't even ask... */ +extern void compute_ascii(void); + +extern u32 size_or_mask, size_and_mask; +extern struct mtrr_ops * mtrr_if; + +#define is_cpu(vnd) (mtrr_if && mtrr_if->vendor == X86_VENDOR_##vnd) +#define use_intel() (mtrr_if && mtrr_if->use_intel_if == 1) + +extern unsigned int num_var_ranges; + +extern char * mtrr_if_name[]; diff -Nru a/arch/i386/kernel/cpu/mtrr/state.c b/arch/i386/kernel/cpu/mtrr/state.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/i386/kernel/cpu/mtrr/state.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,338 @@ +#include +#include +#include +#include +#include +#include +#include "mtrr.h" + +struct mtrr_state { + struct mtrr_var_range *var_ranges; + mtrr_type fixed_ranges[NUM_FIXED_RANGES]; + unsigned char enabled; + mtrr_type def_type; +}; + +static unsigned long smp_changes_mask __initdata = 0; +struct mtrr_state mtrr_state = {}; + +static int __init set_fixed_ranges(mtrr_type * frs) +{ + unsigned long *p = (unsigned long *) frs; + int changed = FALSE; + int i; + unsigned long lo, hi; + + rdmsr(MTRRfix64K_00000_MSR, lo, hi); + if (p[0] != lo || p[1] != hi) { + wrmsr(MTRRfix64K_00000_MSR, p[0], p[1]); + changed = TRUE; + } + + for (i = 0; i < 2; i++) { + rdmsr(MTRRfix16K_80000_MSR + i, lo, hi); + if (p[2 + i * 2] != lo || p[3 + i * 2] != hi) { + wrmsr(MTRRfix16K_80000_MSR + i, p[2 + i * 2], + p[3 + i * 2]); + changed = TRUE; + } + } + + for (i = 0; i < 8; i++) { + rdmsr(MTRRfix4K_C0000_MSR + i, lo, hi); + if (p[6 + i * 2] != lo || p[7 + i * 2] != hi) { + wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i * 2], + p[7 + i * 2]); + changed = TRUE; + } + } + return changed; +} + +/* Set the MSR pair relating to a var range. Returns TRUE if + changes are made */ +static int __init set_mtrr_var_ranges(unsigned int index, struct mtrr_var_range *vr) +{ + unsigned int lo, hi; + int changed = FALSE; + + rdmsr(MTRRphysBase_MSR(index), lo, hi); + if ((vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL) + || (vr->base_hi & 0xfUL) != (hi & 0xfUL)) { + wrmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); + changed = TRUE; + } + + rdmsr(MTRRphysMask_MSR(index), lo, hi); + + if ((vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL) + || (vr->mask_hi & 0xfUL) != (hi & 0xfUL)) { + wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); + changed = TRUE; + } + return changed; +} + +static unsigned long set_mtrr_state(u32 deftype_lo, u32 deftype_hi) +/* [SUMMARY] Set the MTRR state for this CPU. + The MTRR state information to read. + Some relevant CPU context. + [NOTE] The CPU must already be in a safe state for MTRR changes. + [RETURNS] 0 if no changes made, else a mask indication what was changed. +*/ +{ + unsigned int i; + unsigned long change_mask = 0; + + for (i = 0; i < num_var_ranges; i++) + if (set_mtrr_var_ranges(i, &mtrr_state.var_ranges[i])) + change_mask |= MTRR_CHANGE_MASK_VARIABLE; + + if (set_fixed_ranges(mtrr_state.fixed_ranges)) + change_mask |= MTRR_CHANGE_MASK_FIXED; + + /* Set_mtrr_restore restores the old value of MTRRdefType, + so to set it we fiddle with the saved value */ + if ((deftype_lo & 0xff) != mtrr_state.def_type + || ((deftype_lo & 0xc00) >> 10) != mtrr_state.enabled) { + deftype_lo |= (mtrr_state.def_type | mtrr_state.enabled << 10); + change_mask |= MTRR_CHANGE_MASK_DEFTYPE; + } + + return change_mask; +} + + +/* Some BIOS's are fucked and don't set all MTRRs the same! */ +static void __init mtrr_state_warn(void) +{ + unsigned long mask = smp_changes_mask; + if (!mask) + return; + if (mask & MTRR_CHANGE_MASK_FIXED) + printk + ("mtrr: your CPUs had inconsistent fixed MTRR settings\n"); + if (mask & MTRR_CHANGE_MASK_VARIABLE) + printk + ("mtrr: your CPUs had inconsistent variable MTRR settings\n"); + if (mask & MTRR_CHANGE_MASK_DEFTYPE) + printk + ("mtrr: your CPUs had inconsistent MTRRdefType settings\n"); + printk("mtrr: probably your BIOS does not setup all CPUs\n"); +} + +/* Free resources associated with a struct mtrr_state */ +static void __init finalize_mtrr_state(void) +{ + if (mtrr_state.var_ranges) + kfree(mtrr_state.var_ranges); + mtrr_state.var_ranges = NULL; +} + +/* Get the MSR pair relating to a var range */ +static void __init +get_mtrr_var_range(unsigned int index, struct mtrr_var_range *vr) +{ + rdmsr(MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); + rdmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); +} + +static void __init +get_fixed_ranges(mtrr_type * frs) +{ + unsigned long *p = (unsigned long *) frs; + int i; + + rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]); + + for (i = 0; i < 2; i++) + rdmsr(MTRRfix16K_80000_MSR + i, p[2 + i * 2], p[3 + i * 2]); + for (i = 0; i < 8; i++) + rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i * 2], p[7 + i * 2]); +} + +/* Grab all of the MTRR state for this CPU into *state */ +void get_mtrr_state(void) +{ + unsigned int i; + struct mtrr_var_range *vrs; + unsigned long lo, dummy; + + if (!mtrr_state.var_ranges) { + mtrr_state.var_ranges = kmalloc(num_var_ranges * sizeof (struct mtrr_var_range), + GFP_KERNEL); + if (!mtrr_state.var_ranges) + return; + } + vrs = mtrr_state.var_ranges; + + for (i = 0; i < num_var_ranges; i++) + get_mtrr_var_range(i, &vrs[i]); + get_fixed_ranges(mtrr_state.fixed_ranges); + + rdmsr(MTRRdefType_MSR, lo, dummy); + mtrr_state.def_type = (lo & 0xff); + mtrr_state.enabled = (lo & 0xc00) >> 10; +} + + +/* Put the processor into a state where MTRRs can be safely set */ +void set_mtrr_prepare_save(struct set_mtrr_context *ctxt) +{ + unsigned int cr0; + + /* Disable interrupts locally */ + local_irq_save(ctxt->flags); + + if (use_intel() || is_cpu(CYRIX)) { + + /* Save value of CR4 and clear Page Global Enable (bit 7) */ + if ( cpu_has_pge ) { + ctxt->cr4val = read_cr4(); + write_cr4(ctxt->cr4val & (unsigned char) ~(1 << 7)); + } + + /* Disable and flush caches. Note that wbinvd flushes the TLBs as + a side-effect */ + cr0 = read_cr0() | 0x40000000; + wbinvd(); + write_cr0(cr0); + wbinvd(); + + if (use_intel()) + /* Save MTRR state */ + rdmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + else + /* Cyrix ARRs - everything else were excluded at the top */ + ctxt->ccr3 = getCx86(CX86_CCR3); + } +} + +void set_mtrr_cache_disable(struct set_mtrr_context *ctxt) +{ + if (use_intel()) + /* Disable MTRRs, and set the default type to uncached */ + wrmsr(MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, + ctxt->deftype_hi); + else if (is_cpu(CYRIX)) + /* Cyrix ARRs - everything else were excluded at the top */ + setCx86(CX86_CCR3, (ctxt->ccr3 & 0x0f) | 0x10); +} + +/* Restore the processor after a set_mtrr_prepare */ +void set_mtrr_done(struct set_mtrr_context *ctxt) +{ + if (use_intel() || is_cpu(CYRIX)) { + + /* Flush caches and TLBs */ + wbinvd(); + + /* Restore MTRRdefType */ + if (use_intel()) + /* Intel (P6) standard MTRRs */ + wrmsr(MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); + else + /* Cyrix ARRs - everything else was excluded at the top */ + setCx86(CX86_CCR3, ctxt->ccr3); + + /* Enable caches */ + write_cr0(read_cr0() & 0xbfffffff); + + /* Restore value of CR4 */ + if ( cpu_has_pge ) + write_cr4(ctxt->cr4val); + } + /* Re-enable interrupts locally (if enabled previously) */ + local_irq_restore(ctxt->flags); +} + +void __init generic_init_secondary(void) +{ + u32 cr0, cr4 = 0; + u32 deftype_lo, deftype_hi; + unsigned long mask, count; + + /* Note that this is not ideal, since the cache is only flushed/disabled + for this CPU while the MTRRs are changed, but changing this requires + more invasive changes to the way the kernel boots */ + + /* Save value of CR4 and clear Page Global Enable (bit 7) */ + if ( cpu_has_pge ) { + cr4 = read_cr4(); + write_cr4(cr4 & (unsigned char) ~(1 << 7)); + } + + /* Disable and flush caches. Note that wbinvd flushes the TLBs as + a side-effect */ + cr0 = read_cr0() | 0x40000000; + wbinvd(); + write_cr0(cr0); + wbinvd(); + + /* Save MTRR state */ + rdmsr(MTRRdefType_MSR, deftype_lo, deftype_hi); + + /* Disable MTRRs, and set the default type to uncached */ + wrmsr(MTRRdefType_MSR, deftype_lo & 0xf300UL, deftype_hi); + + /* Actually set the state */ + mask = set_mtrr_state(deftype_lo,deftype_hi); + + /* Flush caches and TLBs */ + wbinvd(); + + /* Intel (P6) standard MTRRs */ + wrmsr(MTRRdefType_MSR, deftype_lo, deftype_hi); + + /* Enable caches */ + write_cr0(read_cr0() & 0xbfffffff); + + /* Restore value of CR4 */ + if ( cpu_has_pge ) + write_cr4(cr4); + + /* Use the atomic bitops to update the global mask */ + for (count = 0; count < sizeof mask * 8; ++count) { + if (mask & 0x01) + set_bit(count, &smp_changes_mask); + mask >>= 1; + } +} + +/** + * mtrr_init_secondary - setup AP MTRR state + * + * Yes, this code is exactly the same as the set_mtrr code, except for the + * piece in the middle - you set all the ranges at once, instead of one + * register at a time. + * Shoot me. + */ +void __init mtrr_init_secondary_cpu(void) +{ + unsigned long flags; + + if (!mtrr_if || !mtrr_if->init_secondary) { + /* I see no MTRRs I can support in SMP mode... */ + printk("mtrr: SMP support incomplete for this vendor\n"); + return; + } + + local_irq_save(flags); + mtrr_if->init_secondary(); + local_irq_restore(flags); +} + +/** + * mtrr_final_init - finalize initialization sequence. + */ +static int __init mtrr_finalize_state(void) +{ + if (use_intel()) { + finalize_mtrr_state(); + mtrr_state_warn(); + } + return 0; +} + +arch_initcall(mtrr_finalize_state); + diff -Nru a/arch/i386/kernel/cpu/nexgen.c b/arch/i386/kernel/cpu/nexgen.c --- a/arch/i386/kernel/cpu/nexgen.c Fri Aug 16 14:34:53 2002 +++ b/arch/i386/kernel/cpu/nexgen.c Fri Aug 16 14:34:53 2002 @@ -42,13 +42,13 @@ } static struct cpu_dev nexgen_cpu_dev __initdata = { - c_vendor: "Nexgen", - c_ident: { "NexGenDriven" }, + .c_vendor = "Nexgen", + .c_ident = { "NexGenDriven" }, c_models: { { X86_VENDOR_NEXGEN,5, { [1] "Nx586" } }, }, - c_init: init_nexgen, - c_identify: nexgen_identify, + .c_init = init_nexgen, + .c_identify = nexgen_identify, }; int __init nexgen_init_cpu(void) diff -Nru a/arch/i386/kernel/cpu/proc.c b/arch/i386/kernel/cpu/proc.c --- a/arch/i386/kernel/cpu/proc.c Fri Aug 16 14:35:00 2002 +++ b/arch/i386/kernel/cpu/proc.c Fri Aug 16 14:35:00 2002 @@ -119,8 +119,8 @@ { } struct seq_operations cpuinfo_op = { - start: c_start, - next: c_next, - stop: c_stop, - show: show_cpuinfo, + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo, }; diff -Nru a/arch/i386/kernel/cpu/rise.c b/arch/i386/kernel/cpu/rise.c --- a/arch/i386/kernel/cpu/rise.c Fri Aug 16 14:34:54 2002 +++ b/arch/i386/kernel/cpu/rise.c Fri Aug 16 14:34:54 2002 @@ -29,8 +29,8 @@ } static struct cpu_dev rise_cpu_dev __initdata = { - c_vendor: "Rise", - c_ident: { "RiseRiseRise" }, + .c_vendor = "Rise", + .c_ident = { "RiseRiseRise" }, c_models: { { X86_VENDOR_RISE, 5, { @@ -41,7 +41,7 @@ } }, }, - c_init: init_rise, + .c_init = init_rise, }; int __init rise_init_cpu(void) diff -Nru a/arch/i386/kernel/cpu/transmeta.c b/arch/i386/kernel/cpu/transmeta.c --- a/arch/i386/kernel/cpu/transmeta.c Fri Aug 16 14:34:53 2002 +++ b/arch/i386/kernel/cpu/transmeta.c Fri Aug 16 14:34:53 2002 @@ -80,10 +80,10 @@ } static struct cpu_dev transmeta_cpu_dev __initdata = { - c_vendor: "Transmeta", - c_ident: { "GenuineTMx86", "TransmetaCPU" }, - c_init: init_transmeta, - c_identify: transmeta_identify, + .c_vendor = "Transmeta", + .c_ident = { "GenuineTMx86", "TransmetaCPU" }, + .c_init = init_transmeta, + .c_identify = transmeta_identify, }; int __init transmeta_init_cpu(void) diff -Nru a/arch/i386/kernel/cpu/umc.c b/arch/i386/kernel/cpu/umc.c --- a/arch/i386/kernel/cpu/umc.c Fri Aug 16 14:34:53 2002 +++ b/arch/i386/kernel/cpu/umc.c Fri Aug 16 14:34:53 2002 @@ -11,8 +11,8 @@ } static struct cpu_dev umc_cpu_dev __initdata = { - c_vendor: "UMC", - c_ident: { "UMC UMC UMC" }, + .c_vendor = "UMC", + .c_ident = { "UMC UMC UMC" }, c_models: { { X86_VENDOR_UMC, 4, { @@ -21,7 +21,7 @@ } }, }, - c_init: init_umc, + .c_init = init_umc, }; int __init umc_init_cpu(void) diff -Nru a/arch/i386/kernel/cpuid.c b/arch/i386/kernel/cpuid.c --- a/arch/i386/kernel/cpuid.c Fri Aug 16 14:34:51 2002 +++ b/arch/i386/kernel/cpuid.c Fri Aug 16 14:34:51 2002 @@ -146,10 +146,10 @@ * File operations we support */ static struct file_operations cpuid_fops = { - owner: THIS_MODULE, - llseek: cpuid_seek, - read: cpuid_read, - open: cpuid_open, + .owner = THIS_MODULE, + .llseek = cpuid_seek, + .read = cpuid_read, + .open = cpuid_open, }; int __init cpuid_init(void) diff -Nru a/arch/i386/kernel/entry.S b/arch/i386/kernel/entry.S --- a/arch/i386/kernel/entry.S Fri Aug 16 14:34:55 2002 +++ b/arch/i386/kernel/entry.S Fri Aug 16 14:34:55 2002 @@ -753,6 +753,7 @@ .long sys_sched_setaffinity .long sys_sched_getaffinity .long sys_set_thread_area + .long sys_get_thread_area .rept NR_syscalls-(.-sys_call_table)/4 .long sys_ni_syscall diff -Nru a/arch/i386/kernel/head.S b/arch/i386/kernel/head.S --- a/arch/i386/kernel/head.S Fri Aug 16 14:34:53 2002 +++ b/arch/i386/kernel/head.S Fri Aug 16 14:34:53 2002 @@ -239,12 +239,7 @@ movl %eax,%es movl %eax,%fs movl %eax,%gs -#ifdef CONFIG_SMP - movl $(__KERNEL_DS), %eax - movl %eax,%ss # Reload the stack pointer (segment only) -#else - lss stack_start,%esp # Load processor stack -#endif + movl %eax,%ss xorl %eax,%eax lldt %ax cld # gcc2 wants the direction flag cleared at all times @@ -412,34 +407,40 @@ ALIGN /* - * The Global Descriptor Table contains 20 quadwords, per-CPU. + * The Global Descriptor Table contains 28 quadwords, per-CPU. */ ENTRY(cpu_gdt_table) .quad 0x0000000000000000 /* NULL descriptor */ - .quad 0x0000000000000000 /* TLS descriptor */ - .quad 0x00cf9a000000ffff /* 0x10 kernel 4GB code at 0x00000000 */ - .quad 0x00cf92000000ffff /* 0x18 kernel 4GB data at 0x00000000 */ - .quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */ - .quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */ - .quad 0x0000000000000000 /* TSS descriptor */ - .quad 0x0000000000000000 /* LDT descriptor */ + .quad 0x0000000000000000 /* 0x0b reserved */ + .quad 0x0000000000000000 /* 0x13 reserved */ + .quad 0x0000000000000000 /* 0x1b reserved */ + .quad 0x00cffa000000ffff /* 0x23 user 4GB code at 0x00000000 */ + .quad 0x00cff2000000ffff /* 0x2b user 4GB data at 0x00000000 */ + .quad 0x0000000000000000 /* 0x33 TLS entry 1 */ + .quad 0x0000000000000000 /* 0x3b TLS entry 2 */ + .quad 0x0000000000000000 /* 0x43 TLS entry 3 */ + .quad 0x0000000000000000 /* 0x4b reserved */ + .quad 0x0000000000000000 /* 0x53 reserved */ + .quad 0x0000000000000000 /* 0x5b reserved */ + + .quad 0x00cf9a000000ffff /* 0x60 kernel 4GB code at 0x00000000 */ + .quad 0x00cf92000000ffff /* 0x68 kernel 4GB data at 0x00000000 */ + .quad 0x0000000000000000 /* 0x70 TSS descriptor */ + .quad 0x0000000000000000 /* 0x78 LDT descriptor */ + + /* Segments used for calling PnP BIOS */ + .quad 0x00c09a0000000000 /* 0x80 32-bit code */ + .quad 0x00809a0000000000 /* 0x88 16-bit code */ + .quad 0x0080920000000000 /* 0x90 16-bit data */ + .quad 0x0080920000000000 /* 0x98 16-bit data */ + .quad 0x0080920000000000 /* 0xa0 16-bit data */ /* * The APM segments have byte granularity and their bases * and limits are set at run time. */ - .quad 0x0040920000000000 /* 0x40 APM set up for bad BIOS's */ - .quad 0x00409a0000000000 /* 0x48 APM CS code */ - .quad 0x00009a0000000000 /* 0x50 APM CS 16 code (16 bit) */ - .quad 0x0040920000000000 /* 0x58 APM DS data */ - /* Segments used for calling PnP BIOS */ - .quad 0x00c09a0000000000 /* 0x60 32-bit code */ - .quad 0x00809a0000000000 /* 0x68 16-bit code */ - .quad 0x0080920000000000 /* 0x70 16-bit data */ - .quad 0x0080920000000000 /* 0x78 16-bit data */ - .quad 0x0080920000000000 /* 0x80 16-bit data */ - .quad 0x0000000000000000 /* 0x88 not used */ - .quad 0x0000000000000000 /* 0x90 not used */ - .quad 0x0000000000000000 /* 0x98 not used */ + .quad 0x00409a0000000000 /* 0xa8 APM CS code */ + .quad 0x00009a0000000000 /* 0xb0 APM CS 16 code (16 bit) */ + .quad 0x0040920000000000 /* 0xb8 APM DS data */ #if CONFIG_SMP .fill (NR_CPUS-1)*GDT_ENTRIES,8,0 /* other CPU's GDT */ diff -Nru a/arch/i386/kernel/i8259.c b/arch/i386/kernel/i8259.c --- a/arch/i386/kernel/i8259.c Fri Aug 16 14:35:00 2002 +++ b/arch/i386/kernel/i8259.c Fri Aug 16 14:35:00 2002 @@ -247,13 +247,13 @@ } static struct device_driver driver_i8259A = { - resume: i8259A_resume, + .resume = i8259A_resume, }; static struct device device_i8259A = { - name: "i8259A", - bus_id: "0020", - driver: &driver_i8259A, + .name = "i8259A", + .bus_id = "0020", + .driver = &driver_i8259A, }; static int __init init_8259A_devicefs(void) diff -Nru a/arch/i386/kernel/io_apic.c b/arch/i386/kernel/io_apic.c --- a/arch/i386/kernel/io_apic.c Fri Aug 16 14:34:56 2002 +++ b/arch/i386/kernel/io_apic.c Fri Aug 16 14:34:56 2002 @@ -251,6 +251,9 @@ irq_balance_t *entry = irq_balance + irq; unsigned long now = jiffies; + if (clustered_apic_mode) + return; + if (entry->timestamp != now) { unsigned long allowed_mask; int random_number; diff -Nru a/arch/i386/kernel/ldt.c b/arch/i386/kernel/ldt.c --- a/arch/i386/kernel/ldt.c Fri Aug 16 14:34:54 2002 +++ b/arch/i386/kernel/ldt.c Fri Aug 16 14:34:54 2002 @@ -170,7 +170,7 @@ struct mm_struct * mm = current->mm; __u32 entry_1, entry_2, *lp; int error; - struct modify_ldt_ldt_s ldt_info; + struct user_desc ldt_info; error = -EINVAL; if (bytecount != sizeof(ldt_info)) @@ -200,32 +200,17 @@ /* Allow LDTs to be cleared by the user. */ if (ldt_info.base_addr == 0 && ldt_info.limit == 0) { - if (oldmode || - (ldt_info.contents == 0 && - ldt_info.read_exec_only == 1 && - ldt_info.seg_32bit == 0 && - ldt_info.limit_in_pages == 0 && - ldt_info.seg_not_present == 1 && - ldt_info.useable == 0 )) { + if (oldmode || LDT_empty(&ldt_info)) { entry_1 = 0; entry_2 = 0; goto install; } } - entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) | - (ldt_info.limit & 0x0ffff); - entry_2 = (ldt_info.base_addr & 0xff000000) | - ((ldt_info.base_addr & 0x00ff0000) >> 16) | - (ldt_info.limit & 0xf0000) | - ((ldt_info.read_exec_only ^ 1) << 9) | - (ldt_info.contents << 10) | - ((ldt_info.seg_not_present ^ 1) << 15) | - (ldt_info.seg_32bit << 22) | - (ldt_info.limit_in_pages << 23) | - 0x7000; - if (!oldmode) - entry_2 |= (ldt_info.useable << 20); + entry_1 = LDT_entry_a(&ldt_info); + entry_2 = LDT_entry_b(&ldt_info); + if (oldmode) + entry_2 &= ~(1 << 20); /* Install the new entry ... */ install: diff -Nru a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c --- a/arch/i386/kernel/microcode.c Fri Aug 16 14:34:55 2002 +++ b/arch/i386/kernel/microcode.c Fri Aug 16 14:34:55 2002 @@ -109,17 +109,17 @@ /* we share file_operations between misc and devfs mechanisms */ static struct file_operations microcode_fops = { - owner: THIS_MODULE, - read: microcode_read, - write: microcode_write, - ioctl: microcode_ioctl, - open: microcode_open, + .owner = THIS_MODULE, + .read = microcode_read, + .write = microcode_write, + .ioctl = microcode_ioctl, + .open = microcode_open, }; static struct miscdevice microcode_dev = { - minor: MICROCODE_MINOR, - name: "microcode", - fops: µcode_fops, + .minor = MICROCODE_MINOR, + .name = "microcode", + .fops = µcode_fops, }; static devfs_handle_t devfs_handle; diff -Nru a/arch/i386/kernel/mpparse.c b/arch/i386/kernel/mpparse.c --- a/arch/i386/kernel/mpparse.c Fri Aug 16 14:34:56 2002 +++ b/arch/i386/kernel/mpparse.c Fri Aug 16 14:34:56 2002 @@ -213,7 +213,6 @@ quad = translation_table[mpc_record]->trans_quad; mp_bus_id_to_node[m->mpc_busid] = quad; mp_bus_id_to_local[m->mpc_busid] = translation_table[mpc_record]->trans_local; - quad_local_to_mp_bus_id[quad][translation_table[mpc_record]->trans_local] = m->mpc_busid; printk("Bus #%d is %s (node %d)\n", m->mpc_busid, str, quad); } else { Dprintk("Bus #%d is %s\n", m->mpc_busid, str); @@ -224,6 +223,9 @@ } else if (strncmp(str, BUSTYPE_EISA, sizeof(BUSTYPE_EISA)-1) == 0) { mp_bus_id_to_type[m->mpc_busid] = MP_BUS_EISA; } else if (strncmp(str, BUSTYPE_PCI, sizeof(BUSTYPE_PCI)-1) == 0) { + if (clustered_apic_mode){ + quad_local_to_mp_bus_id[quad][translation_table[mpc_record]->trans_local] = m->mpc_busid; + } mp_bus_id_to_type[m->mpc_busid] = MP_BUS_PCI; mp_bus_id_to_pci_bus[m->mpc_busid] = mp_current_pci_id; mp_current_pci_id++; diff -Nru a/arch/i386/kernel/msr.c b/arch/i386/kernel/msr.c --- a/arch/i386/kernel/msr.c Fri Aug 16 14:34:57 2002 +++ b/arch/i386/kernel/msr.c Fri Aug 16 14:34:57 2002 @@ -246,11 +246,11 @@ * File operations we support */ static struct file_operations msr_fops = { - owner: THIS_MODULE, - llseek: msr_seek, - read: msr_read, - write: msr_write, - open: msr_open, + .owner = THIS_MODULE, + .llseek = msr_seek, + .read = msr_read, + .write = msr_write, + .open = msr_open, }; int __init msr_init(void) diff -Nru a/arch/i386/kernel/mtrr.c b/arch/i386/kernel/mtrr.c --- a/arch/i386/kernel/mtrr.c Fri Aug 16 14:34:52 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,2303 +0,0 @@ -/* Generic MTRR (Memory Type Range Register) driver. - - Copyright (C) 1997-2000 Richard Gooch - - This library is free software; you can redistribute it and/or - modify it under the terms of the GNU Library General Public - License as published by the Free Software Foundation; either - version 2 of the License, or (at your option) any later version. - - This library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Library General Public License for more details. - - You should have received a copy of the GNU Library General Public - License along with this library; if not, write to the Free - Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - - Richard Gooch may be reached by email at rgooch@atnf.csiro.au - The postal address is: - Richard Gooch, c/o ATNF, P. O. Box 76, Epping, N.S.W., 2121, Australia. - - Source: "Pentium Pro Family Developer's Manual, Volume 3: - Operating System Writer's Guide" (Intel document number 242692), - section 11.11.7 - - ChangeLog - - Prehistory Martin Tischhäuser - Initial register-setting code (from proform-1.0). - 19971216 Richard Gooch - Original version for /proc/mtrr interface, SMP-safe. - v1.0 - 19971217 Richard Gooch - Bug fix for ioctls()'s. - Added sample code in Documentation/mtrr.txt - v1.1 - 19971218 Richard Gooch - Disallow overlapping regions. - 19971219 Jens Maurer - Register-setting fixups. - v1.2 - 19971222 Richard Gooch - Fixups for kernel 2.1.75. - v1.3 - 19971229 David Wragg - Register-setting fixups and conformity with Intel conventions. - 19971229 Richard Gooch - Cosmetic changes and wrote this ChangeLog ;-) - 19980106 Richard Gooch - Fixups for kernel 2.1.78. - v1.4 - 19980119 David Wragg - Included passive-release enable code (elsewhere in PCI setup). - v1.5 - 19980131 Richard Gooch - Replaced global kernel lock with private spinlock. - v1.6 - 19980201 Richard Gooch - Added wait for other CPUs to complete changes. - v1.7 - 19980202 Richard Gooch - Bug fix in definition of for UP. - v1.8 - 19980319 Richard Gooch - Fixups for kernel 2.1.90. - 19980323 Richard Gooch - Move SMP BIOS fixup before secondary CPUs call - v1.9 - 19980325 Richard Gooch - Fixed test for overlapping regions: confused by adjacent regions - 19980326 Richard Gooch - Added wbinvd in . - 19980401 Richard Gooch - Bug fix for non-SMP compilation. - 19980418 David Wragg - Fixed-MTRR synchronisation for SMP and use atomic operations - instead of spinlocks. - 19980418 Richard Gooch - Differentiate different MTRR register classes for BIOS fixup. - v1.10 - 19980419 David Wragg - Bug fix in variable MTRR synchronisation. - v1.11 - 19980419 Richard Gooch - Fixups for kernel 2.1.97. - v1.12 - 19980421 Richard Gooch - Safer synchronisation across CPUs when changing MTRRs. - v1.13 - 19980423 Richard Gooch - Bugfix for SMP systems without MTRR support. - v1.14 - 19980427 Richard Gooch - Trap calls to and on non-MTRR machines. - v1.15 - 19980427 Richard Gooch - Use atomic bitops for setting SMP change mask. - v1.16 - 19980428 Richard Gooch - Removed spurious diagnostic message. - v1.17 - 19980429 Richard Gooch - Moved register-setting macros into this file. - Moved setup code from init/main.c to i386-specific areas. - v1.18 - 19980502 Richard Gooch - Moved MTRR detection outside conditionals in . - v1.19 - 19980502 Richard Gooch - Documentation improvement: mention Pentium II and AGP. - v1.20 - 19980521 Richard Gooch - Only manipulate interrupt enable flag on local CPU. - Allow enclosed uncachable regions. - v1.21 - 19980611 Richard Gooch - Always define . - v1.22 - 19980901 Richard Gooch - Removed module support in order to tidy up code. - Added sanity check for / before . - Created addition queue for prior to SMP commence. - v1.23 - 19980902 Richard Gooch - Ported patch to kernel 2.1.120-pre3. - v1.24 - 19980910 Richard Gooch - Removed sanity checks and addition queue: Linus prefers an OOPS. - v1.25 - 19981001 Richard Gooch - Fixed harmless compiler warning in include/asm-i386/mtrr.h - Fixed version numbering and history for v1.23 -> v1.24. - v1.26 - 19990118 Richard Gooch - Added devfs support. - v1.27 - 19990123 Richard Gooch - Changed locking to spin with reschedule. - Made use of new . - v1.28 - 19990201 Zoltán Böszörményi - Extended the driver to be able to use Cyrix style ARRs. - 19990204 Richard Gooch - Restructured Cyrix support. - v1.29 - 19990204 Zoltán Böszörményi - Refined ARR support: enable MAPEN in set_mtrr_prepare() - and disable MAPEN in set_mtrr_done(). - 19990205 Richard Gooch - Minor cleanups. - v1.30 - 19990208 Zoltán Böszörményi - Protect plain 6x86s (and other processors without the - Page Global Enable feature) against accessing CR4 in - set_mtrr_prepare() and set_mtrr_done(). - 19990210 Richard Gooch - Turned and into function pointers. - v1.31 - 19990212 Zoltán Böszörményi - Major rewrite of cyrix_arr_init(): do not touch ARRs, - leave them as the BIOS have set them up. - Enable usage of all 8 ARRs. - Avoid multiplications by 3 everywhere and other - code clean ups/speed ups. - 19990213 Zoltán Böszörményi - Set up other Cyrix processors identical to the boot cpu. - Since Cyrix don't support Intel APIC, this is l'art pour l'art. - Weigh ARRs by size: - If size <= 32M is given, set up ARR# we were given. - If size > 32M is given, set up ARR7 only if it is free, - fail otherwise. - 19990214 Zoltán Böszörményi - Also check for size >= 256K if we are to set up ARR7, - mtrr_add() returns the value it gets from set_mtrr() - 19990218 Zoltán Böszörményi - Remove Cyrix "coma bug" workaround from here. - Moved to linux/arch/i386/kernel/setup.c and - linux/include/asm-i386/bugs.h - 19990228 Richard Gooch - Added MTRRIOC_KILL_ENTRY ioctl(2) - Trap for counter underflow in . - Trap for 4 MiB aligned regions for PPro, stepping <= 7. - 19990301 Richard Gooch - Created hook. - 19990305 Richard Gooch - Temporarily disable AMD support now MTRR capability flag is set. - v1.32 - 19990308 Zoltán Böszörményi - Adjust my changes (19990212-19990218) to Richard Gooch's - latest changes. (19990228-19990305) - v1.33 - 19990309 Richard Gooch - Fixed typo in message. - 19990310 Richard Gooch - Support K6-II/III based on Alan Cox's patches. - v1.34 - 19990511 Bart Hartgers - Support Centaur C6 MCR's. - 19990512 Richard Gooch - Minor cleanups. - v1.35 - 19990707 Zoltán Böszörményi - Check whether ARR3 is protected in cyrix_get_free_region() - and mtrr_del(). The code won't attempt to delete or change it - from now on if the BIOS protected ARR3. It silently skips ARR3 - in cyrix_get_free_region() or returns with an error code from - mtrr_del(). - 19990711 Zoltán Böszörményi - Reset some bits in the CCRs in cyrix_arr_init() to disable SMM - if ARR3 isn't protected. This is needed because if SMM is active - and ARR3 isn't protected then deleting and setting ARR3 again - may lock up the processor. With SMM entirely disabled, it does - not happen. - 19990812 Zoltán Böszörményi - Rearrange switch() statements so the driver accomodates to - the fact that the AMD Athlon handles its MTRRs the same way - as Intel does. - 19990814 Zoltán Böszörményi - Double check for Intel in mtrr_add()'s big switch() because - that revision check is only valid for Intel CPUs. - 19990819 Alan Cox - Tested Zoltan's changes on a pre production Athlon - 100% - success. - 19991008 Manfred Spraul - replaced spin_lock_reschedule() with a normal semaphore. - v1.36 - 20000221 Richard Gooch - Compile fix if procfs and devfs not enabled. - Formatting changes. - v1.37 - 20001109 H. Peter Anvin - Use the new centralized CPU feature detects. - - v1.38 - 20010309 Dave Jones - Add support for Cyrix III. - - v1.39 - 20010312 Dave Jones - Ugh, I broke AMD support. - Reworked fix by Troels Walsted Hansen - - v1.40 - 20010327 Dave Jones - Adapted Cyrix III support to include VIA C3. - -*/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#define MTRR_NEED_STRINGS -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#define MTRR_VERSION "1.40 (20010327)" - -#define TRUE 1 -#define FALSE 0 - -/* - * The code assumes all processors support the same MTRR - * interface. This is generally a good assumption, but could - * potentially be a problem. - */ -enum mtrr_if_type { - MTRR_IF_NONE, /* No MTRRs supported */ - MTRR_IF_INTEL, /* Intel (P6) standard MTRRs */ - MTRR_IF_AMD_K6, /* AMD pre-Athlon MTRRs */ - MTRR_IF_CYRIX_ARR, /* Cyrix ARRs */ - MTRR_IF_CENTAUR_MCR, /* Centaur MCRs */ -} mtrr_if = MTRR_IF_NONE; - -static __initdata char *mtrr_if_name[] = { - "none", "Intel", "AMD K6", "Cyrix ARR", "Centaur MCR" -}; - -#define MTRRcap_MSR 0x0fe -#define MTRRdefType_MSR 0x2ff - -#define MTRRphysBase_MSR(reg) (0x200 + 2 * (reg)) -#define MTRRphysMask_MSR(reg) (0x200 + 2 * (reg) + 1) - -#define NUM_FIXED_RANGES 88 -#define MTRRfix64K_00000_MSR 0x250 -#define MTRRfix16K_80000_MSR 0x258 -#define MTRRfix16K_A0000_MSR 0x259 -#define MTRRfix4K_C0000_MSR 0x268 -#define MTRRfix4K_C8000_MSR 0x269 -#define MTRRfix4K_D0000_MSR 0x26a -#define MTRRfix4K_D8000_MSR 0x26b -#define MTRRfix4K_E0000_MSR 0x26c -#define MTRRfix4K_E8000_MSR 0x26d -#define MTRRfix4K_F0000_MSR 0x26e -#define MTRRfix4K_F8000_MSR 0x26f - -#ifdef CONFIG_SMP -# define MTRR_CHANGE_MASK_FIXED 0x01 -# define MTRR_CHANGE_MASK_VARIABLE 0x02 -# define MTRR_CHANGE_MASK_DEFTYPE 0x04 -#endif - -/* In the Intel processor's MTRR interface, the MTRR type is always held in - an 8 bit field: */ -typedef u8 mtrr_type; - -#define LINE_SIZE 80 -#define JIFFIE_TIMEOUT 100 - -#ifdef CONFIG_SMP -# define set_mtrr(reg,base,size,type) set_mtrr_smp (reg, base, size, type) -#else -# define set_mtrr(reg,base,size,type) (*set_mtrr_up) (reg, base, size, type, \ - TRUE) -#endif - -#if defined(CONFIG_PROC_FS) || defined(CONFIG_DEVFS_FS) -# define USERSPACE_INTERFACE -#endif - -#ifndef USERSPACE_INTERFACE -# define compute_ascii() while (0) -#endif - -#ifdef USERSPACE_INTERFACE -static char *ascii_buffer; -static unsigned int ascii_buf_bytes; -#endif -static unsigned int *usage_table; -static DECLARE_MUTEX(main_lock); - -/* Private functions */ -#ifdef USERSPACE_INTERFACE -static void compute_ascii (void); -#endif - - -struct set_mtrr_context -{ - unsigned long flags; - unsigned long deftype_lo; - unsigned long deftype_hi; - unsigned long cr4val; - unsigned long ccr3; -}; - -static int arr3_protected; - -/* Put the processor into a state where MTRRs can be safely set */ -static void set_mtrr_prepare_save (struct set_mtrr_context *ctxt) -{ - /* Disable interrupts locally */ - local_irq_save(ctxt->flags); - - if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR ) - return; - - /* Save value of CR4 and clear Page Global Enable (bit 7) */ - if ( cpu_has_pge ) { - ctxt->cr4val = read_cr4(); - write_cr4(ctxt->cr4val & (unsigned char) ~(1<<7)); - } - - /* Disable and flush caches. Note that wbinvd flushes the TLBs as - a side-effect */ - { - unsigned int cr0 = read_cr0() | 0x40000000; - wbinvd(); - write_cr0( cr0 ); - wbinvd(); - } - - if ( mtrr_if == MTRR_IF_INTEL ) { - /* Save MTRR state */ - rdmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); - } else { - /* Cyrix ARRs - everything else were excluded at the top */ - ctxt->ccr3 = getCx86 (CX86_CCR3); - } -} /* End Function set_mtrr_prepare_save */ - -static void set_mtrr_cache_disable (struct set_mtrr_context *ctxt) -{ - if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR ) - return; - - if ( mtrr_if == MTRR_IF_INTEL ) { - /* Disable MTRRs, and set the default type to uncached */ - wrmsr (MTRRdefType_MSR, ctxt->deftype_lo & 0xf300UL, ctxt->deftype_hi); - } else { - /* Cyrix ARRs - everything else were excluded at the top */ - setCx86 (CX86_CCR3, (ctxt->ccr3 & 0x0f) | 0x10); - } -} /* End Function set_mtrr_cache_disable */ - -/* Restore the processor after a set_mtrr_prepare */ -static void set_mtrr_done (struct set_mtrr_context *ctxt) -{ - if ( mtrr_if != MTRR_IF_INTEL && mtrr_if != MTRR_IF_CYRIX_ARR ) { - local_irq_restore (ctxt->flags); - return; - } - - /* Flush caches and TLBs */ - wbinvd(); - - /* Restore MTRRdefType */ - if ( mtrr_if == MTRR_IF_INTEL ) { - /* Intel (P6) standard MTRRs */ - wrmsr (MTRRdefType_MSR, ctxt->deftype_lo, ctxt->deftype_hi); - } else { - /* Cyrix ARRs - everything else was excluded at the top */ - setCx86 (CX86_CCR3, ctxt->ccr3); - } - - /* Enable caches */ - write_cr0( read_cr0() & 0xbfffffff ); - - /* Restore value of CR4 */ - if ( cpu_has_pge ) - write_cr4(ctxt->cr4val); - - /* Re-enable interrupts locally (if enabled previously) */ - local_irq_restore (ctxt->flags); -} /* End Function set_mtrr_done */ - -/* This function returns the number of variable MTRRs */ -static unsigned int get_num_var_ranges (void) -{ - unsigned long config, dummy; - - switch ( mtrr_if ) - { - case MTRR_IF_INTEL: - rdmsr (MTRRcap_MSR, config, dummy); - return (config & 0xff); - case MTRR_IF_AMD_K6: - return 2; - case MTRR_IF_CYRIX_ARR: - return 8; - case MTRR_IF_CENTAUR_MCR: - return 8; - default: - return 0; - } -} /* End Function get_num_var_ranges */ - -/* Returns non-zero if we have the write-combining memory type */ -static int have_wrcomb (void) -{ - unsigned long config, dummy; - struct pci_dev *dev = NULL; - - /* ServerWorks LE chipsets have problems with write-combining - Don't allow it and leave room for other chipsets to be tagged */ - - if ((dev = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8, NULL)) != NULL) { - if ((dev->vendor == PCI_VENDOR_ID_SERVERWORKS) && - (dev->device == PCI_DEVICE_ID_SERVERWORKS_LE)) { - printk (KERN_INFO "mtrr: Serverworks LE detected. Write-combining disabled.\n"); - return 0; - } - } - - switch ( mtrr_if ) - { - case MTRR_IF_INTEL: - rdmsr (MTRRcap_MSR, config, dummy); - return (config & (1<<10)); - case MTRR_IF_AMD_K6: - case MTRR_IF_CENTAUR_MCR: - case MTRR_IF_CYRIX_ARR: - return 1; - default: - return 0; - } -} /* End Function have_wrcomb */ - -static u32 size_or_mask, size_and_mask; - -static void intel_get_mtrr (unsigned int reg, unsigned long *base, - unsigned long *size, mtrr_type *type) -{ - unsigned long mask_lo, mask_hi, base_lo, base_hi; - - rdmsr (MTRRphysMask_MSR(reg), mask_lo, mask_hi); - if ( (mask_lo & 0x800) == 0 ) - { - /* Invalid (i.e. free) range */ - *base = 0; - *size = 0; - *type = 0; - return; - } - - rdmsr(MTRRphysBase_MSR(reg), base_lo, base_hi); - - /* Work out the shifted address mask. */ - mask_lo = size_or_mask | mask_hi << (32 - PAGE_SHIFT) - | mask_lo >> PAGE_SHIFT; - - /* This works correctly if size is a power of two, i.e. a - contiguous range. */ - *size = -mask_lo; - *base = base_hi << (32 - PAGE_SHIFT) | base_lo >> PAGE_SHIFT; - *type = base_lo & 0xff; -} /* End Function intel_get_mtrr */ - -static void cyrix_get_arr (unsigned int reg, unsigned long *base, - unsigned long *size, mtrr_type *type) -{ - unsigned long flags; - unsigned char arr, ccr3, rcr, shift; - - arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ - - /* Save flags and disable interrupts */ - local_irq_save(flags); - - ccr3 = getCx86 (CX86_CCR3); - setCx86 (CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ - ((unsigned char *) base)[3] = getCx86 (arr); - ((unsigned char *) base)[2] = getCx86 (arr+1); - ((unsigned char *) base)[1] = getCx86 (arr+2); - rcr = getCx86(CX86_RCR_BASE + reg); - setCx86 (CX86_CCR3, ccr3); /* disable MAPEN */ - - /* Enable interrupts if it was enabled previously */ - local_irq_restore (flags); - shift = ((unsigned char *) base)[1] & 0x0f; - *base >>= PAGE_SHIFT; - - /* Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 - * Note: shift==0xf means 4G, this is unsupported. - */ - if (shift) - *size = (reg < 7 ? 0x1UL : 0x40UL) << (shift - 1); - else - *size = 0; - - /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */ - if (reg < 7) - { - switch (rcr) - { - case 1: *type = MTRR_TYPE_UNCACHABLE; break; - case 8: *type = MTRR_TYPE_WRBACK; break; - case 9: *type = MTRR_TYPE_WRCOMB; break; - case 24: - default: *type = MTRR_TYPE_WRTHROUGH; break; - } - } else - { - switch (rcr) - { - case 0: *type = MTRR_TYPE_UNCACHABLE; break; - case 8: *type = MTRR_TYPE_WRCOMB; break; - case 9: *type = MTRR_TYPE_WRBACK; break; - case 25: - default: *type = MTRR_TYPE_WRTHROUGH; break; - } - } -} /* End Function cyrix_get_arr */ - -static void amd_get_mtrr (unsigned int reg, unsigned long *base, - unsigned long *size, mtrr_type *type) -{ - unsigned long low, high; - - rdmsr (MSR_K6_UWCCR, low, high); - /* Upper dword is region 1, lower is region 0 */ - if (reg == 1) low = high; - /* The base masks off on the right alignment */ - *base = (low & 0xFFFE0000) >> PAGE_SHIFT; - *type = 0; - if (low & 1) *type = MTRR_TYPE_UNCACHABLE; - if (low & 2) *type = MTRR_TYPE_WRCOMB; - if ( !(low & 3) ) - { - *size = 0; - return; - } - /* - * This needs a little explaining. The size is stored as an - * inverted mask of bits of 128K granularity 15 bits long offset - * 2 bits - * - * So to get a size we do invert the mask and add 1 to the lowest - * mask bit (4 as its 2 bits in). This gives us a size we then shift - * to turn into 128K blocks - * - * eg 111 1111 1111 1100 is 512K - * - * invert 000 0000 0000 0011 - * +1 000 0000 0000 0100 - * *128K ... - */ - low = (~low) & 0x1FFFC; - *size = (low + 4) << (15 - PAGE_SHIFT); - return; -} /* End Function amd_get_mtrr */ - -static struct -{ - unsigned long high; - unsigned long low; -} centaur_mcr[8]; - -static u8 centaur_mcr_reserved; -static u8 centaur_mcr_type; /* 0 for winchip, 1 for winchip2 */ - -/* - * Report boot time MCR setups - */ - -void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) -{ - centaur_mcr[mcr].low = lo; - centaur_mcr[mcr].high = hi; -} - -static void centaur_get_mcr (unsigned int reg, unsigned long *base, - unsigned long *size, mtrr_type *type) -{ - *base = centaur_mcr[reg].high >> PAGE_SHIFT; - *size = -(centaur_mcr[reg].low & 0xfffff000) >> PAGE_SHIFT; - *type = MTRR_TYPE_WRCOMB; /* If it is there, it is write-combining */ - if(centaur_mcr_type==1 && ((centaur_mcr[reg].low&31)&2)) - *type = MTRR_TYPE_UNCACHABLE; - if(centaur_mcr_type==1 && (centaur_mcr[reg].low&31)==25) - *type = MTRR_TYPE_WRBACK; - if(centaur_mcr_type==0 && (centaur_mcr[reg].low&31)==31) - *type = MTRR_TYPE_WRBACK; - -} /* End Function centaur_get_mcr */ - -static void (*get_mtrr) (unsigned int reg, unsigned long *base, - unsigned long *size, mtrr_type *type); - -static void intel_set_mtrr_up (unsigned int reg, unsigned long base, - unsigned long size, mtrr_type type, int do_safe) -/* [SUMMARY] Set variable MTRR register on the local CPU. - The register to set. - The base address of the region. - The size of the region. If this is 0 the region is disabled. - The type of the region. - If TRUE, do the change safely. If FALSE, safety measures should - be done externally. - [RETURNS] Nothing. -*/ -{ - struct set_mtrr_context ctxt; - - if (do_safe) { - set_mtrr_prepare_save (&ctxt); - set_mtrr_cache_disable (&ctxt); - } - if (size == 0) - { - /* The invalid bit is kept in the mask, so we simply clear the - relevant mask register to disable a range. */ - wrmsr (MTRRphysMask_MSR (reg), 0, 0); - } - else - { - wrmsr (MTRRphysBase_MSR (reg), base << PAGE_SHIFT | type, - (base & size_and_mask) >> (32 - PAGE_SHIFT)); - wrmsr (MTRRphysMask_MSR (reg), -size << PAGE_SHIFT | 0x800, - (-size & size_and_mask) >> (32 - PAGE_SHIFT)); - } - if (do_safe) set_mtrr_done (&ctxt); -} /* End Function intel_set_mtrr_up */ - -static void cyrix_set_arr_up (unsigned int reg, unsigned long base, - unsigned long size, mtrr_type type, int do_safe) -{ - struct set_mtrr_context ctxt; - unsigned char arr, arr_type, arr_size; - - arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ - - /* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */ - if (reg >= 7) - size >>= 6; - - size &= 0x7fff; /* make sure arr_size <= 14 */ - for(arr_size = 0; size; arr_size++, size >>= 1); - - if (reg<7) - { - switch (type) { - case MTRR_TYPE_UNCACHABLE: arr_type = 1; break; - case MTRR_TYPE_WRCOMB: arr_type = 9; break; - case MTRR_TYPE_WRTHROUGH: arr_type = 24; break; - default: arr_type = 8; break; - } - } - else - { - switch (type) - { - case MTRR_TYPE_UNCACHABLE: arr_type = 0; break; - case MTRR_TYPE_WRCOMB: arr_type = 8; break; - case MTRR_TYPE_WRTHROUGH: arr_type = 25; break; - default: arr_type = 9; break; - } - } - - if (do_safe) { - set_mtrr_prepare_save (&ctxt); - set_mtrr_cache_disable (&ctxt); - } - base <<= PAGE_SHIFT; - setCx86(arr, ((unsigned char *) &base)[3]); - setCx86(arr+1, ((unsigned char *) &base)[2]); - setCx86(arr+2, (((unsigned char *) &base)[1]) | arr_size); - setCx86(CX86_RCR_BASE + reg, arr_type); - if (do_safe) set_mtrr_done (&ctxt); -} /* End Function cyrix_set_arr_up */ - -static void amd_set_mtrr_up (unsigned int reg, unsigned long base, - unsigned long size, mtrr_type type, int do_safe) -/* [SUMMARY] Set variable MTRR register on the local CPU. - The register to set. - The base address of the region. - The size of the region. If this is 0 the region is disabled. - The type of the region. - If TRUE, do the change safely. If FALSE, safety measures should - be done externally. - [RETURNS] Nothing. -*/ -{ - u32 regs[2]; - struct set_mtrr_context ctxt; - - if (do_safe) { - set_mtrr_prepare_save (&ctxt); - set_mtrr_cache_disable (&ctxt); - } - /* - * Low is MTRR0 , High MTRR 1 - */ - rdmsr (MSR_K6_UWCCR, regs[0], regs[1]); - /* - * Blank to disable - */ - if (size == 0) - regs[reg] = 0; - else - /* Set the register to the base, the type (off by one) and an - inverted bitmask of the size The size is the only odd - bit. We are fed say 512K We invert this and we get 111 1111 - 1111 1011 but if you subtract one and invert you get the - desired 111 1111 1111 1100 mask - - But ~(x - 1) == ~x + 1 == -x. Two's complement rocks! */ - regs[reg] = (-size>>(15-PAGE_SHIFT) & 0x0001FFFC) - | (base<base_lo, vr->base_hi); - rdmsr (MTRRphysMask_MSR (index), vr->mask_lo, vr->mask_hi); -} /* End Function get_mtrr_var_range */ - - -/* Set the MSR pair relating to a var range. Returns TRUE if - changes are made */ -static int __init set_mtrr_var_range_testing (unsigned int index, - struct mtrr_var_range *vr) -{ - unsigned int lo, hi; - int changed = FALSE; - - rdmsr(MTRRphysBase_MSR(index), lo, hi); - if ( (vr->base_lo & 0xfffff0ffUL) != (lo & 0xfffff0ffUL) - || (vr->base_hi & 0xfUL) != (hi & 0xfUL) ) - { - wrmsr (MTRRphysBase_MSR(index), vr->base_lo, vr->base_hi); - changed = TRUE; - } - - rdmsr (MTRRphysMask_MSR(index), lo, hi); - - if ( (vr->mask_lo & 0xfffff800UL) != (lo & 0xfffff800UL) - || (vr->mask_hi & 0xfUL) != (hi & 0xfUL) ) - { - wrmsr(MTRRphysMask_MSR(index), vr->mask_lo, vr->mask_hi); - changed = TRUE; - } - return changed; -} /* End Function set_mtrr_var_range_testing */ - -static void __init get_fixed_ranges(mtrr_type *frs) -{ - unsigned long *p = (unsigned long *)frs; - int i; - - rdmsr(MTRRfix64K_00000_MSR, p[0], p[1]); - - for (i = 0; i < 2; i++) - rdmsr(MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]); - for (i = 0; i < 8; i++) - rdmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]); -} /* End Function get_fixed_ranges */ - -static int __init set_fixed_ranges_testing(mtrr_type *frs) -{ - unsigned long *p = (unsigned long *)frs; - int changed = FALSE; - int i; - unsigned long lo, hi; - - rdmsr(MTRRfix64K_00000_MSR, lo, hi); - if (p[0] != lo || p[1] != hi) - { - wrmsr (MTRRfix64K_00000_MSR, p[0], p[1]); - changed = TRUE; - } - - for (i = 0; i < 2; i++) - { - rdmsr (MTRRfix16K_80000_MSR + i, lo, hi); - if (p[2 + i*2] != lo || p[3 + i*2] != hi) - { - wrmsr (MTRRfix16K_80000_MSR + i, p[2 + i*2], p[3 + i*2]); - changed = TRUE; - } - } - - for (i = 0; i < 8; i++) - { - rdmsr (MTRRfix4K_C0000_MSR + i, lo, hi); - if (p[6 + i*2] != lo || p[7 + i*2] != hi) - { - wrmsr(MTRRfix4K_C0000_MSR + i, p[6 + i*2], p[7 + i*2]); - changed = TRUE; - } - } - return changed; -} /* End Function set_fixed_ranges_testing */ - -struct mtrr_state -{ - unsigned int num_var_ranges; - struct mtrr_var_range *var_ranges; - mtrr_type fixed_ranges[NUM_FIXED_RANGES]; - unsigned char enabled; - mtrr_type def_type; -}; - - -/* Grab all of the MTRR state for this CPU into *state */ -static void __init get_mtrr_state(struct mtrr_state *state) -{ - unsigned int nvrs, i; - struct mtrr_var_range *vrs; - unsigned long lo, dummy; - - nvrs = state->num_var_ranges = get_num_var_ranges(); - vrs = state->var_ranges - = kmalloc (nvrs * sizeof (struct mtrr_var_range), GFP_KERNEL); - if (vrs == NULL) - nvrs = state->num_var_ranges = 0; - - for (i = 0; i < nvrs; i++) - get_mtrr_var_range (i, &vrs[i]); - get_fixed_ranges (state->fixed_ranges); - - rdmsr (MTRRdefType_MSR, lo, dummy); - state->def_type = (lo & 0xff); - state->enabled = (lo & 0xc00) >> 10; -} /* End Function get_mtrr_state */ - - -/* Free resources associated with a struct mtrr_state */ -static void __init finalize_mtrr_state(struct mtrr_state *state) -{ - if (state->var_ranges) kfree (state->var_ranges); -} /* End Function finalize_mtrr_state */ - - -static unsigned long __init set_mtrr_state (struct mtrr_state *state, - struct set_mtrr_context *ctxt) -/* [SUMMARY] Set the MTRR state for this CPU. - The MTRR state information to read. - Some relevant CPU context. - [NOTE] The CPU must already be in a safe state for MTRR changes. - [RETURNS] 0 if no changes made, else a mask indication what was changed. -*/ -{ - unsigned int i; - unsigned long change_mask = 0; - - for (i = 0; i < state->num_var_ranges; i++) - if ( set_mtrr_var_range_testing (i, &state->var_ranges[i]) ) - change_mask |= MTRR_CHANGE_MASK_VARIABLE; - - if ( set_fixed_ranges_testing(state->fixed_ranges) ) - change_mask |= MTRR_CHANGE_MASK_FIXED; - /* Set_mtrr_restore restores the old value of MTRRdefType, - so to set it we fiddle with the saved value */ - if ( (ctxt->deftype_lo & 0xff) != state->def_type - || ( (ctxt->deftype_lo & 0xc00) >> 10 ) != state->enabled) - { - ctxt->deftype_lo |= (state->def_type | state->enabled << 10); - change_mask |= MTRR_CHANGE_MASK_DEFTYPE; - } - - return change_mask; -} /* End Function set_mtrr_state */ - - -static atomic_t undone_count; -static volatile int wait_barrier_cache_disable = FALSE; -static volatile int wait_barrier_execute = FALSE; -static volatile int wait_barrier_cache_enable = FALSE; - -struct set_mtrr_data -{ - unsigned long smp_base; - unsigned long smp_size; - unsigned int smp_reg; - mtrr_type smp_type; -}; - -static void ipi_handler (void *info) -/* [SUMMARY] Synchronisation handler. Executed by "other" CPUs. - [RETURNS] Nothing. -*/ -{ - struct set_mtrr_data *data = info; - struct set_mtrr_context ctxt; - set_mtrr_prepare_save (&ctxt); - /* Notify master that I've flushed and disabled my cache */ - atomic_dec (&undone_count); - while (wait_barrier_cache_disable) { rep_nop(); barrier(); } - set_mtrr_cache_disable (&ctxt); - /* Notify master that I've flushed and disabled my cache */ - atomic_dec (&undone_count); - while (wait_barrier_execute) { rep_nop(); barrier(); } - /* The master has cleared me to execute */ - (*set_mtrr_up) (data->smp_reg, data->smp_base, data->smp_size, - data->smp_type, FALSE); - /* Notify master CPU that I've executed the function */ - atomic_dec (&undone_count); - /* Wait for master to clear me to enable cache and return */ - while (wait_barrier_cache_enable) { rep_nop(); barrier(); } - set_mtrr_done (&ctxt); -} /* End Function ipi_handler */ - -static void set_mtrr_smp (unsigned int reg, unsigned long base, - unsigned long size, mtrr_type type) -{ - struct set_mtrr_data data; - struct set_mtrr_context ctxt; - - data.smp_reg = reg; - data.smp_base = base; - data.smp_size = size; - data.smp_type = type; - wait_barrier_cache_disable = TRUE; - wait_barrier_execute = TRUE; - wait_barrier_cache_enable = TRUE; - atomic_set (&undone_count, num_booting_cpus() - 1); - /* Start the ball rolling on other CPUs */ - if (smp_call_function (ipi_handler, &data, 1, 0) != 0) - panic ("mtrr: timed out waiting for other CPUs\n"); - /* Flush and disable the local CPU's cache */ - set_mtrr_prepare_save (&ctxt); - /* Wait for all other CPUs to flush and disable their caches */ - while (atomic_read (&undone_count) > 0) { rep_nop(); barrier(); } - /* Set up for completion wait and then release other CPUs to change MTRRs*/ - atomic_set (&undone_count, num_booting_cpus() - 1); - wait_barrier_cache_disable = FALSE; - set_mtrr_cache_disable (&ctxt); - - /* Wait for all other CPUs to flush and disable their caches */ - while (atomic_read (&undone_count) > 0) { rep_nop(); barrier(); } - /* Set up for completion wait and then release other CPUs to change MTRRs*/ - atomic_set (&undone_count, num_booting_cpus() - 1); - wait_barrier_execute = FALSE; - (*set_mtrr_up) (reg, base, size, type, FALSE); - /* Now wait for other CPUs to complete the function */ - while (atomic_read (&undone_count) > 0) { rep_nop(); barrier(); } - /* Now all CPUs should have finished the function. Release the barrier to - allow them to re-enable their caches and return from their interrupt, - then enable the local cache and return */ - wait_barrier_cache_enable = FALSE; - set_mtrr_done (&ctxt); -} /* End Function set_mtrr_smp */ - - -/* Some BIOS's are fucked and don't set all MTRRs the same! */ -static void __init mtrr_state_warn(unsigned long mask) -{ - if (!mask) return; - if (mask & MTRR_CHANGE_MASK_FIXED) - printk ("mtrr: your CPUs had inconsistent fixed MTRR settings\n"); - if (mask & MTRR_CHANGE_MASK_VARIABLE) - printk ("mtrr: your CPUs had inconsistent variable MTRR settings\n"); - if (mask & MTRR_CHANGE_MASK_DEFTYPE) - printk ("mtrr: your CPUs had inconsistent MTRRdefType settings\n"); - printk ("mtrr: probably your BIOS does not setup all CPUs\n"); -} /* End Function mtrr_state_warn */ - -#endif /* CONFIG_SMP */ - -static char *attrib_to_str (int x) -{ - return (x <= 6) ? mtrr_strings[x] : "?"; -} /* End Function attrib_to_str */ - -static void init_table (void) -{ - int i, max; - - max = get_num_var_ranges (); - if ( ( usage_table = kmalloc (max * sizeof *usage_table, GFP_KERNEL) ) - == NULL ) - { - printk ("mtrr: could not allocate\n"); - return; - } - for (i = 0; i < max; i++) usage_table[i] = 1; -#ifdef USERSPACE_INTERFACE - if ( ( ascii_buffer = kmalloc (max * LINE_SIZE, GFP_KERNEL) ) == NULL ) - { - printk ("mtrr: could not allocate\n"); - return; - } - ascii_buf_bytes = 0; - compute_ascii (); -#endif -} /* End Function init_table */ - -static int generic_get_free_region (unsigned long base, unsigned long size) -/* [SUMMARY] Get a free MTRR. - The starting (base) address of the region. - The size (in bytes) of the region. - [RETURNS] The index of the region on success, else -1 on error. -*/ -{ - int i, max; - mtrr_type ltype; - unsigned long lbase, lsize; - - max = get_num_var_ranges (); - for (i = 0; i < max; ++i) - { - (*get_mtrr) (i, &lbase, &lsize, <ype); - if (lsize == 0) return i; - } - return -ENOSPC; -} /* End Function generic_get_free_region */ - -static int centaur_get_free_region (unsigned long base, unsigned long size) -/* [SUMMARY] Get a free MTRR. - The starting (base) address of the region. - The size (in bytes) of the region. - [RETURNS] The index of the region on success, else -1 on error. -*/ -{ - int i, max; - mtrr_type ltype; - unsigned long lbase, lsize; - - max = get_num_var_ranges (); - for (i = 0; i < max; ++i) - { - if(centaur_mcr_reserved & (1< The starting (base) address of the region. - The size (in bytes) of the region. - [RETURNS] The index of the region on success, else -1 on error. -*/ -{ - int i; - mtrr_type ltype; - unsigned long lbase, lsize; - - /* If we are to set up a region >32M then look at ARR7 immediately */ - if (size > 0x2000) - { - cyrix_get_arr (7, &lbase, &lsize, <ype); - if (lsize == 0) return 7; - /* Else try ARR0-ARR6 first */ - } - else - { - for (i = 0; i < 7; i++) - { - cyrix_get_arr (i, &lbase, &lsize, <ype); - if ((i == 3) && arr3_protected) continue; - if (lsize == 0) return i; - } - /* ARR0-ARR6 isn't free, try ARR7 but its size must be at least 256K */ - cyrix_get_arr (i, &lbase, &lsize, <ype); - if ((lsize == 0) && (size >= 0x40)) return i; - } - return -ENOSPC; -} /* End Function cyrix_get_free_region */ - -static int (*get_free_region) (unsigned long base, - unsigned long size) = generic_get_free_region; - -/** - * mtrr_add_page - Add a memory type region - * @base: Physical base address of region in pages (4 KB) - * @size: Physical size of region in pages (4 KB) - * @type: Type of MTRR desired - * @increment: If this is true do usage counting on the region - * - * Memory type region registers control the caching on newer Intel and - * non Intel processors. This function allows drivers to request an - * MTRR is added. The details and hardware specifics of each processor's - * implementation are hidden from the caller, but nevertheless the - * caller should expect to need to provide a power of two size on an - * equivalent power of two boundary. - * - * If the region cannot be added either because all regions are in use - * or the CPU cannot support it a negative value is returned. On success - * the register number for this entry is returned, but should be treated - * as a cookie only. - * - * On a multiprocessor machine the changes are made to all processors. - * This is required on x86 by the Intel processors. - * - * The available types are - * - * %MTRR_TYPE_UNCACHABLE - No caching - * - * %MTRR_TYPE_WRBACK - Write data back in bursts whenever - * - * %MTRR_TYPE_WRCOMB - Write data back soon but allow bursts - * - * %MTRR_TYPE_WRTHROUGH - Cache reads but not writes - * - * BUGS: Needs a quiet flag for the cases where drivers do not mind - * failures and do not wish system log messages to be sent. - */ - -int mtrr_add_page(unsigned long base, unsigned long size, unsigned int type, char increment) -{ -/* [SUMMARY] Add an MTRR entry. - The starting (base, in pages) address of the region. - The size of the region. (in pages) - The type of the new region. - If true and the region already exists, the usage count will be - incremented. - [RETURNS] The MTRR register on success, else a negative number indicating - the error code. - [NOTE] This routine uses a spinlock. -*/ - int i, max; - mtrr_type ltype; - unsigned long lbase, lsize, last; - - switch ( mtrr_if ) - { - case MTRR_IF_NONE: - return -ENXIO; /* No MTRRs whatsoever */ - - case MTRR_IF_AMD_K6: - /* Apply the K6 block alignment and size rules - In order - o Uncached or gathering only - o 128K or bigger block - o Power of 2 block - o base suitably aligned to the power - */ - if ( type > MTRR_TYPE_WRCOMB || size < (1 << (17-PAGE_SHIFT)) || - (size & ~(size-1))-size || ( base & (size-1) ) ) - return -EINVAL; - break; - - case MTRR_IF_INTEL: - /* For Intel PPro stepping <= 7, must be 4 MiB aligned - and not touch 0x70000000->0x7003FFFF */ - if ( boot_cpu_data.x86_vendor == X86_VENDOR_INTEL && - boot_cpu_data.x86 == 6 && - boot_cpu_data.x86_model == 1 && - boot_cpu_data.x86_mask <= 7 ) - { - if ( base & ((1 << (22-PAGE_SHIFT))-1) ) - { - printk (KERN_WARNING "mtrr: base(0x%lx000) is not 4 MiB aligned\n", base); - return -EINVAL; - } - if (!(base + size < 0x70000000 || base > 0x7003FFFF) && - (type == MTRR_TYPE_WRCOMB || type == MTRR_TYPE_WRBACK)) - { - printk (KERN_WARNING "mtrr: writable mtrr between 0x70000000 and 0x7003FFFF may hang the CPU.\n"); - return -EINVAL; - } - } - /* Fall through */ - - case MTRR_IF_CYRIX_ARR: - case MTRR_IF_CENTAUR_MCR: - if ( mtrr_if == MTRR_IF_CENTAUR_MCR ) - { - /* - * FIXME: Winchip2 supports uncached - */ - if (type != MTRR_TYPE_WRCOMB && (centaur_mcr_type == 0 || type != MTRR_TYPE_UNCACHABLE)) - { - printk (KERN_WARNING "mtrr: only write-combining%s supported\n", - centaur_mcr_type?" and uncacheable are":" is"); - return -EINVAL; - } - } - else if (base + size < 0x100) - { - printk (KERN_WARNING "mtrr: cannot set region below 1 MiB (0x%lx000,0x%lx000)\n", - base, size); - return -EINVAL; - } - /* Check upper bits of base and last are equal and lower bits are 0 - for base and 1 for last */ - last = base + size - 1; - for (lbase = base; !(lbase & 1) && (last & 1); - lbase = lbase >> 1, last = last >> 1); - if (lbase != last) - { - printk (KERN_WARNING "mtrr: base(0x%lx000) is not aligned on a size(0x%lx000) boundary\n", - base, size); - return -EINVAL; - } - break; - - default: - return -EINVAL; - } - - if (type >= MTRR_NUM_TYPES) - { - printk ("mtrr: type: %u illegal\n", type); - return -EINVAL; - } - - /* If the type is WC, check that this processor supports it */ - if ( (type == MTRR_TYPE_WRCOMB) && !have_wrcomb () ) - { - printk (KERN_WARNING "mtrr: your processor doesn't support write-combining\n"); - return -ENOSYS; - } - - if ( base & size_or_mask || size & size_or_mask ) - { - printk ("mtrr: base or size exceeds the MTRR width\n"); - return -EINVAL; - } - - increment = increment ? 1 : 0; - max = get_num_var_ranges (); - /* Search for existing MTRR */ - down(&main_lock); - for (i = 0; i < max; ++i) - { - (*get_mtrr) (i, &lbase, &lsize, <ype); - if (base >= lbase + lsize) continue; - if ( (base < lbase) && (base + size <= lbase) ) continue; - /* At this point we know there is some kind of overlap/enclosure */ - if ( (base < lbase) || (base + size > lbase + lsize) ) - { - up(&main_lock); - printk (KERN_WARNING "mtrr: 0x%lx000,0x%lx000 overlaps existing" - " 0x%lx000,0x%lx000\n", - base, size, lbase, lsize); - return -EINVAL; - } - /* New region is enclosed by an existing region */ - if (ltype != type) - { - if (type == MTRR_TYPE_UNCACHABLE) continue; - up(&main_lock); - printk ( "mtrr: type mismatch for %lx000,%lx000 old: %s new: %s\n", - base, size, attrib_to_str (ltype), attrib_to_str (type) ); - return -EINVAL; - } - if (increment) ++usage_table[i]; - compute_ascii (); - up(&main_lock); - return i; - } - /* Search for an empty MTRR */ - i = (*get_free_region) (base, size); - if (i < 0) - { - up(&main_lock); - printk ("mtrr: no more MTRRs available\n"); - return i; - } - set_mtrr (i, base, size, type); - usage_table[i] = 1; - compute_ascii (); - up(&main_lock); - return i; -} /* End Function mtrr_add_page */ - -/** - * mtrr_add - Add a memory type region - * @base: Physical base address of region - * @size: Physical size of region - * @type: Type of MTRR desired - * @increment: If this is true do usage counting on the region - * - * Memory type region registers control the caching on newer Intel and - * non Intel processors. This function allows drivers to request an - * MTRR is added. The details and hardware specifics of each processor's - * implementation are hidden from the caller, but nevertheless the - * caller should expect to need to provide a power of two size on an - * equivalent power of two boundary. - * - * If the region cannot be added either because all regions are in use - * or the CPU cannot support it a negative value is returned. On success - * the register number for this entry is returned, but should be treated - * as a cookie only. - * - * On a multiprocessor machine the changes are made to all processors. - * This is required on x86 by the Intel processors. - * - * The available types are - * - * %MTRR_TYPE_UNCACHABLE - No caching - * - * %MTRR_TYPE_WRBACK - Write data back in bursts whenever - * - * %MTRR_TYPE_WRCOMB - Write data back soon but allow bursts - * - * %MTRR_TYPE_WRTHROUGH - Cache reads but not writes - * - * BUGS: Needs a quiet flag for the cases where drivers do not mind - * failures and do not wish system log messages to be sent. - */ - -int mtrr_add(unsigned long base, unsigned long size, unsigned int type, char increment) -{ -/* [SUMMARY] Add an MTRR entry. - The starting (base) address of the region. - The size (in bytes) of the region. - The type of the new region. - If true and the region already exists, the usage count will be - incremented. - [RETURNS] The MTRR register on success, else a negative number indicating - the error code. -*/ - - if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) - { - printk ("mtrr: size and base must be multiples of 4 kiB\n"); - printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); - return -EINVAL; - } - return mtrr_add_page(base >> PAGE_SHIFT, size >> PAGE_SHIFT, type, increment); -} /* End Function mtrr_add */ - -/** - * mtrr_del_page - delete a memory type region - * @reg: Register returned by mtrr_add - * @base: Physical base address - * @size: Size of region - * - * If register is supplied then base and size are ignored. This is - * how drivers should call it. - * - * Releases an MTRR region. If the usage count drops to zero the - * register is freed and the region returns to default state. - * On success the register is returned, on failure a negative error - * code. - */ - -int mtrr_del_page (int reg, unsigned long base, unsigned long size) -/* [SUMMARY] Delete MTRR/decrement usage count. - The register. If this is less than 0 then <> and <> must - be supplied. - The base address of the region. This is ignored if <> is >= 0. - The size of the region. This is ignored if <> is >= 0. - [RETURNS] The register on success, else a negative number indicating - the error code. - [NOTE] This routine uses a spinlock. -*/ -{ - int i, max; - mtrr_type ltype; - unsigned long lbase, lsize; - - if ( mtrr_if == MTRR_IF_NONE ) return -ENXIO; - - max = get_num_var_ranges (); - down (&main_lock); - if (reg < 0) - { - /* Search for existing MTRR */ - for (i = 0; i < max; ++i) - { - (*get_mtrr) (i, &lbase, &lsize, <ype); - if (lbase == base && lsize == size) - { - reg = i; - break; - } - } - if (reg < 0) - { - up(&main_lock); - printk ("mtrr: no MTRR for %lx000,%lx000 found\n", base, size); - return -EINVAL; - } - } - if (reg >= max) - { - up (&main_lock); - printk ("mtrr: register: %d too big\n", reg); - return -EINVAL; - } - if ( mtrr_if == MTRR_IF_CYRIX_ARR ) - { - if ( (reg == 3) && arr3_protected ) - { - up (&main_lock); - printk ("mtrr: ARR3 cannot be changed\n"); - return -EINVAL; - } - } - (*get_mtrr) (reg, &lbase, &lsize, <ype); - if (lsize < 1) - { - up (&main_lock); - printk ("mtrr: MTRR %d not used\n", reg); - return -EINVAL; - } - if (usage_table[reg] < 1) - { - up (&main_lock); - printk ("mtrr: reg: %d has count=0\n", reg); - return -EINVAL; - } - if (--usage_table[reg] < 1) set_mtrr (reg, 0, 0, 0); - compute_ascii (); - up (&main_lock); - return reg; -} /* End Function mtrr_del_page */ - -/** - * mtrr_del - delete a memory type region - * @reg: Register returned by mtrr_add - * @base: Physical base address - * @size: Size of region - * - * If register is supplied then base and size are ignored. This is - * how drivers should call it. - * - * Releases an MTRR region. If the usage count drops to zero the - * register is freed and the region returns to default state. - * On success the register is returned, on failure a negative error - * code. - */ - -int mtrr_del (int reg, unsigned long base, unsigned long size) -/* [SUMMARY] Delete MTRR/decrement usage count. - The register. If this is less than 0 then <> and <> must - be supplied. - The base address of the region. This is ignored if <> is >= 0. - The size of the region. This is ignored if <> is >= 0. - [RETURNS] The register on success, else a negative number indicating - the error code. -*/ -{ - if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) - { - printk ("mtrr: size and base must be multiples of 4 kiB\n"); - printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); - return -EINVAL; - } - return mtrr_del_page(reg, base >> PAGE_SHIFT, size >> PAGE_SHIFT); -} - -#ifdef USERSPACE_INTERFACE - -static int mtrr_file_add (unsigned long base, unsigned long size, - unsigned int type, char increment, struct file *file, int page) -{ - int reg, max; - unsigned int *fcount = file->private_data; - - max = get_num_var_ranges (); - if (fcount == NULL) - { - if ( ( fcount = kmalloc (max * sizeof *fcount, GFP_KERNEL) ) == NULL ) - { - printk ("mtrr: could not allocate\n"); - return -ENOMEM; - } - memset (fcount, 0, max * sizeof *fcount); - file->private_data = fcount; - } - if (!page) { - if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) - { - printk ("mtrr: size and base must be multiples of 4 kiB\n"); - printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); - return -EINVAL; - } - base >>= PAGE_SHIFT; - size >>= PAGE_SHIFT; - } - reg = mtrr_add_page (base, size, type, 1); - if (reg >= 0) ++fcount[reg]; - return reg; -} /* End Function mtrr_file_add */ - -static int mtrr_file_del (unsigned long base, unsigned long size, - struct file *file, int page) -{ - int reg; - unsigned int *fcount = file->private_data; - - if (!page) { - if ( (base & (PAGE_SIZE - 1)) || (size & (PAGE_SIZE - 1)) ) - { - printk ("mtrr: size and base must be multiples of 4 kiB\n"); - printk ("mtrr: size: 0x%lx base: 0x%lx\n", size, base); - return -EINVAL; - } - base >>= PAGE_SHIFT; - size >>= PAGE_SHIFT; - } - reg = mtrr_del_page (-1, base, size); - if (reg < 0) return reg; - if (fcount == NULL) return reg; - if (fcount[reg] < 1) return -EINVAL; - --fcount[reg]; - return reg; -} /* End Function mtrr_file_del */ - -static ssize_t mtrr_read (struct file *file, char *buf, size_t len, - loff_t *ppos) -{ - if (*ppos >= ascii_buf_bytes) return 0; - if (*ppos + len > ascii_buf_bytes) len = ascii_buf_bytes - *ppos; - if ( copy_to_user (buf, ascii_buffer + *ppos, len) ) return -EFAULT; - *ppos += len; - return len; -} /* End Function mtrr_read */ - -static ssize_t mtrr_write (struct file *file, const char *buf, size_t len, - loff_t *ppos) -/* Format of control line: - "base=%Lx size=%Lx type=%s" OR: - "disable=%d" -*/ -{ - int i, err; - unsigned long reg; - unsigned long long base, size; - char *ptr; - char line[LINE_SIZE]; - - if ( !capable(CAP_SYS_ADMIN)) return -EPERM; - /* Can't seek (pwrite) on this device */ - if (ppos != &file->f_pos) return -ESPIPE; - memset (line, 0, LINE_SIZE); - if (len > LINE_SIZE) len = LINE_SIZE; - if ( copy_from_user (line, buf, len - 1) ) return -EFAULT; - ptr = line + strlen (line) - 1; - if (*ptr == '\n') *ptr = '\0'; - if ( !strncmp (line, "disable=", 8) ) - { - reg = simple_strtoul (line + 8, &ptr, 0); - err = mtrr_del_page (reg, 0, 0); - if (err < 0) return err; - return len; - } - if ( strncmp (line, "base=", 5) ) - { - printk ("mtrr: no \"base=\" in line: \"%s\"\n", line); - return -EINVAL; - } - base = simple_strtoull (line + 5, &ptr, 0); - for (; isspace (*ptr); ++ptr); - if ( strncmp (ptr, "size=", 5) ) - { - printk ("mtrr: no \"size=\" in line: \"%s\"\n", line); - return -EINVAL; - } - size = simple_strtoull (ptr + 5, &ptr, 0); - if ( (base & 0xfff) || (size & 0xfff) ) - { - printk ("mtrr: size and base must be multiples of 4 kiB\n"); - printk ("mtrr: size: 0x%Lx base: 0x%Lx\n", size, base); - return -EINVAL; - } - for (; isspace (*ptr); ++ptr); - if ( strncmp (ptr, "type=", 5) ) - { - printk ("mtrr: no \"type=\" in line: \"%s\"\n", line); - return -EINVAL; - } - ptr += 5; - for (; isspace (*ptr); ++ptr); - for (i = 0; i < MTRR_NUM_TYPES; ++i) - { - if ( strcmp (ptr, mtrr_strings[i]) ) continue; - base >>= PAGE_SHIFT; - size >>= PAGE_SHIFT; - err = mtrr_add_page ((unsigned long)base, (unsigned long)size, i, 1); - if (err < 0) return err; - return len; - } - printk ("mtrr: illegal type: \"%s\"\n", ptr); - return -EINVAL; -} /* End Function mtrr_write */ - -static int mtrr_ioctl (struct inode *inode, struct file *file, - unsigned int cmd, unsigned long arg) -{ - int err; - mtrr_type type; - struct mtrr_sentry sentry; - struct mtrr_gentry gentry; - - switch (cmd) - { - default: - return -ENOIOCTLCMD; - case MTRRIOC_ADD_ENTRY: - if ( ! capable(CAP_SYS_ADMIN) ) return -EPERM; - if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) - return -EFAULT; - err = mtrr_file_add (sentry.base, sentry.size, sentry.type, 1, file, 0); - if (err < 0) return err; - break; - case MTRRIOC_SET_ENTRY: - if ( !capable(CAP_SYS_ADMIN) ) return -EPERM; - if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) - return -EFAULT; - err = mtrr_add (sentry.base, sentry.size, sentry.type, 0); - if (err < 0) return err; - break; - case MTRRIOC_DEL_ENTRY: - if ( !capable(CAP_SYS_ADMIN) ) return -EPERM; - if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) - return -EFAULT; - err = mtrr_file_del (sentry.base, sentry.size, file, 0); - if (err < 0) return err; - break; - case MTRRIOC_KILL_ENTRY: - if ( !capable(CAP_SYS_ADMIN) ) return -EPERM; - if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) - return -EFAULT; - err = mtrr_del (-1, sentry.base, sentry.size); - if (err < 0) return err; - break; - case MTRRIOC_GET_ENTRY: - if ( copy_from_user (&gentry, (void *) arg, sizeof gentry) ) - return -EFAULT; - if ( gentry.regnum >= get_num_var_ranges () ) return -EINVAL; - (*get_mtrr) (gentry.regnum, &gentry.base, &gentry.size, &type); - - /* Hide entries that go above 4GB */ - if (gentry.base + gentry.size > 0x100000 || gentry.size == 0x100000) - gentry.base = gentry.size = gentry.type = 0; - else { - gentry.base <<= PAGE_SHIFT; - gentry.size <<= PAGE_SHIFT; - gentry.type = type; - } - - if ( copy_to_user ( (void *) arg, &gentry, sizeof gentry) ) - return -EFAULT; - break; - case MTRRIOC_ADD_PAGE_ENTRY: - if ( !capable(CAP_SYS_ADMIN) ) return -EPERM; - if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) - return -EFAULT; - err = mtrr_file_add (sentry.base, sentry.size, sentry.type, 1, file, 1); - if (err < 0) return err; - break; - case MTRRIOC_SET_PAGE_ENTRY: - if ( !capable(CAP_SYS_ADMIN) ) return -EPERM; - if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) - return -EFAULT; - err = mtrr_add_page (sentry.base, sentry.size, sentry.type, 0); - if (err < 0) return err; - break; - case MTRRIOC_DEL_PAGE_ENTRY: - if ( !capable(CAP_SYS_ADMIN) ) return -EPERM; - if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) - return -EFAULT; - err = mtrr_file_del (sentry.base, sentry.size, file, 1); - if (err < 0) return err; - break; - case MTRRIOC_KILL_PAGE_ENTRY: - if ( !capable(CAP_SYS_ADMIN) ) return -EPERM; - if ( copy_from_user (&sentry, (void *) arg, sizeof sentry) ) - return -EFAULT; - err = mtrr_del_page (-1, sentry.base, sentry.size); - if (err < 0) return err; - break; - case MTRRIOC_GET_PAGE_ENTRY: - if ( copy_from_user (&gentry, (void *) arg, sizeof gentry) ) - return -EFAULT; - if ( gentry.regnum >= get_num_var_ranges () ) return -EINVAL; - (*get_mtrr) (gentry.regnum, &gentry.base, &gentry.size, &type); - gentry.type = type; - - if ( copy_to_user ( (void *) arg, &gentry, sizeof gentry) ) - return -EFAULT; - break; - } - return 0; -} /* End Function mtrr_ioctl */ - -static int mtrr_close (struct inode *ino, struct file *file) -{ - int i, max; - unsigned int *fcount = file->private_data; - - if (fcount == NULL) return 0; - max = get_num_var_ranges (); - for (i = 0; i < max; ++i) - { - while (fcount[i] > 0) - { - if (mtrr_del (i, 0, 0) < 0) printk ("mtrr: reg %d not used\n", i); - --fcount[i]; - } - } - kfree (fcount); - file->private_data = NULL; - return 0; -} /* End Function mtrr_close */ - -static struct file_operations mtrr_fops = -{ - owner: THIS_MODULE, - read: mtrr_read, - write: mtrr_write, - ioctl: mtrr_ioctl, - release: mtrr_close, -}; - -# ifdef CONFIG_PROC_FS - -static struct proc_dir_entry *proc_root_mtrr; - -# endif /* CONFIG_PROC_FS */ - -static devfs_handle_t devfs_handle; - -static void compute_ascii (void) -{ - char factor; - int i, max; - mtrr_type type; - unsigned long base, size; - - ascii_buf_bytes = 0; - max = get_num_var_ranges (); - for (i = 0; i < max; i++) - { - (*get_mtrr) (i, &base, &size, &type); - if (size == 0) usage_table[i] = 0; - else - { - if (size < (0x100000 >> PAGE_SHIFT)) - { - /* less than 1MB */ - factor = 'K'; - size <<= PAGE_SHIFT - 10; - } - else - { - factor = 'M'; - size >>= 20 - PAGE_SHIFT; - } - sprintf - (ascii_buffer + ascii_buf_bytes, - "reg%02i: base=0x%05lx000 (%4liMB), size=%4li%cB: %s, count=%d\n", - i, base, base >> (20 - PAGE_SHIFT), size, factor, - attrib_to_str (type), usage_table[i]); - ascii_buf_bytes += strlen (ascii_buffer + ascii_buf_bytes); - } - } - devfs_set_file_size (devfs_handle, ascii_buf_bytes); -# ifdef CONFIG_PROC_FS - if (proc_root_mtrr) - proc_root_mtrr->size = ascii_buf_bytes; -# endif /* CONFIG_PROC_FS */ -} /* End Function compute_ascii */ - -#endif /* USERSPACE_INTERFACE */ - -EXPORT_SYMBOL(mtrr_add); -EXPORT_SYMBOL(mtrr_del); - -#ifdef CONFIG_SMP - -typedef struct -{ - unsigned long base; - unsigned long size; - mtrr_type type; -} arr_state_t; - -arr_state_t arr_state[8] __initdata = -{ - {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, - {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL}, {0UL,0UL,0UL} -}; - -unsigned char ccr_state[7] __initdata = { 0, 0, 0, 0, 0, 0, 0 }; - -static void __init cyrix_arr_init_secondary(void) -{ - struct set_mtrr_context ctxt; - int i; - - /* flush cache and enable MAPEN */ - set_mtrr_prepare_save (&ctxt); - set_mtrr_cache_disable (&ctxt); - - /* the CCRs are not contiguous */ - for(i=0; i<4; i++) setCx86(CX86_CCR0 + i, ccr_state[i]); - for( ; i<7; i++) setCx86(CX86_CCR4 + i, ccr_state[i]); - for(i=0; i<8; i++) - cyrix_set_arr_up(i, - arr_state[i].base, arr_state[i].size, arr_state[i].type, FALSE); - - set_mtrr_done (&ctxt); /* flush cache and disable MAPEN */ -} /* End Function cyrix_arr_init_secondary */ - -#endif - -/* - * On Cyrix 6x86(MX) and M II the ARR3 is special: it has connection - * with the SMM (System Management Mode) mode. So we need the following: - * Check whether SMI_LOCK (CCR3 bit 0) is set - * if it is set, write a warning message: ARR3 cannot be changed! - * (it cannot be changed until the next processor reset) - * if it is reset, then we can change it, set all the needed bits: - * - disable access to SMM memory through ARR3 range (CCR1 bit 7 reset) - * - disable access to SMM memory (CCR1 bit 2 reset) - * - disable SMM mode (CCR1 bit 1 reset) - * - disable write protection of ARR3 (CCR6 bit 1 reset) - * - (maybe) disable ARR3 - * Just to be sure, we enable ARR usage by the processor (CCR5 bit 5 set) - */ -static void __init cyrix_arr_init(void) -{ - struct set_mtrr_context ctxt; - unsigned char ccr[7]; - int ccrc[7] = { 0, 0, 0, 0, 0, 0, 0 }; -#ifdef CONFIG_SMP - int i; -#endif - - /* flush cache and enable MAPEN */ - set_mtrr_prepare_save (&ctxt); - set_mtrr_cache_disable (&ctxt); - - /* Save all CCRs locally */ - ccr[0] = getCx86 (CX86_CCR0); - ccr[1] = getCx86 (CX86_CCR1); - ccr[2] = getCx86 (CX86_CCR2); - ccr[3] = ctxt.ccr3; - ccr[4] = getCx86 (CX86_CCR4); - ccr[5] = getCx86 (CX86_CCR5); - ccr[6] = getCx86 (CX86_CCR6); - - if (ccr[3] & 1) - { - ccrc[3] = 1; - arr3_protected = 1; - } - else - { - /* Disable SMM mode (bit 1), access to SMM memory (bit 2) and - * access to SMM memory through ARR3 (bit 7). - */ - if (ccr[1] & 0x80) { ccr[1] &= 0x7f; ccrc[1] |= 0x80; } - if (ccr[1] & 0x04) { ccr[1] &= 0xfb; ccrc[1] |= 0x04; } - if (ccr[1] & 0x02) { ccr[1] &= 0xfd; ccrc[1] |= 0x02; } - arr3_protected = 0; - if (ccr[6] & 0x02) { - ccr[6] &= 0xfd; ccrc[6] = 1; /* Disable write protection of ARR3 */ - setCx86 (CX86_CCR6, ccr[6]); - } - /* Disable ARR3. This is safe now that we disabled SMM. */ - /* cyrix_set_arr_up (3, 0, 0, 0, FALSE); */ - } - /* If we changed CCR1 in memory, change it in the processor, too. */ - if (ccrc[1]) setCx86 (CX86_CCR1, ccr[1]); - - /* Enable ARR usage by the processor */ - if (!(ccr[5] & 0x20)) - { - ccr[5] |= 0x20; ccrc[5] = 1; - setCx86 (CX86_CCR5, ccr[5]); - } - -#ifdef CONFIG_SMP - for(i=0; i<7; i++) ccr_state[i] = ccr[i]; - for(i=0; i<8; i++) - cyrix_get_arr(i, - &arr_state[i].base, &arr_state[i].size, &arr_state[i].type); -#endif - - set_mtrr_done (&ctxt); /* flush cache and disable MAPEN */ - - if ( ccrc[5] ) printk ("mtrr: ARR usage was not enabled, enabled manually\n"); - if ( ccrc[3] ) printk ("mtrr: ARR3 cannot be changed\n"); -/* - if ( ccrc[1] & 0x80) printk ("mtrr: SMM memory access through ARR3 disabled\n"); - if ( ccrc[1] & 0x04) printk ("mtrr: SMM memory access disabled\n"); - if ( ccrc[1] & 0x02) printk ("mtrr: SMM mode disabled\n"); -*/ - if ( ccrc[6] ) printk ("mtrr: ARR3 was write protected, unprotected\n"); -} /* End Function cyrix_arr_init */ - -/* - * Initialise the later (saner) Winchip MCR variant. In this version - * the BIOS can pass us the registers it has used (but not their values) - * and the control register is read/write - */ - -static void __init centaur_mcr1_init(void) -{ - unsigned i; - u32 lo, hi; - - /* Unfortunately, MCR's are read-only, so there is no way to - * find out what the bios might have done. - */ - - rdmsr(MSR_IDT_MCR_CTRL, lo, hi); - if(((lo>>17)&7)==1) /* Type 1 Winchip2 MCR */ - { - lo&= ~0x1C0; /* clear key */ - lo|= 0x040; /* set key to 1 */ - wrmsr(MSR_IDT_MCR_CTRL, lo, hi); /* unlock MCR */ - } - - centaur_mcr_type = 1; - - /* - * Clear any unconfigured MCR's. - */ - - for (i = 0; i < 8; ++i) - { - if(centaur_mcr[i]. high == 0 && centaur_mcr[i].low == 0) - { - if(!(lo & (1<<(9+i)))) - wrmsr (MSR_IDT_MCR0 + i , 0, 0); - else - /* - * If the BIOS set up an MCR we cannot see it - * but we don't wish to obliterate it - */ - centaur_mcr_reserved |= (1<= 0x80000008)) { - u32 phys_addr; - phys_addr = cpuid_eax(0x80000008) & 0xff ; - size_or_mask = ~((1 << (phys_addr - PAGE_SHIFT)) - 1); - size_and_mask = ~size_or_mask & 0xfff00000; - break; - } - size_or_mask = 0xff000000; /* 36 bits */ - size_and_mask = 0x00f00000; - break; - - case X86_VENDOR_CENTAUR: - /* VIA Cyrix family have Intel style MTRRs, but don't support PAE */ - if (boot_cpu_data.x86 == 6) { - size_or_mask = 0xfff00000; /* 32 bits */ - size_and_mask = 0; - } - break; - - default: - /* Intel, etc. */ - size_or_mask = 0xff000000; /* 36 bits */ - size_and_mask = 0x00f00000; - break; - } - - } else if ( cpu_has_k6_mtrr ) { - /* Pre-Athlon (K6) AMD CPU MTRRs */ - mtrr_if = MTRR_IF_AMD_K6; - get_mtrr = amd_get_mtrr; - set_mtrr_up = amd_set_mtrr_up; - size_or_mask = 0xfff00000; /* 32 bits */ - size_and_mask = 0; - } else if ( cpu_has_cyrix_arr ) { - /* Cyrix ARRs */ - mtrr_if = MTRR_IF_CYRIX_ARR; - get_mtrr = cyrix_get_arr; - set_mtrr_up = cyrix_set_arr_up; - get_free_region = cyrix_get_free_region; - cyrix_arr_init(); - size_or_mask = 0xfff00000; /* 32 bits */ - size_and_mask = 0; - } else if ( cpu_has_centaur_mcr ) { - /* Centaur MCRs */ - mtrr_if = MTRR_IF_CENTAUR_MCR; - get_mtrr = centaur_get_mcr; - set_mtrr_up = centaur_set_mcr_up; - get_free_region = centaur_get_free_region; - centaur_mcr_init(); - size_or_mask = 0xfff00000; /* 32 bits */ - size_and_mask = 0; - } else { - /* No supported MTRR interface */ - mtrr_if = MTRR_IF_NONE; - } - - printk ("mtrr: v%s Richard Gooch (rgooch@atnf.csiro.au)\n" - "mtrr: detected mtrr type: %s\n", - MTRR_VERSION, mtrr_if_name[mtrr_if]); - - return (mtrr_if != MTRR_IF_NONE); -} /* End Function mtrr_setup */ - -#ifdef CONFIG_SMP - -static volatile unsigned long smp_changes_mask __initdata = 0; -static struct mtrr_state smp_mtrr_state __initdata = {0, 0}; - -void __init mtrr_init_boot_cpu(void) -{ - if ( !mtrr_setup () ) - return; - - if ( mtrr_if == MTRR_IF_INTEL ) { - /* Only for Intel MTRRs */ - get_mtrr_state (&smp_mtrr_state); - } -} /* End Function mtrr_init_boot_cpu */ - -static void __init intel_mtrr_init_secondary_cpu(void) -{ - unsigned long mask, count; - struct set_mtrr_context ctxt; - - /* Note that this is not ideal, since the cache is only flushed/disabled - for this CPU while the MTRRs are changed, but changing this requires - more invasive changes to the way the kernel boots */ - set_mtrr_prepare_save (&ctxt); - set_mtrr_cache_disable (&ctxt); - mask = set_mtrr_state (&smp_mtrr_state, &ctxt); - set_mtrr_done (&ctxt); - /* Use the atomic bitops to update the global mask */ - for (count = 0; count < sizeof mask * 8; ++count) - { - if (mask & 0x01) set_bit (count, &smp_changes_mask); - mask >>= 1; - } -} /* End Function intel_mtrr_init_secondary_cpu */ - -void __init mtrr_init_secondary_cpu(void) -{ - switch ( mtrr_if ) { - case MTRR_IF_INTEL: - /* Intel (P6) standard MTRRs */ - intel_mtrr_init_secondary_cpu(); - break; - case MTRR_IF_CYRIX_ARR: - /* This is _completely theoretical_! - * I assume here that one day Cyrix will support Intel APIC. - * In reality on non-Intel CPUs we won't even get to this routine. - * Hopefully no one will plug two Cyrix processors in a dual P5 board. - * :-) - */ - cyrix_arr_init_secondary (); - break; - case MTRR_IF_NONE: - break; - default: - /* I see no MTRRs I can support in SMP mode... */ - printk ("mtrr: SMP support incomplete for this vendor\n"); - } -} /* End Function mtrr_init_secondary_cpu */ -#endif /* CONFIG_SMP */ - -int __init mtrr_init(void) -{ -#ifdef CONFIG_SMP - /* mtrr_setup() should already have been called from mtrr_init_boot_cpu() */ - - if ( mtrr_if == MTRR_IF_INTEL ) { - finalize_mtrr_state (&smp_mtrr_state); - mtrr_state_warn (smp_changes_mask); - } -#else - if ( !mtrr_setup() ) - return 0; /* MTRRs not supported? */ -#endif - -#ifdef CONFIG_PROC_FS - proc_root_mtrr = create_proc_entry ("mtrr", S_IWUSR | S_IRUGO, &proc_root); - if (proc_root_mtrr) { - proc_root_mtrr->owner = THIS_MODULE; - proc_root_mtrr->proc_fops = &mtrr_fops; - } -#endif -#ifdef USERSPACE_INTERFACE - devfs_handle = devfs_register (NULL, "cpu/mtrr", DEVFS_FL_DEFAULT, 0, 0, - S_IFREG | S_IRUGO | S_IWUSR, - &mtrr_fops, NULL); -#endif - init_table (); - return 0; -} /* End Function mtrr_init */ - -/* - * Local Variables: - * mode:c - * c-file-style:"k&r" - * c-basic-offset:4 - * End: - */ diff -Nru a/arch/i386/kernel/process.c b/arch/i386/kernel/process.c --- a/arch/i386/kernel/process.c Fri Aug 16 14:34:50 2002 +++ b/arch/i386/kernel/process.c Fri Aug 16 14:34:50 2002 @@ -566,6 +566,7 @@ struct_cpy(childregs, regs); childregs->eax = 0; childregs->esp = esp; + p->user_tid = NULL; p->thread.esp = (unsigned long) childregs; p->thread.esp0 = (unsigned long) (childregs+1); @@ -587,6 +588,45 @@ IO_BITMAP_BYTES); } + /* + * The common fastpath: + */ + if (!(clone_flags & (CLONE_SETTLS | CLONE_SETTID | CLONE_CLEARTID))) + return 0; + /* + * Set a new TLS for the child thread? + */ + if (clone_flags & CLONE_SETTLS) { + struct desc_struct *desc; + struct user_desc info; + int idx; + + if (copy_from_user(&info, (void *)childregs->esi, sizeof(info))) + return -EFAULT; + if (LDT_empty(&info)) + return -EINVAL; + + idx = info.entry_number; + if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) + return -EINVAL; + + desc = p->thread.tls_array + idx - GDT_ENTRY_TLS_MIN; + desc->a = LDT_entry_a(&info); + desc->b = LDT_entry_b(&info); + } + + /* + * Notify the child of the TID? + */ + if (clone_flags & CLONE_SETTID) + if (put_user(p->pid, (pid_t *)childregs->edx)) + return -EFAULT; + + /* + * Does the userspace VM want the TID cleared on mm_release()? + */ + if (clone_flags & CLONE_CLEARTID) + p->user_tid = (long *) childregs->edx; return 0; } @@ -681,11 +721,8 @@ /* * Load the per-thread Thread-Local Storage descriptor. - * - * NOTE: it's faster to do the two stores unconditionally - * than to branch away. */ - load_TLS_desc(next, cpu); + load_TLS(next, cpu); /* * Save away %fs and %gs. No need to save %es and %ds, as @@ -834,35 +871,114 @@ #undef first_sched /* - * Set the Thread-Local Storage area: + * sys_alloc_thread_area: get a yet unused TLS descriptor index. + */ +static int get_free_idx(void) +{ + struct thread_struct *t = ¤t->thread; + int idx; + + for (idx = 0; idx < GDT_ENTRY_TLS_ENTRIES; idx++) + if (desc_empty(t->tls_array + idx)) + return idx + GDT_ENTRY_TLS_MIN; + return -ESRCH; +} + +/* + * Set a given TLS descriptor: */ -asmlinkage int sys_set_thread_area(unsigned long base, unsigned long flags) +asmlinkage int sys_set_thread_area(struct user_desc *u_info) { struct thread_struct *t = ¤t->thread; - int writable = 0; - int cpu; + struct user_desc info; + struct desc_struct *desc; + int cpu, idx; + + if (copy_from_user(&info, u_info, sizeof(info))) + return -EFAULT; + idx = info.entry_number; - /* do not allow unused flags */ - if (flags & ~TLS_FLAGS_MASK) + /* + * index -1 means the kernel should try to find and + * allocate an empty descriptor: + */ + if (idx == -1) { + idx = get_free_idx(); + if (idx < 0) + return idx; + if (put_user(idx, &u_info->entry_number)) + return -EFAULT; + } + + if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) return -EINVAL; - if (flags & TLS_FLAG_WRITABLE) - writable = 1; + desc = t->tls_array + idx - GDT_ENTRY_TLS_MIN; /* * We must not get preempted while modifying the TLS. */ cpu = get_cpu(); - t->tls_desc.a = ((base & 0x0000ffff) << 16) | 0xffff; - - t->tls_desc.b = (base & 0xff000000) | ((base & 0x00ff0000) >> 16) | - 0xf0000 | (writable << 9) | (1 << 15) | - (1 << 22) | (1 << 23) | 0x7000; + if (LDT_empty(&info)) { + desc->a = 0; + desc->b = 0; + } else { + desc->a = LDT_entry_a(&info); + desc->b = LDT_entry_b(&info); + } + load_TLS(t, cpu); - load_TLS_desc(t, cpu); put_cpu(); - return TLS_ENTRY*8 + 3; + return 0; +} + +/* + * Get the current Thread-Local Storage area: + */ + +#define GET_BASE(desc) ( \ + (((desc)->a >> 16) & 0x0000ffff) | \ + (((desc)->b << 16) & 0x00ff0000) | \ + ( (desc)->b & 0xff000000) ) + +#define GET_LIMIT(desc) ( \ + ((desc)->a & 0x0ffff) | \ + ((desc)->b & 0xf0000) ) + +#define GET_32BIT(desc) (((desc)->b >> 23) & 1) +#define GET_CONTENTS(desc) (((desc)->b >> 10) & 3) +#define GET_WRITABLE(desc) (((desc)->b >> 9) & 1) +#define GET_LIMIT_PAGES(desc) (((desc)->b >> 23) & 1) +#define GET_PRESENT(desc) (((desc)->b >> 15) & 1) +#define GET_USEABLE(desc) (((desc)->b >> 20) & 1) + +asmlinkage int sys_get_thread_area(struct user_desc *u_info) +{ + struct user_desc info; + struct desc_struct *desc; + int idx; + + if (get_user(idx, &u_info->entry_number)) + return -EFAULT; + if (idx < GDT_ENTRY_TLS_MIN || idx > GDT_ENTRY_TLS_MAX) + return -EINVAL; + + desc = current->thread.tls_array + idx - GDT_ENTRY_TLS_MIN; + + info.entry_number = idx; + info.base_addr = GET_BASE(desc); + info.limit = GET_LIMIT(desc); + info.seg_32bit = GET_32BIT(desc); + info.contents = GET_CONTENTS(desc); + info.read_exec_only = !GET_WRITABLE(desc); + info.limit_in_pages = GET_LIMIT_PAGES(desc); + info.seg_not_present = !GET_PRESENT(desc); + info.useable = GET_USEABLE(desc); + + if (copy_to_user(u_info, &info, sizeof(info))) + return -EFAULT; + return 0; } diff -Nru a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c --- a/arch/i386/kernel/setup.c Fri Aug 16 14:34:57 2002 +++ b/arch/i386/kernel/setup.c Fri Aug 16 14:34:57 2002 @@ -275,16 +275,17 @@ * replaces the original e820 map with a new one, removing overlaps. * */ +struct change_member { + struct e820entry *pbios; /* pointer to original bios entry */ + unsigned long long addr; /* address for this change point */ +}; +struct change_member change_point_list[2*E820MAX] __initdata; +struct change_member *change_point[2*E820MAX] __initdata; +struct e820entry *overlap_list[E820MAX] __initdata; +struct e820entry new_bios[E820MAX] __initdata; + static int __init sanitize_e820_map(struct e820entry * biosmap, char * pnr_map) { - struct change_member { - struct e820entry *pbios; /* pointer to original bios entry */ - unsigned long long addr; /* address for this change point */ - }; - struct change_member change_point_list[2*E820MAX]; - struct change_member *change_point[2*E820MAX]; - struct e820entry *overlap_list[E820MAX]; - struct e820entry new_bios[E820MAX]; struct change_member *change_tmp; unsigned long current_type, last_type; unsigned long long last_addr; diff -Nru a/arch/i386/kernel/smpboot.c b/arch/i386/kernel/smpboot.c --- a/arch/i386/kernel/smpboot.c Fri Aug 16 14:34:57 2002 +++ b/arch/i386/kernel/smpboot.c Fri Aug 16 14:34:57 2002 @@ -968,20 +968,6 @@ { int apicid, cpu, bit; - if (clustered_apic_mode && (numnodes > 1)) { - printk("Remapping cross-quad port I/O for %d quads\n", - numnodes); - printk("xquad_portio vaddr 0x%08lx, len %08lx\n", - (u_long) xquad_portio, - (u_long) numnodes * XQUAD_PORTIO_LEN); - xquad_portio = ioremap (XQUAD_PORTIO_BASE, - numnodes * XQUAD_PORTIO_LEN); - } - -#ifdef CONFIG_MTRR - /* Must be done before other processors booted */ - mtrr_init_boot_cpu (); -#endif /* * Initialize the logical to physical CPU number mapping * and the per-CPU profiling counter/multiplier @@ -1075,6 +1061,16 @@ if (GET_APIC_ID(apic_read(APIC_ID)) != boot_cpu_physical_apicid) BUG(); + + if (clustered_apic_mode && (numnodes > 1)) { + printk("Remapping cross-quad port I/O for %d quads\n", + numnodes); + printk("xquad_portio vaddr 0x%08lx, len %08lx\n", + (u_long) xquad_portio, + (u_long) numnodes * XQUAD_PORTIO_LEN); + xquad_portio = ioremap (XQUAD_PORTIO_BASE, + numnodes * XQUAD_PORTIO_LEN); + } /* * Scan the CPU present map and fire up the other CPUs via do_boot_cpu diff -Nru a/arch/i386/kernel/suspend.c b/arch/i386/kernel/suspend.c --- a/arch/i386/kernel/suspend.c Fri Aug 16 14:34:59 2002 +++ b/arch/i386/kernel/suspend.c Fri Aug 16 14:34:59 2002 @@ -207,7 +207,7 @@ struct tss_struct * t = init_tss + cpu; set_tss_desc(cpu,t); /* This just modifies memory; should not be neccessary. But... This is neccessary, because 386 hardware has concept of busy tsc or some similar stupidity. */ - cpu_gdt_table[cpu][TSS_ENTRY].b &= 0xfffffdff; + cpu_gdt_table[cpu][GDT_ENTRY_TSS].b &= 0xfffffdff; load_TR_desc(); /* This does ltr */ load_LDT(¤t->mm->context); /* This does lldt */ diff -Nru a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c --- a/arch/i386/kernel/time.c Fri Aug 16 14:34:57 2002 +++ b/arch/i386/kernel/time.c Fri Aug 16 14:34:57 2002 @@ -639,8 +639,8 @@ } static struct device device_i8253 = { - name: "i8253", - bus_id: "0040", + .name = "i8253", + .bus_id = "0040", }; static int time_init_driverfs(void) diff -Nru a/arch/i386/mm/ioremap.c b/arch/i386/mm/ioremap.c --- a/arch/i386/mm/ioremap.c Fri Aug 16 14:34:55 2002 +++ b/arch/i386/mm/ioremap.c Fri Aug 16 14:34:55 2002 @@ -159,7 +159,7 @@ area->phys_addr = phys_addr; addr = area->addr; if (remap_area_pages(VMALLOC_VMADDR(addr), phys_addr, size, flags)) { - vfree(addr); + vunmap(addr); return NULL; } return (void *) (offset + (char *)addr); @@ -215,13 +215,13 @@ struct vm_struct *p; if (addr <= high_memory) return; - p = remove_kernel_area((void *) (PAGE_MASK & (unsigned long) addr)); + p = remove_vm_area((void *) (PAGE_MASK & (unsigned long) addr)); if (!p) { printk("__iounmap: bad address %p\n", addr); return; } - vmfree_area_pages(VMALLOC_VMADDR(p->addr), p->size); + unmap_vm_area(p); if (p->flags && p->phys_addr < virt_to_phys(high_memory)) { change_page_attr(virt_to_page(__va(p->phys_addr)), p->size >> PAGE_SHIFT, diff -Nru a/arch/ia64/Makefile b/arch/ia64/Makefile --- a/arch/ia64/Makefile Fri Aug 16 14:34:52 2002 +++ b/arch/ia64/Makefile Fri Aug 16 14:34:52 2002 @@ -13,7 +13,7 @@ export AWK -OBJCOPYFLAGS := --strip-all +OBJCOPYFLAGS := --strip-all LDFLAGS_vmlinux := -static -T arch/$(ARCH)/vmlinux.lds AFLAGS_KERNEL := -mconstant-gp EXTRA = @@ -26,7 +26,7 @@ GCC_VERSION=$(shell $(CC) -v 2>&1 | fgrep 'gcc version' | cut -f3 -d' ' | cut -f1 -d'.') ifneq ($(GCC_VERSION),2) - CFLAGS += -frename-registers --param max-inline-insns=2000 + CFLAGS += -frename-registers --param max-inline-insns=5000 endif ifeq ($(CONFIG_ITANIUM_BSTEP_SPECIFIC),y) diff -Nru a/arch/ia64/boot/Makefile b/arch/ia64/boot/Makefile --- a/arch/ia64/boot/Makefile Fri Aug 16 14:34:58 2002 +++ b/arch/ia64/boot/Makefile Fri Aug 16 14:34:58 2002 @@ -12,8 +12,10 @@ OBJECTS = bootloader.o -targets-$(CONFIG_IA64_HP_SIM) += bootloader -targets-$(CONFIG_IA64_GENERIC) += bootloader +targets-$(CONFIG_IA64_HP_SIM) += bootloader +targets-$(CONFIG_IA64_GENERIC) += bootloader + +CFLAGS := $(CFLAGS) $(CFLAGS_KERNEL) all: $(targets-y) diff -Nru a/arch/ia64/config.in b/arch/ia64/config.in --- a/arch/ia64/config.in Fri Aug 16 14:35:01 2002 +++ b/arch/ia64/config.in Fri Aug 16 14:35:01 2002 @@ -64,12 +64,13 @@ fi fi -if [ "$CONFIG_IA64_GENERIC" = "y" -o "$CONFIG_IA64_DIG" = "y" -o "$CONFIG_IA64_HP_ZX1" = "y" ]; then +if [ "$CONFIG_IA64_GENERIC" = "y" -o "$CONFIG_IA64_DIG" = "y" -o "$CONFIG_IA64_HP_ZX1" = "y" ]; +then bool ' Enable IA-64 Machine Check Abort' CONFIG_IA64_MCA define_bool CONFIG_PM y fi -if [ "$CONFIG_IA64_SGI_SN1" = "y" -o "$CONFIG_IA64_SGI_SN2" = "y" ]; then +if [ "$CONFIG_IA64_SGI_SN1" = "y" -o "$CONFIG_IA64_SGI_SN2" = "y" ]; then define_bool CONFIG_IA64_SGI_SN y bool ' Enable extra debugging code' CONFIG_IA64_SGI_SN_DEBUG bool ' Enable SGI Medusa Simulator Support' CONFIG_IA64_SGI_SN_SIM @@ -99,63 +100,45 @@ tristate 'Kernel support for MISC binaries' CONFIG_BINFMT_MISC if [ "$CONFIG_IA64_HP_SIM" = "n" ]; then + source drivers/acpi/Config.in -source drivers/acpi/Config.in + bool 'PCI support' CONFIG_PCI + source drivers/pci/Config.in -bool 'PCI support' CONFIG_PCI -source drivers/pci/Config.in - -bool 'Support for hot-pluggable devices' CONFIG_HOTPLUG -if [ "$CONFIG_HOTPLUG" = "y" ]; then - source drivers/pcmcia/Config.in -else - define_bool CONFIG_PCMCIA n -fi - -source drivers/parport/Config.in + bool 'Support for hot-pluggable devices' CONFIG_HOTPLUG + if [ "$CONFIG_HOTPLUG" = "y" ]; then + source drivers/hotplug/Config.in + source drivers/pcmcia/Config.in + else + define_bool CONFIG_PCMCIA n + fi + source drivers/parport/Config.in fi # !HP_SIM endmenu -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - if [ "$CONFIG_IA64_HP_SIM" = "n" ]; then + source drivers/mtd/Config.in + source drivers/pnp/Config.in + source drivers/block/Config.in + source drivers/ieee1394/Config.in + source drivers/message/i2o/Config.in + source drivers/md/Config.in + source drivers/message/fusion/Config.in -source drivers/mtd/Config.in -source drivers/pnp/Config.in -source drivers/block/Config.in -source drivers/ieee1394/Config.in -source drivers/message/i2o/Config.in -source drivers/md/Config.in -source drivers/message/fusion/Config.in - -mainmenu_option next_comment -comment 'ATA/ATAPI/MFM/RLL support' - -tristate 'ATA/ATAPI/MFM/RLL support' CONFIG_IDE + mainmenu_option next_comment + comment 'ATA/ATAPI/MFM/RLL support' -if [ "$CONFIG_IDE" != "n" ]; then - source drivers/ide/Config.in -else - define_bool CONFIG_BLK_DEV_HD n -fi -endmenu + tristate 'ATA/ATAPI/MFM/RLL support' CONFIG_IDE -else # ! HP_SIM -mainmenu_option next_comment -comment 'Block devices' -tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP -dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET - -tristate 'RAM disk support' CONFIG_BLK_DEV_RAM -if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then - int ' Default RAM disk size' CONFIG_BLK_DEV_RAM_SIZE 4096 + if [ "$CONFIG_IDE" != "n" ]; then + source drivers/ide/Config.in + else + define_bool CONFIG_BLK_DEV_HD n + fi + endmenu fi -endmenu -fi # !HP_SIM mainmenu_option next_comment comment 'SCSI support' @@ -167,81 +150,88 @@ fi endmenu +if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in +fi + if [ "$CONFIG_IA64_HP_SIM" = "n" ]; then + if [ "$CONFIG_NET" = "y" ]; then + mainmenu_option next_comment + comment 'Network device support' + + bool 'Network device support' CONFIG_NETDEVICES + if [ "$CONFIG_NETDEVICES" = "y" ]; then + source drivers/net/Config.in + fi + endmenu + fi + source net/ax25/Config.in + source drivers/isdn/Config.in -if [ "$CONFIG_NET" = "y" ]; then mainmenu_option next_comment - comment 'Network device support' + comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' - bool 'Network device support' CONFIG_NETDEVICES - if [ "$CONFIG_NETDEVICES" = "y" ]; then - source drivers/net/Config.in + bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI + if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then + source drivers/cdrom/Config.in fi endmenu -fi -source net/ax25/Config.in + # + # input before char - char/joystick depends on it. As does USB. + # + source drivers/input/Config.in + source drivers/char/Config.in -source drivers/isdn/Config.in + #source drivers/misc/Config.in -mainmenu_option next_comment -comment 'CD-ROM drivers (not for SCSI or IDE/ATAPI drives)' - -bool 'Support non-SCSI/IDE/ATAPI CDROM drives' CONFIG_CD_NO_IDESCSI -if [ "$CONFIG_CD_NO_IDESCSI" != "n" ]; then - source drivers/cdrom/Config.in -fi -endmenu - -fi # !HP_SIM - -# -# input before char - char/joystick depends on it. As does USB. -# -source drivers/input/Config.in -source drivers/char/Config.in + source drivers/media/Config.in +else # HP_SIM -#source drivers/misc/Config.in - -source drivers/media/Config.in - -source fs/Config.in - -if [ "$CONFIG_VT" = "y" ]; then mainmenu_option next_comment - comment 'Console drivers' - bool 'VGA text console' CONFIG_VGA_CONSOLE - source drivers/video/Config.in - if [ "$CONFIG_FB" = "y" ]; then - define_bool CONFIG_PCI_CONSOLE y + comment 'Block devices' + tristate 'Loopback device support' CONFIG_BLK_DEV_LOOP + dep_tristate 'Network block device support' CONFIG_BLK_DEV_NBD $CONFIG_NET + + tristate 'RAM disk support' CONFIG_BLK_DEV_RAM + if [ "$CONFIG_BLK_DEV_RAM" = "y" -o "$CONFIG_BLK_DEV_RAM" = "m" ]; then + int ' Default RAM disk size' CONFIG_BLK_DEV_RAM_SIZE 4096 fi endmenu -fi +fi # HP_SIM -if [ "$CONFIG_IA64_HP_SIM" = "n" ]; then - -mainmenu_option next_comment -comment 'Sound' - -tristate 'Sound card support' CONFIG_SOUND -if [ "$CONFIG_SOUND" != "n" ]; then - source sound/Config.in -fi -endmenu +source fs/Config.in -source drivers/usb/Config.in +if [ "$CONFIG_IA64_HP_SIM" = "n" ]; then + if [ "$CONFIG_VT" = "y" ]; then + mainmenu_option next_comment + comment 'Console drivers' + bool 'VGA text console' CONFIG_VGA_CONSOLE + source drivers/video/Config.in + if [ "$CONFIG_FB" = "y" ]; then + define_bool CONFIG_PCI_CONSOLE y + fi + endmenu + fi -source lib/Config.in + mainmenu_option next_comment + comment 'Sound' -source net/bluetooth/Config.in + tristate 'Sound card support' CONFIG_SOUND + if [ "$CONFIG_SOUND" != "n" ]; then + source sound/Config.in + fi + endmenu + source drivers/usb/Config.in + source lib/Config.in + source net/bluetooth/Config.in fi # !HP_SIM if [ "$CONFIG_IA64_HP_SIM" != "n" -o "$CONFIG_IA64_GENERIC" != "n" ]; then - source arch/ia64/hp/Config.in + source arch/ia64/hp/sim/Config.in fi - mainmenu_option next_comment comment 'Kernel hacking' @@ -255,7 +245,14 @@ bool ' Disable VHPT' CONFIG_DISABLE_VHPT bool ' Magic SysRq key' CONFIG_MAGIC_SYSRQ - bool ' Early printk support (requires VGA!)' CONFIG_IA64_EARLY_PRINTK + bool ' Early printk support' CONFIG_IA64_EARLY_PRINTK + if [ "$CONFIG_IA64_EARLY_PRINTK" != "n" ]; then + bool ' Early printk on MMIO serial port' CONFIG_IA64_EARLY_PRINTK_UART + if [ "$CONFIG_IA64_EARLY_PRINTK_UART" != "n" ]; then + hex ' UART MMIO base address' CONFIG_IA64_EARLY_PRINTK_UART_BASE ff5e0000 + fi + bool ' Early printk on VGA' CONFIG_IA64_EARLY_PRINTK_VGA + fi bool ' Debug memory allocations' CONFIG_DEBUG_SLAB bool ' Spinlock debugging' CONFIG_DEBUG_SPINLOCK bool ' Turn on compare-and-exchange bug checking (slow!)' CONFIG_IA64_DEBUG_CMPXCHG diff -Nru a/arch/ia64/hp/Config.in b/arch/ia64/hp/Config.in --- a/arch/ia64/hp/Config.in Fri Aug 16 14:34:52 2002 +++ /dev/null Wed Dec 31 16:00:00 1969 @@ -1,9 +0,0 @@ -mainmenu_option next_comment -comment 'HP Simulator drivers' - -bool 'Simulated Ethernet ' CONFIG_HP_SIMETH -bool 'Simulated serial driver support' CONFIG_HP_SIMSERIAL -if [ "$CONFIG_SCSI" != "n" ]; then - bool 'Simulated SCSI disk' CONFIG_HP_SIMSCSI -fi -endmenu diff -Nru a/arch/ia64/hp/common/sba_iommu.c b/arch/ia64/hp/common/sba_iommu.c --- a/arch/ia64/hp/common/sba_iommu.c Fri Aug 16 14:34:54 2002 +++ b/arch/ia64/hp/common/sba_iommu.c Fri Aug 16 14:34:54 2002 @@ -2,6 +2,7 @@ ** IA64 System Bus Adapter (SBA) I/O MMU manager ** ** (c) Copyright 2002 Alex Williamson +** (c) Copyright 2002 Grant Grundler ** (c) Copyright 2002 Hewlett-Packard Company ** ** Portions (c) 2000 Grant Grundler (from parisc I/O MMU code) @@ -110,7 +111,7 @@ */ #define DELAYED_RESOURCE_CNT 16 -#define DEFAULT_DMA_HINT_REG 0 +#define DEFAULT_DMA_HINT_REG(d) 0 #define ZX1_FUNC_ID_VALUE ((PCI_DEVICE_ID_HP_ZX1_SBA << 16) | PCI_VENDOR_ID_HP) #define ZX1_MC_ID ((PCI_DEVICE_ID_HP_ZX1_MC << 16) | PCI_VENDOR_ID_HP) @@ -216,9 +217,10 @@ static int reserve_sba_gart = 1; static struct pci_dev sac_only_dev; -#define sba_sg_iova(sg) (sg->address) +#define sba_sg_address(sg) (page_address((sg)->page) + (sg)->offset) #define sba_sg_len(sg) (sg->length) -#define sba_sg_buffer(sg) (sg->orig_address) +#define sba_sg_iova(sg) (sg->dma_address) +#define sba_sg_iova_len(sg) (sg->dma_length) /* REVISIT - fix me for multiple SBAs/IOCs */ #define GET_IOC(dev) (sba_list->ioc) @@ -232,7 +234,7 @@ ** rather than the HW. I/O MMU allocation alogorithms can be ** faster with smaller size is (to some degree). */ -#define DMA_CHUNK_SIZE (BITS_PER_LONG*PAGE_SIZE) +#define DMA_CHUNK_SIZE (BITS_PER_LONG*IOVP_SIZE) /* Looks nice and keeps the compiler happy */ #define SBA_DEV(d) ((struct sba_device *) (d)) @@ -255,7 +257,7 @@ * sba_dump_tlb - debugging only - print IOMMU operating parameters * @hpa: base address of the IOMMU * - * Print the size/location of the IO MMU PDIR. + * Print the size/location of the IO MMU Pdir. */ static void sba_dump_tlb(char *hpa) @@ -273,12 +275,12 @@ #ifdef ASSERT_PDIR_SANITY /** - * sba_dump_pdir_entry - debugging only - print one IOMMU PDIR entry + * sba_dump_pdir_entry - debugging only - print one IOMMU Pdir entry * @ioc: IO MMU structure which owns the pdir we are interested in. * @msg: text to print ont the output line. * @pide: pdir index. * - * Print one entry of the IO MMU PDIR in human readable form. + * Print one entry of the IO MMU Pdir in human readable form. */ static void sba_dump_pdir_entry(struct ioc *ioc, char *msg, uint pide) @@ -360,25 +362,25 @@ * print the SG list so we can verify it's correct by hand. */ static void -sba_dump_sg( struct ioc *ioc, struct scatterlist *startsg, int nents) +sba_dump_sg(struct ioc *ioc, struct scatterlist *startsg, int nents) { while (nents-- > 0) { - printk(" %d : %08lx/%05x %p\n", + printk(" %d : DMA %08lx/%05x CPU %p\n", nents, (unsigned long) sba_sg_iova(startsg), - sba_sg_len(startsg), - sba_sg_buffer(startsg)); + sba_sg_iova_len(startsg), + sba_sg_address(startsg)); startsg++; } } static void -sba_check_sg( struct ioc *ioc, struct scatterlist *startsg, int nents) +sba_check_sg(struct ioc *ioc, struct scatterlist *startsg, int nents) { struct scatterlist *the_sg = startsg; int the_nents = nents; while (the_nents-- > 0) { - if (sba_sg_buffer(the_sg) == 0x0UL) + if (sba_sg_address(the_sg) == 0x0UL) sba_dump_sg(NULL, startsg, nents); the_sg++; } @@ -404,7 +406,6 @@ #define SBA_IOVA(ioc,iovp,offset,hint_reg) ((ioc->ibase) | (iovp) | (offset) | ((hint_reg)<<(ioc->hint_shift_pdir))) #define SBA_IOVP(ioc,iova) (((iova) & ioc->hint_mask_pdir) & ~(ioc->ibase)) -/* FIXME : review these macros to verify correctness and usage */ #define PDIR_INDEX(iovp) ((iovp)>>IOVP_SHIFT) #define RESMAP_MASK(n) ~(~0UL << (n)) @@ -412,7 +413,7 @@ /** - * sba_search_bitmap - find free space in IO PDIR resource bitmap + * sba_search_bitmap - find free space in IO Pdir resource bitmap * @ioc: IO MMU structure which owns the pdir we are interested in. * @bits_wanted: number of entries we need. * @@ -449,7 +450,7 @@ ** We need the alignment to invalidate I/O TLB using ** SBA HW features in the unmap path. */ - unsigned long o = 1 << get_order(bits_wanted << PAGE_SHIFT); + unsigned long o = 1UL << get_order(bits_wanted << IOVP_SHIFT); uint bitshiftcnt = ROUNDUP(ioc->res_bitshift, o); unsigned long mask; @@ -495,7 +496,7 @@ /** - * sba_alloc_range - find free bits and mark them in IO PDIR resource bitmap + * sba_alloc_range - find free bits and mark them in IO Pdir resource bitmap * @ioc: IO MMU structure which owns the pdir we are interested in. * @size: number of bytes to create a mapping for * @@ -557,7 +558,7 @@ /** - * sba_free_range - unmark bits in IO PDIR resource bitmap + * sba_free_range - unmark bits in IO Pdir resource bitmap * @ioc: IO MMU structure which owns the pdir we are interested in. * @iova: IO virtual address which was previously allocated. * @size: number of bytes to create a mapping for @@ -604,14 +605,14 @@ /** - * sba_io_pdir_entry - fill in one IO PDIR entry - * @pdir_ptr: pointer to IO PDIR entry - * @vba: Virtual CPU address of buffer to map + * sba_io_pdir_entry - fill in one IO Pdir entry + * @pdir_ptr: pointer to IO Pdir entry + * @phys_page: phys CPU address of page to map * * SBA Mapping Routine * - * Given a virtual address (vba, arg1) sba_io_pdir_entry() - * loads the I/O PDIR entry pointed to by pdir_ptr (arg0). + * Given a physical address (phys_page, arg1) sba_io_pdir_entry() + * loads the I/O Pdir entry pointed to by pdir_ptr (arg0). * Each IO Pdir entry consists of 8 bytes as shown below * (LSB == bit 0): * @@ -623,20 +624,12 @@ * V == Valid Bit * U == Unused * PPN == Physical Page Number - * - * The physical address fields are filled with the results of virt_to_phys() - * on the vba. */ -#if 1 -#define sba_io_pdir_entry(pdir_ptr, vba) *pdir_ptr = ((vba & ~0xE000000000000FFFULL) | 0x80000000000000FFULL) -#else -void SBA_INLINE -sba_io_pdir_entry(u64 *pdir_ptr, unsigned long vba) -{ - *pdir_ptr = ((vba & ~0xE000000000000FFFULL) | 0x80000000000000FFULL); -} -#endif +#define SBA_VALID_MASK 0x80000000000000FFULL +#define sba_io_pdir_entry(pdir_ptr, phys_page) *pdir_ptr = (phys_page | SBA_VALID_MASK) +#define sba_io_page(pdir_ptr) (*pdir_ptr & ~SBA_VALID_MASK) + #ifdef ENABLE_MARK_CLEAN /** @@ -660,12 +653,12 @@ #endif /** - * sba_mark_invalid - invalidate one or more IO PDIR entries + * sba_mark_invalid - invalidate one or more IO Pdir entries * @ioc: IO MMU structure which owns the pdir we are interested in. * @iova: IO Virtual Address mapped earlier * @byte_cnt: number of bytes this mapping covers. * - * Marking the IO PDIR entry(ies) as Invalid and invalidate + * Marking the IO Pdir entry(ies) as Invalid and invalidate * corresponding IO TLB entry. The PCOM (Purge Command Register) * is to purge stale entries in the IO TLB when unmapping entries. * @@ -700,14 +693,14 @@ iovp |= IOVP_SHIFT; /* set "size" field for PCOM */ /* - ** clear I/O PDIR entry "valid" bit + ** clear I/O Pdir entry "valid" bit ** Do NOT clear the rest - save it for debugging. ** We should only clear bits that have previously ** been enabled. */ - ioc->pdir_base[off] &= ~(0x80000000000000FFULL); + ioc->pdir_base[off] &= ~SBA_VALID_MASK; } else { - u32 t = get_order(byte_cnt) + PAGE_SHIFT; + u32 t = get_order(byte_cnt) + IOVP_SHIFT; iovp |= t; ASSERT(t <= 31); /* 2GB! Max value of "size" field */ @@ -716,7 +709,7 @@ /* verify this pdir entry is enabled */ ASSERT(ioc->pdir_base[off] >> 63); /* clear I/O Pdir entry "valid" bit first */ - ioc->pdir_base[off] &= ~(0x80000000000000FFULL); + ioc->pdir_base[off] &= ~SBA_VALID_MASK; off++; byte_cnt -= IOVP_SIZE; } while (byte_cnt > 0); @@ -744,7 +737,7 @@ u64 *pdir_start; int pide; #ifdef ALLOW_IOV_BYPASS - unsigned long pci_addr = virt_to_phys(addr); + unsigned long phys_addr = virt_to_phys(addr); #endif ioc = GET_IOC(dev); @@ -754,7 +747,7 @@ /* ** Check if the PCI device can DMA to ptr... if so, just return ptr */ - if ((pci_addr & ~dev->dma_mask) == 0) { + if ((phys_addr & ~dev->dma_mask) == 0) { /* ** Device is bit capable of DMA'ing to the buffer... ** just return the PCI address of ptr @@ -765,8 +758,8 @@ spin_unlock_irqrestore(&ioc->res_lock, flags); #endif DBG_BYPASS("sba_map_single() bypass mask/addr: 0x%lx/0x%lx\n", - dev->dma_mask, pci_addr); - return pci_addr; + dev->dma_mask, phys_addr); + return phys_addr; } #endif @@ -799,7 +792,8 @@ while (size > 0) { ASSERT(((u8 *)pdir_start)[7] == 0); /* verify availability */ - sba_io_pdir_entry(pdir_start, (unsigned long) addr); + + sba_io_pdir_entry(pdir_start, virt_to_phys(addr)); DBG_RUN(" pdir 0x%p %lx\n", pdir_start, *pdir_start); @@ -812,7 +806,7 @@ sba_check_pdir(ioc,"Check after sba_map_single()"); #endif spin_unlock_irqrestore(&ioc->res_lock, flags); - return SBA_IOVA(ioc, iovp, offset, DEFAULT_DMA_HINT_REG); + return SBA_IOVA(ioc, iovp, offset, DEFAULT_DMA_HINT_REG(direction)); } /** @@ -866,6 +860,29 @@ size += offset; size = ROUNDUP(size, IOVP_SIZE); +#ifdef ENABLE_MARK_CLEAN + /* + ** Don't need to hold the spinlock while telling VM pages are "clean". + ** The pages are "busy" in the resource map until we mark them free. + ** But tell VM pages are clean *before* releasing the resource + ** in order to avoid race conditions. + */ + if (direction == PCI_DMA_FROMDEVICE) { + u32 iovp = (u32) SBA_IOVP(ioc,iova); + unsigned int pide = PDIR_INDEX(iovp); + u64 *pdirp = &(ioc->pdir_base[pide]); + size_t byte_cnt = size; + void *addr; + + do { + addr = phys_to_virt(sba_io_page(pdirp)); + mark_clean(addr, min(byte_cnt, IOVP_SIZE)); + pdirp++; + byte_cnt -= IOVP_SIZE; + } while (byte_cnt > 0); + } +#endif + spin_lock_irqsave(&ioc->res_lock, flags); #ifdef CONFIG_PROC_FS ioc->usingle_calls++; @@ -891,40 +908,7 @@ sba_free_range(ioc, iova, size); READ_REG(ioc->ioc_hpa+IOC_PCOM); /* flush purges */ #endif /* DELAYED_RESOURCE_CNT == 0 */ -#ifdef ENABLE_MARK_CLEAN - if (direction == PCI_DMA_FROMDEVICE) { - u32 iovp = (u32) SBA_IOVP(ioc,iova); - int off = PDIR_INDEX(iovp); - void *addr; - - if (size <= IOVP_SIZE) { - addr = phys_to_virt(ioc->pdir_base[off] & - ~0xE000000000000FFFULL); - mark_clean(addr, size); - } else { - size_t byte_cnt = size; - - do { - addr = phys_to_virt(ioc->pdir_base[off] & - ~0xE000000000000FFFULL); - mark_clean(addr, min(byte_cnt, IOVP_SIZE)); - off++; - byte_cnt -= IOVP_SIZE; - - } while (byte_cnt > 0); - } - } -#endif spin_unlock_irqrestore(&ioc->res_lock, flags); - - /* XXX REVISIT for 2.5 Linux - need syncdma for zero-copy support. - ** For Astro based systems this isn't a big deal WRT performance. - ** As long as 2.4 kernels copyin/copyout data from/to userspace, - ** we don't need the syncdma. The issue here is I/O MMU cachelines - ** are *not* coherent in all cases. May be hwrev dependent. - ** Need to investigate more. - asm volatile("syncdma"); - */ } @@ -980,242 +964,109 @@ } -/* -** Since 0 is a valid pdir_base index value, can't use that -** to determine if a value is valid or not. Use a flag to indicate -** the SG list entry contains a valid pdir index. -*/ -#define PIDE_FLAG 0x1UL - #ifdef DEBUG_LARGE_SG_ENTRIES int dump_run_sg = 0; #endif - -/** - * sba_fill_pdir - write allocated SG entries into IO PDIR - * @ioc: IO MMU structure which owns the pdir we are interested in. - * @startsg: list of IOVA/size pairs - * @nents: number of entries in startsg list - * - * Take preprocessed SG list and write corresponding entries - * in the IO PDIR. - */ - -static SBA_INLINE int -sba_fill_pdir( - struct ioc *ioc, - struct scatterlist *startsg, - int nents) -{ - struct scatterlist *dma_sg = startsg; /* pointer to current DMA */ - int n_mappings = 0; - u64 *pdirp = 0; - unsigned long dma_offset = 0; - - dma_sg--; - while (nents-- > 0) { - int cnt = sba_sg_len(startsg); - sba_sg_len(startsg) = 0; - -#ifdef DEBUG_LARGE_SG_ENTRIES - if (dump_run_sg) - printk(" %2d : %08lx/%05x %p\n", - nents, - (unsigned long) sba_sg_iova(startsg), cnt, - sba_sg_buffer(startsg) - ); -#else - DBG_RUN_SG(" %d : %08lx/%05x %p\n", - nents, - (unsigned long) sba_sg_iova(startsg), cnt, - sba_sg_buffer(startsg) - ); -#endif - /* - ** Look for the start of a new DMA stream - */ - if ((u64)sba_sg_iova(startsg) & PIDE_FLAG) { - u32 pide = (u64)sba_sg_iova(startsg) & ~PIDE_FLAG; - dma_offset = (unsigned long) pide & ~IOVP_MASK; - sba_sg_iova(startsg) = 0; - dma_sg++; - sba_sg_iova(dma_sg) = (char *)(pide | ioc->ibase); - pdirp = &(ioc->pdir_base[pide >> IOVP_SHIFT]); - n_mappings++; - } - - /* - ** Look for a VCONTIG chunk - */ - if (cnt) { - unsigned long vaddr = (unsigned long) sba_sg_buffer(startsg); - ASSERT(pdirp); - - /* Since multiple Vcontig blocks could make up - ** one DMA stream, *add* cnt to dma_len. - */ - sba_sg_len(dma_sg) += cnt; - cnt += dma_offset; - dma_offset=0; /* only want offset on first chunk */ - cnt = ROUNDUP(cnt, IOVP_SIZE); -#ifdef CONFIG_PROC_FS - ioc->msg_pages += cnt >> IOVP_SHIFT; -#endif - do { - sba_io_pdir_entry(pdirp, vaddr); - vaddr += IOVP_SIZE; - cnt -= IOVP_SIZE; - pdirp++; - } while (cnt > 0); - } - startsg++; - } -#ifdef DEBUG_LARGE_SG_ENTRIES - dump_run_sg = 0; -#endif - return(n_mappings); -} - - -/* -** Two address ranges are DMA contiguous *iff* "end of prev" and -** "start of next" are both on a page boundry. -** -** (shift left is a quick trick to mask off upper bits) -*/ -#define DMA_CONTIG(__X, __Y) \ - (((((unsigned long) __X) | ((unsigned long) __Y)) << (BITS_PER_LONG - PAGE_SHIFT)) == 0UL) +#define SG_ENT_VIRT_PAGE(sg) page_address((sg)->page) +#define SG_ENT_PHYS_PAGE(SG) virt_to_phys(SG_ENT_VIRT_PAGE(SG)) /** * sba_coalesce_chunks - preprocess the SG list * @ioc: IO MMU structure which owns the pdir we are interested in. - * @startsg: list of IOVA/size pairs + * @startsg: input=SG list output=DMA addr/len pairs filled in * @nents: number of entries in startsg list + * @direction: R/W or both. + * + * Walk the SG list and determine where the breaks are in the DMA stream. + * Allocate IO Pdir resources and fill them in separate loop. + * Returns the number of DMA streams used for output IOVA list. + * Note each DMA stream can consume multiple IO Pdir entries. * - * First pass is to walk the SG list and determine where the breaks are - * in the DMA stream. Allocates PDIR entries but does not fill them. - * Returns the number of DMA chunks. - * - * Doing the fill seperate from the coalescing/allocation keeps the - * code simpler. Future enhancement could make one pass through - * the sglist do both. + * Code is written assuming some coalescing is possible. */ static SBA_INLINE int -sba_coalesce_chunks( struct ioc *ioc, - struct scatterlist *startsg, - int nents) -{ - struct scatterlist *vcontig_sg; /* VCONTIG chunk head */ - unsigned long vcontig_len; /* len of VCONTIG chunk */ - unsigned long vcontig_end; - struct scatterlist *dma_sg; /* next DMA stream head */ - unsigned long dma_offset, dma_len; /* start/len of DMA stream */ +sba_coalesce_chunks(struct ioc *ioc, struct scatterlist *startsg, + int nents, int direction) +{ + struct scatterlist *dma_sg = startsg; /* return array */ int n_mappings = 0; - while (nents > 0) { - unsigned long vaddr = (unsigned long) (startsg->address); + ASSERT(nents > 1); + + do { + unsigned int dma_cnt = 1; /* number of pages in DMA stream */ + unsigned int pide; /* index into IO Pdir array */ + u64 *pdirp; /* pointer into IO Pdir array */ + unsigned long dma_offset, dma_len; /* cumulative DMA stream */ /* ** Prepare for first/next DMA stream */ - dma_sg = vcontig_sg = startsg; - dma_len = vcontig_len = vcontig_end = sba_sg_len(startsg); - vcontig_end += vaddr; - dma_offset = vaddr & ~IOVP_MASK; - - /* PARANOID: clear entries */ - sba_sg_buffer(startsg) = sba_sg_iova(startsg); - sba_sg_iova(startsg) = 0; - sba_sg_len(startsg) = 0; + dma_len = sba_sg_len(startsg); + dma_offset = (unsigned long) sba_sg_address(startsg); + startsg++; + nents--; /* - ** This loop terminates one iteration "early" since - ** it's always looking one "ahead". + ** We want to know how many entries can be coalesced + ** before trying to allocate IO Pdir space. + ** IOVAs can then be allocated "naturally" aligned + ** to take advantage of the block IO TLB flush. */ - while (--nents > 0) { - unsigned long vaddr; /* tmp */ + while (nents) { + unsigned long end_offset = dma_offset + dma_len; - startsg++; - - /* catch brokenness in SCSI layer */ - ASSERT(startsg->length <= DMA_CHUNK_SIZE); + /* prev entry must end on a page boundary */ + if (end_offset & IOVP_MASK) + break; - /* - ** First make sure current dma stream won't - ** exceed DMA_CHUNK_SIZE if we coalesce the - ** next entry. - */ - if (((dma_len + dma_offset + startsg->length + ~IOVP_MASK) & IOVP_MASK) > DMA_CHUNK_SIZE) + /* next entry start on a page boundary? */ + if (startsg->offset) break; /* - ** Then look for virtually contiguous blocks. - ** - ** append the next transaction? + ** make sure current dma stream won't exceed + ** DMA_CHUNK_SIZE if coalescing entries. */ - vaddr = (unsigned long) sba_sg_iova(startsg); - if (vcontig_end == vaddr) - { - vcontig_len += sba_sg_len(startsg); - vcontig_end += sba_sg_len(startsg); - dma_len += sba_sg_len(startsg); - sba_sg_buffer(startsg) = (char *)vaddr; - sba_sg_iova(startsg) = 0; - sba_sg_len(startsg) = 0; - continue; - } + if (((end_offset + startsg->length + ~IOVP_MASK) + & IOVP_MASK) + > DMA_CHUNK_SIZE) + break; -#ifdef DEBUG_LARGE_SG_ENTRIES - dump_run_sg = (vcontig_len > IOVP_SIZE); -#endif + dma_len += sba_sg_len(startsg); + startsg++; + nents--; + dma_cnt++; + } - /* - ** Not virtually contigous. - ** Terminate prev chunk. - ** Start a new chunk. - ** - ** Once we start a new VCONTIG chunk, dma_offset - ** can't change. And we need the offset from the first - ** chunk - not the last one. Ergo Successive chunks - ** must start on page boundaries and dove tail - ** with it's predecessor. - */ - sba_sg_len(vcontig_sg) = vcontig_len; + ASSERT(dma_len <= DMA_CHUNK_SIZE); - vcontig_sg = startsg; - vcontig_len = sba_sg_len(startsg); + /* allocate IO Pdir resource. + ** returns index into (u64) IO Pdir array. + ** IOVA is formed from this. + */ + pide = sba_alloc_range(ioc, dma_cnt << IOVP_SHIFT); + pdirp = &(ioc->pdir_base[pide]); - /* - ** 3) do the entries end/start on page boundaries? - ** Don't update vcontig_end until we've checked. - */ - if (DMA_CONTIG(vcontig_end, vaddr)) - { - vcontig_end = vcontig_len + vaddr; - dma_len += vcontig_len; - sba_sg_buffer(startsg) = (char *)vaddr; - sba_sg_iova(startsg) = 0; - continue; - } else { - break; - } + /* fill_pdir: write stream into IO Pdir */ + while (dma_cnt--) { + sba_io_pdir_entry(pdirp, SG_ENT_PHYS_PAGE(startsg)); + startsg++; + pdirp++; } - /* - ** End of DMA Stream - ** Terminate last VCONTIG block. - ** Allocate space for DMA stream. - */ - sba_sg_len(vcontig_sg) = vcontig_len; - dma_len = (dma_len + dma_offset + ~IOVP_MASK) & IOVP_MASK; - ASSERT(dma_len <= DMA_CHUNK_SIZE); - sba_sg_iova(dma_sg) = (char *) (PIDE_FLAG - | (sba_alloc_range(ioc, dma_len) << IOVP_SHIFT) - | dma_offset); + /* "output" IOVA */ + sba_sg_iova(dma_sg) = SBA_IOVA(ioc, + ((dma_addr_t) pide << IOVP_SHIFT), + dma_offset, + DEFAULT_DMA_HINT_REG(direction)); + sba_sg_iova_len(dma_sg) = dma_len; + + dma_sg++; n_mappings++; - } + } while (nents); return n_mappings; } @@ -1223,7 +1074,7 @@ /** * sba_map_sg - map Scatter/Gather list - * @dev: instance of PCI owned by the driver that's asking. + * @dev: instance of PCI device owned by the driver that's asking. * @sglist: array of buffer/length pairs * @nents: number of entries in list * @direction: R/W or both. @@ -1234,42 +1085,46 @@ int direction) { struct ioc *ioc; - int coalesced, filled = 0; + int filled = 0; unsigned long flags; #ifdef ALLOW_IOV_BYPASS struct scatterlist *sg; #endif - DBG_RUN_SG("%s() START %d entries\n", __FUNCTION__, nents); + DBG_RUN_SG("%s() START %d entries, 0x%p,0x%x\n", __FUNCTION__, nents, + sba_sg_address(sglist), sba_sg_len(sglist)); + ioc = GET_IOC(dev); ASSERT(ioc); #ifdef ALLOW_IOV_BYPASS if (dev->dma_mask >= ioc->dma_mask) { - for (sg = sglist ; filled < nents ; filled++, sg++){ - sba_sg_buffer(sg) = sba_sg_iova(sg); - sba_sg_iova(sg) = (char *)virt_to_phys(sba_sg_buffer(sg)); + for (sg = sglist ; filled < nents ; filled++, sg++) { + sba_sg_iova(sg) = virt_to_phys(sba_sg_address(sg)); + sba_sg_iova_len(sg) = sba_sg_len(sg); } #ifdef CONFIG_PROC_FS spin_lock_irqsave(&ioc->res_lock, flags); ioc->msg_bypass++; spin_unlock_irqrestore(&ioc->res_lock, flags); #endif + DBG_RUN_SG("%s() DONE %d mappings bypassed\n", __FUNCTION__, filled); return filled; } #endif /* Fast path single entry scatterlists. */ if (nents == 1) { - sba_sg_buffer(sglist) = sba_sg_iova(sglist); - sba_sg_iova(sglist) = (char *)sba_map_single(dev, - sba_sg_buffer(sglist), - sba_sg_len(sglist), direction); + sba_sg_iova(sglist) = sba_map_single(dev, + (void *) sba_sg_iova(sglist), + sba_sg_len(sglist), direction); + sba_sg_iova_len(sglist) = sba_sg_len(sglist); #ifdef CONFIG_PROC_FS /* ** Should probably do some stats counting, but trying to ** be precise quickly starts wasting CPU time. */ #endif + DBG_RUN_SG("%s() DONE 1 mapping\n", __FUNCTION__); return 1; } @@ -1286,26 +1141,11 @@ #ifdef CONFIG_PROC_FS ioc->msg_calls++; #endif - - /* - ** First coalesce the chunks and allocate I/O pdir space - ** - ** If this is one DMA stream, we can properly map using the - ** correct virtual address associated with each DMA page. - ** w/o this association, we wouldn't have coherent DMA! - ** Access to the virtual address is what forces a two pass algorithm. - */ - coalesced = sba_coalesce_chunks(ioc, sglist, nents); /* - ** Program the I/O Pdir - ** - ** map the virtual addresses to the I/O Pdir - ** o dma_address will contain the pdir index - ** o dma_len will contain the number of bytes to map - ** o address contains the virtual address. + ** coalesce and program the I/O Pdir */ - filled = sba_fill_pdir(ioc, sglist, nents); + filled = sba_coalesce_chunks(ioc, sglist, nents, direction); #ifdef ASSERT_PDIR_SANITY if (sba_check_pdir(ioc,"Check after sba_map_sg()")) @@ -1317,7 +1157,6 @@ spin_unlock_irqrestore(&ioc->res_lock, flags); - ASSERT(coalesced == filled); DBG_RUN_SG("%s() DONE %d mappings\n", __FUNCTION__, filled); return filled; @@ -1341,8 +1180,8 @@ unsigned long flags; #endif - DBG_RUN_SG("%s() START %d entries, %p,%x\n", - __FUNCTION__, nents, sba_sg_buffer(sglist), sglist->length); + DBG_RUN_SG("%s() START %d entries, 0x%p,0x%x\n", + __FUNCTION__, nents, sba_sg_address(sglist), sba_sg_len(sglist)); ioc = GET_IOC(dev); ASSERT(ioc); @@ -1360,7 +1199,7 @@ while (sba_sg_len(sglist) && nents--) { sba_unmap_single(dev, (dma_addr_t)sba_sg_iova(sglist), - sba_sg_len(sglist), direction); + sba_sg_iova_len(sglist), direction); #ifdef CONFIG_PROC_FS /* ** This leaves inconsistent data in the stats, but we can't @@ -1368,7 +1207,7 @@ ** were coalesced to a single entry. The stats are fun, ** but speed is more important. */ - ioc->usg_pages += (((u64)sba_sg_iova(sglist) & ~IOVP_MASK) + sba_sg_len(sglist) + IOVP_SIZE - 1) >> PAGE_SHIFT; + ioc->usg_pages += (((u64)sba_sg_iova(sglist) & ~IOVP_MASK) + sba_sg_len(sglist) + IOVP_SIZE - 1) >> IOVP_SHIFT; #endif ++sglist; } @@ -1429,12 +1268,12 @@ __FUNCTION__, ioc->ioc_hpa, iova_space_size>>20, iov_order + PAGE_SHIFT, ioc->pdir_size); - /* FIXME : DMA HINTs not used */ + /* XXX DMA HINTs not used */ ioc->hint_shift_pdir = iov_order + PAGE_SHIFT; ioc->hint_mask_pdir = ~(0x3 << (iov_order + PAGE_SHIFT)); - ioc->pdir_base = - pdir_base = (void *) __get_free_pages(GFP_KERNEL, get_order(pdir_size)); + ioc->pdir_base = pdir_base = + (void *) __get_free_pages(GFP_KERNEL, get_order(pdir_size)); if (NULL == pdir_base) { panic(__FILE__ ":%s() could not allocate I/O Page Table\n", __FUNCTION__); @@ -1452,20 +1291,8 @@ /* build IMASK for IOC and Elroy */ iova_space_mask = 0xffffffff; - iova_space_mask <<= (iov_order + PAGE_SHIFT); + iova_space_mask <<= (iov_order + IOVP_SHIFT); -#ifdef CONFIG_IA64_HP_PROTO - /* - ** REVISIT - this is a kludge, but we won't be supporting anything but - ** zx1 2.0 or greater for real. When fw is in shape, ibase will - ** be preprogrammed w/ the IOVA hole base and imask will give us - ** the size. - */ - if ((sba_dev->hw_rev & 0xFF) < 0x20) { - DBG_INIT("%s() Found SBA rev < 2.0, setting IOVA base to 0. This device will not be supported in the future.\n", __FUNCTION__); - ioc->ibase = 0x0; - } else -#endif ioc->ibase = READ_REG(ioc->ioc_hpa + IOC_IBASE) & 0xFFFFFFFEUL; ioc->imask = iova_space_mask; /* save it */ @@ -1474,7 +1301,7 @@ __FUNCTION__, ioc->ibase, ioc->imask); /* - ** FIXME: Hint registers are programmed with default hint + ** XXX DMA HINT registers are programmed with default hint ** values during boot, so hints should be sane even if we ** can't reprogram them the way drivers want. */ @@ -1487,8 +1314,8 @@ */ ioc->imask |= 0xFFFFFFFF00000000UL; - /* Set I/O PDIR Page size to system page size */ - switch (PAGE_SHIFT) { + /* Set I/O Pdir page size to system page size */ + switch (IOVP_SHIFT) { case 12: /* 4K */ tcnfg = 0; break; @@ -1628,7 +1455,7 @@ sba_dev->ioc[i].pdir_base[0] = 0x8000badbadc0ffeeULL; for (reserved_iov = 0xA0000 ; reserved_iov < 0xC0000 ; reserved_iov += IOVP_SIZE) { - u64 *res_ptr = sba_dev->ioc[i].res_map; + u64 *res_ptr = (u64 *) sba_dev->ioc[i].res_map; int index = PDIR_INDEX(reserved_iov); int res_word; u64 mask; @@ -1636,7 +1463,7 @@ res_word = (int)(index / BITS_PER_LONG); mask = 0x1UL << (index - (res_word * BITS_PER_LONG)); res_ptr[res_word] |= mask; - sba_dev->ioc[i].pdir_base[PDIR_INDEX(reserved_iov)] = (0x80000000000000FFULL | reserved_iov); + sba_dev->ioc[i].pdir_base[PDIR_INDEX(reserved_iov)] = (SBA_VALID_MASK | reserved_iov); } } @@ -1759,8 +1586,8 @@ for (i = 0; i < PCI_NUM_RESOURCES; i++) { if (pci_resource_flags(device, i) == IORESOURCE_MEM) { - hpa = ioremap(pci_resource_start(device, i), - pci_resource_len(device, i)); + hpa = (u64) ioremap(pci_resource_start(device, i), + pci_resource_len(device, i)); break; } } @@ -1768,7 +1595,7 @@ func_id = READ_REG(hpa + SBA_FUNC_ID); if (func_id == ZX1_FUNC_ID_VALUE) { - (void)strcpy(sba_rev, "zx1"); + strcpy(sba_rev, "zx1"); func_offset = zx1_func_offsets; } else { return; diff -Nru a/arch/ia64/hp/sim/Config.in b/arch/ia64/hp/sim/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ia64/hp/sim/Config.in Fri Aug 16 14:34:52 2002 @@ -0,0 +1,9 @@ +mainmenu_option next_comment +comment 'HP Simulator drivers' + +bool 'Simulated Ethernet ' CONFIG_HP_SIMETH +bool 'Simulated serial driver support' CONFIG_HP_SIMSERIAL +if [ "$CONFIG_SCSI" != "n" ]; then + bool 'Simulated SCSI disk' CONFIG_HP_SIMSCSI +fi +endmenu diff -Nru a/arch/ia64/hp/sim/hpsim_console.c b/arch/ia64/hp/sim/hpsim_console.c --- a/arch/ia64/hp/sim/hpsim_console.c Fri Aug 16 14:34:55 2002 +++ b/arch/ia64/hp/sim/hpsim_console.c Fri Aug 16 14:34:55 2002 @@ -30,12 +30,12 @@ static kdev_t simcons_console_device (struct console *); struct console hpsim_cons = { - name: "simcons", - write: simcons_write, - device: simcons_console_device, - setup: simcons_init, - flags: CON_PRINTBUFFER, - index: -1, + .name = "simcons", + .write = simcons_write, + .device = simcons_console_device, + .setup = simcons_init, + .flags = CON_PRINTBUFFER, + .index = -1, }; static int diff -Nru a/arch/ia64/hp/sim/hpsim_irq.c b/arch/ia64/hp/sim/hpsim_irq.c --- a/arch/ia64/hp/sim/hpsim_irq.c Fri Aug 16 14:34:58 2002 +++ b/arch/ia64/hp/sim/hpsim_irq.c Fri Aug 16 14:34:58 2002 @@ -22,14 +22,14 @@ } static struct hw_interrupt_type irq_type_hp_sim = { - typename: "hpsim", - startup: hpsim_irq_startup, - shutdown: hpsim_irq_noop, - enable: hpsim_irq_noop, - disable: hpsim_irq_noop, - ack: hpsim_irq_noop, - end: hpsim_irq_noop, - set_affinity: (void (*)(unsigned int, unsigned long)) hpsim_irq_noop, + .typename = "hpsim", + .startup = hpsim_irq_startup, + .shutdown = hpsim_irq_noop, + .enable = hpsim_irq_noop, + .disable = hpsim_irq_noop, + .ack = hpsim_irq_noop, + .end = hpsim_irq_noop, + .set_affinity = (void (*)(unsigned int, unsigned long)) hpsim_irq_noop, }; void __init diff -Nru a/arch/ia64/hp/sim/hpsim_setup.c b/arch/ia64/hp/sim/hpsim_setup.c --- a/arch/ia64/hp/sim/hpsim_setup.c Fri Aug 16 14:34:59 2002 +++ b/arch/ia64/hp/sim/hpsim_setup.c Fri Aug 16 14:34:59 2002 @@ -1,18 +1,19 @@ /* * Platform dependent support for HP simulator. * - * Copyright (C) 1998, 1999 Hewlett-Packard Co - * Copyright (C) 1998, 1999 David Mosberger-Tang + * Copyright (C) 1998, 1999, 2002 Hewlett-Packard Co + * David Mosberger-Tang * Copyright (C) 1999 Vijay Chander */ +#include #include +#include #include +#include #include +#include #include #include -#include -#include -#include #include #include @@ -55,5 +56,5 @@ { ROOT_DEV = Root_SDA1; /* default to first SCSI drive */ - register_console (&hpsim_cons); + register_console(&hpsim_cons); } diff -Nru a/arch/ia64/hp/sim/simscsi.c b/arch/ia64/hp/sim/simscsi.c --- a/arch/ia64/hp/sim/simscsi.c Fri Aug 16 14:34:57 2002 +++ b/arch/ia64/hp/sim/simscsi.c Fri Aug 16 14:34:57 2002 @@ -62,7 +62,9 @@ extern long ia64_ssc (long arg0, long arg1, long arg2, long arg3, int nr); -static int desc[8] = { -1, -1, -1, -1, -1, -1, -1, -1 }; +static int desc[16] = { + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 +}; static struct queue_entry { Scsi_Cmnd *sc; @@ -148,9 +150,9 @@ { int size = disk->capacity; - ip[0] = 64; - ip[1] = 32; - ip[2] = size >> 11; + ip[0] = 64; /* heads */ + ip[1] = 32; /* sectors */ + ip[2] = size >> 11; /* cylinders */ return 0; } @@ -229,6 +231,29 @@ simscsi_readwrite(sc, mode, offset, sc->cmnd[4]*512); } +static size_t +simscsi_get_disk_size (int fd) +{ + struct disk_stat stat; + size_t bit, sectors = 0; + struct disk_req req; + char buf[512]; + + /* + * This is a bit kludgey: the simulator doesn't provide a direct way of determining + * the disk size, so we do a binary search, assuming a maximum disk size of 4GB. + */ + for (bit = (4UL << 30)/512; bit != 0; bit >>= 1) { + req.addr = __pa(&buf); + req.len = sizeof(buf); + ia64_ssc(fd, 1, __pa(&req), ((sectors | bit) - 1)*512, SSC_READ); + stat.fd = fd; + ia64_ssc(__pa(&stat), 0, 0, 0, SSC_WAIT_COMPLETION); + if (stat.count == sizeof(buf)) + sectors |= bit; + } + return sectors - 1; /* return last valid sector number */ +} static void simscsi_readwrite10 (Scsi_Cmnd *sc, int mode) @@ -247,6 +272,7 @@ simscsi_queuecommand (Scsi_Cmnd *sc, void (*done)(Scsi_Cmnd *)) { char fname[MAX_ROOT_LEN+16]; + size_t disk_size; char *buf; #if DEBUG_SIMSCSI register long sp asm ("sp"); @@ -258,15 +284,15 @@ sc->result = DID_BAD_TARGET << 16; sc->scsi_done = done; - if (sc->target <= 7 && sc->lun == 0) { + if (sc->target <= 15 && sc->lun == 0) { switch (sc->cmnd[0]) { case INQUIRY: if (sc->request_bufflen < 35) { break; } sprintf (fname, "%s%c", simscsi_root, 'a' + sc->target); - desc[sc->target] = ia64_ssc (__pa(fname), SSC_READ_ACCESS|SSC_WRITE_ACCESS, - 0, 0, SSC_OPEN); + desc[sc->target] = ia64_ssc(__pa(fname), SSC_READ_ACCESS|SSC_WRITE_ACCESS, + 0, 0, SSC_OPEN); if (desc[sc->target] < 0) { /* disk doesn't exist... */ break; @@ -319,11 +345,13 @@ } buf = sc->request_buffer; + disk_size = simscsi_get_disk_size(desc[sc->target]); + /* pretend to be a 1GB disk (partition table contains real stuff): */ - buf[0] = 0x00; - buf[1] = 0x1f; - buf[2] = 0xff; - buf[3] = 0xff; + buf[0] = (disk_size >> 24) & 0xff; + buf[1] = (disk_size >> 16) & 0xff; + buf[2] = (disk_size >> 8) & 0xff; + buf[3] = (disk_size >> 0) & 0xff; /* set block size of 512 bytes: */ buf[4] = 0; buf[5] = 0; diff -Nru a/arch/ia64/hp/sim/simserial.c b/arch/ia64/hp/sim/simserial.c --- a/arch/ia64/hp/sim/simserial.c Fri Aug 16 14:34:57 2002 +++ b/arch/ia64/hp/sim/simserial.c Fri Aug 16 14:34:57 2002 @@ -13,6 +13,7 @@ * * 02/04/00 D. Mosberger Merged in serial.c bug fixes in rs_close(). * 02/25/00 D. Mosberger Synced up with 2.3.99pre-5 version of serial.c. + * 07/30/02 D. Mosberger Replace sti()/cli() with explicit spinlocks & local irq masking */ #include @@ -31,6 +32,7 @@ #include #include +#include #include #ifdef CONFIG_KDB @@ -61,6 +63,7 @@ static char *serial_name = "SimSerial driver"; static char *serial_version = "0.6"; +static spinlock_t serial_lock = SPIN_LOCK_UNLOCKED; /* * This has been extracted from asm/serial.h. We need one eventually but @@ -232,14 +235,14 @@ if (!tty || !info->xmit.buf) return; - save_flags(flags); cli(); + spin_lock_irqsave(&serial_lock, flags); if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) { - restore_flags(flags); + spin_unlock_irqrestore(&serial_lock, flags); return; } info->xmit.buf[info->xmit.head] = ch; info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); - restore_flags(flags); + spin_unlock_irqrestore(&serial_lock, flags); } static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) @@ -247,7 +250,7 @@ int count; unsigned long flags; - save_flags(flags); cli(); + spin_lock_irqsave(&serial_lock, flags); if (info->x_char) { char c = info->x_char; @@ -290,7 +293,7 @@ info->xmit.tail += count; } out: - restore_flags(flags); + spin_unlock_irqrestore(&serial_lock, flags); } static void rs_flush_chars(struct tty_struct *tty) @@ -314,7 +317,6 @@ if (!tty || !info->xmit.buf || !tmp_buf) return 0; - save_flags(flags); if (from_user) { down(&tmp_buf_sem); while (1) { @@ -331,21 +333,26 @@ ret = -EFAULT; break; } - cli(); - c1 = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); - if (c1 < c) - c = c1; - memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); - info->xmit.head = ((info->xmit.head + c) & - (SERIAL_XMIT_SIZE-1)); - restore_flags(flags); + + spin_lock_irqsave(&serial_lock, flags); + { + c1 = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, + SERIAL_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + } + spin_unlock_irqrestore(&serial_lock, flags); + buf += c; count -= c; ret += c; } up(&tmp_buf_sem); } else { - cli(); + spin_lock_irqsave(&serial_lock, flags); while (1) { c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); if (count < c) @@ -360,7 +367,7 @@ count -= c; ret += c; } - restore_flags(flags); + spin_unlock_irqrestore(&serial_lock, flags); } /* * Hey, we transmit directly from here in our case @@ -391,9 +398,9 @@ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; - save_flags(flags); cli(); + spin_lock_irqsave(&serial_lock, flags); info->xmit.head = info->xmit.tail = 0; - restore_flags(flags); + spin_unlock_irqrestore(&serial_lock, flags); wake_up_interruptible(&tty->write_wait); @@ -566,44 +573,45 @@ state->irq); #endif - save_flags(flags); cli(); /* Disable interrupts */ - - /* - * First unlink the serial port from the IRQ chain... - */ - if (info->next_port) - info->next_port->prev_port = info->prev_port; - if (info->prev_port) - info->prev_port->next_port = info->next_port; - else - IRQ_ports[state->irq] = info->next_port; + spin_lock_irqsave(&serial_lock, flags); + { + /* + * First unlink the serial port from the IRQ chain... + */ + if (info->next_port) + info->next_port->prev_port = info->prev_port; + if (info->prev_port) + info->prev_port->next_port = info->next_port; + else + IRQ_ports[state->irq] = info->next_port; - /* - * Free the IRQ, if necessary - */ - if (state->irq && (!IRQ_ports[state->irq] || - !IRQ_ports[state->irq]->next_port)) { - if (IRQ_ports[state->irq]) { - free_irq(state->irq, NULL); - retval = request_irq(state->irq, rs_interrupt_single, - IRQ_T(info), "serial", NULL); - - if (retval) - printk("serial shutdown: request_irq: error %d" - " Couldn't reacquire IRQ.\n", retval); - } else - free_irq(state->irq, NULL); - } + /* + * Free the IRQ, if necessary + */ + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { + free_irq(state->irq, NULL); + retval = request_irq(state->irq, rs_interrupt_single, + IRQ_T(info), "serial", NULL); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq, NULL); + } - if (info->xmit.buf) { - free_page((unsigned long) info->xmit.buf); - info->xmit.buf = 0; - } + if (info->xmit.buf) { + free_page((unsigned long) info->xmit.buf); + info->xmit.buf = 0; + } - if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); + if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); - info->flags &= ~ASYNC_INITIALIZED; - restore_flags(flags); + info->flags &= ~ASYNC_INITIALIZED; + } + spin_unlock_irqrestore(&serial_lock, flags); } /* @@ -626,14 +634,13 @@ state = info->state; - save_flags(flags); cli(); - + spin_lock_irqsave(&serial_lock, flags); if (tty_hung_up_p(filp)) { #ifdef SIMSERIAL_DEBUG printk("rs_close: hung_up\n"); #endif MOD_DEC_USE_COUNT; - restore_flags(flags); + spin_unlock_irqrestore(&serial_lock, flags); return; } #ifdef SIMSERIAL_DEBUG @@ -658,11 +665,11 @@ } if (state->count) { MOD_DEC_USE_COUNT; - restore_flags(flags); + spin_unlock_irqrestore(&serial_lock, flags); return; } info->flags |= ASYNC_CLOSING; - restore_flags(flags); + spin_unlock_irqrestore(&serial_lock, flags); /* * Now we wait for the transmit buffer to clear; and we notify @@ -770,7 +777,7 @@ if (!page) return -ENOMEM; - save_flags(flags); cli(); + spin_lock_irqsave(&serial_lock, flags); if (info->flags & ASYNC_INITIALIZED) { free_page(page); @@ -851,11 +858,11 @@ } info->flags |= ASYNC_INITIALIZED; - restore_flags(flags); + spin_unlock_irqrestore(&serial_lock, flags); return 0; errout: - restore_flags(flags); + spin_unlock_irqrestore(&serial_lock, flags); return retval; } diff -Nru a/arch/ia64/hp/zx1/hpzx1_machvec.c b/arch/ia64/hp/zx1/hpzx1_machvec.c --- a/arch/ia64/hp/zx1/hpzx1_machvec.c Fri Aug 16 14:34:56 2002 +++ b/arch/ia64/hp/zx1/hpzx1_machvec.c Fri Aug 16 14:34:56 2002 @@ -1,4 +1,2 @@ #define MACHVEC_PLATFORM_NAME hpzx1 #include -#define MACHVEC_PLATFORM_NAME hpzx1 -#include diff -Nru a/arch/ia64/hp/zx1/hpzx1_misc.c b/arch/ia64/hp/zx1/hpzx1_misc.c --- a/arch/ia64/hp/zx1/hpzx1_misc.c Fri Aug 16 14:34:57 2002 +++ b/arch/ia64/hp/zx1/hpzx1_misc.c Fri Aug 16 14:34:57 2002 @@ -12,111 +12,65 @@ #include #include #include -#include + +#include #include +#include -#include "../drivers/acpi/include/platform/acgcc.h" -#include "../drivers/acpi/include/actypes.h" -#include "../drivers/acpi/include/acexcep.h" -#include "../drivers/acpi/include/acpixf.h" -#include "../drivers/acpi/include/actbl.h" -#include "../drivers/acpi/include/acconfig.h" -#include "../drivers/acpi/include/acmacros.h" -#include "../drivers/acpi/include/aclocal.h" -#include "../drivers/acpi/include/acobject.h" -#include "../drivers/acpi/include/acstruct.h" -#include "../drivers/acpi/include/acnamesp.h" -#include "../drivers/acpi/include/acutils.h" +extern acpi_status acpi_evaluate_integer (acpi_handle, acpi_string, acpi_object_list *, + unsigned long *); #define PFX "hpzx1: " +static int hpzx1_devices; + struct fake_pci_dev { - struct fake_pci_dev *next; - unsigned char bus; - unsigned int devfn; - int sizing; // in middle of BAR sizing operation? unsigned long csr_base; - unsigned int csr_size; + unsigned long csr_size; unsigned long mapped_csrs; // ioremapped + int sizing; // in middle of BAR sizing operation? }; -static struct fake_pci_dev *fake_pci_head, **fake_pci_tail = &fake_pci_head; - static struct pci_ops *orig_pci_ops; -static inline struct fake_pci_dev * -fake_pci_find_slot(unsigned char bus, unsigned int devfn) -{ - struct fake_pci_dev *dev; - - for (dev = fake_pci_head; dev; dev = dev->next) - if (dev->bus == bus && dev->devfn == devfn) - return dev; - return NULL; +#define HP_CFG_RD(sz, bits, name) \ +static int hp_cfg_read##sz (struct pci_dev *dev, int where, u##bits *value) \ +{ \ + struct fake_pci_dev *fake_dev; \ + if (!(fake_dev = (struct fake_pci_dev *) dev->sysdata)) \ + return orig_pci_ops->name(dev, where, value); \ + \ + if (where == PCI_BASE_ADDRESS_0) { \ + if (fake_dev->sizing) \ + *value = ~(fake_dev->csr_size - 1); \ + else \ + *value = (fake_dev->csr_base & \ + PCI_BASE_ADDRESS_MEM_MASK) | \ + PCI_BASE_ADDRESS_SPACE_MEMORY; \ + fake_dev->sizing = 0; \ + return PCIBIOS_SUCCESSFUL; \ + } \ + *value = read##sz(fake_dev->mapped_csrs + where); \ + if (where == PCI_COMMAND) \ + *value |= PCI_COMMAND_MEMORY; /* SBA omits this */ \ + return PCIBIOS_SUCCESSFUL; \ } -static struct fake_pci_dev * -alloc_fake_pci_dev(void) -{ - struct fake_pci_dev *dev; - - dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) - return NULL; - - memset(dev, 0, sizeof(*dev)); - - *fake_pci_tail = dev; - fake_pci_tail = &dev->next; - - return dev; -} - -#define HP_CFG_RD(sz, bits, name) \ -static int hp_cfg_read##sz (struct pci_dev *dev, int where, u##bits *value) \ -{ \ - struct fake_pci_dev *fake_dev; \ - if (!(fake_dev = fake_pci_find_slot(dev->bus->number, dev->devfn))) \ - return orig_pci_ops->name(dev, where, value); \ - \ - switch (where) { \ - case PCI_COMMAND: \ - *value = read##sz(fake_dev->mapped_csrs + where); \ - *value |= PCI_COMMAND_MEMORY; /* SBA omits this */ \ - break; \ - case PCI_BASE_ADDRESS_0: \ - if (fake_dev->sizing) \ - *value = ~(fake_dev->csr_size - 1); \ - else \ - *value = (fake_dev->csr_base & \ - PCI_BASE_ADDRESS_MEM_MASK) | \ - PCI_BASE_ADDRESS_SPACE_MEMORY; \ - fake_dev->sizing = 0; \ - break; \ - default: \ - *value = read##sz(fake_dev->mapped_csrs + where); \ - break; \ - } \ - return PCIBIOS_SUCCESSFUL; \ -} - -#define HP_CFG_WR(sz, bits, name) \ -static int hp_cfg_write##sz (struct pci_dev *dev, int where, u##bits value) \ -{ \ - struct fake_pci_dev *fake_dev; \ - if (!(fake_dev = fake_pci_find_slot(dev->bus->number, dev->devfn))) \ - return orig_pci_ops->name(dev, where, value); \ - \ - switch (where) { \ - case PCI_BASE_ADDRESS_0: \ - if (value == ~0) \ - fake_dev->sizing = 1; \ - break; \ - default: \ - write##sz(value, fake_dev->mapped_csrs + where); \ - break; \ - } \ - return PCIBIOS_SUCCESSFUL; \ +#define HP_CFG_WR(sz, bits, name) \ +static int hp_cfg_write##sz (struct pci_dev *dev, int where, u##bits value) \ +{ \ + struct fake_pci_dev *fake_dev; \ + \ + if (!(fake_dev = (struct fake_pci_dev *) dev->sysdata)) \ + return orig_pci_ops->name(dev, where, value); \ + \ + if (where == PCI_BASE_ADDRESS_0) { \ + if (value == (u##bits) ~0) \ + fake_dev->sizing = 1; \ + return PCIBIOS_SUCCESSFUL; \ + } else \ + write##sz(value, fake_dev->mapped_csrs + where); \ + return PCIBIOS_SUCCESSFUL; \ } HP_CFG_RD(b, 8, read_byte) @@ -135,51 +89,86 @@ hp_cfg_writel, }; -/* - * Assume we'll never have a physical slot higher than 0x10, so we can - * use slots above that for "fake" PCI devices to represent things - * that only show up in the ACPI namespace. - */ -#define HP_MAX_SLOT 0x10 - -static struct fake_pci_dev * -hpzx1_fake_pci_dev(unsigned long addr, unsigned int bus, unsigned int size) +static void +hpzx1_fake_pci_dev(char *name, unsigned int busnum, unsigned long addr, unsigned int size) { - struct fake_pci_dev *dev; - int slot; + struct fake_pci_dev *fake; + int slot, ret; + struct pci_dev *dev; + struct pci_bus *b, *bus = NULL; + u8 hdr; + + fake = kmalloc(sizeof(*fake), GFP_KERNEL); + if (!fake) { + printk(KERN_ERR PFX "No memory for %s (0x%p) sysdata\n", name, (void *) addr); + return; + } - // Note: lspci thinks 0x1f is invalid - for (slot = 0x1e; slot > HP_MAX_SLOT; slot--) { - if (!fake_pci_find_slot(bus, PCI_DEVFN(slot, 0))) + memset(fake, 0, sizeof(*fake)); + fake->csr_base = addr; + fake->csr_size = size; + fake->mapped_csrs = (unsigned long) ioremap(addr, size); + fake->sizing = 0; + + pci_for_each_bus(b) + if (busnum == b->number) { + bus = b; break; + } + + if (!bus) { + printk(KERN_ERR PFX "No host bus 0x%02x for %s (0x%p)\n", + busnum, name, (void *) addr); + kfree(fake); + return; } - if (slot == HP_MAX_SLOT) { - printk(KERN_ERR PFX - "no slot space for device (0x%p) on bus 0x%02x\n", - (void *) addr, bus); - return NULL; + + for (slot = 0x1e; slot; slot--) + if (!pci_find_slot(busnum, PCI_DEVFN(slot, 0))) + break; + + if (slot < 0) { + printk(KERN_ERR PFX "No space for %s (0x%p) on bus 0x%02x\n", + name, (void *) addr, busnum); + kfree(fake); + return; } - dev = alloc_fake_pci_dev(); + dev = kmalloc(sizeof(*dev), GFP_KERNEL); if (!dev) { - printk(KERN_ERR PFX - "no memory for device (0x%p) on bus 0x%02x\n", - (void *) addr, bus); - return NULL; + printk(KERN_ERR PFX "No memory for %s (0x%p)\n", name, (void *) addr); + kfree(fake); + return; } + bus->ops = &hp_pci_conf; // replace pci ops for this bus + + memset(dev, 0, sizeof(*dev)); dev->bus = bus; + dev->sysdata = fake; + dev->dev.parent = bus->dev; + dev->dev.bus = &pci_bus_type; dev->devfn = PCI_DEVFN(slot, 0); - dev->csr_base = addr; - dev->csr_size = size; + pci_read_config_word(dev, PCI_VENDOR_ID, &dev->vendor); + pci_read_config_word(dev, PCI_DEVICE_ID, &dev->device); + pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr); + dev->hdr_type = hdr & 0x7f; + + pci_setup_device(dev); + + // pci_insert_device() without running /sbin/hotplug + list_add_tail(&dev->bus_list, &bus->devices); + list_add_tail(&dev->global_list, &pci_devices); + + strcpy(dev->dev.name, dev->name); + strcpy(dev->dev.bus_id, dev->slot_name); + ret = device_register(&dev->dev); + if (ret < 0) + printk(KERN_INFO PFX "fake device registration failed (%d)\n", ret); - /* - * Drivers should ioremap what they need, but we have to do - * it here, too, so PCI config accesses work. - */ - dev->mapped_csrs = ioremap(dev->csr_base, dev->csr_size); + printk(KERN_INFO PFX "%s at 0x%lx; pci dev %s\n", name, addr, dev->slot_name); - return dev; + hpzx1_devices++; } typedef struct { @@ -189,10 +178,10 @@ u8 csr_length[8]; } acpi_hp_vendor_long; -#define HP_CCSR_LENGTH 0x21 -#define HP_CCSR_TYPE 0x2 -#define HP_CCSR_GUID EFI_GUID(0x69e9adf9, 0x924f, 0xab5f, \ - 0xf6, 0x4a, 0x24, 0xd2, 0x01, 0x37, 0x0e, 0xad) +#define HP_CCSR_LENGTH 0x21 +#define HP_CCSR_TYPE 0x2 +#define HP_CCSR_GUID EFI_GUID(0x69e9adf9, 0x924f, 0xab5f, \ + 0xf6, 0x4a, 0x24, 0xd2, 0x01, 0x37, 0x0e, 0xad) extern acpi_status acpi_get_crs(acpi_handle, acpi_buffer *); extern acpi_resource *acpi_get_crs_next(acpi_buffer *, int *); @@ -213,7 +202,7 @@ *csr_length = 0; status = acpi_get_crs(obj, &buf); - if (status != AE_OK) { + if (ACPI_FAILURE(status)) { printk(KERN_ERR PFX "Unable to get _CRS data on object\n"); return status; } @@ -254,13 +243,12 @@ hpzx1_sba_probe(acpi_handle obj, u32 depth, void *context, void **ret) { u64 csr_base = 0, csr_length = 0; - char *name = context; - struct fake_pci_dev *dev; acpi_status status; + char *name = context; + char fullname[16]; status = hp_csr_space(obj, &csr_base, &csr_length); - - if (status != AE_OK) + if (ACPI_FAILURE(status)) return status; /* @@ -268,14 +256,10 @@ * includes both SBA and IOC. Make SBA and IOC show up * separately in PCI space. */ - if ((dev = hpzx1_fake_pci_dev(csr_base, 0, 0x1000))) - printk(KERN_INFO PFX "%s SBA at 0x%lx; pci dev %02x:%02x.%d\n", - name, csr_base, dev->bus, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - if ((dev = hpzx1_fake_pci_dev(csr_base + 0x1000, 0, 0x1000))) - printk(KERN_INFO PFX "%s IOC at 0x%lx; pci dev %02x:%02x.%d\n", - name, csr_base + 0x1000, dev->bus, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + sprintf(fullname, "%s SBA", name); + hpzx1_fake_pci_dev(fullname, 0, csr_base, 0x1000); + sprintf(fullname, "%s IOC", name); + hpzx1_fake_pci_dev(fullname, 0, csr_base + 0x1000, 0x1000); return AE_OK; } @@ -283,28 +267,24 @@ static acpi_status hpzx1_lba_probe(acpi_handle obj, u32 depth, void *context, void **ret) { - acpi_status status; u64 csr_base = 0, csr_length = 0; + acpi_status status; + NATIVE_UINT busnum; char *name = context; - NATIVE_UINT busnum = 0; - struct fake_pci_dev *dev; + char fullname[32]; status = hp_csr_space(obj, &csr_base, &csr_length); - - if (status != AE_OK) + if (ACPI_FAILURE(status)) return status; status = acpi_evaluate_integer(obj, METHOD_NAME__BBN, NULL, &busnum); if (ACPI_FAILURE(status)) { - printk(KERN_ERR PFX "evaluate _BBN fail=0x%x\n", status); + printk(KERN_WARNING PFX "evaluate _BBN fail=0x%x\n", status); busnum = 0; // no _BBN; stick it on bus 0 } - if ((dev = hpzx1_fake_pci_dev(csr_base, busnum, csr_length))) - printk(KERN_INFO PFX "%s LBA at 0x%lx, _BBN 0x%02x; " - "pci dev %02x:%02x.%d\n", - name, csr_base, busnum, dev->bus, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); + sprintf(fullname, "%s _BBN 0x%02x", name, (unsigned int) busnum); + hpzx1_fake_pci_dev(fullname, busnum, csr_base, csr_length); return AE_OK; } @@ -314,6 +294,8 @@ { extern struct pci_ops *pci_root_ops; + orig_pci_ops = pci_root_ops; + /* * Make fake PCI devices for the following hardware in the * ACPI namespace. This makes it more convenient for drivers @@ -328,10 +310,10 @@ */ acpi_get_devices("HWP0001", hpzx1_sba_probe, "HWP0001", NULL); #ifdef CONFIG_IA64_HP_PROTO - if (fake_pci_tail != &fake_pci_head) { + if (hpzx1_devices) { #endif - acpi_get_devices("HWP0002", hpzx1_lba_probe, "HWP0002", NULL); - acpi_get_devices("HWP0003", hpzx1_lba_probe, "HWP0003", NULL); + acpi_get_devices("HWP0002", hpzx1_lba_probe, "HWP0002 PCI LBA", NULL); + acpi_get_devices("HWP0003", hpzx1_lba_probe, "HWP0003 AGP LBA", NULL); #ifdef CONFIG_IA64_HP_PROTO } @@ -342,48 +324,25 @@ * if we didn't find anything, add the things we know are * there. */ - if (fake_pci_tail == &fake_pci_head) { + if (hpzx1_devices == 0) { u64 hpa, csr_base; - struct fake_pci_dev *dev; csr_base = 0xfed00000UL; - hpa = (u64) ioremap(csr_base, 0x1000); + hpa = (u64) ioremap(csr_base, 0x2000); if (__raw_readl(hpa) == ZX1_FUNC_ID_VALUE) { - if ((dev = hpzx1_fake_pci_dev(csr_base, 0, 0x1000))) - printk(KERN_INFO PFX "HWP0001 SBA at 0x%lx; " - "pci dev %02x:%02x.%d\n", csr_base, - dev->bus, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn)); - if ((dev = hpzx1_fake_pci_dev(csr_base + 0x1000, 0, - 0x1000))) - printk(KERN_INFO PFX "HWP0001 IOC at 0x%lx; " - "pci dev %02x:%02x.%d\n", - csr_base + 0x1000, - dev->bus, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn)); + hpzx1_fake_pci_dev("HWP0001 SBA", 0, csr_base, 0x1000); + hpzx1_fake_pci_dev("HWP0001 IOC", 0, csr_base + 0x1000, + 0x1000); csr_base = 0xfed24000UL; iounmap(hpa); hpa = (u64) ioremap(csr_base, 0x1000); - if ((dev = hpzx1_fake_pci_dev(csr_base, 0x40, 0x1000))) - printk(KERN_INFO PFX "HWP0003 AGP LBA at " - "0x%lx; pci dev %02x:%02x.%d\n", - csr_base, - dev->bus, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn)); + hpzx1_fake_pci_dev("HWP0003 AGP LBA", 0x40, csr_base, + 0x1000); } iounmap(hpa); } #endif - - if (fake_pci_tail == &fake_pci_head) - return; - - /* - * Replace PCI ops, but only if we made fake devices. - */ - orig_pci_ops = pci_root_ops; - pci_root_ops = &hp_pci_conf; } extern void sba_init(void); @@ -391,9 +350,16 @@ void hpzx1_pci_fixup (int phase) { - if (phase == 0) - hpzx1_acpi_dev_init(); iosapic_pci_fixup(phase); - if (phase == 1) + switch (phase) { + case 0: + /* zx1 has a hardware I/O TLB which lets us DMA from any device to any address */ + MAX_DMA_ADDRESS = ~0UL; + break; + + case 1: + hpzx1_acpi_dev_init(); sba_init(); + break; + } } diff -Nru a/arch/ia64/ia32/binfmt_elf32.c b/arch/ia64/ia32/binfmt_elf32.c --- a/arch/ia64/ia32/binfmt_elf32.c Fri Aug 16 14:35:00 2002 +++ b/arch/ia64/ia32/binfmt_elf32.c Fri Aug 16 14:35:00 2002 @@ -67,7 +67,7 @@ } static struct vm_operations_struct ia32_shared_page_vm_ops = { - nopage: ia32_install_shared_page + .nopage =ia32_install_shared_page }; void diff -Nru a/arch/ia64/ia32/ia32_ioctl.c b/arch/ia64/ia32/ia32_ioctl.c --- a/arch/ia64/ia32/ia32_ioctl.c Fri Aug 16 14:34:59 2002 +++ b/arch/ia64/ia32/ia32_ioctl.c Fri Aug 16 14:34:59 2002 @@ -30,6 +30,8 @@ #include #include <../drivers/char/drm/drm.h> +#include <../drivers/char/drm/mga_drm.h> +#include <../drivers/char/drm/i810_drm.h> #define IOCTL_NR(a) ((a) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) diff -Nru a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c --- a/arch/ia64/kernel/acpi.c Fri Aug 16 14:34:57 2002 +++ b/arch/ia64/kernel/acpi.c Fri Aug 16 14:34:57 2002 @@ -56,16 +56,19 @@ void (*pm_idle) (void); void (*pm_power_off) (void); +unsigned char acpi_kbd_controller_present = 1; + const char * acpi_get_sysname (void) { #ifdef CONFIG_IA64_GENERIC - unsigned long rsdp_phys = 0; + unsigned long rsdp_phys; struct acpi20_table_rsdp *rsdp; struct acpi_table_xsdt *xsdt; struct acpi_table_header *hdr; - if ((0 != acpi_find_rsdp(&rsdp_phys)) || !rsdp_phys) { + rsdp_phys = acpi_find_rsdp(); + if (!rsdp_phys) { printk("ACPI 2.0 RSDP not found, default to \"dig\"\n"); return "dig"; } @@ -99,6 +102,8 @@ return "sn2"; # elif defined (CONFIG_IA64_DIG) return "dig"; +# elif defined (CONFIG_IA64_HP_ZX1) + return "hpzx1"; # else # error Unknown platform. Fix acpi.c. # endif @@ -130,9 +135,7 @@ if (!buf->pointer) return -ENOMEM; - result = acpi_get_current_resources(obj, buf); - - return result; + return acpi_get_current_resources(obj, buf); } acpi_resource * @@ -175,6 +178,8 @@ /* Array to record platform interrupt vectors for generic interrupt routing. */ int platform_irq_list[ACPI_MAX_PLATFORM_IRQS] = { [0 ... ACPI_MAX_PLATFORM_IRQS - 1] = -1 }; +enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_IOSAPIC; + /* * Interrupt routing API for device drivers. Provides interrupt vector for * a generic platform event. Currently only CPEI is implemented. @@ -189,10 +194,14 @@ vector = platform_irq_list[int_type]; } else printk("acpi_request_vector(): invalid interrupt type\n"); - return vector; } +char * +__acpi_map_table (unsigned long phys_addr, unsigned long size) +{ + return __va(phys_addr); +} /* -------------------------------------------------------------------------- Boot-time Table Parsing @@ -206,7 +215,7 @@ static int __init acpi_parse_lapic_addr_ovr (acpi_table_entry_header *header) { - struct acpi_table_lapic_addr_ovr *lapic = NULL; + struct acpi_table_lapic_addr_ovr *lapic; lapic = (struct acpi_table_lapic_addr_ovr *) header; if (!lapic) @@ -218,7 +227,6 @@ iounmap((void *) ipi_base_addr); ipi_base_addr = (unsigned long) ioremap(lapic->address, 0); } - return 0; } @@ -226,7 +234,7 @@ static int __init acpi_parse_lsapic (acpi_table_entry_header *header) { - struct acpi_table_lsapic *lsapic = NULL; + struct acpi_table_lsapic *lsapic; lsapic = (struct acpi_table_lsapic *) header; if (!lsapic) @@ -262,7 +270,7 @@ static int __init acpi_parse_lapic_nmi (acpi_table_entry_header *header) { - struct acpi_table_lapic_nmi *lacpi_nmi = NULL; + struct acpi_table_lapic_nmi *lacpi_nmi; lacpi_nmi = (struct acpi_table_lapic_nmi*) header; if (!lacpi_nmi) @@ -271,7 +279,6 @@ acpi_table_print_madt_entry(header); /* TBD: Support lapic_nmi entries */ - return 0; } @@ -279,11 +286,11 @@ static int __init acpi_find_iosapic (int global_vector, u32 *irq_base, char **iosapic_address) { - struct acpi_table_iosapic *iosapic = NULL; - int ver = 0; - int max_pin = 0; - char *p = 0; - char *end = 0; + struct acpi_table_iosapic *iosapic; + int ver; + int max_pin; + char *p; + char *end; if (!irq_base || !iosapic_address) return -ENODEV; @@ -338,10 +345,10 @@ static int __init acpi_parse_plat_int_src (acpi_table_entry_header *header) { - struct acpi_table_plat_int_src *plintsrc = NULL; - int vector = 0; - u32 irq_base = 0; - char *iosapic_address = NULL; + struct acpi_table_plat_int_src *plintsrc; + int vector; + u32 irq_base; + char *iosapic_address; plintsrc = (struct acpi_table_plat_int_src *) header; if (!plintsrc) @@ -354,7 +361,7 @@ return -ENODEV; } - if (0 != acpi_find_iosapic(plintsrc->global_irq, &irq_base, &iosapic_address)) { + if (acpi_find_iosapic(plintsrc->global_irq, &irq_base, &iosapic_address)) { printk(KERN_WARNING PREFIX "IOSAPIC not found\n"); return -ENODEV; } @@ -363,15 +370,15 @@ * Get vector assignment for this IRQ, set attributes, and program the * IOSAPIC routing table. */ - vector = iosapic_register_platform_irq (plintsrc->type, - plintsrc->global_irq, - plintsrc->iosapic_vector, - plintsrc->eid, - plintsrc->id, - (plintsrc->flags.polarity == 1) ? 1 : 0, - (plintsrc->flags.trigger == 1) ? 1 : 0, - irq_base, - iosapic_address); + vector = iosapic_register_platform_irq(plintsrc->type, + plintsrc->global_irq, + plintsrc->iosapic_vector, + plintsrc->eid, + plintsrc->id, + (plintsrc->flags.polarity == 1) ? 1 : 0, + (plintsrc->flags.trigger == 1) ? 1 : 0, + irq_base, + iosapic_address); platform_irq_list[plintsrc->type] = vector; return 0; @@ -381,7 +388,7 @@ static int __init acpi_parse_int_src_ovr (acpi_table_entry_header *header) { - struct acpi_table_int_src_ovr *p = NULL; + struct acpi_table_int_src_ovr *p; p = (struct acpi_table_int_src_ovr *) header; if (!p) @@ -394,9 +401,8 @@ return 0; iosapic_register_legacy_irq(p->bus_irq, p->global_irq, - (p->flags.polarity == 1) ? 1 : 0, - (p->flags.trigger == 1) ? 1 : 0); - + (p->flags.polarity == 1) ? 1 : 0, + (p->flags.trigger == 1) ? 1 : 0); return 0; } @@ -404,7 +410,7 @@ static int __init acpi_parse_nmi_src (acpi_table_entry_header *header) { - struct acpi_table_nmi_src *nmi_src = NULL; + struct acpi_table_nmi_src *nmi_src; nmi_src = (struct acpi_table_nmi_src*) header; if (!nmi_src) @@ -413,7 +419,6 @@ acpi_table_print_madt_entry(header); /* TBD: Support nimsrc entries */ - return 0; } @@ -425,50 +430,59 @@ return -EINVAL; acpi_madt = (struct acpi_table_madt *) __va(phys_addr); - if (!acpi_madt) { - printk(KERN_WARNING PREFIX "Unable to map MADT\n"); - return -ENODEV; - } /* Get base address of IPI Message Block */ if (acpi_madt->lapic_address) - ipi_base_addr = (unsigned long) - ioremap(acpi_madt->lapic_address, 0); + ipi_base_addr = (unsigned long) ioremap(acpi_madt->lapic_address, 0); printk(KERN_INFO PREFIX "Local APIC address 0x%lx\n", ipi_base_addr); - return 0; } - -int __init -acpi_find_rsdp (unsigned long *rsdp_phys) +static int __init +acpi_parse_fadt (unsigned long phys_addr, unsigned long size) { - if (!rsdp_phys) + struct acpi_table_header *fadt_header; + fadt_descriptor_rev2 *fadt; + + if (!phys_addr || !size) return -EINVAL; - if (efi.acpi20) { - (*rsdp_phys) = __pa(efi.acpi20); - return 0; - } - else if (efi.acpi) { - printk(KERN_WARNING PREFIX "v1.0/r0.71 tables no longer supported\n"); - } + fadt_header = (struct acpi_table_header *) __va(phys_addr); + if (fadt_header->revision != 3) + return -ENODEV; /* Only deal with ACPI 2.0 FADT */ - return -ENODEV; + fadt = (fadt_descriptor_rev2 *) fadt_header; + + if (!(fadt->iapc_boot_arch & BAF_8042_KEYBOARD_CONTROLLER)) + acpi_kbd_controller_present = 0; + + return 0; +} + +unsigned long __init +acpi_find_rsdp (void) +{ + unsigned long rsdp_phys = 0; + + if (efi.acpi20) + rsdp_phys = __pa(efi.acpi20); + else if (efi.acpi) + printk(KERN_WARNING PREFIX "v1.0/r0.71 tables no longer supported\n"); + return rsdp_phys; } -#ifdef CONFIG_SERIAL_ACPI +#ifdef CONFIG_SERIAL_8250_ACPI #include static int __init acpi_parse_spcr (unsigned long phys_addr, unsigned long size) { - acpi_ser_t *spcr = NULL; - unsigned long global_int = 0; + acpi_ser_t *spcr; + unsigned long global_int; if (!phys_addr || !size) return -EINVAL; @@ -486,11 +500,6 @@ */ spcr = (acpi_ser_t *) __va(phys_addr); - if (!spcr) { - printk(KERN_WARNING PREFIX "Unable to map SPCR\n"); - return -ENODEV; - } - setup_serial_acpi(spcr); if (spcr->length < sizeof(acpi_ser_t)) @@ -500,38 +509,37 @@ if ((spcr->base_addr.space_id != ACPI_SERIAL_PCICONF_SPACE) && (spcr->int_type == ACPI_SERIAL_INT_SAPIC)) { - u32 irq_base = 0; - char *iosapic_address = NULL; - int vector = 0; + u32 irq_base; + char *iosapic_address; + int vector; /* We have a UART in memory space with an SAPIC interrupt */ - global_int = ( (spcr->global_int[3] << 24) | - (spcr->global_int[2] << 16) | - (spcr->global_int[1] << 8) | - (spcr->global_int[0]) ); + global_int = ((spcr->global_int[3] << 24) | + (spcr->global_int[2] << 16) | + (spcr->global_int[1] << 8) | + (spcr->global_int[0]) ); /* Which iosapic does this IRQ belong to? */ - if (0 == acpi_find_iosapic(global_int, &irq_base, &iosapic_address)) { - vector = iosapic_register_irq (global_int, 1, 1, - irq_base, iosapic_address); - } + if (!acpi_find_iosapic(global_int, &irq_base, &iosapic_address)) + vector = iosapic_register_irq(global_int, 1, 1, + irq_base, iosapic_address); } return 0; } -#endif /*CONFIG_SERIAL_ACPI*/ +#endif /* CONFIG_SERIAL_8250_ACPI */ int __init acpi_boot_init (char *cmdline) { - int result = 0; + int result; /* Initialize the ACPI boot-time table parser */ result = acpi_table_init(cmdline); - if (0 != result) + if (result) return result; /* @@ -542,59 +550,44 @@ * information -- the successor to MPS tables. */ - result = acpi_table_parse(ACPI_APIC, acpi_parse_madt); - if (1 > result) - return result; + if (acpi_table_parse(ACPI_APIC, acpi_parse_madt) < 1) { + printk(KERN_ERR PREFIX "Can't find MADT\n"); + goto skip_madt; + } /* Local APIC */ - result = acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr); - if (0 > result) { + if (acpi_table_parse_madt(ACPI_MADT_LAPIC_ADDR_OVR, acpi_parse_lapic_addr_ovr) < 0) printk(KERN_ERR PREFIX "Error parsing LAPIC address override entry\n"); - return result; - } - result = acpi_table_parse_madt(ACPI_MADT_LSAPIC, acpi_parse_lsapic); - if (1 > result) { - printk(KERN_ERR PREFIX "Error parsing MADT - no LAPIC entries!\n"); - return -ENODEV; - } + if (acpi_table_parse_madt(ACPI_MADT_LSAPIC, acpi_parse_lsapic) < 1) + printk(KERN_ERR PREFIX "Error parsing MADT - no LAPIC entries\n"); - result = acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi); - if (0 > result) { + if (acpi_table_parse_madt(ACPI_MADT_LAPIC_NMI, acpi_parse_lapic_nmi) < 0) printk(KERN_ERR PREFIX "Error parsing LAPIC NMI entry\n"); - return result; - } /* I/O APIC */ - result = acpi_table_parse_madt(ACPI_MADT_IOSAPIC, acpi_parse_iosapic); - if (1 > result) { - printk(KERN_ERR PREFIX "Error parsing MADT - no IOAPIC entries!\n"); - return ((result == 0) ? -ENODEV : result); - } + if (acpi_table_parse_madt(ACPI_MADT_IOSAPIC, acpi_parse_iosapic) < 1) + printk(KERN_ERR PREFIX "Error parsing MADT - no IOSAPIC entries\n"); /* System-Level Interrupt Routing */ - result = acpi_table_parse_madt(ACPI_MADT_PLAT_INT_SRC, acpi_parse_plat_int_src); - if (0 > result) { + if (acpi_table_parse_madt(ACPI_MADT_PLAT_INT_SRC, acpi_parse_plat_int_src) < 0) printk(KERN_ERR PREFIX "Error parsing platform interrupt source entry\n"); - return result; - } - result = acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr); - if (0 > result) { + if (acpi_table_parse_madt(ACPI_MADT_INT_SRC_OVR, acpi_parse_int_src_ovr) < 0) printk(KERN_ERR PREFIX "Error parsing interrupt source overrides entry\n"); - return result; - } - result = acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src); - if (0 > result) { + if (acpi_table_parse_madt(ACPI_MADT_NMI_SRC, acpi_parse_nmi_src) < 0) printk(KERN_ERR PREFIX "Error parsing NMI SRC entry\n"); - return result; - } + skip_madt: -#ifdef CONFIG_SERIAL_ACPI + /* FADT says whether a legacy keyboard controller is present. */ + if (acpi_table_parse(ACPI_FACP, acpi_parse_fadt) < 1) + printk(KERN_ERR PREFIX "Can't find FADT\n"); + +#ifdef CONFIG_SERIAL_8250_ACPI /* * TBD: Need phased approach to table parsing (only do those absolutely * required during boot-up). Recommend expanding concept of fix- @@ -602,7 +595,7 @@ * serial ports, EC, SMBus, etc. */ acpi_table_parse(ACPI_SPCR, acpi_parse_spcr); -#endif /*CONFIG_SERIAL_ACPI*/ +#endif #ifdef CONFIG_SMP if (available_cpus == 0) { @@ -610,10 +603,11 @@ available_cpus = 1; /* We've got at least one of these, no? */ } smp_boot_data.cpu_count = total_cpus; + + smp_build_cpu_map(); #endif /* Make boot-up look pretty */ printk("%d CPUs available, %d CPUs total\n", available_cpus, total_cpus); - return 0; } @@ -625,9 +619,9 @@ int __init acpi_get_prt (struct pci_vector_struct **vectors, int *count) { - struct pci_vector_struct *vector = NULL; - struct list_head *node = NULL; - struct acpi_prt_entry *entry = NULL; + struct pci_vector_struct *vector; + struct list_head *node; + struct acpi_prt_entry *entry; int i = 0; if (!vectors || !count) @@ -636,14 +630,14 @@ *vectors = NULL; *count = 0; - if (acpi_prts.count <= 0) { + if (acpi_prt.count < 0) { printk(KERN_ERR PREFIX "No PCI IRQ routing entries\n"); return -ENODEV; } /* Allocate vectors */ - *vectors = kmalloc(sizeof(struct pci_vector_struct) * acpi_prts.count, GFP_KERNEL); + *vectors = kmalloc(sizeof(struct pci_vector_struct) * acpi_prt.count, GFP_KERNEL); if (!(*vectors)) return -ENOMEM; @@ -651,15 +645,15 @@ vector = *vectors; - list_for_each(node, &acpi_prts.entries) { + list_for_each(node, &acpi_prt.entries) { entry = (struct acpi_prt_entry *)node; vector[i].bus = entry->id.bus; - vector[i].pci_id = ((u32) entry->id.dev << 16) | 0xffff; - vector[i].pin = entry->id.pin; - vector[i].irq = entry->source.index; + vector[i].pci_id = ((u32) entry->id.device << 16) | 0xffff; + vector[i].pin = entry->pin; + vector[i].irq = entry->link.index; i++; } - *count = acpi_prts.count; + *count = acpi_prt.count; return 0; } @@ -671,8 +665,7 @@ if (!type) return -EINVAL; - *type = ACPI_INT_MODEL_IOSAPIC; - + *type = ACPI_IRQ_MODEL_IOSAPIC; return 0; } diff -Nru a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c --- a/arch/ia64/kernel/efi.c Fri Aug 16 14:34:52 2002 +++ b/arch/ia64/kernel/efi.c Fri Aug 16 14:34:52 2002 @@ -125,9 +125,79 @@ tv->tv_usec = tm.nanosecond / 1000; } +static int +is_available_memory (efi_memory_desc_t *md) +{ + if (!(md->attribute & EFI_MEMORY_WB)) + return 0; + + switch (md->type) { + case EFI_LOADER_CODE: + case EFI_LOADER_DATA: + case EFI_BOOT_SERVICES_CODE: + case EFI_BOOT_SERVICES_DATA: + case EFI_CONVENTIONAL_MEMORY: + return 1; + } + return 0; +} + /* - * Walks the EFI memory map and calls CALLBACK once for each EFI - * memory descriptor that has memory that is available for OS use. + * Trim descriptor MD so its starts at address START_ADDR. If the descriptor covers + * memory that is normally available to the kernel, issue a warning that some memory + * is being ignored. + */ +static void +trim_bottom (efi_memory_desc_t *md, u64 start_addr) +{ + u64 num_skipped_pages; + + if (md->phys_addr >= start_addr || !md->num_pages) + return; + + num_skipped_pages = (start_addr - md->phys_addr) >> EFI_PAGE_SHIFT; + if (num_skipped_pages > md->num_pages) + num_skipped_pages = md->num_pages; + + if (is_available_memory(md)) + printk(KERN_NOTICE "efi.%s: ignoring %luKB of memory at 0x%lx due to granule hole " + "at 0x%lx\n", __FUNCTION__, + (num_skipped_pages << EFI_PAGE_SHIFT) >> 10, + md->phys_addr, start_addr - IA64_GRANULE_SIZE); + /* + * NOTE: Don't set md->phys_addr to START_ADDR because that could cause the memory + * descriptor list to become unsorted. In such a case, md->num_pages will be + * zero, so the Right Thing will happen. + */ + md->phys_addr += num_skipped_pages << EFI_PAGE_SHIFT; + md->num_pages -= num_skipped_pages; +} + +static void +trim_top (efi_memory_desc_t *md, u64 end_addr) +{ + u64 num_dropped_pages, md_end_addr; + + md_end_addr = md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT); + + if (md_end_addr <= end_addr || !md->num_pages) + return; + + num_dropped_pages = (md_end_addr - end_addr) >> EFI_PAGE_SHIFT; + if (num_dropped_pages > md->num_pages) + num_dropped_pages = md->num_pages; + + if (is_available_memory(md)) + printk(KERN_NOTICE "efi.%s: ignoring %luKB of memory at 0x%lx due to granule hole " + "at 0x%lx\n", __FUNCTION__, + (num_dropped_pages << EFI_PAGE_SHIFT) >> 10, + md->phys_addr, end_addr); + md->num_pages -= num_dropped_pages; +} + +/* + * Walks the EFI memory map and calls CALLBACK once for each EFI memory descriptor that + * has memory that is available for OS use. */ void efi_memmap_walk (efi_freemem_callback_t callback, void *arg) @@ -137,9 +207,9 @@ u64 start; u64 end; } prev, curr; - void *efi_map_start, *efi_map_end, *p; - efi_memory_desc_t *md; - u64 efi_desc_size, start, end; + void *efi_map_start, *efi_map_end, *p, *q; + efi_memory_desc_t *md, *check_md; + u64 efi_desc_size, start, end, granule_addr, first_non_wb_addr = 0; efi_map_start = __va(ia64_boot_param->efi_memmap); efi_map_end = efi_map_start + ia64_boot_param->efi_memmap_size; @@ -147,24 +217,56 @@ for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { md = p; - switch (md->type) { - case EFI_LOADER_CODE: - case EFI_LOADER_DATA: - case EFI_BOOT_SERVICES_CODE: - case EFI_BOOT_SERVICES_DATA: - case EFI_CONVENTIONAL_MEMORY: - if (!(md->attribute & EFI_MEMORY_WB)) - continue; + + /* skip over non-WB memory descriptors; that's all we're interested in... */ + if (!(md->attribute & EFI_MEMORY_WB)) + continue; + + if (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) > first_non_wb_addr) { + /* + * Search for the next run of contiguous WB memory. Start search + * at first granule boundary covered by md. + */ + granule_addr = ((md->phys_addr + IA64_GRANULE_SIZE - 1) + & -IA64_GRANULE_SIZE); + first_non_wb_addr = granule_addr; + for (q = p; q < efi_map_end; q += efi_desc_size) { + check_md = q; + + if (check_md->attribute & EFI_MEMORY_WB) + trim_bottom(md, granule_addr); + + if (check_md->phys_addr < granule_addr) + continue; + + if (!(check_md->attribute & EFI_MEMORY_WB)) + break; /* hit a non-WB region; stop search */ + + if (check_md->phys_addr != first_non_wb_addr) + break; /* hit a memory hole; stop search */ + + first_non_wb_addr += check_md->num_pages << EFI_PAGE_SHIFT; + } + /* round it down to the previous granule-boundary: */ + first_non_wb_addr &= -IA64_GRANULE_SIZE; + + if (!(first_non_wb_addr > granule_addr)) + continue; /* couldn't find enough contiguous memory */ + } + + /* BUG_ON((md->phys_addr >> IA64_GRANULE_SHIFT) < first_non_wb_addr); */ + + trim_top(md, first_non_wb_addr); + + if (is_available_memory(md)) { if (md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) > mem_limit) { if (md->phys_addr > mem_limit) continue; md->num_pages = (mem_limit - md->phys_addr) >> EFI_PAGE_SHIFT; } - if (md->num_pages == 0) { - printk("efi_memmap_walk: ignoring empty region at 0x%lx", - md->phys_addr); + + if (md->num_pages == 0) continue; - } curr.start = PAGE_OFFSET + md->phys_addr; curr.end = curr.start + (md->num_pages << EFI_PAGE_SHIFT); @@ -187,10 +289,6 @@ prev = curr; } } - break; - - default: - continue; } } if (prev_valid) { @@ -268,8 +366,9 @@ */ psr = ia64_clear_ic(); ia64_itr(0x1, IA64_TR_PALCODE, vaddr & mask, - pte_val(pfn_pte(md->phys_addr >> PAGE_SHIFT, PAGE_KERNEL)), IA64_GRANULE_SHIFT); - ia64_set_psr(psr); + pte_val(pfn_pte(md->phys_addr >> PAGE_SHIFT, PAGE_KERNEL)), + IA64_GRANULE_SHIFT); + ia64_set_psr(psr); /* restore psr */ ia64_srlz_i(); } } @@ -347,6 +446,9 @@ } else if (efi_guidcmp(config_tables[i].guid, SAL_SYSTEM_TABLE_GUID) == 0) { efi.sal_systab = __va(config_tables[i].table); printk(" SALsystab=0x%lx", config_tables[i].table); + } else if (efi_guidcmp(config_tables[i].guid, HCDP_TABLE_GUID) == 0) { + efi.hcdp = __va(config_tables[i].table); + printk(" HCDP=0x%lx", config_tables[i].table); } } printk("\n"); @@ -376,7 +478,7 @@ md = p; printk("mem%02u: type=%u, attr=0x%lx, range=[0x%016lx-0x%016lx) (%luMB)\n", i, md->type, md->attribute, md->phys_addr, - md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT) - 1, + md->phys_addr + (md->num_pages << EFI_PAGE_SHIFT), md->num_pages >> (20 - EFI_PAGE_SHIFT)); } } diff -Nru a/arch/ia64/kernel/entry.S b/arch/ia64/kernel/entry.S --- a/arch/ia64/kernel/entry.S Fri Aug 16 14:34:55 2002 +++ b/arch/ia64/kernel/entry.S Fri Aug 16 14:34:55 2002 @@ -175,6 +175,7 @@ (p6) srlz.d ld8 sp=[r21] // load kernel stack pointer of new task mov IA64_KR(CURRENT)=r20 // update "current" application register + mov r8=r13 // return pointer to previously running task mov r13=in0 // set "current" pointer ;; DO_LOAD_SWITCH_STACK diff -Nru a/arch/ia64/kernel/ia64_ksyms.c b/arch/ia64/kernel/ia64_ksyms.c --- a/arch/ia64/kernel/ia64_ksyms.c Fri Aug 16 14:34:57 2002 +++ b/arch/ia64/kernel/ia64_ksyms.c Fri Aug 16 14:34:57 2002 @@ -88,12 +88,6 @@ #include EXPORT_SYMBOL(kernel_flag); -/* #include */ -EXPORT_SYMBOL(__global_sti); -EXPORT_SYMBOL(__global_cli); -EXPORT_SYMBOL(__global_save_flags); -EXPORT_SYMBOL(__global_restore_flags); - #else /* !CONFIG_SMP */ EXPORT_SYMBOL(__flush_tlb_all); diff -Nru a/arch/ia64/kernel/init_task.c b/arch/ia64/kernel/init_task.c --- a/arch/ia64/kernel/init_task.c Fri Aug 16 14:35:01 2002 +++ b/arch/ia64/kernel/init_task.c Fri Aug 16 14:35:01 2002 @@ -34,8 +34,8 @@ } s; unsigned long stack[KERNEL_STACK_SIZE/sizeof (unsigned long)]; } init_thread_union __attribute__((section(".data.init_task"))) = {{ - task: INIT_TASK(init_thread_union.s.task), - thread_info: INIT_THREAD_INFO(init_thread_union.s.thread_info) + .task = INIT_TASK(init_thread_union.s.task), + .thread_info = INIT_THREAD_INFO(init_thread_union.s.thread_info) }}; asm (".global init_task; init_task = init_thread_union"); diff -Nru a/arch/ia64/kernel/iosapic.c b/arch/ia64/kernel/iosapic.c --- a/arch/ia64/kernel/iosapic.c Fri Aug 16 14:34:57 2002 +++ b/arch/ia64/kernel/iosapic.c Fri Aug 16 14:34:57 2002 @@ -88,7 +88,7 @@ static struct iosapic_irq { char *addr; /* base address of IOSAPIC */ - unsigned char base_irq; /* first irq assigned to this IOSAPIC */ + unsigned int base_irq; /* first irq assigned to this IOSAPIC */ char pin; /* IOSAPIC pin (-1 => not an IOSAPIC irq) */ unsigned char dmode : 3; /* delivery mode (see iosapic.h) */ unsigned char polarity : 1; /* interrupt polarity (see iosapic.h) */ @@ -97,9 +97,9 @@ static struct iosapic { char *addr; /* base address of IOSAPIC */ - unsigned char pcat_compat; /* 8259 compatibility flag */ - unsigned char base_irq; /* first irq assigned to this IOSAPIC */ + unsigned int base_irq; /* first irq assigned to this IOSAPIC */ unsigned short max_pin; /* max input pin supported in this IOSAPIC */ + unsigned char pcat_compat; /* 8259 compatibility flag */ } iosapic_lists[256] __initdata; static int num_iosapic = 0; @@ -160,6 +160,10 @@ int pin; char redir; +#ifdef DEBUG_IRQ_ROUTING + printk(KERN_DEBUG "set_rte: routing vector 0x%02x to 0x%lx\n", vector, dest); +#endif + pin = iosapic_irq[vector].pin; if (pin < 0) return; /* not an IOSAPIC interrupt */ @@ -322,14 +326,14 @@ #define iosapic_ack_level_irq nop struct hw_interrupt_type irq_type_iosapic_level = { - typename: "IO-SAPIC-level", - startup: iosapic_startup_level_irq, - shutdown: iosapic_shutdown_level_irq, - enable: iosapic_enable_level_irq, - disable: iosapic_disable_level_irq, - ack: iosapic_ack_level_irq, - end: iosapic_end_level_irq, - set_affinity: iosapic_set_affinity + .typename = "IO-SAPIC-level", + .startup = iosapic_startup_level_irq, + .shutdown = iosapic_shutdown_level_irq, + .enable = iosapic_enable_level_irq, + .disable = iosapic_disable_level_irq, + .ack = iosapic_ack_level_irq, + .end = iosapic_end_level_irq, + .set_affinity = iosapic_set_affinity }; /* @@ -366,14 +370,14 @@ #define iosapic_end_edge_irq nop struct hw_interrupt_type irq_type_iosapic_edge = { - typename: "IO-SAPIC-edge", - startup: iosapic_startup_edge_irq, - shutdown: iosapic_disable_edge_irq, - enable: iosapic_enable_edge_irq, - disable: iosapic_disable_edge_irq, - ack: iosapic_ack_edge_irq, - end: iosapic_end_edge_irq, - set_affinity: iosapic_set_affinity + .typename = "IO-SAPIC-edge", + .startup = iosapic_startup_edge_irq, + .shutdown = iosapic_disable_edge_irq, + .enable = iosapic_enable_edge_irq, + .disable = iosapic_disable_edge_irq, + .ack = iosapic_ack_edge_irq, + .end = iosapic_end_edge_irq, + .set_affinity = iosapic_set_affinity }; unsigned int @@ -406,7 +410,7 @@ || iosapic_irq[vector].polarity || iosapic_irq[vector].trigger) { new_vector = ia64_alloc_irq(); - printk("Reassigning Vector 0x%x to 0x%x\n", vector, new_vector); + printk("Reassigning vector 0x%x to 0x%x\n", vector, new_vector); memcpy (&iosapic_irq[new_vector], &iosapic_irq[vector], sizeof(struct iosapic_irq)); memset (&iosapic_irq[vector], 0, sizeof(struct iosapic_irq)); @@ -422,6 +426,7 @@ irq_desc_t *idesc; struct hw_interrupt_type *irq_type; + gsi_to_vector(global_vector) = vector; iosapic_irq[vector].pin = pin; iosapic_irq[vector].polarity = polarity ? IOSAPIC_POL_HIGH : IOSAPIC_POL_LOW; iosapic_irq[vector].dmode = delivery; @@ -640,7 +645,7 @@ unsigned int irq; char *addr; - if (0 != acpi_get_prt(&pci_irq.route, &pci_irq.num_routes)) + if (acpi_get_prt(&pci_irq.route, &pci_irq.num_routes)) return; for (i = 0; i < pci_irq.num_routes; i++) { @@ -679,11 +684,10 @@ pci_irq.route[i].bus, pci_irq.route[i].pci_id>>16, pci_irq.route[i].pin, iosapic_irq[vector].base_irq + iosapic_irq[vector].pin, vector); #endif - /* - * Forget not to program the IOSAPIC RTE per ACPI _PRT + * NOTE: The IOSAPIC RTE will be programmed in iosapic_pci_fixup(). It + * needs to be done there to ensure PCI hotplug works right. */ - set_rte(vector, (ia64_get_lid() >> 16) & 0xffff); } } @@ -757,10 +761,11 @@ if (!(smp_int_redirect & SMP_IRQ_REDIRECTION)) { static int cpu_index = 0; - set_rte(vector, cpu_physical_id(cpu_index) & 0xffff); + while (!cpu_online(cpu_index)) + if (++cpu_index >= NR_CPUS) + cpu_index = 0; - for (cpu_index++; !cpu_online(cpu_index % NR_CPUS); cpu_index++); - cpu_index %= NR_CPUS; + set_rte(vector, cpu_physical_id(cpu_index) & 0xffff); } else { /* * Direct the interrupt vector to the current cpu, diff -Nru a/arch/ia64/kernel/irq.c b/arch/ia64/kernel/irq.c --- a/arch/ia64/kernel/irq.c Fri Aug 16 14:34:56 2002 +++ b/arch/ia64/kernel/irq.c Fri Aug 16 14:34:56 2002 @@ -200,277 +200,12 @@ return 0; } - -/* - * Global interrupt locks for SMP. Allow interrupts to come in on any - * CPU, yet make cli/sti act globally to protect critical regions.. - */ - -#ifdef CONFIG_SMP -unsigned int global_irq_holder = NO_PROC_ID; -unsigned volatile long global_irq_lock; /* pedantic: long for set_bit --RR */ - -extern void show_stack(unsigned long* esp); - -static void show(char * str) +#if CONFIG_SMP +inline void synchronize_irq(unsigned int irq) { - int i; - int cpu = smp_processor_id(); - - printk("\n%s, CPU %d:\n", str, cpu); - printk("irq: %d [",irqs_running()); - for(i=0;i < NR_CPUS;i++) - printk(" %d",irq_count(i)); - printk(" ]\nbh: %d [",spin_is_locked(&global_bh_lock) ? 1 : 0); - for(i=0;i < NR_CPUS;i++) - printk(" %d",bh_count(i)); - - printk(" ]\nStack dumps:"); -#if defined(CONFIG_IA64) - /* - * We can't unwind the stack of another CPU without access to - * the registers of that CPU. And sending an IPI when we're - * in a potentially wedged state doesn't sound like a smart - * idea. - */ -#elif defined(CONFIG_X86) - for(i=0;i< NR_CPUS;i++) { - unsigned long esp; - if(i==cpu) - continue; - printk("\nCPU %d:",i); - esp = init_tss[i].esp0; - if(esp==NULL) { - /* tss->esp0 is set to NULL in cpu_init(), - * it's initialized when the cpu returns to user - * space. -- manfreds - */ - printk(" "); - continue; - } - esp &= ~(THREAD_SIZE-1); - esp += sizeof(struct task_struct); - show_stack((void*)esp); - } -#else - You lose... -#endif - printk("\nCPU %d:",cpu); - show_stack(NULL); - printk("\n"); + while (irq_desc(irq)->status & IRQ_INPROGRESS) + cpu_relax(); } - -#define MAXCOUNT 100000000 - -/* - * I had a lockup scenario where a tight loop doing - * spin_unlock()/spin_lock() on CPU#1 was racing with - * spin_lock() on CPU#0. CPU#0 should have noticed spin_unlock(), but - * apparently the spin_unlock() information did not make it - * through to CPU#0 ... nasty, is this by design, do we have to limit - * 'memory update oscillation frequency' artificially like here? - * - * Such 'high frequency update' races can be avoided by careful design, but - * some of our major constructs like spinlocks use similar techniques, - * it would be nice to clarify this issue. Set this define to 0 if you - * want to check whether your system freezes. I suspect the delay done - * by SYNC_OTHER_CORES() is in correlation with 'snooping latency', but - * i thought that such things are guaranteed by design, since we use - * the 'LOCK' prefix. - */ -#define SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND 0 - -#if SUSPECTED_CPU_OR_CHIPSET_BUG_WORKAROUND -# define SYNC_OTHER_CORES(x) udelay(x+1) -#else -/* - * We have to allow irqs to arrive between local_irq_enable and local_irq_disable - */ -# ifdef CONFIG_IA64 -# define SYNC_OTHER_CORES(x) __asm__ __volatile__ ("nop 0") -# else -# define SYNC_OTHER_CORES(x) __asm__ __volatile__ ("nop") -# endif -#endif - -static inline void wait_on_irq(void) -{ - int count = MAXCOUNT; - - for (;;) { - - /* - * Wait until all interrupts are gone. Wait - * for bottom half handlers unless we're - * already executing in one.. - */ - if (!irqs_running()) - if (really_local_bh_count() || !spin_is_locked(&global_bh_lock)) - break; - - /* Duh, we have to loop. Release the lock to avoid deadlocks */ - smp_mb__before_clear_bit(); /* need barrier before releasing lock... */ - clear_bit(0,&global_irq_lock); - - for (;;) { - if (!--count) { - show("wait_on_irq"); - count = ~0; - } - local_irq_enable(); - SYNC_OTHER_CORES(smp_processor_id()); - local_irq_disable(); - if (irqs_running()) - continue; - if (global_irq_lock) - continue; - if (!really_local_bh_count() && spin_is_locked(&global_bh_lock)) - continue; - if (!test_and_set_bit(0,&global_irq_lock)) - break; - } - } -} - -/* - * This is called when we want to synchronize with - * interrupts. We may for example tell a device to - * stop sending interrupts: but to make sure there - * are no interrupts that are executing on another - * CPU we need to call this function. - */ -void synchronize_irq(void) -{ - if (irqs_running()) { - /* Stupid approach */ - cli(); - sti(); - } -} - -static inline void get_irqlock(void) -{ - if (test_and_set_bit(0,&global_irq_lock)) { - /* do we already hold the lock? */ - if (smp_processor_id() == global_irq_holder) - return; - /* Uhhuh.. Somebody else got it. Wait.. */ - do { - do { -#ifdef CONFIG_X86 - rep_nop(); -#endif - } while (test_bit(0,&global_irq_lock)); - } while (test_and_set_bit(0,&global_irq_lock)); - } - /* - * We also to make sure that nobody else is running - * in an interrupt context. - */ - wait_on_irq(); - - /* - * Ok, finally.. - */ - global_irq_holder = smp_processor_id(); -} - -#define EFLAGS_IF_SHIFT 9 - -/* - * A global "cli()" while in an interrupt context - * turns into just a local cli(). Interrupts - * should use spinlocks for the (very unlikely) - * case that they ever want to protect against - * each other. - * - * If we already have local interrupts disabled, - * this will not turn a local disable into a - * global one (problems with spinlocks: this makes - * save_flags+cli+sti usable inside a spinlock). - */ -void __global_cli(void) -{ - unsigned int flags; - -#ifdef CONFIG_IA64 - local_save_flags(flags); - if (flags & IA64_PSR_I) { - local_irq_disable(); - if (!really_local_irq_count()) - get_irqlock(); - } -#else - local_save_flags(flags); - if (flags & (1 << EFLAGS_IF_SHIFT)) { - local_irq_disable(); - if (!really_local_irq_count()) - get_irqlock(); - } -#endif -} - -void __global_sti(void) -{ - if (!really_local_irq_count()) - release_irqlock(smp_processor_id()); - local_irq_enable(); -} - -/* - * SMP flags value to restore to: - * 0 - global cli - * 1 - global sti - * 2 - local cli - * 3 - local sti - */ -unsigned long __global_save_flags(void) -{ - int retval; - int local_enabled; - unsigned long flags; - int cpu = smp_processor_id(); - - local_save_flags(flags); -#ifdef CONFIG_IA64 - local_enabled = (flags & IA64_PSR_I) != 0; -#else - local_enabled = (flags >> EFLAGS_IF_SHIFT) & 1; -#endif - /* default to local */ - retval = 2 + local_enabled; - - /* check for global flags if we're not in an interrupt */ - if (!really_local_irq_count()) { - if (local_enabled) - retval = 1; - if (global_irq_holder == cpu) - retval = 0; - } - return retval; -} - -void __global_restore_flags(unsigned long flags) -{ - switch (flags) { - case 0: - __global_cli(); - break; - case 1: - __global_sti(); - break; - case 2: - local_irq_disable(); - break; - case 3: - local_irq_enable(); - break; - default: - printk("global_restore_flags: %08lx (%08lx)\n", - flags, (&flags)[-1]); - } -} - #endif /* @@ -482,11 +217,7 @@ */ int handle_IRQ_event(unsigned int irq, struct pt_regs * regs, struct irqaction * action) { - int status; - - local_irq_enter(irq); - - status = 1; /* Force the "do bottom halves" bit */ + int status = 1; /* Force the "do bottom halves" bit */ if (!(action->flags & SA_INTERRUPT)) local_irq_enable(); @@ -500,11 +231,16 @@ add_interrupt_randomness(irq); local_irq_disable(); - local_irq_exit(irq); - return status; } +/* + * Generic enable/disable code: this just calls + * down into the PIC-specific version for the actual + * hardware disable after having gotten the irq + * controller lock. + */ + /** * disable_irq_nosync - disable an irq without waiting * @irq: Interrupt to disable @@ -546,14 +282,7 @@ void disable_irq(unsigned int irq) { disable_irq_nosync(irq); - -#ifdef CONFIG_SMP - if (!really_local_irq_count()) { - do { - barrier(); - } while (irq_desc(irq)->status & IRQ_INPROGRESS); - } -#endif + synchronize_irq(irq); } /** @@ -616,6 +345,7 @@ struct irqaction * action; unsigned int status; + irq_enter(); kstat.irqs[cpu][irq]++; if (desc->status & IRQ_PER_CPU) { @@ -638,7 +368,7 @@ * use the action we have. */ action = NULL; - if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { action = desc->action; status &= ~IRQ_PENDING; /* we commit to handling */ status |= IRQ_INPROGRESS; /* we are handling it */ @@ -651,7 +381,7 @@ * a different instance of this same irq, the other processor * will take care of it. */ - if (!action) + if (unlikely(!action)) goto out; /* @@ -673,8 +403,8 @@ break; desc->status &= ~IRQ_PENDING; } - desc->status &= ~IRQ_INPROGRESS; out: + desc->status &= ~IRQ_INPROGRESS; /* * The ->end() handler has to deal with interrupts which got * disabled while the handler was running. @@ -682,6 +412,7 @@ desc->handler->end(irq); spin_unlock(&desc->lock); } + irq_exit(); return 1; } @@ -811,7 +542,7 @@ #ifdef CONFIG_SMP /* Wait to make sure it's not being used on another CPU */ while (desc->status & IRQ_INPROGRESS) - barrier(); + synchronize_irq(irq); #endif kfree(action); return; @@ -864,7 +595,7 @@ /* Wait for longstanding interrupts to trigger. */ for (delay = jiffies + HZ/50; time_after(delay, jiffies); ) - /* about 20ms delay */ synchronize_irq(); + /* about 20ms delay */ barrier(); /* * enable any unassigned irqs @@ -887,7 +618,7 @@ * Wait for spurious interrupts to trigger */ for (delay = jiffies + HZ/10; time_after(delay, jiffies); ) - /* about 100ms delay */ synchronize_irq(); + /* about 100ms delay */ barrier(); /* * Now filter out any obviously spurious interrupts diff -Nru a/arch/ia64/kernel/irq_ia64.c b/arch/ia64/kernel/irq_ia64.c --- a/arch/ia64/kernel/irq_ia64.c Fri Aug 16 14:34:56 2002 +++ b/arch/ia64/kernel/irq_ia64.c Fri Aug 16 14:34:56 2002 @@ -36,6 +36,10 @@ #include #include +#ifdef CONFIG_PERFMON +# include +#endif + #define IRQ_DEBUG 0 /* default base addr of IPI table */ @@ -50,6 +54,11 @@ 0x28, 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21 }; +/* + * GSI to IA-64 vector translation table. + */ +__u8 gsi_to_vector_map[255]; + int ia64_alloc_irq (void) { @@ -144,9 +153,9 @@ extern void handle_IPI (int irq, void *dev_id, struct pt_regs *regs); static struct irqaction ipi_irqaction = { - handler: handle_IPI, - flags: SA_INTERRUPT, - name: "IPI" + .handler = handle_IPI, + .flags = SA_INTERRUPT, + .name = "IPI" }; #endif @@ -172,6 +181,9 @@ register_percpu_irq(IA64_SPURIOUS_INT_VECTOR, NULL); #ifdef CONFIG_SMP register_percpu_irq(IA64_IPI_VECTOR, &ipi_irqaction); +#endif +#ifdef CONFIG_PERFMON + perfmon_init_percpu(); #endif platform_irq_init(); } diff -Nru a/arch/ia64/kernel/irq_lsapic.c b/arch/ia64/kernel/irq_lsapic.c --- a/arch/ia64/kernel/irq_lsapic.c Fri Aug 16 14:34:55 2002 +++ b/arch/ia64/kernel/irq_lsapic.c Fri Aug 16 14:34:55 2002 @@ -27,12 +27,12 @@ } struct hw_interrupt_type irq_type_ia64_lsapic = { - typename: "LSAPIC", - startup: lsapic_noop_startup, - shutdown: lsapic_noop, - enable: lsapic_noop, - disable: lsapic_noop, - ack: lsapic_noop, - end: lsapic_noop, - set_affinity: (void (*)(unsigned int, unsigned long)) lsapic_noop + .typename = "LSAPIC", + .startup = lsapic_noop_startup, + .shutdown = lsapic_noop, + .enable = lsapic_noop, + .disable = lsapic_noop, + .ack = lsapic_noop, + .end = lsapic_noop, + .set_affinity = (void (*)(unsigned int, unsigned long)) lsapic_noop }; diff -Nru a/arch/ia64/kernel/machvec.c b/arch/ia64/kernel/machvec.c --- a/arch/ia64/kernel/machvec.c Fri Aug 16 14:34:57 2002 +++ b/arch/ia64/kernel/machvec.c Fri Aug 16 14:34:57 2002 @@ -11,13 +11,16 @@ struct ia64_machine_vector ia64_mv; /* - * Most platforms use this routine for mapping page frame addresses - * into a memory map index. + * Most platforms use this routine for mapping page frame addresses into a memory map + * index. + * + * Note: we can't use __pa() because map_nr_dense(X) MUST map to something >= max_mapnr if + * X is outside the identity mapped kernel space. */ unsigned long map_nr_dense (unsigned long addr) { - return MAP_NR_DENSE(addr); + return (addr - PAGE_OFFSET) >> PAGE_SHIFT; } static struct ia64_machine_vector * diff -Nru a/arch/ia64/kernel/mca.c b/arch/ia64/kernel/mca.c --- a/arch/ia64/kernel/mca.c Fri Aug 16 14:34:57 2002 +++ b/arch/ia64/kernel/mca.c Fri Aug 16 14:34:57 2002 @@ -82,27 +82,27 @@ extern struct hw_interrupt_type irq_type_iosapic_level; static struct irqaction cmci_irqaction = { - handler: ia64_mca_cmc_int_handler, - flags: SA_INTERRUPT, - name: "cmc_hndlr" + .handler = ia64_mca_cmc_int_handler, + .flags = SA_INTERRUPT, + .name = "cmc_hndlr" }; static struct irqaction mca_rdzv_irqaction = { - handler: ia64_mca_rendez_int_handler, - flags: SA_INTERRUPT, - name: "mca_rdzv" + .handler = ia64_mca_rendez_int_handler, + .flags = SA_INTERRUPT, + .name = "mca_rdzv" }; static struct irqaction mca_wkup_irqaction = { - handler: ia64_mca_wakeup_int_handler, - flags: SA_INTERRUPT, - name: "mca_wkup" + .handler = ia64_mca_wakeup_int_handler, + .flags = SA_INTERRUPT, + .name = "mca_wkup" }; static struct irqaction mca_cpe_irqaction = { - handler: ia64_mca_cpe_int_handler, - flags: SA_INTERRUPT, - name: "cpe_hndlr" + .handler = ia64_mca_cpe_int_handler, + .flags = SA_INTERRUPT, + .name = "cpe_hndlr" }; /* @@ -626,9 +626,12 @@ void ia64_mca_rendez_int_handler(int rendez_irq, void *arg, struct pt_regs *ptregs) { - int flags, cpu = 0; + unsigned long flags; + int cpu = 0; + /* Mask all interrupts */ - save_and_cli(flags); +#warning XXX fix me: this used to be: save_and_cli(flags); + local_irq_save(flags); #ifdef CONFIG_SMP cpu = cpu_logical_id(hard_smp_processor_id()); @@ -646,7 +649,7 @@ ia64_mca_wakeup_ipi_wait(); /* Enable all interrupts */ - restore_flags(flags); + local_irq_restore(flags); } diff -Nru a/arch/ia64/kernel/mca_asm.S b/arch/ia64/kernel/mca_asm.S --- a/arch/ia64/kernel/mca_asm.S Fri Aug 16 14:34:56 2002 +++ b/arch/ia64/kernel/mca_asm.S Fri Aug 16 14:34:56 2002 @@ -684,9 +684,9 @@ movl r3=SAL_GET_STATE_INFO;; DATA_VA_TO_PA(r7);; // convert to physical address ld8 r8=[r7],8;; // get pdesc function pointer - DATA_VA_TO_PA(r8) // convert to physical address + dep r8=0,r8,61,3;; // convert SAL VA to PA ld8 r1=[r7];; // set new (ia64_sal) gp - DATA_VA_TO_PA(r1) // convert to physical address + dep r1=0,r1,61,3;; // convert SAL VA to PA mov b6=r8 alloc r5=ar.pfs,8,0,8,0;; // allocate stack frame for SAL call diff -Nru a/arch/ia64/kernel/pci.c b/arch/ia64/kernel/pci.c --- a/arch/ia64/kernel/pci.c Fri Aug 16 14:34:57 2002 +++ b/arch/ia64/kernel/pci.c Fri Aug 16 14:34:57 2002 @@ -165,7 +165,7 @@ */ struct pci_bus * -pcibios_scan_root(int seg, int bus) +pcibios_scan_root(int bus) { struct list_head *list = NULL; struct pci_bus *pci_bus = NULL; @@ -174,12 +174,12 @@ pci_bus = pci_bus_b(list); if (pci_bus->number == bus) { /* Already scanned */ - printk("PCI: Bus (%02x:%02x) already probed\n", seg, bus); + printk("PCI: Bus (%02x) already probed\n", bus); return pci_bus; } } - printk("PCI: Probing PCI hardware on bus (%02x:%02x)\n", seg, bus); + printk("PCI: Probing PCI hardware on bus (%02x)\n", bus); return pci_scan_bus(bus, pci_root_ops, NULL); } @@ -265,12 +265,37 @@ int pcibios_enable_device (struct pci_dev *dev) { + u16 cmd, old_cmd; + int idx; + struct resource *r; + if (!dev) return -EINVAL; - /* Not needed, since we enable all devices at startup. */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + old_cmd = cmd; + for (idx=0; idx<6; idx++) { + r = &dev->resource[idx]; + if (!r->start && r->end) { + printk(KERN_ERR + "PCI: Device %s not available because of resource collisions\n", + dev->slot_name); + return -EINVAL; + } + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; + } + if (dev->resource[PCI_ROM_RESOURCE].start) + cmd |= PCI_COMMAND_MEMORY; + if (cmd != old_cmd) { + printk("PCI: Enabling device %s (%04x -> %04x)\n", dev->slot_name, old_cmd, cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } printk(KERN_INFO "PCI: Found IRQ %d for device %s\n", dev->irq, dev->slot_name); + return 0; } diff -Nru a/arch/ia64/kernel/perfmon.c b/arch/ia64/kernel/perfmon.c --- a/arch/ia64/kernel/perfmon.c Fri Aug 16 14:35:00 2002 +++ b/arch/ia64/kernel/perfmon.c Fri Aug 16 14:35:00 2002 @@ -106,6 +106,12 @@ #define PFM_REG_RETFLAG_SET(flags, val) do { flags &= ~PFM_REG_RETFL_MASK; flags |= (val); } while(0) +#ifdef CONFIG_SMP +#define cpu_is_online(i) (cpu_online_map & (1UL << i)) +#else +#define cpu_is_online(i) (i==0) +#endif + /* * debugging */ @@ -277,8 +283,8 @@ typedef struct { pfm_pmu_reg_type_t type; int pm_pos; - int (*read_check)(struct task_struct *task, unsigned int cnum, unsigned long *val); - int (*write_check)(struct task_struct *task, unsigned int cnum, unsigned long *val); + int (*read_check)(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs); + int (*write_check)(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs); unsigned long dep_pmd[4]; unsigned long dep_pmc[4]; } pfm_reg_desc_t; @@ -369,8 +375,8 @@ static pfm_session_t pfm_sessions; /* global sessions information */ static struct proc_dir_entry *perfmon_dir; /* for debug only */ static pfm_stats_t pfm_stats; -int __per_cpu_data pfm_syst_wide; -static int __per_cpu_data pfm_dcr_pp; +DEFINE_PER_CPU(int, pfm_syst_wide); +static DEFINE_PER_CPU(int, pfm_dcr_pp); /* sysctl() controls */ static pfm_sysctl_t pfm_sysctl; @@ -396,7 +402,7 @@ static void pfm_vm_close(struct vm_area_struct * area); static struct vm_operations_struct pfm_vm_ops={ - close: pfm_vm_close + .close = pfm_vm_close }; /* @@ -902,8 +908,8 @@ /* * and it must be a valid CPU */ - cpu = ffs(pfx->ctx_cpu_mask); - if (!cpu_online(cpu)) { + cpu = ffz(~pfx->ctx_cpu_mask); + if (cpu_is_online(cpu) == 0) { DBprintk(("CPU%d is not online\n", cpu)); return -EINVAL; } @@ -925,11 +931,12 @@ DBprintk(("must have notify_pid when blocking for [%d]\n", task->pid)); return -EINVAL; } - +#if 0 if ((ctx_flags & PFM_FL_NOTIFY_BLOCK) && pfx->ctx_notify_pid == task->pid) { DBprintk(("cannot notify self when blocking for [%d]\n", task->pid)); return -EINVAL; } +#endif } /* probably more to add here */ @@ -968,7 +975,7 @@ if (ctx_flags & PFM_FL_SYSTEM_WIDE) { /* at this point, we know there is at least one bit set */ - cpu = ffs(tmp.ctx_cpu_mask) - 1; + cpu = ffz(~tmp.ctx_cpu_mask); DBprintk(("requesting CPU%d currently on CPU%d\n",cpu, smp_processor_id())); @@ -1280,7 +1287,7 @@ /* * execute write checker, if any */ - if (PMC_WR_FUNC(cnum)) ret = PMC_WR_FUNC(cnum)(task, cnum, &tmp.reg_value); + if (PMC_WR_FUNC(cnum)) ret = PMC_WR_FUNC(cnum)(task, cnum, &tmp.reg_value, regs); abort_mission: if (ret == -EINVAL) reg_retval = PFM_REG_RETFL_EINVAL; @@ -1371,7 +1378,7 @@ /* * execute write checker, if any */ - if (PMD_WR_FUNC(cnum)) ret = PMD_WR_FUNC(cnum)(task, cnum, &tmp.reg_value); + if (PMD_WR_FUNC(cnum)) ret = PMD_WR_FUNC(cnum)(task, cnum, &tmp.reg_value, regs); abort_mission: if (ret == -EINVAL) reg_retval = PFM_REG_RETFL_EINVAL; @@ -1394,6 +1401,8 @@ /* keep track of what we use */ CTX_USED_PMD(ctx, pmu_conf.pmd_desc[(cnum)].dep_pmd[0]); + /* mark this register as used as well */ + CTX_USED_PMD(ctx, RDEP(cnum)); /* writes to unimplemented part is ignored, so this is safe */ ia64_set_pmd(cnum, tmp.reg_value & pmu_conf.perf_ovfl_val); @@ -1438,7 +1447,7 @@ DBprintk(("ctx_last_cpu=%d for [%d]\n", atomic_read(&ctx->ctx_last_cpu), task->pid)); for (i = 0; i < count; i++, req++) { - unsigned long reg_val = ~0UL, ctx_val = ~0UL; + unsigned long ctx_val = ~0UL; if (copy_from_user(&tmp, req, sizeof(tmp))) return -EFAULT; @@ -1462,7 +1471,7 @@ */ if (atomic_read(&ctx->ctx_last_cpu) == smp_processor_id()){ ia64_srlz_d(); - val = reg_val = ia64_get_pmd(cnum); + val = ia64_get_pmd(cnum); DBprintk(("reading pmd[%u]=0x%lx from hw\n", cnum, val)); } else { #ifdef CONFIG_SMP @@ -1484,7 +1493,7 @@ } #endif /* context has been saved */ - val = reg_val = th->pmd[cnum]; + val = th->pmd[cnum]; } if (PMD_IS_COUNTING(cnum)) { /* @@ -1493,9 +1502,7 @@ val &= pmu_conf.perf_ovfl_val; val += ctx_val = ctx->ctx_soft_pmds[cnum].val; - } else { - val = reg_val = ia64_get_pmd(cnum); - } + } tmp.reg_value = val; @@ -1503,14 +1510,13 @@ * execute read checker, if any */ if (PMD_RD_FUNC(cnum)) { - ret = PMD_RD_FUNC(cnum)(task, cnum, &tmp.reg_value); + ret = PMD_RD_FUNC(cnum)(task, cnum, &tmp.reg_value, regs); } PFM_REG_RETFLAG_SET(tmp.reg_flags, ret); - DBprintk(("read pmd[%u] ret=%d soft_pmd=0x%lx reg=0x%lx pmc=0x%lx\n", - cnum, ret, ctx_val, reg_val, - ia64_get_pmc(cnum))); + DBprintk(("read pmd[%u] ret=%d value=0x%lx pmc=0x%lx\n", + cnum, ret, val, ia64_get_pmc(cnum))); if (copy_to_user(req, &tmp, sizeof(tmp))) return -EFAULT; } @@ -1553,15 +1559,11 @@ */ if (ctx && ctx->ctx_fl_using_dbreg == 1) return -1; - /* - * XXX: not pretty - */ LOCK_PFS(); /* - * We only allow the use of debug registers when there is no system - * wide monitoring - * XXX: we could relax this by + * We cannot allow setting breakpoints when system wide monitoring + * sessions are using the debug registers. */ if (pfm_sessions.pfs_sys_use_dbregs> 0) ret = -1; @@ -1762,7 +1764,7 @@ ia64_srlz_i(); #ifdef CONFIG_SMP - this_cpu(pfm_dcr_pp) = 0; + __get_cpu_var(pfm_dcr_pp) = 0; #else pfm_tasklist_toggle_pp(0); #endif @@ -1921,7 +1923,6 @@ dbr_mask_reg_t dbr; } dbreg_t; - static int pfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, struct pt_regs *regs) { @@ -1963,8 +1964,8 @@ if (ctx->ctx_fl_system) { /* we mark ourselves as owner of the debug registers */ ctx->ctx_fl_using_dbreg = 1; - } else { - if (ctx->ctx_fl_using_dbreg == 0) { + DBprintk(("system-wide setting fl_using_dbreg for [%d]\n", task->pid)); + } else if (first_time) { ret= -EBUSY; if ((thread->flags & IA64_THREAD_DBG_VALID) != 0) { DBprintk(("debug registers already in use for [%d]\n", task->pid)); @@ -1973,6 +1974,7 @@ /* we mark ourselves as owner of the debug registers */ ctx->ctx_fl_using_dbreg = 1; + DBprintk(("setting fl_using_dbreg for [%d]\n", task->pid)); /* * Given debug registers cannot be used for both debugging * and performance monitoring at the same time, we reuse @@ -1980,20 +1982,27 @@ */ memset(task->thread.dbr, 0, sizeof(task->thread.dbr)); memset(task->thread.ibr, 0, sizeof(task->thread.ibr)); + } - /* - * clear hardware registers to make sure we don't - * pick up stale state - */ - for (i=0; i < pmu_conf.num_ibrs; i++) { - ia64_set_ibr(i, 0UL); - } - ia64_srlz_i(); - for (i=0; i < pmu_conf.num_dbrs; i++) { - ia64_set_dbr(i, 0UL); - } - ia64_srlz_d(); + if (first_time) { + DBprintk(("[%d] clearing ibrs,dbrs\n", task->pid)); + /* + * clear hardware registers to make sure we don't + * pick up stale state. + * + * for a system wide session, we do not use + * thread.dbr, thread.ibr because this process + * never leaves the current CPU and the state + * is shared by all processes running on it + */ + for (i=0; i < pmu_conf.num_ibrs; i++) { + ia64_set_ibr(i, 0UL); } + ia64_srlz_i(); + for (i=0; i < pmu_conf.num_dbrs; i++) { + ia64_set_dbr(i, 0UL); + } + ia64_srlz_d(); } ret = -EFAULT; @@ -2163,7 +2172,7 @@ if (ctx->ctx_fl_system) { #ifdef CONFIG_SMP - this_cpu(pfm_dcr_pp) = 1; + __get_cpu_var(pfm_dcr_pp) = 1; #else pfm_tasklist_toggle_pp(1); #endif @@ -2218,8 +2227,8 @@ ia64_srlz_i(); #ifdef CONFIG_SMP - this_cpu(pfm_syst_wide) = 1; - this_cpu(pfm_dcr_pp) = 0; + __get_cpu_var(pfm_syst_wide) = 1; + __get_cpu_var(pfm_dcr_pp) = 0; #endif } else { /* @@ -2361,9 +2370,9 @@ { struct pt_regs *regs = (struct pt_regs *)&stack; struct task_struct *task = current; - pfm_context_t *ctx = task->thread.pfm_context; + pfm_context_t *ctx; size_t sz; - int ret = -ESRCH, narg; + int ret, narg; /* * reject any call if perfmon was disabled at initialization time @@ -2393,6 +2402,8 @@ if (pid != current->pid) { + ret = -ESRCH; + read_lock(&tasklist_lock); task = find_task_by_pid(pid); @@ -2407,10 +2418,11 @@ ret = check_task_state(task); if (ret != 0) goto abort_call; } - ctx = task->thread.pfm_context; } } + ctx = task->thread.pfm_context; + if (PFM_CMD_USE_CTX(cmd)) { ret = -EINVAL; if (ctx == NULL) { @@ -2953,11 +2965,6 @@ static int perfmon_proc_info(char *page) { -#ifdef CONFIG_SMP -#define cpu_is_online(i) (cpu_online_map & (1UL << i)) -#else -#define cpu_is_online(i) 1 -#endif char *p = page; int i; @@ -2973,9 +2980,9 @@ p += sprintf(p, "CPU%d syst_wide : %d\n" "CPU%d dcr_pp : %d\n", smp_processor_id(), - this_cpu(pfm_syst_wide), + __get_cpu_var(pfm_syst_wide), smp_processor_id(), - this_cpu(pfm_dcr_pp)); + __get_cpu_var(pfm_dcr_pp)); #endif LOCK_PFS(); @@ -3045,7 +3052,7 @@ /* * propagate the value of the dcr_pp bit to the psr */ - ia64_psr(regs)->pp = mode ? this_cpu(pfm_dcr_pp) : 0; + ia64_psr(regs)->pp = mode ? __get_cpu_var(pfm_dcr_pp) : 0; } #endif @@ -3539,8 +3546,8 @@ ia64_srlz_i(); #ifdef CONFIG_SMP - this_cpu(pfm_syst_wide) = 0; - this_cpu(pfm_dcr_pp) = 0; + __get_cpu_var(pfm_syst_wide) = 0; + __get_cpu_var(pfm_dcr_pp) = 0; #else pfm_tasklist_toggle_pp(0); #endif @@ -4118,9 +4125,9 @@ } static struct irqaction perfmon_irqaction = { - handler: perfmon_interrupt, - flags: SA_INTERRUPT, - name: "perfmon" + .handler = perfmon_interrupt, + .flags = SA_INTERRUPT, + .name = "perfmon" }; @@ -4150,11 +4157,6 @@ pal_perf_mon_info_u_t pm_info; s64 status; - register_percpu_irq(IA64_PERFMON_VECTOR, &perfmon_irqaction); - - ia64_set_pmv(IA64_PERFMON_VECTOR); - ia64_srlz_d(); - pmu_conf.pfm_is_disabled = 1; printk("perfmon: version %u.%u (sampling format v%u.%u) IRQ %u\n", @@ -4232,6 +4234,9 @@ void perfmon_init_percpu (void) { + if (smp_processor_id() == 0) + register_percpu_irq(IA64_PERFMON_VECTOR, &perfmon_irqaction); + ia64_set_pmv(IA64_PERFMON_VECTOR); ia64_srlz_d(); } diff -Nru a/arch/ia64/kernel/perfmon_itanium.h b/arch/ia64/kernel/perfmon_itanium.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ia64/kernel/perfmon_itanium.h Fri Aug 16 14:35:01 2002 @@ -0,0 +1,99 @@ +/* + * This file contains the Itanium PMU register description tables + * and pmc checker used by perfmon.c. + * + * Copyright (C) 2002 Hewlett Packard Co + * Stephane Eranian + */ + +#define RDEP(x) (1UL<<(x)) + +#ifndef CONFIG_ITANIUM +#error "This file is only valid when CONFIG_ITANIUM is defined" +#endif + +static int pfm_ita_pmc_check(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs); +static int pfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, struct pt_regs *regs); + +static pfm_reg_desc_t pmc_desc[256]={ +/* pmc0 */ { PFM_REG_CONTROL, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc1 */ { PFM_REG_CONTROL, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc2 */ { PFM_REG_CONTROL, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc3 */ { PFM_REG_CONTROL, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc4 */ { PFM_REG_COUNTING, 6, NULL, NULL, {RDEP(4),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc5 */ { PFM_REG_COUNTING, 6, NULL, NULL, {RDEP(5),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc6 */ { PFM_REG_COUNTING, 6, NULL, NULL, {RDEP(6),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc7 */ { PFM_REG_COUNTING, 6, NULL, NULL, {RDEP(7),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc8 */ { PFM_REG_CONFIG, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc9 */ { PFM_REG_CONFIG, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc10 */ { PFM_REG_MONITOR, 6, NULL, NULL, {RDEP(0)|RDEP(1),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc11 */ { PFM_REG_MONITOR, 6, NULL, pfm_ita_pmc_check, {RDEP(2)|RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc12 */ { PFM_REG_MONITOR, 6, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc13 */ { PFM_REG_CONFIG, 0, NULL, pfm_ita_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, + { PFM_REG_NONE, 0, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +static pfm_reg_desc_t pmd_desc[256]={ +/* pmd0 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(1),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}}, +/* pmd1 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(0),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}}, +/* pmd2 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, +/* pmd3 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(2)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, +/* pmd4 */ { PFM_REG_COUNTING, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(4),0UL, 0UL, 0UL}}, +/* pmd5 */ { PFM_REG_COUNTING, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(5),0UL, 0UL, 0UL}}, +/* pmd6 */ { PFM_REG_COUNTING, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(6),0UL, 0UL, 0UL}}, +/* pmd7 */ { PFM_REG_COUNTING, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(7),0UL, 0UL, 0UL}}, +/* pmd8 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd9 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd10 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd11 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd12 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd13 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd14 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd15 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd16 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd17 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(2)|RDEP(3),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, + { PFM_REG_NONE, 0, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +static int +pfm_ita_pmc_check(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs) +{ + pfm_context_t *ctx = task->thread.pfm_context; + int ret; + + /* + * we must clear the (instruction) debug registers if pmc13.ta bit is cleared + * before they are written (fl_using_dbreg==0) to avoid picking up stale information. + */ + if (cnum == 13 && ((*val & 0x1) == 0UL) && ctx->ctx_fl_using_dbreg == 0) { + + /* don't mix debug with perfmon */ + if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL; + + /* + * a count of 0 will mark the debug registers as in use and also + * ensure that they are properly cleared. + */ + ret = pfm_write_ibr_dbr(1, task, NULL, 0, regs); + if (ret) return ret; + } + + /* + * we must clear the (data) debug registers if pmc11.pt bit is cleared + * before they are written (fl_using_dbreg==0) to avoid picking up stale information. + */ + if (cnum == 11 && ((*val >> 28)& 0x1) == 0 && ctx->ctx_fl_using_dbreg == 0) { + + /* don't mix debug with perfmon */ + if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL; + + /* + * a count of 0 will mark the debug registers as in use and also + * ensure that they are properly cleared. + */ + ret = pfm_write_ibr_dbr(0, task, NULL, 0, regs); + if (ret) return ret; + } + return 0; +} + diff -Nru a/arch/ia64/kernel/perfmon_mckinley.h b/arch/ia64/kernel/perfmon_mckinley.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ia64/kernel/perfmon_mckinley.h Fri Aug 16 14:35:01 2002 @@ -0,0 +1,134 @@ +/* + * This file contains the McKinley PMU register description tables + * and pmc checker used by perfmon.c. + * + * Copyright (C) 2002 Hewlett Packard Co + * Stephane Eranian + */ + +#define RDEP(x) (1UL<<(x)) + +#ifndef CONFIG_MCKINLEY +#error "This file is only valid when CONFIG_MCKINLEY is defined" +#endif + +static int pfm_mck_pmc_check(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs); +static int pfm_write_ibr_dbr(int mode, struct task_struct *task, void *arg, int count, struct pt_regs *regs); + +static pfm_reg_desc_t pmc_desc[256]={ +/* pmc0 */ { PFM_REG_CONTROL, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc1 */ { PFM_REG_CONTROL, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc2 */ { PFM_REG_CONTROL, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc3 */ { PFM_REG_CONTROL, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc4 */ { PFM_REG_COUNTING, 6, NULL, pfm_mck_pmc_check, {RDEP(4),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc5 */ { PFM_REG_COUNTING, 6, NULL, NULL, {RDEP(5),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc6 */ { PFM_REG_COUNTING, 6, NULL, NULL, {RDEP(6),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc7 */ { PFM_REG_COUNTING, 6, NULL, NULL, {RDEP(7),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc8 */ { PFM_REG_CONFIG, 0, NULL, pfm_mck_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc9 */ { PFM_REG_CONFIG, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc10 */ { PFM_REG_MONITOR, 4, NULL, NULL, {RDEP(0)|RDEP(1),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc11 */ { PFM_REG_MONITOR, 6, NULL, NULL, {RDEP(2)|RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc12 */ { PFM_REG_MONITOR, 6, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc13 */ { PFM_REG_CONFIG, 0, NULL, pfm_mck_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc14 */ { PFM_REG_CONFIG, 0, NULL, pfm_mck_pmc_check, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, +/* pmc15 */ { PFM_REG_CONFIG, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {0UL,0UL, 0UL, 0UL}}, + { PFM_REG_NONE, 0, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +static pfm_reg_desc_t pmd_desc[256]={ +/* pmd0 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(1),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}}, +/* pmd1 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(0),0UL, 0UL, 0UL}, {RDEP(10),0UL, 0UL, 0UL}}, +/* pmd2 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(3)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, +/* pmd3 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(2)|RDEP(17),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, +/* pmd4 */ { PFM_REG_COUNTING, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(4),0UL, 0UL, 0UL}}, +/* pmd5 */ { PFM_REG_COUNTING, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(5),0UL, 0UL, 0UL}}, +/* pmd6 */ { PFM_REG_COUNTING, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(6),0UL, 0UL, 0UL}}, +/* pmd7 */ { PFM_REG_COUNTING, 0, NULL, NULL, {0UL,0UL, 0UL, 0UL}, {RDEP(7),0UL, 0UL, 0UL}}, +/* pmd8 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd9 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd10 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd11 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd12 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(13)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd13 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(14)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd14 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(15)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd15 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(16),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd16 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(8)|RDEP(9)|RDEP(10)|RDEP(11)|RDEP(12)|RDEP(13)|RDEP(14)|RDEP(15),0UL, 0UL, 0UL}, {RDEP(12),0UL, 0UL, 0UL}}, +/* pmd17 */ { PFM_REG_BUFFER, 0, NULL, NULL, {RDEP(2)|RDEP(3),0UL, 0UL, 0UL}, {RDEP(11),0UL, 0UL, 0UL}}, + { PFM_REG_NONE, 0, NULL, NULL, {0,}, {0,}}, /* end marker */ +}; + +static int +pfm_mck_pmc_check(struct task_struct *task, unsigned int cnum, unsigned long *val, struct pt_regs *regs) +{ + struct thread_struct *th = &task->thread; + pfm_context_t *ctx = task->thread.pfm_context; + int ret = 0, check_case1 = 0; + unsigned long val8 = 0, val14 = 0, val13 = 0; + + /* + * we must clear the debug registers if any pmc13.ena_dbrpX bit is enabled + * before they are written (fl_using_dbreg==0) to avoid picking up stale information. + */ + if (cnum == 13 && (*val & (0xfUL << 45)) && ctx->ctx_fl_using_dbreg == 0) { + + /* don't mix debug with perfmon */ + if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL; + + /* + * a count of 0 will mark the debug registers as in use and also + * ensure that they are properly cleared. + */ + ret = pfm_write_ibr_dbr(1, task, NULL, 0, regs); + if (ret) return ret; + } + /* + * we must clear the (instruction) debug registers if any pmc14.ibrpX bit is enabled + * before they are (fl_using_dbreg==0) to avoid picking up stale information. + */ + if (cnum == 14 && ((*val & 0x2222) != 0x2222) && ctx->ctx_fl_using_dbreg == 0) { + + /* don't mix debug with perfmon */ + if ((task->thread.flags & IA64_THREAD_DBG_VALID) != 0) return -EINVAL; + + /* + * a count of 0 will mark the debug registers as in use and also + * ensure that they are properly cleared. + */ + ret = pfm_write_ibr_dbr(0, task, NULL, 0, regs); + if (ret) return ret; + + } + + switch(cnum) { + case 4: *val |= 1UL << 23; /* force power enable bit */ + break; + case 8: val8 = *val; + val13 = th->pmc[13]; + val14 = th->pmc[14]; + check_case1 = 1; + break; + case 13: val8 = th->pmc[8]; + val13 = *val; + val14 = th->pmc[14]; + check_case1 = 1; + break; + case 14: val8 = th->pmc[13]; + val13 = th->pmc[13]; + val14 = *val; + check_case1 = 1; + break; + } + /* check illegal configuration which can produce inconsistencies in tagging + * i-side events in L1D and L2 caches + */ + if (check_case1) { + ret = ((val13 >> 45) & 0xf) == 0 + && ((val8 & 0x1) == 0) + && ((((val14>>1) & 0x3) == 0x2 || ((val14>>1) & 0x3) == 0x0) + ||(((val14>>4) & 0x3) == 0x2 || ((val14>>4) & 0x3) == 0x0)); + + if (ret) printk("perfmon: failure check_case1\n"); + } + + return ret ? -EINVAL : 0; +} diff -Nru a/arch/ia64/kernel/process.c b/arch/ia64/kernel/process.c --- a/arch/ia64/kernel/process.c Fri Aug 16 14:34:57 2002 +++ b/arch/ia64/kernel/process.c Fri Aug 16 14:34:57 2002 @@ -194,7 +194,7 @@ pfm_save_regs(task); # ifdef CONFIG_SMP - if (this_cpu(pfm_syst_wide)) + if (__get_cpu_var(pfm_syst_wide)) pfm_syst_wide_update_task(task, 0); # endif #endif @@ -216,7 +216,7 @@ pfm_load_regs(task); # ifdef CONFIG_SMP - if (this_cpu(pfm_syst_wide)) pfm_syst_wide_update_task(task, 1); + if (__get_cpu_var(pfm_syst_wide)) pfm_syst_wide_update_task(task, 1); # endif #endif @@ -325,6 +325,11 @@ /* copy parts of thread_struct: */ p->thread.ksp = (unsigned long) child_stack - 16; + + /* stop some PSR bits from being inherited: */ + child_ptregs->cr_ipsr = ((child_ptregs->cr_ipsr | IA64_PSR_BITS_TO_SET) + & ~IA64_PSR_BITS_TO_CLEAR); + /* * NOTE: The calling convention considers all floating point * registers in the high partition (fph) to be scratch. Since diff -Nru a/arch/ia64/kernel/setup.c b/arch/ia64/kernel/setup.c --- a/arch/ia64/kernel/setup.c Fri Aug 16 14:34:53 2002 +++ b/arch/ia64/kernel/setup.c Fri Aug 16 14:34:53 2002 @@ -58,7 +58,7 @@ unsigned long __per_cpu_offset[NR_CPUS]; #endif -struct cpuinfo_ia64 cpu_info __per_cpu_data; +DECLARE_PER_CPU(struct cpuinfo_ia64, cpu_info); unsigned long ia64_phys_stacked_size_p8; unsigned long ia64_cycles_per_usec; @@ -347,6 +347,14 @@ #ifdef CONFIG_ACPI_BOOT acpi_boot_init(*cmdline_p); #endif +#ifdef CONFIG_SERIAL_HCDP + if (efi.hcdp) { + void setup_serial_hcdp(void *); + + /* Setup the serial ports described by HCDP */ + setup_serial_hcdp(efi.hcdp); + } +#endif #ifdef CONFIG_VT # if defined(CONFIG_DUMMY_CONSOLE) conswitchp = &dummy_con; @@ -436,7 +444,7 @@ c_start (struct seq_file *m, loff_t *pos) { #ifdef CONFIG_SMP - while (*pos < NR_CPUS && !(cpu_online_map & (1 << *pos))) + while (*pos < NR_CPUS && !(cpu_online_map & (1UL << *pos))) ++*pos; #endif return *pos < NR_CPUS ? cpu_data(*pos) : NULL; @@ -455,10 +463,10 @@ } struct seq_operations cpuinfo_op = { - start: c_start, - next: c_next, - stop: c_stop, - show: show_cpuinfo + .start = c_start, + .next = c_next, + .stop = c_stop, + .show = show_cpuinfo }; void @@ -542,7 +550,18 @@ extern char __per_cpu_end[]; int cpu = smp_processor_id(); - my_cpu_data = alloc_bootmem_pages(__per_cpu_end - __per_cpu_start); + if (__per_cpu_end - __per_cpu_start > PAGE_SIZE) + panic("Per-cpu data area too big! (%Zu > %Zu)", + __per_cpu_end - __per_cpu_start, PAGE_SIZE); + + /* + * On the BSP, the page allocator isn't initialized by the time we get here. On + * the APs, the bootmem allocator is no longer available... + */ + if (cpu == 0) + my_cpu_data = alloc_bootmem_pages(__per_cpu_end - __per_cpu_start); + else + my_cpu_data = (void *) get_free_page(GFP_KERNEL); memcpy(my_cpu_data, __phys_per_cpu_start, __per_cpu_end - __per_cpu_start); __per_cpu_offset[cpu] = (char *) my_cpu_data - __per_cpu_start; my_cpu_info = my_cpu_data + ((char *) &cpu_info - __per_cpu_start); diff -Nru a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c --- a/arch/ia64/kernel/signal.c Fri Aug 16 14:34:52 2002 +++ b/arch/ia64/kernel/signal.c Fri Aug 16 14:34:52 2002 @@ -146,6 +146,7 @@ if (from->si_code < 0) { if (__copy_to_user(to, from, sizeof(siginfo_t))) return -EFAULT; + return 0; } else { int err; diff -Nru a/arch/ia64/kernel/smp.c b/arch/ia64/kernel/smp.c --- a/arch/ia64/kernel/smp.c Fri Aug 16 14:35:01 2002 +++ b/arch/ia64/kernel/smp.c Fri Aug 16 14:35:01 2002 @@ -80,7 +80,7 @@ #define IPI_CPU_STOP 1 /* This needs to be cacheline aligned because it is written to by *other* CPUs. */ -static __u64 ipi_operation __per_cpu_data ____cacheline_aligned; +static DECLARE_PER_CPU(__u64, ipi_operation) ____cacheline_aligned; static void stop_this_cpu (void) @@ -99,7 +99,7 @@ handle_IPI (int irq, void *dev_id, struct pt_regs *regs) { int this_cpu = smp_processor_id(); - unsigned long *pending_ipis = &this_cpu(ipi_operation); + unsigned long *pending_ipis = &__get_cpu_var(ipi_operation); unsigned long ops; /* Count this now; we may make a call that never returns. */ diff -Nru a/arch/ia64/kernel/smpboot.c b/arch/ia64/kernel/smpboot.c --- a/arch/ia64/kernel/smpboot.c Fri Aug 16 14:34:55 2002 +++ b/arch/ia64/kernel/smpboot.c Fri Aug 16 14:34:55 2002 @@ -1,10 +1,14 @@ /* * SMP boot-related support * - * Copyright (C) 2001 David Mosberger-Tang + * Copyright (C) 1998-2002 Hewlett-Packard Co + * David Mosberger-Tang * * 01/05/16 Rohit Seth Moved SMP booting functions from smp.c to here. * 01/04/27 David Mosberger Added ITC synching code. + * 02/07/31 David Mosberger Switch over to hotplug-CPU boot-sequence. + * smp_boot_cpus()/smp_commence() is replaced by + * smp_prepare_cpus()/__cpu_up()/smp_cpus_done(). */ @@ -66,18 +70,16 @@ #define DEBUG_ITC_SYNC 0 -extern void __init calibrate_delay(void); -extern void start_ap(void); +extern void __init calibrate_delay (void); +extern void start_ap (void); extern unsigned long ia64_iobase; int cpucount; task_t *task_for_booting_cpu; -/* Setup configured maximum number of CPUs to activate */ -static int max_cpus = -1; - /* Bitmask of currently online CPUs */ volatile unsigned long cpu_online_map; +unsigned long phys_cpu_present_map; /* which logical CPU number maps to which CPU (physical APIC ID) */ volatile int ia64_cpu_to_sapicid[NR_CPUS]; @@ -86,44 +88,12 @@ struct smp_boot_data smp_boot_data __initdata; -/* Set when the idlers are all forked */ -volatile int smp_threads_ready; - unsigned long ap_wakeup_vector = -1; /* External Int use to wakeup APs */ char __initdata no_int_routing; unsigned char smp_int_redirect; /* are INT and IPI redirectable by the chipset? */ -/* - * Setup routine for controlling SMP activation - * - * Command-line option of "nosmp" or "maxcpus=0" will disable SMP - * activation entirely (the MPS table probe still happens, though). - * - * Command-line option of "maxcpus=", where is an integer - * greater than 0, limits the maximum number of CPUs activated in - * SMP mode to . - */ - -static int __init -nosmp (char *str) -{ - max_cpus = 0; - return 1; -} - -__setup("nosmp", nosmp); - -static int __init -maxcpus (char *str) -{ - get_option(&str, &max_cpus); - return 1; -} - -__setup("maxcpus=", maxcpus); - static int __init nointroute (char *str) { @@ -299,7 +269,7 @@ static volatile atomic_t smp_commenced = ATOMIC_INIT(0); -void __init +static void __init smp_commence (void) { /* @@ -308,7 +278,7 @@ Dprintk("Setting commenced=1, go go go\n"); wmb(); - atomic_set(&smp_commenced,1); + atomic_set(&smp_commenced, 1); } @@ -380,11 +350,7 @@ efi_map_pal_code(); cpu_init(); smp_callin(); - Dprintk("CPU %d is set to go.\n", smp_processor_id()); - while (!atomic_read(&smp_commenced)) - cpu_relax(); - Dprintk("CPU %d is starting idle.\n", smp_processor_id()); return cpu_idle(); } @@ -392,22 +358,20 @@ fork_by_hand (void) { /* - * don't care about the eip and regs settings since - * we'll never reschedule the forked task. + * don't care about the eip and regs settings since we'll never reschedule the + * forked task. */ return do_fork(CLONE_VM|CLONE_IDLETASK, 0, 0, 0); } -static void __init -do_boot_cpu (int sapicid) +static int __init +do_boot_cpu (int sapicid, int cpu) { struct task_struct *idle; - int timeout, cpu; + int timeout; - cpu = ++cpucount; /* - * We can't use kernel_thread since we must avoid to - * reschedule the child. + * We can't use kernel_thread since we must avoid to reschedule the child. */ idle = fork_by_hand(); if (IS_ERR(idle)) @@ -419,13 +383,11 @@ */ init_idle(idle, cpu); - ia64_cpu_to_sapicid[cpu] = sapicid; - unhash_process(idle); task_for_booting_cpu = idle; - Dprintk("Sending wakeup vector %u to AP 0x%x/0x%x.\n", ap_wakeup_vector, cpu, sapicid); + Dprintk("Sending wakeup vector %lu to AP 0x%x/0x%x.\n", ap_wakeup_vector, cpu, sapicid); platform_send_ipi(cpu, ap_wakeup_vector, IA64_IPI_DM_INT, 0); @@ -448,8 +410,10 @@ } else { printk(KERN_ERR "Processor 0x%x/0x%x is stuck.\n", cpu, sapicid); ia64_cpu_to_sapicid[cpu] = -1; - cpucount--; + clear_bit(cpu, &cpu_online_map); /* was set in smp_callin() */ + return -EINVAL; } + return 0; } unsigned long cache_decay_ticks; /* # of ticks an idle task is considered cache-hot */ @@ -464,21 +428,42 @@ } /* + * Initialize the logical CPU number to SAPICID mapping + */ +void __init +smp_build_cpu_map (void) +{ + int sapicid, cpu, i; + int boot_cpu_id = hard_smp_processor_id(); + + for (cpu = 0; cpu < NR_CPUS; cpu++) + ia64_cpu_to_sapicid[cpu] = -1; + + ia64_cpu_to_sapicid[0] = boot_cpu_id; + phys_cpu_present_map = 1; + + for (cpu = 1, i = 0; i < smp_boot_data.cpu_count; i++) { + sapicid = smp_boot_data.cpu_phys_id[i]; + if (sapicid == -1 || sapicid == boot_cpu_id) + continue; + phys_cpu_present_map |= (1 << cpu); + ia64_cpu_to_sapicid[cpu] = sapicid; + cpu++; + } +} + +/* * Cycle through the APs sending Wakeup IPIs to boot each. */ void __init -smp_boot_cpus (void) +smp_prepare_cpus (unsigned int max_cpus) { - int sapicid, cpu; int boot_cpu_id = hard_smp_processor_id(); /* - * Initialize the logical to physical CPU number mapping - * and the per-CPU profiling counter/multiplier + * Initialize the per-CPU profiling counter/multiplier */ - for (cpu = 0; cpu < NR_CPUS; cpu++) - ia64_cpu_to_sapicid[cpu] = -1; smp_setup_percpu_timer(); /* @@ -492,7 +477,6 @@ printk("Boot processor id 0x%x/0x%x\n", 0, boot_cpu_id); - global_irq_holder = NO_PROC_ID; current_thread_info()->cpu = 0; smp_tune_scheduling(); @@ -501,55 +485,48 @@ */ if (!max_cpus || (max_cpus < -1)) { printk(KERN_INFO "SMP mode deactivated.\n"); - cpu_online_map = 1; - goto smp_done; + cpu_online_map = phys_cpu_present_map = 1; + return; } - if (max_cpus != -1) - printk (KERN_INFO "Limiting CPUs to %d\n", max_cpus); +} + +void +smp_cpus_done (unsigned int dummy) +{ + int cpu; + unsigned long bogosum = 0; - if (smp_boot_data.cpu_count > 1) { + /* + * Allow the user to impress friends. + */ - printk(KERN_INFO "SMP: starting up secondaries.\n"); + for (cpu = 0; cpu < NR_CPUS; cpu++) + if (cpu_online(cpu)) + bogosum += cpu_data(cpu)->loops_per_jiffy; - for (cpu = 0; cpu < smp_boot_data.cpu_count; cpu++) { - /* - * Don't even attempt to start the boot CPU! - */ - sapicid = smp_boot_data.cpu_phys_id[cpu]; - if ((sapicid == -1) || (sapicid == hard_smp_processor_id())) - continue; - - if ((max_cpus > 0) && (cpucount + 1 >= max_cpus)) - break; - - do_boot_cpu(sapicid); - - /* - * Make sure we unmap all failed CPUs - */ - if (ia64_cpu_to_sapicid[cpu] == -1) - printk("phys CPU#%d not responding - cannot use it.\n", cpu); - } + printk(KERN_INFO"Total of %d processors activated (%lu.%02lu BogoMIPS).\n", + num_online_cpus(), bogosum/(500000/HZ), (bogosum/(5000/HZ))%100); +} - /* - * Allow the user to impress friends. - */ - - printk("Before bogomips.\n"); - if (!cpucount) { - printk(KERN_ERR "Error: only one processor found.\n"); - } else { - unsigned long bogosum = 0; - for (cpu = 0; cpu < NR_CPUS; cpu++) - if (cpu_online_map & (1<loops_per_jiffy; +int __devinit +__cpu_up (unsigned int cpu) +{ + int ret; + int sapicid; - printk(KERN_INFO"Total of %d processors activated (%lu.%02lu BogoMIPS).\n", - cpucount + 1, bogosum/(500000/HZ), (bogosum/(5000/HZ))%100); - } - } - smp_done: - ; + sapicid = ia64_cpu_to_sapicid[cpu]; + if (sapicid == -1) + return -EINVAL; + + printk(KERN_INFO "Processor %d/%d is spinning up...\n", sapicid, cpu); + + /* Processor goes to start_secondary(), sets online flag */ + ret = do_boot_cpu(sapicid, cpu); + if (ret < 0) + return ret; + + printk(KERN_INFO "Processor %d has spun up...\n", cpu); + return 0; } /* @@ -571,9 +548,6 @@ ap_startup = (struct fptr *) start_ap; sal_ret = ia64_sal_set_vectors(SAL_VECTOR_OS_BOOT_RENDEZ, __pa(ap_startup->fp), __pa(ap_startup->gp), 0, 0, 0, 0); - if (sal_ret < 0) { - printk("SMP: Can't set SAL AP Boot Rendezvous: %s\n Forcing UP mode\n", - ia64_sal_strerror(sal_ret)); - max_cpus = 0; - } + if (sal_ret < 0) + printk("SMP: Can't set SAL AP Boot Rendezvous: %s\n", ia64_sal_strerror(sal_ret)); } diff -Nru a/arch/ia64/kernel/sys_ia64.c b/arch/ia64/kernel/sys_ia64.c --- a/arch/ia64/kernel/sys_ia64.c Fri Aug 16 14:34:58 2002 +++ b/arch/ia64/kernel/sys_ia64.c Fri Aug 16 14:34:58 2002 @@ -82,7 +82,6 @@ ia64_shmat (int shmid, void *shmaddr, int shmflg, long arg3, long arg4, long arg5, long arg6, long arg7, long stack) { - extern int sys_shmat (int shmid, char *shmaddr, int shmflg, ulong *raddr); struct pt_regs *regs = (struct pt_regs *) &stack; unsigned long raddr; int retval; @@ -136,10 +135,6 @@ /* Check against existing mmap mappings. */ if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE)) - goto out; - - /* Check if we have enough memory.. */ - if (!vm_enough_memory((newbrk-oldbrk) >> PAGE_SHIFT)) goto out; /* Ok, looks good - let it rip. */ diff -Nru a/arch/ia64/kernel/time.c b/arch/ia64/kernel/time.c --- a/arch/ia64/kernel/time.c Fri Aug 16 14:34:54 2002 +++ b/arch/ia64/kernel/time.c Fri Aug 16 14:34:54 2002 @@ -41,21 +41,22 @@ extern unsigned long prof_cpu_mask; extern char _stext; + if (!prof_buffer) + return; + if (!((1UL << smp_processor_id()) & prof_cpu_mask)) return; - if (prof_buffer && current->pid) { - ip -= (unsigned long) &_stext; - ip >>= prof_shift; - /* - * Don't ignore out-of-bounds IP values silently, put them into the last - * histogram slot, so if present, they will show up as a sharp peak. - */ - if (ip > prof_len - 1) - ip = prof_len - 1; + ip -= (unsigned long) &_stext; + ip >>= prof_shift; + /* + * Don't ignore out-of-bounds IP values silently, put them into the last + * histogram slot, so if present, they will show up as a sharp peak. + */ + if (ip > prof_len - 1) + ip = prof_len - 1; - atomic_inc((atomic_t *) &prof_buffer[ip]); - } + atomic_inc((atomic_t *) &prof_buffer[ip]); } /* @@ -285,9 +286,9 @@ } static struct irqaction timer_irqaction = { - handler: timer_interrupt, - flags: SA_INTERRUPT, - name: "timer" + .handler = timer_interrupt, + .flags = SA_INTERRUPT, + .name = "timer" }; void __init diff -Nru a/arch/ia64/kernel/traps.c b/arch/ia64/kernel/traps.c --- a/arch/ia64/kernel/traps.c Fri Aug 16 14:35:01 2002 +++ b/arch/ia64/kernel/traps.c Fri Aug 16 14:35:01 2002 @@ -49,10 +49,15 @@ void __init trap_init (void) { - printk("fpswa interface at %lx\n", ia64_boot_param->fpswa); - if (ia64_boot_param->fpswa) + int major = 0, minor = 0; + + if (ia64_boot_param->fpswa) { /* FPSWA fixup: make the interface pointer a kernel virtual address: */ fpswa_interface = __va(ia64_boot_param->fpswa); + major = fpswa_interface->revision & 0xffff; + minor = fpswa_interface->revision >> 16; + } + printk("fpswa interface at %lx (rev %d.%d)\n", ia64_boot_param->fpswa, major, minor); } /* @@ -62,27 +67,26 @@ void bust_spinlocks (int yes) { + int loglevel_save = console_loglevel; + spin_lock_init(&timerlist_lock); if (yes) { oops_in_progress = 1; -#ifdef CONFIG_SMP - global_irq_lock = 0; /* Many serial drivers do __global_cli() */ -#endif - } else { - int loglevel_save = console_loglevel; + return; + } + #ifdef CONFIG_VT - unblank_screen(); + unblank_screen(); #endif - oops_in_progress = 0; - /* - * OK, the message is on the console. Now we call printk() without - * oops_in_progress set so that printk will give klogd a poke. Hold onto - * your hats... - */ - console_loglevel = 15; /* NMI oopser may have shut the console up */ - printk(" "); - console_loglevel = loglevel_save; - } + oops_in_progress = 0; + /* + * OK, the message is on the console. Now we call printk() without + * oops_in_progress set so that printk will give klogd a poke. Hold onto + * your hats... + */ + console_loglevel = 15; /* NMI oopser may have shut the console up */ + printk(" "); + console_loglevel = loglevel_save; } void @@ -93,9 +97,9 @@ int lock_owner; int lock_owner_depth; } die = { - lock: SPIN_LOCK_UNLOCKED, - lock_owner: -1, - lock_owner_depth: 0 + .lock = SPIN_LOCK_UNLOCKED, + .lock_owner = -1, + .lock_owner_depth = 0 }; if (die.lock_owner != smp_processor_id()) { @@ -131,6 +135,8 @@ siginfo_t siginfo; int sig, code; + die_if_kernel("bad break", regs, break_num); + /* SIGILL, SIGFPE, SIGSEGV, and SIGBUS want these field initialized: */ siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri); siginfo.si_imm = break_num; @@ -247,10 +253,9 @@ if (fpu_owner) ia64_flush_fph(fpu_owner); - - ia64_set_fpu_owner(current); } #endif /* !CONFIG_SMP */ + ia64_set_fpu_owner(current); if ((current->thread.flags & IA64_THREAD_FPH_VALID) != 0) { __ia64_load_fpu(current->thread.fph); psr->mfh = 0; @@ -435,7 +440,7 @@ unsigned long code, error = isr; struct siginfo siginfo; char buf[128]; - int result; + int result, sig; static const char *reason[] = { "IA-64 Illegal Operation fault", "IA-64 Privileged Operation fault", @@ -479,6 +484,30 @@ break; case 26: /* NaT Consumption */ + if (user_mode(regs)) { + if (((isr >> 4) & 0xf) == 2) { + /* NaT page consumption */ + sig = SIGSEGV; + code = SEGV_ACCERR; + } else { + /* register NaT consumption */ + sig = SIGILL; + code = ILL_ILLOPN; + } + siginfo.si_signo = sig; + siginfo.si_code = code; + siginfo.si_errno = 0; + siginfo.si_addr = (void *) (regs->cr_iip + ia64_psr(regs)->ri); + siginfo.si_imm = vector; + siginfo.si_flags = __ISR_VALID; + siginfo.si_isr = isr; + force_sig_info(sig, &siginfo, current); + return; + } else if (done_with_exception(regs)) + return; + sprintf(buf, "NaT consumption"); + break; + case 31: /* Unsupported Data Reference */ if (user_mode(regs)) { siginfo.si_signo = SIGILL; @@ -491,7 +520,7 @@ force_sig_info(SIGILL, &siginfo, current); return; } - sprintf(buf, (vector == 26) ? "NaT consumption" : "Unsupported data reference"); + sprintf(buf, "Unsupported data reference"); break; case 29: /* Debug */ @@ -508,16 +537,15 @@ if (ia64_psr(regs)->is == 0) ifa = regs->cr_iip; #endif - siginfo.si_addr = (void *) ifa; break; - case 35: siginfo.si_code = TRAP_BRANCH; break; - case 36: siginfo.si_code = TRAP_TRACE; break; + case 35: siginfo.si_code = TRAP_BRANCH; ifa = 0; break; + case 36: siginfo.si_code = TRAP_TRACE; ifa = 0; break; } siginfo.si_signo = SIGTRAP; siginfo.si_errno = 0; siginfo.si_flags = 0; siginfo.si_isr = 0; - siginfo.si_addr = 0; + siginfo.si_addr = (void *) ifa; siginfo.si_imm = 0; force_sig_info(SIGTRAP, &siginfo, current); return; diff -Nru a/arch/ia64/kernel/unwind.c b/arch/ia64/kernel/unwind.c --- a/arch/ia64/kernel/unwind.c Fri Aug 16 14:35:01 2002 +++ b/arch/ia64/kernel/unwind.c Fri Aug 16 14:35:01 2002 @@ -140,13 +140,13 @@ } stat; # endif } unw = { - tables: &unw.kernel_table, - lock: SPIN_LOCK_UNLOCKED, - save_order: { + .tables = &unw.kernel_table, + .lock = SPIN_LOCK_UNLOCKED, + .save_order = { UNW_REG_RP, UNW_REG_PFS, UNW_REG_PSP, UNW_REG_PR, UNW_REG_UNAT, UNW_REG_LC, UNW_REG_FPSR, UNW_REG_PRI_UNAT_GR }, - preg_index: { + .preg_index = { struct_offset(struct unw_frame_info, pri_unat_loc)/8, /* PRI_UNAT_GR */ struct_offset(struct unw_frame_info, pri_unat_loc)/8, /* PRI_UNAT_MEM */ struct_offset(struct unw_frame_info, bsp_loc)/8, @@ -189,9 +189,9 @@ struct_offset(struct unw_frame_info, fr_loc[30 - 16])/8, struct_offset(struct unw_frame_info, fr_loc[31 - 16])/8, }, - hash : { [0 ... UNW_HASH_SIZE - 1] = -1 }, + .hash = { [0 ... UNW_HASH_SIZE - 1] = -1 }, #if UNW_DEBUG - preg_name: { + .preg_name = { "pri_unat_gr", "pri_unat_mem", "bsp", "bspstore", "ar.pfs", "ar.rnat", "psp", "rp", "r4", "r5", "r6", "r7", "ar.unat", "pr", "ar.lc", "ar.fpsr", @@ -634,8 +634,8 @@ for (reg = hi; reg >= lo; --reg) { if (reg->where == UNW_WHERE_SPILL_HOME) { reg->where = UNW_WHERE_PSPREL; - reg->val = 0x10 - *offp; - *offp += regsize; + *offp -= regsize; + reg->val = *offp; } } } @@ -814,7 +814,8 @@ } for (i = 0; i < 20; ++i) { if ((frmask & 1) != 0) { - set_reg(sr->curr.reg + UNW_REG_F2 + i, UNW_WHERE_SPILL_HOME, + int base = (i < 4) ? UNW_REG_F2 : UNW_REG_F16 - 4; + set_reg(sr->curr.reg + base + i, UNW_WHERE_SPILL_HOME, sr->region_start + sr->region_len - 1, 0); sr->any_spills = 1; } diff -Nru a/arch/ia64/lib/Makefile b/arch/ia64/lib/Makefile --- a/arch/ia64/lib/Makefile Fri Aug 16 14:35:01 2002 +++ b/arch/ia64/lib/Makefile Fri Aug 16 14:35:01 2002 @@ -6,43 +6,51 @@ export-objs := swiotlb.o -obj-y := __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o \ - __divdi3.o __udivdi3.o __moddi3.o __umoddi3.o \ - checksum.o clear_page.o csum_partial_copy.o copy_page.o \ - copy_user.o clear_user.o strncpy_from_user.o strlen_user.o strnlen_user.o \ - flush.o io.o ip_fast_csum.o do_csum.o \ - memcpy.o memset.o strlen.o swiotlb.o +obj-y := __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o \ + __divdi3.o __udivdi3.o __moddi3.o __umoddi3.o \ + checksum.o clear_page.o csum_partial_copy.o copy_page.o \ + clear_user.o strncpy_from_user.o strlen_user.o strnlen_user.o \ + flush.o io.o ip_fast_csum.o do_csum.o \ + memset.o strlen.o swiotlb.o -obj-$(CONFIG_ITANIUM) += copy_page.o -obj-$(CONFIG_MCKINLEY) += copy_page_mck.o +obj-$(CONFIG_ITANIUM) += copy_page.o copy_user.o memcpy.o +obj-$(CONFIG_MCKINLEY) += copy_page_mck.o memcpy_mck.o IGNORE_FLAGS_OBJS = __divsi3.o __udivsi3.o __modsi3.o __umodsi3.o \ __divdi3.o __udivdi3.o __moddi3.o __umoddi3.o -$(L_TARGET): $(obj-y) $(export-objs) +include $(TOPDIR)/Rules.make + +AFLAGS___divdi3.o = +AFLAGS___udivdi3.o = -DUNSIGNED +AFLAGS___moddi3.o = -DMODULO +AFLAGS___umoddi3.o = -DUNSIGNED -DMODULO + +AFLAGS___divsi3.o = +AFLAGS___udivsi3.o = -DUNSIGNED +AFLAGS___modsi3.o = -DMODULO +AFLAGS___umodsi3.o = -DUNSIGNED -DMODULO __divdi3.o: idiv64.S - $(CC) $(AFLAGS) -c -o $@ $< + $(cmd_as_o_S) __udivdi3.o: idiv64.S - $(CC) $(AFLAGS) -c -DUNSIGNED -c -o $@ $< + $(cmd_as_o_S) __moddi3.o: idiv64.S - $(CC) $(AFLAGS) -c -DMODULO -c -o $@ $< + $(cmd_as_o_S) __umoddi3.o: idiv64.S - $(CC) $(AFLAGS) -c -DMODULO -DUNSIGNED -c -o $@ $< + $(cmd_as_o_S) __divsi3.o: idiv32.S - $(CC) $(AFLAGS) -c -o $@ $< + $(cmd_as_o_S) __udivsi3.o: idiv32.S - $(CC) $(AFLAGS) -c -DUNSIGNED -c -o $@ $< + $(cmd_as_o_S) __modsi3.o: idiv32.S - $(CC) $(AFLAGS) -c -DMODULO -c -o $@ $< + $(cmd_as_o_S) __umodsi3.o: idiv32.S - $(CC) $(AFLAGS) -c -DMODULO -DUNSIGNED -c -o $@ $< - -include $(TOPDIR)/Rules.make + $(cmd_as_o_S) diff -Nru a/arch/ia64/lib/copy_user.S b/arch/ia64/lib/copy_user.S --- a/arch/ia64/lib/copy_user.S Fri Aug 16 14:34:54 2002 +++ b/arch/ia64/lib/copy_user.S Fri Aug 16 14:34:54 2002 @@ -237,15 +237,17 @@ .copy_user_bit##rshift: \ 1: \ EX(.failure_out,(EPI) st8 [dst1]=tmp,8); \ -(EPI_1) shrp tmp=val1[PIPE_DEPTH-3],val1[PIPE_DEPTH-2],rshift; \ - EX(3f,(p16) ld8 val1[0]=[src1],8); \ +(EPI_1) shrp tmp=val1[PIPE_DEPTH-2],val1[PIPE_DEPTH-1],rshift; \ + EX(3f,(p16) ld8 val1[1]=[src1],8); \ +(p16) mov val1[0]=r0; \ br.ctop.dptk 1b; \ ;; \ br.cond.sptk.many .diff_align_do_tail; \ 2: \ (EPI) st8 [dst1]=tmp,8; \ -(EPI_1) shrp tmp=val1[PIPE_DEPTH-3],val1[PIPE_DEPTH-2],rshift; \ +(EPI_1) shrp tmp=val1[PIPE_DEPTH-2],val1[PIPE_DEPTH-1],rshift; \ 3: \ +(p16) mov val1[1]=r0; \ (p16) mov val1[0]=r0; \ br.ctop.dptk 2b; \ ;; \ diff -Nru a/arch/ia64/lib/io.c b/arch/ia64/lib/io.c --- a/arch/ia64/lib/io.c Fri Aug 16 14:34:51 2002 +++ b/arch/ia64/lib/io.c Fri Aug 16 14:34:51 2002 @@ -87,6 +87,12 @@ __ia64_outl(val, port); } +void +ia64_mmiob (void) +{ + __ia64_mmiob(); +} + /* define aliases: */ asm (".global __ia64_inb, __ia64_inw, __ia64_inl"); @@ -98,5 +104,8 @@ asm ("__ia64_outb = ia64_outb"); asm ("__ia64_outw = ia64_outw"); asm ("__ia64_outl = ia64_outl"); + +asm (".global __ia64_mmiob"); +asm ("__ia64_mmiob = ia64_mmiob"); #endif /* CONFIG_IA64_GENERIC */ diff -Nru a/arch/ia64/lib/memcpy_mck.S b/arch/ia64/lib/memcpy_mck.S --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/arch/ia64/lib/memcpy_mck.S Fri Aug 16 14:35:01 2002 @@ -0,0 +1,674 @@ +/* + * Itanium 2-optimized version of memcpy and copy_user function + * + * Inputs: + * in0: destination address + * in1: source address + * in2: number of bytes to copy + * Output: + * 0 if success, or number of byte NOT copied if error occurred. + * + * Copyright (C) 2002 Intel Corp. + * Copyright (C) 2002 Ken Chen + */ +#include +#include +#include + +#if __GNUC__ >= 3 +# define EK(y...) EX(y) +#else +# define EK(y,x...) x +#endif + +GLOBAL_ENTRY(bcopy) + .regstk 3,0,0,0 + mov r8=in0 + mov in0=in1 + ;; + mov in1=r8 + ;; +END(bcopy) + +/* McKinley specific optimization */ + +#define retval r8 +#define saved_pfs r31 +#define saved_lc r10 +#define saved_pr r11 +#define saved_in0 r14 +#define saved_in1 r15 +#define saved_in2 r16 + +#define src0 r2 +#define src1 r3 +#define dst0 r17 +#define dst1 r18 +#define cnt r9 + +/* r19-r30 are temp for each code section */ +#define PREFETCH_DIST 8 +#define src_pre_mem r19 +#define dst_pre_mem r20 +#define src_pre_l2 r21 +#define dst_pre_l2 r22 +#define t1 r23 +#define t2 r24 +#define t3 r25 +#define t4 r26 +#define t5 t1 // alias! +#define t6 t2 // alias! +#define t7 t3 // alias! +#define n8 r27 +#define t9 t5 // alias! +#define t10 t4 // alias! +#define t11 t7 // alias! +#define t12 t6 // alias! +#define t14 t10 // alias! +#define t13 r28 +#define t15 r29 +#define tmp r30 + +/* defines for long_copy block */ +#define A 0 +#define B (PREFETCH_DIST) +#define C (B + PREFETCH_DIST) +#define D (C + 1) +#define N (D + 1) +#define Nrot ((N + 7) & ~7) + +/* alias */ +#define in0 r32 +#define in1 r33 +#define in2 r34 + +GLOBAL_ENTRY(memcpy) + and r28=0x7,in0 + and r29=0x7,in1 + mov f6=f0 + br.cond.sptk .common_code + ;; +GLOBAL_ENTRY(__copy_user) + .prologue +// check dest alignment + and r28=0x7,in0 + and r29=0x7,in1 + mov f6=f1 + mov saved_in0=in0 // save dest pointer + mov saved_in1=in1 // save src pointer + mov saved_in2=in2 // save len + ;; +.common_code: + cmp.gt p15,p0=8,in2 // check for small size + cmp.ne p13,p0=0,r28 // check dest alignment + cmp.ne p14,p0=0,r29 // check src alignment + add src0=0,in1 + sub r30=8,r28 // for .align_dest + mov retval=r0 // initialize return value + ;; + add dst0=0,in0 + add dst1=1,in0 // dest odd index + cmp.le p6,p0 = 1,r30 // for .align_dest +(p15) br.cond.dpnt .memcpy_short +(p13) br.cond.dpnt .align_dest +(p14) br.cond.dpnt .unaligned_src + ;; + +// both dest and src are aligned on 8-byte boundary +.aligned_src: + .save ar.pfs, saved_pfs + alloc saved_pfs=ar.pfs,3,Nrot-3,0,Nrot + .save pr, saved_pr + mov saved_pr=pr + + shr.u cnt=in2,7 // this much cache line + ;; + cmp.lt p6,p0=2*PREFETCH_DIST,cnt + cmp.lt p7,p8=1,cnt + .save ar.lc, saved_lc + mov saved_lc=ar.lc + .body + add cnt=-1,cnt + add src_pre_mem=0,in1 // prefetch src pointer + add dst_pre_mem=0,in0 // prefetch dest pointer + ;; +(p7) mov ar.lc=cnt // prefetch count +(p8) mov ar.lc=r0 +(p6) br.cond.dpnt .long_copy + ;; + +.prefetch: + lfetch.fault [src_pre_mem], 128 + lfetch.fault.excl [dst_pre_mem], 128 + br.cloop.dptk.few .prefetch + ;; + +.medium_copy: + and tmp=31,in2 // copy length after iteration + shr.u r29=in2,5 // number of 32-byte iteration + add dst1=8,dst0 // 2nd dest pointer + ;; + add cnt=-1,r29 // ctop iteration adjustment + cmp.eq p10,p0=r29,r0 // do we really need to loop? + add src1=8,src0 // 2nd src pointer + cmp.le p6,p0=8,tmp + ;; + cmp.le p7,p0=16,tmp + mov ar.lc=cnt // loop setup + cmp.eq p16,p17 = r0,r0 + mov ar.ec=2 +(p10) br.dpnt.few .aligned_src_tail + ;; + .align 32 +1: +EX(.ex_handler, (p16) ld8 r34=[src0],16) +EK(.ex_handler, (p16) ld8 r38=[src1],16) +EX(.ex_handler, (p17) st8 [dst0]=r33,16) +EK(.ex_handler, (p17) st8 [dst1]=r37,16) + ;; +EX(.ex_handler, (p16) ld8 r32=[src0],16) +EK(.ex_handler, (p16) ld8 r36=[src1],16) +EX(.ex_handler, (p16) st8 [dst0]=r34,16) +EK(.ex_handler, (p16) st8 [dst1]=r38,16) + br.ctop.dptk.few 1b + ;; + +.aligned_src_tail: +EX(.ex_handler, (p6) ld8 t1=[src0]) + mov ar.lc=saved_lc + mov ar.pfs=saved_pfs +EX(.ex_hndlr_s, (p7) ld8 t2=[src1],8) + cmp.le p8,p0=24,tmp + and r21=-8,tmp + ;; +EX(.ex_hndlr_s, (p8) ld8 t3=[src1]) +EX(.ex_handler, (p6) st8 [dst0]=t1) // store byte 1 + and in2=7,tmp // remaining length +EX(.ex_hndlr_d, (p7) st8 [dst1]=t2,8) // store byte 2 + add src0=src0,r21 // setting up src pointer + add dst0=dst0,r21 // setting up dest pointer + ;; +EX(.ex_handler, (p8) st8 [dst1]=t3) // store byte 3 + mov pr=saved_pr,-1 + br.dptk.many .memcpy_short + ;; + +/* code taken from copy_page_mck */ +.long_copy: + .rotr v[2*PREFETCH_DIST] + .rotp p[N] + + mov src_pre_mem = src0 + mov pr.rot = 0x10000 + mov ar.ec = 1 // special unrolled loop + + mov dst_pre_mem = dst0 + + add src_pre_l2 = 8*8, src0 + add dst_pre_l2 = 8*8, dst0 + ;; + add src0 = 8, src_pre_mem // first t1 src + mov ar.lc = 2*PREFETCH_DIST - 1 + shr.u cnt=in2,7 // number of lines + add src1 = 3*8, src_pre_mem // first t3 src + add dst0 = 8, dst_pre_mem // first t1 dst + add dst1 = 3*8, dst_pre_mem // first t3 dst + ;; + and tmp=127,in2 // remaining bytes after this block + add cnt = -(2*PREFETCH_DIST) - 1, cnt + // same as .line_copy loop, but with all predicated-off instructions removed: +.prefetch_loop: +EX(.ex_hndlr_lcpy_1, (p[A]) ld8 v[A] = [src_pre_mem], 128) // M0 +EK(.ex_hndlr_lcpy_1, (p[B]) st8 [dst_pre_mem] = v[B], 128) // M2 + br.ctop.sptk .prefetch_loop + ;; + cmp.eq p16, p0 = r0, r0 // reset p16 to 1 + mov ar.lc = cnt + mov ar.ec = N // # of stages in pipeline + ;; +.line_copy: +EX(.ex_handler, (p[D]) ld8 t2 = [src0], 3*8) // M0 +EK(.ex_handler, (p[D]) ld8 t4 = [src1], 3*8) // M1 +EX(.ex_handler_lcpy, (p[B]) st8 [dst_pre_mem] = v[B], 128) // M2 prefetch dst from memory +EK(.ex_handler_lcpy, (p[D]) st8 [dst_pre_l2] = n8, 128) // M3 prefetch dst from L2 + ;; +EX(.ex_handler_lcpy, (p[A]) ld8 v[A] = [src_pre_mem], 128) // M0 prefetch src from memory +EK(.ex_handler_lcpy, (p[C]) ld8 n8 = [src_pre_l2], 128) // M1 prefetch src from L2 +EX(.ex_handler, (p[D]) st8 [dst0] = t1, 8) // M2 +EK(.ex_handler, (p[D]) st8 [dst1] = t3, 8) // M3 + ;; +EX(.ex_handler, (p[D]) ld8 t5 = [src0], 8) +EK(.ex_handler, (p[D]) ld8 t7 = [src1], 3*8) +EX(.ex_handler, (p[D]) st8 [dst0] = t2, 3*8) +EK(.ex_handler, (p[D]) st8 [dst1] = t4, 3*8) + ;; +EX(.ex_handler, (p[D]) ld8 t6 = [src0], 3*8) +EK(.ex_handler, (p[D]) ld8 t10 = [src1], 8) +EX(.ex_handler, (p[D]) st8 [dst0] = t5, 8) +EK(.ex_handler, (p[D]) st8 [dst1] = t7, 3*8) + ;; +EX(.ex_handler, (p[D]) ld8 t9 = [src0], 3*8) +EK(.ex_handler, (p[D]) ld8 t11 = [src1], 3*8) +EX(.ex_handler, (p[D]) st8 [dst0] = t6, 3*8) +EK(.ex_handler, (p[D]) st8 [dst1] = t10, 8) + ;; +EX(.ex_handler, (p[D]) ld8 t12 = [src0], 8) +EK(.ex_handler, (p[D]) ld8 t14 = [src1], 8) +EX(.ex_handler, (p[D]) st8 [dst0] = t9, 3*8) +EK(.ex_handler, (p[D]) st8 [dst1] = t11, 3*8) + ;; +EX(.ex_handler, (p[D]) ld8 t13 = [src0], 4*8) +EK(.ex_handler, (p[D]) ld8 t15 = [src1], 4*8) +EX(.ex_handler, (p[D]) st8 [dst0] = t12, 8) +EK(.ex_handler, (p[D]) st8 [dst1] = t14, 8) + ;; +EX(.ex_handler, (p[C]) ld8 t1 = [src0], 8) +EK(.ex_handler, (p[C]) ld8 t3 = [src1], 8) +EX(.ex_handler, (p[D]) st8 [dst0] = t13, 4*8) +EK(.ex_handler, (p[D]) st8 [dst1] = t15, 4*8) + br.ctop.sptk .line_copy + ;; + + add dst0=-8,dst0 + add src0=-8,src0 + mov in2=tmp + .restore sp + br.sptk.many .medium_copy + ;; + +#define BLOCK_SIZE 128*32 +#define blocksize r23 +#define curlen r24 + +// dest is on 8-byte boundary, src is not. We need to do +// ld8-ld8, shrp, then st8. Max 8 byte copy per cycle. +.unaligned_src: + .prologue + .save ar.pfs, saved_pfs + alloc saved_pfs=ar.pfs,3,5,0,8 + .save ar.lc, saved_lc + mov saved_lc=ar.lc + .save pr, saved_pr + mov saved_pr=pr + .body +.4k_block: + mov saved_in0=dst0 // need to save all input arguments + mov saved_in2=in2 + mov blocksize=BLOCK_SIZE + ;; + cmp.lt p6,p7=blocksize,in2 + mov saved_in1=src0 + ;; +(p6) mov in2=blocksize + ;; + shr.u r21=in2,7 // this much cache line + shr.u r22=in2,4 // number of 16-byte iteration + and curlen=15,in2 // copy length after iteration + and r30=7,src0 // source alignment + ;; + cmp.lt p7,p8=1,r21 + add cnt=-1,r21 + ;; + + add src_pre_mem=0,src0 // prefetch src pointer + add dst_pre_mem=0,dst0 // prefetch dest pointer + and src0=-8,src0 // 1st src pointer +(p7) mov ar.lc = r21 +(p8) mov ar.lc = r0 + ;; + .align 32 +1: lfetch.fault [src_pre_mem], 128 + lfetch.fault.excl [dst_pre_mem], 128 + br.cloop.dptk.few 1b + ;; + + shladd dst1=r22,3,dst0 // 2nd dest pointer + shladd src1=r22,3,src0 // 2nd src pointer + cmp.eq p8,p9=r22,r0 // do we really need to loop? + cmp.le p6,p7=8,curlen; // have at least 8 byte remaining? + add cnt=-1,r22 // ctop iteration adjustment + ;; +EX(.ex_handler, (p9) ld8 r33=[src0],8) // loop primer +EK(.ex_handler, (p9) ld8 r37=[src1],8) +(p8) br.dpnt.few .noloop + ;; + +// The jump address is calculated based on src alignment. The COPYU +// macro below need to confine its size to power of two, so an entry +// can be caulated using shl instead of an expensive multiply. The +// size is then hard coded by the following #define to match the +// actual size. This make it somewhat tedious when COPYU macro gets +// changed and this need to be adjusted to match. +#define LOOP_SIZE 6 +1: + mov r29=ip // jmp_table thread + mov ar.lc=cnt + ;; + add r29=.jump_table - 1b - (.jmp1-.jump_table), r29 + shl r28=r30, LOOP_SIZE // jmp_table thread + mov ar.ec=2 // loop setup + ;; + add r29=r29,r28 // jmp_table thread + cmp.eq p16,p17=r0,r0 + ;; + mov b6=r29 // jmp_table thread + ;; + br.cond.sptk.few b6 + +// for 8-15 byte case +// We will skip the loop, but need to replicate the side effect +// that the loop produces. +.noloop: +EX(.ex_handler, (p6) ld8 r37=[src1],8) + add src0=8,src0 +(p6) shl r25=r30,3 + ;; +EX(.ex_handler, (p6) ld8 r27=[src1]) +(p6) shr.u r28=r37,r25 +(p6) sub r26=64,r25 + ;; +(p6) shl r27=r27,r26 + ;; +(p6) or r21=r28,r27 + +.unaligned_src_tail: +/* check if we have more than blocksize to copy, if so go back */ + cmp.gt p8,p0=saved_in2,blocksize + ;; +(p8) add dst0=saved_in0,blocksize +(p8) add src0=saved_in1,blocksize +(p8) sub in2=saved_in2,blocksize +(p8) br.dpnt .4k_block + ;; + +/* we have up to 15 byte to copy in the tail. + * part of work is already done in the jump table code + * we are at the following state. + * src side: + * + * xxxxxx xx <----- r21 has xxxxxxxx already + * -------- -------- -------- + * 0 8 16 + * ^ + * | + * src1 + * + * dst + * -------- -------- -------- + * ^ + * | + * dst1 + */ +EX(.ex_handler, (p6) st8 [dst1]=r21,8) // more than 8 byte to copy +(p6) add curlen=-8,curlen // update length + mov ar.pfs=saved_pfs + ;; + mov ar.lc=saved_lc + mov pr=saved_pr,-1 + mov in2=curlen // remaining length + mov dst0=dst1 // dest pointer + add src0=src1,r30 // forward by src alignment + ;; + +// 7 byte or smaller. +.memcpy_short: + cmp.le p8,p9 = 1,in2 + cmp.le p10,p11 = 2,in2 + cmp.le p12,p13 = 3,in2 + cmp.le p14,p15 = 4,in2 + add src1=1,src0 // second src pointer + add dst1=1,dst0 // second dest pointer + ;; + +EX(.ex_handler_short, (p8) ld1 t1=[src0],2) +EK(.ex_handler_short, (p10) ld1 t2=[src1],2) +(p9) br.ret.dpnt rp // 0 byte copy + ;; + +EX(.ex_handler_short, (p8) st1 [dst0]=t1,2) +EK(.ex_handler_short, (p10) st1 [dst1]=t2,2) +(p11) br.ret.dpnt rp // 1 byte copy + +EX(.ex_handler_short, (p12) ld1 t3=[src0],2) +EK(.ex_handler_short, (p14) ld1 t4=[src1],2) +(p13) br.ret.dpnt rp // 2 byte copy + ;; + + cmp.le p6,p7 = 5,in2 + cmp.le p8,p9 = 6,in2 + cmp.le p10,p11 = 7,in2 + +EX(.ex_handler_short, (p12) st1 [dst0]=t3,2) +EK(.ex_handler_short, (p14) st1 [dst1]=t4,2) +(p15) br.ret.dpnt rp // 3 byte copy + ;; + +EX(.ex_handler_short, (p6) ld1 t5=[src0],2) +EK(.ex_handler_short, (p8) ld1 t6=[src1],2) +(p7) br.ret.dpnt rp // 4 byte copy + ;; + +EX(.ex_handler_short, (p6) st1 [dst0]=t5,2) +EK(.ex_handler_short, (p8) st1 [dst1]=t6,2) +(p9) br.ret.dptk rp // 5 byte copy + +EX(.ex_handler_short, (p10) ld1 t7=[src0],2) +(p11) br.ret.dptk rp // 6 byte copy + ;; + +EX(.ex_handler_short, (p10) st1 [dst0]=t7,2) + br.ret.dptk rp // done all cases + + +/* Align dest to nearest 8-byte boundary. We know we have at + * least 7 bytes to copy, enough to crawl to 8-byte boundary. + * Actual number of byte to crawl depend on the dest alignment. + * 7 byte or less is taken care at .memcpy_short + + * src0 - source even index + * src1 - source odd index + * dst0 - dest even index + * dst1 - dest odd index + * r30 - distance to 8-byte boundary + */ + +.align_dest: + add src1=1,in1 // source odd index + cmp.le p7,p0 = 2,r30 // for .align_dest + cmp.le p8,p0 = 3,r30 // for .align_dest +EX(.ex_handler_short, (p6) ld1 t1=[src0],2) + cmp.le p9,p0 = 4,r30 // for .align_dest + cmp.le p10,p0 = 5,r30 + ;; +EX(.ex_handler_short, (p7) ld1 t2=[src1],2) +EK(.ex_handler_short, (p8) ld1 t3=[src0],2) + cmp.le p11,p0 = 6,r30 +EX(.ex_handler_short, (p6) st1 [dst0] = t1,2) + cmp.le p12,p0 = 7,r30 + ;; +EX(.ex_handler_short, (p9) ld1 t4=[src1],2) +EK(.ex_handler_short, (p10) ld1 t5=[src0],2) +EX(.ex_handler_short, (p7) st1 [dst1] = t2,2) +EK(.ex_handler_short, (p8) st1 [dst0] = t3,2) + ;; +EX(.ex_handler_short, (p11) ld1 t6=[src1],2) +EK(.ex_handler_short, (p12) ld1 t7=[src0],2) + cmp.eq p6,p7=r28,r29 +EX(.ex_handler_short, (p9) st1 [dst1] = t4,2) +EK(.ex_handler_short, (p10) st1 [dst0] = t5,2) + sub in2=in2,r30 + ;; +EX(.ex_handler_short, (p11) st1 [dst1] = t6,2) +EK(.ex_handler_short, (p12) st1 [dst0] = t7) + add dst0=in0,r30 // setup arguments + add src0=in1,r30 +(p6) br.cond.dptk .aligned_src +(p7) br.cond.dpnt .unaligned_src + ;; + +/* main loop body in jump table format */ +#define COPYU(shift) \ +1: \ +EX(.ex_handler, (p16) ld8 r32=[src0],8); /* 1 */ \ +EK(.ex_handler, (p16) ld8 r36=[src1],8); \ + (p17) shrp r35=r33,r34,shift;; /* 1 */ \ +EX(.ex_handler, (p6) ld8 r22=[src1]); /* common, prime for tail section */ \ + nop.m 0; \ + (p16) shrp r38=r36,r37,shift; \ +EX(.ex_handler, (p17) st8 [dst0]=r35,8); /* 1 */ \ +EK(.ex_handler, (p17) st8 [dst1]=r39,8); \ + br.ctop.dptk.few 1b;; \ + (p7) add src1=-8,src1; /* back out for <8 byte case */ \ + shrp r21=r22,r38,shift; /* speculative work */ \ + br.sptk.few .unaligned_src_tail /* branch out of jump table */ \ + ;; + .align 32 +.jump_table: + COPYU(8) // unaligned cases +.jmp1: + COPYU(16) + COPYU(24) + COPYU(32) + COPYU(40) + COPYU(48) + COPYU(56) + +#undef A +#undef B +#undef C +#undef D +END(memcpy) + +/* + * Due to lack of local tag support in gcc 2.x assembler, it is not clear which + * instruction failed in the bundle. The exception algorithm is that we + * first figure out the faulting address, then detect if there is any + * progress made on the copy, if so, redo the copy from last known copied + * location up to the faulting address (exclusive). In the copy_from_user + * case, remaining byte in kernel buffer will be zeroed. + * + * Take copy_from_user as an example, in the code there are multiple loads + * in a bundle and those multiple loads could span over two pages, the + * faulting address is calculated as page_round_down(max(src0, src1)). + * This is based on knowledge that if we can access one byte in a page, we + * can access any byte in that page. + * + * predicate used in the exception handler: + * p6-p7: direction + * p10-p11: src faulting addr calculation + * p12-p13: dst faulting addr calculation + */ + +#define A r19 +#define B r20 +#define C r21 +#define D r22 +#define F r28 + +#define memset_arg0 r32 +#define memset_arg2 r33 + +#define saved_retval loc0 +#define saved_rtlink loc1 +#define saved_pfs_stack loc2 + +.ex_hndlr_s: + add src0=8,src0 + br.sptk .ex_handler + ;; +.ex_hndlr_d: + add dst0=8,dst0 + br.sptk .ex_handler + ;; +.ex_hndlr_lcpy_1: + mov src1=src_pre_mem + mov dst1=dst_pre_mem + cmp.gtu p10,p11=src_pre_mem,saved_in1 + cmp.gtu p12,p13=dst_pre_mem,saved_in0 + ;; +(p10) add src0=8,saved_in1 +(p11) mov src0=saved_in1 +(p12) add dst0=8,saved_in0 +(p13) mov dst0=saved_in0 + br.sptk .ex_handler +.ex_handler_lcpy: + // in line_copy block, the preload addresses should always ahead + // of the other two src/dst pointers. Furthermore, src1/dst1 should + // always ahead of src0/dst0. + mov src1=src_pre_mem + mov dst1=dst_pre_mem +.ex_handler: + mov pr=saved_pr,-1 // first restore pr, lc, and pfs + mov ar.lc=saved_lc + mov ar.pfs=saved_pfs + ;; +.ex_handler_short: // fault occurred in these sections didn't change pr, lc, pfs + cmp.ltu p6,p7=saved_in0, saved_in1 // get the copy direction + cmp.ltu p10,p11=src0,src1 + cmp.ltu p12,p13=dst0,dst1 + fcmp.eq p8,p0=f6,f0 // is it memcpy? + mov tmp = dst0 + ;; +(p11) mov src1 = src0 // pick the larger of the two +(p13) mov dst0 = dst1 // make dst0 the smaller one +(p13) mov dst1 = tmp // and dst1 the larger one + ;; +(p6) dep F = r0,dst1,0,PAGE_SHIFT // usr dst round down to page boundary +(p7) dep F = r0,src1,0,PAGE_SHIFT // usr src round down to page boundary + ;; +(p6) cmp.le p14,p0=dst0,saved_in0 // no progress has been made on store +(p7) cmp.le p14,p0=src0,saved_in1 // no progress has been made on load + mov retval=saved_in2 +(p8) ld1 tmp=[src1] // force an oops for memcpy call +(p8) st1 [dst1]=r0 // force an oops for memcpy call +(p14) br.ret.sptk.many rp + +/* + * The remaining byte to copy is calculated as: + * + * A = (faulting_addr - orig_src) -> len to faulting ld address + * or + * (faulting_addr - orig_dst) -> len to faulting st address + * B = (cur_dst - orig_dst) -> len copied so far + * C = A - B -> len need to be copied + * D = orig_len - A -> len need to be zeroed + */ +(p6) sub A = F, saved_in0 +(p7) sub A = F, saved_in1 + clrrrb + ;; + alloc saved_pfs_stack=ar.pfs,3,3,3,0 + sub B = dst0, saved_in0 // how many byte copied so far + ;; + sub C = A, B + sub D = saved_in2, A + ;; + cmp.gt p8,p0=C,r0 // more than 1 byte? + add memset_arg0=saved_in0, A +(p6) mov memset_arg2=0 // copy_to_user should not call memset +(p7) mov memset_arg2=D // copy_from_user need to have kbuf zeroed + mov r8=0 + mov saved_retval = D + mov saved_rtlink = b0 + + add out0=saved_in0, B + add out1=saved_in1, B + mov out2=C +(p8) br.call.sptk.few b0=__copy_user // recursive call + ;; + + add saved_retval=saved_retval,r8 // above might return non-zero value + cmp.gt p8,p0=memset_arg2,r0 // more than 1 byte? + mov out0=memset_arg0 // *s + mov out1=r0 // c + mov out2=memset_arg2 // n +(p8) br.call.sptk.few b0=memset + ;; + + mov retval=saved_retval + mov ar.pfs=saved_pfs_stack + mov b0=saved_rtlink + br.ret.sptk.many rp + +/* end of McKinley specific optimization */ +END(__copy_user) diff -Nru a/arch/ia64/lib/swiotlb.c b/arch/ia64/lib/swiotlb.c --- a/arch/ia64/lib/swiotlb.c Fri Aug 16 14:34:59 2002 +++ b/arch/ia64/lib/swiotlb.c Fri Aug 16 14:34:59 2002 @@ -415,18 +415,21 @@ swiotlb_map_sg (struct pci_dev *hwdev, struct scatterlist *sg, int nelems, int direction) { void *addr; + unsigned long pci_addr; int i; if (direction == PCI_DMA_NONE) BUG(); for (i = 0; i < nelems; i++, sg++) { - sg->orig_address = SG_ENT_VIRT_ADDRESS(sg); - if ((SG_ENT_PHYS_ADDRESS(sg) & ~hwdev->dma_mask) != 0) { - addr = map_single(hwdev, sg->orig_address, sg->length, direction); - sg->page = virt_to_page(addr); - sg->offset = (u64) addr & ~PAGE_MASK; - } + addr = SG_ENT_VIRT_ADDRESS(sg); + pci_addr = virt_to_phys(addr); + if ((pci_addr & ~hwdev->dma_mask) != 0) + sg->dma_address = (dma_addr_t) + map_single(hwdev, addr, sg->length, direction); + else + sg->dma_address = pci_addr; + sg->dma_length = sg->length; } return nelems; } @@ -444,12 +447,10 @@ BUG(); for (i = 0; i < nelems; i++, sg++) - if (sg->orig_address != SG_ENT_VIRT_ADDRESS(sg)) { - unmap_single(hwdev, SG_ENT_VIRT_ADDRESS(sg), sg->length, direction); - sg->page = virt_to_page(sg->orig_address); - sg->offset = (u64) sg->orig_address & ~PAGE_MASK; - } else if (direction == PCI_DMA_FROMDEVICE) - mark_clean(SG_ENT_VIRT_ADDRESS(sg), sg->length); + if (sg->dma_address != SG_ENT_PHYS_ADDRESS(sg)) + unmap_single(hwdev, (void *) sg->dma_address, sg->dma_length, direction); + else if (direction == PCI_DMA_FROMDEVICE) + mark_clean(SG_ENT_VIRT_ADDRESS(sg), sg->dma_length); } /* @@ -468,14 +469,14 @@ BUG(); for (i = 0; i < nelems; i++, sg++) - if (sg->orig_address != SG_ENT_VIRT_ADDRESS(sg)) - sync_single(hwdev, SG_ENT_VIRT_ADDRESS(sg), sg->length, direction); + if (sg->dma_address != SG_ENT_PHYS_ADDRESS(sg)) + sync_single(hwdev, (void *) sg->dma_address, sg->dma_length, direction); } unsigned long swiotlb_dma_address (struct scatterlist *sg) { - return SG_ENT_PHYS_ADDRESS(sg); + return sg->dma_address; } /* diff -Nru a/arch/ia64/mm/init.c b/arch/ia64/mm/init.c --- a/arch/ia64/mm/init.c Fri Aug 16 14:34:57 2002 +++ b/arch/ia64/mm/init.c Fri Aug 16 14:34:57 2002 @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -68,10 +69,9 @@ struct vm_area_struct *vma; /* - * If we're out of memory and kmem_cache_alloc() returns NULL, - * we simply ignore the problem. When the process attempts to - * write to the register backing store for the first time, it - * will get a SEGFAULT in this case. + * If we're out of memory and kmem_cache_alloc() returns NULL, we simply ignore + * the problem. When the process attempts to write to the register backing store + * for the first time, it will get a SEGFAULT in this case. */ vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); if (vma) { @@ -86,6 +86,19 @@ vma->vm_private_data = NULL; insert_vm_struct(current->mm, vma); } + + /* map NaT-page at address zero to speed up speculative dereferencing of NULL: */ + if (!(current->personality & MMAP_PAGE_ZERO)) { + vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); + if (vma) { + memset(vma, 0, sizeof(*vma)); + vma->vm_mm = current->mm; + vma->vm_end = PAGE_SIZE; + vma->vm_page_prot = __pgprot(pgprot_val(PAGE_READONLY) | _PAGE_MA_NAT); + vma->vm_flags = VM_READ | VM_MAYREAD | VM_IO | VM_RESERVED; + insert_vm_struct(current->mm, vma); + } + } } void @@ -95,7 +108,7 @@ addr = (unsigned long) &__init_begin; for (; addr < (unsigned long) &__init_end; addr += PAGE_SIZE) { - clear_bit(PG_reserved, &virt_to_page(addr)->flags); + ClearPageReserved(virt_to_page(addr)); set_page_count(virt_to_page(addr), 1); free_page(addr); ++totalram_pages; @@ -149,9 +162,9 @@ if (!virt_addr_valid(start)) continue; page = virt_to_page(start); - clear_bit(PG_reserved, &page->flags); + ClearPageReserved(page); set_page_count(page, 1); - __free_page(page); + free_page(start); ++totalram_pages; } } diff -Nru a/arch/ia64/mm/tlb.c b/arch/ia64/mm/tlb.c --- a/arch/ia64/mm/tlb.c Fri Aug 16 14:34:59 2002 +++ b/arch/ia64/mm/tlb.c Fri Aug 16 14:34:59 2002 @@ -35,12 +35,14 @@ 1 << _PAGE_SIZE_4K ) struct ia64_ctx ia64_ctx = { - lock: SPIN_LOCK_UNLOCKED, - next: 1, - limit: (1 << 15) - 1, /* start out with the safe (architected) limit */ - max_ctx: ~0U + .lock = SPIN_LOCK_UNLOCKED, + .next = 1, + .limit = (1 << 15) - 1, /* start out with the safe (architected) limit */ + .max_ctx = ~0U }; +u8 ia64_need_tlb_flush __per_cpu_data; + /* * Acquire the ia64_ctx.lock before calling this function! */ @@ -49,6 +51,7 @@ { unsigned long tsk_context, max_ctx = ia64_ctx.max_ctx; struct task_struct *tsk; + int i; if (ia64_ctx.next > max_ctx) ia64_ctx.next = 300; /* skip daemons */ @@ -77,7 +80,11 @@ ia64_ctx.limit = tsk_context; } read_unlock(&tasklist_lock); - flush_tlb_all(); + /* can't call flush_tlb_all() here because of race condition with O(1) scheduler [EF] */ + for (i = 0; i < NR_CPUS; ++i) + if (i != smp_processor_id()) + per_cpu(ia64_need_tlb_flush, i) = 1; + __flush_tlb_all(); } void diff -Nru a/arch/ia64/sn/io/ifconfig_net.c b/arch/ia64/sn/io/ifconfig_net.c --- a/arch/ia64/sn/io/ifconfig_net.c Fri Aug 16 14:34:56 2002 +++ b/arch/ia64/sn/io/ifconfig_net.c Fri Aug 16 14:34:56 2002 @@ -279,9 +279,9 @@ } struct file_operations ifconfig_net_fops = { - ioctl:ifconfig_net_ioctl, /* ioctl */ - open:ifconfig_net_open, /* open */ - release:ifconfig_net_close /* release */ + .ioctl =ifconfig_net_ioctl, /* ioctl */ + .open =ifconfig_net_open, /* open */ + .release =ifconfig_net_close /* release */ }; diff -Nru a/arch/ia64/sn/io/pciba.c b/arch/ia64/sn/io/pciba.c --- a/arch/ia64/sn/io/pciba.c Fri Aug 16 14:34:52 2002 +++ b/arch/ia64/sn/io/pciba.c Fri Aug 16 14:34:52 2002 @@ -210,31 +210,31 @@ /* file operations for each type of node */ static struct file_operations rom_fops = { - owner: THIS_MODULE, - mmap: rom_mmap, - open: generic_open, - release: rom_release + .owner = THIS_MODULE, + .mmap = rom_mmap, + .open = generic_open, + .release = rom_release }; static struct file_operations base_fops = { - owner: THIS_MODULE, - mmap: base_mmap, - open: generic_open + .owner = THIS_MODULE, + .mmap = base_mmap, + .open = generic_open }; static struct file_operations config_fops = { - owner: THIS_MODULE, - ioctl: config_ioctl, - open: generic_open + .owner = THIS_MODULE, + .ioctl = config_ioctl, + .open = generic_open }; static struct file_operations dma_fops = { - owner: THIS_MODULE, - ioctl: dma_ioctl, - mmap: dma_mmap, - open: generic_open + .owner = THIS_MODULE, + .ioctl = dma_ioctl, + .mmap = dma_mmap, + .open = generic_open }; diff -Nru a/arch/ia64/sn/io/sn1/hubcounters.c b/arch/ia64/sn/io/sn1/hubcounters.c --- a/arch/ia64/sn/io/sn1/hubcounters.c Fri Aug 16 14:34:55 2002 +++ b/arch/ia64/sn/io/sn1/hubcounters.c Fri Aug 16 14:34:55 2002 @@ -24,7 +24,7 @@ static int hubstats_ioctl(struct inode *, struct file *, unsigned int, unsigned long); struct file_operations hub_mon_fops = { - ioctl: hubstats_ioctl, + .ioctl = hubstats_ioctl, }; #define HUB_CAPTURE_TICKS (2 * HZ) diff -Nru a/arch/ia64/sn/io/sn1/pcibr.c b/arch/ia64/sn/io/sn1/pcibr.c --- a/arch/ia64/sn/io/sn1/pcibr.c Fri Aug 16 14:34:59 2002 +++ b/arch/ia64/sn/io/sn1/pcibr.c Fri Aug 16 14:34:59 2002 @@ -307,22 +307,22 @@ * appropriate function name below. */ struct file_operations pcibr_fops = { - owner: THIS_MODULE, - llseek: NULL, - read: NULL, - write: NULL, - readdir: NULL, - poll: NULL, - ioctl: NULL, - mmap: NULL, - open: NULL, - flush: NULL, - release: NULL, - fsync: NULL, - fasync: NULL, - lock: NULL, - readv: NULL, - writev: NULL + .owner = THIS_MODULE, + .llseek = NULL, + .read = NULL, + .write = NULL, + .readdir = NULL, + .poll = NULL, + .ioctl = NULL, + .mmap = NULL, + .open = NULL, + .flush = NULL, + .release = NULL, + .fsync = NULL, + .fasync = NULL, + .lock = NULL, + .readv = NULL, + .writev = NULL }; extern devfs_handle_t hwgraph_root; diff -Nru a/arch/ia64/sn/io/sn2/pcibr/pcibr_dvr.c b/arch/ia64/sn/io/sn2/pcibr/pcibr_dvr.c --- a/arch/ia64/sn/io/sn2/pcibr/pcibr_dvr.c Fri Aug 16 14:34:58 2002 +++ b/arch/ia64/sn/io/sn2/pcibr/pcibr_dvr.c Fri Aug 16 14:34:58 2002 @@ -64,22 +64,22 @@ * appropriate function name below. */ struct file_operations pcibr_fops = { - owner: THIS_MODULE, - llseek: NULL, - read: NULL, - write: NULL, - readdir: NULL, - poll: NULL, - ioctl: NULL, - mmap: NULL, - open: NULL, - flush: NULL, - release: NULL, - fsync: NULL, - fasync: NULL, - lock: NULL, - readv: NULL, - writev: NULL + .owner =THIS_MODULE, + .llseek = NULL, + .read = NULL, + .write = NULL, + .readdir = NULL, + .poll = NULL, + .ioctl = NULL, + .mmap = NULL, + .open = NULL, + .flush = NULL, + .release = NULL, + .fsync = NULL, + .fasync = NULL, + .lock = NULL, + .readv = NULL, + .writev = NULL }; #ifdef LATER diff -Nru a/arch/ia64/sn/kernel/setup.c b/arch/ia64/sn/kernel/setup.c --- a/arch/ia64/sn/kernel/setup.c Fri Aug 16 14:34:55 2002 +++ b/arch/ia64/sn/kernel/setup.c Fri Aug 16 14:34:55 2002 @@ -109,14 +109,14 @@ * VGA color display. */ struct screen_info sn1_screen_info = { - orig_x: 0, - orig_y: 0, - orig_video_mode: 3, - orig_video_cols: 80, - orig_video_ega_bx: 3, - orig_video_lines: 25, - orig_video_isVGA: 1, - orig_video_points: 16 + .orig_x = 0, + .orig_y = 0, + .orig_video_mode = 3, + .orig_video_cols = 80, + .orig_video_ega_bx = 3, + .orig_video_lines = 25, + .orig_video_isVGA = 1, + .orig_video_points = 16 }; /* @@ -170,9 +170,9 @@ #ifdef NOT_YET_CONFIG_IA64_MCA extern void ia64_mca_cpe_int_handler (int cpe_irq, void *arg, struct pt_regs *ptregs); static struct irqaction mca_cpe_irqaction = { - handler: ia64_mca_cpe_int_handler, - flags: SA_INTERRUPT, - name: "cpe_hndlr" + .handler = ia64_mca_cpe_int_handler, + .flags = SA_INTERRUPT, + .name = "cpe_hndlr" }; #endif #ifdef CONFIG_IA64_MCA diff -Nru a/arch/ia64/tools/Makefile b/arch/ia64/tools/Makefile --- a/arch/ia64/tools/Makefile Fri Aug 16 14:34:50 2002 +++ b/arch/ia64/tools/Makefile Fri Aug 16 14:34:50 2002 @@ -4,7 +4,9 @@ all: -mrproper: +fastdep: + +mrproper: clean clean: rm -f print_offsets.s print_offsets offsets.h diff -Nru a/arch/ia64/vmlinux.lds.S b/arch/ia64/vmlinux.lds.S --- a/arch/ia64/vmlinux.lds.S Fri Aug 16 14:34:55 2002 +++ b/arch/ia64/vmlinux.lds.S Fri Aug 16 14:34:55 2002 @@ -41,9 +41,6 @@ /* Read-only data */ - . = ALIGN(16); - __gp = . + 0x200000; /* gp must be 16-byte aligned for exc. table */ - /* Global data */ _data = .; @@ -145,6 +142,9 @@ .data : AT(ADDR(.data) - PAGE_OFFSET) { *(.data) *(.gnu.linkonce.d*) CONSTRUCTORS } + + . = ALIGN(16); + __gp = . + 0x200000; /* gp must be 16-byte aligned for exc. table */ .got : AT(ADDR(.got) - PAGE_OFFSET) { *(.got.plt) *(.got) } diff -Nru a/arch/m68k/config.in b/arch/m68k/config.in --- a/arch/m68k/config.in Fri Aug 16 14:34:59 2002 +++ b/arch/m68k/config.in Fri Aug 16 14:34:59 2002 @@ -152,10 +152,6 @@ source drivers/md/Config.in -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - if [ "$CONFIG_MAC" = "y" ]; then source drivers/input/Config.in fi @@ -271,6 +267,7 @@ endmenu if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in mainmenu_option next_comment comment 'Network device support' diff -Nru a/arch/mips/config.in b/arch/mips/config.in --- a/arch/mips/config.in Fri Aug 16 14:34:59 2002 +++ b/arch/mips/config.in Fri Aug 16 14:34:59 2002 @@ -334,12 +334,6 @@ source drivers/md/Config.in -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - -source drivers/telephony/Config.in - if [ "$CONFIG_SGI_IP22" != "y" -a \ "$CONFIG_DECSTATION" != "y" ]; then @@ -372,6 +366,8 @@ fi if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in + mainmenu_option next_comment comment 'Network device support' @@ -390,6 +386,8 @@ source net/irda/Config.in source drivers/isdn/Config.in + +source drivers/telephony/Config.in mainmenu_option next_comment comment 'Old CD-ROM drivers (not SCSI, not IDE)' diff -Nru a/arch/mips64/config.in b/arch/mips64/config.in --- a/arch/mips64/config.in Fri Aug 16 14:34:53 2002 +++ b/arch/mips64/config.in Fri Aug 16 14:34:53 2002 @@ -132,12 +132,6 @@ source drivers/md/Config.in -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - -source drivers/telephony/Config.in - mainmenu_option next_comment comment 'ATA/ATAPI/MFM/RLL support' @@ -163,6 +157,8 @@ #source drivers/message/i2o/Config.in if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in + mainmenu_option next_comment comment 'Network device support' @@ -181,6 +177,8 @@ source net/irda/Config.in source drivers/isdn/Config.in + +source drivers/telephony/Config.in mainmenu_option next_comment comment 'Old CD-ROM drivers (not SCSI, not IDE)' diff -Nru a/arch/parisc/config.in b/arch/parisc/config.in --- a/arch/parisc/config.in Fri Aug 16 14:35:01 2002 +++ b/arch/parisc/config.in Fri Aug 16 14:35:01 2002 @@ -99,10 +99,6 @@ source drivers/block/Config.in -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - mainmenu_option next_comment comment 'SCSI support' @@ -149,6 +145,8 @@ endmenu if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in + mainmenu_option next_comment comment 'Network device support' diff -Nru a/arch/ppc/config.in b/arch/ppc/config.in --- a/arch/ppc/config.in Fri Aug 16 14:34:51 2002 +++ b/arch/ppc/config.in Fri Aug 16 14:34:51 2002 @@ -419,10 +419,6 @@ source drivers/block/Config.in source drivers/md/Config.in -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - mainmenu_option next_comment comment 'ATA/IDE/MFM/RLL support' @@ -449,6 +445,8 @@ source drivers/message/i2o/Config.in if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in + mainmenu_option next_comment comment 'Network device support' diff -Nru a/arch/ppc64/Makefile b/arch/ppc64/Makefile --- a/arch/ppc64/Makefile Fri Aug 16 14:34:53 2002 +++ b/arch/ppc64/Makefile Fri Aug 16 14:34:53 2002 @@ -18,9 +18,9 @@ LDFLAGS := -m elf64ppc LDFLAGS_vmlinux = -T arch/ppc64/vmlinux.lds -Bstatic \ -e $(KERNELLOAD) -Ttext $(KERNELLOAD) -CFLAGS := $(CFLAGS) -fsigned-char -msoft-float -pipe \ +CFLAGS := $(CFLAGS) -msoft-float -pipe \ -Wno-uninitialized -mminimal-toc -mtraceback=full \ - -Wa,-mpower4 -finline-limit-2000 -mcpu=630 + -finline-limit-2000 -mcpu=power4 CPP = $(CC) -E $(CFLAGS) diff -Nru a/arch/ppc64/config.in b/arch/ppc64/config.in --- a/arch/ppc64/config.in Fri Aug 16 14:34:53 2002 +++ b/arch/ppc64/config.in Fri Aug 16 14:34:53 2002 @@ -87,10 +87,6 @@ source drivers/block/Config.in source drivers/md/Config.in -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - mainmenu_option next_comment comment 'ATA/ATAPI/MFM/RLL support' @@ -118,6 +114,8 @@ source drivers/message/i2o/Config.in if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in + mainmenu_option next_comment comment 'Network device support' diff -Nru a/arch/ppc64/defconfig b/arch/ppc64/defconfig --- a/arch/ppc64/defconfig Fri Aug 16 14:34:55 2002 +++ b/arch/ppc64/defconfig Fri Aug 16 14:34:55 2002 @@ -125,7 +125,6 @@ # CONFIG_INET_ECN is not set CONFIG_SYN_COOKIES=y CONFIG_IPV6=m -# CONFIG_KHTTPD is not set # CONFIG_ATM is not set # CONFIG_VLAN_8021Q is not set @@ -156,7 +155,7 @@ # CONFIG_NET_SCHED is not set # -# ATA/IDE/MFM/RLL support +# ATA/ATAPI/MFM/RLL support # # CONFIG_IDE is not set # CONFIG_BLK_DEV_HD is not set @@ -434,6 +433,7 @@ CONFIG_FBCON_CFB16=y CONFIG_FBCON_CFB24=y CONFIG_FBCON_CFB32=y +CONFIG_FBCON_ACCEL=y CONFIG_FBCON_FONTWIDTH8_ONLY=y CONFIG_FBCON_FONTS=y CONFIG_FONT_8x8=y @@ -474,7 +474,6 @@ # CONFIG_SERIO_I8042 is not set # CONFIG_SERIO_SERPORT is not set # CONFIG_SERIO_CT82C710 is not set -# CONFIG_SERIO_Q40KBD is not set # CONFIG_SERIO_PARKBD is not set # @@ -487,10 +486,26 @@ # CONFIG_VT=y CONFIG_VT_CONSOLE=y -CONFIG_SERIAL=y -CONFIG_SERIAL_CONSOLE=y -# CONFIG_SERIAL_EXTENDED is not set # CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +CONFIG_SERIAL_8250_CONSOLE=y +# CONFIG_SERIAL_8250_CS is not set +# CONFIG_SERIAL_8250_EXTENDED is not set +# CONFIG_SERIAL_8250_MANY_PORTS is not set +# CONFIG_SERIAL_8250_SHARE_IRQ is not set +# CONFIG_SERIAL_8250_DETECT_IRQ is not set +# CONFIG_SERIAL_8250_MULTIPORT is not set +# CONFIG_SERIAL_8250_RSA is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y CONFIG_UNIX98_PTYS=y CONFIG_UNIX98_PTY_COUNT=256 CONFIG_HVC_CONSOLE=y @@ -523,6 +538,7 @@ # CONFIG_FTAPE is not set # CONFIG_AGP is not set # CONFIG_DRM is not set +CONFIG_RAW_DRIVER=y # # Multimedia devices @@ -686,6 +702,11 @@ CONFIG_XMON=y CONFIG_XMON_DEFAULT=y # CONFIG_PPCDBG is not set + +# +# Security options +# +CONFIG_SECURITY_CAPABILITIES=y # # Library routines diff -Nru a/arch/ppc64/kernel/bitops.c b/arch/ppc64/kernel/bitops.c --- a/arch/ppc64/kernel/bitops.c Fri Aug 16 14:34:59 2002 +++ b/arch/ppc64/kernel/bitops.c Fri Aug 16 14:34:59 2002 @@ -45,17 +45,6 @@ return result + ffz(tmp); } -static __inline__ unsigned long ___ffs(unsigned long word) -{ - unsigned long result = 0; - - while (!(word & 1UL)) { - result++; - word >>= 1; - } - return result; -} - unsigned long find_next_bit(unsigned long *addr, unsigned long size, unsigned long offset) { unsigned long *p = addr + (offset >> 6); @@ -91,7 +80,7 @@ if (tmp == 0UL) /* Are any bits set? */ return result + size; /* Nope. */ found_middle: - return result + ___ffs(tmp); + return result + __ffs(tmp); } static __inline__ unsigned int ext2_ilog2(unsigned int x) diff -Nru a/arch/ppc64/kernel/head.S b/arch/ppc64/kernel/head.S --- a/arch/ppc64/kernel/head.S Fri Aug 16 14:34:58 2002 +++ b/arch/ppc64/kernel/head.S Fri Aug 16 14:34:58 2002 @@ -1421,7 +1421,7 @@ addi r4,r4,THREAD /* want THREAD of last_task_used_math */ SAVE_32FPRS(0, r4) mffs fr0 - stfd fr0,THREAD_FPSCR-4(r4) + stfd fr0,THREAD_FPSCR(r4) ld r5,PT_REGS(r4) ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) li r20,MSR_FP|MSR_FE0|MSR_FE1 @@ -1430,10 +1430,12 @@ 1: #endif /* CONFIG_SMP */ /* enable use of FP after return */ - ori r23,r23,MSR_FP|MSR_FE0|MSR_FE1 - ld r4, PACACURRENT(r13) + ld r4,PACACURRENT(r13) addi r5,r4,THREAD /* Get THREAD */ - lfd fr0,THREAD_FPSCR-4(r5) + lwz r4,THREAD_FPEXC_MODE(r5) + ori r23,r23,MSR_FP + or r23,r23,r4 + lfd fr0,THREAD_FPSCR(r5) mtfsf 0xff,fr0 REST_32FPRS(0, r5) #ifndef CONFIG_SMP @@ -1476,7 +1478,7 @@ cmpi 0,r5,0 SAVE_32FPRS(0, r3) mffs fr0 - stfd fr0,THREAD_FPSCR-4(r3) + stfd fr0,THREAD_FPSCR(r3) beq 1f ld r4,_MSR-STACK_FRAME_OVERHEAD(r5) li r3,MSR_FP|MSR_FE0|MSR_FE1 diff -Nru a/arch/ppc64/kernel/htab.c b/arch/ppc64/kernel/htab.c --- a/arch/ppc64/kernel/htab.c Fri Aug 16 14:34:58 2002 +++ b/arch/ppc64/kernel/htab.c Fri Aug 16 14:34:58 2002 @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include diff -Nru a/arch/ppc64/kernel/i8259.c b/arch/ppc64/kernel/i8259.c --- a/arch/ppc64/kernel/i8259.c Fri Aug 16 14:35:01 2002 +++ b/arch/ppc64/kernel/i8259.c Fri Aug 16 14:35:01 2002 @@ -123,7 +123,8 @@ static void i8259_end_irq(unsigned int irq) { - if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS))) + if (!(irq_desc[irq].status & (IRQ_DISABLED|IRQ_INPROGRESS)) && + irq_desc[irq].action) i8259_unmask_irq(irq); } diff -Nru a/arch/ppc64/kernel/idle.c b/arch/ppc64/kernel/idle.c --- a/arch/ppc64/kernel/idle.c Fri Aug 16 14:34:59 2002 +++ b/arch/ppc64/kernel/idle.c Fri Aug 16 14:34:59 2002 @@ -18,6 +18,7 @@ #include #include #include +#include #include #include diff -Nru a/arch/ppc64/kernel/irq.c b/arch/ppc64/kernel/irq.c --- a/arch/ppc64/kernel/irq.c Fri Aug 16 14:34:57 2002 +++ b/arch/ppc64/kernel/irq.c Fri Aug 16 14:34:57 2002 @@ -169,10 +169,8 @@ inline void synchronize_irq(unsigned int irq) { - while (irq_desc[irq].status & IRQ_INPROGRESS) { - barrier(); + while (irq_desc[irq].status & IRQ_INPROGRESS) cpu_relax(); - } } #endif /* CONFIG_SMP */ @@ -500,7 +498,7 @@ * use the action we have. */ action = NULL; - if (!(status & (IRQ_DISABLED | IRQ_INPROGRESS))) { + if (likely(!(status & (IRQ_DISABLED | IRQ_INPROGRESS)))) { action = desc->action; if (!action || !action->handler) { ppc_spurious_interrupts++; @@ -527,10 +525,9 @@ a different instance of this same irq, the other processor will take care of it. */ - if (!action) + if (unlikely(!action)) goto out; - /* * Edge triggered interrupts need to remember * pending events. @@ -546,12 +543,12 @@ handle_irq_event(irq, regs, action); spin_lock(&desc->lock); - if (!(desc->status & IRQ_PENDING)) + if (likely(!(desc->status & IRQ_PENDING))) break; desc->status &= ~IRQ_PENDING; } - desc->status &= ~IRQ_INPROGRESS; out: + desc->status &= ~IRQ_INPROGRESS; /* * The ->end() handler has to deal with interrupts which got * disabled while the handler was running. @@ -567,7 +564,6 @@ int do_IRQ(struct pt_regs *regs) { - int cpu = smp_processor_id(); int irq, first = 1; #ifdef CONFIG_PPC_ISERIES struct paca_struct *lpaca; @@ -605,7 +601,7 @@ ppc_spurious_interrupts++; #endif - irq_exit(); + irq_exit(); #ifdef CONFIG_PPC_ISERIES if (lpaca->xLpPaca.xIntDword.xFields.xDecrInt) { @@ -614,9 +610,6 @@ timer_interrupt(regs); } #endif - - if (softirq_pending(cpu)) - do_softirq(); return 1; /* lets ret_from_int know we can do checks */ } diff -Nru a/arch/ppc64/kernel/misc.S b/arch/ppc64/kernel/misc.S --- a/arch/ppc64/kernel/misc.S Fri Aug 16 14:34:56 2002 +++ b/arch/ppc64/kernel/misc.S Fri Aug 16 14:34:56 2002 @@ -460,21 +460,21 @@ blr _GLOBAL(cvt_fd) - lfd 0,-4(r5) /* load up fpscr value */ + lfd 0,0(r5) /* load up fpscr value */ mtfsf 0xff,0 lfs 0,0(r3) stfd 0,0(r4) mffs 0 /* save new fpscr value */ - stfd 0,-4(r5) + stfd 0,0(r5) blr _GLOBAL(cvt_df) - lfd 0,-4(r5) /* load up fpscr value */ + lfd 0,0(r5) /* load up fpscr value */ mtfsf 0xff,0 lfd 0,0(r3) stfs 0,0(r4) mffs 0 /* save new fpscr value */ - stfd 0,-4(r5) + stfd 0,0(r5) blr /* @@ -518,8 +518,8 @@ .llong .sys32_execve .llong .sys_chdir .llong .sys32_time - .llong .sys32_mknod - .llong .sys32_chmod /* 15 */ + .llong .sys_mknod + .llong .sys_chmod /* 15 */ .llong .sys_lchown .llong .sys_ni_syscall /* old break syscall holder */ .llong .sys32_stat @@ -605,7 +605,7 @@ .llong .sys_ni_syscall /* old profil syscall holder */ .llong .sys32_statfs .llong .sys32_fstatfs /* 100 */ - .llong .sys32_ioperm + .llong .sys_ni_syscall /* old ioperm syscall holder */ .llong .sys32_socketcall .llong .sys32_syslog .llong .sys32_setitimer @@ -614,10 +614,10 @@ .llong .sys32_newlstat .llong .sys32_newfstat .llong .sys_uname - .llong .sys32_iopl /* 110 */ + .llong .sys_ni_syscall /* 110 old iopl syscall holder */ .llong .sys_vhangup - .llong .sys_ni_syscall /* old 'idle' syscall */ - .llong .sys32_vm86 + .llong .sys_ni_syscall /* old 'idle' syscall */ + .llong .sys_ni_syscall /* old vm86 syscall holder */ .llong .sys32_wait4 .llong .sys_swapoff /* 115 */ .llong .sys32_sysinfo @@ -627,7 +627,7 @@ .llong .sys32_clone /* 120 */ .llong .sys32_setdomainname .llong .ppc64_newuname - .llong .sys32_modify_ldt + .llong .sys_ni_syscall /* old modify_ldt syscall holder */ .llong .sys32_adjtimex .llong .sys_mprotect /* 125 */ .llong .sys32_sigprocmask @@ -683,8 +683,8 @@ .llong .sys32_rt_sigtimedwait .llong .sys32_rt_sigqueueinfo .llong .sys32_rt_sigsuspend - .llong .sys32_pread - .llong .sys32_pwrite /* 180 */ + .llong .sys32_pread64 + .llong .sys32_pwrite64 /* 180 */ .llong .sys_chown .llong .sys_getcwd .llong .sys_capget @@ -695,7 +695,7 @@ .llong .sys_ni_syscall /* streams2 */ .llong .sys32_vfork .llong .sys32_getrlimit /* 190 */ - .llong .sys_ni_syscall /* 191 */ /* Unused */ + .llong .sys32_readahead .llong .sys_ni_syscall /* 192 - reserved - mmap2 */ .llong .sys32_truncate64 /* 193 - truncate64 */ .llong .sys32_ftruncate64 /* 194 - ftruncate64 */ @@ -726,11 +726,12 @@ .llong .sys_lremovexattr .llong .sys_fremovexattr /* 220 */ .llong .sys_futex - .llong .sys_ni_syscall /* reserved for tux */ .llong .sys32_sched_setaffinity .llong .sys32_sched_getaffinity + .llong .sys_ni_syscall /* reserved for security */ + .llong .sys_ni_syscall /* 225 - reserved for tux */ - .rept NR_syscalls-224 + .rept NR_syscalls-225 .llong .sys_ni_syscall .endr #endif @@ -838,7 +839,7 @@ .llong .sys_ni_syscall /* old profil syscall holder */ .llong .sys_statfs .llong .sys_fstatfs /* 100 */ - .llong .sys_ioperm + .llong .sys_ni_syscall /* old ioperm syscall holder */ .llong .sys_socketcall .llong .sys_syslog .llong .sys_setitimer @@ -847,10 +848,10 @@ .llong .sys_newlstat .llong .sys_newfstat .llong .sys_uname - .llong .sys_iopl /* 110 */ + .llong .sys_ni_syscall /* 110 old iopl syscall holder */ .llong .sys_vhangup .llong .sys_ni_syscall /* old 'idle' syscall */ - .llong .sys_vm86 + .llong .sys_ni_syscall /* old vm86 syscall holder */ .llong .sys_wait4 .llong .sys_swapoff /* 115 */ .llong .sys_sysinfo @@ -860,7 +861,7 @@ .llong .sys_clone /* 120 */ .llong .sys_setdomainname .llong .ppc64_newuname - .llong .sys_modify_ldt + .llong .sys_ni_syscall /* old modify_ldt syscall */ .llong .sys_adjtimex .llong .sys_mprotect /* 125 */ .llong .sys_sigprocmask @@ -928,7 +929,7 @@ .llong .sys_ni_syscall /* streams2 */ .llong .sys_vfork .llong .sys_getrlimit /* 190 */ - .llong .sys_ni_syscall /* 191 */ /* Unused */ + .llong .sys_readahead .llong .sys_ni_syscall /* 192 - reserved - mmap2 */ .llong .sys_ni_syscall /* 193 - reserved - truncate64 */ .llong .sys_ni_syscall /* 194 - reserved - ftruncate64 */ @@ -959,10 +960,11 @@ .llong .sys_lremovexattr .llong .sys_fremovexattr /* 220 */ .llong .sys_futex - .llong .sys_ni_syscall /* reserved for tux */ .llong .sys_sched_setaffinity .llong .sys_sched_getaffinity + .llong .sys_ni_syscall /* reserved for security */ + .llong .sys_ni_syscall /* reserved for tux */ - .rept NR_syscalls-224 + .rept NR_syscalls-225 .llong .sys_ni_syscall .endr diff -Nru a/arch/ppc64/kernel/mk_defs.c b/arch/ppc64/kernel/mk_defs.c --- a/arch/ppc64/kernel/mk_defs.c Fri Aug 16 14:34:59 2002 +++ b/arch/ppc64/kernel/mk_defs.c Fri Aug 16 14:34:59 2002 @@ -51,6 +51,7 @@ /* task_struct->thread */ DEFINE(THREAD, offsetof(struct task_struct, thread)); DEFINE(PT_REGS, offsetof(struct thread_struct, regs)); + DEFINE(THREAD_FPEXC_MODE, offsetof(struct thread_struct, fpexc_mode)); DEFINE(THREAD_FPR0, offsetof(struct thread_struct, fpr[0])); DEFINE(THREAD_FPSCR, offsetof(struct thread_struct, fpscr)); DEFINE(KSP, offsetof(struct thread_struct, ksp)); diff -Nru a/arch/ppc64/kernel/open_pic.c b/arch/ppc64/kernel/open_pic.c --- a/arch/ppc64/kernel/open_pic.c Fri Aug 16 14:34:58 2002 +++ b/arch/ppc64/kernel/open_pic.c Fri Aug 16 14:34:58 2002 @@ -576,7 +576,7 @@ */ static spinlock_t openpic_setup_lock __initdata = SPIN_LOCK_UNLOCKED; -void __init do_openpic_setup_cpu(void) +void __devinit do_openpic_setup_cpu(void) { #ifdef CONFIG_IRQ_ALL_CPUS int i; diff -Nru a/arch/ppc64/kernel/pSeries_lpar.c b/arch/ppc64/kernel/pSeries_lpar.c --- a/arch/ppc64/kernel/pSeries_lpar.c Fri Aug 16 14:35:01 2002 +++ b/arch/ppc64/kernel/pSeries_lpar.c Fri Aug 16 14:35:01 2002 @@ -155,7 +155,7 @@ unsigned long ptex, unsigned long avpn) { - return plpar_hcall_norets(H_PROTECT, flags, ptex); + return plpar_hcall_norets(H_PROTECT, flags, ptex, avpn); } long plpar_tce_get(unsigned long liobn, @@ -552,6 +552,7 @@ int secondary, unsigned long hpteflags, int bolted, int large) { + /* XXX fix for large page */ unsigned long avpn = vpn >> 11; unsigned long arpn = physRpn_to_absRpn(prpn); unsigned long lpar_rc; @@ -651,11 +652,10 @@ unsigned long va, int large) { unsigned long lpar_rc; - unsigned long flags; - flags = (newpp & 7) | H_AVPN; - unsigned long vpn = va >> PAGE_SHIFT; + unsigned long flags = (newpp & 7) | H_AVPN; + unsigned long avpn = va >> 23; - lpar_rc = plpar_pte_protect(flags, slot, (vpn >> 4) & ~0x7fUL); + lpar_rc = plpar_pte_protect(flags, slot, (avpn << 7)); if (lpar_rc == H_Not_Found) { udbg_printf("updatepp missed\n"); @@ -748,18 +748,11 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, int large, int local) { - unsigned long vpn, avpn; + unsigned long avpn = va >> 23; unsigned long lpar_rc; unsigned long dummy1, dummy2; - if (large) - vpn = va >> LARGE_PAGE_SHIFT; - else - vpn = va >> PAGE_SHIFT; - - avpn = vpn >> 11; - - lpar_rc = plpar_pte_remove(H_AVPN, slot, (vpn >> 4) & ~0x7fUL, &dummy1, + lpar_rc = plpar_pte_remove(H_AVPN, slot, (avpn << 7), &dummy1, &dummy2); if (lpar_rc == H_Not_Found) { diff -Nru a/arch/ppc64/kernel/ppc_ksyms.c b/arch/ppc64/kernel/ppc_ksyms.c --- a/arch/ppc64/kernel/ppc_ksyms.c Fri Aug 16 14:34:54 2002 +++ b/arch/ppc64/kernel/ppc_ksyms.c Fri Aug 16 14:34:54 2002 @@ -54,12 +54,6 @@ /* Tell string.h we don't want memcpy etc. as cpp defines */ #define EXPORT_SYMTAB_STROPS -extern void do_IRQ(struct pt_regs *regs, int isfake); -extern void SystemResetException(struct pt_regs *regs); -extern void MachineCheckException(struct pt_regs *regs); -extern void AlignmentException(struct pt_regs *regs); -extern void ProgramCheckException(struct pt_regs *regs); -extern void SingleStepException(struct pt_regs *regs); extern int sys_sigreturn(struct pt_regs *regs); extern int do_signal(sigset_t *, struct pt_regs *); extern int register_ioctl32_conversion(unsigned int cmd, int (*handler)(unsigned int, unsigned int, unsigned long, struct file *)); @@ -74,17 +68,12 @@ extern struct pci_dev * iSeries_vio_dev; EXPORT_SYMBOL(do_signal); -EXPORT_SYMBOL(do_IRQ); -EXPORT_SYMBOL(SystemResetException); -EXPORT_SYMBOL(MachineCheckException); -EXPORT_SYMBOL(AlignmentException); -EXPORT_SYMBOL(ProgramCheckException); -EXPORT_SYMBOL(SingleStepException); EXPORT_SYMBOL(sys_sigreturn); EXPORT_SYMBOL(enable_irq); EXPORT_SYMBOL(disable_irq); EXPORT_SYMBOL(disable_irq_nosync); #ifdef CONFIG_SMP +EXPORT_SYMBOL(synchronize_irq); EXPORT_SYMBOL(kernel_flag); #endif /* CONFIG_SMP */ @@ -126,15 +115,6 @@ EXPORT_SYMBOL(__strncpy_from_user); EXPORT_SYMBOL(__strnlen_user); -/* -EXPORT_SYMBOL(inb); -EXPORT_SYMBOL(inw); -EXPORT_SYMBOL(inl); -EXPORT_SYMBOL(outb); -EXPORT_SYMBOL(outw); -EXPORT_SYMBOL(outl); -EXPORT_SYMBOL(outsl);*/ - #ifdef CONFIG_MSCHUNKS EXPORT_SYMBOL(msChunks); #endif @@ -243,8 +223,6 @@ EXPORT_SYMBOL(timer_interrupt); EXPORT_SYMBOL(irq_desc); -void ppc_irq_dispatch_handler(struct pt_regs *, int); -EXPORT_SYMBOL(ppc_irq_dispatch_handler); EXPORT_SYMBOL(get_wchan); EXPORT_SYMBOL(console_drivers); #ifdef CONFIG_XMON @@ -265,10 +243,6 @@ EXPORT_SYMBOL(debugger_iabr_match); EXPORT_SYMBOL(debugger_dabr_match); EXPORT_SYMBOL(debugger_fault_handler); -#endif - -#ifdef CONFIG_SMP -EXPORT_SYMBOL(atomic_dec_and_lock); #endif EXPORT_SYMBOL(tb_ticks_per_usec); diff -Nru a/arch/ppc64/kernel/process.c b/arch/ppc64/kernel/process.c --- a/arch/ppc64/kernel/process.c Fri Aug 16 14:34:58 2002 +++ b/arch/ppc64/kernel/process.c Fri Aug 16 14:34:58 2002 @@ -163,8 +163,7 @@ */ int copy_thread(int nr, unsigned long clone_flags, unsigned long usp, - unsigned long unused, - struct task_struct *p, struct pt_regs *regs) + unsigned long unused, struct task_struct *p, struct pt_regs *regs) { struct pt_regs *childregs, *kregs; extern void ret_from_fork(void); @@ -208,17 +207,6 @@ */ kregs->nip = *((unsigned long *)ret_from_fork); - /* - * copy fpu info - assume lazy fpu switch now always - * -- Cort - */ - if (regs->msr & MSR_FP) { - giveup_fpu(current); - childregs->msr &= ~(MSR_FP | MSR_FE0 | MSR_FE1); - } - memcpy(&p->thread.fpr, ¤t->thread.fpr, sizeof(p->thread.fpr)); - p->thread.fpscr = current->thread.fpscr; - return 0; } @@ -247,10 +235,38 @@ current->thread.fpscr = 0; } +/* XXX temporary */ +#define PR_FP_EXC_PRECISE 3 /* precise exception mode */ + +int set_fpexc_mode(struct task_struct *tsk, unsigned int val) +{ + struct pt_regs *regs = tsk->thread.regs; + + if (val > PR_FP_EXC_PRECISE) + return -EINVAL; + tsk->thread.fpexc_mode = __pack_fe01(val); + if (regs != NULL && (regs->msr & MSR_FP) != 0) + regs->msr = (regs->msr & ~(MSR_FE0|MSR_FE1)) + | tsk->thread.fpexc_mode; + return 0; +} + +int get_fpexc_mode(struct task_struct *tsk, unsigned long adr) +{ + unsigned int val; + + val = __unpack_fe01(tsk->thread.fpexc_mode); + return put_user(val, (unsigned int *) adr); +} + int sys_clone(int p1, int p2, int p3, int p4, int p5, int p6, struct pt_regs *regs) { struct task_struct *p; + + if (regs->msr & MSR_FP) + giveup_fpu(current); + p = do_fork(p1 & ~CLONE_IDLETASK, regs->gpr[1], regs, 0); return IS_ERR(p) ? PTR_ERR(p) : p->pid; } @@ -259,6 +275,10 @@ struct pt_regs *regs) { struct task_struct *p; + + if (regs->msr & MSR_FP) + giveup_fpu(current); + p = do_fork(SIGCHLD, regs->gpr[1], regs, 0); return IS_ERR(p) ? PTR_ERR(p) : p->pid; } @@ -267,6 +287,10 @@ struct pt_regs *regs) { struct task_struct *p; + + if (regs->msr & MSR_FP) + giveup_fpu(current); + p = do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->gpr[1], regs, 0); return IS_ERR(p) ? PTR_ERR(p) : p->pid; } diff -Nru a/arch/ppc64/kernel/prom.c b/arch/ppc64/kernel/prom.c --- a/arch/ppc64/kernel/prom.c Fri Aug 16 14:34:59 2002 +++ b/arch/ppc64/kernel/prom.c Fri Aug 16 14:34:59 2002 @@ -144,6 +144,7 @@ #define FB_MAX 8 #endif +static int ppc64_is_smp; struct prom_t prom = { 0, /* entry */ diff -Nru a/arch/ppc64/kernel/ptrace32.c b/arch/ppc64/kernel/ptrace32.c --- a/arch/ppc64/kernel/ptrace32.c Fri Aug 16 14:34:52 2002 +++ b/arch/ppc64/kernel/ptrace32.c Fri Aug 16 14:34:52 2002 @@ -177,10 +177,7 @@ if (numReg >= PT_FPR0) { if (child->thread.regs->msr & MSR_FP) giveup_fpu(child); - if (numReg == PT_FPSCR) - tmp = ((unsigned int *)child->thread.fpscr); - else - tmp = ((unsigned long int *)child->thread.fpr)[numReg - PT_FPR0]; + tmp = ((unsigned long int *)child->thread.fpr)[numReg - PT_FPR0]; } else { /* register within PT_REGS struct */ tmp = get_reg(child, numReg); } diff -Nru a/arch/ppc64/kernel/rtasd.c b/arch/ppc64/kernel/rtasd.c --- a/arch/ppc64/kernel/rtasd.c Fri Aug 16 14:34:56 2002 +++ b/arch/ppc64/kernel/rtasd.c Fri Aug 16 14:34:56 2002 @@ -17,7 +17,6 @@ #include #include #include -#include #include #include diff -Nru a/arch/ppc64/kernel/signal.c b/arch/ppc64/kernel/signal.c --- a/arch/ppc64/kernel/signal.c Fri Aug 16 14:34:51 2002 +++ b/arch/ppc64/kernel/signal.c Fri Aug 16 14:34:51 2002 @@ -55,7 +55,19 @@ * able to continue 'til the next breakpoint from within the signal * handler, even if the handler returns. */ +#if 0 #define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) +#else +/* + * glibc tries to set FE0/FE1 via a signal handler. Since it only ever + * sets both bits and this is the default setting we now disable this + * behaviour. This is done to insure the new prctl which alters FE0/FE1 does + * not get overriden by glibc. Setting and clearing FE0/FE1 via signal + * handler has always been bogus since load_up_fpu used to set FE0/FE1 + * unconditionally. + */ +#define MSR_USERCHANGE 0 +#endif /* * When we have signals to deliver, we set up on the diff -Nru a/arch/ppc64/kernel/signal32.c b/arch/ppc64/kernel/signal32.c --- a/arch/ppc64/kernel/signal32.c Fri Aug 16 14:35:00 2002 +++ b/arch/ppc64/kernel/signal32.c Fri Aug 16 14:35:00 2002 @@ -39,7 +39,19 @@ * able to continue 'til the next breakpoint from within the signal * handler, even if the handler returns. */ +#if 0 #define MSR_USERCHANGE (MSR_FE0 | MSR_FE1) +#else +/* + * glibc tries to set FE0/FE1 via a signal handler. Since it only ever + * sets both bits and this is the default setting we now disable this + * behaviour. This is done to insure the new prctl which alters FE0/FE1 does + * not get overriden by glibc. Setting and clearing FE0/FE1 via signal + * handler has always been bogus since load_up_fpu used to set FE0/FE1 + * unconditionally. + */ +#define MSR_USERCHANGE 0 +#endif struct timespec32 { s32 tv_sec; diff -Nru a/arch/ppc64/kernel/smp.c b/arch/ppc64/kernel/smp.c --- a/arch/ppc64/kernel/smp.c Fri Aug 16 14:34:59 2002 +++ b/arch/ppc64/kernel/smp.c Fri Aug 16 14:34:59 2002 @@ -25,8 +25,6 @@ #include #include #include -#define __KERNEL_SYSCALLS__ -#include #include #include #include @@ -53,22 +51,19 @@ #include int smp_threads_ready = 0; -volatile int smp_commenced = 0; -int smp_tb_synchronized = 0; spinlock_t kernel_flag __cacheline_aligned = SPIN_LOCK_UNLOCKED; unsigned long cache_decay_ticks; -static int max_cpus __initdata = NR_CPUS; /* initialised so it doesnt end up in bss */ unsigned long cpu_online_map = 0; int boot_cpuid = 0; -int ppc64_is_smp = 0; -volatile unsigned long cpu_callin_map[NR_CPUS] = {0,}; +static struct smp_ops_t *smp_ops; + +volatile unsigned long cpu_callin_map[NR_CPUS]; extern unsigned char stab_array[]; -int start_secondary(void *); extern int cpu_idle(void *unused); void smp_call_function_interrupt(void); void smp_message_pass(int target, int msg, unsigned long data, int wait); @@ -86,13 +81,13 @@ struct xics_ipi_struct xics_ipi_message[NR_CPUS] __cacheline_aligned; -#define smp_message_pass(t,m,d,w) ppc_md.smp_message_pass((t),(m),(d),(w)) +#define smp_message_pass(t,m,d,w) smp_ops->message_pass((t),(m),(d),(w)) static inline void set_tb(unsigned int upper, unsigned int lower) { - mtspr(SPRN_TBWL, 0); - mtspr(SPRN_TBWU, upper); - mtspr(SPRN_TBWL, lower); + mttbl(0); + mttbu(upper); + mttbl(lower); } void iSeries_smp_message_recv( struct pt_regs * regs ) @@ -106,7 +101,6 @@ for ( msg = 0; msg < 4; ++msg ) if ( test_and_clear_bit( msg, &iSeries_smp_message[cpu] ) ) smp_message_recv( msg, regs ); - } static void smp_iSeries_message_pass(int target, int msg, unsigned long data, int wait) @@ -127,6 +121,7 @@ } } +#ifdef CONFIG_PPC_ISERIES static int smp_iSeries_numProcs(void) { unsigned np, i; @@ -141,24 +136,23 @@ } return np; } +#endif -static void smp_iSeries_probe(void) +static int smp_iSeries_probe(void) { unsigned i; - unsigned np; - struct ItLpPaca * lpPaca; + unsigned np = 0; + struct ItLpPaca *lpPaca; - np = 0; for (i=0; i < MAX_PACAS; ++i) { lpPaca = paca[i].xLpPacaPtr; - if ( lpPaca->xDynProcStatus < 2 ) { + if (lpPaca->xDynProcStatus < 2) { paca[i].active = 1; ++np; - paca[i].next_jiffy_update_tb = paca[0].next_jiffy_update_tb; } } - smp_tb_synchronized = 1; + return np; } static void smp_iSeries_kick_cpu(int nr) @@ -187,17 +181,18 @@ paca[nr].xProcStart = 1; } -static void smp_iSeries_setup_cpu(int nr) +static void __devinit smp_iSeries_setup_cpu(int nr) { } /* This is called very early. */ -void smp_init_iSeries(void) +void __init smp_init_iSeries(void) { - ppc_md.smp_message_pass = smp_iSeries_message_pass; - ppc_md.smp_probe = smp_iSeries_probe; - ppc_md.smp_kick_cpu = smp_iSeries_kick_cpu; - ppc_md.smp_setup_cpu = smp_iSeries_setup_cpu; + smp_ops = &ppc_md.smp_ops; + smp_ops->message_pass = smp_iSeries_message_pass; + smp_ops->probe = smp_iSeries_probe; + smp_ops->kick_cpu = smp_iSeries_kick_cpu; + smp_ops->setup_cpu = smp_iSeries_setup_cpu; #ifdef CONFIG_PPC_ISERIES #warning fix for iseries naca->processorCount = smp_iSeries_numProcs(); @@ -229,10 +224,20 @@ } } -static void smp_chrp_probe(void) +static int __init smp_chrp_probe(void) { - if (ppc64_is_smp) + int i; + int nr_cpus = 0; + + for (i = 0; i < NR_CPUS; i++) { + if (cpu_possible(i)) + nr_cpus++; + } + + if (nr_cpus > 1) openpic_request_IPIs(); + + return nr_cpus; } static void @@ -252,78 +257,32 @@ /* The processor is currently spinning, waiting * for the xProcStart field to become non-zero * After we set xProcStart, the processor will - * continue on to secondary_start in iSeries_head.S + * continue on to secondary_start */ paca[nr].xProcStart = 1; } -extern struct gettimeofday_struct do_gtod; - -static void smp_space_timers() +static void __init smp_space_timers(unsigned int max_cpus) { int i; - unsigned long offset = tb_ticks_per_jiffy / NR_CPUS; + unsigned long offset = tb_ticks_per_jiffy / max_cpus; + unsigned long previous_tb = paca[boot_cpuid].next_jiffy_update_tb; - for (i = 1; i < NR_CPUS; ++i) - paca[i].next_jiffy_update_tb = - paca[i-1].next_jiffy_update_tb + offset; -} - -static void -smp_chrp_setup_cpu(int cpu_nr) -{ - static atomic_t ready = ATOMIC_INIT(1); - static volatile int frozen = 0; - - if (naca->platform == PLATFORM_PSERIES_LPAR) { - /* timebases already synced under the hypervisor. */ - paca[cpu_nr].next_jiffy_update_tb = tb_last_stamp = get_tb(); - if (cpu_nr == boot_cpuid) { - do_gtod.tb_orig_stamp = tb_last_stamp; - /* Should update do_gtod.stamp_xsec. - * For now we leave it which means the time can be some - * number of msecs off until someone does a settimeofday() - */ - } - smp_tb_synchronized = 1; - } else { - if (cpu_nr == boot_cpuid) { - /* wait for all the others */ - while (atomic_read(&ready) < num_online_cpus()) - barrier(); - atomic_set(&ready, 1); - /* freeze the timebase */ - rtas_call(rtas_token("freeze-time-base"), 0, 1, NULL); - mb(); - frozen = 1; - set_tb(0, 0); - paca[boot_cpuid].next_jiffy_update_tb = 0; - smp_space_timers(); - while (atomic_read(&ready) < num_online_cpus()) - barrier(); - /* thaw the timebase again */ - rtas_call(rtas_token("thaw-time-base"), 0, 1, NULL); - mb(); - frozen = 0; - tb_last_stamp = get_tb(); - do_gtod.tb_orig_stamp = tb_last_stamp; - smp_tb_synchronized = 1; - } else { - atomic_inc(&ready); - while (!frozen) - barrier(); - set_tb(0, 0); - mb(); - atomic_inc(&ready); - while (frozen) - barrier(); + for (i = 0; i < NR_CPUS; i++) { + if (cpu_possible(i) && i != boot_cpuid) { + paca[i].next_jiffy_update_tb = + previous_tb + offset; + previous_tb = paca[i].next_jiffy_update_tb; } } +} +static void __devinit pSeries_setup_cpu(int cpu) +{ if (OpenPIC_Addr) { do_openpic_setup_cpu(); } else { - if (cpu_nr != boot_cpuid) + if (cpu != boot_cpuid) xics_setup_cpu(); } } @@ -347,26 +306,65 @@ } } -static void smp_xics_probe(void) +static int __init smp_xics_probe(void) +{ + int i; + int nr_cpus = 0; + + for (i = 0; i < NR_CPUS; i++) { + if (cpu_possible(i)) + nr_cpus++; + } + + return nr_cpus; +} + +static spinlock_t timebase_lock = SPIN_LOCK_UNLOCKED; +static unsigned long timebase = 0; + +static void __devinit pSeries_give_timebase(void) +{ + spin_lock(&timebase_lock); + rtas_call(rtas_token("freeze-time-base"), 0, 1, NULL); + timebase = get_tb(); + spin_unlock(&timebase_lock); + + while (timebase) + barrier(); + rtas_call(rtas_token("thaw-time-base"), 0, 1, NULL); +} + +static void __devinit pSeries_take_timebase(void) { + while (!timebase) + barrier(); + spin_lock(&timebase_lock); + set_tb(timebase >> 32, timebase & 0xffffffff); + timebase = 0; + spin_unlock(&timebase_lock); } /* This is called very early */ -void smp_init_pSeries(void) +void __init smp_init_pSeries(void) { - if(naca->interrupt_controller == IC_OPEN_PIC) { - ppc_md.smp_message_pass = smp_openpic_message_pass; - ppc_md.smp_probe = smp_chrp_probe; - ppc_md.smp_kick_cpu = smp_kick_cpu; - ppc_md.smp_setup_cpu = smp_chrp_setup_cpu; + smp_ops = &ppc_md.smp_ops; + + if (naca->interrupt_controller == IC_OPEN_PIC) { + smp_ops->message_pass = smp_openpic_message_pass; + smp_ops->probe = smp_chrp_probe; } else { - ppc_md.smp_message_pass = smp_xics_message_pass; - ppc_md.smp_probe = smp_xics_probe; - ppc_md.smp_kick_cpu = smp_kick_cpu; - ppc_md.smp_setup_cpu = smp_chrp_setup_cpu; + smp_ops->message_pass = smp_xics_message_pass; + smp_ops->probe = smp_xics_probe; + } + + if (naca->platform == PLATFORM_PSERIES) { + smp_ops->give_timebase = pSeries_give_timebase; + smp_ops->take_timebase = pSeries_take_timebase; } -} + smp_ops->kick_cpu = smp_kick_cpu; + smp_ops->setup_cpu = pSeries_setup_cpu; +} void smp_local_timer_interrupt(struct pt_regs * regs) { @@ -469,8 +467,7 @@ * hardware interrupt handler or from a bottom half handler. */ int smp_call_function (void (*func) (void *info), void *info, int nonatomic, - int wait) - + int wait) { struct call_data_struct data; int ret = -1, cpus = num_online_cpus()-1; @@ -553,38 +550,41 @@ atomic_inc(&call_data->finished); } - extern unsigned long decr_overclock; +extern struct gettimeofday_struct do_gtod; -struct thread_struct *current_set[NR_CPUS] = {&init_thread_union, 0}; +struct thread_info *current_set[NR_CPUS]; -void __init smp_boot_cpus(void) +static void __devinit smp_store_cpu_info(int id) { - int i, cpu_nr = 0; - struct task_struct *p; - - printk("Entering SMP Mode...\n"); + paca[id].pvr = _get_PVR(); +} - smp_store_cpu_info(boot_cpuid); - cpu_callin_map[boot_cpuid] = 1; +void __init smp_prepare_cpus(unsigned int max_cpus) +{ + int i; - /* XXX buggy - Anton */ - current_thread_info()->cpu = 0; + /* Fixup boot cpu */ + smp_store_cpu_info(smp_processor_id()); + cpu_callin_map[smp_processor_id()] = 1; for (i = 0; i < NR_CPUS; i++) { paca[i].prof_counter = 1; paca[i].prof_multiplier = 1; if (i != boot_cpuid) { + void *tmp; /* * the boot cpu segment table is statically * initialized to real address 0x5000. The * Other processor's tables are created and * initialized here. */ - paca[i].xStab_data.virt = (unsigned long)&stab_array[PAGE_SIZE * (i-1)]; - memset((void *)paca[i].xStab_data.virt, 0, PAGE_SIZE); - paca[i].xStab_data.real = __v2a(paca[i].xStab_data.virt); - paca[i].default_decr = tb_ticks_per_jiffy / decr_overclock; + tmp = &stab_array[PAGE_SIZE * (i-1)]; + memset(tmp, 0, PAGE_SIZE); + paca[i].xStab_data.virt = (unsigned long)tmp; + paca[i].xStab_data.real = (unsigned long)__v2a(tmp); + paca[i].default_decr = tb_ticks_per_jiffy / + decr_overclock; } } @@ -593,135 +593,83 @@ */ cache_decay_ticks = HZ/100; - ppc_md.smp_probe(); +#ifndef CONFIG_PPC_ISERIES + paca[boot_cpuid].next_jiffy_update_tb = tb_last_stamp = get_tb(); - for (i = 0; i < NR_CPUS; i++) { - if (paca[i].active) - cpu_nr++; - } - printk("Probe found %d CPUs\n", cpu_nr); - -#ifdef CONFIG_ISERIES - smp_space_timers(); + /* + * Should update do_gtod.stamp_xsec. + * For now we leave it which means the time can be some + * number of msecs off until someone does a settimeofday() + */ + do_gtod.tb_orig_stamp = tb_last_stamp; #endif - printk("Waiting for %d CPUs\n", cpu_nr-1); - - for (i = 1 ; i < NR_CPUS; i++) { - int c; - struct pt_regs regs; - - if (!paca[i].active) - continue; + max_cpus = smp_ops->probe(); + smp_space_timers(max_cpus); +} - if (i == boot_cpuid) - continue; +int __devinit __cpu_up(unsigned int cpu) +{ + struct pt_regs regs; + struct task_struct *p; + int c; - if (num_online_cpus() >= max_cpus) - break; + /* create a process for the processor */ + /* only regs.msr is actually used, and 0 is OK for it */ + memset(®s, 0, sizeof(struct pt_regs)); + p = do_fork(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0); + if (IS_ERR(p)) + panic("failed fork for CPU %u: %li", cpu, PTR_ERR(p)); - /* create a process for the processor */ - /* we don't care about the values in regs since we'll - never reschedule the forked task. */ - /* We DO care about one bit in the pt_regs we - pass to do_fork. That is the MSR_FP bit in - regs.msr. If that bit is on, then do_fork - (via copy_thread) will call giveup_fpu. - giveup_fpu will get a pointer to our (current's) - last register savearea via current->thread.regs - and using that pointer will turn off the MSR_FP, - MSR_FE0 and MSR_FE1 bits. At this point, this - pointer is pointing to some arbitrary point within - our stack */ - - memset(®s, 0, sizeof(struct pt_regs)); - - p = do_fork(CLONE_VM|CLONE_IDLETASK, 0, ®s, 0); - if (IS_ERR(p)) - panic("failed fork for CPU %d", i); - - init_idle(p, i); - - unhash_process(p); - - paca[i].xCurrent = (u64)p; - current_set[i] = p->thread_info; - - /* wake up cpus */ - ppc_md.smp_kick_cpu(i); - - /* - * wait to see if the cpu made a callin (is actually up). - * use this value that I found through experimentation. - * -- Cort - */ - for ( c = 5000; c && !cpu_callin_map[i] ; c-- ) { - udelay(100); - } - - if ( cpu_callin_map[i] ) - { - printk("Processor %d found.\n", i); - /* this sync's the decr's -- Cort */ - } else { - printk("Processor %d is stuck.\n", i); - } - } + init_idle(p, cpu); + unhash_process(p); - /* Setup boot cpu last (important) */ - ppc_md.smp_setup_cpu(boot_cpuid); + paca[cpu].xCurrent = (u64)p; + current_set[cpu] = p->thread_info; - if (num_online_cpus() < 2) { - tb_last_stamp = get_tb(); - smp_tb_synchronized = 1; - } -} + /* wake up cpus */ + smp_ops->kick_cpu(cpu); -void __init smp_commence(void) -{ /* - * Lets the callin's below out of their loop. + * wait to see if the cpu made a callin (is actually up). + * use this value that I found through experimentation. + * -- Cort */ - PPCDBG(PPCDBG_SMP, "smp_commence: start\n"); - wmb(); - smp_commenced = 1; -} + for (c = 5000; c && !cpu_callin_map[cpu]; c--) + udelay(100); -void __init smp_callin(void) -{ - int cpu = smp_processor_id(); - - smp_store_cpu_info(cpu); - set_dec(paca[cpu].default_decr); - set_bit(smp_processor_id(), &cpu_online_map); - smp_mb(); - cpu_callin_map[cpu] = 1; - - ppc_md.smp_setup_cpu(cpu); - - while(!smp_commenced) { - barrier(); + if (!cpu_callin_map[cpu]) { + printk("Processor %u is stuck.\n", cpu); + return -ENOENT; } - local_irq_enable(); -} -/* intel needs this */ -void __init initialize_secondary(void) -{ + printk("Processor %u found.\n", cpu); + + if (smp_ops->give_timebase) + smp_ops->give_timebase(); + set_bit(cpu, &cpu_online_map); + return 0; } /* Activate a secondary processor. */ -int start_secondary(void *unused) +int __devinit start_secondary(void *unused) { + unsigned int cpu = smp_processor_id(); + atomic_inc(&init_mm.mm_count); current->active_mm = &init_mm; - smp_callin(); - return cpu_idle(NULL); -} + smp_store_cpu_info(cpu); + set_dec(paca[cpu].default_decr); + cpu_callin_map[cpu] = 1; -void __init smp_setup(char *str, int *ints) -{ + smp_ops->setup_cpu(cpu); + if (smp_ops->take_timebase) + smp_ops->take_timebase(); + + local_irq_enable(); + + return cpu_idle(NULL); } int setup_profiling_timer(unsigned int multiplier) @@ -729,17 +677,10 @@ return 0; } -/* this function is called for each processor - */ -void __init smp_store_cpu_info(int id) +void __init smp_cpus_done(unsigned int max_cpus) { - paca[id].pvr = _get_PVR(); -} + smp_ops->setup_cpu(boot_cpuid); -static int __init maxcpus(char *str) -{ - get_option(&str, &max_cpus); - return 1; + /* XXX fix this, xics currently relies on it - Anton */ + smp_threads_ready = 1; } - -__setup("maxcpus=", maxcpus); diff -Nru a/arch/ppc64/kernel/sys32.S b/arch/ppc64/kernel/sys32.S --- a/arch/ppc64/kernel/sys32.S Fri Aug 16 14:35:00 2002 +++ b/arch/ppc64/kernel/sys32.S Fri Aug 16 14:35:00 2002 @@ -19,8 +19,6 @@ #include #include -/* NOTE: call as jump breaks return stack, we have to avoid that */ - .text _GLOBAL(sys32_mmap) @@ -31,14 +29,6 @@ _GLOBAL(sys32_lseek) extsw r4,r4 /* sign extend off_t offset parm */ b .sys_lseek - -_GLOBAL(sys32_chmod) -/* Ken Aaker.. hmmm maybe I don't need to do anything here */ - b .sys_chmod - -_GLOBAL(sys32_mknod) -/* Ken Aaker.. hmmm maybe I don't need to do anything here */ - b .sys_mknod _GLOBAL(sys32_sendto) clrldi r7, r7, 32 /* struct sockaddr *addr parm */ diff -Nru a/arch/ppc64/kernel/sys_ppc32.c b/arch/ppc64/kernel/sys_ppc32.c --- a/arch/ppc64/kernel/sys_ppc32.c Fri Aug 16 14:34:59 2002 +++ b/arch/ppc64/kernel/sys_ppc32.c Fri Aug 16 14:34:59 2002 @@ -95,8 +95,6 @@ int ret; char *filenam; - PPCDBG(PPCDBG_SYS32NI, "sys32_utime - running - filename=%s, times=%p - pid=%ld, comm=%s \n", filename, times, current->pid, current->comm); - if (!times) return sys_utime(filename, NULL); if (get_user(t.actime, ×->actime) || __get_user(t.modtime, ×->modtime)) @@ -225,8 +223,6 @@ struct file *file; long ret = -EBADF; - PPCDBG(PPCDBG_SYS32, "sys32_readv - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - file = fget(fd); if(!file) goto bad_file; @@ -237,7 +233,6 @@ fput(file); bad_file: - PPCDBG(PPCDBG_SYS32, "sys32_readv - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); return ret; } @@ -246,8 +241,6 @@ struct file *file; int ret = -EBADF; - PPCDBG(PPCDBG_SYS32, "sys32_writev - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - file = fget(fd); if(!file) goto bad_file; @@ -257,7 +250,6 @@ fput(file); bad_file: - PPCDBG(PPCDBG_SYS32, "sys32_writev - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); return ret; } @@ -404,8 +396,6 @@ unsigned long dir_page = 0; int err, is_smb, is_ncp; - PPCDBG(PPCDBG_SYS32, "sys32_mount - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - is_smb = is_ncp = 0; err = copy_mount_stuff_to_kernel((const void *)type, &type_page); @@ -460,23 +450,9 @@ free_page(type_page); out: - - PPCDBG(PPCDBG_SYS32, "sys32_mount - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - return err; } -struct dqblk32 { - __u32 dqb_bhardlimit; - __u32 dqb_bsoftlimit; - __u32 dqb_curblocks; - __u32 dqb_ihardlimit; - __u32 dqb_isoftlimit; - __u32 dqb_curinodes; - __kernel_time_t32 dqb_btime; - __kernel_time_t32 dqb_itime; -}; - /* readdir & getdents */ #define NAME_OFFSET(de) ((int) ((de)->d_name - (char *) (de))) #define ROUND_UP(x) (((x)+sizeof(u32)-1) & ~(sizeof(u32)-1)) @@ -594,8 +570,6 @@ struct getdents_callback32 buf; int error = -EBADF; - PPCDBG(PPCDBG_SYS32NI, "sys32_getdents - running - fd=%x, pid=%ld, comm=%s \n", fd, current->pid, current->comm); - file = fget(fd); if (!file) goto out; @@ -711,8 +685,6 @@ long timeout; int ret, size; - PPCDBG(PPCDBG_SYS32X, "sys32_select - entered - n=%x, inp=%p, outp=%p - pid=%ld comm=%s \n", n, inp, outp, current->pid, current->comm); - timeout = MAX_SCHEDULE_TIMEOUT; if (tvp) { time_t sec, usec; @@ -776,7 +748,7 @@ put_user(usec, &tvp->tv_usec); } - if (ret < 0) + if (ret < 0) goto out; if (!ret) { ret = -ERESTARTNOHAND; @@ -793,7 +765,6 @@ kfree(bits); out_nofds: - PPCDBG(PPCDBG_SYS32X, "sys32_select - exited - pid=%ld, comm=%s \n", current->pid, current->comm); return ret; } @@ -894,8 +865,6 @@ mm_segment_t old_fs = get_fs(); char *pth; - PPCDBG(PPCDBG_SYS32X, "sys32_statfs - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); - pth = getname (path); ret = PTR_ERR(pth); if (!IS_ERR(pth)) { @@ -907,8 +876,6 @@ return -EFAULT; } - PPCDBG(PPCDBG_SYS32X, "sys32_statfs - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - return ret; } @@ -920,16 +887,12 @@ struct statfs s; mm_segment_t old_fs = get_fs(); - PPCDBG(PPCDBG_SYS32X, "sys32_fstatfs - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); - set_fs (KERNEL_DS); ret = sys_fstatfs(fd, &s); set_fs (old_fs); if (put_statfs(buf, &s)) return -EFAULT; - PPCDBG(PPCDBG_SYS32X, "sys32_fstatfs - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); - return ret; } @@ -944,7 +907,6 @@ */ asmlinkage long sys32_sysfs(u32 option, u32 arg1, u32 arg2) { - PPCDBG(PPCDBG_SYS32, "sys32_sysfs - running - pid=%ld, comm=%s\n", current->pid, current->comm); return sys_sysfs((int)option, arg1, arg2); } @@ -961,10 +923,6 @@ unsigned long ret = -EINVAL; unsigned long new_addr = AA(__new_addr); - PPCDBG(PPCDBG_SYS32, "sys32_mremap - entered - pid=%ld current=%lx comm=%s\n", - current->pid, current, current->comm); - - if (old_len > 0xf0000000UL || new_len > 0xf0000000UL) goto out; if (addr > 0xf0000000UL - old_len) @@ -986,10 +944,6 @@ out_sem: up_write(¤t->mm->mmap_sem); out: - - PPCDBG(PPCDBG_SYS32, "sys32_mremap - exited - pid=%ld current=%lx comm=%s\n", - current->pid, current, current->comm); - return ret; } @@ -1017,8 +971,6 @@ struct timex txc; int ret; - PPCDBG(PPCDBG_SYS32, "sys32_adjtimex - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - memset(&txc, 0, sizeof(struct timex)); if(get_user(txc.modes, &utp->modes) || @@ -1081,9 +1033,6 @@ asmlinkage unsigned long sys32_create_module(const char *name_user, __kernel_size_t32 size) { - - PPCDBG(PPCDBG_SYS32M, "sys32_create_module - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); - return sys_create_module(name_user, (size_t)size); } @@ -1093,9 +1042,6 @@ asmlinkage long sys32_init_module(const char *name_user, struct module *mod_user) { - - PPCDBG(PPCDBG_SYS32, "sys32_init_module - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); - return sys_init_module(name_user, mod_user); } @@ -1105,9 +1051,6 @@ asmlinkage long sys32_delete_module(const char *name_user) { - - PPCDBG(PPCDBG_SYS32, "sys32_delete_module - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); - return sys_delete_module(name_user); } @@ -1384,9 +1327,6 @@ struct module *mod; int err; - PPCDBG(PPCDBG_SYS32M, "sys32_query_module - entered - pid=%ld current=%lx comm=%s\n", - current->pid, current, current->comm); - lock_kernel(); if (name_user == 0) { /* This finds "kernel_module" which is not exported. */ @@ -1439,8 +1379,6 @@ out: unlock_kernel(); - PPCDBG(PPCDBG_SYS32, "sys32_query_module - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); - return err; } @@ -1459,9 +1397,6 @@ struct kernel_sym *tbl; mm_segment_t old_fs; - PPCDBG(PPCDBG_SYS32, "sys32_get_kernel_syms - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - - len = sys_get_kernel_syms(NULL); if (!table) return len; tbl = kmalloc (len * sizeof (struct kernel_sym), GFP_KERNEL); @@ -1477,8 +1412,6 @@ } kfree (tbl); - PPCDBG(PPCDBG_SYS32, "sys32_get_kernel_syms - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - return i; } @@ -1486,23 +1419,16 @@ asmlinkage unsigned long sys32_create_module(const char *name_user, size_t size) { - - PPCDBG(PPCDBG_SYS32, "sys32_create_module - running - pid=%ld, comm=%s\n", current->pid, current->comm); - return -ENOSYS; } asmlinkage long sys32_init_module(const char *name_user, struct module *mod_user) { - PPCDBG(PPCDBG_SYS32, "sys32_init_module - running - pid=%ld, comm=%s\n", current->pid, current->comm); - return -ENOSYS; } asmlinkage long sys32_delete_module(const char *name_user) { - PPCDBG(PPCDBG_SYS32, "sys32_delete_module - running - pid=%ld, comm=%s\n", current->pid, current->comm); - return -ENOSYS; } @@ -1513,20 +1439,15 @@ */ asmlinkage long sys32_query_module(const char *name_user, u32 which, char *buf, size_t bufsize, size_t *ret) { - PPCDBG(PPCDBG_SYS32, "sys32_query_module - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); - /* Let the program know about the new interface. Not that it'll do them much good. */ if ((int)which == 0) return 0; - PPCDBG(PPCDBG_SYS32, "sys32_query_module - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); return -ENOSYS; } asmlinkage long sys32_get_kernel_syms(struct kernel_sym *table) { - PPCDBG(PPCDBG_SYS32, "sys32_get_kernel_syms - running - pid=%ld, comm=%s\n", current->pid, current->comm); - return -ENOSYS; } @@ -1893,8 +1814,6 @@ int ret; mm_segment_t old_fs = get_fs (); - PPCDBG(PPCDBG_SYS32NI, "sys32_nanosleep - running - pid=%ld, comm=%s \n", current->pid, current->comm); - if (get_user (t.tv_sec, &rqtp->tv_sec) || __get_user (t.tv_nsec, &rqtp->tv_nsec)) return -EFAULT; @@ -1916,9 +1835,6 @@ /* These are here just in case some old sparc32 binary calls it. */ asmlinkage long sys32_pause(void) { - - PPCDBG(PPCDBG_SYS32, "sys32_pause - running - pid=%ld, comm=%s \n", current->pid, current->comm); - current->state = TASK_INTERRUPTIBLE; schedule(); @@ -1974,14 +1890,10 @@ struct itimerval kit; int error; - PPCDBG(PPCDBG_SYS32, "sys32_getitimer - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - error = do_getitimer((int)which, &kit); if (!error && put_it32(it, &kit)) error = -EFAULT; - - PPCDBG(PPCDBG_SYS32, "sys32_getitimer - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); return error; } @@ -1999,8 +1911,6 @@ struct itimerval kin, kout; int error; - PPCDBG(PPCDBG_SYS32, "sys32_setitimer - entered - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); - if (in) { if (get_it32(&kin, in)) return -EFAULT; @@ -2013,8 +1923,6 @@ if (put_it32(out, &kout)) return -EFAULT; - - PPCDBG(PPCDBG_SYS32, "sys32_setitimer - exited - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); return 0; } @@ -2049,32 +1957,23 @@ { struct rlimit x; // 64-bit version of the resource limits. struct rlimit32 x32; // 32-bit version of the resource limits. - long rc = 0; - - if (resource >= RLIM_NLIMITS) { - PPCDBG(PPCDBG_SYS32, "sys32_old_getrlimit - specified resource is too large (%x) - pid=%ld, comm=%s\n", resource, current->pid, current->comm); + + if (resource >= RLIM_NLIMITS) return -EINVAL; - } memcpy(&x, current->rlim+resource, sizeof(struct rlimit)); - if(x.rlim_cur > RLIM_INFINITY32) + if (x.rlim_cur > RLIM_INFINITY32) x32.rlim_cur = RLIM_INFINITY32; else x32.rlim_cur = x.rlim_cur; - if(x.rlim_max > RLIM_INFINITY32) + if (x.rlim_max > RLIM_INFINITY32) x32.rlim_max = RLIM_INFINITY32; else x32.rlim_max = x.rlim_max; - rc = (copy_to_user(rlim, &x32, sizeof(x32))) ? (-EFAULT) : 0; - if (rc == 0) { - PPCDBG(PPCDBG_SYS32, "sys32_old_getrlimit - current=%x, maximum=%x - pid=%ld, comm=%s\n", x32.rlim_cur, x32.rlim_max, current->pid, current->comm); - } else { - PPCDBG(PPCDBG_SYS32, "sys32_old_getrlimit - unable to copy into user's storage - pid=%ld, comm=%s\n", current->pid, current->comm); - } - return rc; + return (copy_to_user(rlim, &x32, sizeof(x32))) ? (-EFAULT) : 0; } extern asmlinkage long sys_setrlimit(unsigned int resource, struct rlimit *rlim); @@ -2084,8 +1983,6 @@ long ret; mm_segment_t old_fs = get_fs (); - PPCDBG(PPCDBG_SYS32, "sys32_setrlimit - entered - resource=%x, rlim=%p - pid=%ld, comm=%s\n", resource, rlim, current->pid, current->comm); - if (resource >= RLIM_NLIMITS) return -EINVAL; if (get_user (r.rlim_cur, &rlim->rlim_cur) || __get_user (r.rlim_max, &rlim->rlim_max)) @@ -2098,7 +1995,6 @@ ret = sys_setrlimit(resource, &r); set_fs (old_fs); - PPCDBG(PPCDBG_SYS32, "sys32_setrlimit - exited w/ ret=%x - pid=%ld, comm=%s\n", ret, current->pid, current->comm); return ret; } @@ -2161,8 +2057,6 @@ int ret; mm_segment_t old_fs = get_fs(); - PPCDBG(PPCDBG_SYS32X, "sys32_getrusage - running - pid=%ld, comm=%s\n", current->pid, current->comm); - set_fs (KERNEL_DS); ret = sys_getrusage((int)who, &r); set_fs (old_fs); @@ -2196,8 +2090,6 @@ int ret, err; mm_segment_t old_fs = get_fs (); - PPCDBG(PPCDBG_SYS32, "sys32_sysinfo - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - set_fs (KERNEL_DS); ret = sys_sysinfo(&s); set_fs (old_fs); @@ -2215,8 +2107,6 @@ if (err) return -EFAULT; - PPCDBG(PPCDBG_SYS32, "sys32_sysinfo - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - return ret; } @@ -2230,9 +2120,6 @@ asmlinkage long sys32_gettimeofday(struct timeval32 *tv, struct timezone *tz) { - - PPCDBG(PPCDBG_SYS32X, "sys32_gettimeofday - running - pid=%ld, comm=%s\n", current->pid, current->comm); - if (tv) { struct timeval ktv; do_gettimeofday(&ktv); @@ -2254,8 +2141,6 @@ struct timeval ktv; struct timezone ktz; - PPCDBG(PPCDBG_SYS32, "sys32_settimeofday - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - if (tv) { if (get_tv32(&ktv, tv)) return -EFAULT; @@ -2269,8 +2154,6 @@ } - - struct tms32 { __kernel_clock_t32 tms_utime; __kernel_clock_t32 tms_stime; @@ -2287,8 +2170,6 @@ mm_segment_t old_fs = get_fs (); int err; - PPCDBG(PPCDBG_SYS32, "sys32_times - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - set_fs (KERNEL_DS); ret = sys_times(tbuf ? &t : NULL); set_fs (old_fs); @@ -2301,8 +2182,6 @@ ret = -EFAULT; } - PPCDBG(PPCDBG_SYS32, "sys32_times - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - return ret; } @@ -2847,9 +2726,6 @@ int third = (int)third_parm; int version, err; - PPCDBG(PPCDBG_SYS32, "sys32_ipc - entered - call=%x, parm1=%x, parm2=%x, parm3=%x, parm4=%x, parm5=%x \n", - call, first_parm, second_parm, third_parm, ptr, fifth); - version = call >> 16; /* hack for backward compatibility */ call &= 0xffff; @@ -2900,9 +2776,6 @@ err = -EINVAL; break; } - - - PPCDBG(PPCDBG_SYS32, "sys32_ipc - exited w/ %d/0x%x \n", err, err); return err; } @@ -2999,9 +2872,6 @@ asmlinkage long sys32_setsockopt(int fd, int level, int optname, char* optval, int optlen) { - - PPCDBG(PPCDBG_SYS32,"sys32_setsockopt - running - pid=%ld, comm=%s\n", current->pid, current->comm); - if (optname == SO_ATTACH_FILTER) { struct sock_fprog32 { __u16 len; @@ -3272,8 +3142,6 @@ struct msghdr kern_msg; int err, total_len; - PPCDBG(PPCDBG_SYS32, "sys32_sendmsg - entered - fd=%x, user_msg@=%p, user_flags=%x \n", fd, user_msg, user_flags); - if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) return -EFAULT; if(kern_msg.msg_iovlen > UIO_MAXIOV) @@ -3306,8 +3174,6 @@ if(kern_msg.msg_iov != iov) kfree(kern_msg.msg_iov); out: - - PPCDBG(PPCDBG_SYS32, "sys32_sendmsg - exited w/ %lx \n", err); return err; } @@ -3510,8 +3376,6 @@ unsigned long cmsg_ptr; int err, total_len, len = 0; - PPCDBG(PPCDBG_SYS32, "sys32_recvmsg - entered - fd=%x, user_msg@=%p, user_flags=%x \n", fd, user_msg, user_flags); - if(msghdr_from_user32_to_kern(&kern_msg, user_msg)) return -EFAULT; if(kern_msg.msg_iovlen > UIO_MAXIOV) @@ -3579,7 +3443,6 @@ if(err < 0) return err; - PPCDBG(PPCDBG_SYS32, "sys32_recvmsg - exited w/ %lx \n", len); return len; } @@ -3804,8 +3667,6 @@ */ asmlinkage long sys32_prctl(u32 option, u32 arg2, u32 arg3, u32 arg4, u32 arg5) { - PPCDBG(PPCDBG_SYS32, "sys32_prctl - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); - return sys_prctl((int)option, (unsigned long) arg2, (unsigned long) arg3, @@ -3826,8 +3687,6 @@ int ret; mm_segment_t old_fs = get_fs (); - PPCDBG(PPCDBG_SYS32, "sys32_sched_rr_get_interval - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - set_fs (KERNEL_DS); ret = sys_sched_rr_get_interval((int)pid, &t); set_fs (old_fs); @@ -3835,7 +3694,6 @@ __put_user (t.tv_nsec, &interval->tv_nsec)) return -EFAULT; - PPCDBG(PPCDBG_SYS32, "sys32_sched_rr_get_interval - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); return ret; } @@ -3844,9 +3702,6 @@ asmlinkage int sys32_pciconfig_read(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) { - - PPCDBG(PPCDBG_SYS32, "sys32_pciconfig_read - running - pid=%ld current=%lx comm=%s\n", current->pid, current, current->comm); - return sys_pciconfig_read((unsigned long) bus, (unsigned long) dfn, (unsigned long) off, @@ -3862,9 +3717,6 @@ asmlinkage int sys32_pciconfig_write(u32 bus, u32 dfn, u32 off, u32 len, u32 ubuf) { - - PPCDBG(PPCDBG_SYS32, "sys32_pciconfig_write - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - return sys_pciconfig_write((unsigned long) bus, (unsigned long) dfn, (unsigned long) off, @@ -3962,8 +3814,6 @@ */ asmlinkage long sys32_wait4(u32 pid, unsigned int * stat_addr, u32 options, struct rusage * ru) { - PPCDBG(PPCDBG_SYS32, "sys32_wait4 - running - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - if (!ru) return sys_wait4((int)pid, stat_addr, options, NULL); else { @@ -4062,30 +3912,6 @@ } -extern asmlinkage long sys_ioperm(unsigned long from, unsigned long num, int on); - -/* Note: it is necessary to treat on as an unsigned int, - * with the corresponding cast to a signed int to insure that the - * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) - * and the register representation of a signed int (msr in 64-bit mode) is performed. - */ -asmlinkage long sys32_ioperm(unsigned long from, unsigned long num, u32 on) -{ - return sys_ioperm(from, num, (int)on); -} - - -extern asmlinkage int sys_iopl(int a1, int a2, int a3, int a4); - -/* Note: it is necessary to treat a1, a2, a3, and a4 as unsigned ints, - * with the corresponding cast to a signed int to insure that the - * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) - * and the register representation of a signed int (msr in 64-bit mode) is performed. - */ -asmlinkage int sys32_iopl(u32 a1, u32 a2, u32 a3, u32 a4) -{ - return sys_iopl((int)a1, (int)a2, (int)a3, (int)a4); -} extern asmlinkage long sys_kill(int pid, int sig); @@ -4127,19 +3953,6 @@ } -extern asmlinkage int sys_modify_ldt(int a1, int a2, int a3, int a4); - -/* Note: it is necessary to treat a1, a2, a3, and a4 as unsigned ints, - * with the corresponding cast to a signed int to insure that the - * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) - * and the register representation of a signed int (msr in 64-bit mode) is performed. - */ -asmlinkage int sys32_modify_ldt(u32 a1, u32 a2, u32 a3, u32 a4) -{ - return sys_modify_ldt((int)a1, (int)a2, (int)a3, (int)a4); -} - - extern asmlinkage long sys_msync(unsigned long start, size_t len, int flags); /* Note: it is necessary to treat flags as an unsigned int, @@ -4426,42 +4239,32 @@ } -extern asmlinkage int sys_vm86(int a1, int a2, int a3, int a4); - -/* Note: it is necessary to treat a1, a2, a3, and a4 as unsigned ints, - * with the corresponding cast to a signed int to insure that the - * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) - * and the register representation of a signed int (msr in 64-bit mode) is performed. - */ -asmlinkage int sys32_vm86(u32 a1, u32 a2, u32 a3, u32 a4) -{ - return sys_vm86((int)a1, (int)a2, (int)a3, (int)a4); -} - - - - - -extern asmlinkage ssize_t sys_pread(unsigned int fd, char * buf, - size_t count, loff_t pos); +extern ssize_t sys_pread64(unsigned int fd, char *buf, size_t count, + loff_t pos); -extern asmlinkage ssize_t sys_pwrite(unsigned int fd, const char * buf, - size_t count, loff_t pos); +extern ssize_t sys_pwrite64(unsigned int fd, const char *buf, size_t count, + loff_t pos); typedef __kernel_ssize_t32 ssize_t32; -asmlinkage ssize_t32 sys32_pread(unsigned int fd, char *ubuf, - __kernel_size_t32 count, u32 reg6, u32 poshi, u32 poslo) +ssize_t32 sys32_pread64(unsigned int fd, char *ubuf, __kernel_size_t32 count, + u32 reg6, u32 poshi, u32 poslo) { - return sys_pread(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); + return sys_pread64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); } -asmlinkage ssize_t32 sys32_pwrite(unsigned int fd, char *ubuf, - __kernel_size_t32 count, u32 reg6 ,u32 poshi, u32 poslo) +ssize_t32 sys32_pwrite64(unsigned int fd, char *ubuf, __kernel_size_t32 count, + u32 reg6 ,u32 poshi, u32 poslo) { - return sys_pwrite(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); + return sys_pwrite64(fd, ubuf, count, ((loff_t)AA(poshi) << 32) | AA(poslo)); } +extern ssize_t sys_readahead(int fd, loff_t offset, size_t count); + +ssize_t32 sys32_readahead(int fd, u32 offhi, u32 offlo, s32 count) +{ + return sys_readahead(fd, ((loff_t)AA(offhi) << 32) | AA(offlo), count); +} extern asmlinkage long sys_truncate(const char * path, unsigned long length); extern asmlinkage long sys_ftruncate(unsigned int fd, unsigned long length); diff -Nru a/arch/ppc64/kernel/syscalls.c b/arch/ppc64/kernel/syscalls.c --- a/arch/ppc64/kernel/syscalls.c Fri Aug 16 14:34:54 2002 +++ b/arch/ppc64/kernel/syscalls.c Fri Aug 16 14:34:54 2002 @@ -52,30 +52,11 @@ { } -asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on) +int sys_ioperm(unsigned long from, unsigned long num, int on) { - printk(KERN_ERR "sys_ioperm()\n"); return -EIO; } -int sys_iopl(int a1, int a2, int a3, int a4) -{ - printk(KERN_ERR "sys_iopl(%x, %x, %x, %x)!\n", a1, a2, a3, a4); - return (-ENOSYS); -} - -int sys_vm86(int a1, int a2, int a3, int a4) -{ - printk(KERN_ERR "sys_vm86(%x, %x, %x, %x)!\n", a1, a2, a3, a4); - return (-ENOSYS); -} - -int sys_modify_ldt(int a1, int a2, int a3, int a4) -{ - printk(KERN_ERR "sys_modify_ldt(%x, %x, %x, %x)!\n", a1, a2, a3, a4); - return (-ENOSYS); -} - /* * sys_ipc() is the de-multiplexer for the SysV IPC calls.. * @@ -86,8 +67,6 @@ { int version, ret; - PPCDBG(PPCDBG_SYS64X, "sys_ipc - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - version = call >> 16; /* hack for backward compatibility */ call &= 0xffff; @@ -174,7 +153,6 @@ break; } - PPCDBG(PPCDBG_SYS64X, "sys_ipc - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); return ret; } @@ -187,32 +165,27 @@ int fd[2]; int error; - PPCDBG(PPCDBG_SYS64X, "sys_pipe - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - error = do_pipe(fd); if (!error) { if (copy_to_user(fildes, fd, 2*sizeof(int))) error = -EFAULT; } - PPCDBG(PPCDBG_SYS64X, "sys_pipe - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); return error; } -asmlinkage unsigned long sys_mmap(unsigned long addr, size_t len, - unsigned long prot, unsigned long flags, - unsigned long fd, off_t offset) +unsigned long sys_mmap(unsigned long addr, size_t len, + unsigned long prot, unsigned long flags, + unsigned long fd, off_t offset) { struct file * file = NULL; unsigned long ret = -EBADF; - PPCDBG(PPCDBG_SYS64X, "sys_mmap - entered - addr=%lx, len=%lx - pid=%ld, comm=%s \n", addr, len, current->pid, current->comm); - if (!(flags & MAP_ANONYMOUS)) { if (!(file = fget(fd))) goto out; } - + flags &= ~(MAP_EXECUTABLE | MAP_DENYWRITE); down_write(¤t->mm->mmap_sem); ret = do_mmap(file, addr, len, prot, flags, offset); @@ -221,9 +194,6 @@ fput(file); out: - - PPCDBG(PPCDBG_SYS64X, "sys_mmap - exited - ret=%x \n", ret); - return ret; } @@ -240,14 +210,11 @@ { int err = -EFAULT; - PPCDBG(PPCDBG_SYS64X, "sys_uname - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - down_read(&uts_sem); if (name && !copy_to_user(name, &system_utsname, sizeof (*name))) err = 0; up_read(&uts_sem); - PPCDBG(PPCDBG_SYS64X, "sys_uname - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); return err; } @@ -255,8 +222,6 @@ { int error; - PPCDBG(PPCDBG_SYS64X, "sys_olduname - entered - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); - if (!name) return -EFAULT; if (!access_ok(VERIFY_WRITE,name,sizeof(struct oldold_utsname))) @@ -277,7 +242,6 @@ error = error ? -EFAULT : 0; - PPCDBG(PPCDBG_SYS64X, "sys_olduname - exited - pid=%ld current=%lx comm=%s \n", current->pid, current, current->comm); return error; } diff -Nru a/arch/ppc64/kernel/time.c b/arch/ppc64/kernel/time.c --- a/arch/ppc64/kernel/time.c Fri Aug 16 14:35:00 2002 +++ b/arch/ppc64/kernel/time.c Fri Aug 16 14:35:00 2002 @@ -60,6 +60,7 @@ #include #include +#include void smp_local_timer_interrupt(struct pt_regs *); @@ -293,9 +294,6 @@ irq_exit(); - if (softirq_pending(cpu)) - do_softirq(); - return 1; } diff -Nru a/arch/ppc64/kernel/traps.c b/arch/ppc64/kernel/traps.c --- a/arch/ppc64/kernel/traps.c Fri Aug 16 14:34:58 2002 +++ b/arch/ppc64/kernel/traps.c Fri Aug 16 14:34:58 2002 @@ -128,13 +128,12 @@ SystemResetException(struct pt_regs *regs) { if (fwnmi_active) { - char *msg; unsigned long *r3 = __va(regs->gpr[3]); /* for FWNMI debug */ struct rtas_error_log *errlog; - msg = "FWNMI is active with save area at %016lx\n"; - udbg_printf(msg, r3); printk(msg, r3); + udbg_printf("FWNMI is active with save area at %016lx\n", r3); errlog = FWNMI_get_errinfo(regs); + FWNMI_release_errinfo(); } if (debugger) @@ -241,14 +240,12 @@ static void parse_fpe(struct pt_regs *regs) { siginfo_t info; - unsigned int *tmp; - unsigned int fpscr; + unsigned long fpscr; if (regs->msr & MSR_FP) giveup_fpu(current); - tmp = ¤t->thread.fpscr; - fpscr = *tmp; + fpscr = current->thread.fpscr; /* Invalid operation */ if ((fpscr & FPSCR_VE) && (fpscr & FPSCR_VX)) diff -Nru a/arch/ppc64/kernel/xics.c b/arch/ppc64/kernel/xics.c --- a/arch/ppc64/kernel/xics.c Fri Aug 16 14:34:51 2002 +++ b/arch/ppc64/kernel/xics.c Fri Aug 16 14:34:51 2002 @@ -373,7 +373,7 @@ if (naca->platform == PLATFORM_PSERIES) { #ifdef CONFIG_SMP for (i = 0; i < NR_CPUS; ++i) { - if (!paca[i].active) + if (!cpu_possible(i)) continue; xics_info.per_cpu[i] = __ioremap((ulong)inodes[i].addr, diff -Nru a/arch/ppc64/lib/Makefile b/arch/ppc64/lib/Makefile --- a/arch/ppc64/lib/Makefile Fri Aug 16 14:34:51 2002 +++ b/arch/ppc64/lib/Makefile Fri Aug 16 14:34:51 2002 @@ -4,6 +4,8 @@ O_TARGET = lib.o +export-objs := dec_and_lock.o + obj-y := checksum.o dec_and_lock.o string.o strcase.o copypage.o \ memcpy.o copyuser.o diff -Nru a/arch/ppc64/lib/dec_and_lock.c b/arch/ppc64/lib/dec_and_lock.c --- a/arch/ppc64/lib/dec_and_lock.c Fri Aug 16 14:34:56 2002 +++ b/arch/ppc64/lib/dec_and_lock.c Fri Aug 16 14:34:56 2002 @@ -7,32 +7,49 @@ * 2 of the License, or (at your option) any later version. */ +#include #include -#include #include +#include +/* + * This is an implementation of the notion of "decrement a + * reference count, and return locked if it decremented to zero". + * + * This implementation can be used on any architecture that + * has a cmpxchg, and where atomic->value is an int holding + * the value of the atomic (i.e. the high bits aren't used + * for a lock or anything like that). + * + * N.B. ATOMIC_DEC_AND_LOCK gets defined in include/linux/spinlock.h + * if spinlocks are empty and thus atomic_dec_and_lock is defined + * to be atomic_dec_and_test - in that case we don't need it + * defined here as well. + */ + +#ifndef ATOMIC_DEC_AND_LOCK int atomic_dec_and_lock(atomic_t *atomic, spinlock_t *lock) { int counter; int newcount; -repeat: - counter = atomic_read(atomic); - newcount = counter-1; - - if (!newcount) - goto slow_path; + for (;;) { + counter = atomic_read(atomic); + newcount = counter - 1; + if (!newcount) + break; /* do it the slow way */ + + newcount = cmpxchg(&atomic->counter, counter, newcount); + if (newcount == counter) + return 0; + } - newcount = cmpxchg(&atomic->counter, counter, newcount); - - if (newcount != counter) - goto repeat; - return 0; - -slow_path: spin_lock(lock); if (atomic_dec_and_test(atomic)) return 1; spin_unlock(lock); return 0; } + +EXPORT_SYMBOL(atomic_dec_and_lock); +#endif /* ATOMIC_DEC_AND_LOCK */ diff -Nru a/arch/ppc64/mm/imalloc.c b/arch/ppc64/mm/imalloc.c --- a/arch/ppc64/mm/imalloc.c Fri Aug 16 14:34:55 2002 +++ b/arch/ppc64/mm/imalloc.c Fri Aug 16 14:34:55 2002 @@ -13,6 +13,7 @@ #include #include +#include rwlock_t imlist_lock = RW_LOCK_UNLOCKED; struct vm_struct * imlist = NULL; diff -Nru a/arch/ppc64/mm/init.c b/arch/ppc64/mm/init.c --- a/arch/ppc64/mm/init.c Fri Aug 16 14:34:58 2002 +++ b/arch/ppc64/mm/init.c Fri Aug 16 14:34:58 2002 @@ -566,7 +566,8 @@ */ void flush_dcache_page(struct page *page) { - clear_bit(PG_arch_1, &page->flags); + if (test_bit(PG_arch_1, &page->flags)) + clear_bit(PG_arch_1, &page->flags); } void flush_icache_page(struct vm_area_struct *vma, struct page *page) @@ -589,10 +590,12 @@ clear_page(page); /* XXX we shouldnt have to do this, but glibc requires it */ - if (cpu_has_noexecute()) - clear_bit(PG_arch_1, &pg->flags); - else + if (cpu_has_noexecute()) { + if (test_bit(PG_arch_1, &pg->flags)) + clear_bit(PG_arch_1, &pg->flags); + } else { __flush_dcache_icache(page); + } } void copy_user_page(void *vto, void *vfrom, unsigned long vaddr, @@ -609,10 +612,12 @@ return; #endif - if (cpu_has_noexecute()) - clear_bit(PG_arch_1, &pg->flags); - else + if (cpu_has_noexecute()) { + if (test_bit(PG_arch_1, &pg->flags)) + clear_bit(PG_arch_1, &pg->flags); + } else { __flush_dcache_icache(vto); + } } void flush_icache_user_range(struct vm_area_struct *vma, struct page *page, diff -Nru a/arch/sh/config.in b/arch/sh/config.in --- a/arch/sh/config.in Fri Aug 16 14:34:58 2002 +++ b/arch/sh/config.in Fri Aug 16 14:34:58 2002 @@ -207,10 +207,6 @@ source drivers/md/Config.in -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - mainmenu_option next_comment comment 'ATA/ATAPI/MFM/RLL support' @@ -236,6 +232,8 @@ source drivers/ieee1394/Config.in if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in + mainmenu_option next_comment comment 'Network device support' diff -Nru a/arch/sparc/config.in b/arch/sparc/config.in --- a/arch/sparc/config.in Fri Aug 16 14:34:52 2002 +++ b/arch/sparc/config.in Fri Aug 16 14:34:53 2002 @@ -87,10 +87,6 @@ endmenu -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - # Don't frighten a common SBus user if [ "$CONFIG_PCI" = "y" ]; then @@ -158,6 +154,8 @@ source drivers/fc4/Config.in if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in + mainmenu_option next_comment comment 'Network device support' diff -Nru a/arch/sparc/kernel/ebus.c b/arch/sparc/kernel/ebus.c --- a/arch/sparc/kernel/ebus.c Fri Aug 16 14:35:01 2002 +++ b/arch/sparc/kernel/ebus.c Fri Aug 16 14:35:01 2002 @@ -24,9 +24,6 @@ struct linux_ebus *ebus_chain = 0; -#ifdef CONFIG_SUN_AUXIO -extern void auxio_probe(void); -#endif extern void rs_init(void); /* We are together with pcic.c under CONFIG_PCI. */ @@ -366,7 +363,4 @@ } rs_init(); -#ifdef CONFIG_SUN_AUXIO - auxio_probe(); -#endif } diff -Nru a/arch/sparc/kernel/irq.c b/arch/sparc/kernel/irq.c --- a/arch/sparc/kernel/irq.c Fri Aug 16 14:34:52 2002 +++ b/arch/sparc/kernel/irq.c Fri Aug 16 14:34:52 2002 @@ -415,7 +415,7 @@ extern void smp4m_irq_rotate(int cpu); #endif - irq_enter(cpu, irq); + irq_enter(); disable_pil_irq(irq); #ifdef CONFIG_SMP /* Only rotate on lower priority IRQ's (scsi, ethernet, etc.). */ @@ -431,9 +431,7 @@ action = action->next; } while (action); enable_pil_irq(irq); - irq_exit(cpu, irq); - if (softirq_pending(cpu)) - do_softirq(); + irq_exit(); } #ifdef CONFIG_BLK_DEV_FD @@ -444,13 +442,14 @@ int cpu = smp_processor_id(); disable_pil_irq(irq); - irq_enter(cpu, irq); + irq_enter(); kstat.irqs[cpu][irq]++; floppy_interrupt(irq, dev_id, regs); - irq_exit(cpu, irq); + irq_exit(); enable_pil_irq(irq); - if (softirq_pending(cpu)) - do_softirq(); + // XXX Eek, it's totally changed with preempt_count() and such + // if (softirq_pending(cpu)) + // do_softirq(); } #endif @@ -644,7 +643,7 @@ extern void sun4c_init_IRQ( void ); extern void sun4m_init_IRQ( void ); extern void sun4d_init_IRQ( void ); - + switch(sparc_cpu_model) { case sun4c: case sun4: diff -Nru a/arch/sparc/kernel/pcic.c b/arch/sparc/kernel/pcic.c --- a/arch/sparc/kernel/pcic.c Fri Aug 16 14:34:51 2002 +++ b/arch/sparc/kernel/pcic.c Fri Aug 16 14:34:51 2002 @@ -55,10 +55,6 @@ #else -#ifdef CONFIG_SUN_JSFLASH -extern int jsflash_init(void); -#endif - struct pci_fixup pcibios_fixups[] = { { 0 } }; @@ -435,7 +431,7 @@ /* * Main entry point from the PCI subsystem. */ -void __init pcibios_init(void) +static int __init pcibios_init(void) { struct linux_pcic *pcic; @@ -444,7 +440,7 @@ * So, here we report the presence of PCIC and do some magic passes. */ if(!pcic0_up) - return; + return 0; pcic = &pcic0; /* @@ -465,9 +461,7 @@ pcic_pbm_scan_bus(pcic); ebus_init(); -#ifdef CONFIG_SUN_JSFLASH - jsflash_init(); -#endif + return 0; } int pcic_present(void) @@ -1037,3 +1031,5 @@ } #endif + +subsys_initcall(pcibios_init); diff -Nru a/arch/sparc/kernel/rtrap.S b/arch/sparc/kernel/rtrap.S --- a/arch/sparc/kernel/rtrap.S Fri Aug 16 14:34:56 2002 +++ b/arch/sparc/kernel/rtrap.S Fri Aug 16 14:34:56 2002 @@ -86,7 +86,8 @@ wr %t_psr, 0x0, %psr WRITE_PAUSE - ld [%curptr + AOFF_task_thread + AOFF_thread_w_saved], %twin_tmp1 + ld [%curptr + TI_TASK], %o5 + ld [%o5 + AOFF_task_thread + AOFF_thread_w_saved], %twin_tmp1 orcc %g0, %twin_tmp1, %g0 be ret_trap_nobufwins nop diff -Nru a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c --- a/arch/sparc/kernel/setup.c Fri Aug 16 14:34:52 2002 +++ b/arch/sparc/kernel/setup.c Fri Aug 16 14:34:52 2002 @@ -130,7 +130,7 @@ static void prom_console_write(struct console *con, const char *s, unsigned n) { - prom_printf("%s", s); + prom_write(s, n); } static struct console prom_debug_console = { diff -Nru a/arch/sparc/kernel/sun4d_irq.c b/arch/sparc/kernel/sun4d_irq.c --- a/arch/sparc/kernel/sun4d_irq.c Fri Aug 16 14:34:50 2002 +++ b/arch/sparc/kernel/sun4d_irq.c Fri Aug 16 14:34:50 2002 @@ -198,7 +198,7 @@ cc_set_iclr(1 << irq); - irq_enter(cpu, irq); + irq_enter(); kstat.irqs[cpu][irq]++; if (!sbusl) { action = *(irq + irq_action); @@ -239,9 +239,7 @@ } } } - irq_exit(cpu, irq); - if (softirq_pending(cpu)) - do_softirq(); + irq_exit(); } unsigned int sun4d_build_irq(struct sbus_dev *sdev, int irq) diff -Nru a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c --- a/arch/sparc/kernel/sun4d_smp.c Fri Aug 16 14:34:53 2002 +++ b/arch/sparc/kernel/sun4d_smp.c Fri Aug 16 14:34:53 2002 @@ -458,9 +458,9 @@ if(!--prof_counter[cpu]) { int user = user_mode(regs); - irq_enter(cpu, 0); + irq_enter(); update_process_times(user); - irq_exit(cpu, 0); + irq_exit(); prof_counter[cpu] = prof_multiplier[cpu]; } diff -Nru a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c --- a/arch/sparc/kernel/sun4m_smp.c Fri Aug 16 14:35:00 2002 +++ b/arch/sparc/kernel/sun4m_smp.c Fri Aug 16 14:35:00 2002 @@ -445,9 +445,9 @@ if(!--prof_counter[cpu]) { int user = user_mode(regs); - irq_enter(cpu, 0); + irq_enter(); update_process_times(user); - irq_exit(cpu, 0); + irq_exit(); prof_counter[cpu] = prof_multiplier[cpu]; } diff -Nru a/arch/sparc/mm/srmmu.c b/arch/sparc/mm/srmmu.c --- a/arch/sparc/mm/srmmu.c Fri Aug 16 14:34:52 2002 +++ b/arch/sparc/mm/srmmu.c Fri Aug 16 14:34:52 2002 @@ -49,6 +49,26 @@ #include +/* + * To support pagetables in highmem, Linux introduces APIs which + * return struct page* and generally manipulate page tables when + * they are not mapped into kernel space. Our hardware page tables + * are smaller than pages. We lump hardware tabes into big, page sized + * software tables. + * + * PMD_SHIFT determines the size of the area a second-level page table entry + * can map, and our pmd_t is 16 times larger than normal. + */ +#define SRMMU_PTRS_PER_PMD_SOFT 0x4 /* Each pmd_t contains 16 hard PTPs */ +#define SRMMU_PTRS_PER_PTE_SOFT 0x400 /* 16 hard tables per 4K page */ +#define SRMMU_PTE_SZ_SOFT 0x1000 /* same as above, in bytes */ + +#define SRMMU_PMD_SHIFT_SOFT 22 +#define SRMMU_PMD_SIZE_SOFT (1UL << SRMMU_PMD_SHIFT_SOFT) +#define SRMMU_PMD_MASK_SOFT (~(SRMMU_PMD_SIZE_SOFT-1)) +// #define SRMMU_PMD_ALIGN(addr) (((addr)+SRMMU_PMD_SIZE-1)&SRMMU_PMD_MASK) + + enum mbus_module srmmu_modtype; unsigned int hwbug_bitmask; int vac_cache_size; @@ -129,36 +149,30 @@ #define __nocache_va(PADDR) (__va((unsigned long)PADDR) - (unsigned long)srmmu_nocache_pool + SRMMU_NOCACHE_VADDR) #define __nocache_fix(VADDR) __va(__nocache_pa(VADDR)) -static inline unsigned long srmmu_pgd_page(pgd_t pgd) -{ return srmmu_device_memory(pgd_val(pgd))?~0:(unsigned long)__nocache_va((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4); } - -static inline unsigned long srmmu_pmd_page_kernel(pmd_t pmd) -{ - return (unsigned long) - __nocache_va((pmd_val(pmd) & SRMMU_PTD_PMASK) << 4); -} - -static struct page *srmmu_pmd_page(pmd_t pmd) /* XXX inline later */ +static inline unsigned long srmmu_pte_pfn(pte_t pte) { - - if (srmmu_device_memory(pmd_val(pmd))) { + if (srmmu_device_memory(pte_val(pte))) { /* XXX Anton obviously had something in mind when he did this. * But what? */ /* return (struct page *)~0; */ - BUG(); /* NO WAY */ + BUG(); } - return virt_to_page(srmmu_pmd_page_kernel(pmd)); + return (pte_val(pte) & SRMMU_PTE_PMASK) >> (PAGE_SHIFT-4); } -static inline unsigned long srmmu_pte_pfn(pte_t pte) +static struct page *srmmu_pmd_page(pmd_t pmd) { - if (srmmu_device_memory(pte_val(pte))) + + if (srmmu_device_memory(pmd_val(pmd))) BUG(); - return (unsigned long) - (((pte_val(pte) & SRMMU_PTE_PMASK) << 4) >> PAGE_SHIFT); + return pfn_to_page((pmd_val(pmd) & SRMMU_PTD_PMASK) >> (PAGE_SHIFT-4)); } +static inline unsigned long srmmu_pgd_page(pgd_t pgd) +{ return srmmu_device_memory(pgd_val(pgd))?~0:(unsigned long)__nocache_va((pgd_val(pgd) & SRMMU_PTD_PMASK) << 4); } + + static inline int srmmu_pte_none(pte_t pte) { return !(pte_val(pte) & 0xFFFFFFF); } @@ -177,8 +191,11 @@ static inline int srmmu_pmd_present(pmd_t pmd) { return ((pmd_val(pmd) & SRMMU_ET_MASK) == SRMMU_ET_PTD); } -static inline void srmmu_pmd_clear(pmd_t *pmdp) -{ srmmu_set_pte((pte_t *)pmdp, __pte(0)); } +static inline void srmmu_pmd_clear(pmd_t *pmdp) { + int i; + for (i = 0; i < SRMMU_PTRS_PER_PTE_SOFT/SRMMU_PTRS_PER_PTE; i++) + srmmu_set_pte((pte_t *)&pmdp->pmdv[i], __pte(0)); +} static inline int srmmu_pgd_none(pgd_t pgd) { return !(pgd_val(pgd) & 0xFFFFFFF); } @@ -224,7 +241,7 @@ * and a page entry and page directory to the page they refer to. */ static pte_t srmmu_mk_pte(struct page *page, pgprot_t pgprot) -{ return __pte((((page - mem_map) << PAGE_SHIFT) >> 4) | pgprot_val(pgprot)); } +{ return __pte(((page - mem_map) << (PAGE_SHIFT-4)) | pgprot_val(pgprot)); } static pte_t srmmu_mk_pte_phys(unsigned long page, pgprot_t pgprot) { return __pte(((page) >> 4) | pgprot_val(pgprot)); } @@ -239,16 +256,28 @@ static inline void srmmu_pgd_set(pgd_t * pgdp, pmd_t * pmdp) { srmmu_set_pte((pte_t *)pgdp, (SRMMU_ET_PTD | (__nocache_pa((unsigned long) pmdp) >> 4))); } -static inline void srmmu_pmd_set(pmd_t * pmdp, pte_t * ptep) +static void srmmu_pmd_set(pmd_t *pmdp, pte_t *ptep) { - srmmu_set_pte((pte_t *)pmdp, - (SRMMU_ET_PTD | (__nocache_pa((unsigned long) ptep) >> 4))); + unsigned long ptp; /* Physical address, shifted right by 4 */ + int i; + + ptp = __nocache_pa((unsigned long) ptep) >> 4; + for (i = 0; i < SRMMU_PTRS_PER_PTE_SOFT/SRMMU_PTRS_PER_PTE; i++) { + srmmu_set_pte((pte_t *)&pmdp->pmdv[i], SRMMU_ET_PTD | ptp); + ptp += (SRMMU_PTRS_PER_PTE*sizeof(pte_t) >> 4); + } } -static inline void srmmu_pmd_populate(pmd_t * pmdp, struct page * ptep) +static void srmmu_pmd_populate(pmd_t *pmdp, struct page *ptep) { - srmmu_set_pte((pte_t *)pmdp, - (SRMMU_ET_PTD | (((ptep - mem_map) << PAGE_SHIFT) >> 4))); + unsigned long ptp; /* Physical address, shifted right by 4 */ + int i; + + ptp = (ptep - mem_map) << (PAGE_SHIFT-4); /* watch for overflow */ + for (i = 0; i < SRMMU_PTRS_PER_PTE_SOFT/SRMMU_PTRS_PER_PTE; i++) { + srmmu_set_pte((pte_t *)&pmdp->pmdv[i], SRMMU_ET_PTD | ptp); + ptp += (SRMMU_PTRS_PER_PTE*sizeof(pte_t) >> 4); + } } static inline pte_t srmmu_pte_modify(pte_t pte, pgprot_t newprot) @@ -260,19 +289,27 @@ /* Find an entry in the second-level page table.. */ static inline pmd_t *srmmu_pmd_offset(pgd_t * dir, unsigned long address) -{ return (pmd_t *) srmmu_pgd_page(*dir) + ((address >> SRMMU_PMD_SHIFT) & (SRMMU_PTRS_PER_PMD - 1)); } +{ + return (pmd_t *) srmmu_pgd_page(*dir) + + ((address >> SRMMU_PMD_SHIFT_SOFT) & (SRMMU_PTRS_PER_PMD_SOFT - 1)); +} /* Find an entry in the third-level page table.. */ static inline pte_t *srmmu_pte_offset(pmd_t * dir, unsigned long address) { - unsigned long pte; + void *pte; - pte = srmmu_pmd_page_kernel(*dir); + pte = __nocache_va((dir->pmdv[0] & SRMMU_PTD_PMASK) << 4); return (pte_t *) pte + - ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE - 1)); + ((address >> PAGE_SHIFT) & (SRMMU_PTRS_PER_PTE_SOFT - 1)); } -unsigned long __srmmu_get_nocache(int size, int align) +/* + * size: bytes to allocate in the nocache area. + * align: bytes, number to align at. + * Returns the virtual address of the allocated area. + */ +static unsigned long __srmmu_get_nocache(int size, int align) { int offset = srmmu_nocache_low; int i; @@ -423,36 +460,55 @@ srmmu_free_nocache((unsigned long)pgd, SRMMU_PGD_TABLE_SIZE); } -static pte_t *srmmu_pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) +static pmd_t *srmmu_pmd_alloc_one(struct mm_struct *mm, unsigned long address) { - return (pte_t *)srmmu_get_nocache(SRMMU_PTE_TABLE_SIZE, SRMMU_PTE_TABLE_SIZE); + return (pmd_t *)srmmu_get_nocache(SRMMU_PMD_TABLE_SIZE, SRMMU_PMD_TABLE_SIZE); } -static struct page *srmmu_pte_alloc_one(struct mm_struct *mm, unsigned long address) +static void srmmu_pmd_free(pmd_t * pmd) { - return virt_to_page(srmmu_pte_alloc_one_kernel(mm, address)); + srmmu_free_nocache((unsigned long)pmd, SRMMU_PMD_TABLE_SIZE); } -static void srmmu_free_pte_fast(pte_t *pte) +/* + * Hardware needs alignment to 256 only, but we align to whole page size + * to reduce fragmentation problems due to the buddy principle. + * XXX Provide actual fragmentation statistics in /proc. + * + * Alignments up to the page size are the same for physical and virtual + * addresses of the nocache area. + */ +static pte_t * +srmmu_pte_alloc_one_kernel(struct mm_struct *mm, unsigned long address) { - srmmu_free_nocache((unsigned long)pte, SRMMU_PTE_TABLE_SIZE); + return (pte_t *)srmmu_get_nocache(SRMMU_PTE_SZ_SOFT, SRMMU_PTE_SZ_SOFT); } -static void srmmu_pte_free(struct page *pte) +static struct page * +srmmu_pte_alloc_one(struct mm_struct *mm, unsigned long address) { - srmmu_free_nocache((unsigned long)page_address(pte), SRMMU_PTE_TABLE_SIZE); + unsigned long pte; + + if ((pte = (unsigned long)srmmu_pte_alloc_one_kernel(mm, address)) == 0) + return NULL; + return mem_map + (__nocache_pa(pte) >> PAGE_SHIFT); } -static pmd_t *srmmu_pmd_alloc_one_fast(struct mm_struct *mm, unsigned long address) +static void srmmu_free_pte_fast(pte_t *pte) { - return (pmd_t *)srmmu_get_nocache(SRMMU_PMD_TABLE_SIZE, SRMMU_PMD_TABLE_SIZE); + srmmu_free_nocache((unsigned long)pte, SRMMU_PTE_SZ_SOFT); } -static void srmmu_free_pmd_fast(pmd_t * pmd) +static void srmmu_pte_free(struct page *pte) { - srmmu_free_nocache((unsigned long)pmd, SRMMU_PMD_TABLE_SIZE); + unsigned long p = (unsigned long)page_address(pte); + if (p == 0) + BUG(); + srmmu_free_nocache(p, SRMMU_PTE_SZ_SOFT); } +/* + */ static inline void alloc_context(struct mm_struct *old_mm, struct mm_struct *mm) { struct ctx_list *ctxp; @@ -966,7 +1022,8 @@ while(start < end) { pgdp = pgd_offset_k(start); if(srmmu_pgd_none(*(pgd_t *)__nocache_fix(pgdp))) { - pmdp = (pmd_t *)__srmmu_get_nocache(SRMMU_PMD_TABLE_SIZE, SRMMU_PMD_TABLE_SIZE); + pmdp = (pmd_t *) __srmmu_get_nocache( + SRMMU_PMD_TABLE_SIZE, SRMMU_PMD_TABLE_SIZE); if (pmdp == NULL) early_pgtable_allocfail("pmd"); memset(__nocache_fix(pmdp), 0, SRMMU_PMD_TABLE_SIZE); @@ -974,10 +1031,11 @@ } pmdp = srmmu_pmd_offset(__nocache_fix(pgdp), start); if(srmmu_pmd_none(*(pmd_t *)__nocache_fix(pmdp))) { - ptep = (pte_t *)__srmmu_get_nocache(SRMMU_PTE_TABLE_SIZE, SRMMU_PTE_TABLE_SIZE); + ptep = (pte_t *)__srmmu_get_nocache(SRMMU_PTE_SZ_SOFT, + SRMMU_PTE_SZ_SOFT); if (ptep == NULL) early_pgtable_allocfail("pte"); - memset(__nocache_fix(ptep), 0, SRMMU_PTE_TABLE_SIZE); + memset(__nocache_fix(ptep), 0, SRMMU_PTE_SZ_SOFT); srmmu_pmd_set(__nocache_fix(pmdp), ptep); } start = (start + SRMMU_PMD_SIZE) & SRMMU_PMD_MASK; @@ -1001,10 +1059,11 @@ } pmdp = srmmu_pmd_offset(pgdp, start); if(srmmu_pmd_none(*pmdp)) { - ptep = (pte_t *)__srmmu_get_nocache(SRMMU_PTE_TABLE_SIZE, SRMMU_PTE_TABLE_SIZE); + ptep = (pte_t *) __srmmu_get_nocache(SRMMU_PTE_SZ_SOFT, + SRMMU_PTE_SZ_SOFT); if (ptep == NULL) early_pgtable_allocfail("pte"); - memset(ptep, 0, SRMMU_PTE_TABLE_SIZE); + memset(ptep, 0, SRMMU_PTE_SZ_SOFT); srmmu_pmd_set(pmdp, ptep); } start = (start + SRMMU_PMD_SIZE) & SRMMU_PMD_MASK; @@ -1062,18 +1121,26 @@ srmmu_pgd_set(__nocache_fix(pgdp), pmdp); } pmdp = srmmu_pmd_offset(__nocache_fix(pgdp), start); - if(what == 1) { - *(pmd_t *)__nocache_fix(pmdp) = __pmd(prompte); - start += SRMMU_PMD_SIZE; - continue; - } if(srmmu_pmd_none(*(pmd_t *)__nocache_fix(pmdp))) { - ptep = (pte_t *)__srmmu_get_nocache(SRMMU_PTE_TABLE_SIZE, SRMMU_PTE_TABLE_SIZE); + ptep = (pte_t *) __srmmu_get_nocache(SRMMU_PTE_SZ_SOFT, + SRMMU_PTE_SZ_SOFT); if (ptep == NULL) early_pgtable_allocfail("pte"); - memset(__nocache_fix(ptep), 0, SRMMU_PTE_TABLE_SIZE); + memset(__nocache_fix(ptep), 0, SRMMU_PTE_SZ_SOFT); srmmu_pmd_set(__nocache_fix(pmdp), ptep); } + if(what == 1) { + /* + * We bend the rule where all 16 PTPs in a pmd_t point + * inside the same PTE page, and we leak a perfectly + * good hardware PTE piece. Alternatives seem worse. + */ + unsigned int x; /* Index of HW PMD in soft cluster */ + x = (start >> SRMMU_PMD_SHIFT) & 15; + *(ulong *)__nocache_fix(&pmdp->pmdv[x]) = prompte; + start += SRMMU_PMD_SIZE; + continue; + } ptep = srmmu_pte_offset(__nocache_fix(pmdp), start); *(pte_t *)__nocache_fix(ptep) = __pte(prompte); start += PAGE_SIZE; @@ -2032,17 +2099,17 @@ extern void ld_mmu_iommu(void); extern void ld_mmu_iounit(void); extern void ___xchg32_sun4md(void); - - /* First the constants */ - BTFIXUPSET_SIMM13(pmd_shift, SRMMU_PMD_SHIFT); - BTFIXUPSET_SETHI(pmd_size, SRMMU_PMD_SIZE); - BTFIXUPSET_SETHI(pmd_mask, SRMMU_PMD_MASK); + + BTFIXUPSET_SIMM13(pmd_shift, SRMMU_PMD_SHIFT_SOFT); + BTFIXUPSET_SETHI(pmd_size, SRMMU_PMD_SIZE_SOFT); + BTFIXUPSET_SETHI(pmd_mask, SRMMU_PMD_MASK_SOFT); + BTFIXUPSET_SIMM13(pgdir_shift, SRMMU_PGDIR_SHIFT); BTFIXUPSET_SETHI(pgdir_size, SRMMU_PGDIR_SIZE); BTFIXUPSET_SETHI(pgdir_mask, SRMMU_PGDIR_MASK); - BTFIXUPSET_SIMM13(ptrs_per_pte, SRMMU_PTRS_PER_PTE); - BTFIXUPSET_SIMM13(ptrs_per_pmd, SRMMU_PTRS_PER_PMD); + BTFIXUPSET_SIMM13(ptrs_per_pte, SRMMU_PTRS_PER_PTE_SOFT); + BTFIXUPSET_SIMM13(ptrs_per_pmd, SRMMU_PTRS_PER_PMD_SOFT); BTFIXUPSET_SIMM13(ptrs_per_pgd, SRMMU_PTRS_PER_PGD); BTFIXUPSET_INT(page_none, pgprot_val(SRMMU_PAGE_NONE)); @@ -2090,12 +2157,13 @@ BTFIXUPSET_INT(pte_modify_mask, SRMMU_CHG_MASK); BTFIXUPSET_CALL(pmd_offset, srmmu_pmd_offset, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(pte_offset_kernel, srmmu_pte_offset, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(free_pte_fast, srmmu_free_pte_fast, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(pte_free, srmmu_pte_free, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(pte_alloc_one_kernel, srmmu_pte_alloc_one_kernel, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(pte_alloc_one, srmmu_pte_alloc_one, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(free_pmd_fast, srmmu_free_pmd_fast, BTFIXUPCALL_NORM); - BTFIXUPSET_CALL(pmd_alloc_one_fast, srmmu_pmd_alloc_one_fast, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(free_pmd_fast, srmmu_pmd_free, BTFIXUPCALL_NORM); + BTFIXUPSET_CALL(pmd_alloc_one, srmmu_pmd_alloc_one, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(free_pgd_fast, srmmu_free_pgd_fast, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(get_pgd_fast, srmmu_get_pgd_fast, BTFIXUPCALL_NORM); diff -Nru a/arch/sparc/mm/sun4c.c b/arch/sparc/mm/sun4c.c --- a/arch/sparc/mm/sun4c.c Fri Aug 16 14:34:54 2002 +++ b/arch/sparc/mm/sun4c.c Fri Aug 16 14:34:54 2002 @@ -1710,13 +1710,13 @@ static void sun4c_pmd_set(pmd_t * pmdp, pte_t * ptep) { - *pmdp = __pmd(PGD_TABLE | (unsigned long) ptep); + pmdp->pmdv[0] = PGD_TABLE | (unsigned long) ptep; } static void sun4c_pmd_populate(pmd_t * pmdp, struct page * ptep) { if (page_address(ptep) == NULL) BUG(); /* No highmem on sun4c */ - *pmdp = __pmd(PGD_TABLE | (unsigned long) page_address(ptep)); + pmdp->pmdv[0] = PGD_TABLE | (unsigned long) page_address(ptep); } static int sun4c_pte_present(pte_t pte) @@ -1735,7 +1735,14 @@ { return ((pmd_val(pmd) & PGD_PRESENT) != 0); } + +#if 0 /* if PMD takes one word */ static void sun4c_pmd_clear(pmd_t *pmdp) { *pmdp = __pmd(0); } +#else /* if pmd_t is a longish aggregate */ +static void sun4c_pmd_clear(pmd_t *pmdp) { + memset((void *)pmdp, 0, sizeof(pmd_t)); +} +#endif static int sun4c_pgd_none(pgd_t pgd) { return 0; } static int sun4c_pgd_bad(pgd_t pgd) { return 0; } @@ -1913,7 +1920,7 @@ * allocating and freeing a pmd is trivial: the 1-entry pmd is * inside the pgd, so has no extra memory associated with it. */ -static pmd_t *sun4c_pmd_alloc_one_fast(struct mm_struct *mm, unsigned long address) +static pmd_t *sun4c_pmd_alloc_one(struct mm_struct *mm, unsigned long address) { BUG(); return NULL; @@ -2176,7 +2183,7 @@ BTFIXUPSET_CALL(pte_alloc_one_kernel, sun4c_pte_alloc_one_kernel, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(pte_alloc_one, sun4c_pte_alloc_one, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(free_pmd_fast, sun4c_free_pmd_fast, BTFIXUPCALL_NOP); - BTFIXUPSET_CALL(pmd_alloc_one_fast, sun4c_pmd_alloc_one_fast, BTFIXUPCALL_RETO0); + BTFIXUPSET_CALL(pmd_alloc_one, sun4c_pmd_alloc_one, BTFIXUPCALL_RETO0); BTFIXUPSET_CALL(free_pgd_fast, sun4c_free_pgd_fast, BTFIXUPCALL_NORM); BTFIXUPSET_CALL(get_pgd_fast, sun4c_get_pgd_fast, BTFIXUPCALL_NORM); diff -Nru a/arch/sparc/prom/printf.c b/arch/sparc/prom/printf.c --- a/arch/sparc/prom/printf.c Fri Aug 16 14:34:53 2002 +++ b/arch/sparc/prom/printf.c Fri Aug 16 14:34:53 2002 @@ -1,11 +1,15 @@ -/* $Id: printf.c,v 1.7 2000/02/08 20:24:23 davem Exp $ +/* * printf.c: Internal prom library printf facility. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) - */ - -/* This routine is internal to the prom library, no one else should know - * about or use it! It's simple and smelly anyway.... + * Copyright (c) 2002 Pete Zaitcev (zaitcev@yahoo.com) + * + * We used to warn all over the code: DO NOT USE prom_printf(), + * and yet people do. Anton's banking code was outputing banks + * with prom_printf for most of the 2.4 lifetime. Since an effective + * stick is not available, we deployed a carrot: an early printk + * through PROM by means of -p boot option. This ought to fix it. + * USE printk; if you need, deploy -p. */ #include @@ -16,23 +20,27 @@ static char ppbuf[1024]; void +prom_write(const char *buf, unsigned int n) +{ + char ch; + + while (n != 0) { + --n; + if ((ch = *buf++) == '\n') + prom_putchar('\r'); + prom_putchar(ch); + } +} + +void prom_printf(char *fmt, ...) { va_list args; - char ch, *bptr; int i; va_start(args, fmt); - i = vsprintf(ppbuf, fmt, args); - - bptr = ppbuf; - - while((ch = *(bptr++)) != 0) { - if(ch == '\n') - prom_putchar('\r'); - - prom_putchar(ch); - } + i = vsnprintf(ppbuf, sizeof(ppbuf), fmt, args); va_end(args); - return; + + prom_write(ppbuf, i); } diff -Nru a/arch/sparc64/config.in b/arch/sparc64/config.in --- a/arch/sparc64/config.in Fri Aug 16 14:34:55 2002 +++ b/arch/sparc64/config.in Fri Aug 16 14:34:55 2002 @@ -95,10 +95,6 @@ endmenu -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - mainmenu_option next_comment comment 'ATA/ATAPI/MFM/RLL device support' @@ -207,6 +203,8 @@ source drivers/ieee1394/Config.in if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in + mainmenu_option next_comment comment 'Network device support' diff -Nru a/arch/sparc64/defconfig b/arch/sparc64/defconfig --- a/arch/sparc64/defconfig Fri Aug 16 14:34:56 2002 +++ b/arch/sparc64/defconfig Fri Aug 16 14:34:56 2002 @@ -35,6 +35,7 @@ CONFIG_HAVE_DEC_LOCK=y # CONFIG_RWSEM_GENERIC_SPINLOCK is not set CONFIG_RWSEM_XCHGADD_ALGORITHM=y +CONFIG_GENERIC_ISA_DMA=y # CONFIG_ISA is not set # CONFIG_ISAPNP is not set # CONFIG_EISA is not set @@ -236,6 +237,7 @@ # CONFIG_NET_SCHED=y CONFIG_NET_SCH_CBQ=m +CONFIG_NET_SCH_HTB=m CONFIG_NET_SCH_CSZ=m CONFIG_NET_SCH_PRIO=m CONFIG_NET_SCH_RED=m @@ -942,6 +944,7 @@ CONFIG_BLUEZ_HCIUART=m CONFIG_BLUEZ_HCIUART_H4=y # CONFIG_BLUEZ_HCIDTL1 is not set +# CONFIG_BLUEZ_HCIBT3C is not set # CONFIG_BLUEZ_HCIBLUECARD is not set CONFIG_BLUEZ_HCIVHCI=m diff -Nru a/arch/sparc64/kernel/pci.c b/arch/sparc64/kernel/pci.c --- a/arch/sparc64/kernel/pci.c Fri Aug 16 14:34:50 2002 +++ b/arch/sparc64/kernel/pci.c Fri Aug 16 14:34:50 2002 @@ -114,6 +114,53 @@ printk("PCI: Ignoring controller...\n"); } +static int pci_is_controller(char *model_name, int namelen, int node) +{ + int i; + + for (i = 0; i < PCI_NUM_CONTROLLER_TYPES; i++) { + if (!strncmp(model_name, + pci_controller_table[i].model_name, + namelen)) { + return 1; + } + } + return 0; +} + +/* Is there some PCI controller in the system? */ +int pcic_present(void) +{ + char namebuf[16]; + int node; + + node = prom_getchild(prom_root_node); + while ((node = prom_searchsiblings(node, "pci")) != 0) { + int len, ret; + + len = prom_getproperty(node, "model", + namebuf, sizeof(namebuf)); + + ret = 0; + if (len > 0) { + ret = pci_is_controller(namebuf, len, node); + } else { + len = prom_getproperty(node, "compatible", + namebuf, sizeof(namebuf)); + if (len > 0) + ret = pci_is_controller(namebuf, len, node); + } + if (ret) + return ret; + + node = prom_getsibling(node); + if (!node) + break; + } + + return 0; +} + /* Find each controller in the system, attach and initialize * software state structure for each and link into the * pci_controller_root. Setup the controller enough such diff -Nru a/arch/sparc64/kernel/setup.c b/arch/sparc64/kernel/setup.c --- a/arch/sparc64/kernel/setup.c Fri Aug 16 14:34:56 2002 +++ b/arch/sparc64/kernel/setup.c Fri Aug 16 14:34:56 2002 @@ -73,7 +73,7 @@ static void prom_console_write(struct console *con, const char *s, unsigned n) { - prom_printf("%s", s); + prom_write(s, n); } static struct console prom_console = { diff -Nru a/arch/sparc64/mm/modutil.c b/arch/sparc64/mm/modutil.c --- a/arch/sparc64/mm/modutil.c Fri Aug 16 14:34:56 2002 +++ b/arch/sparc64/mm/modutil.c Fri Aug 16 14:34:56 2002 @@ -4,6 +4,8 @@ * Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz) * Based upon code written by Linus Torvalds and others. */ + +#warning "major untested changes to this file --hch (2002/08/05)" #include #include @@ -16,6 +18,7 @@ void module_unmap (void * addr) { struct vm_struct **p, *tmp; + int i; if (!addr) return; @@ -23,21 +26,38 @@ printk("Trying to unmap module with bad address (%p)\n", addr); return; } + for (p = &modvmlist ; (tmp = *p) ; p = &tmp->next) { if (tmp->addr == addr) { *p = tmp->next; - vmfree_area_pages(VMALLOC_VMADDR(tmp->addr), tmp->size); - kfree(tmp); - return; } } printk("Trying to unmap nonexistent module vm area (%p)\n", addr); + return; + +found: + + unmap_vm_area(tmp); + + for (i = 0; i < tmp->nr_pages; i++) { + if (unlikely(!tmp->pages[i])) + BUG(); + __free_page(tmp->pages[i]); + } + + kfree(tmp->pages); + kfree(tmp); } + void * module_map (unsigned long size) { - void * addr; struct vm_struct **p, *tmp, *area; + struct vm_struct *area; + struct page **pages; + void * addr; + unsigned int nr_pages, array_size, i; + size = PAGE_ALIGN(size); if (!size || size > MODULES_LEN) return NULL; @@ -55,11 +75,32 @@ area->size = size + PAGE_SIZE; area->addr = addr; area->next = *p; + area->pages = NULL; + area->nr_pages = 0; + area->phys_addr = 0; *p = area; - if (vmalloc_area_pages(VMALLOC_VMADDR(addr), size, GFP_KERNEL, PAGE_KERNEL)) { - module_unmap(addr); + nr_pages = (size+PAGE_SIZE) >> PAGE_SHIFT; + array_size = (nr_pages * sizeof(struct page *)); + + area->nr_pages = nr_pages; + area->pages = pages = kmalloc(array_size, (gfp_mask & ~__GFP_HIGHMEM)); + if (!area->pages) return NULL; + memset(area->pages, 0, array_size); + + for (i = 0; i < area->nr_pages; i++) { + area->pages[i] = alloc_page(gfp_mask); + if (unlikely(!area->pages[i])) + goto fail; } - return addr; + + if (map_vm_area(area, prot, &pages)) + goto fail; + return area->addr; + +fail: + vfree(area->addr); + return NULL; +} } diff -Nru a/arch/sparc64/prom/printf.c b/arch/sparc64/prom/printf.c --- a/arch/sparc64/prom/printf.c Fri Aug 16 14:34:58 2002 +++ b/arch/sparc64/prom/printf.c Fri Aug 16 14:34:58 2002 @@ -1,12 +1,16 @@ -/* $Id: printf.c,v 1.3 1997/03/18 18:00:00 jj Exp $ +/* * printf.c: Internal prom library printf facility. * * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) - */ - -/* This routine is internal to the prom library, no one else should know - * about or use it! It's simple and smelly anyway.... + * Copyright (c) 2002 Pete Zaitcev (zaitcev@yahoo.com) + * + * We used to warn all over the code: DO NOT USE prom_printf(), + * and yet people do. Anton's banking code was outputing banks + * with prom_printf for most of the 2.4 lifetime. Since an effective + * stick is not available, we deployed a carrot: an early printk + * through PROM by means of -p boot option. This ought to fix it. + * USE printk; if you need, deploy -p. */ #include @@ -16,31 +20,28 @@ static char ppbuf[1024]; -extern void prom_puts (char *, int); +void +prom_write(const char *buf, unsigned int n) +{ + char ch; + + while (n != 0) { + --n; + if ((ch = *buf++) == '\n') + prom_putchar('\r'); + prom_putchar(ch); + } +} void prom_printf(char *fmt, ...) { va_list args; - char ch, *bptr, *last; int i; va_start(args, fmt); - i = vsprintf(ppbuf, fmt, args); - - bptr = ppbuf; - last = ppbuf; - - while((ch = *(bptr++)) != 0) { - if(ch == '\n') { - if (last < bptr - 1) - prom_puts (last, bptr - 1 - last); - prom_putchar('\r'); - last = bptr - 1; - } - } - if (last < bptr - 1) - prom_puts (last, bptr - 1 - last); + i = vsnprintf(ppbuf, sizeof(ppbuf), fmt, args); va_end(args); - return; + + prom_write(ppbuf, i); } diff -Nru a/arch/x86_64/config.in b/arch/x86_64/config.in --- a/arch/x86_64/config.in Fri Aug 16 14:34:55 2002 +++ b/arch/x86_64/config.in Fri Aug 16 14:34:55 2002 @@ -119,12 +119,6 @@ source drivers/md/Config.in -if [ "$CONFIG_NET" = "y" ]; then - source net/Config.in -fi - -source drivers/telephony/Config.in - mainmenu_option next_comment comment 'ATA/ATAPI/MFM/RLL support' @@ -155,6 +149,8 @@ #source drivers/message/i2o/Config.in if [ "$CONFIG_NET" = "y" ]; then + source net/Config.in + mainmenu_option next_comment comment 'Network device support' @@ -174,6 +170,8 @@ source net/irda/Config.in source drivers/isdn/Config.in + +source drivers/telephony/Config.in # no support for non IDE/SCSI cdroms as they were all ISA only diff -Nru a/drivers/acpi/Makefile b/drivers/acpi/Makefile --- a/drivers/acpi/Makefile Fri Aug 16 14:35:01 2002 +++ b/drivers/acpi/Makefile Fri Aug 16 14:35:01 2002 @@ -7,7 +7,7 @@ ACPI_CFLAGS := -D_LINUX -I$(CURDIR)/include ifdef CONFIG_ACPI_DEBUG - ACPI_CFLAGS += -DACPI_DEBUG + ACPI_CFLAGS += -DACPI_DEBUG_OUTPUT endif EXTRA_CFLAGS += $(ACPI_CFLAGS) diff -Nru a/drivers/acpi/acpi_ksyms.c b/drivers/acpi/acpi_ksyms.c --- a/drivers/acpi/acpi_ksyms.c Fri Aug 16 14:34:56 2002 +++ b/drivers/acpi/acpi_ksyms.c Fri Aug 16 14:34:56 2002 @@ -62,6 +62,7 @@ EXPORT_SYMBOL(acpi_get_next_object); EXPORT_SYMBOL(acpi_evaluate_object); EXPORT_SYMBOL(acpi_get_table); +EXPORT_SYMBOL(acpi_get_firmware_table); EXPORT_SYMBOL(acpi_install_notify_handler); EXPORT_SYMBOL(acpi_remove_notify_handler); EXPORT_SYMBOL(acpi_install_gpe_handler); diff -Nru a/drivers/acpi/bus.c b/drivers/acpi/bus.c --- a/drivers/acpi/bus.c Fri Aug 16 14:34:58 2002 +++ b/drivers/acpi/bus.c Fri Aug 16 14:34:58 2002 @@ -2052,7 +2052,7 @@ /* Mimic structured exception handling */ error4: - remove_proc_entry("ACPI", NULL); + remove_proc_entry(ACPI_BUS_FILE_ROOT, NULL); error3: acpi_bus_remove(acpi_root, ACPI_BUS_REMOVAL_NORMAL); error2: diff -Nru a/drivers/acpi/debugger/dbcmds.c b/drivers/acpi/debugger/dbcmds.c --- a/drivers/acpi/debugger/dbcmds.c Fri Aug 16 14:34:52 2002 +++ b/drivers/acpi/debugger/dbcmds.c Fri Aug 16 14:34:52 2002 @@ -1,7 +1,7 @@ /******************************************************************************* * * Module Name: dbcmds - debug commands and output routines - * $Revision: 85 $ + * $Revision: 87 $ * ******************************************************************************/ @@ -33,9 +33,9 @@ #include "acresrc.h" #include "acdisasm.h" -#ifdef ENABLE_DEBUGGER +#ifdef ACPI_DEBUGGER -#define _COMPONENT ACPI_DEBUGGER +#define _COMPONENT ACPI_CA_DEBUGGER ACPI_MODULE_NAME ("dbcmds") @@ -1111,4 +1111,4 @@ } -#endif /* ENABLE_DEBUGGER */ +#endif /* ACPI_DEBUGGER */ diff -Nru a/drivers/acpi/debugger/dbdisply.c b/drivers/acpi/debugger/dbdisply.c --- a/drivers/acpi/debugger/dbdisply.c Fri Aug 16 14:34:54 2002 +++ b/drivers/acpi/debugger/dbdisply.c Fri Aug 16 14:34:54 2002 @@ -1,7 +1,7 @@ /******************************************************************************* * * Module Name: dbdisply - debug display commands - * $Revision: 76 $ + * $Revision: 78 $ * ******************************************************************************/ @@ -33,10 +33,10 @@ #include "acdebug.h" -#ifdef ENABLE_DEBUGGER +#ifdef ACPI_DEBUGGER -#define _COMPONENT ACPI_DEBUGGER +#define _COMPONENT ACPI_CA_DEBUGGER ACPI_MODULE_NAME ("dbdisply") @@ -855,5 +855,5 @@ acpi_db_display_internal_object (obj_desc, walk_state); } -#endif /* ENABLE_DEBUGGER */ +#endif /* ACPI_DEBUGGER */ diff -Nru a/drivers/acpi/debugger/dbexec.c b/drivers/acpi/debugger/dbexec.c --- a/drivers/acpi/debugger/dbexec.c Fri Aug 16 14:34:55 2002 +++ b/drivers/acpi/debugger/dbexec.c Fri Aug 16 14:34:55 2002 @@ -1,7 +1,7 @@ /******************************************************************************* * * Module Name: dbexec - debugger control method execution - * $Revision: 42 $ + * $Revision: 44 $ * ******************************************************************************/ @@ -27,9 +27,9 @@ #include "acpi.h" #include "acdebug.h" -#ifdef ENABLE_DEBUGGER +#ifdef ACPI_DEBUGGER -#define _COMPONENT ACPI_DEBUGGER +#define _COMPONENT ACPI_CA_DEBUGGER ACPI_MODULE_NAME ("dbexec") @@ -209,7 +209,7 @@ acpi_buffer return_obj; -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT u32 previous_allocations; u32 allocations; @@ -236,7 +236,7 @@ acpi_os_sleep (0, 10); -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT /* Memory allocation tracking */ @@ -400,6 +400,6 @@ } -#endif /* ENABLE_DEBUGGER */ +#endif /* ACPI_DEBUGGER */ diff -Nru a/drivers/acpi/debugger/dbfileio.c b/drivers/acpi/debugger/dbfileio.c --- a/drivers/acpi/debugger/dbfileio.c Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/debugger/dbfileio.c Fri Aug 16 14:34:53 2002 @@ -2,7 +2,7 @@ * * Module Name: dbfileio - Debugger file I/O commands. These can't usually * be used when running the debugger in Ring 0 (Kernel mode) - * $Revision: 67 $ + * $Revision: 68 $ * ******************************************************************************/ @@ -30,9 +30,9 @@ #include "acnamesp.h" #include "actables.h" -#if (defined ENABLE_DEBUGGER || defined ACPI_DISASSEMBLER) +#if (defined ACPI_DEBUGGER || defined ACPI_DISASSEMBLER) -#define _COMPONENT ACPI_DEBUGGER +#define _COMPONENT ACPI_CA_DEBUGGER ACPI_MODULE_NAME ("dbfileio") @@ -86,7 +86,7 @@ } -#ifdef ENABLE_DEBUGGER +#ifdef ACPI_DEBUGGER /******************************************************************************* * * FUNCTION: Acpi_db_close_debug_file @@ -395,5 +395,5 @@ } -#endif /* ENABLE_DEBUGGER */ +#endif /* ACPI_DEBUGGER */ diff -Nru a/drivers/acpi/debugger/dbhistry.c b/drivers/acpi/debugger/dbhistry.c --- a/drivers/acpi/debugger/dbhistry.c Fri Aug 16 14:34:58 2002 +++ b/drivers/acpi/debugger/dbhistry.c Fri Aug 16 14:34:58 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: dbhistry - debugger HISTORY command - * $Revision: 24 $ + * $Revision: 25 $ * *****************************************************************************/ @@ -27,9 +27,9 @@ #include "acpi.h" #include "acdebug.h" -#ifdef ENABLE_DEBUGGER +#ifdef ACPI_DEBUGGER -#define _COMPONENT ACPI_DEBUGGER +#define _COMPONENT ACPI_CA_DEBUGGER ACPI_MODULE_NAME ("dbhistry") @@ -185,5 +185,5 @@ } -#endif /* ENABLE_DEBUGGER */ +#endif /* ACPI_DEBUGGER */ diff -Nru a/drivers/acpi/debugger/dbinput.c b/drivers/acpi/debugger/dbinput.c --- a/drivers/acpi/debugger/dbinput.c Fri Aug 16 14:34:56 2002 +++ b/drivers/acpi/debugger/dbinput.c Fri Aug 16 14:34:56 2002 @@ -1,7 +1,7 @@ /******************************************************************************* * * Module Name: dbinput - user front-end to the AML debugger - * $Revision: 86 $ + * $Revision: 87 $ * ******************************************************************************/ @@ -28,9 +28,9 @@ #include "acdebug.h" -#ifdef ENABLE_DEBUGGER +#ifdef ACPI_DEBUGGER -#define _COMPONENT ACPI_DEBUGGER +#define _COMPONENT ACPI_CA_DEBUGGER ACPI_MODULE_NAME ("dbinput") @@ -888,5 +888,5 @@ } -#endif /* ENABLE_DEBUGGER */ +#endif /* ACPI_DEBUGGER */ diff -Nru a/drivers/acpi/debugger/dbstats.c b/drivers/acpi/debugger/dbstats.c --- a/drivers/acpi/debugger/dbstats.c Fri Aug 16 14:34:51 2002 +++ b/drivers/acpi/debugger/dbstats.c Fri Aug 16 14:34:51 2002 @@ -1,7 +1,7 @@ /******************************************************************************* * * Module Name: dbstats - Generation and display of ACPI table statistics - * $Revision: 60 $ + * $Revision: 61 $ * ******************************************************************************/ @@ -28,9 +28,9 @@ #include #include -#ifdef ENABLE_DEBUGGER +#ifdef ACPI_DEBUGGER -#define _COMPONENT ACPI_DEBUGGER +#define _COMPONENT ACPI_CA_DEBUGGER ACPI_MODULE_NAME ("dbstats") /* @@ -148,8 +148,6 @@ } -#ifndef PARSER_ONLY - /******************************************************************************* * * FUNCTION: Acpi_db_classify_one_object @@ -254,8 +252,6 @@ FALSE, acpi_db_classify_one_object, NULL, NULL); } -#endif - /******************************************************************************* * @@ -303,13 +299,11 @@ switch (type) { -#ifndef PARSER_ONLY case CMD_STAT_ALLOCATIONS: #ifdef ACPI_DBG_TRACK_ALLOCATIONS acpi_ut_dump_allocation_info (); #endif break; -#endif case CMD_STAT_TABLES: @@ -322,8 +316,6 @@ case CMD_STAT_OBJECTS: -#ifndef PARSER_ONLY - acpi_db_count_namespace_objects (); acpi_os_printf ("\nObjects defined in the current namespace:\n\n"); @@ -340,8 +332,6 @@ acpi_os_printf ("%16.16s % 10ld% 10ld\n", "TOTALS:", acpi_gbl_num_nodes, acpi_gbl_num_objects); - -#endif break; case CMD_STAT_MEMORY: @@ -445,7 +435,7 @@ case CMD_STAT_STACK: -#if defined(ACPI_DEBUG) +#if defined(ACPI_DEBUG_OUTPUT) size = (u32) (acpi_gbl_entry_stack_pointer - acpi_gbl_lowest_stack_pointer); @@ -466,4 +456,4 @@ } -#endif /* ENABLE_DEBUGGER */ +#endif /* ACPI_DEBUGGER */ diff -Nru a/drivers/acpi/debugger/dbutils.c b/drivers/acpi/debugger/dbutils.c --- a/drivers/acpi/debugger/dbutils.c Fri Aug 16 14:34:54 2002 +++ b/drivers/acpi/debugger/dbutils.c Fri Aug 16 14:34:54 2002 @@ -1,7 +1,7 @@ /******************************************************************************* * * Module Name: dbutils - AML debugger utilities - * $Revision: 55 $ + * $Revision: 56 $ * ******************************************************************************/ @@ -32,9 +32,9 @@ #include "acdispat.h" -#ifdef ENABLE_DEBUGGER +#ifdef ACPI_DEBUGGER -#define _COMPONENT ACPI_DEBUGGER +#define _COMPONENT ACPI_CA_DEBUGGER ACPI_MODULE_NAME ("dbutils") @@ -375,6 +375,6 @@ } -#endif /* ENABLE_DEBUGGER */ +#endif /* ACPI_DEBUGGER */ diff -Nru a/drivers/acpi/debugger/dbxface.c b/drivers/acpi/debugger/dbxface.c --- a/drivers/acpi/debugger/dbxface.c Fri Aug 16 14:34:59 2002 +++ b/drivers/acpi/debugger/dbxface.c Fri Aug 16 14:34:59 2002 @@ -1,7 +1,7 @@ /******************************************************************************* * * Module Name: dbxface - AML Debugger external interfaces - * $Revision: 61 $ + * $Revision: 64 $ * ******************************************************************************/ @@ -30,9 +30,9 @@ #include "acdisasm.h" -#ifdef ENABLE_DEBUGGER +#ifdef ACPI_DEBUGGER -#define _COMPONENT ACPI_DEBUGGER +#define _COMPONENT ACPI_CA_DEBUGGER ACPI_MODULE_NAME ("dbxface") @@ -184,7 +184,7 @@ /* Restore everything */ op->common.next = next; - acpi_os_printf ("\n"); + acpi_os_printf ("\n\n"); acpi_dbg_level = original_debug_level; } @@ -385,4 +385,4 @@ } -#endif /* ENABLE_DEBUGGER */ +#endif /* ACPI_DEBUGGER */ diff -Nru a/drivers/acpi/dispatcher/dsfield.c b/drivers/acpi/dispatcher/dsfield.c --- a/drivers/acpi/dispatcher/dsfield.c Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/dispatcher/dsfield.c Fri Aug 16 14:34:53 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: dsfield - Dispatcher field routines - * $Revision: 65 $ + * $Revision: 66 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/dispatcher/dsmethod.c b/drivers/acpi/dispatcher/dsmethod.c --- a/drivers/acpi/dispatcher/dsmethod.c Fri Aug 16 14:34:57 2002 +++ b/drivers/acpi/dispatcher/dsmethod.c Fri Aug 16 14:34:57 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: dsmethod - Parser/Interpreter interface - control method parsing - * $Revision: 87 $ + * $Revision: 88 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/dispatcher/dsmthdat.c b/drivers/acpi/dispatcher/dsmthdat.c --- a/drivers/acpi/dispatcher/dsmthdat.c Fri Aug 16 14:34:56 2002 +++ b/drivers/acpi/dispatcher/dsmthdat.c Fri Aug 16 14:34:56 2002 @@ -1,7 +1,7 @@ /******************************************************************************* * * Module Name: dsmthdat - control method arguments and local variables - * $Revision: 62 $ + * $Revision: 63 $ * ******************************************************************************/ @@ -587,23 +587,40 @@ * * Weird, but true. */ - if ((opcode == AML_ARG_OP) && - (ACPI_GET_DESCRIPTOR_TYPE (current_obj_desc) == ACPI_DESC_TYPE_NAMED)) { - ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, - "Arg (%p) is an Obj_ref(Node), storing in node %p\n", - obj_desc, current_obj_desc)); - - /* Detach an existing object from the Node */ - - acpi_ns_detach_object ((acpi_namespace_node *) current_obj_desc); + if (opcode == AML_ARG_OP) { + /* + * Make sure that the object is the correct type. This may be overkill, but + * it is here because references were NS nodes in the past. Now they are + * operand objects of type Reference. + */ + if (ACPI_GET_DESCRIPTOR_TYPE (current_obj_desc) != ACPI_DESC_TYPE_OPERAND) { + ACPI_REPORT_ERROR (("Invalid descriptor type while storing to method arg: %X\n", + current_obj_desc->common.type)); + return_ACPI_STATUS (AE_AML_INTERNAL); + } /* - * Store this object into the Node - * (perform the indirect store) + * If we have a valid reference object that came from Ref_of(), do the + * indirect store */ - status = acpi_ns_attach_object ((acpi_namespace_node *) current_obj_desc, - obj_desc, ACPI_GET_OBJECT_TYPE (obj_desc)); - return_ACPI_STATUS (status); + if ((current_obj_desc->common.type == INTERNAL_TYPE_REFERENCE) && + (current_obj_desc->reference.opcode == AML_REF_OF_OP)) { + ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, + "Arg (%p) is an Obj_ref(Node), storing in node %p\n", + obj_desc, current_obj_desc)); + + /* Detach an existing object from the referenced Node */ + + acpi_ns_detach_object (current_obj_desc->reference.object); + + /* + * Store this object into the Node + * (perform the indirect store) + */ + status = acpi_ns_attach_object (current_obj_desc->reference.object, + obj_desc, ACPI_GET_OBJECT_TYPE (obj_desc)); + return_ACPI_STATUS (status); + } } /* diff -Nru a/drivers/acpi/dispatcher/dsobject.c b/drivers/acpi/dispatcher/dsobject.c --- a/drivers/acpi/dispatcher/dsobject.c Fri Aug 16 14:34:52 2002 +++ b/drivers/acpi/dispatcher/dsobject.c Fri Aug 16 14:34:52 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: dsobject - Dispatcher object management routines - * $Revision: 105 $ + * $Revision: 106 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/dispatcher/dsopcode.c b/drivers/acpi/dispatcher/dsopcode.c --- a/drivers/acpi/dispatcher/dsopcode.c Fri Aug 16 14:34:55 2002 +++ b/drivers/acpi/dispatcher/dsopcode.c Fri Aug 16 14:34:55 2002 @@ -2,7 +2,7 @@ * * Module Name: dsopcode - Dispatcher Op Region support and handling of * "control" opcodes - * $Revision: 80 $ + * $Revision: 81 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/dispatcher/dswexec.c b/drivers/acpi/dispatcher/dswexec.c --- a/drivers/acpi/dispatcher/dswexec.c Fri Aug 16 14:35:01 2002 +++ b/drivers/acpi/dispatcher/dswexec.c Fri Aug 16 14:35:01 2002 @@ -2,7 +2,7 @@ * * Module Name: dswexec - Dispatcher method execution callbacks; * dispatch to interpreter. - * $Revision: 94 $ + * $Revision: 95 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/dispatcher/dswload.c b/drivers/acpi/dispatcher/dswload.c --- a/drivers/acpi/dispatcher/dswload.c Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/dispatcher/dswload.c Fri Aug 16 14:34:53 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: dswload - Dispatcher namespace load callbacks - * $Revision: 69 $ + * $Revision: 70 $ * *****************************************************************************/ @@ -296,7 +296,7 @@ NATIVE_CHAR *buffer_ptr; - ACPI_FUNCTION_NAME ("Ds_load2_begin_op"); + ACPI_FUNCTION_TRACE ("Ds_load2_begin_op"); op = walk_state->op; @@ -307,7 +307,7 @@ if ((!(walk_state->op_info->flags & AML_NSOPCODE) && (walk_state->opcode != AML_INT_NAMEPATH_OP)) || (!(walk_state->op_info->flags & AML_NAMED))) { - return (AE_OK); + return_ACPI_STATUS (AE_OK); } /* @@ -320,7 +320,7 @@ if (!buffer_ptr) { /* No name, just exit */ - return (AE_OK); + return_ACPI_STATUS (AE_OK); } } else { @@ -368,11 +368,11 @@ if (acpi_ns_opens_scope (object_type)) { status = acpi_ds_scope_stack_push (node, object_type, walk_state); if (ACPI_FAILURE (status)) { - return (status); + return_ACPI_STATUS (status); } } - return (AE_OK); + return_ACPI_STATUS (AE_OK); } /* @@ -390,7 +390,7 @@ op = acpi_ps_alloc_op (walk_state->opcode); if (!op) { - return (AE_NO_MEMORY); + return_ACPI_STATUS (AE_NO_MEMORY); } /* Initialize the new op */ @@ -410,7 +410,7 @@ op->common.node = node; } - return (status); + return_ACPI_STATUS (status); } @@ -444,7 +444,7 @@ #endif - ACPI_FUNCTION_NAME ("Ds_load2_end_op"); + ACPI_FUNCTION_TRACE ("Ds_load2_end_op"); op = walk_state->op; ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "Opcode [%s] Op %p State %p\n", @@ -453,7 +453,7 @@ /* Only interested in opcodes that have namespace objects */ if (!(walk_state->op_info->flags & AML_NSOBJECT)) { - return (AE_OK); + return_ACPI_STATUS (AE_OK); } if (op->common.aml_opcode == AML_SCOPE_OP) { @@ -479,13 +479,13 @@ /* Pop the scope stack */ - if (acpi_ns_opens_scope (object_type)) { + if (acpi_ns_opens_scope (object_type) && (op->common.aml_opcode != AML_INT_METHODCALL_OP)) { ACPI_DEBUG_PRINT ((ACPI_DB_DISPATCH, "(%s) Popping scope for Op %p\n", acpi_ut_get_type_name (object_type), op)); status = acpi_ds_scope_stack_pop (walk_state); if (ACPI_FAILURE (status)) { - return (status); + return_ACPI_STATUS (status); } } @@ -730,7 +730,7 @@ walk_state->operands[0] = NULL; walk_state->num_operands = 0; - return (status); + return_ACPI_STATUS (status); } diff -Nru a/drivers/acpi/dispatcher/dswstate.c b/drivers/acpi/dispatcher/dswstate.c --- a/drivers/acpi/dispatcher/dswstate.c Fri Aug 16 14:35:00 2002 +++ b/drivers/acpi/dispatcher/dswstate.c Fri Aug 16 14:35:00 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: dswstate - Dispatcher parse tree walk management routines - * $Revision: 67 $ + * $Revision: 68 $ * *****************************************************************************/ @@ -835,6 +835,8 @@ walk_state->method_desc = mth_desc; walk_state->thread = thread; + walk_state->parser_state.start_op = origin; + /* Init the method args/local */ #if (!defined (ACPI_NO_METHOD_EXECUTION) && !defined (ACPI_CONSTANT_EVAL_ONLY)) @@ -883,6 +885,7 @@ { acpi_status status; acpi_parse_state *parser_state = &walk_state->parser_state; + acpi_parse_object *extra_op; ACPI_FUNCTION_TRACE ("Ds_init_aml_walk"); @@ -925,9 +928,23 @@ } } else { - /* Setup the current scope */ + /* + * Setup the current scope. + * Find a Named Op that has a namespace node associated with it. + * search upwards from this Op. Current scope is the first + * Op with a namespace node. + */ + extra_op = parser_state->start_op; + while (extra_op && !extra_op->common.node) { + extra_op = extra_op->common.parent; + } + if (!extra_op) { + parser_state->start_node = NULL; + } + else { + parser_state->start_node = extra_op->common.node; + } - parser_state->start_node = parser_state->start_op->common.node; if (parser_state->start_node) { /* Push start scope on scope stack and make it current */ diff -Nru a/drivers/acpi/events/evevent.c b/drivers/acpi/events/evevent.c --- a/drivers/acpi/events/evevent.c Fri Aug 16 14:35:00 2002 +++ b/drivers/acpi/events/evevent.c Fri Aug 16 14:35:00 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: evevent - Fixed and General Purpose Even handling and dispatch - * $Revision: 88 $ + * $Revision: 90 $ * *****************************************************************************/ @@ -104,7 +104,7 @@ acpi_status status; - ACPI_FUNCTION_TRACE ("Ev_initialize"); + ACPI_FUNCTION_TRACE ("Ev_handler_initialize"); /* Install the SCI handler */ diff -Nru a/drivers/acpi/events/evmisc.c b/drivers/acpi/events/evmisc.c --- a/drivers/acpi/events/evmisc.c Fri Aug 16 14:34:58 2002 +++ b/drivers/acpi/events/evmisc.c Fri Aug 16 14:34:58 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: evmisc - Miscellaneous event manager support functions - * $Revision: 53 $ + * $Revision: 56 $ * *****************************************************************************/ @@ -442,11 +442,13 @@ ACPI_FUNCTION_TRACE ("Ev_acquire_global_lock"); +#ifndef ACPI_APPLICATION /* Make sure that we actually have a global lock */ if (!acpi_gbl_global_lock_present) { return_ACPI_STATUS (AE_NO_GLOBAL_LOCK); } +#endif /* One more thread wants the global lock */ @@ -552,9 +554,56 @@ void acpi_ev_terminate (void) { + NATIVE_UINT_MAX32 i; + acpi_status status; + ACPI_FUNCTION_TRACE ("Ev_terminate"); + /* + * Disable all event-related functionality. + * In all cases, on error, print a message but obviously we don't abort. + */ + + /* + * Disable all fixed events + */ + for (i = 0; i < ACPI_NUM_FIXED_EVENTS; i++) { + status = acpi_disable_event(i, ACPI_EVENT_FIXED, 0); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Failed to disable fixed event %d.\n", i)); + } + } + + /* + * Disable all GPEs + */ + for (i = 0; i < acpi_gbl_gpe_number_max; i++) { + if (acpi_ev_get_gpe_number_index(i) != ACPI_GPE_INVALID) { + status = acpi_hw_disable_gpe(i); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Failed to disable GPE %d.\n", i)); + } + } + } + + /* + * Remove SCI handler + */ + status = acpi_ev_remove_sci_handler(); + if (ACPI_FAILURE(status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unable to remove SCI handler.\n")); + } + + /* + * Return to original mode if necessary + */ + if (acpi_gbl_original_mode == ACPI_SYS_MODE_LEGACY) { + status = acpi_disable (); + if (ACPI_FAILURE (status)) { + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Acpi_disable failed.\n")); + } + } /* * Free global tables, etc. diff -Nru a/drivers/acpi/events/evregion.c b/drivers/acpi/events/evregion.c --- a/drivers/acpi/events/evregion.c Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/events/evregion.c Fri Aug 16 14:34:53 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: evregion - ACPI Address_space (Op_region) handler dispatch - * $Revision: 134 $ + * $Revision: 135 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/events/evrgnini.c b/drivers/acpi/events/evrgnini.c --- a/drivers/acpi/events/evrgnini.c Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/events/evrgnini.c Fri Aug 16 14:34:53 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: evrgnini- ACPI Address_space (Op_region) init - * $Revision: 62 $ + * $Revision: 63 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/events/evxface.c b/drivers/acpi/events/evxface.c --- a/drivers/acpi/events/evxface.c Fri Aug 16 14:34:58 2002 +++ b/drivers/acpi/events/evxface.c Fri Aug 16 14:34:58 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: evxface - External interfaces for ACPI events - * $Revision: 129 $ + * $Revision: 130 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/events/evxfevnt.c b/drivers/acpi/events/evxfevnt.c --- a/drivers/acpi/events/evxfevnt.c Fri Aug 16 14:34:55 2002 +++ b/drivers/acpi/events/evxfevnt.c Fri Aug 16 14:34:55 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: evxfevnt - External Interfaces, ACPI event disable/enable - * $Revision: 55 $ + * $Revision: 57 $ * *****************************************************************************/ @@ -52,16 +52,14 @@ ACPI_FUNCTION_TRACE ("Acpi_enable"); - /* Make sure we have ACPI tables */ + /* Make sure we have the FADT*/ - if (!acpi_gbl_DSDT) { - ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "No ACPI tables present!\n")); + if (!acpi_gbl_FADT) { + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "No FADT information present!\n")); return_ACPI_STATUS (AE_NO_ACPI_TABLES); } - acpi_gbl_original_mode = acpi_hw_get_mode (); - - if (acpi_gbl_original_mode == ACPI_SYS_MODE_ACPI) { + if (acpi_hw_get_mode() == ACPI_SYS_MODE_ACPI) { ACPI_DEBUG_PRINT ((ACPI_DB_OK, "Already in ACPI mode.\n")); } else { @@ -88,8 +86,7 @@ * * RETURN: Status * - * DESCRIPTION: Returns the system to original ACPI/legacy mode, and - * uninstalls the SCI interrupt handler. + * DESCRIPTION: Transfers the system into LEGACY mode. * ******************************************************************************/ @@ -101,20 +98,26 @@ ACPI_FUNCTION_TRACE ("Acpi_disable"); + if (!acpi_gbl_FADT) { + ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "No FADT information present!\n")); + return_ACPI_STATUS (AE_NO_ACPI_TABLES); + } - if (acpi_hw_get_mode () != acpi_gbl_original_mode) { - /* Restore original mode */ + if (acpi_hw_get_mode() == ACPI_SYS_MODE_LEGACY) { + ACPI_DEBUG_PRINT ((ACPI_DB_OK, "Already in LEGACY mode.\n")); + } + else { + /* Transition to LEGACY mode */ + status = acpi_hw_set_mode (ACPI_SYS_MODE_LEGACY); - status = acpi_hw_set_mode (acpi_gbl_original_mode); if (ACPI_FAILURE (status)) { - ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Unable to transition to original mode")); + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Could not transition to LEGACY mode.")); return_ACPI_STATUS (status); } - } - /* Unload the SCI interrupt handler */ + ACPI_DEBUG_PRINT ((ACPI_DB_OK, "Transition to LEGACY mode successful\n")); + } - status = acpi_ev_remove_sci_handler (); return_ACPI_STATUS (status); } diff -Nru a/drivers/acpi/executer/exdump.c b/drivers/acpi/executer/exdump.c --- a/drivers/acpi/executer/exdump.c Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/executer/exdump.c Fri Aug 16 14:34:53 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: exdump - Interpreter debug output routines - * $Revision: 157 $ + * $Revision: 159 $ * *****************************************************************************/ @@ -38,7 +38,7 @@ * The following routines are used for debug output only */ -#if defined(ACPI_DEBUG) || defined(ENABLE_DEBUGGER) +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) /***************************************************************************** * diff -Nru a/drivers/acpi/executer/exfldio.c b/drivers/acpi/executer/exfldio.c --- a/drivers/acpi/executer/exfldio.c Fri Aug 16 14:34:56 2002 +++ b/drivers/acpi/executer/exfldio.c Fri Aug 16 14:34:56 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: exfldio - Aml Field I/O - * $Revision: 87 $ + * $Revision: 88 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/executer/exmisc.c b/drivers/acpi/executer/exmisc.c --- a/drivers/acpi/executer/exmisc.c Fri Aug 16 14:35:00 2002 +++ b/drivers/acpi/executer/exmisc.c Fri Aug 16 14:35:00 2002 @@ -2,7 +2,7 @@ /****************************************************************************** * * Module Name: exmisc - ACPI AML (p-code) execution - specific opcodes - * $Revision: 107 $ + * $Revision: 108 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/executer/exoparg1.c b/drivers/acpi/executer/exoparg1.c --- a/drivers/acpi/executer/exoparg1.c Fri Aug 16 14:35:00 2002 +++ b/drivers/acpi/executer/exoparg1.c Fri Aug 16 14:35:00 2002 @@ -2,7 +2,7 @@ /****************************************************************************** * * Module Name: exoparg1 - AML execution - opcodes with 1 argument - * $Revision: 141 $ + * $Revision: 142 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/executer/exoparg2.c b/drivers/acpi/executer/exoparg2.c --- a/drivers/acpi/executer/exoparg2.c Fri Aug 16 14:34:58 2002 +++ b/drivers/acpi/executer/exoparg2.c Fri Aug 16 14:34:58 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: exoparg2 - AML execution - opcodes with 2 arguments - * $Revision: 109 $ + * $Revision: 110 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/executer/exprep.c b/drivers/acpi/executer/exprep.c --- a/drivers/acpi/executer/exprep.c Fri Aug 16 14:34:54 2002 +++ b/drivers/acpi/executer/exprep.c Fri Aug 16 14:34:54 2002 @@ -2,7 +2,7 @@ /****************************************************************************** * * Module Name: exprep - ACPI AML (p-code) execution - field prep utilities - * $Revision: 118 $ + * $Revision: 119 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/executer/exregion.c b/drivers/acpi/executer/exregion.c --- a/drivers/acpi/executer/exregion.c Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/executer/exregion.c Fri Aug 16 14:34:53 2002 @@ -2,7 +2,7 @@ /****************************************************************************** * * Module Name: exregion - ACPI default Op_region (address space) handlers - * $Revision: 79 $ + * $Revision: 80 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/executer/exresop.c b/drivers/acpi/executer/exresop.c --- a/drivers/acpi/executer/exresop.c Fri Aug 16 14:35:00 2002 +++ b/drivers/acpi/executer/exresop.c Fri Aug 16 14:35:00 2002 @@ -2,7 +2,7 @@ /****************************************************************************** * * Module Name: exresop - AML Interpreter operand/object resolution - * $Revision: 54 $ + * $Revision: 55 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/executer/exstore.c b/drivers/acpi/executer/exstore.c --- a/drivers/acpi/executer/exstore.c Fri Aug 16 14:34:57 2002 +++ b/drivers/acpi/executer/exstore.c Fri Aug 16 14:34:57 2002 @@ -2,7 +2,7 @@ /****************************************************************************** * * Module Name: exstore - AML Interpreter object store support - * $Revision: 168 $ + * $Revision: 169 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/executer/exutils.c b/drivers/acpi/executer/exutils.c --- a/drivers/acpi/executer/exutils.c Fri Aug 16 14:34:54 2002 +++ b/drivers/acpi/executer/exutils.c Fri Aug 16 14:34:54 2002 @@ -2,7 +2,7 @@ /****************************************************************************** * * Module Name: exutils - interpreter/scanner utilities - * $Revision: 102 $ + * $Revision: 103 $ * *****************************************************************************/ @@ -261,7 +261,8 @@ if (ACPI_FAILURE (status)) { /* Report the error, but there isn't much else we can do */ - ACPI_REPORT_ERROR (("Could not release ACPI Global Lock\n")); + ACPI_REPORT_ERROR (("Could not release ACPI Global Lock, %s\n", + acpi_format_exception (status))); } } diff -Nru a/drivers/acpi/hardware/hwacpi.c b/drivers/acpi/hardware/hwacpi.c --- a/drivers/acpi/hardware/hwacpi.c Fri Aug 16 14:34:51 2002 +++ b/drivers/acpi/hardware/hwacpi.c Fri Aug 16 14:34:51 2002 @@ -2,7 +2,7 @@ /****************************************************************************** * * Module Name: hwacpi - ACPI Hardware Initialization/Mode Interface - * $Revision: 58 $ + * $Revision: 60 $ * *****************************************************************************/ @@ -95,6 +95,27 @@ ACPI_FUNCTION_TRACE ("Hw_set_mode"); + + /* + * ACPI 2.0 clarified that if SMI_CMD in FADT is zero, + * system does not support mode transition. + */ + if (!acpi_gbl_FADT->smi_cmd) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "No SMI_CMD in FADT, mode transition failed.\n")); + return_ACPI_STATUS (AE_NO_HARDWARE_RESPONSE); + } + + /* + * ACPI 2.0 clarified the meaning of ACPI_ENABLE and ACPI_DISABLE + * in FADT: If it is zero, enabling or disabling is not supported. + * As old systems may have used zero for mode transition, + * we make sure both the numbers are zero to determine these + * transitions are not supported. + */ + if (!acpi_gbl_FADT->acpi_enable && !acpi_gbl_FADT->acpi_disable) { + ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "No mode transition supported in this system.\n")); + return_ACPI_STATUS (AE_OK); + } switch (mode) { case ACPI_SYS_MODE_ACPI: diff -Nru a/drivers/acpi/hardware/hwgpe.c b/drivers/acpi/hardware/hwgpe.c --- a/drivers/acpi/hardware/hwgpe.c Fri Aug 16 14:34:52 2002 +++ b/drivers/acpi/hardware/hwgpe.c Fri Aug 16 14:34:52 2002 @@ -2,7 +2,7 @@ /****************************************************************************** * * Module Name: hwgpe - Low level GPE enable/disable/clear functions - * $Revision: 41 $ + * $Revision: 42 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/hardware/hwregs.c b/drivers/acpi/hardware/hwregs.c --- a/drivers/acpi/hardware/hwregs.c Fri Aug 16 14:35:00 2002 +++ b/drivers/acpi/hardware/hwregs.c Fri Aug 16 14:35:00 2002 @@ -3,7 +3,7 @@ * * Module Name: hwregs - Read/write access functions for the various ACPI * control and status registers. - * $Revision: 133 $ + * $Revision: 134 $ * ******************************************************************************/ diff -Nru a/drivers/acpi/hardware/hwsleep.c b/drivers/acpi/hardware/hwsleep.c --- a/drivers/acpi/hardware/hwsleep.c Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/hardware/hwsleep.c Fri Aug 16 14:34:53 2002 @@ -2,7 +2,7 @@ /****************************************************************************** * * Name: hwsleep.c - ACPI Hardware Sleep/Wake Interface - * $Revision: 45 $ + * $Revision: 46 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/include/acconfig.h b/drivers/acpi/include/acconfig.h --- a/drivers/acpi/include/acconfig.h Fri Aug 16 14:34:54 2002 +++ b/drivers/acpi/include/acconfig.h Fri Aug 16 14:34:54 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Name: acconfig.h - Global configuration constants - * $Revision: 107 $ + * $Revision: 109 $ * *****************************************************************************/ @@ -34,7 +34,7 @@ *****************************************************************************/ /* - * ACPI_DEBUG - This switch enables all the debug facilities of the + * ACPI_DEBUG_OUTPUT - This switch enables all the debug facilities of the * ACPI subsystem. This includes the DEBUG_PRINT output * statements. When disabled, all DEBUG_PRINT * statements are compiled out. @@ -54,7 +54,7 @@ /* Version string */ -#define ACPI_CA_VERSION 0x20020725 +#define ACPI_CA_VERSION 0x20020815 /* Version of ACPI supported */ diff -Nru a/drivers/acpi/include/acdisasm.h b/drivers/acpi/include/acdisasm.h --- a/drivers/acpi/include/acdisasm.h Fri Aug 16 14:34:59 2002 +++ b/drivers/acpi/include/acdisasm.h Fri Aug 16 14:34:59 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Name: acdisasm.h - AML disassembler - * $Revision: 2 $ + * $Revision: 3 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/include/acglobal.h b/drivers/acpi/include/acglobal.h --- a/drivers/acpi/include/acglobal.h Fri Aug 16 14:34:50 2002 +++ b/drivers/acpi/include/acglobal.h Fri Aug 16 14:34:50 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Name: acglobal.h - Declarations for global variables - * $Revision: 128 $ + * $Revision: 130 $ * *****************************************************************************/ @@ -152,8 +152,12 @@ ****************************************************************************/ #define NUM_NS_TYPES INTERNAL_TYPE_INVALID+1 -#define NUM_PREDEFINED_NAMES 9 +#if defined (ACPI_NO_METHOD_EXECUTION) || defined (ACPI_CONSTANT_EVAL_ONLY) +#define NUM_PREDEFINED_NAMES 10 +#else +#define NUM_PREDEFINED_NAMES 9 +#endif ACPI_EXTERN acpi_namespace_node acpi_gbl_root_node_struct; ACPI_EXTERN acpi_namespace_node *acpi_gbl_root_node; @@ -161,7 +165,7 @@ extern const u8 acpi_gbl_ns_properties[NUM_NS_TYPES]; extern const acpi_predefined_names acpi_gbl_pre_defined_names [NUM_PREDEFINED_NAMES]; -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT ACPI_EXTERN u32 acpi_gbl_current_node_count; ACPI_EXTERN u32 acpi_gbl_current_node_size; ACPI_EXTERN u32 acpi_gbl_max_concurrent_node_count; @@ -245,7 +249,7 @@ #endif -#ifdef ENABLE_DEBUGGER +#ifdef ACPI_DEBUGGER extern u8 acpi_gbl_method_executing; extern u8 acpi_gbl_db_terminate_threads; @@ -287,7 +291,7 @@ ACPI_EXTERN u32 acpi_gbl_size_of_node_entries; ACPI_EXTERN u32 acpi_gbl_size_of_acpi_objects; -#endif /* ENABLE_DEBUGGER */ +#endif /* ACPI_DEBUGGER */ #endif /* __ACGLOBAL_H__ */ diff -Nru a/drivers/acpi/include/aclocal.h b/drivers/acpi/include/aclocal.h --- a/drivers/acpi/include/aclocal.h Fri Aug 16 14:34:52 2002 +++ b/drivers/acpi/include/aclocal.h Fri Aug 16 14:34:52 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Name: aclocal.h - Internal data types used across the ACPI subsystem - * $Revision: 173 $ + * $Revision: 175 $ * *****************************************************************************/ @@ -73,7 +73,7 @@ #define NUM_MTX MAX_MTX+1 -#if defined(ACPI_DEBUG) || defined(ENABLE_DEBUGGER) +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) #ifdef DEFINE_ACPI_GLOBALS /* Names for the mutexes used in the subsystem */ @@ -567,7 +567,7 @@ */ typedef struct acpi_opcode_info { -#if defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUG) +#if defined(ACPI_DISASSEMBLER) || defined(ACPI_DEBUG_OUTPUT) NATIVE_CHAR *name; /* Opcode name (disassembler/debug only) */ #endif u32 parse_args; /* Grammar/Parse time arguments */ diff -Nru a/drivers/acpi/include/acmacros.h b/drivers/acpi/include/acmacros.h --- a/drivers/acpi/include/acmacros.h Fri Aug 16 14:34:58 2002 +++ b/drivers/acpi/include/acmacros.h Fri Aug 16 14:34:58 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Name: acmacros.h - C macros for the entire subsystem. - * $Revision: 126 $ + * $Revision: 128 $ * *****************************************************************************/ @@ -287,7 +287,7 @@ /* * Macros for the master AML opcode table */ -#if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUG) +#if defined(ACPI_DISASSEMBLER) || defined (ACPI_DEBUG_OUTPUT) #define ACPI_OP(name,Pargs,Iargs,obj_type,class,type,flags) {name,Pargs,Iargs,flags,obj_type,class,type} #else #define ACPI_OP(name,Pargs,Iargs,obj_type,class,type,flags) {Pargs,Iargs,flags,obj_type,class,type} @@ -353,11 +353,11 @@ /* * Error reporting. These versions add callers module and line#. Since - * _THIS_MODULE gets compiled out when ACPI_DEBUG isn't defined, only + * _THIS_MODULE gets compiled out when ACPI_DEBUG_OUTPUT isn't defined, only * use it in debug mode. */ -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT #define ACPI_REPORT_INFO(fp) {acpi_ut_report_info(_THIS_MODULE,__LINE__,_COMPONENT); \ acpi_os_printf ACPI_PARAM_LIST(fp);} @@ -390,7 +390,7 @@ * Debug macros that are conditionally compiled */ -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT #define ACPI_MODULE_NAME(name) static char *_THIS_MODULE = name; @@ -525,9 +525,9 @@ /* * Some code only gets executed when the debugger is built in. * Note that this is entirely independent of whether the - * DEBUG_PRINT stuff (set by ACPI_DEBUG) is on, or not. + * DEBUG_PRINT stuff (set by ACPI_DEBUG_OUTPUT) is on, or not. */ -#ifdef ENABLE_DEBUGGER +#ifdef ACPI_DEBUGGER #define ACPI_DEBUGGER_EXEC(a) a #else #define ACPI_DEBUGGER_EXEC(a) @@ -536,7 +536,7 @@ /* * For 16-bit code, we want to shrink some things even though - * we are using ACPI_DEBUG to get the debug output + * we are using ACPI_DEBUG_OUTPUT to get the debug output */ #if ACPI_MACHINE_WIDTH == 16 #undef ACPI_DEBUG_ONLY_MEMBERS @@ -545,7 +545,7 @@ #endif -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT /* * 1) Set name to blanks * 2) Copy the object name diff -Nru a/drivers/acpi/include/acobject.h b/drivers/acpi/include/acobject.h --- a/drivers/acpi/include/acobject.h Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/include/acobject.h Fri Aug 16 14:34:53 2002 @@ -2,7 +2,7 @@ /****************************************************************************** * * Name: acobject.h - Definition of acpi_operand_object (Internal object only) - * $Revision: 112 $ + * $Revision: 113 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/include/acoutput.h b/drivers/acpi/include/acoutput.h --- a/drivers/acpi/include/acoutput.h Fri Aug 16 14:34:56 2002 +++ b/drivers/acpi/include/acoutput.h Fri Aug 16 14:34:56 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Name: acoutput.h -- debug output - * $Revision: 87 $ + * $Revision: 90 $ * *****************************************************************************/ @@ -43,18 +43,20 @@ #define ACPI_DISPATCHER 0x00000040 #define ACPI_EXECUTER 0x00000080 #define ACPI_RESOURCES 0x00000100 -#define ACPI_DEBUGGER 0x00000200 +#define ACPI_CA_DEBUGGER 0x00000200 #define ACPI_OS_SERVICES 0x00000400 - -#define ACPI_ALL_COMPONENTS 0x00000FFF - -#define ACPI_COMPONENT_DEFAULT (ACPI_ALL_COMPONENTS) +#define ACPI_CA_DISASSEMBLER 0x00000800 /* Component IDs for ACPI tools and utilities */ #define ACPI_COMPILER 0x00001000 #define ACPI_TOOLS 0x00002000 +#define ACPI_ALL_COMPONENTS 0x00003FFF + +#define ACPI_COMPONENT_DEFAULT (ACPI_ALL_COMPONENTS) + + /* Component IDs reserved for ACPI drivers */ #define ACPI_ALL_DRIVERS 0xFFFF0000 @@ -94,7 +96,8 @@ #define ACPI_LV_ALLOCATIONS 0x00100000 #define ACPI_LV_FUNCTIONS 0x00200000 -#define ACPI_LV_VERBOSITY2 0x00300000 | ACPI_LV_VERBOSITY1 +#define ACPI_LV_OPTIMIZATIONS 0x00400000 +#define ACPI_LV_VERBOSITY2 0x00700000 | ACPI_LV_VERBOSITY1 #define ACPI_LV_ALL ACPI_LV_VERBOSITY2 /* Trace verbosity level 3 [Threading, I/O, and Interrupts] */ @@ -144,6 +147,7 @@ #define ACPI_DB_BFIELD ACPI_DEBUG_LEVEL (ACPI_LV_BFIELD) #define ACPI_DB_TABLES ACPI_DEBUG_LEVEL (ACPI_LV_TABLES) #define ACPI_DB_FUNCTIONS ACPI_DEBUG_LEVEL (ACPI_LV_FUNCTIONS) +#define ACPI_DB_OPTIMIZATIONS ACPI_DEBUG_LEVEL (ACPI_LV_OPTIMIZATIONS) #define ACPI_DB_VALUES ACPI_DEBUG_LEVEL (ACPI_LV_VALUES) #define ACPI_DB_OBJECTS ACPI_DEBUG_LEVEL (ACPI_LV_OBJECTS) #define ACPI_DB_ALLOCATIONS ACPI_DEBUG_LEVEL (ACPI_LV_ALLOCATIONS) diff -Nru a/drivers/acpi/include/acparser.h b/drivers/acpi/include/acparser.h --- a/drivers/acpi/include/acparser.h Fri Aug 16 14:34:59 2002 +++ b/drivers/acpi/include/acparser.h Fri Aug 16 14:34:59 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: acparser.h - AML Parser subcomponent prototypes and defines - * $Revision: 60 $ + * $Revision: 61 $ * *****************************************************************************/ @@ -86,7 +86,7 @@ u32 arg_type, acpi_parse_object *arg); -void +acpi_status acpi_ps_get_next_namepath ( acpi_parse_state *parser_state, acpi_parse_object *arg, @@ -97,11 +97,12 @@ acpi_ps_get_next_field ( acpi_parse_state *parser_state); -acpi_parse_object * +acpi_status acpi_ps_get_next_arg ( acpi_parse_state *parser_state, u32 arg_type, - u32 *arg_count); + u32 *arg_count, + acpi_parse_object **return_arg); /* psfind */ diff -Nru a/drivers/acpi/include/acresrc.h b/drivers/acpi/include/acresrc.h --- a/drivers/acpi/include/acresrc.h Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/include/acresrc.h Fri Aug 16 14:34:53 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Name: acresrc.h - Resource Manager function prototypes - * $Revision: 33 $ + * $Revision: 34 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/include/actbl2.h b/drivers/acpi/include/actbl2.h --- a/drivers/acpi/include/actbl2.h Fri Aug 16 14:34:58 2002 +++ b/drivers/acpi/include/actbl2.h Fri Aug 16 14:34:58 2002 @@ -139,8 +139,8 @@ u16 plvl3_lat; /* Worst case HW latency to enter/exit C3 state */ u16 flush_size; /* Number of flush strides that need to be read */ u16 flush_stride; /* Processor's memory cache line width, in bytes */ - u8 duty_offset; /* Processor_’s duty cycle index in processor's P_CNT reg*/ - u8 duty_width; /* Processor_’s duty cycle value bit width in P_CNT register.*/ + u8 duty_offset; /* Processor’s duty cycle index in processor's P_CNT reg*/ + u8 duty_width; /* Processor’s duty cycle value bit width in P_CNT register.*/ u8 day_alrm; /* Index to day-of-month alarm in RTC CMOS RAM */ u8 mon_alrm; /* Index to month-of-year alarm in RTC CMOS RAM */ u8 century; /* Index to century in RTC CMOS RAM */ diff -Nru a/drivers/acpi/include/acutils.h b/drivers/acpi/include/acutils.h --- a/drivers/acpi/include/acutils.h Fri Aug 16 14:34:51 2002 +++ b/drivers/acpi/include/acutils.h Fri Aug 16 14:34:51 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Name: acutils.h -- prototypes for the common (subsystem-wide) procedures - * $Revision: 142 $ + * $Revision: 143 $ * *****************************************************************************/ @@ -94,7 +94,7 @@ * Ut_global - Global data structures and procedures */ -#if defined(ACPI_DEBUG) || defined(ENABLE_DEBUGGER) +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) NATIVE_CHAR * acpi_ut_get_mutex_name ( @@ -686,7 +686,7 @@ acpi_ut_set_integer_width ( u8 revision); -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT void acpi_ut_display_init_pathname ( acpi_handle obj_handle, diff -Nru a/drivers/acpi/include/amlcode.h b/drivers/acpi/include/amlcode.h --- a/drivers/acpi/include/amlcode.h Fri Aug 16 14:34:59 2002 +++ b/drivers/acpi/include/amlcode.h Fri Aug 16 14:34:59 2002 @@ -3,7 +3,7 @@ * Name: amlcode.h - Definitions for AML, as included in "definition blocks" * Declarations and definitions contained herein are derived * directly from the ACPI specification. - * $Revision: 69 $ + * $Revision: 70 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/include/amlresrc.h b/drivers/acpi/include/amlresrc.h --- a/drivers/acpi/include/amlresrc.h Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/include/amlresrc.h Fri Aug 16 14:34:53 2002 @@ -2,7 +2,7 @@ /****************************************************************************** * * Module Name: amlresrc.h - AML resource descriptors - * $Revision: 20 $ + * $Revision: 22 $ * *****************************************************************************/ @@ -326,173 +326,6 @@ u8 U8item; } ASL_RESOURCE_DESC; - - -#define NEXT_RESOURCE_DESC(a,b) (ASL_RESOURCE_DESC *) (((char *) (a)) + sizeof(b)) - -#define DEFAULT_RESOURCE_DESC_SIZE (sizeof (ASL_RESOURCE_DESC) + sizeof (ASL_END_TAG_DESC)) - - -/* - * Resource utilities - */ - -ASL_RESOURCE_NODE * -rs_allocate_resource_node ( - u32 size); - - void -rs_create_bit_field ( - acpi_parse_object *op, - char *name, - u32 byte_offset, - u32 bit_offset); - -void -rs_create_byte_field ( - acpi_parse_object *op, - char *name, - u32 byte_offset); - -void -rs_set_flag_bits ( - u8 *flags, - acpi_parse_object *op, - u8 position, - u8 default); - -acpi_parse_object * -rs_complete_node_and_get_next ( - acpi_parse_object *op); - -ASL_RESOURCE_NODE * -rs_do_one_resource_descriptor ( - acpi_parse_object *descriptor_type_op, - u32 current_byte_offset); - -u32 -rs_link_descriptor_chain ( - ASL_RESOURCE_NODE **previous_rnode, - ASL_RESOURCE_NODE *rnode); - - -/* - * Small descriptors - */ - -ASL_RESOURCE_NODE * -rs_do_dma_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_end_dependent_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_fixed_io_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_interrupt_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_io_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_irq_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_irq_no_flags_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_memory24_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_memory32_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_memory32_fixed_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_start_dependent_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_start_dependent_no_pri_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_vendor_small_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - - -/* - * Large descriptors - */ - -u32 -rs_get_string_data_length ( - acpi_parse_object *initializer_op); - -ASL_RESOURCE_NODE * -rs_do_dword_io_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_dword_memory_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_qword_io_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_qword_memory_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_word_io_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_word_bus_number_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_vendor_large_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); - -ASL_RESOURCE_NODE * -rs_do_general_register_descriptor ( - acpi_parse_object *op, - u32 current_byte_offset); #endif diff -Nru a/drivers/acpi/include/platform/acenv.h b/drivers/acpi/include/platform/acenv.h --- a/drivers/acpi/include/platform/acenv.h Fri Aug 16 14:34:55 2002 +++ b/drivers/acpi/include/platform/acenv.h Fri Aug 16 14:34:55 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Name: acenv.h - Generation environment specific items - * $Revision: 99 $ + * $Revision: 101 $ * *****************************************************************************/ @@ -33,7 +33,7 @@ #ifdef _ACPI_DUMP_APP #ifndef MSDOS -#define ACPI_DEBUG +#define ACPI_DEBUG_OUTPUT #endif #define ACPI_APPLICATION #define ACPI_DISASSEMBLER @@ -44,15 +44,15 @@ #ifdef _ACPI_EXEC_APP #undef DEBUGGER_THREADING #define DEBUGGER_THREADING DEBUGGER_SINGLE_THREADED -#define ACPI_DEBUG +#define ACPI_DEBUG_OUTPUT #define ACPI_APPLICATION -#define ENABLE_DEBUGGER +#define ACPI_DEBUGGER #define ACPI_DISASSEMBLER #define ACPI_USE_SYSTEM_CLIBRARY #endif #ifdef _ACPI_ASL_COMPILER -#define ACPI_DEBUG +#define ACPI_DEBUG_OUTPUT #define ACPI_APPLICATION #define ACPI_DISASSEMBLER #define ACPI_CONSTANT_EVAL_ONLY @@ -149,7 +149,7 @@ * 1) This is the debug version * 2) This is NOT a 16-bit version of the code (not enough real-mode memory) */ -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT #if ACPI_MACHINE_WIDTH != 16 #define ACPI_DBG_TRACK_ALLOCATIONS #endif diff -Nru a/drivers/acpi/namespace/nsaccess.c b/drivers/acpi/namespace/nsaccess.c --- a/drivers/acpi/namespace/nsaccess.c Fri Aug 16 14:35:01 2002 +++ b/drivers/acpi/namespace/nsaccess.c Fri Aug 16 14:35:01 2002 @@ -1,7 +1,7 @@ /******************************************************************************* * * Module Name: nsaccess - Top-level functions for accessing ACPI namespace - * $Revision: 156 $ + * $Revision: 161 $ * ******************************************************************************/ @@ -117,6 +117,19 @@ * used for initial values are implemented here. */ switch (init_val->type) { + case ACPI_TYPE_METHOD: + obj_desc->method.param_count = + (u8) ACPI_STRTOUL (init_val->val, NULL, 10); + obj_desc->common.flags |= AOPOBJ_DATA_VALID; + +#if defined (ACPI_NO_METHOD_EXECUTION) || defined (ACPI_CONSTANT_EVAL_ONLY) + + /* Compiler cheats by putting parameter count in the Owner_iD */ + + new_node->owner_id = obj_desc->method.param_count; +#endif + break; + case ACPI_TYPE_INTEGER: obj_desc->integer.value = @@ -228,6 +241,7 @@ acpi_namespace_node **return_node) { acpi_status status; + NATIVE_CHAR *path = pathname; acpi_namespace_node *prefix_node; acpi_namespace_node *current_node = NULL; acpi_namespace_node *this_node = NULL; @@ -235,7 +249,9 @@ acpi_name simple_name; acpi_object_type type_to_check_for; acpi_object_type this_search_type; - u32 local_flags = flags & ~ACPI_NS_ERROR_IF_FOUND; + u32 search_parent_flag = ACPI_NS_SEARCH_PARENT; + u32 local_flags = flags & ~(ACPI_NS_ERROR_IF_FOUND | + ACPI_NS_SEARCH_PARENT); ACPI_FUNCTION_TRACE ("Ns_lookup"); @@ -266,6 +282,21 @@ } else { prefix_node = scope_info->scope.node; + if (ACPI_GET_DESCRIPTOR_TYPE (prefix_node) != ACPI_DESC_TYPE_NAMED) { + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "[%p] Not a namespace node\n", + prefix_node)); + return_ACPI_STATUS (AE_AML_INTERNAL); + } + + /* + * This node might not be a actual "scope" node (such as a + * Device/Method, etc.) It could be a Package or other object node. + * Backup up the tree to find the containing scope node. + */ + while (!acpi_ns_opens_scope (prefix_node->type) && + prefix_node->type != ACPI_TYPE_ANY) { + prefix_node = acpi_ns_get_parent_node (prefix_node); + } } /* @@ -297,7 +328,7 @@ num_segments = 0; this_node = acpi_gbl_root_node; - pathname = ""; + path = ""; ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "Null Pathname (Zero segments), Flags=%X\n", flags)); @@ -316,23 +347,24 @@ * Parent Prefixes (in which case the name's scope is relative * to the current scope). */ - if (*pathname == (u8) AML_ROOT_PREFIX) { + if (*path == (u8) AML_ROOT_PREFIX) { /* Pathname is fully qualified, start from the root */ this_node = acpi_gbl_root_node; + search_parent_flag = ACPI_NS_NO_UPSEARCH; /* Point to name segment part */ - pathname++; + path++; - ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "Searching from root [%p]\n", - this_node)); + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Path is absolute from root [%p]\n", this_node)); } else { /* Pathname is relative to current scope, start there */ ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, - "Searching relative to pfx scope [%p]\n", + "Searching relative to prefix scope [%p]\n", prefix_node)); /* @@ -340,12 +372,15 @@ * the parent node for each prefix instance. */ this_node = prefix_node; - while (*pathname == (u8) AML_PARENT_PREFIX) { + while (*path == (u8) AML_PARENT_PREFIX) { + /* Name is fully qualified, no search rules apply */ + + search_parent_flag = ACPI_NS_NO_UPSEARCH; /* * Point past this prefix to the name segment * part or the next Parent Prefix */ - pathname++; + path++; /* Backup to the parent node */ @@ -358,6 +393,11 @@ return_ACPI_STATUS (AE_NOT_FOUND); } } + + if (search_parent_flag == ACPI_NS_NO_UPSEARCH) { + ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, + "Path is absolute with one or more carats\n")); + } } /* @@ -373,7 +413,7 @@ * Examine the name prefix opcode, if any, to determine the number of * segments. */ - switch (*pathname) { + switch (*path) { case 0: /* * Null name after a root or parent prefixes. We already @@ -387,10 +427,14 @@ case AML_DUAL_NAME_PREFIX: + /* More than one Name_seg, search rules do not apply */ + + search_parent_flag = ACPI_NS_NO_UPSEARCH; + /* Two segments, point to first name segment */ num_segments = 2; - pathname++; + path++; ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "Dual Pathname (2 segments, Flags=%X)\n", flags)); @@ -398,11 +442,15 @@ case AML_MULTI_NAME_PREFIX_OP: + /* More than one Name_seg, search rules do not apply */ + + search_parent_flag = ACPI_NS_NO_UPSEARCH; + /* Extract segment count, point to first name segment */ - pathname++; - num_segments = (u32) (u8) *pathname; - pathname++; + path++; + num_segments = (u32) (u8) *path; + path++; ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "Multi Pathname (%d Segments, Flags=%X) \n", @@ -421,33 +469,49 @@ break; } - ACPI_DEBUG_EXEC (acpi_ns_print_pathname (num_segments, pathname)); + ACPI_DEBUG_EXEC (acpi_ns_print_pathname (num_segments, path)); } + /* * Search namespace for each segment of the name. Loop through and - * verify/add each name segment. + * verify (or add to the namespace) each name segment. + * + * The object type is significant only at the last name + * segment. (We don't care about the types along the path, only + * the type of the final target object.) */ + this_search_type = ACPI_TYPE_ANY; current_node = this_node; while (num_segments && current_node) { - /* - * Search for the current name segment under the current - * named object. The Type is significant only at the last name - * segment. (We don't care about the types along the path, only - * the type of the final target object.) - */ - this_search_type = ACPI_TYPE_ANY; num_segments--; if (!num_segments) { + /* + * This is the last segment, enable typechecking + */ this_search_type = type; - local_flags = flags; + + /* + * Only allow automatic parent search (search rules) if the caller + * requested it AND we have a single, non-fully-qualified Name_seg + */ + if ((search_parent_flag != ACPI_NS_NO_UPSEARCH) && + (flags & ACPI_NS_SEARCH_PARENT)) { + local_flags |= ACPI_NS_SEARCH_PARENT; + } + + /* Set error flag according to caller */ + + if (flags & ACPI_NS_ERROR_IF_FOUND) { + local_flags |= ACPI_NS_ERROR_IF_FOUND; + } } /* Extract one ACPI name from the front of the pathname */ - ACPI_MOVE_UNALIGNED32_TO_32 (&simple_name, pathname); + ACPI_MOVE_UNALIGNED32_TO_32 (&simple_name, path); - /* Try to find the ACPI name */ + /* Try to find the single (4 character) ACPI name */ status = acpi_ns_search_and_enter (simple_name, walk_state, current_node, interpreter_mode, this_search_type, local_flags, &this_node); @@ -457,7 +521,8 @@ ACPI_DEBUG_PRINT ((ACPI_DB_NAMES, "Name [%4.4s] not found in scope [%4.4s] %p\n", - (char *) &simple_name, (char *) ¤t_node->name, current_node)); + (char *) &simple_name, (char *) ¤t_node->name, + current_node)); } return_ACPI_STATUS (status); @@ -504,7 +569,7 @@ /* Point to next name segment and make this node current */ - pathname += ACPI_NAME_SIZE; + path += ACPI_NAME_SIZE; current_node = this_node; } diff -Nru a/drivers/acpi/namespace/nsdump.c b/drivers/acpi/namespace/nsdump.c --- a/drivers/acpi/namespace/nsdump.c Fri Aug 16 14:35:00 2002 +++ b/drivers/acpi/namespace/nsdump.c Fri Aug 16 14:35:00 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: nsdump - table dumping routines for debug - * $Revision: 137 $ + * $Revision: 139 $ * *****************************************************************************/ @@ -32,7 +32,7 @@ #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME ("nsdump") -#if defined(ACPI_DEBUG) || defined(ENABLE_DEBUGGER) +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) /******************************************************************************* diff -Nru a/drivers/acpi/namespace/nsdumpdv.c b/drivers/acpi/namespace/nsdumpdv.c --- a/drivers/acpi/namespace/nsdumpdv.c Fri Aug 16 14:34:51 2002 +++ b/drivers/acpi/namespace/nsdumpdv.c Fri Aug 16 14:34:51 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: nsdump - table dumping routines for debug - * $Revision: 1 $ + * $Revision: 3 $ * *****************************************************************************/ @@ -33,7 +33,7 @@ ACPI_MODULE_NAME ("nsdumpdv") -#if defined(ACPI_DEBUG) || defined(ENABLE_DEBUGGER) +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) /******************************************************************************* * diff -Nru a/drivers/acpi/namespace/nseval.c b/drivers/acpi/namespace/nseval.c --- a/drivers/acpi/namespace/nseval.c Fri Aug 16 14:34:56 2002 +++ b/drivers/acpi/namespace/nseval.c Fri Aug 16 14:34:56 2002 @@ -2,7 +2,7 @@ * * Module Name: nseval - Object evaluation interfaces -- includes control * method lookup and execution. - * $Revision: 117 $ + * $Revision: 118 $ * ******************************************************************************/ diff -Nru a/drivers/acpi/namespace/nsinit.c b/drivers/acpi/namespace/nsinit.c --- a/drivers/acpi/namespace/nsinit.c Fri Aug 16 14:34:56 2002 +++ b/drivers/acpi/namespace/nsinit.c Fri Aug 16 14:34:56 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: nsinit - namespace initialization - * $Revision: 47 $ + * $Revision: 49 $ * *****************************************************************************/ @@ -369,7 +369,7 @@ if (status != AE_NOT_FOUND) { /* Ignore error and move on to next device */ - #ifdef ACPI_DEBUG + #ifdef ACPI_DEBUG_OUTPUT NATIVE_CHAR *scope_name = acpi_ns_get_external_pathname (obj_handle); ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "%s._INI failed: %s\n", diff -Nru a/drivers/acpi/namespace/nsload.c b/drivers/acpi/namespace/nsload.c --- a/drivers/acpi/namespace/nsload.c Fri Aug 16 14:35:00 2002 +++ b/drivers/acpi/namespace/nsload.c Fri Aug 16 14:35:00 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: nsload - namespace loading/expanding/contracting procedures - * $Revision: 57 $ + * $Revision: 58 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/namespace/nsnames.c b/drivers/acpi/namespace/nsnames.c --- a/drivers/acpi/namespace/nsnames.c Fri Aug 16 14:35:01 2002 +++ b/drivers/acpi/namespace/nsnames.c Fri Aug 16 14:35:01 2002 @@ -1,7 +1,7 @@ /******************************************************************************* * * Module Name: nsnames - Name manipulation and search - * $Revision: 78 $ + * $Revision: 79 $ * ******************************************************************************/ @@ -103,7 +103,7 @@ } -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT /******************************************************************************* * * FUNCTION: Acpi_ns_get_external_pathname diff -Nru a/drivers/acpi/namespace/nssearch.c b/drivers/acpi/namespace/nssearch.c --- a/drivers/acpi/namespace/nssearch.c Fri Aug 16 14:35:01 2002 +++ b/drivers/acpi/namespace/nssearch.c Fri Aug 16 14:35:01 2002 @@ -1,7 +1,7 @@ /******************************************************************************* * * Module Name: nssearch - Namespace search - * $Revision: 86 $ + * $Revision: 89 $ * ******************************************************************************/ @@ -71,7 +71,7 @@ ACPI_FUNCTION_TRACE ("Ns_search_node"); -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT if (ACPI_LV_NAMES & acpi_dbg_level) { NATIVE_CHAR *scope_name; @@ -290,10 +290,10 @@ /* Parameter validation */ if (!node || !target_name || !return_node) { - ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Null param- Table %p Name %X Return %p\n", + ACPI_DEBUG_PRINT ((ACPI_DB_ERROR, "Null param: Node %p Name %X Return_node %p\n", node, target_name, return_node)); - ACPI_REPORT_ERROR (("Ns_search_and_enter: bad (null) parameter\n")); + ACPI_REPORT_ERROR (("Ns_search_and_enter: Null parameter\n")); return_ACPI_STATUS (AE_BAD_PARAMETER); } diff -Nru a/drivers/acpi/namespace/nsutils.c b/drivers/acpi/namespace/nsutils.c --- a/drivers/acpi/namespace/nsutils.c Fri Aug 16 14:34:58 2002 +++ b/drivers/acpi/namespace/nsutils.c Fri Aug 16 14:34:58 2002 @@ -2,7 +2,7 @@ * * Module Name: nsutils - Utilities for accessing ACPI namespace, accessing * parents and siblings and Scope manipulation - * $Revision: 110 $ + * $Revision: 112 $ * *****************************************************************************/ @@ -434,9 +434,12 @@ case '^': for (i = 0; i < internal_name_length; i++) { - if (internal_name[i] != '^') { + if (internal_name[i] == '^') { prefix_length = i + 1; } + else { + break; + } } if (i == internal_name_length) { @@ -834,7 +837,7 @@ } -#if defined(ACPI_DEBUG) || defined(ENABLE_DEBUGGER) +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) /******************************************************************************* * @@ -868,7 +871,7 @@ return (FALSE); } -#endif /* ACPI_DEBUG */ +#endif /* ACPI_DEBUG_OUTPUT */ /******************************************************************************* diff -Nru a/drivers/acpi/namespace/nsxfeval.c b/drivers/acpi/namespace/nsxfeval.c --- a/drivers/acpi/namespace/nsxfeval.c Fri Aug 16 14:35:01 2002 +++ b/drivers/acpi/namespace/nsxfeval.c Fri Aug 16 14:35:01 2002 @@ -2,7 +2,7 @@ * * Module Name: nsxfeval - Public interfaces to the ACPI subsystem * ACPI Object evaluation interfaces - * $Revision: 1 $ + * $Revision: 2 $ * ******************************************************************************/ diff -Nru a/drivers/acpi/parser/psargs.c b/drivers/acpi/parser/psargs.c --- a/drivers/acpi/parser/psargs.c Fri Aug 16 14:34:55 2002 +++ b/drivers/acpi/parser/psargs.c Fri Aug 16 14:34:55 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: psargs - Parse AML opcode arguments - * $Revision: 62 $ + * $Revision: 64 $ * *****************************************************************************/ @@ -162,7 +162,7 @@ /* Handle multiple prefix characters */ while (acpi_ps_is_prefix_char (ACPI_GET8 (end))) { - /* include prefix '\\' or '^' */ + /* Include prefix '\\' or '^' */ end++; } @@ -218,20 +218,17 @@ * Method_call - Whether the namepath can be the start * of a method call * - * RETURN: None + * RETURN: Status * - * DESCRIPTION: Get next name (if method call, push appropriate # args). Names - * are looked up in either the parsed or internal namespace to - * determine if the name represents a control method. If a method + * DESCRIPTION: Get next name (if method call, return # of required args). + * Names are looked up in the internal namespace to determine + * if the name represents a control method. If a method * is found, the number of arguments to the method is returned. * This information is critical for parsing to continue correctly. * ******************************************************************************/ - -#ifdef PARSER_ONLY - -void +acpi_status acpi_ps_get_next_namepath ( acpi_parse_state *parser_state, acpi_parse_object *arg, @@ -240,188 +237,91 @@ { NATIVE_CHAR *path; acpi_parse_object *name_op; - acpi_parse_object *op; - acpi_parse_object *count; + acpi_status status = AE_OK; + acpi_operand_object *method_desc; + acpi_namespace_node *node; + acpi_generic_state scope_info; ACPI_FUNCTION_TRACE ("Ps_get_next_namepath"); path = acpi_ps_get_next_namestring (parser_state); - if (!path || !method_call) { - /* Null name case, create a null namepath object */ - acpi_ps_init_op (arg, AML_INT_NAMEPATH_OP); - arg->common.value.name = path; - return_VOID; - } + /* Null path case is allowed */ - - if (acpi_gbl_parsed_namespace_root) { + if (path) { /* - * Lookup the name in the parsed namespace + * Lookup the name in the internal namespace */ - op = NULL; - if (method_call) { - op = acpi_ps_find (acpi_ps_get_parent_scope (parser_state), - path, AML_METHOD_OP, 0); + scope_info.scope.node = NULL; + node = parser_state->start_node; + if (node) { + scope_info.scope.node = node; } - if (op) { - if (op->common.aml_opcode == AML_METHOD_OP) { - /* - * The name refers to a control method, so this namepath is a - * method invocation. We need to 1) Get the number of arguments - * associated with this method, and 2) Change the NAMEPATH - * object into a METHODCALL object. - */ - count = acpi_ps_get_arg (op, 0); - if (count && count->common.aml_opcode == AML_BYTE_OP) { - name_op = acpi_ps_alloc_op (AML_INT_NAMEPATH_OP); - if (name_op) { - /* Change arg into a METHOD CALL and attach the name */ - - acpi_ps_init_op (arg, AML_INT_METHODCALL_OP); - - name_op->common.value.name = path; - - /* Point METHODCALL/NAME to the METHOD Node */ - - name_op->common.node = (acpi_namespace_node *) op; - acpi_ps_append_arg (arg, name_op); - - *arg_count = (u32) count->common.value.integer & - METHOD_FLAGS_ARG_COUNT; - } + /* + * Lookup object. We don't want to add anything new to the namespace + * here, however. So we use MODE_EXECUTE. Allow searching of the + * parent tree, but don't open a new scope -- we just want to lookup the + * object (MUST BE mode EXECUTE to perform upsearch) + */ + status = acpi_ns_lookup (&scope_info, path, ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, + ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE, NULL, + &node); + if (ACPI_SUCCESS (status) && method_call) { + if (node->type == ACPI_TYPE_METHOD) { + method_desc = acpi_ns_get_attached_object (node); + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Control Method - %p Desc %p Path=%p\n", + node, method_desc, path)); + + name_op = acpi_ps_alloc_op (AML_INT_NAMEPATH_OP); + if (!name_op) { + return_ACPI_STATUS (AE_NO_MEMORY); } - return_VOID; - } - - /* - * Else this is normal named object reference. - * Just init the NAMEPATH object with the pathname. - * (See code below) - */ - } - } - - /* - * Either we didn't find the object in the namespace, or the object is - * something other than a control method. Just initialize the Op with the - * pathname - */ - acpi_ps_init_op (arg, AML_INT_NAMEPATH_OP); - arg->common.value.name = path; - - - return_VOID; -} - - -#else - - -void -acpi_ps_get_next_namepath ( - acpi_parse_state *parser_state, - acpi_parse_object *arg, - u32 *arg_count, - u8 method_call) -{ - NATIVE_CHAR *path; - acpi_parse_object *name_op; - acpi_status status; - acpi_operand_object *method_desc; - acpi_namespace_node *node; - acpi_generic_state scope_info; - - - ACPI_FUNCTION_TRACE ("Ps_get_next_namepath"); - - - path = acpi_ps_get_next_namestring (parser_state); - if (!path || !method_call) { - /* Null name case, create a null namepath object */ - - acpi_ps_init_op (arg, AML_INT_NAMEPATH_OP); - arg->common.value.name = path; - return_VOID; - } - - /* - * Lookup the name in the internal namespace - */ - scope_info.scope.node = NULL; - node = parser_state->start_node; - if (node) { - scope_info.scope.node = node; - } + /* Change arg into a METHOD CALL and attach name to it */ - /* - * Lookup object. We don't want to add anything new to the namespace - * here, however. So we use MODE_EXECUTE. Allow searching of the - * parent tree, but don't open a new scope -- we just want to lookup the - * object (MUST BE mode EXECUTE to perform upsearch) - */ - status = acpi_ns_lookup (&scope_info, path, ACPI_TYPE_ANY, ACPI_IMODE_EXECUTE, - ACPI_NS_SEARCH_PARENT | ACPI_NS_DONT_OPEN_SCOPE, NULL, - &node); - if (ACPI_SUCCESS (status)) { - if (node->type == ACPI_TYPE_METHOD) { - method_desc = acpi_ns_get_attached_object (node); - ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Control Method - %p Desc %p Path=%p\n", - node, method_desc, path)); - - name_op = acpi_ps_alloc_op (AML_INT_NAMEPATH_OP); - if (!name_op) { - return_VOID; - } + acpi_ps_init_op (arg, AML_INT_METHODCALL_OP); - /* Change arg into a METHOD CALL and attach name to it */ + name_op->common.value.name = path; - acpi_ps_init_op (arg, AML_INT_METHODCALL_OP); + /* Point METHODCALL/NAME to the METHOD Node */ - name_op->common.value.name = path; + name_op->common.node = node; + acpi_ps_append_arg (arg, name_op); - /* Point METHODCALL/NAME to the METHOD Node */ + if (!method_desc) { + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Control Method - %p has no attached object\n", + node)); + return_ACPI_STATUS (AE_AML_INTERNAL); + } - name_op->common.node = node; - acpi_ps_append_arg (arg, name_op); + ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Control Method - %p Args %X\n", + node, method_desc->method.param_count)); - if (!method_desc) { - ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Control Method - %p has no attached object\n", - node)); - return_VOID; + *arg_count = method_desc->method.param_count; + return_ACPI_STATUS (AE_OK); } - ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, "Control Method - %p Args %X\n", - node, method_desc->method.param_count)); - - *arg_count = method_desc->method.param_count; - return_VOID; + /* + * Else this is normal named object reference. + * Just init the NAMEPATH object with the pathname. + * (See code below) + */ } - - /* - * Else this is normal named object reference. - * Just init the NAMEPATH object with the pathname. - * (See code below) - */ } /* - * Either we didn't find the object in the namespace, or the object is - * something other than a control method. Just initialize the Op with the - * pathname. + * Regardless of success/failure above, + * Just initialize the Op with the pathname. */ acpi_ps_init_op (arg, AML_INT_NAMEPATH_OP); arg->common.value.name = path; - - return_VOID; + return_ACPI_STATUS (status); } -#endif /******************************************************************************* * @@ -630,23 +530,25 @@ * Arg_count - If the argument points to a control method * the method's argument is returned here. * - * RETURN: An op object containing the next argument. + * RETURN: Status, and an op object containing the next argument. * * DESCRIPTION: Get next argument (including complex list arguments that require * pushing the parser stack) * ******************************************************************************/ -acpi_parse_object * +acpi_status acpi_ps_get_next_arg ( acpi_parse_state *parser_state, u32 arg_type, - u32 *arg_count) + u32 *arg_count, + acpi_parse_object **return_arg) { acpi_parse_object *arg = NULL; acpi_parse_object *prev = NULL; acpi_parse_object *field; u32 subop; + acpi_status status = AE_OK; ACPI_FUNCTION_TRACE_PTR ("Ps_get_next_arg", parser_state); @@ -663,15 +565,16 @@ /* constants, strings, and namestrings are all the same size */ arg = acpi_ps_alloc_op (AML_BYTE_OP); - if (arg) { - acpi_ps_get_next_simple_arg (parser_state, arg_type, arg); + if (!arg) { + return_ACPI_STATUS (AE_NO_MEMORY); } + acpi_ps_get_next_simple_arg (parser_state, arg_type, arg); break; case ARGP_PKGLENGTH: - /* package length, nothing returned */ + /* Package length, nothing returned */ parser_state->pkg_end = acpi_ps_get_next_package_end (parser_state); break; @@ -680,18 +583,17 @@ case ARGP_FIELDLIST: if (parser_state->aml < parser_state->pkg_end) { - /* non-empty list */ + /* Non-empty list */ while (parser_state->aml < parser_state->pkg_end) { field = acpi_ps_get_next_field (parser_state); if (!field) { - break; + return_ACPI_STATUS (AE_NO_MEMORY); } if (prev) { prev->common.next = field; } - else { arg = field; } @@ -699,7 +601,7 @@ prev = field; } - /* skip to End of byte data */ + /* Skip to End of byte data */ parser_state->aml = parser_state->pkg_end; } @@ -709,17 +611,19 @@ case ARGP_BYTELIST: if (parser_state->aml < parser_state->pkg_end) { - /* non-empty list */ + /* Non-empty list */ arg = acpi_ps_alloc_op (AML_INT_BYTELIST_OP); - if (arg) { - /* fill in bytelist data */ - - arg->common.value.size = ACPI_PTR_DIFF (parser_state->pkg_end, parser_state->aml); - arg->named.data = parser_state->aml; + if (!arg) { + return_ACPI_STATUS (AE_NO_MEMORY); } - /* skip to End of byte data */ + /* Fill in bytelist data */ + + arg->common.value.size = ACPI_PTR_DIFF (parser_state->pkg_end, parser_state->aml); + arg->named.data = parser_state->aml; + + /* Skip to End of byte data */ parser_state->aml = parser_state->pkg_end; } @@ -728,24 +632,25 @@ case ARGP_TARGET: case ARGP_SUPERNAME: - case ARGP_SIMPLENAME: { - subop = acpi_ps_peek_opcode (parser_state); - if (subop == 0 || - acpi_ps_is_leading_char (subop) || - acpi_ps_is_prefix_char (subop)) { - /* Null_name or Name_string */ - - arg = acpi_ps_alloc_op (AML_INT_NAMEPATH_OP); - if (arg) { - acpi_ps_get_next_namepath (parser_state, arg, arg_count, 0); - } + case ARGP_SIMPLENAME: + + subop = acpi_ps_peek_opcode (parser_state); + if (subop == 0 || + acpi_ps_is_leading_char (subop) || + acpi_ps_is_prefix_char (subop)) { + /* Null_name or Name_string */ + + arg = acpi_ps_alloc_op (AML_INT_NAMEPATH_OP); + if (!arg) { + return_ACPI_STATUS (AE_NO_MEMORY); } - else { - /* single complex argument, nothing returned */ + status = acpi_ps_get_next_namepath (parser_state, arg, arg_count, 0); + } + else { + /* single complex argument, nothing returned */ - *arg_count = 1; - } + *arg_count = 1; } break; @@ -770,10 +675,14 @@ } break; + default: + ACPI_REPORT_ERROR (("Invalid Arg_type: %X\n", arg_type)); + status = AE_AML_OPERAND_TYPE; break; } - return_PTR (arg); + *return_arg = arg; + return_ACPI_STATUS (status); } diff -Nru a/drivers/acpi/parser/psopcode.c b/drivers/acpi/parser/psopcode.c --- a/drivers/acpi/parser/psopcode.c Fri Aug 16 14:34:54 2002 +++ b/drivers/acpi/parser/psopcode.c Fri Aug 16 14:34:54 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: psopcode - Parser/Interpreter opcode information table - * $Revision: 71 $ + * $Revision: 72 $ * *****************************************************************************/ @@ -558,17 +558,17 @@ /* 60 */ ACPI_OP ("LNotEqual", ARGP_LNOTEQUAL_OP, ARGI_LNOTEQUAL_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT), /* 61 */ ACPI_OP ("LLessEqual", ARGP_LLESSEQUAL_OP, ARGI_LLESSEQUAL_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT), /* 62 */ ACPI_OP ("LGreaterEqual", ARGP_LGREATEREQUAL_OP, ARGI_LGREATEREQUAL_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_HAS_ARGS | AML_CONSTANT), -/* 63 */ ACPI_OP ("[NamePath]", ARGP_NAMEPATH_OP, ARGI_NAMEPATH_OP, INTERNAL_TYPE_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_LITERAL, AML_NSOBJECT | AML_NSNODE ), -/* 64 */ ACPI_OP ("[MethodCall]", ARGP_METHODCALL_OP, ARGI_METHODCALL_OP, ACPI_TYPE_METHOD, AML_CLASS_METHOD_CALL, AML_TYPE_METHOD_CALL, AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE), -/* 65 */ ACPI_OP ("[ByteList]", ARGP_BYTELIST_OP, ARGI_BYTELIST_OP, ACPI_TYPE_ANY, AML_CLASS_ARGUMENT, AML_TYPE_LITERAL, 0), -/* 66 */ ACPI_OP ("[ReservedField]", ARGP_RESERVEDFIELD_OP, ARGI_RESERVEDFIELD_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), -/* 67 */ ACPI_OP ("[NamedField]", ARGP_NAMEDFIELD_OP, ARGI_NAMEDFIELD_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED ), -/* 68 */ ACPI_OP ("[AccessField]", ARGP_ACCESSFIELD_OP, ARGI_ACCESSFIELD_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), -/* 69 */ ACPI_OP ("[StaticString", ARGP_STATICSTRING_OP, ARGI_STATICSTRING_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), -/* 6A */ ACPI_OP ("[Return Value]", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, AML_CLASS_RETURN_VALUE, AML_TYPE_RETURN, AML_HAS_ARGS | AML_HAS_RETVAL), -/* 6B */ ACPI_OP ("UNKNOWN_OP!", ARG_NONE, ARG_NONE, INTERNAL_TYPE_INVALID, AML_CLASS_UNKNOWN, AML_TYPE_BOGUS, AML_HAS_ARGS), -/* 6C */ ACPI_OP ("ASCII_ONLY!", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, AML_CLASS_ASCII, AML_TYPE_BOGUS, AML_HAS_ARGS), -/* 6D */ ACPI_OP ("PREFIX_ONLY!", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, AML_CLASS_PREFIX, AML_TYPE_BOGUS, AML_HAS_ARGS), +/* 63 */ ACPI_OP ("-NamePath-", ARGP_NAMEPATH_OP, ARGI_NAMEPATH_OP, INTERNAL_TYPE_REFERENCE, AML_CLASS_ARGUMENT, AML_TYPE_LITERAL, AML_NSOBJECT | AML_NSNODE ), +/* 64 */ ACPI_OP ("-MethodCall-", ARGP_METHODCALL_OP, ARGI_METHODCALL_OP, ACPI_TYPE_METHOD, AML_CLASS_METHOD_CALL, AML_TYPE_METHOD_CALL, AML_HAS_ARGS | AML_NSOBJECT | AML_NSNODE), +/* 65 */ ACPI_OP ("-ByteList-", ARGP_BYTELIST_OP, ARGI_BYTELIST_OP, ACPI_TYPE_ANY, AML_CLASS_ARGUMENT, AML_TYPE_LITERAL, 0), +/* 66 */ ACPI_OP ("-ReservedField-", ARGP_RESERVEDFIELD_OP, ARGI_RESERVEDFIELD_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), +/* 67 */ ACPI_OP ("-NamedField-", ARGP_NAMEDFIELD_OP, ARGI_NAMEDFIELD_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, AML_NSOBJECT | AML_NSOPCODE | AML_NSNODE | AML_NAMED ), +/* 68 */ ACPI_OP ("-AccessField-", ARGP_ACCESSFIELD_OP, ARGI_ACCESSFIELD_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), +/* 69 */ ACPI_OP ("-StaticString", ARGP_STATICSTRING_OP, ARGI_STATICSTRING_OP, ACPI_TYPE_ANY, AML_CLASS_INTERNAL, AML_TYPE_BOGUS, 0), +/* 6A */ ACPI_OP ("-Return Value-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, AML_CLASS_RETURN_VALUE, AML_TYPE_RETURN, AML_HAS_ARGS | AML_HAS_RETVAL), +/* 6B */ ACPI_OP ("-UNKNOWN_OP-", ARG_NONE, ARG_NONE, INTERNAL_TYPE_INVALID, AML_CLASS_UNKNOWN, AML_TYPE_BOGUS, AML_HAS_ARGS), +/* 6C */ ACPI_OP ("-ASCII_ONLY-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, AML_CLASS_ASCII, AML_TYPE_BOGUS, AML_HAS_ARGS), +/* 6D */ ACPI_OP ("-PREFIX_ONLY-", ARG_NONE, ARG_NONE, ACPI_TYPE_ANY, AML_CLASS_PREFIX, AML_TYPE_BOGUS, AML_HAS_ARGS), /* ACPI 2.0 opcodes */ diff -Nru a/drivers/acpi/parser/psparse.c b/drivers/acpi/parser/psparse.c --- a/drivers/acpi/parser/psparse.c Fri Aug 16 14:34:54 2002 +++ b/drivers/acpi/parser/psparse.c Fri Aug 16 14:34:54 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: psparse - Parser top level AML parse routines - * $Revision: 129 $ + * $Revision: 133 $ * *****************************************************************************/ @@ -113,58 +113,6 @@ /******************************************************************************* * - * FUNCTION: Acpi_ps_find_object - * - * PARAMETERS: Opcode - Current opcode - * Parser_state - Current state - * Walk_state - Current state - * *Op - Where found/new op is returned - * - * RETURN: Status - * - * DESCRIPTION: Find a named object. Two versions - one to search the parse - * tree (for parser-only applications such as acpidump), another - * to search the ACPI internal namespace (the parse tree may no - * longer exist) - * - ******************************************************************************/ - -#ifdef PARSER_ONLY - -acpi_status -acpi_ps_find_object ( - acpi_walk_state *walk_state, - acpi_parse_object **out_op) -{ - NATIVE_CHAR *path; - - - /* We are only interested in opcodes that have an associated name */ - - if (!(walk_state->op_info->flags & AML_NAMED)) { - *out_op = walk_state->op; - return (AE_OK); - } - - /* Find the name in the parse tree */ - - path = acpi_ps_get_next_namestring (&walk_state->parser_state); - - *out_op = acpi_ps_find (acpi_ps_get_parent_scope (&walk_state->parser_state), - path, walk_state->opcode, 1); - - if (!(*out_op)) { - return (AE_NOT_FOUND); - } - - return (AE_OK); -} - -#endif - - -/******************************************************************************* - * * FUNCTION: Acpi_ps_complete_this_op * * PARAMETERS: Walk_state - Current State @@ -181,7 +129,6 @@ acpi_walk_state *walk_state, acpi_parse_object *op) { -#ifndef PARSER_ONLY acpi_parse_object *prev; acpi_parse_object *next; const acpi_opcode_info *parent_info; @@ -317,10 +264,6 @@ } return_VOID; - -#else - return; -#endif } @@ -381,6 +324,13 @@ parser_state->aml = walk_state->aml_last_while; break; +#if 0 + case AE_CTRL_SKIP: + + parser_state->aml = parser_state->scope->parse_scope.pkg_end; + status = AE_OK; + break; +#endif case AE_CTRL_TRUE: @@ -585,14 +535,25 @@ * Get and append arguments until we find the node that contains * the name (the type ARGP_NAME). */ - while (GET_CURRENT_ARG_TYPE (walk_state->arg_types) != ARGP_NAME) { - arg = acpi_ps_get_next_arg (parser_state, + while (GET_CURRENT_ARG_TYPE (walk_state->arg_types) && + (GET_CURRENT_ARG_TYPE (walk_state->arg_types) != ARGP_NAME)) { + status = acpi_ps_get_next_arg (parser_state, GET_CURRENT_ARG_TYPE (walk_state->arg_types), - &walk_state->arg_count); + &walk_state->arg_count, &arg); + if (ACPI_FAILURE (status)) { + goto close_this_op; + } + acpi_ps_append_arg (&pre_op, arg); INCREMENT_ARG_LIST (walk_state->arg_types); } + /* Make sure that we found a NAME and didn't run out of arguments */ + + if (!GET_CURRENT_ARG_TYPE (walk_state->arg_types)) { + return_ACPI_STATUS (AE_AML_NO_OPERAND); + } + /* We know that this arg is a name, move to next arg */ INCREMENT_ARG_LIST (walk_state->arg_types); @@ -688,8 +649,8 @@ if (walk_state->op_info) { ACPI_DEBUG_PRINT ((ACPI_DB_PARSE, - "Opcode %4.4hX [%s] Op %p Aml %p Aml_offset %5.5X\n", - op->common.aml_opcode, walk_state->op_info->name, + "Opcode %4.4X [%s] Op %p Aml %p Aml_offset %5.5X\n", + (u32) op->common.aml_opcode, walk_state->op_info->name, op, parser_state->aml, op->common.aml_offset)); } } @@ -717,7 +678,17 @@ case AML_INT_NAMEPATH_OP: /* AML_NAMESTRING_ARG */ - acpi_ps_get_next_namepath (parser_state, op, &walk_state->arg_count, 1); + status = acpi_ps_get_next_namepath (parser_state, op, &walk_state->arg_count, 1); + if (ACPI_FAILURE (status)) { + /* NOT_FOUND is an error only if we are actually executing a method */ + + if ((((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) == ACPI_PARSE_EXECUTE) && + (status == AE_NOT_FOUND)) || + (status != AE_NOT_FOUND)) { + goto close_this_op; + } + } + walk_state->arg_types = 0; break; @@ -729,14 +700,24 @@ while (GET_CURRENT_ARG_TYPE (walk_state->arg_types) && !walk_state->arg_count) { walk_state->aml_offset = ACPI_PTR_DIFF (parser_state->aml, parser_state->aml_start); - arg = acpi_ps_get_next_arg (parser_state, + status = acpi_ps_get_next_arg (parser_state, GET_CURRENT_ARG_TYPE (walk_state->arg_types), - &walk_state->arg_count); + &walk_state->arg_count, &arg); + if (ACPI_FAILURE (status)) { + /* NOT_FOUND is an error only if we are actually executing a method */ + + if ((((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) == ACPI_PARSE_EXECUTE) && + (status == AE_NOT_FOUND) && + (op->common.aml_opcode != AML_COND_REF_OF_OP)) || + (status != AE_NOT_FOUND)) { + goto close_this_op; + } + } + if (arg) { arg->common.aml_offset = walk_state->aml_offset; acpi_ps_append_arg (op, arg); } - INCREMENT_ARG_LIST (walk_state->arg_types); } @@ -887,15 +868,17 @@ acpi_ps_pop_scope (parser_state, &op, &walk_state->arg_types, &walk_state->arg_count); - walk_state->op = op; - walk_state->op_info = acpi_ps_get_opcode_info (op->common.aml_opcode); - walk_state->opcode = op->common.aml_opcode; + if (op) { + walk_state->op = op; + walk_state->op_info = acpi_ps_get_opcode_info (op->common.aml_opcode); + walk_state->opcode = op->common.aml_opcode; - status = walk_state->ascending_callback (walk_state); - status = acpi_ps_next_parse_state (walk_state, op, status); + status = walk_state->ascending_callback (walk_state); + status = acpi_ps_next_parse_state (walk_state, op, status); - acpi_ps_complete_this_op (walk_state, op); - op = NULL; + acpi_ps_complete_this_op (walk_state, op); + op = NULL; + } status = AE_OK; break; @@ -1053,6 +1036,7 @@ acpi_walk_state *walk_state) { acpi_status status; + acpi_status terminate_status; ACPI_THREAD_STATE *thread; ACPI_THREAD_STATE *prev_walk_list = acpi_gbl_current_walk_list; acpi_walk_state *previous_walk_state; @@ -1131,10 +1115,9 @@ * there's lots of cleanup to do */ if ((walk_state->parse_flags & ACPI_PARSE_MODE_MASK) == ACPI_PARSE_EXECUTE) { - status = acpi_ds_terminate_control_method (walk_state); - if (ACPI_FAILURE (status)) { + terminate_status = acpi_ds_terminate_control_method (walk_state); + if (ACPI_FAILURE (terminate_status)) { ACPI_REPORT_ERROR (("Could not terminate control method properly\n")); - status = AE_OK; /* Ignore error and continue */ } diff -Nru a/drivers/acpi/parser/pstree.c b/drivers/acpi/parser/pstree.c --- a/drivers/acpi/parser/pstree.c Fri Aug 16 14:34:55 2002 +++ b/drivers/acpi/parser/pstree.c Fri Aug 16 14:34:55 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: pstree - Parser op tree manipulation/traversal/search - * $Revision: 39 $ + * $Revision: 40 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/resources/rsdump.c b/drivers/acpi/resources/rsdump.c --- a/drivers/acpi/resources/rsdump.c Fri Aug 16 14:34:58 2002 +++ b/drivers/acpi/resources/rsdump.c Fri Aug 16 14:34:58 2002 @@ -1,7 +1,7 @@ /******************************************************************************* * * Module Name: rsdump - Functions to display the resource structures. - * $Revision: 32 $ + * $Revision: 33 $ * ******************************************************************************/ @@ -31,7 +31,7 @@ ACPI_MODULE_NAME ("rsdump") -#if defined(ACPI_DEBUG) || defined(ENABLE_DEBUGGER) +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) /******************************************************************************* * diff -Nru a/drivers/acpi/system.c b/drivers/acpi/system.c --- a/drivers/acpi/system.c Fri Aug 16 14:34:58 2002 +++ b/drivers/acpi/system.c Fri Aug 16 14:34:58 2002 @@ -256,7 +256,8 @@ acpi_status status = AE_ERROR; unsigned long flags = 0; - save_flags(flags); + local_irq_save(flags); + local_irq_disable(); switch (state) { @@ -270,7 +271,7 @@ do_suspend_lowlevel(0); break; } - restore_flags(flags); + local_irq_restore(flags); return status; } diff -Nru a/drivers/acpi/tables/tbconvrt.c b/drivers/acpi/tables/tbconvrt.c --- a/drivers/acpi/tables/tbconvrt.c Fri Aug 16 14:34:56 2002 +++ b/drivers/acpi/tables/tbconvrt.c Fri Aug 16 14:34:56 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: tbconvrt - ACPI Table conversion utilities - * $Revision: 42 $ + * $Revision: 43 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/tables/tbget.c b/drivers/acpi/tables/tbget.c --- a/drivers/acpi/tables/tbget.c Fri Aug 16 14:34:59 2002 +++ b/drivers/acpi/tables/tbget.c Fri Aug 16 14:34:59 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: tbget - ACPI Table get* routines - * $Revision: 78 $ + * $Revision: 79 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/tables/tbgetall.c b/drivers/acpi/tables/tbgetall.c --- a/drivers/acpi/tables/tbgetall.c Fri Aug 16 14:34:54 2002 +++ b/drivers/acpi/tables/tbgetall.c Fri Aug 16 14:34:54 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: tbgetall - Get all required ACPI tables - * $Revision: 1 $ + * $Revision: 2 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/tables/tbinstal.c b/drivers/acpi/tables/tbinstal.c --- a/drivers/acpi/tables/tbinstal.c Fri Aug 16 14:34:55 2002 +++ b/drivers/acpi/tables/tbinstal.c Fri Aug 16 14:34:55 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: tbinstal - ACPI table installation and removal - * $Revision: 62 $ + * $Revision: 63 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/tables/tbrsdt.c b/drivers/acpi/tables/tbrsdt.c --- a/drivers/acpi/tables/tbrsdt.c Fri Aug 16 14:34:52 2002 +++ b/drivers/acpi/tables/tbrsdt.c Fri Aug 16 14:34:52 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: tbrsdt - ACPI RSDT table utilities - * $Revision: 2 $ + * $Revision: 3 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/tables/tbutils.c b/drivers/acpi/tables/tbutils.c --- a/drivers/acpi/tables/tbutils.c Fri Aug 16 14:34:55 2002 +++ b/drivers/acpi/tables/tbutils.c Fri Aug 16 14:34:55 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: tbutils - Table manipulation utilities - * $Revision: 55 $ + * $Revision: 56 $ * *****************************************************************************/ @@ -135,7 +135,7 @@ table_header, (char *) &signature)); ACPI_REPORT_WARNING (("Invalid table header length (0x%X) found\n", - table_header->length)); + (u32) table_header->length)); ACPI_DUMP_BUFFER (table_header, sizeof (acpi_table_header)); return (AE_BAD_HEADER); } @@ -176,7 +176,7 @@ if (checksum) { ACPI_REPORT_WARNING (("Invalid checksum (%X) in table %4.4s\n", - checksum, table_header->signature)); + (u32) checksum, table_header->signature)); status = AE_BAD_CHECKSUM; } diff -Nru a/drivers/acpi/tables.c b/drivers/acpi/tables.c --- a/drivers/acpi/tables.c Fri Aug 16 14:34:56 2002 +++ b/drivers/acpi/tables.c Fri Aug 16 14:34:56 2002 @@ -36,7 +36,7 @@ #define PREFIX "ACPI: " -#define ACPI_MAX_TABLES ACPI_TABLE_COUNT +#define ACPI_MAX_TABLES 256 static char *acpi_table_signatures[ACPI_TABLE_COUNT] = { [ACPI_TABLE_UNKNOWN] = "????", @@ -338,7 +338,7 @@ sdt.count = (header->length - sizeof(struct acpi_table_header)) >> 3; if (sdt.count > ACPI_MAX_TABLES) { printk(KERN_WARNING PREFIX "Truncated %lu XSDT entries\n", - (ACPI_MAX_TABLES - sdt.count)); + (sdt.count - ACPI_MAX_TABLES)); sdt.count = ACPI_MAX_TABLES; } @@ -383,7 +383,7 @@ sdt.count = (header->length - sizeof(struct acpi_table_header)) >> 2; if (sdt.count > ACPI_MAX_TABLES) { printk(KERN_WARNING PREFIX "Truncated %lu RSDT entries\n", - (ACPI_TABLE_COUNT - sdt.count)); + (sdt.count - ACPI_TABLE_COUNT)); sdt.count = ACPI_MAX_TABLES; } diff -Nru a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c --- a/drivers/acpi/thermal.c Fri Aug 16 14:34:56 2002 +++ b/drivers/acpi/thermal.c Fri Aug 16 14:34:56 2002 @@ -473,6 +473,7 @@ trend, passive->tc1, tz->temperature, tz->last_temperature, passive->tc2, tz->temperature, passive->temperature)); + tz->trips.passive.flags.enabled = 1; /* Heating up? */ if (trend > 0) for (i=0; idevices.count; i++) @@ -518,6 +519,7 @@ struct acpi_thermal_active *active = NULL; int i = 0; int j = 0; + unsigned long maxtemp = 0; ACPI_FUNCTION_TRACE("acpi_thermal_active"); @@ -537,7 +539,8 @@ * associated with this active threshold. */ if (tz->temperature >= active->temperature) { - tz->state.active_index = i; + if (active->temperature > maxtemp) + tz->state.active_index = i, maxtemp = active->temperature; if (!active->flags.enabled) { for (j = 0; j < active->devices.count; j++) { result = acpi_bus_set_power(active->devices.handles[j], ACPI_STATE_D0); @@ -591,6 +594,7 @@ struct acpi_thermal *tz = (struct acpi_thermal *) data; unsigned long sleep_time = 0; int i = 0; + struct acpi_thermal_state state = tz->state; ACPI_FUNCTION_TRACE("acpi_thermal_check"); @@ -613,15 +617,15 @@ * this function determines when a state is entered, but the * individual policy decides when it is exited (e.g. hysteresis). */ - if ((tz->trips.critical.flags.valid) && (tz->temperature >= tz->trips.critical.temperature)) - tz->trips.critical.flags.enabled = 1; - if ((tz->trips.hot.flags.valid) && (tz->temperature >= tz->trips.hot.temperature)) - tz->trips.hot.flags.enabled = 1; - if ((tz->trips.passive.flags.valid) && (tz->temperature >= tz->trips.passive.temperature)) - tz->trips.passive.flags.enabled = 1; + if (tz->trips.critical.flags.valid) + state.critical |= (tz->temperature >= tz->trips.critical.temperature); + if (tz->trips.hot.flags.valid) + state.hot |= (tz->temperature >= tz->trips.hot.temperature); + if (tz->trips.passive.flags.valid) + state.passive |= (tz->temperature >= tz->trips.passive.temperature); for (i=0; itrips.active[i].flags.valid) && (tz->temperature >= tz->trips.active[i].temperature)) - tz->trips.active[i].flags.enabled = 1; + if (tz->trips.active[i].flags.valid) + state.active |= (tz->temperature >= tz->trips.active[i].temperature); /* * Invoke Policy @@ -629,13 +633,13 @@ * Separated from the above check to allow individual policy to * determine when to exit a given state. */ - if (tz->trips.critical.flags.enabled) + if (state.critical) acpi_thermal_critical(tz); - if (tz->trips.hot.flags.enabled) + if (state.hot) acpi_thermal_hot(tz); - if (tz->trips.passive.flags.enabled) + if (state.passive) acpi_thermal_passive(tz); - if (tz->trips.active[0].flags.enabled) + if (state.active) acpi_thermal_active(tz); /* diff -Nru a/drivers/acpi/utilities/utdebug.c b/drivers/acpi/utilities/utdebug.c --- a/drivers/acpi/utilities/utdebug.c Fri Aug 16 14:34:58 2002 +++ b/drivers/acpi/utilities/utdebug.c Fri Aug 16 14:34:58 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: utdebug - Debug print routines - * $Revision: 103 $ + * $Revision: 104 $ * *****************************************************************************/ @@ -30,7 +30,7 @@ ACPI_MODULE_NAME ("utdebug") -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT static u32 acpi_gbl_prev_thread_id = 0xFFFFFFFF; static char *acpi_gbl_fn_entry_str = "----Entry"; diff -Nru a/drivers/acpi/utilities/uteval.c b/drivers/acpi/utilities/uteval.c --- a/drivers/acpi/utilities/uteval.c Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/utilities/uteval.c Fri Aug 16 14:34:53 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: uteval - Object evaluation - * $Revision: 40 $ + * $Revision: 41 $ * *****************************************************************************/ diff -Nru a/drivers/acpi/utilities/utglobal.c b/drivers/acpi/utilities/utglobal.c --- a/drivers/acpi/utilities/utglobal.c Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/utilities/utglobal.c Fri Aug 16 14:34:53 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: utglobal - Global variables for the ACPI subsystem - * $Revision: 165 $ + * $Revision: 168 $ * *****************************************************************************/ @@ -126,7 +126,7 @@ /* Debug switch - level and trace mask */ -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT u32 acpi_dbg_level = DEBUG_DEFAULT; #else u32 acpi_dbg_level = NORMAL_DEFAULT; @@ -185,6 +185,10 @@ {"_REV", ACPI_TYPE_INTEGER, "2"}, {"_OS_", ACPI_TYPE_STRING, ACPI_OS_NAME}, {"_GL_", ACPI_TYPE_MUTEX, "0"}, + +#if defined (ACPI_NO_METHOD_EXECUTION) || defined (ACPI_CONSTANT_EVAL_ONLY) + {"_OSI", ACPI_TYPE_METHOD, "1"}, +#endif {NULL, ACPI_TYPE_ANY, NULL} /* Table terminator */ }; @@ -522,7 +526,7 @@ } -#if defined(ACPI_DEBUG) || defined(ENABLE_DEBUGGER) +#if defined(ACPI_DEBUG_OUTPUT) || defined(ACPI_DEBUGGER) /* * Strings and procedures used for debug only @@ -773,7 +777,7 @@ acpi_gbl_root_node_struct.flags = ANOBJ_END_OF_PEER_LIST; -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT acpi_gbl_lowest_stack_pointer = ACPI_SIZE_MAX; #endif diff -Nru a/drivers/acpi/utilities/utinit.c b/drivers/acpi/utilities/utinit.c --- a/drivers/acpi/utilities/utinit.c Fri Aug 16 14:34:58 2002 +++ b/drivers/acpi/utilities/utinit.c Fri Aug 16 14:34:58 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: utinit - Common ACPI subsystem initialization - * $Revision: 112 $ + * $Revision: 113 $ * *****************************************************************************/ @@ -198,13 +198,13 @@ acpi_gbl_shutdown = TRUE; ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Shutting down ACPI Subsystem...\n")); - /* Close the Namespace */ - - acpi_ns_terminate (); - /* Close the Acpi_event Handling */ acpi_ev_terminate (); + + /* Close the Namespace */ + + acpi_ns_terminate (); /* Close the globals */ diff -Nru a/drivers/acpi/utilities/utmisc.c b/drivers/acpi/utilities/utmisc.c --- a/drivers/acpi/utilities/utmisc.c Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/utilities/utmisc.c Fri Aug 16 14:34:53 2002 @@ -1,7 +1,7 @@ /******************************************************************************* * * Module Name: utmisc - common utility procedures - * $Revision: 78 $ + * $Revision: 80 $ * ******************************************************************************/ @@ -104,7 +104,7 @@ } -#ifdef ACPI_DEBUG +#ifdef ACPI_DEBUG_OUTPUT /******************************************************************************* * * FUNCTION: Acpi_ut_display_init_pathname diff -Nru a/drivers/acpi/utilities/utxface.c b/drivers/acpi/utilities/utxface.c --- a/drivers/acpi/utilities/utxface.c Fri Aug 16 14:34:53 2002 +++ b/drivers/acpi/utilities/utxface.c Fri Aug 16 14:34:53 2002 @@ -1,7 +1,7 @@ /****************************************************************************** * * Module Name: utxface - External interfaces for "global" ACPI functions - * $Revision: 97 $ + * $Revision: 100 $ * *****************************************************************************/ @@ -159,6 +159,8 @@ if (!(flags & ACPI_NO_ACPI_ENABLE)) { ACPI_DEBUG_PRINT ((ACPI_DB_EXEC, "[Init] Going into ACPI mode\n")); + acpi_gbl_original_mode = acpi_hw_get_mode(); + status = acpi_enable (); if (ACPI_FAILURE (status)) { ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Acpi_enable failed.\n")); @@ -291,7 +293,7 @@ acpi_ut_mutex_terminate (); -#ifdef ENABLE_DEBUGGER +#ifdef ACPI_DEBUGGER /* Shut down the debugger */ diff -Nru a/drivers/base/base.h b/drivers/base/base.h --- a/drivers/base/base.h Fri Aug 16 14:34:56 2002 +++ b/drivers/base/base.h Fri Aug 16 14:34:56 2002 @@ -6,8 +6,10 @@ # define DBG(x...) #endif -extern struct device device_root; +extern struct list_head global_device_list; extern spinlock_t device_lock; + +extern struct device * get_device_locked(struct device *); extern int bus_add_device(struct device * dev); extern void bus_remove_device(struct device * dev); diff -Nru a/drivers/base/bus.c b/drivers/base/bus.c --- a/drivers/base/bus.c Fri Aug 16 14:34:55 2002 +++ b/drivers/base/bus.c Fri Aug 16 14:34:55 2002 @@ -16,6 +16,9 @@ static LIST_HEAD(bus_driver_list); +#define to_dev(node) container_of(node,struct device,bus_list) +#define to_drv(node) container_of(node,struct device_driver,bus_list) + /** * bus_for_each_dev - walk list of devices and do something to each * @bus: bus in question @@ -26,42 +29,41 @@ * counting on devices as we touch each one. * * Algorithm: - * Take the bus lock and get the first node in the list. We increment - * the reference count and unlock the bus. If we have a device from a - * previous iteration, we decrement the reference count. - * After we call the callback, we get the next node in the list and loop. - * At the end, if @dev is not null, we still have it pinned, so we need - * to let it go. + * Take device_lock and get the first node in the list. + * Try and increment the reference count on it. If we can't, it's in the + * process of being removed, but that process hasn't acquired device_lock. + * It's still in the list, so we grab the next node and try that one. + * We drop the lock to call the callback. + * We can't decrement the reference count yet, because we need the next + * node in the list. So, we set @prev to point to the current device. + * On the next go-round, we decrement the reference count on @prev, so if + * it's being removed, it won't affect us. */ int bus_for_each_dev(struct bus_type * bus, void * data, int (*callback)(struct device * dev, void * data)) { - struct device * next; - struct device * dev = NULL; struct list_head * node; + struct device * prev = NULL; int error = 0; get_bus(bus); - read_lock(&bus->lock); - node = bus->devices.next; - while (node != &bus->devices) { - next = list_entry(node,struct device,bus_list); - get_device(next); - read_unlock(&bus->lock); - - if (dev) - put_device(dev); - dev = next; - if ((error = callback(dev,data))) { - put_device(dev); - break; + spin_lock(&device_lock); + list_for_each(node,&bus->devices) { + struct device * dev = get_device_locked(to_dev(node)); + if (dev) { + spin_unlock(&device_lock); + error = callback(dev,data); + if (prev) + put_device(prev); + prev = dev; + spin_lock(&device_lock); + if (error) + break; } - read_lock(&bus->lock); - node = dev->bus_list.next; } - read_unlock(&bus->lock); - if (dev) - put_device(dev); + spin_unlock(&device_lock); + if (prev) + put_device(prev); put_bus(bus); return error; } @@ -69,34 +71,30 @@ int bus_for_each_drv(struct bus_type * bus, void * data, int (*callback)(struct device_driver * drv, void * data)) { - struct device_driver * next; - struct device_driver * drv = NULL; struct list_head * node; + struct device_driver * prev = NULL; int error = 0; /* pin bus in memory */ get_bus(bus); - read_lock(&bus->lock); - node = bus->drivers.next; - while (node != &bus->drivers) { - next = list_entry(node,struct device_driver,bus_list); - get_driver(next); - read_unlock(&bus->lock); - - if (drv) - put_driver(drv); - drv = next; - if ((error = callback(drv,data))) { - put_driver(drv); - break; + spin_lock(&device_lock); + list_for_each(node,&bus->drivers) { + struct device_driver * drv = get_driver(to_drv(node)); + if (drv) { + spin_unlock(&device_lock); + error = callback(drv,data); + if (prev) + put_driver(prev); + prev = drv; + spin_lock(&device_lock); + if (error) + break; } - read_lock(&bus->lock); - node = drv->bus_list.next; } - read_unlock(&bus->lock); - if (drv) - put_driver(drv); + spin_unlock(&device_lock); + if (prev) + put_driver(prev); put_bus(bus); return error; } @@ -115,9 +113,9 @@ if (dev->bus) { pr_debug("registering %s with bus '%s'\n",dev->bus_id,dev->bus->name); get_bus(dev->bus); - write_lock(&dev->bus->lock); + spin_lock(&device_lock); list_add_tail(&dev->bus_list,&dev->bus->devices); - write_unlock(&dev->bus->lock); + spin_unlock(&device_lock); device_bus_link(dev); } return 0; @@ -134,9 +132,6 @@ { if (dev->bus) { device_remove_symlink(&dev->bus->device_dir,dev->bus_id); - write_lock(&dev->bus->lock); - list_del_init(&dev->bus_list); - write_unlock(&dev->bus->lock); put_bus(dev->bus); } } diff -Nru a/drivers/base/core.c b/drivers/base/core.c --- a/drivers/base/core.c Fri Aug 16 14:34:52 2002 +++ b/drivers/base/core.c Fri Aug 16 14:34:52 2002 @@ -12,19 +12,17 @@ #include #include #include -#include #include "base.h" -struct device device_root = { - bus_id: "root", - name: "System root", -}; +LIST_HEAD(global_device_list); int (*platform_notify)(struct device * dev) = NULL; int (*platform_notify_remove)(struct device * dev) = NULL; spinlock_t device_lock = SPIN_LOCK_UNLOCKED; +#define to_dev(node) container_of(node,struct device,driver_list) + /** * found_match - do actual binding of device to driver @@ -53,9 +51,9 @@ pr_debug("bound device '%s' to driver '%s'\n", dev->bus_id,drv->name); - write_lock(&drv->lock); + spin_lock(&device_lock); list_add_tail(&dev->driver_list,&drv->devices); - write_unlock(&drv->lock); + spin_unlock(&device_lock); goto Done; @@ -101,19 +99,14 @@ struct device_driver * drv; if (dev->driver) { - lock_device(dev); + spin_lock(&device_lock); drv = dev->driver; dev->driver = NULL; - unlock_device(dev); - - write_lock(&drv->lock); - list_del_init(&dev->driver_list); - write_unlock(&drv->lock); + spin_unlock(&device_lock); /* detach from driver */ - if (drv->remove) + if (drv && drv->remove) drv->remove(dev); - put_driver(drv); } } @@ -134,47 +127,26 @@ return bus_for_each_dev(drv->bus,drv,do_driver_attach); } -static int do_driver_detach(struct device * dev, struct device_driver * drv) -{ - lock_device(dev); - if (dev->driver == drv) { - dev->driver = NULL; - unlock_device(dev); - if (drv->remove) - drv->remove(dev); - } else - unlock_device(dev); - return 0; -} - void driver_detach(struct device_driver * drv) { - struct device * next; - struct device * dev = NULL; struct list_head * node; - int error = 0; + struct device * prev = NULL; - write_lock(&drv->lock); - node = drv->devices.next; - while (node != &drv->devices) { - next = list_entry(node,struct device,driver_list); - get_device(next); - list_del_init(&next->driver_list); - write_unlock(&drv->lock); - - if (dev) - put_device(dev); - dev = next; - if ((error = do_driver_detach(dev,drv))) { - put_device(dev); - break; + spin_lock(&device_lock); + list_for_each(node,&drv->devices) { + struct device * dev = get_device_locked(to_dev(node)); + if (dev) { + if (prev) + list_del_init(&prev->driver_list); + spin_unlock(&device_lock); + device_detach(dev); + if (prev) + put_device(prev); + prev = dev; + spin_lock(&device_lock); } - write_lock(&drv->lock); - node = drv->devices.next; } - write_unlock(&drv->lock); - if (dev) - put_device(dev); + spin_unlock(&device_lock); } /** @@ -191,7 +163,6 @@ int device_register(struct device *dev) { int error; - struct device *prev_dev; if (!dev || !strlen(dev->bus_id)) return -EINVAL; @@ -199,23 +170,18 @@ INIT_LIST_HEAD(&dev->node); INIT_LIST_HEAD(&dev->children); INIT_LIST_HEAD(&dev->g_list); + INIT_LIST_HEAD(&dev->driver_list); + INIT_LIST_HEAD(&dev->bus_list); spin_lock_init(&dev->lock); atomic_set(&dev->refcount,2); - + spin_lock(&device_lock); - if (dev != &device_root) { - if (!dev->parent) - dev->parent = &device_root; - get_device(dev->parent); - - if (list_empty(&dev->parent->children)) - prev_dev = dev->parent; - else - prev_dev = list_entry(dev->parent->children.prev, struct device, node); - list_add(&dev->g_list, &prev_dev->g_list); - + if (dev->parent) { + get_device_locked(dev->parent); + list_add_tail(&dev->g_list,&dev->parent->g_list); list_add_tail(&dev->node,&dev->parent->children); - } + } else + list_add_tail(&dev->g_list,&global_device_list); spin_unlock(&device_lock); pr_debug("DEV: registering device: ID = '%s', name = %s\n", @@ -234,12 +200,37 @@ platform_notify(dev); register_done: + if (error) { + spin_lock(&device_lock); + list_del_init(&dev->g_list); + list_del_init(&dev->node); + spin_unlock(&device_lock); + if (dev->parent) + put_device(dev->parent); + } put_device(dev); - if (error && dev->parent) - put_device(dev->parent); return error; } +struct device * get_device_locked(struct device * dev) +{ + struct device * ret = dev; + if (dev && atomic_read(&dev->refcount) > 0) + atomic_inc(&dev->refcount); + else + ret = NULL; + return ret; +} + +struct device * get_device(struct device * dev) +{ + struct device * ret; + spin_lock(&device_lock); + ret = get_device_locked(dev); + spin_unlock(&device_lock); + return ret; +} + /** * put_device - decrement reference count, and clean up when it hits 0 * @dev: device in question @@ -250,6 +241,8 @@ return; list_del_init(&dev->node); list_del_init(&dev->g_list); + list_del_init(&dev->bus_list); + list_del_init(&dev->driver_list); spin_unlock(&device_lock); pr_debug("DEV: Unregistering device. ID = '%s', name = '%s'\n", @@ -270,12 +263,8 @@ if (dev->release) dev->release(dev); - put_device(dev->parent); -} - -static int __init device_init_root(void) -{ - return device_register(&device_root); + if (dev->parent) + put_device(dev->parent); } static int __init device_init(void) @@ -283,17 +272,13 @@ int error; error = init_driverfs_fs(); - if (error) { - panic("DEV: could not initialize driverfs"); - return error; - } - error = device_init_root(); if (error) - printk(KERN_ERR "%s: device root init failed!\n", __FUNCTION__); - return error; + panic("DEV: could not initialize driverfs"); + return 0; } core_initcall(device_init); EXPORT_SYMBOL(device_register); +EXPORT_SYMBOL(get_device); EXPORT_SYMBOL(put_device); diff -Nru a/drivers/base/driver.c b/drivers/base/driver.c --- a/drivers/base/driver.c Fri Aug 16 14:35:00 2002 +++ b/drivers/base/driver.c Fri Aug 16 14:35:00 2002 @@ -10,35 +10,31 @@ #include #include "base.h" +#define to_dev(node) container_of(node,struct device,driver_list) -int driver_for_each_dev(struct device_driver * drv, void * data, int (*callback)(struct device *, void * )) +int driver_for_each_dev(struct device_driver * drv, void * data, + int (*callback)(struct device *, void * )) { - struct device * next; - struct device * dev = NULL; struct list_head * node; + struct device * prev = NULL; int error = 0; get_driver(drv); - read_lock(&drv->lock); - node = drv->devices.next; - while (node != &drv->devices) { - next = list_entry(node,struct device,driver_list); - get_device(next); - read_unlock(&drv->lock); - - if (dev) - put_device(dev); - dev = next; - if ((error = callback(dev,data))) { - put_device(dev); - break; + spin_lock(&device_lock); + list_for_each(node,&drv->devices) { + struct device * dev = get_device_locked(to_dev(node)); + if (dev) { + spin_unlock(&device_lock); + error = callback(dev,data); + if (prev) + put_device(prev); + prev = dev; + spin_lock(&device_lock); + if (error) + break; } - read_lock(&drv->lock); - node = dev->driver_list.next; } - read_unlock(&drv->lock); - if (dev) - put_device(dev); + spin_unlock(&device_lock); put_driver(drv); return error; } @@ -60,9 +56,9 @@ atomic_set(&drv->refcount,2); rwlock_init(&drv->lock); INIT_LIST_HEAD(&drv->devices); - write_lock(&drv->bus->lock); + spin_lock(&device_lock); list_add(&drv->bus_list,&drv->bus->drivers); - write_unlock(&drv->bus->lock); + spin_unlock(&device_lock); driver_make_dir(drv); driver_attach(drv); put_driver(drv); @@ -81,10 +77,10 @@ void remove_driver(struct device_driver * drv) { - write_lock(&drv->bus->lock); + spin_lock(&device_lock); atomic_set(&drv->refcount,0); list_del_init(&drv->bus_list); - write_unlock(&drv->bus->lock); + spin_unlock(&device_lock); __remove_driver(drv); } @@ -94,13 +90,10 @@ */ void put_driver(struct device_driver * drv) { - write_lock(&drv->bus->lock); - if (!atomic_dec_and_test(&drv->refcount)) { - write_unlock(&drv->bus->lock); + if (!atomic_dec_and_lock(&drv->refcount,&device_lock)) return; - } list_del_init(&drv->bus_list); - write_unlock(&drv->bus->lock); + spin_unlock(&device_lock); __remove_driver(drv); } diff -Nru a/drivers/base/fs/bus.c b/drivers/base/fs/bus.c --- a/drivers/base/fs/bus.c Fri Aug 16 14:34:53 2002 +++ b/drivers/base/fs/bus.c Fri Aug 16 14:34:53 2002 @@ -56,10 +56,10 @@ } static struct driverfs_ops bus_attr_ops = { - open: bus_attr_open, - close: bus_attr_close, - show: bus_attr_show, - store: bus_attr_store, + .open = bus_attr_open, + .close = bus_attr_close, + .show = bus_attr_show, + .store = bus_attr_store, }; int bus_create_file(struct bus_type * bus, struct bus_attribute * attr) @@ -107,8 +107,8 @@ } static struct driver_dir_entry bus_dir = { - name: "bus", - mode: (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO), + .name = "bus", + .mode = (S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO), }; static int __init bus_init(void) diff -Nru a/drivers/base/fs/device.c b/drivers/base/fs/device.c --- a/drivers/base/fs/device.c Fri Aug 16 14:34:58 2002 +++ b/drivers/base/fs/device.c Fri Aug 16 14:34:58 2002 @@ -10,11 +10,17 @@ #include #include #include +#include #include #include #include #include +static struct driver_dir_entry device_root_dir = { + .name = "root", + .mode = (S_IRWXU | S_IRUGO | S_IXUGO), +}; + extern struct device_attribute * device_default_files[]; #define to_dev_attr(_attr) container_of(_attr,struct device_attribute,attr) @@ -67,10 +73,10 @@ } static struct driverfs_ops dev_attr_ops = { - open: dev_attr_open, - close: dev_attr_close, - show: dev_attr_show, - store: dev_attr_store, + .open = dev_attr_open, + .close = dev_attr_close, + .show = dev_attr_show, + .store = dev_attr_store, }; /** @@ -164,7 +170,7 @@ * one to get to the 'bus' directory, and one to get to the root * of the fs.) */ - length += strlen("../../.."); + length += strlen("../../../root"); if (length > PATH_MAX) return -ENAMETOOLONG; @@ -174,7 +180,7 @@ memset(path,0,length); /* our relative position */ - strcpy(path,"../../.."); + strcpy(path,"../../../root"); fill_devpath(dev,path,length); error = driverfs_create_symlink(&dev->bus->device_dir,dev->bus_id,path); @@ -207,13 +213,12 @@ */ int device_make_dir(struct device * dev) { - struct driver_dir_entry * parent = NULL; + struct driver_dir_entry * parent; struct device_attribute * entry; int error; int i; - if (dev->parent) - parent = &dev->parent->dir; + parent = dev->parent ? &dev->parent->dir : &device_root_dir; dev->dir.name = dev->bus_id; dev->dir.ops = &dev_attr_ops; @@ -229,6 +234,12 @@ return error; } +static int device_driverfs_init(void) +{ + return driverfs_create_dir(&device_root_dir,NULL); +} + +core_initcall(device_driverfs_init); + EXPORT_SYMBOL(device_create_file); EXPORT_SYMBOL(device_remove_file); - diff -Nru a/drivers/base/fs/driver.c b/drivers/base/fs/driver.c --- a/drivers/base/fs/driver.c Fri Aug 16 14:34:52 2002 +++ b/drivers/base/fs/driver.c Fri Aug 16 14:34:52 2002 @@ -54,10 +54,10 @@ } static struct driverfs_ops drv_attr_ops = { - open: drv_attr_open, - close: drv_attr_close, - show: drv_attr_show, - store: drv_attr_store, + .open = drv_attr_open, + .close = drv_attr_close, + .show = drv_attr_show, + .store = drv_attr_store, }; int driver_create_file(struct device_driver * drv, struct driver_attribute * attr) diff -Nru a/drivers/base/interface.c b/drivers/base/interface.c --- a/drivers/base/interface.c Fri Aug 16 14:34:53 2002 +++ b/drivers/base/interface.c Fri Aug 16 14:34:53 2002 @@ -14,7 +14,7 @@ return off ? 0 : sprintf(buf,"%s\n",dev->name); } -static DEVICE_ATTR(name,"name",S_IRUGO,device_read_name,NULL); +static DEVICE_ATTR(name,S_IRUGO,device_read_name,NULL); static ssize_t device_read_power(struct device * dev, char * page, size_t count, loff_t off) @@ -85,7 +85,7 @@ return error < 0 ? error : count; } -static DEVICE_ATTR(power,"power",S_IWUSR | S_IRUGO, +static DEVICE_ATTR(power,S_IWUSR | S_IRUGO, device_read_power,device_write_power); struct device_attribute * device_default_files[] = { diff -Nru a/drivers/base/power.c b/drivers/base/power.c --- a/drivers/base/power.c Fri Aug 16 14:34:53 2002 +++ b/drivers/base/power.c Fri Aug 16 14:34:53 2002 @@ -14,6 +14,8 @@ #include #include "base.h" +#define to_dev(node) container_of(node,struct device,g_list) + /** * device_suspend - suspend all devices on the device tree * @state: state we're entering @@ -25,30 +27,26 @@ */ int device_suspend(u32 state, u32 level) { - struct device * dev; - struct device * prev = &device_root; + struct list_head * node; + struct device * prev = NULL; int error = 0; printk(KERN_EMERG "Suspending Devices\n"); - get_device(prev); - spin_lock(&device_lock); - dev = g_list_to_dev(prev->g_list.next); - while(dev != &device_root && !error) { - get_device(dev); - spin_unlock(&device_lock); - put_device(prev); - - if (dev->driver && dev->driver->suspend) - error = dev->driver->suspend(dev,state,level); - - spin_lock(&device_lock); - prev = dev; - dev = g_list_to_dev(prev->g_list.next); + list_for_each(node,&global_device_list) { + struct device * dev = get_device_locked(to_dev(node)); + if (dev) { + spin_unlock(&device_lock); + if (dev->driver && dev->driver->suspend) + error = dev->driver->suspend(dev,state,level); + if (prev) + put_device(prev); + prev = dev; + spin_lock(&device_lock); + } } spin_unlock(&device_lock); - put_device(prev); return error; } @@ -63,27 +61,23 @@ */ void device_resume(u32 level) { - struct device * dev; - struct device * prev = &device_root; - - get_device(prev); + struct list_head * node; + struct device * prev = NULL; spin_lock(&device_lock); - dev = g_list_to_dev(prev->g_list.prev); - while(dev != &device_root) { - get_device(dev); - spin_unlock(&device_lock); - put_device(prev); - - if (dev->driver && dev->driver->resume) - dev->driver->resume(dev,level); - - spin_lock(&device_lock); - prev = dev; - dev = g_list_to_dev(prev->g_list.prev); + list_for_each_prev(node,&global_device_list) { + struct device * dev = get_device_locked(to_dev(node)); + if (dev) { + spin_unlock(&device_lock); + if (dev->driver && dev->driver->resume) + dev->driver->resume(dev,level); + if (prev) + put_device(prev); + prev = dev; + spin_lock(&device_lock); + } } spin_unlock(&device_lock); - put_device(prev); printk(KERN_EMERG "Devices Resumed\n"); } @@ -98,29 +92,25 @@ */ void device_shutdown(void) { - struct device * dev; - struct device * prev = &device_root; + struct list_head * node; + struct device * prev = NULL; printk(KERN_EMERG "Shutting down devices\n"); - get_device(prev); - spin_lock(&device_lock); - dev = g_list_to_dev(prev->g_list.next); - while(dev != &device_root) { - get_device(dev); - spin_unlock(&device_lock); - put_device(prev); - - if (dev->driver && dev->driver->remove) - dev->driver->remove(dev); - - spin_lock(&device_lock); - prev = dev; - dev = g_list_to_dev(prev->g_list.next); + list_for_each(node,&global_device_list) { + struct device * dev = get_device_locked(to_dev(node)); + if (dev) { + spin_unlock(&device_lock); + if (dev->driver && dev->driver->remove) + dev->driver->remove(dev); + if (prev) + put_device(prev); + prev = dev; + spin_lock(&device_lock); + } } spin_unlock(&device_lock); - put_device(prev); } EXPORT_SYMBOL(device_suspend); diff -Nru a/drivers/base/sys.c b/drivers/base/sys.c --- a/drivers/base/sys.c Fri Aug 16 14:34:50 2002 +++ b/drivers/base/sys.c Fri Aug 16 14:34:50 2002 @@ -17,8 +17,8 @@ #include static struct device system_bus = { - name: "System Bus", - bus_id: "sys", + .name = "System Bus", + .bus_id = "sys", }; int register_sys_device(struct device * dev) diff -Nru a/drivers/block/DAC960.c b/drivers/block/DAC960.c --- a/drivers/block/DAC960.c Fri Aug 16 14:34:59 2002 +++ b/drivers/block/DAC960.c Fri Aug 16 14:34:59 2002 @@ -75,17 +75,19 @@ *DAC960_Controllers[DAC960_MaxControllers] = { NULL }; +static int DAC960_revalidate(kdev_t); /* DAC960_BlockDeviceOperations is the Block Device Operations structure for DAC960 Logical Disk Devices. */ -static BlockDeviceOperations_T - DAC960_BlockDeviceOperations = - { owner: THIS_MODULE, - open: DAC960_Open, - release: DAC960_Release, - ioctl: DAC960_IOCTL }; +static struct block_device_operations DAC960_BlockDeviceOperations = { + owner: THIS_MODULE, + open: DAC960_Open, + release: DAC960_Release, + ioctl: DAC960_IOCTL, + revalidate: DAC960_revalidate, +}; /* @@ -306,9 +308,9 @@ static void DAC960_WaitForCommand(DAC960_Controller_T *Controller) { - spin_unlock_irq(&Controller->RequestQueue->queue_lock); + spin_unlock_irq(Controller->RequestQueue->queue_lock); __wait_event(Controller->CommandWaitQueue, Controller->FreeCommands); - spin_lock_irq(&Controller->RequestQueue->queue_lock); + spin_lock_irq(Controller->RequestQueue->queue_lock); } @@ -1920,7 +1922,6 @@ return true; } - /* DAC960_RegisterBlockDevice registers the Block Device structures associated with Controller. @@ -1929,8 +1930,16 @@ static boolean DAC960_RegisterBlockDevice(DAC960_Controller_T *Controller) { int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber; + char *names; RequestQueue_T *RequestQueue; int MinorNumber; + int n; + + names = kmalloc(9 * DAC960_MaxLogicalDrives, GFP_KERNEL); + if (!names) { + DAC960_Error("out of memory", Controller); + return false; + } /* Register the Block Device Major Number for this DAC960 Controller. */ @@ -1939,13 +1948,15 @@ { DAC960_Error("UNABLE TO ACQUIRE MAJOR NUMBER %d - DETACHING\n", Controller, MajorNumber); + kfree(names); return false; } /* Initialize the I/O Request Queue. */ RequestQueue = BLK_DEFAULT_QUEUE(MajorNumber); - blk_init_queue(RequestQueue, DAC960_RequestFunction); + Controller->queue_lock = SPIN_LOCK_UNLOCKED; + blk_init_queue(RequestQueue, DAC960_RequestFunction, &Controller->queue_lock); RequestQueue->queuedata = Controller; blk_queue_max_hw_segments(RequestQueue, Controller->DriverScatterGatherLimit); @@ -1953,27 +1964,19 @@ blk_queue_max_sectors(RequestQueue, Controller->MaxBlocksPerCommand); Controller->RequestQueue = RequestQueue; - /* - Initialize the Disk Partitions array, Partition Sizes array, Block Sizes - array, and Max Sectors per Request array. - */ - for (MinorNumber = 0; MinorNumber < DAC960_MinorCount; MinorNumber++) - Controller->MaxSectorsPerRequest[MinorNumber] = - Controller->MaxBlocksPerCommand; - Controller->GenericDiskInfo.part = Controller->DiskPartitions; - /* - Complete initialization of the Generic Disk Information structure. - */ - Controller->GenericDiskInfo.major = MajorNumber; - Controller->GenericDiskInfo.major_name = "rd"; - Controller->GenericDiskInfo.minor_shift = DAC960_MaxPartitionsBits; - Controller->GenericDiskInfo.nr_real = DAC960_MaxLogicalDrives; - Controller->GenericDiskInfo.next = NULL; - Controller->GenericDiskInfo.fops = &DAC960_BlockDeviceOperations; - /* - Install the Generic Disk Information structure at the end of the list. - */ - add_gendisk(&Controller->GenericDiskInfo); + for (n = 0; n < DAC960_MaxLogicalDrives; n++) { + struct gendisk *disk = &Controller->disks[n]; + memset(disk, 0, sizeof(struct gendisk)); + sprintf(names + 9 * n, "rd/c%dd%d", Controller->ControllerNumber, n); + disk->part = Controller->DiskPartitions + (n<major = MajorNumber; + disk->first_minor = n << DAC960_MaxPartitionsBits; + disk->major_name = names + 9 * n; + disk->minor_shift = DAC960_MaxPartitionsBits; + disk->nr_real = 1; + disk->fops = &DAC960_BlockDeviceOperations; + add_gendisk(disk); + } /* Indicate the Block Device Registration completed successfully, */ @@ -1989,6 +1992,11 @@ static void DAC960_UnregisterBlockDevice(DAC960_Controller_T *Controller) { int MajorNumber = DAC960_MAJOR + Controller->ControllerNumber; + int disk; + for (disk = 0; disk < DAC960_MaxLogicalDrives; disk++) { + del_gendisk(&Controller->disks[disk]); + kfree(Controller->disks[0].major_name); + } /* Unregister the Block Device Major Number for this DAC960 Controller. */ @@ -2001,14 +2009,23 @@ Remove the Disk Partitions array, Partition Sizes array, Block Sizes array, Max Sectors per Request array, and Max Segments per Request array. */ - Controller->GenericDiskInfo.part = NULL; blk_clear(MajorNumber); - /* - Remove the Generic Disk Information structure from the list. - */ - del_gendisk(&Controller->GenericDiskInfo); } +static long disk_size(DAC960_Controller_T *Controller, int disk) +{ + if (Controller->FirmwareType == DAC960_V1_Controller) { + if (disk >= Controller->LogicalDriveCount) + return 0; + return Controller->V1.LogicalDriveInformation[disk].LogicalDriveSize; + } else { + DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo = + Controller->V2.LogicalDeviceInformation[disk]; + if (LogicalDeviceInfo == NULL) + return 0; + return LogicalDeviceInfo->ConfigurableDeviceSize; + } +} /* DAC960_ComputeGenericDiskInfo computes the values for the Generic Disk @@ -2017,59 +2034,21 @@ static void DAC960_ComputeGenericDiskInfo(DAC960_Controller_T *Controller) { - GenericDiskInfo_T *GenericDiskInfo = &Controller->GenericDiskInfo; - int LogicalDriveNumber, i; - for (LogicalDriveNumber = 0; - LogicalDriveNumber < DAC960_MaxLogicalDrives; - LogicalDriveNumber++) - { - int MinorNumber = DAC960_MinorNumber(LogicalDriveNumber, 0); - if (Controller->FirmwareType == DAC960_V1_Controller) - { - if (LogicalDriveNumber < Controller->LogicalDriveCount) - GenericDiskInfo->part[MinorNumber].nr_sects = - Controller->V1.LogicalDriveInformation - [LogicalDriveNumber].LogicalDriveSize; - else GenericDiskInfo->part[MinorNumber].nr_sects = 0; - } - else - { - DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo = - Controller->V2.LogicalDeviceInformation[LogicalDriveNumber]; - if (LogicalDeviceInfo != NULL) - GenericDiskInfo->part[MinorNumber].nr_sects = - LogicalDeviceInfo->ConfigurableDeviceSize; - else GenericDiskInfo->part[MinorNumber].nr_sects = 0; - } - } + struct gendisk *disks = Controller->disks; + int disk; + for (disk = 0; disk < DAC960_MaxLogicalDrives; disk++) + disks->part[0].nr_sects = disk_size(Controller, disk); } - -/* - DAC960_RegisterDisk registers the DAC960 Logical Disk Device for Logical - Drive Number if it exists. -*/ - -static void DAC960_RegisterDisk(DAC960_Controller_T *Controller, - int LogicalDriveNumber) -{ - long size; - if (Controller->FirmwareType == DAC960_V1_Controller) { - if (LogicalDriveNumber > Controller->LogicalDriveCount - 1) return; - size = Controller->V1.LogicalDriveInformation - [LogicalDriveNumber].LogicalDriveSize; - } else { - DAC960_V2_LogicalDeviceInfo_T *LogicalDeviceInfo = - Controller->V2.LogicalDeviceInformation[LogicalDriveNumber]; - if (LogicalDeviceInfo == NULL) return; - size = LogicalDeviceInfo->ConfigurableDeviceSize; - } - register_disk(&Controller->GenericDiskInfo, - DAC960_KernelDevice(Controller->ControllerNumber, LogicalDriveNumber, 0), - DAC960_MaxPartitions, &DAC960_BlockDeviceOperations, size); +static int DAC960_revalidate(kdev_t dev) +{ + int ctlr = DAC960_ControllerNumber(dev); + int disk = DAC960_LogicalDriveNumber(dev); + DAC960_Controller_T *p = DAC960_Controllers[ctlr]; + p->disks[disk].part[0].nr_sects = disk_size(p, disk); + return 0; } - /* DAC960_ReportErrorStatus reports Controller BIOS Messages passed through the Error Status Register when the driver performs the BIOS handshaking. @@ -2642,14 +2621,15 @@ ControllerNumber++) { DAC960_Controller_T *Controller = DAC960_Controllers[ControllerNumber]; - int LogicalDriveNumber; + int disk; if (Controller == NULL) continue; DAC960_InitializeController(Controller); - DAC960_ComputeGenericDiskInfo(Controller); - for (LogicalDriveNumber = 0; - LogicalDriveNumber < DAC960_MaxLogicalDrives; - LogicalDriveNumber++) - DAC960_RegisterDisk(Controller, LogicalDriveNumber); + for (disk = 0; disk < DAC960_MaxLogicalDrives; disk++) { + long size = disk_size(Controller, disk); + register_disk(&Controller->disks[disk], + DAC960_KernelDevice(Controller->ControllerNumber, disk, 0), + DAC960_MaxPartitions, &DAC960_BlockDeviceOperations, size); + } } DAC960_CreateProcEntries(); register_reboot_notifier(&DAC960_NotifierBlock); @@ -2732,17 +2712,17 @@ if (bio_data(BufferHeader) == LastDataEndPointer) { ScatterGatherList[SegmentNumber-1].SegmentByteCount += - bio_size(BufferHeader); - LastDataEndPointer += bio_size(BufferHeader); + BufferHeader->bi_size; + LastDataEndPointer += BufferHeader->bi_size; } else { ScatterGatherList[SegmentNumber].SegmentDataPointer = Virtual_to_Bus32(bio_data(BufferHeader)); ScatterGatherList[SegmentNumber].SegmentByteCount = - bio_size(BufferHeader); + BufferHeader->bi_size; LastDataEndPointer = bio_data(BufferHeader) + - bio_size(BufferHeader); + BufferHeader->bi_size; if (SegmentNumber++ > Controller->DriverScatterGatherLimit) panic("DAC960: Scatter/Gather Segment Overflow\n"); } @@ -2823,17 +2803,17 @@ if (bio_data(BufferHeader) == LastDataEndPointer) { ScatterGatherList[SegmentNumber-1].SegmentByteCount += - bio_size(BufferHeader); - LastDataEndPointer += bio_size(BufferHeader); + BufferHeader->bi_size; + LastDataEndPointer += BufferHeader->bi_size; } else { ScatterGatherList[SegmentNumber].SegmentDataPointer = Virtual_to_Bus64(bio_data(BufferHeader)); ScatterGatherList[SegmentNumber].SegmentByteCount = - bio_size(BufferHeader); + BufferHeader->bi_size; LastDataEndPointer = bio_data(BufferHeader) + - bio_size(BufferHeader); + BufferHeader->bi_size; if (SegmentNumber++ > Controller->DriverScatterGatherLimit) panic("DAC960: Scatter/Gather Segment Overflow\n"); } @@ -3055,7 +3035,7 @@ Command->CommandType = DAC960_WriteRetryCommand; CommandMailbox->Type5.CommandOpcode = DAC960_V1_Write; } - Command->BlockCount = bio_size(BufferHeader) >> DAC960_BlockSizeBits; + Command->BlockCount = BufferHeader->bi_size >> DAC960_BlockSizeBits; CommandMailbox->Type5.LD.TransferLength = Command->BlockCount; CommandMailbox->Type5.BusAddress = Virtual_to_Bus32(bio_data(BufferHeader)); @@ -3104,9 +3084,9 @@ DAC960_V1_CommandMailbox_T *CommandMailbox = &Command->V1.CommandMailbox; Command->BlockNumber += - bio_size(BufferHeader) >> DAC960_BlockSizeBits; + BufferHeader->bi_size >> DAC960_BlockSizeBits; Command->BlockCount = - bio_size(NextBufferHeader) >> DAC960_BlockSizeBits; + NextBufferHeader->bi_size >> DAC960_BlockSizeBits; Command->BufferHeader = NextBufferHeader; CommandMailbox->Type5.LD.TransferLength = Command->BlockCount; CommandMailbox->Type5.LogicalBlockAddress = Command->BlockNumber; @@ -4152,7 +4132,7 @@ if (CommandType == DAC960_ReadCommand) Command->CommandType = DAC960_ReadRetryCommand; else Command->CommandType = DAC960_WriteRetryCommand; - Command->BlockCount = bio_size(BufferHeader) >> DAC960_BlockSizeBits; + Command->BlockCount = BufferHeader->bi_size >> DAC960_BlockSizeBits; CommandMailbox->SCSI_10.CommandControlBits .AdditionalScatterGatherListMemory = false; CommandMailbox->SCSI_10.DataTransferSize = @@ -4208,9 +4188,9 @@ if (NextBufferHeader != NULL) { Command->BlockNumber += - bio_size(BufferHeader) >> DAC960_BlockSizeBits; + BufferHeader->bi_size >> DAC960_BlockSizeBits; Command->BlockCount = - bio_size(NextBufferHeader) >> DAC960_BlockSizeBits; + NextBufferHeader->bi_size >> DAC960_BlockSizeBits; Command->BufferHeader = NextBufferHeader; CommandMailbox->SCSI_10.DataTransferSize = Command->BlockCount << DAC960_BlockSizeBits; @@ -5289,11 +5269,15 @@ } if (!Controller->LogicalDriveInitiallyAccessible[LogicalDriveNumber]) { + long size; Controller->LogicalDriveInitiallyAccessible[LogicalDriveNumber] = true; - DAC960_ComputeGenericDiskInfo(Controller); - DAC960_RegisterDisk(Controller, LogicalDriveNumber); + size = disk_size(Controller, LogicalDriveNumber); + /* BROKEN, same as modular ide-floppy/ide-disk; same fix - ->probe() */ + register_disk(&Controller->disks[LogicalDriveNumber], + DAC960_KernelDevice(Controller->ControllerNumber, LogicalDriveNumber, 0), + DAC960_MaxPartitions, &DAC960_BlockDeviceOperations, size); } - if (Controller->GenericDiskInfo.part[minor(Inode->i_rdev)].nr_sects == 0) + if (Controller->disks[LogicalDriveNumber].part[0].nr_sects == 0) return -ENXIO; /* Increment Controller and Logical Drive Usage Counts. @@ -5391,18 +5375,6 @@ Geometry.start = get_start_sect(Inode->i_bdev); return (copy_to_user(UserGeometry, &Geometry, sizeof(DiskGeometry_T)) ? -EFAULT : 0); - - case BLKRRPART: - /* Re-Read Partition Table. */ - if (!capable(CAP_SYS_ADMIN)) return -EACCES; - if (Controller->LogicalDriveUsageCount[LogicalDriveNumber] > 1) - return -EBUSY; - res = wipe_partitions(Inode->i_rdev); - if (res) /* nothing */ - return res; - - DAC960_RegisterDisk(Controller, LogicalDriveNumber); - return 0; } return -EINVAL; } @@ -5528,11 +5500,11 @@ while (Controller->V1.DirectCommandActive[DCDB.Channel] [DCDB.TargetID]) { - spin_unlock_irq(&Controller->RequestQueue->queue_lock); + spin_unlock_irq(Controller->RequestQueue->queue_lock); __wait_event(Controller->CommandWaitQueue, !Controller->V1.DirectCommandActive [DCDB.Channel][DCDB.TargetID]); - spin_lock_irq(&Controller->RequestQueue->queue_lock); + spin_lock_irq(Controller->RequestQueue->queue_lock); } Controller->V1.DirectCommandActive[DCDB.Channel] [DCDB.TargetID] = true; @@ -5570,7 +5542,6 @@ DataTransferBuffer, DataTransferLength)) ErrorCode = -EFAULT; goto Failure1; - } } if (CommandOpcode == DAC960_V1_DCDB) { diff -Nru a/drivers/block/DAC960.h b/drivers/block/DAC960.h --- a/drivers/block/DAC960.h Fri Aug 16 14:34:59 2002 +++ b/drivers/block/DAC960.h Fri Aug 16 14:34:59 2002 @@ -2195,7 +2195,6 @@ typedef struct file File_T; typedef struct block_device_operations BlockDeviceOperations_T; typedef struct completion Completion_T; -typedef struct gendisk GenericDiskInfo_T; typedef struct hd_geometry DiskGeometry_T; typedef struct hd_struct DiskPartition_T; typedef struct inode Inode_T; @@ -2362,11 +2361,12 @@ boolean MonitoringAlertMode; boolean SuppressEnclosureMessages; Timer_T MonitoringTimer; - GenericDiskInfo_T GenericDiskInfo; + struct gendisk disks[DAC960_MaxLogicalDrives]; DAC960_Command_T *FreeCommands; unsigned char *CombinedStatusBuffer; unsigned char *CurrentStatusBuffer; RequestQueue_T *RequestQueue; + spinlock_t queue_lock; WaitQueue_T CommandWaitQueue; WaitQueue_T HealthStatusWaitQueue; DAC960_Command_T InitialCommand; @@ -2506,7 +2506,7 @@ void DAC960_AcquireControllerLock(DAC960_Controller_T *Controller, ProcessorFlags_T *ProcessorFlags) { - spin_lock_irqsave(&Controller->RequestQueue->queue_lock, *ProcessorFlags); + spin_lock_irqsave(Controller->RequestQueue->queue_lock, *ProcessorFlags); } @@ -2518,7 +2518,7 @@ void DAC960_ReleaseControllerLock(DAC960_Controller_T *Controller, ProcessorFlags_T *ProcessorFlags) { - spin_unlock_irqrestore(&Controller->RequestQueue->queue_lock, *ProcessorFlags); + spin_unlock_irqrestore(Controller->RequestQueue->queue_lock, *ProcessorFlags); } @@ -2555,7 +2555,7 @@ void DAC960_AcquireControllerLockIH(DAC960_Controller_T *Controller, ProcessorFlags_T *ProcessorFlags) { - spin_lock_irqsave(&Controller->RequestQueue->queue_lock, *ProcessorFlags); + spin_lock_irqsave(Controller->RequestQueue->queue_lock, *ProcessorFlags); } @@ -2568,7 +2568,7 @@ void DAC960_ReleaseControllerLockIH(DAC960_Controller_T *Controller, ProcessorFlags_T *ProcessorFlags) { - spin_unlock_irqrestore(&Controller->RequestQueue->queue_lock, *ProcessorFlags); + spin_unlock_irqrestore(Controller->RequestQueue->queue_lock, *ProcessorFlags); } #error I am a non-portable driver, please convert me to use the Documentation/DMA-mapping.txt interfaces @@ -4229,13 +4229,5 @@ DAC960_Controller_T *, ...); static void DAC960_CreateProcEntries(void); static void DAC960_DestroyProcEntries(void); - - -/* - Export the Kernel Mode IOCTL interface. -*/ - -EXPORT_SYMBOL(DAC960_KernelIOCTL); - #endif /* DAC960_DriverVersion */ diff -Nru a/drivers/block/acsi.c b/drivers/block/acsi.c --- a/drivers/block/acsi.c Fri Aug 16 14:34:57 2002 +++ b/drivers/block/acsi.c Fri Aug 16 14:34:57 2002 @@ -244,9 +244,10 @@ char *acsi_buffer; unsigned long phys_acsi_buffer; -static int NDevices = 0; -static struct hd_struct acsi_part[MAX_DEV<<4] = { {0,0}, }; -static int access_count[MAX_DEV] = { 0, }; +static int NDevices; +static struct hd_struct acsi_part[MAX_DEV<<4]; +static char acsi_names[MAX_DEV*4]; +static int access_count[MAX_DEV]; static int CurrentNReq; static int CurrentNSect; @@ -1345,14 +1346,8 @@ extern struct block_device_operations acsi_fops; -static struct gendisk acsi_gendisk = { - major: MAJOR_NR, - major_name: "ad", - minor_shift: 4, - part: acsi_part, - fops: &acsi_fops, -}; - +static struct gendisk acsi_gendisk[MAX_DEV]; + #define MAX_SCSI_DEVICE_CODE 10 static const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] = @@ -1700,12 +1695,22 @@ NDevices, n_slm ); #endif - for( i = 0; i < NDevices; ++i ) - register_disk(&acsi_gendisk, mk_kdev(MAJOR_NR,i<<4), - (acsi_info[i].type==HARDDISK)?1<<4:1, - &acsi_fops, + for( i = 0; i < NDevices; ++i ) { + struct gendisk *disk = acsi_gendisk + i; + sprintf(acsi_names + 4*i, "ad%c", 'a'+i); + disk->major = MAJOR_NR; + disk->first_minor = i << 4; + disk->major_name = acsi_names + 4*i; + disk->minor_shift = (acsi_info[i].type==HARDDISK)?4:0; + disk->part = acsi_part + (i<<4); + disk->fops = &acsi_fops; + disk->nr_real = 1; + add_gendisk(disk); + register_disk(disk, mk_kdev(disk->major, disk->first_minor), + 1<minor_shift, + disk->fops, acsi_info[i].size); - acsi_gendisk.nr_real = NDevices; + } } #ifdef CONFIG_ATARI_SLM_MODULE @@ -1744,8 +1749,6 @@ STramMask = ATARIHW_PRESENT(EXTD_DMA) ? 0x00000000 : 0xff000000; blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_acsi_request, &acsi_lock); - add_gendisk(&acsi_gendisk); - #ifdef CONFIG_ATARI_SLM err = slm_init(); #endif @@ -1771,6 +1774,7 @@ void cleanup_module(void) { + int i; del_timer( &acsi_timer ); blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); atari_stram_free( acsi_buffer ); @@ -1778,7 +1782,8 @@ if (unregister_blkdev( MAJOR_NR, "ad" ) != 0) printk( KERN_ERR "acsi: cleanup_module failed\n"); - del_gendisk(&acsi_gendisk); + for (i = 0; i < NDevices; i++) + del_gendisk(acsi_gendisk + i); } #endif diff -Nru a/drivers/block/cciss.c b/drivers/block/cciss.c --- a/drivers/block/cciss.c Fri Aug 16 14:35:01 2002 +++ b/drivers/block/cciss.c Fri Aug 16 14:35:01 2002 @@ -105,10 +105,9 @@ unsigned int cmd, unsigned long arg); static int revalidate_allvol(kdev_t dev); -static int revalidate_logvol(kdev_t dev, int maxusage); static int cciss_revalidate(kdev_t dev); static int deregister_disk(int ctlr, int logvol); -static int register_new_disk(kdev_t dev, int cltr); +static int register_new_disk(int cltr); static void cciss_getgeometry(int cntl_num); @@ -333,27 +332,6 @@ } } -/* - * fills in the disk information. - */ -static void cciss_geninit( int ctlr) -{ - drive_info_struct *drv; - int i; - - /* Loop through each real device */ - hba[ctlr]->gendisk.nr_real = 0; - for(i=0; i< NWD; i++) - { - drv = &(hba[ctlr]->drv[i]); - if( !(drv->nr_blocks)) - continue; - hba[ctlr]->hd[i << NWD_SHIFT].nr_sects = drv->nr_blocks; - //hba[ctlr]->gendisk.nr_real++; - (BLK_DEFAULT_QUEUE(MAJOR_NR + ctlr))->hardsect_size = drv->block_size; - } - hba[ctlr]->gendisk.nr_real = hba[ctlr]->highest_lun+1; -} /* * Open. Make sure the device is really there. */ @@ -599,7 +577,7 @@ case CCISS_REGNEWD: { - return(register_new_disk(inode->i_rdev, ctlr)); + return(register_new_disk(ctlr)); } case CCISS_PASSTHRU: { @@ -726,49 +704,11 @@ { int ctlr = major(dev) - MAJOR_NR; int target = minor(dev) >> NWD_SHIFT; - struct gendisk *gdev = &(hba[ctlr]->gendisk); - gdev->part[minor(dev)].nr_sects = hba[ctlr]->drv[target].nr_blocks; + struct gendisk *disk = &hba[ctlr]->gendisk[target]; + disk->part[0].nr_sects = hba[ctlr]->drv[target].nr_blocks; return 0; } -/* Borrowed and adapted from sd.c */ -/* - * FIXME: we are missing the exclusion with ->open() here - it can happen - * just as we are rereading partition tables. - */ -static int revalidate_logvol(kdev_t dev, int maxusage) -{ - int ctlr, target; - struct gendisk *gdev; - unsigned long flags; - int res; - - target = minor(dev) >> NWD_SHIFT; - ctlr = major(dev) - MAJOR_NR; - gdev = &(hba[ctlr]->gendisk); - - spin_lock_irqsave(CCISS_LOCK(ctlr), flags); - if (hba[ctlr]->drv[target].usage_count > maxusage) { - spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); - printk(KERN_WARNING "cciss: Device busy for " - "revalidation (usage=%d)\n", - hba[ctlr]->drv[target].usage_count); - return -EBUSY; - } - hba[ctlr]->drv[target].usage_count++; - spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); - - res = wipe_partitions(dev); - if (res) - goto leave; - - /* setup partitions per disk */ - grok_partitions(dev, hba[ctlr]->drv[target].nr_blocks); -leave: - hba[ctlr]->drv[target].usage_count--; - return res; -} - /* * revalidate_allvol is for online array config utilities. After a * utility reconfigures the drives in the array, it can use this function @@ -799,6 +739,15 @@ hba[ctlr]->usage_count++; spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); + for(i=0; i< NWD; i++) { + struct gendisk *disk = &hba[ctlr]->gendisk[i]; + if (disk->major_name) { + wipe_partitions(mk_kdev(disk->major, disk->first_minor)); + del_gendisk(disk); + disk->major_name = NULL; + } + } + /* * Set the partition and block size structures for all volumes * on this controller to zero. We will reread all of this data @@ -806,8 +755,6 @@ memset(hba[ctlr]->hd, 0, sizeof(struct hd_struct) * 256); memset(hba[ctlr]->drv, 0, sizeof(drive_info_struct) * CISS_MAX_LUN); - hba[ctlr]->gendisk.nr_real = 0; - /* * Tell the array controller not to give us any interrupts while * we check the new geometry. Then turn interrupts back on when @@ -817,13 +764,20 @@ cciss_getgeometry(ctlr); hba[ctlr]->access.set_intr_mask(hba[ctlr], CCISS_INTR_ON); - cciss_geninit(ctlr); - for(i=0; igendisk.part[ i<gendisk[i]; + drive_info_struct *drv = &(hba[ctlr]->drv[i]); + if (!drv->nr_blocks) + continue; + (BLK_DEFAULT_QUEUE(MAJOR_NR + ctlr))->hardsect_size = drv->block_size; + disk->major_name = hba[ctlr]->names + 12 * i; + add_gendisk(disk); + register_disk(disk, + mk_kdev(disk->major, disk->first_minor), + 1<minor_shift, disk->fops, + drv->nr_blocks); } - hba[ctlr]->usage_count--; return 0; } @@ -831,18 +785,15 @@ static int deregister_disk(int ctlr, int logvol) { unsigned long flags; - struct gendisk *gdev = &(hba[ctlr]->gendisk); + struct gendisk *disk = &hba[ctlr]->gendisk[logvol]; ctlr_info_t *h = hba[ctlr]; - int start, max_p; - if (!capable(CAP_SYS_RAWIO)) return -EPERM; spin_lock_irqsave(CCISS_LOCK(ctlr), flags); /* make sure logical volume is NOT is use */ - if( h->drv[logvol].usage_count > 1) - { + if( h->drv[logvol].usage_count > 1) { spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); return -EBUSY; } @@ -850,25 +801,24 @@ spin_unlock_irqrestore(CCISS_LOCK(ctlr), flags); /* invalidate the devices and deregister the disk */ - max_p = 1 << gdev->minor_shift; - start = logvol << gdev->minor_shift; - wipe_partitions(mk_kdev(MAJOR_NR+ctlr, start)); + if (disk->major_name) { + wipe_partitions(mk_kdev(disk->major, disk->first_minor)); + del_gendisk(disk); + disk->major_name = NULL; + } /* check to see if it was the last disk */ - if (logvol == h->highest_lun) - { + if (logvol == h->highest_lun) { /* if so, find the new hightest lun */ int i, newhighest =-1; - for(i=0; ihighest_lun; i++) - { + for(i=0; ihighest_lun; i++) { /* if the disk has size > 0, it is available */ - if (h->gendisk.part[i << gdev->minor_shift].nr_sects) + if (h->drv[i].nr_blocks) newhighest = i; } h->highest_lun = newhighest; } --h->num_luns; - gdev->nr_real = h->highest_lun+1; /* zero out the disk size info */ h->drv[logvol].nr_blocks = 0; h->drv[logvol].block_size = 0; @@ -1065,11 +1015,11 @@ return(return_status); } -static int register_new_disk(kdev_t dev, int ctlr) +static int register_new_disk(int ctlr) { - struct gendisk *gdev = &(hba[ctlr]->gendisk); + struct gendisk *disk; ctlr_info_t *h = hba[ctlr]; - int start, max_p, i; + int i; int num_luns; int logvol; int new_lun_found = 0; @@ -1084,7 +1034,6 @@ __u32 lunid = 0; unsigned int block_size; unsigned int total_size; - kdev_t kdev; if (!capable(CAP_SYS_RAWIO)) return -EPERM; @@ -1292,15 +1241,16 @@ hba[ctlr]->drv[logvol].sectors, hba[ctlr]->drv[logvol].cylinders); hba[ctlr]->drv[logvol].usage_count = 0; - max_p = 1 << gdev->minor_shift; - start = logvol<< gdev->minor_shift; - kdev = mk_kdev(MAJOR_NR + ctlr, logvol<< gdev->minor_shift); - - wipe_partitions(kdev); ++hba[ctlr]->num_luns; - gdev->nr_real = hba[ctlr]->highest_lun + 1; /* setup partitions per disk */ - grok_partitions(kdev, hba[ctlr]->drv[logvol].nr_blocks); + disk = &hba[ctlr]->gendisk[logvol]; + disk->major_name = hba[ctlr]->names + 12 * logvol; + add_gendisk(disk); + register_disk(disk, + mk_kdev(disk->major, disk->first_minor), + 1<minor_shift, + disk->fops, + hba[ctlr]->drv[logvol].nr_blocks); kfree(ld_buff); kfree(size_buff); @@ -2488,22 +2438,28 @@ blk_queue_max_sectors(q, 512); - /* Fill in the gendisk data */ - hba[i]->gendisk.major = MAJOR_NR + i; - hba[i]->gendisk.major_name = "cciss"; - hba[i]->gendisk.minor_shift = NWD_SHIFT; - hba[i]->gendisk.part = hba[i]->hd; - hba[i]->gendisk.nr_real = hba[i]->highest_lun+1; - - /* Get on the disk list */ - add_gendisk(&(hba[i]->gendisk)); - - cciss_geninit(i); - for(j=0; jgendisk), - mk_kdev(MAJOR_NR+i, j <<4), - MAX_PART, &cciss_fops, - hba[i]->drv[j].nr_blocks); + + for(j=0; jdrv[j]); + struct gendisk *disk = hba[i]->gendisk + j; + + sprintf(hba[i]->names + 12 * j, "cciss/c%dd%d", i, j); + disk->major = MAJOR_NR + i; + disk->first_minor = j << NWD_SHIFT; + disk->major_name = NULL; + disk->minor_shift = NWD_SHIFT; + disk->part = hba[i]->hd + (j << NWD_SHIFT); + disk->nr_real = 1; + if( !(drv->nr_blocks)) + continue; + (BLK_DEFAULT_QUEUE(MAJOR_NR + i))->hardsect_size = drv->block_size; + disk->major_name = hba[i]->names + 12 * j; + add_gendisk(disk); + register_disk(disk, + mk_kdev(disk->major, disk->first_minor), + 1<minor_shift, disk->fops, + drv->nr_blocks); + } cciss_register_scsi(i, 1); /* hook ourself into SCSI subsystem */ @@ -2513,7 +2469,7 @@ static void __devexit cciss_remove_one (struct pci_dev *pdev) { ctlr_info_t *tmp_ptr; - int i; + int i, j; char flush_buf[4]; int return_code; @@ -2548,7 +2504,13 @@ remove_proc_entry(hba[i]->devname, proc_cciss); /* remove it from the disk list */ - del_gendisk(&(hba[i]->gendisk)); + for (j = 0; j < NWD; j++) { + struct gendisk *disk = &hba[i]->gendisk[j]; + if (disk->major_name) { + del_gendisk(disk); + disk->major_name = NULL; + } + } pci_free_consistent(hba[i]->pdev, NR_CMDS * sizeof(CommandList_struct), hba[i]->cmd_pool, hba[i]->cmd_pool_dhandle); diff -Nru a/drivers/block/cciss.h b/drivers/block/cciss.h --- a/drivers/block/cciss.h Fri Aug 16 14:34:51 2002 +++ b/drivers/block/cciss.h Fri Aug 16 14:34:51 2002 @@ -81,7 +81,8 @@ int nr_frees; // Disk structures we need to pass back - struct gendisk gendisk; + struct gendisk gendisk[NWD]; + char names[12 * NWD]; // indexed by minor numbers struct hd_struct hd[256]; int sizes[256]; diff -Nru a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c --- a/drivers/block/cpqarray.c Fri Aug 16 14:34:53 2002 +++ b/drivers/block/cpqarray.c Fri Aug 16 14:34:53 2002 @@ -1483,6 +1483,7 @@ if (!drv->nr_blks) continue; (BLK_DEFAULT_QUEUE(MAJOR_NR + ctlr))->hardsect_size = drv->blk_size; + disk->major_name = ida_names + (ctlr*NWD+i)*10; add_gendisk(disk); register_disk(disk, mk_kdev(disk->major,disk->first_minor), diff -Nru a/drivers/block/loop.c b/drivers/block/loop.c --- a/drivers/block/loop.c Fri Aug 16 14:34:52 2002 +++ b/drivers/block/loop.c Fri Aug 16 14:34:52 2002 @@ -74,6 +74,7 @@ #include #include #include +#include #include /* for invalidate_bdev() */ #include @@ -235,6 +236,7 @@ up(&mapping->host->i_sem); out: kunmap(bvec->bv_page); + balance_dirty_pages(mapping); return ret; unlock: diff -Nru a/drivers/block/ps2esdi.c b/drivers/block/ps2esdi.c --- a/drivers/block/ps2esdi.c Fri Aug 16 14:34:51 2002 +++ b/drivers/block/ps2esdi.c Fri Aug 16 14:34:51 2002 @@ -157,7 +157,7 @@ nr_real: 1 },{ major: MAJOR_NR, - first_minor: 64 + first_minor: 64, major_name: "edb", minor_shift: 6, part: ps2esdi+64, @@ -491,7 +491,6 @@ u_int block, count; /* since, this routine is called with interrupts cleared - they must be before it finishes */ - sti(); #if 0 printk("%s:got request. device : %d minor : %d command : %d sector : %ld count : %ld, buffer: %p\n", @@ -604,13 +603,16 @@ CURRENT->current_nr_sectors, drive); /* send the command block to the controller */ + spin_unlock_irq(&ps2esdi_lock); if (ps2esdi_out_cmd_blk(cmd_blk)) { + spin_lock_irq(&ps2esdi_lock); printk("%s: Controller failed\n", DEVICE_NAME); if ((++CURRENT->errors) >= MAX_RETRIES) end_request(CURRENT, FAIL); } /* check for failure to put out the command block */ else { + spin_lock_irq(&ps2esdi_lock); #if 0 printk("%s: waiting for xfer\n", DEVICE_NAME); #endif @@ -1099,7 +1101,7 @@ put_user(ps2esdi_info[dev].head, (char *) &geometry->heads); put_user(ps2esdi_info[dev].sect, (char *) &geometry->sectors); put_user(ps2esdi_info[dev].cyl, (short *) &geometry->cylinders); - put_user(get_start_sect(inode->b_rdev), (long *) &geometry->start); + put_user(get_start_sect(inode->i_bdev), (long *) &geometry->start); return 0; } diff -Nru a/drivers/block/umem.c b/drivers/block/umem.c --- a/drivers/block/umem.c Fri Aug 16 14:35:01 2002 +++ b/drivers/block/umem.c Fri Aug 16 14:35:01 2002 @@ -164,14 +164,8 @@ static int num_cards = 0; -struct gendisk mm_gendisk = { - major: 0, /* Major number assigned later */ - major_name: "umem", /* Name of the major device */ - minor_shift: MM_SHIFT, /* Shift to get device number */ - fops: &mm_fops, /* Block dev operations */ -/* everything else is dynamic */ -}; - +static struct gendisk mm_gendisk[MM_MAXCARDS]; +static char mm_names[MM_MAXCARDS * 6]; static void check_batteries(struct cardinfo *card); @@ -818,7 +812,7 @@ static int mm_revalidate(kdev_t i_rdev) { int card_number = DEVICE_NR(i_rdev); - mm_partitions[minor(i_rdev)] = cards[card_number].mm_size << 1; + mm_partitions[minor(i_rdev)].nr_sects = cards[card_number].mm_size << 1; return 0; } /* @@ -828,7 +822,7 @@ */ static int mm_ioctl(struct inode *i, struct file *f, unsigned int cmd, unsigned long arg) { - if (cmd == case HDIO_GETGEO) { + if (cmd == HDIO_GETGEO) { unsigned int minor = minor(i->i_rdev); int err, size, card_number = (minor >> MM_SHIFT); struct hd_geometry geo; @@ -1182,10 +1176,6 @@ printk(KERN_INFO DRIVER_VERSION " : " DRIVER_DESC "\n"); - memset (cards, 0, MM_MAXCARDS * sizeof(struct cardinfo)); - memset (mm_partitions, 0, - (MM_MAXCARDS << MM_SHIFT) * sizeof(struct hd_struct)); - retval = pci_module_init(&mm_pci_driver); if (retval) return -ENOMEM; @@ -1197,23 +1187,23 @@ } devfs_handle = devfs_mk_dir(NULL, "umem", NULL); - - /* Initialize partition size: partion 0 of each card is the entire card */ + blk_dev[MAJOR_NR].queue = mm_queue_proc; for (i = 0; i < num_cards; i++) { + struct gendisk *disk = mm_gendisk + i; + sprintf(mm_names + i*6, "umem%c", 'a'+i); spin_lock_init(&cards[i].lock); - mm_partitions[i << MM_SHIFT].nr_sects = - cards[i].mm_size * (1024 / MM_HARDSECT); - } - - mm_gendisk.part = mm_partitions; - mm_gendisk.nr_real = num_cards; - - blk_dev[MAJOR_NR].queue = mm_queue_proc; - add_gendisk(&mm_gendisk); - - for (i = 0; i < num_cards; i++) { - register_disk(&mm_gendisk, mk_kdev(MAJOR_NR, i<part = mm_partitions + (i << MM_SHIFT); + disk->nr_real = 1; + disk->major = major_nr; + disk->first_minor = i << MM_SHIFT; + disk->major_name = mm_names + i*6; + disk->minor_shift = MM_SHIFT; + disk->fops = &mm_fops; + add_gendisk(disk); + register_disk(disk, mk_kdev(disk->major, disk->first_minor), + 1<minor_shift, + disk->fops, + cards[i].mm_size << 1); } init_battery_timer(); @@ -1232,8 +1222,10 @@ del_battery_timer(); - for (i=0; i < num_cards ; i++) - devfs_register_partitions(&mm_gendisk, i<major,disk->first_minor), + 1<minor_shift, + disk->fops, xd_info[i].heads * xd_info[i].cylinders * xd_info[i].sectors); - xd_gendisk.nr_real = xd_drives; + } + + return 0; +} + +/* xd_detect: scan the possible BIOS ROM locations for the signature strings */ +static u_char __init xd_detect (u_char *controller, unsigned int *address) +{ + u_char i,j,found = 0; + + if (xd_override) + { + *controller = xd_type; + *address = 0; + return(1); + } + + for (i = 0; i < (sizeof(xd_bases) / sizeof(xd_bases[0])) && !found; i++) + for (j = 1; j < (sizeof(xd_sigs) / sizeof(xd_sigs[0])) && !found; j++) + if (isa_check_signature(xd_bases[i] + xd_sigs[j].offset,xd_sigs[j].string,strlen(xd_sigs[j].string))) { + *controller = j; + xd_type = j; + *address = xd_bases[i]; + found++; + } + return (found); } /* xd_open: open a device */ @@ -262,9 +277,9 @@ u_int block,count,retry; int code; - sti(); if (xdc_busy) return; + while (1) { code = 0; /* do some checking on the request structure */ @@ -297,11 +312,7 @@ /* xd_ioctl: handle device ioctl's */ static int xd_ioctl (struct inode *inode,struct file *file,u_int cmd,u_long arg) { - int dev; - - if ((!inode) || kdev_none(inode->i_rdev)) - return -EINVAL; - dev = DEVICE_NR(inode->i_rdev); + int dev = DEVICE_NR(inode->i_rdev); if (dev >= xd_drives) return -EINVAL; switch (cmd) { @@ -309,7 +320,6 @@ { struct hd_geometry g; struct hd_geometry *geometry = (struct hd_geometry *) arg; - if (!geometry) return -EINVAL; g.heads = xd_info[dev].heads; g.sectors = xd_info[dev].sectors; g.cylinders = xd_info[dev].cylinders; @@ -324,6 +334,12 @@ xd_dma_mem_free((unsigned long)xd_dma_buffer, xd_maxsectors * 0x200); xd_dma_buffer = 0; + } else if (!nodma && !xd_dma_buffer) { + xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200); + if (!xd_dma_buffer) { + nodma = XD_DONT_USE_DMA; + return -ENOMEM; + } } return 0; case HDIO_GET_DMA: @@ -348,6 +364,8 @@ printk("xd_readwrite: operation = %s, drive = %d, buffer = 0x%X, block = %d, count = %d\n",operation == READ ? "read" : "write",drive,buffer,block,count); #endif /* DEBUG_READWRITE */ + spin_unlock_irq(&xd_lock); + control = xd_info[drive].control; if (!xd_dma_buffer) xd_dma_buffer = (char *)xd_dma_mem_alloc(xd_maxsectors * 0x200); @@ -378,6 +396,7 @@ case 1: printk("xd%c: %s timeout, recalibrating drive\n",'a'+drive,(operation == READ ? "read" : "write")); xd_recalibrate(drive); + spin_lock_irq(&xd_lock); return (0); case 2: if (sense[0] & 0x30) { @@ -398,6 +417,7 @@ /* reported drive number = (sense[1] & 0xE0) >> 5 */ else printk(" - no valid disk address\n"); + spin_lock_irq(&xd_lock); return (0); } if (xd_dma_buffer) @@ -406,6 +426,7 @@ count -= temp, buffer += temp * 0x200, block += temp; } + spin_lock_irq(&xd_lock); return (1); } @@ -472,13 +493,6 @@ return (cmdblk); } -/* xd_wakeup is called from timer interrupt */ -static void xd_wakeup (unsigned long unused) -{ - wake_up(&xdc_wait); -} - -/* xd_wakeup is called from timer interrupt */ static void xd_watchdog (unsigned long unused) { xd_error = 1; @@ -493,12 +507,8 @@ xdc_busy = 1; while ((success = ((inb(port) & mask) != flags)) && time_before(jiffies, expiry)) { - xd_timer.expires = jiffies; - cli(); - add_timer(&xd_timer); - sleep_on(&xdc_wait); - del_timer(&xd_timer); - sti(); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); } xdc_busy = 0; return (success); @@ -600,16 +610,14 @@ for (i = 0; i < XD_MAXDRIVES; i++) { xd_build(cmdblk,CMD_TESTREADY,i,0,0,0,0,0); if (!xd_command(cmdblk,PIO_MODE,0,0,0,XD_TIMEOUT * 8)) { - xd_timer.expires = jiffies + XD_INIT_DISK_DELAY; - add_timer(&xd_timer); - sleep_on(&xdc_wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(XD_INIT_DISK_DELAY); init_drive(count); count++; - xd_timer.expires = jiffies + XD_INIT_DISK_DELAY; - add_timer(&xd_timer); - sleep_on(&xdc_wait); + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(XD_INIT_DISK_DELAY); } } return (count); @@ -730,9 +738,8 @@ outb(0,XD_RESET); /* reset the controller */ - xd_timer.expires = jiffies + XD_INIT_DISK_DELAY; - add_timer(&xd_timer); - sleep_on(&xdc_wait); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(XD_INIT_DISK_DELAY); } static void __init xd_wd_init_drive (u_char drive) @@ -906,9 +913,8 @@ xd_maxsectors = 0x01; outb(0,XD_RESET); /* reset the controller */ - xd_timer.expires = jiffies + XD_INIT_DISK_DELAY; - add_timer(&xd_timer); - sleep_on(&xdc_wait); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(XD_INIT_DISK_DELAY); } static void __init xd_xebec_init_drive (u_char drive) @@ -1034,8 +1040,10 @@ static void xd_done (void) { + int i; + for (i = 0; i < xd_drives; i++) + del_gendisk(xd_gendisk + i); blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); - del_gendisk(&xd_gendisk); blk_clear(MAJOR_NR); release_region(xd_iobase,4); } diff -Nru a/drivers/block/xd.h b/drivers/block/xd.h --- a/drivers/block/xd.h Fri Aug 16 14:34:55 2002 +++ b/drivers/block/xd.h Fri Aug 16 14:34:55 2002 @@ -108,7 +108,6 @@ #endif /* MODULE */ static u_char xd_detect (u_char *controller, unsigned int *address); static u_char xd_initdrives (void (*init_drive)(u_char drive)); -static void xd_geninit (void); static int xd_open (struct inode *inode,struct file *file); static void do_xd_request (request_queue_t * q); @@ -119,7 +118,6 @@ static void xd_interrupt_handler (int irq, void *dev_id, struct pt_regs *regs); static u_char xd_setup_dma (u_char opcode,u_char *buffer,u_int count); static u_char *xd_build (u_char *cmdblk,u_char command,u_char drive,u_char head,u_short cylinder,u_char sector,u_char count,u_char control); -static void xd_wakeup (unsigned long unused); static void xd_watchdog (unsigned long unused); static inline u_char xd_waitport (u_short port,u_char flags,u_char mask,u_long timeout); static u_int xd_command (u_char *command,u_char mode,u_char *indata,u_char *outdata,u_char *sense,u_long timeout); diff -Nru a/drivers/char/Config.help b/drivers/char/Config.help --- a/drivers/char/Config.help Fri Aug 16 14:34:57 2002 +++ b/drivers/char/Config.help Fri Aug 16 14:34:57 2002 @@ -969,6 +969,31 @@ The module is called rtc.o. If you want to compile it as a module, say M here and read . +Generic Real Time Clock Support +CONFIG_GEN_RTC + If you say Y here and create a character special file /dev/rtc with + major number 10 and minor number 135 using mknod ("man mknod"), you + will get access to the real time clock (or hardware clock) built + into your computer. + + It reports status information via the file /proc/driver/rtc and its + behaviour is set by various ioctls on /dev/rtc. If you enable the + "extended RTC operation" below it will also provide an emulation + for RTC_UIE which is required by some programs and may improve + precision in some cases. + + This driver is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module is called genrtc.o. If you want to compile it as a module, + say M here and read . To load the + module automaticaly add 'alias char-major-10-135 genrtc' to your + /etc/modules.conf + +Extended RTC operation +CONFIG_GEN_RTC_X + Provides an emulation for RTC_UIE which is required by some programs + and may improve precision of the generic RTC support in some cases. + CONFIG_H8 The Hitachi H8/337 is a microcontroller used to deal with the power and thermal environment. If you say Y here, you will be able to diff -Nru a/drivers/char/Config.in b/drivers/char/Config.in --- a/drivers/char/Config.in Fri Aug 16 14:34:54 2002 +++ b/drivers/char/Config.in Fri Aug 16 14:34:54 2002 @@ -151,6 +151,12 @@ fi tristate '/dev/nvram support' CONFIG_NVRAM tristate 'Enhanced Real Time Clock Support' CONFIG_RTC +if [ "$CONFIG_RTC" != "y" ]; then + tristate 'Generic /dev/rtc emulation' CONFIG_GEN_RTC + if [ "$CONFIG_GEN_RTC" != "n" ]; then + bool ' Extended RTC operation' CONFIG_GEN_RTC_X + fi +fi if [ "$CONFIG_IA64" = "y" ]; then bool 'EFI Real Time Clock Services' CONFIG_EFI_RTC fi diff -Nru a/drivers/char/Makefile b/drivers/char/Makefile --- a/drivers/char/Makefile Fri Aug 16 14:35:01 2002 +++ b/drivers/char/Makefile Fri Aug 16 14:35:01 2002 @@ -167,6 +167,7 @@ obj-$(CONFIG_SONYPI) += sonypi.o obj-$(CONFIG_ATARIMOUSE) += atarimouse.o obj-$(CONFIG_RTC) += rtc.o +obj-$(CONFIG_GEN_RTC) += genrtc.o obj-$(CONFIG_EFI_RTC) += efirtc.o ifeq ($(CONFIG_PPC),) obj-$(CONFIG_NVRAM) += nvram.o diff -Nru a/drivers/char/genrtc.c b/drivers/char/genrtc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/char/genrtc.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,518 @@ +/* + * Real Time Clock interface for q40 and other m68k machines + * emulate some RTC irq capabilities in software + * + * Copyright (C) 1999 Richard Zidlicky + * + * based on Paul Gortmaker's rtc.c device and + * Sam Creasey Generic rtc driver + * + * This driver allows use of the real time clock (built into + * nearly all computers) from user space. It exports the /dev/rtc + * interface supporting various ioctl() and also the /proc/dev/rtc + * pseudo-file for status information. + * + * The ioctls can be used to set the interrupt behaviour where + * supported. + * + * The /dev/rtc interface will block on reads until an interrupt + * has been received. If a RTC interrupt has already happened, + * it will output an unsigned long and then block. The output value + * contains the interrupt status in the low byte and the number of + * interrupts since the last read in the remaining high bytes. The + * /dev/rtc interface can also be used with the select(2) call. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + + * 1.01 fix for 2.3.X rz@linux-m68k.org + * 1.02 merged with code from genrtc.c rz@linux-m68k.org + * 1.03 make it more portable zippel@linux-m68k.org + * 1.04 removed useless timer code rz@linux-m68k.org + * 1.05 portable RTC_UIE emulation rz@linux-m68k.org + * 1.06 set_rtc_time can return an error trini@kernel.crashing.org + */ + +#define RTC_VERSION "1.06" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * We sponge a minor off of the misc major. No need slurping + * up another valuable major dev number for this. If you add + * an ioctl, make sure you don't conflict with SPARC's RTC + * ioctls. + */ + +static DECLARE_WAIT_QUEUE_HEAD(gen_rtc_wait); + +static int gen_rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +/* + * Bits in gen_rtc_status. + */ + +#define RTC_IS_OPEN 0x01 /* means /dev/rtc is in use */ + +unsigned char gen_rtc_status; /* bitmapped status byte. */ +unsigned long gen_rtc_irq_data; /* our output to the world */ + +/* months start at 0 now */ +unsigned char days_in_mo[] = +{31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +static int irq_active; + +#ifdef CONFIG_GEN_RTC_X +struct tq_struct genrtc_task; +static struct timer_list timer_task; + +static unsigned int oldsecs; +static int lostint; +static int tt_exp; + +void gen_rtc_timer(unsigned long data); + +static volatile int stask_active; /* schedule_task */ +static volatile int ttask_active; /* timer_task */ +static int stop_rtc_timers; /* don't requeue tasks */ +static spinlock_t gen_rtc_lock = SPIN_LOCK_UNLOCKED; + +/* + * Routine to poll RTC seconds field for change as often as posible, + * after first RTC_UIE use timer to reduce polling + */ +void genrtc_troutine(void *data) +{ + unsigned int tmp = get_rtc_ss(); + + if (stop_rtc_timers) { + stask_active = 0; + return; + } + + if (oldsecs != tmp){ + oldsecs = tmp; + + timer_task.function = gen_rtc_timer; + timer_task.expires = jiffies + HZ - (HZ/10); + tt_exp=timer_task.expires; + ttask_active=1; + stask_active=0; + add_timer(&timer_task); + + gen_rtc_interrupt(0); + } else if (schedule_task(&genrtc_task) == 0) + stask_active = 0; +} + +void gen_rtc_timer(unsigned long data) +{ + lostint = get_rtc_ss() - oldsecs ; + if (lostint<0) + lostint = 60 - lostint; + if (time_after(jiffies, tt_exp)) + printk(KERN_INFO "genrtc: timer task delayed by %ld jiffies\n", + jiffies-tt_exp); + ttask_active=0; + stask_active=1; + if ((schedule_task(&genrtc_task) == 0)) + stask_active = 0; +} + +/* + * call gen_rtc_interrupt function to signal an RTC_UIE, + * arg is unused. + * Could be invoked either from a real interrupt handler or + * from some routine that periodically (eg 100HZ) monitors + * whether RTC_SECS changed + */ +void gen_rtc_interrupt(unsigned long arg) +{ + /* We store the status in the low byte and the number of + * interrupts received since the last read in the remainder + * of rtc_irq_data. */ + + gen_rtc_irq_data += 0x100; + gen_rtc_irq_data &= ~0xff; + gen_rtc_irq_data |= RTC_UIE; + + if (lostint){ + printk("genrtc: system delaying clock ticks?\n"); + /* increment count so that userspace knows something is wrong */ + gen_rtc_irq_data += ((lostint-1)<<8); + lostint = 0; + } + + wake_up_interruptible(&gen_rtc_wait); +} + +/* + * Now all the various file operations that we export. + */ +static ssize_t gen_rtc_read(struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long data; + ssize_t retval; + + if (count != sizeof (unsigned int) && count != sizeof (unsigned long)) + return -EINVAL; + + if (file->f_flags & O_NONBLOCK && !gen_rtc_irq_data) + return -EAGAIN; + + add_wait_queue(&gen_rtc_wait, &wait); + retval = -ERESTARTSYS; + + while (1) { + set_current_state(TASK_INTERRUPTIBLE); + data = xchg(&gen_rtc_irq_data, 0); + if (data) + break; + if (signal_pending(current)) + goto out; + schedule(); + } + + /* first test allows optimizer to nuke this case for 32-bit machines */ + if (sizeof (int) != sizeof (long) && count == sizeof (unsigned int)) { + unsigned int uidata = data; + retval = put_user(uidata, (unsigned long *)buf); + } + else { + retval = put_user(data, (unsigned long *)buf); + } + if (!retval) + retval = sizeof(unsigned long); + out: + current->state = TASK_RUNNING; + remove_wait_queue(&gen_rtc_wait, &wait); + + return retval; +} + +static unsigned int gen_rtc_poll(struct file *file, + struct poll_table_struct *wait) +{ + poll_wait(file, &gen_rtc_wait, wait); + if (gen_rtc_irq_data != 0) + return POLLIN | POLLRDNORM; + return 0; +} + +#endif + +/* + * Used to disable/enable interrupts, only RTC_UIE supported + * We also clear out any old irq data after an ioctl() that + * meddles with the interrupt enable/disable bits. + */ + +static inline void gen_clear_rtc_irq_bit(unsigned char bit) +{ +#ifdef CONFIG_GEN_RTC_X + stop_rtc_timers = 1; + if (ttask_active){ + del_timer_sync(&timer_task); + ttask_active = 0; + } + while (stask_active) + schedule(); + + spin_lock(&gen_rtc_lock); + irq_active = 0; + spin_unlock(&gen_rtc_lock); +#endif +} + +static inline int gen_set_rtc_irq_bit(unsigned char bit) +{ +#ifdef CONFIG_GEN_RTC_X + spin_lock(&gen_rtc_lock); + if ( !irq_active ) { + irq_active = 1; + stop_rtc_timers = 0; + lostint = 0; + genrtc_task.routine = genrtc_troutine; + oldsecs = get_rtc_ss(); + init_timer(&timer_task); + + stask_active = 1; + if (schedule_task(&genrtc_task) == 0){ + stask_active = 0; + } + } + spin_unlock(&gen_rtc_lock); + gen_rtc_irq_data = 0; + return 0; +#else + return -EINVAL; +#endif +} + +static int gen_rtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct rtc_time wtime; + struct rtc_pll_info pll; + + switch (cmd) { + + case RTC_PLL_GET: + if (get_rtc_pll(&pll)) + return -EINVAL; + else + return copy_to_user((void *)arg, &pll, sizeof pll) ? -EFAULT : 0; + + case RTC_PLL_SET: + if (!capable(CAP_SYS_TIME)) + return -EACCES; + if (copy_from_user(&pll, (struct rtc_pll_info*)arg, + sizeof(pll))) + return -EFAULT; + return set_rtc_pll(&pll); + + case RTC_UIE_OFF: /* disable ints from RTC updates. */ + gen_clear_rtc_irq_bit(RTC_UIE); + return 0; + + case RTC_UIE_ON: /* enable ints for RTC updates. */ + return gen_set_rtc_irq_bit(RTC_UIE); + + case RTC_RD_TIME: /* Read the time/date from RTC */ + /* this doesn't get week-day, who cares */ + memset(&wtime, 0, sizeof(wtime)); + get_rtc_time(&wtime); + + return copy_to_user((void *)arg, &wtime, sizeof(wtime)) ? -EFAULT : 0; + + case RTC_SET_TIME: /* Set the RTC */ + { + int year; + unsigned char leap_yr; + + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&wtime, (struct rtc_time *)arg, + sizeof(wtime))) + return -EFAULT; + + year = wtime.tm_year + 1900; + leap_yr = ((!(year % 4) && (year % 100)) || + !(year % 400)); + + if ((wtime.tm_mon < 0 || wtime.tm_mon > 11) || (wtime.tm_mday < 1)) + return -EINVAL; + + if (wtime.tm_mday < 0 || wtime.tm_mday > + (days_in_mo[wtime.tm_mon] + ((wtime.tm_mon == 1) && leap_yr))) + return -EINVAL; + + if (wtime.tm_hour < 0 || wtime.tm_hour >= 24 || + wtime.tm_min < 0 || wtime.tm_min >= 60 || + wtime.tm_sec < 0 || wtime.tm_sec >= 60) + return -EINVAL; + + return set_rtc_time(&wtime); + } + } + + return -EINVAL; +} + +/* + * We enforce only one user at a time here with the open/close. + * Also clear the previous interrupt data on an open, and clean + * up things on a close. + */ + +static int gen_rtc_open(struct inode *inode, struct file *file) +{ + if (gen_rtc_status & RTC_IS_OPEN) + return -EBUSY; + + MOD_INC_USE_COUNT; + + gen_rtc_status |= RTC_IS_OPEN; + gen_rtc_irq_data = 0; + irq_active = 0; + + return 0; +} + +static int gen_rtc_release(struct inode *inode, struct file *file) +{ + /* + * Turn off all interrupts once the device is no longer + * in use and clear the data. + */ + + gen_clear_rtc_irq_bit(RTC_PIE|RTC_AIE|RTC_UIE); + + gen_rtc_status &= ~RTC_IS_OPEN; + MOD_DEC_USE_COUNT; + + return 0; +} + +static int gen_rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data); + + +/* + * The various file operations we support. + */ + +static struct file_operations gen_rtc_fops = { + .owner = THIS_MODULE, +#ifdef CONFIG_GEN_RTC_X + .read = gen_rtc_read, + .poll = gen_rtc_poll, +#endif + .ioctl = gen_rtc_ioctl, + .open = gen_rtc_open, + .release = gen_rtc_release +}; + +static struct miscdevice rtc_gen_dev = +{ + RTC_MINOR, + "rtc", + &gen_rtc_fops +}; + +int __init rtc_generic_init(void) +{ + + printk(KERN_INFO "Generic RTC Driver v%s\n", RTC_VERSION); + + misc_register(&rtc_gen_dev); + create_proc_read_entry ("driver/rtc", 0, 0, gen_rtc_read_proc, NULL); + + return 0; +} + +static void __exit rtc_generic_exit(void) +{ + remove_proc_entry ("driver/rtc", NULL); + misc_deregister(&rtc_gen_dev); +} + +module_init(rtc_generic_init); +module_exit(rtc_generic_exit); +EXPORT_NO_SYMBOLS; + + +/* + * Info exported via "/proc/rtc". + */ + +int gen_rtc_proc_output(char *buf) +{ + char *p; + struct rtc_time tm; + unsigned tmp; + struct rtc_pll_info pll; + + p = buf; + + get_rtc_time(&tm); + + p += sprintf(p, + "rtc_time\t: %02d:%02d:%02d\n" + "rtc_date\t: %04d-%02d-%02d\n" + "rtc_epoch\t: %04u\n", + tm.tm_hour, tm.tm_min, tm.tm_sec, + tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, 1900); + + tm.tm_hour=0;tm.tm_min=0;tm.tm_sec=0; + + p += sprintf(p, "alarm\t\t: "); + if (tm.tm_hour <= 24) + p += sprintf(p, "%02d:", tm.tm_hour); + else + p += sprintf(p, "**:"); + + if (tm.tm_min <= 59) + p += sprintf(p, "%02d:", tm.tm_min); + else + p += sprintf(p, "**:"); + + if (tm.tm_sec <= 59) + p += sprintf(p, "%02d\n", tm.tm_sec); + else + p += sprintf(p, "**\n"); + + tmp= RTC_24H ; + p += sprintf(p, + "DST_enable\t: %s\n" + "BCD\t\t: %s\n" + "24hr\t\t: %s\n" + "square_wave\t: %s\n" + "alarm_IRQ\t: %s\n" + "update_IRQ\t: %s\n" + "periodic_IRQ\t: %s\n" + "periodic_freq\t: %ld\n" + "batt_status\t: %s\n", + (tmp & RTC_DST_EN) ? "yes" : "no", + (tmp & RTC_DM_BINARY) ? "no" : "yes", + (tmp & RTC_24H) ? "yes" : "no", + (tmp & RTC_SQWE) ? "yes" : "no", + (tmp & RTC_AIE) ? "yes" : "no", + irq_active ? "yes" : "no", + (tmp & RTC_PIE) ? "yes" : "no", + 0L /* freq */, + "okay" ); + if (!get_rtc_pll(&pll)) + p += sprintf(p, + "PLL adjustment\t: %d\n" + "PLL max +ve adjustment\t: %d\n" + "PLL max -ve adjustment\t: %d\n" + "PLL +ve adjustment factor\t: %d\n" + "PLL -ve adjustment factor\t: %d\n" + "PLL frequency\t: %ld\n", + pll.pll_value, + pll.pll_max, + pll.pll_min, + pll.pll_posmult, + pll.pll_negmult, + pll.pll_clock); + return p - buf; +} + +static int gen_rtc_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len = gen_rtc_proc_output (page); + if (len <= off+count) *eof = 1; + *start = page + off; + len -= off; + if (len>count) len = count; + if (len<0) len = 0; + return len; +} + + +MODULE_AUTHOR("Richard Zidlicky"); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c --- a/drivers/char/hvc_console.c Fri Aug 16 14:35:00 2002 +++ b/drivers/char/hvc_console.c Fri Aug 16 14:35:00 2002 @@ -204,7 +204,7 @@ sysrq_pressed = 1; continue; } else if (sysrq_pressed) { - handle_sysrq(buf[i], NULL, NULL, tty); + handle_sysrq(buf[i], NULL, tty); sysrq_pressed = 0; continue; } diff -Nru a/drivers/char/mem.c b/drivers/char/mem.c --- a/drivers/char/mem.c Fri Aug 16 14:34:58 2002 +++ b/drivers/char/mem.c Fri Aug 16 14:34:58 2002 @@ -210,6 +210,9 @@ return 0; } +extern long vread(char *buf, char *addr, unsigned long count); +extern long vwrite(char *buf, char *addr, unsigned long count); + /* * This function reads the *virtual* memory as seen by the kernel. */ @@ -272,8 +275,6 @@ *ppos = p; return virtr + read; } - -extern long vwrite(char *buf, char *addr, unsigned long count); /* * This function writes to the *virtual* memory as seen by the kernel. diff -Nru a/drivers/i2c/Config.in b/drivers/i2c/Config.in --- a/drivers/i2c/Config.in Fri Aug 16 14:35:01 2002 +++ b/drivers/i2c/Config.in Fri Aug 16 14:35:01 2002 @@ -32,10 +32,10 @@ dep_tristate ' Embedded Planet RPX Lite/Classic suppoort' CONFIG_I2C_RPXLITE $CONFIG_I2C_ALGO8XX fi fi - if [ "$CONFIG_405" = "y" ]; then - dep_tristate 'PPC 405 I2C Algorithm' CONFIG_I2C_PPC405_ALGO $CONFIG_I2C - if [ "$CONFIG_I2C_PPC405_ALGO" != "n" ]; then - dep_tristate ' PPC 405 I2C Adapter' CONFIG_I2C_PPC405_ADAP $CONFIG_I2C_PPC405_ALGO + if [ "$CONFIG_IBM_OCP" = "y" ]; then + dep_tristate 'IBM on-chip I2C Algorithm' CONFIG_I2C_IBM_OCP_ALGO $CONFIG_I2C + if [ "$CONFIG_I2C_IBM_OCP_ALGO" != "n" ]; then + dep_tristate ' IBM on-chip I2C Adapter' CONFIG_I2C_IBM_OCP_ADAP $CONFIG_I2C_IBM_OCP_ALGO fi fi diff -Nru a/drivers/i2c/i2c-algo-bit.c b/drivers/i2c/i2c-algo-bit.c --- a/drivers/i2c/i2c-algo-bit.c Fri Aug 16 14:34:58 2002 +++ b/drivers/i2c/i2c-algo-bit.c Fri Aug 16 14:34:58 2002 @@ -21,7 +21,7 @@ /* With some changes from Kyösti Mälkki and even Frodo Looijaard */ -/* $Id: i2c-algo-bit.c,v 1.34 2001/11/19 18:45:02 mds Exp $ */ +/* $Id: i2c-algo-bit.c,v 1.37 2002/07/08 00:41:49 mds Exp $ */ #include #include @@ -143,7 +143,7 @@ /* scl, sda may not be high */ DEBPROTO(printk(" Sr ")); setsda(adap,1); - setscl(adap,1); + sclhi(adap); udelay(adap->udelay); sdalo(adap); diff -Nru a/drivers/i2c/i2c-algo-ibm_ocp.c b/drivers/i2c/i2c-algo-ibm_ocp.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-algo-ibm_ocp.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,966 @@ +/* + ------------------------------------------------------------------------- + i2c-algo-ibm_ocp.c i2c driver algorithms for IBM PPC 405 adapters + ------------------------------------------------------------------------- + + Ian DaSilva, MontaVista Software, Inc. + idasilva@mvista.com or source@mvista.com + + Copyright 2000 MontaVista Software Inc. + + Changes made to support the IIC peripheral on the IBM PPC 405 + + + --------------------------------------------------------------------------- + This file was highly leveraged from i2c-algo-pcf.c, which was created + by Simon G. Vogl and Hans Berglund: + + + Copyright (C) 1995-1997 Simon G. Vogl + 1998-2000 Hans Berglund + + With some changes from Kyösti Mälkki and + Frodo Looijaard ,and also from Martin Bailey + + + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + --------------------------------------------------------------------------- + + History: 01/20/12 - Armin + akuster@mvista.com + ported up to 2.4.16+ + + Version 02/03/25 - Armin + converted to ocp format + removed commented out or #if 0 code + added Gérard Basler's fix to iic_combined_transaction() such that it + returns the number of successfully completed transfers . +*/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "i2c-algo-ibm_ocp.h" +//ACC#include + +#ifdef MODULE_LICENSE +MODULE_LICENSE("GPL"); +#endif + + +/* ----- global defines ----------------------------------------------- */ +#define DEB(x) if (i2c_debug>=1) x +#define DEB2(x) if (i2c_debug>=2) x +#define DEB3(x) if (i2c_debug>=3) x /* print several statistical values*/ +#define DEBPROTO(x) if (i2c_debug>=9) x; + /* debug the protocol by showing transferred bits */ +#define DEF_TIMEOUT 5 + +/* debugging - slow down transfer to have a look at the data .. */ +/* I use this with two leds&resistors, each one connected to sda,scl */ +/* respectively. This makes sure that the algorithm works. Some chips */ +/* might not like this, as they have an internal timeout of some mils */ +/* +#define SLO_IO jif=jiffies;while(jiffies<=jif+i2c_table[minor].veryslow)\ + if (need_resched) schedule(); +*/ + + +/* ----- global variables --------------------------------------------- */ + +#ifdef SLO_IO + int jif; +#endif + +/* module parameters: + */ +static int i2c_debug=0; +static int iic_scan=0; /* have a look at what's hanging 'round */ + +/* --- setting states on the bus with the right timing: --------------- */ + +#define iic_outb(adap, reg, val) adap->setiic(adap->data, (int) &(reg), val) +#define iic_inb(adap, reg) adap->getiic(adap->data, (int) &(reg)) + +#define IICO_I2C_SDAHIGH 0x0780 +#define IICO_I2C_SDALOW 0x0781 +#define IICO_I2C_SCLHIGH 0x0782 +#define IICO_I2C_SCLLOW 0x0783 +#define IICO_I2C_LINEREAD 0x0784 + +#define IIC_SINGLE_XFER 0 +#define IIC_COMBINED_XFER 1 + +#define IIC_ERR_LOST_ARB -2 +#define IIC_ERR_INCOMPLETE_XFR -3 +#define IIC_ERR_NACK -1 + +/* --- other auxiliary functions -------------------------------------- */ + + +// +// Description: Puts this process to sleep for a period equal to timeout +// +static inline void iic_sleep(unsigned long timeout) +{ + schedule_timeout( timeout * HZ); +} + + +// +// Description: This performs the IBM PPC 405 IIC initialization sequence +// as described in the PPC405GP data book. +// +static int iic_init (struct i2c_algo_iic_data *adap) +{ + struct iic_regs *iic; + struct iic_ibm *adap_priv_data = adap->data; + unsigned short retval; + iic = (struct iic_regs *) adap_priv_data->iic_base; + + /* Clear master low master address */ + iic_outb(adap,iic->lmadr, 0); + + /* Clear high master address */ + iic_outb(adap,iic->hmadr, 0); + + /* Clear low slave address */ + iic_outb(adap,iic->lsadr, 0); + + /* Clear high slave address */ + iic_outb(adap,iic->hsadr, 0); + + /* Clear status */ + iic_outb(adap,iic->sts, 0x0a); + + /* Clear extended status */ + iic_outb(adap,iic->extsts, 0x8f); + + /* Set clock division */ + iic_outb(adap,iic->clkdiv, 0x04); + + retval = iic_inb(adap, iic->clkdiv); + DEB(printk("iic_init: CLKDIV register = %x\n", retval)); + + /* Enable interrupts on Requested Master Transfer Complete */ + iic_outb(adap,iic->intmsk, 0x01); + + /* Clear transfer count */ + iic_outb(adap,iic->xfrcnt, 0x0); + + /* Clear extended control and status */ + iic_outb(adap,iic->xtcntlss, 0xf0); + + /* Set mode control (flush master data buf, enable hold SCL, exit */ + /* unknown state. */ + iic_outb(adap,iic->mdcntl, 0x47); + + /* Clear control register */ + iic_outb(adap,iic->cntl, 0x0); + + DEB2(printk(KERN_DEBUG "iic_init: Initialized IIC on PPC 405\n")); + return 0; +} + + +// +// Description: After we issue a transaction on the IIC bus, this function +// is called. It puts this process to sleep until we get an interrupt from +// from the controller telling us that the transaction we requested in complete. +// +static int wait_for_pin(struct i2c_algo_iic_data *adap, int *status) +{ + + int timeout = DEF_TIMEOUT; + int retval; + struct iic_regs *iic; + struct iic_ibm *adap_priv_data = adap->data; + iic = (struct iic_regs *) adap_priv_data->iic_base; + + + *status = iic_inb(adap, iic->sts); +#ifndef STUB_I2C + + while (timeout-- && (*status & 0x01)) { + adap->waitforpin(adap->data); + *status = iic_inb(adap, iic->sts); + } +#endif + if (timeout <= 0) { + /* Issue stop signal on the bus, and force an interrupt */ + retval = iic_inb(adap, iic->cntl); + iic_outb(adap, iic->cntl, retval | 0x80); + /* Clear status register */ + iic_outb(adap, iic->sts, 0x0a); + /* Exit unknown bus state */ + retval = iic_inb(adap, iic->mdcntl); + iic_outb(adap, iic->mdcntl, (retval | 0x02)); + + // Check the status of the controller. Does it still see a + // pending transfer, even though we've tried to stop any + // ongoing transaction? + retval = iic_inb(adap, iic->sts); + retval = retval & 0x01; + if(retval) { + // The iic controller is hosed. It is not responding to any + // of our commands. We have already tried to force it into + // a known state, but it has not worked. Our only choice now + // is a soft reset, which will clear all registers, and force + // us to re-initialize the controller. + /* Soft reset */ + iic_outb(adap, iic->xtcntlss, 0x01); + udelay(500); + iic_init(adap); + /* Is the pending transfer bit in the sts reg finally cleared? */ + retval = iic_inb(adap, iic->sts); + retval = retval & 0x01; + if(retval) { + printk(KERN_CRIT "The IIC Controller is hosed. A processor reset is required\n"); + } + // For some reason, even though the interrupt bit in this + // register was set during iic_init, it didn't take. We + // need to set it again. Don't ask me why....this is just what + // I saw when testing timeouts. + iic_outb(adap, iic->intmsk, 0x01); + } + return(-1); + } + else + return(0); +} + + +//------------------------------------ +// Utility functions +// + + +// +// Description: Look at the status register to see if there was an error +// in the requested transaction. If there is, look at the extended status +// register and determine the exact cause. +// +int analyze_status(struct i2c_algo_iic_data *adap, int *error_code) +{ + int ret; + struct iic_regs *iic; + struct iic_ibm *adap_priv_data = adap->data; + iic = (struct iic_regs *) adap_priv_data->iic_base; + + + ret = iic_inb(adap, iic->sts); + if(ret & 0x04) { + // Error occurred + ret = iic_inb(adap, iic->extsts); + if(ret & 0x04) { + // Lost arbitration + *error_code = IIC_ERR_LOST_ARB; + } + if(ret & 0x02) { + // Incomplete transfer + *error_code = IIC_ERR_INCOMPLETE_XFR; + } + if(ret & 0x01) { + // Master transfer aborted by a NACK during the transfer of the + // address byte + *error_code = IIC_ERR_NACK; + } + return -1; + } + return 0; +} + + +// +// Description: This function is called by the upper layers to do the +// grunt work for a master send transaction +// +static int iic_sendbytes(struct i2c_adapter *i2c_adap,const char *buf, + int count, int xfer_flag) +{ + struct iic_regs *iic; + struct i2c_algo_iic_data *adap = i2c_adap->algo_data; + struct iic_ibm *adap_priv_data = adap->data; + int wrcount, status, timeout; + int loops, remainder, i, j; + int ret, error_code; + iic = (struct iic_regs *) adap_priv_data->iic_base; + + + if( count == 0 ) return 0; + wrcount = 0; + loops = count / 4; + remainder = count % 4; + + if((loops > 1) && (remainder == 0)) { + for(i=0; i<(loops-1); i++) { + // + // Write four bytes to master data buffer + // + for(j=0; j<4; j++) { + iic_outb(adap, iic->mdbuf, + buf[wrcount++]); + } + // + // Issue command to IICO device to begin transmission + // + iic_outb(adap, iic->cntl, 0x35); + // + // Wait for transmission to complete. When it does, + //loop to the top of the for statement and write the + // next four bytes. + // + timeout = wait_for_pin(adap, &status); + if(timeout < 0) { + // + // Error handling + // + //printk(KERN_ERR "Error: write timeout\n"); + return wrcount; + } + ret = analyze_status(adap, &error_code); + if(ret < 0) { + if(error_code == IIC_ERR_INCOMPLETE_XFR) { + // Return the number of bytes transferred + ret = iic_inb(adap, iic->xfrcnt); + ret = ret & 0x07; + return (wrcount-4+ret); + } + else return error_code; + } + } + } + else if((loops >= 1) && (remainder > 0)){ + //printk(KERN_DEBUG "iic_sendbytes: (loops >= 1)\n"); + for(i=0; imdbuf, + buf[wrcount++]); + } + // + // Issue command to IICO device to begin transmission + // + iic_outb(adap, iic->cntl, 0x35); + // + // Wait for transmission to complete. When it does, + //loop to the top of the for statement and write the + // next four bytes. + // + timeout = wait_for_pin(adap, &status); + if(timeout < 0) { + // + // Error handling + // + //printk(KERN_ERR "Error: write timeout\n"); + return wrcount; + } + ret = analyze_status(adap, &error_code); + if(ret < 0) { + if(error_code == IIC_ERR_INCOMPLETE_XFR) { + // Return the number of bytes transferred + ret = iic_inb(adap, iic->xfrcnt); + ret = ret & 0x07; + return (wrcount-4+ret); + } + else return error_code; + } + } + } + + //printk(KERN_DEBUG "iic_sendbytes: expedite write\n"); + if(remainder == 0) remainder = 4; + // remainder = remainder - 1; + // + // Write the remaining bytes (less than or equal to 4) + // + for(i=0; imdbuf, buf[wrcount++]); + //printk(KERN_DEBUG "iic_sendbytes: data transferred = %x, wrcount = %d\n", buf[wrcount-1], (wrcount-1)); + } + //printk(KERN_DEBUG "iic_sendbytes: Issuing write\n"); + + if(xfer_flag == IIC_COMBINED_XFER) { + iic_outb(adap, iic->cntl, (0x09 | ((remainder-1) << 4))); + } + else { + iic_outb(adap, iic->cntl, (0x01 | ((remainder-1) << 4))); + } + DEB2(printk(KERN_DEBUG "iic_sendbytes: Waiting for interrupt\n")); + timeout = wait_for_pin(adap, &status); + if(timeout < 0) { + // + // Error handling + // + //printk(KERN_ERR "Error: write timeout\n"); + return wrcount; + } + ret = analyze_status(adap, &error_code); + if(ret < 0) { + if(error_code == IIC_ERR_INCOMPLETE_XFR) { + // Return the number of bytes transferred + ret = iic_inb(adap, iic->xfrcnt); + ret = ret & 0x07; + return (wrcount-4+ret); + } + else return error_code; + } + DEB2(printk(KERN_DEBUG "iic_sendbytes: Got interrupt\n")); + return wrcount; +} + + +// +// Description: Called by the upper layers to do the grunt work for +// a master read transaction. +// +static int iic_readbytes(struct i2c_adapter *i2c_adap, char *buf, int count, int xfer_type) +{ + struct iic_regs *iic; + int rdcount=0, i, status, timeout; + struct i2c_algo_iic_data *adap = i2c_adap->algo_data; + struct iic_ibm *adap_priv_data = adap->data; + int loops, remainder, j; + int ret, error_code; + iic = (struct iic_regs *) adap_priv_data->iic_base; + + if(count == 0) return 0; + loops = count / 4; + remainder = count % 4; + + //printk(KERN_DEBUG "iic_readbytes: loops = %d, remainder = %d\n", loops, remainder); + + if((loops > 1) && (remainder == 0)) { + //printk(KERN_DEBUG "iic_readbytes: (loops > 1) && (remainder == 0)\n"); + for(i=0; i<(loops-1); i++) { + // + // Issue command to begin master read (4 bytes maximum) + // + //printk(KERN_DEBUG "--->Issued read command\n"); + iic_outb(adap, iic->cntl, 0x37); + // + // Wait for transmission to complete. When it does, + // loop to the top of the for statement and write the + // next four bytes. + // + //printk(KERN_DEBUG "--->Waiting for interrupt\n"); + timeout = wait_for_pin(adap, &status); + if(timeout < 0) { + // Error Handler + //printk(KERN_ERR "Error: read timed out\n"); + return rdcount; + } + //printk(KERN_DEBUG "--->Got interrupt\n"); + + ret = analyze_status(adap, &error_code); + if(ret < 0) { + if(error_code == IIC_ERR_INCOMPLETE_XFR) + return rdcount; + else + return error_code; + } + + for(j=0; j<4; j++) { + // Wait for data to shuffle to top of data buffer + // This value needs to optimized. + udelay(1); + buf[rdcount] = iic_inb(adap, iic->mdbuf); + rdcount++; + //printk(KERN_DEBUG "--->Read one byte\n"); + } + } + } + + else if((loops >= 1) && (remainder > 0)){ + //printk(KERN_DEBUG "iic_readbytes: (loops >=1) && (remainder > 0)\n"); + for(i=0; iIssued read command\n"); + iic_outb(adap, iic->cntl, 0x37); + // + // Wait for transmission to complete. When it does, + // loop to the top of the for statement and write the + // next four bytes. + // + //printk(KERN_DEBUG "--->Waiting for interrupt\n"); + timeout = wait_for_pin(adap, &status); + if(timeout < 0) { + // Error Handler + //printk(KERN_ERR "Error: read timed out\n"); + return rdcount; + } + //printk(KERN_DEBUG "--->Got interrupt\n"); + + ret = analyze_status(adap, &error_code); + if(ret < 0) { + if(error_code == IIC_ERR_INCOMPLETE_XFR) + return rdcount; + else + return error_code; + } + + for(j=0; j<4; j++) { + // Wait for data to shuffle to top of data buffer + // This value needs to optimized. + udelay(1); + buf[rdcount] = iic_inb(adap, iic->mdbuf); + rdcount++; + //printk(KERN_DEBUG "--->Read one byte\n"); + } + } + } + + //printk(KERN_DEBUG "iic_readbytes: expedite read\n"); + if(remainder == 0) remainder = 4; + DEB2(printk(KERN_DEBUG "iic_readbytes: writing %x to IICO_CNTL\n", (0x03 | ((remainder-1) << 4)))); + + if(xfer_type == IIC_COMBINED_XFER) { + iic_outb(adap, iic->cntl, (0x0b | ((remainder-1) << 4))); + } + else { + iic_outb(adap, iic->cntl, (0x03 | ((remainder-1) << 4))); + } + DEB2(printk(KERN_DEBUG "iic_readbytes: Wait for pin\n")); + timeout = wait_for_pin(adap, &status); + DEB2(printk(KERN_DEBUG "iic_readbytes: Got the interrupt\n")); + if(timeout < 0) { + // Error Handler + //printk(KERN_ERR "Error: read timed out\n"); + return rdcount; + } + + ret = analyze_status(adap, &error_code); + if(ret < 0) { + if(error_code == IIC_ERR_INCOMPLETE_XFR) + return rdcount; + else + return error_code; + } + + //printk(KERN_DEBUG "iic_readbyte: Begin reading data buffer\n"); + for(i=0; imdbuf); + // printk(KERN_DEBUG "iic_readbytes: Character read = %x\n", buf[rdcount]); + rdcount++; + } + + return rdcount; +} + + +// +// Description: This function implements combined transactions. Combined +// transactions consist of combinations of reading and writing blocks of data. +// Each transfer (i.e. a read or a write) is separated by a repeated start +// condition. +// +static int iic_combined_transaction(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num) +{ + int i; + struct i2c_msg *pmsg; + int ret; + + DEB2(printk(KERN_DEBUG "Beginning combined transaction\n")); + for(i=0; i < num; i++) { + pmsg = &msgs[i]; + if(pmsg->flags & I2C_M_RD) { + + // Last read or write segment needs to be terminated with a stop + if(i < num-1) { + DEB2(printk(KERN_DEBUG "This one is a read\n")); + } + else { + DEB2(printk(KERN_DEBUG "Doing the last read\n")); + } + ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, (i < num-1) ? IIC_COMBINED_XFER : IIC_SINGLE_XFER); + + if (ret != pmsg->len) { + DEB2(printk("i2c-algo-ppc405.o: fail: " + "only read %d bytes.\n",ret)); + return i; + } + else { + DEB2(printk("i2c-algo-ppc405.o: read %d bytes.\n",ret)); + } + } + else if(!(pmsg->flags & I2C_M_RD)) { + + // Last read or write segment needs to be terminated with a stop + if(i < num-1) { + DEB2(printk(KERN_DEBUG "This one is a write\n")); + } + else { + DEB2(printk(KERN_DEBUG "Doing the last write\n")); + } + ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, (i < num-1) ? IIC_COMBINED_XFER : IIC_SINGLE_XFER); + + if (ret != pmsg->len) { + DEB2(printk("i2c-algo-ppc405.o: fail: " + "only wrote %d bytes.\n",ret)); + return i; + } + else { + DEB2(printk("i2c-algo-ppc405.o: wrote %d bytes.\n",ret)); + } + } + } + + return num; +} + + +// +// Description: Whenever we initiate a transaction, the first byte clocked +// onto the bus after the start condition is the address (7 bit) of the +// device we want to talk to. This function manipulates the address specified +// so that it makes sense to the hardware when written to the IIC peripheral. +// +// Note: 10 bit addresses are not supported in this driver, although they are +// supported by the hardware. This functionality needs to be implemented. +// +static inline int iic_doAddress(struct i2c_algo_iic_data *adap, + struct i2c_msg *msg, int retries) +{ + struct iic_regs *iic; + unsigned short flags = msg->flags; + unsigned char addr; + struct iic_ibm *adap_priv_data = adap->data; + iic = (struct iic_regs *) adap_priv_data->iic_base; + +// +// The following segment for 10 bit addresses needs to be ported +// +/* Ten bit addresses not supported right now + if ( (flags & I2C_M_TEN) ) { + // a ten bit address + addr = 0xf0 | (( msg->addr >> 7) & 0x03); + DEB2(printk(KERN_DEBUG "addr0: %d\n",addr)); + // try extended address code... + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk(KERN_ERR "iic_doAddress: died at extended address code.\n"); + return -EREMOTEIO; + } + // the remaining 8 bit address + iic_outb(adap,msg->addr & 0x7f); + // Status check comes here + if (ret != 1) { + printk(KERN_ERR "iic_doAddress: died at 2nd address code.\n"); + return -EREMOTEIO; + } + if ( flags & I2C_M_RD ) { + i2c_repstart(adap); + // okay, now switch into reading mode + addr |= 0x01; + ret = try_address(adap, addr, retries); + if (ret!=1) { + printk(KERN_ERR "iic_doAddress: died at extended address code.\n"); + return -EREMOTEIO; + } + } + } else ----------> // normal 7 bit address + +Ten bit addresses not supported yet */ + + addr = ( msg->addr << 1 ); + if (flags & I2C_M_RD ) + addr |= 1; + if (flags & I2C_M_REV_DIR_ADDR ) + addr ^= 1; + // + // Write to the low slave address + // + iic_outb(adap, iic->lmadr, addr); + // + // Write zero to the high slave register since we are + // only using 7 bit addresses + // + iic_outb(adap, iic->hmadr, 0); + + return 0; +} + + +// +// Description: Prepares the controller for a transaction (clearing status +// registers, data buffers, etc), and then calls either iic_readbytes or +// iic_sendbytes to do the actual transaction. +// +static int iic_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], + int num) +{ + struct iic_regs *iic; + struct i2c_algo_iic_data *adap = i2c_adap->algo_data; + struct iic_ibm *adap_priv_data = adap->data; + struct i2c_msg *pmsg; + int i = 0; + int ret; + iic = (struct iic_regs *) adap_priv_data->iic_base; + + pmsg = &msgs[i]; + + // + // Clear status register + // + DEB2(printk(KERN_DEBUG "iic_xfer: iic_xfer: Clearing status register\n")); + iic_outb(adap, iic->sts, 0x0a); + + // + // Wait for any pending transfers to complete + // + DEB2(printk(KERN_DEBUG "iic_xfer: Waiting for any pending transfers to complete\n")); + while((ret = iic_inb(adap, iic->sts)) == 0x01) { + ; + } + + // + // Flush master data buf + // + DEB2(printk(KERN_DEBUG "iic_xfer: Clearing master data buffer\n")); + ret = iic_inb(adap, iic->mdcntl); + iic_outb(adap, iic->mdcntl, ret | 0x40); + + // + // Load slave address + // + DEB2(printk(KERN_DEBUG "iic_xfer: Loading slave address\n")); + ret = iic_doAddress(adap, pmsg, i2c_adap->retries); + + // + // Check to see if the bus is busy + // + ret = iic_inb(adap, iic->extsts); + // Mask off the irrelevent bits + ret = ret & 0x70; + // When the bus is free, the BCS bits in the EXTSTS register are 0b100 + if(ret != 0x40) return IIC_ERR_LOST_ARB; + + // + // Combined transaction (read and write) + // + if(num > 1) { + DEB2(printk(KERN_DEBUG "iic_xfer: Call combined transaction\n")); + ret = iic_combined_transaction(i2c_adap, msgs, num); + } + // + // Read only + // + else if((num == 1) && (pmsg->flags & I2C_M_RD)) { + // + // Tell device to begin reading data from the master data + // + DEB2(printk(KERN_DEBUG "iic_xfer: Call adapter's read\n")); + ret = iic_readbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER); + } + // + // Write only + // + else if((num == 1 ) && (!(pmsg->flags & I2C_M_RD))) { + // + // Write data to master data buffers and tell our device + // to begin transmitting + // + DEB2(printk(KERN_DEBUG "iic_xfer: Call adapter's write\n")); + ret = iic_sendbytes(i2c_adap, pmsg->buf, pmsg->len, IIC_SINGLE_XFER); + } + + return ret; +} + + +// +// Description: Implements device specific ioctls. Higher level ioctls can +// be found in i2c-core.c and are typical of any i2c controller (specifying +// slave address, timeouts, etc). These ioctls take advantage of any hardware +// features built into the controller for which this algorithm-adapter set +// was written. These ioctls allow you to take control of the data and clock +// lines on the IBM PPC 405 IIC controller and set the either high or low, +// similar to a GPIO pin. +// +static int algo_control(struct i2c_adapter *adapter, + unsigned int cmd, unsigned long arg) +{ + struct iic_regs *iic; + struct i2c_algo_iic_data *adap = adapter->algo_data; + struct iic_ibm *adap_priv_data = adap->data; + int ret=0; + int lines; + iic = (struct iic_regs *) adap_priv_data->iic_base; + + lines = iic_inb(adap, iic->directcntl); + + if (cmd == IICO_I2C_SDAHIGH) { + lines = lines & 0x01; + if( lines ) lines = 0x04; + else lines = 0; + iic_outb(adap, iic->directcntl,(0x08|lines)); + } + else if (cmd == IICO_I2C_SDALOW) { + lines = lines & 0x01; + if( lines ) lines = 0x04; + else lines = 0; + iic_outb(adap, iic->directcntl,(0x00|lines)); + } + else if (cmd == IICO_I2C_SCLHIGH) { + lines = lines & 0x02; + if( lines ) lines = 0x08; + else lines = 0; + iic_outb(adap, iic->directcntl,(0x04|lines)); + } + else if (cmd == IICO_I2C_SCLLOW) { + lines = lines & 0x02; + if( lines ) lines = 0x08; + else lines = 0; + iic_outb(adap, iic->directcntl,(0x00|lines)); + } + else if (cmd == IICO_I2C_LINEREAD) { + ret = lines; + } + return ret; +} + + +static u32 iic_func(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR | + I2C_FUNC_PROTOCOL_MANGLING; +} + + +/* -----exported algorithm data: ------------------------------------- */ + +static struct i2c_algorithm iic_algo = { + "IBM on-chip IIC algorithm", + I2C_ALGO_OCP, + iic_xfer, + NULL, + NULL, /* slave_xmit */ + NULL, /* slave_recv */ + algo_control, /* ioctl */ + iic_func, /* functionality */ +}; + + +/* + * registering functions to load algorithms at runtime + */ + + +// +// Description: Register bus structure +// +int i2c_iic_add_bus(struct i2c_adapter *adap) +{ + struct i2c_algo_iic_data *iic_adap = adap->algo_data; + + DEB2(printk(KERN_DEBUG "i2c-algo-iic.o: hw routines for %s registered.\n", + adap->name)); + + /* register new adapter to i2c module... */ + + adap->id |= iic_algo.id; + adap->algo = &iic_algo; + + adap->timeout = 100; /* default values, should */ + adap->retries = 3; /* be replaced by defines */ + +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif + + iic_init(iic_adap); + i2c_add_adapter(adap); + + /* scan bus */ + /* By default scanning the bus is turned off. */ + if (iic_scan) { + printk(KERN_INFO " i2c-algo-iic.o: scanning bus %s.\n", + adap->name); + } + return 0; +} + + +// +// Done +// +int i2c_iic_del_bus(struct i2c_adapter *adap) +{ + int res; + if ((res = i2c_del_adapter(adap)) < 0) + return res; + DEB2(printk(KERN_DEBUG "i2c-algo-iic.o: adapter unregistered: %s\n",adap->name)); + +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif + return 0; +} + + +// +// Done +// +int __init i2c_algo_iic_init (void) +{ + printk(KERN_INFO "IBM On-chip iic (i2c) algorithm module 2002.27.03\n"); + return 0; +} + + +void i2c_algo_iic_exit(void) +{ + return; +} + + +EXPORT_SYMBOL(i2c_iic_add_bus); +EXPORT_SYMBOL(i2c_iic_del_bus); + +// +// The MODULE_* macros resolve to nothing if MODULES is not defined +// when this file is compiled. +// +MODULE_AUTHOR("MontaVista Software "); +MODULE_DESCRIPTION("PPC 405 iic algorithm"); + +MODULE_PARM(iic_test, "i"); +MODULE_PARM(iic_scan, "i"); +MODULE_PARM(i2c_debug,"i"); + +MODULE_PARM_DESC(iic_test, "Test if the I2C bus is available"); +MODULE_PARM_DESC(iic_scan, "Scan for active chips on the bus"); +MODULE_PARM_DESC(i2c_debug, + "debug level - 0 off; 1 normal; 2,3 more verbose; 9 iic-protocol"); + + +module_init(i2c_algo_iic_init); +module_exit(i2c_algo_iic_exit); diff -Nru a/drivers/i2c/i2c-algo-ibm_ocp.h b/drivers/i2c/i2c-algo-ibm_ocp.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-algo-ibm_ocp.h Fri Aug 16 14:35:01 2002 @@ -0,0 +1,55 @@ +/* ------------------------------------------------------------------------- */ +/* i2c-algo-ibm_ocp.h i2c driver algorithms for IBM PPC 405 IIC adapters */ +/* ------------------------------------------------------------------------- */ +/* Copyright (C) 1995-97 Simon G. Vogl + 1998-99 Hans Berglund + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* ------------------------------------------------------------------------- */ + +/* With some changes from Kyösti Mälkki and even + Frodo Looijaard */ + +/* Modifications by MontaVista Software, August 2000 + Changes made to support the IIC peripheral on the IBM PPC 405 */ + +#ifndef I2C_ALGO_IIC_H +#define I2C_ALGO_IIC_H 1 + +/* --- Defines for pcf-adapters --------------------------------------- */ +#include + +struct i2c_algo_iic_data { + struct iic_regs *data; /* private data for lolevel routines */ + void (*setiic) (void *data, int ctl, int val); + int (*getiic) (void *data, int ctl); + int (*getown) (void *data); + int (*getclock) (void *data); + void (*waitforpin) (void *data); + + /* local settings */ + int udelay; + int mdelay; + int timeout; +}; + + +#define I2C_IIC_ADAP_MAX 16 + + +int i2c_iic_add_bus(struct i2c_adapter *); +int i2c_iic_del_bus(struct i2c_adapter *); + +#endif /* I2C_ALGO_IIC_H */ diff -Nru a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c --- a/drivers/i2c/i2c-core.c Fri Aug 16 14:34:54 2002 +++ b/drivers/i2c/i2c-core.c Fri Aug 16 14:34:54 2002 @@ -18,9 +18,10 @@ /* ------------------------------------------------------------------------- */ /* With some changes from Kyösti Mälkki . - All SMBus-related things are written by Frodo Looijaard */ + All SMBus-related things are written by Frodo Looijaard + SMBus 2.0 support by Mark Studebaker */ -/* $Id: i2c-core.c,v 1.73 2002/03/03 17:37:44 mds Exp $ */ +/* $Id: i2c-core.c,v 1.83 2002/07/08 01:37:15 mds Exp $ */ #include #include @@ -371,7 +372,7 @@ struct i2c_client *client = adap->clients[j]; if (client != NULL && client->driver == driver) { - DEB2(printk("i2c-core.o: " + DEB2(printk(KERN_DEBUG "i2c-core.o: " "detaching client %s:\n", client->name)); if ((res = driver-> @@ -1002,6 +1003,123 @@ /* The SMBus parts */ +#define POLY (0x1070U << 3) +static u8 +crc8(u16 data) +{ + int i; + + for(i = 0; i < 8; i++) { + if (data & 0x8000) + data = data ^ POLY; + data = data << 1; + } + return (u8)(data >> 8); +} + +/* CRC over count bytes in the first array plus the bytes in the rest + array if it is non-null. rest[0] is the (length of rest) - 1 + and is included. */ +u8 i2c_smbus_partial_pec(u8 crc, int count, u8 *first, u8 *rest) +{ + int i; + + for(i = 0; i < count; i++) + crc = crc8((crc ^ first[i]) << 8); + if(rest != NULL) + for(i = 0; i <= rest[0]; i++) + crc = crc8((crc ^ rest[i]) << 8); + return crc; +} + +u8 i2c_smbus_pec(int count, u8 *first, u8 *rest) +{ + return i2c_smbus_partial_pec(0, count, first, rest); +} + +/* Returns new "size" (transaction type) + Note that we convert byte to byte_data and byte_data to word_data + rather than invent new xxx_PEC transactions. */ +int i2c_smbus_add_pec(u16 addr, u8 command, int size, + union i2c_smbus_data *data) +{ + u8 buf[3]; + + buf[0] = addr << 1; + buf[1] = command; + switch(size) { + case I2C_SMBUS_BYTE: + data->byte = i2c_smbus_pec(2, buf, NULL); + size = I2C_SMBUS_BYTE_DATA; + break; + case I2C_SMBUS_BYTE_DATA: + buf[2] = data->byte; + data->word = buf[2] || + (i2c_smbus_pec(3, buf, NULL) << 8); + size = I2C_SMBUS_WORD_DATA; + break; + case I2C_SMBUS_WORD_DATA: + /* unsupported */ + break; + case I2C_SMBUS_BLOCK_DATA: + data->block[data->block[0] + 1] = + i2c_smbus_pec(2, buf, data->block); + size = I2C_SMBUS_BLOCK_DATA_PEC; + break; + } + return size; +} + +int i2c_smbus_check_pec(u16 addr, u8 command, int size, u8 partial, + union i2c_smbus_data *data) +{ + u8 buf[3], rpec, cpec; + + buf[1] = command; + switch(size) { + case I2C_SMBUS_BYTE_DATA: + buf[0] = (addr << 1) | 1; + cpec = i2c_smbus_pec(2, buf, NULL); + rpec = data->byte; + break; + case I2C_SMBUS_WORD_DATA: + buf[0] = (addr << 1) | 1; + buf[2] = data->word & 0xff; + cpec = i2c_smbus_pec(3, buf, NULL); + rpec = data->word >> 8; + break; + case I2C_SMBUS_WORD_DATA_PEC: + /* unsupported */ + cpec = rpec = 0; + break; + case I2C_SMBUS_PROC_CALL_PEC: + /* unsupported */ + cpec = rpec = 0; + break; + case I2C_SMBUS_BLOCK_DATA_PEC: + buf[0] = (addr << 1); + buf[2] = (addr << 1) | 1; + cpec = i2c_smbus_pec(3, buf, data->block); + rpec = data->block[data->block[0] + 1]; + break; + case I2C_SMBUS_BLOCK_PROC_CALL_PEC: + buf[0] = (addr << 1) | 1; + rpec = i2c_smbus_partial_pec(partial, 1, + buf, data->block); + cpec = data->block[data->block[0] + 1]; + break; + default: + cpec = rpec = 0; + break; + } + if(rpec != cpec) { + DEB(printk(KERN_DEBUG "i2c-core.o: Bad PEC 0x%02x vs. 0x%02x\n", + rpec, cpec)); + return -1; + } + return 0; +} + extern s32 i2c_smbus_write_quick(struct i2c_client * client, u8 value) { return i2c_smbus_xfer(client->adapter,client->addr,client->flags, @@ -1020,8 +1138,9 @@ extern s32 i2c_smbus_write_byte(struct i2c_client * client, u8 value) { + union i2c_smbus_data data; /* only for PEC */ return i2c_smbus_xfer(client->adapter,client->addr,client->flags, - I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,NULL); + I2C_SMBUS_WRITE,value, I2C_SMBUS_BYTE,&data); } extern s32 i2c_smbus_read_byte_data(struct i2c_client * client, u8 command) @@ -1099,8 +1218,8 @@ { union i2c_smbus_data data; int i; - if (length > 32) - length = 32; + if (length > I2C_SMBUS_BLOCK_MAX) + length = I2C_SMBUS_BLOCK_MAX; for (i = 1; i <= length; i++) data.block[i] = values[i-1]; data.block[0] = length; @@ -1110,6 +1229,26 @@ } /* Returns the number of read bytes */ +extern s32 i2c_smbus_block_process_call(struct i2c_client * client, + u8 command, u8 length, u8 *values) +{ + union i2c_smbus_data data; + int i; + if (length > I2C_SMBUS_BLOCK_MAX - 1) + return -1; + data.block[0] = length; + for (i = 1; i <= length; i++) + data.block[i] = values[i-1]; + if(i2c_smbus_xfer(client->adapter,client->addr,client->flags, + I2C_SMBUS_WRITE, command, + I2C_SMBUS_BLOCK_PROC_CALL, &data)) + return -1; + for (i = 1; i <= data.block[0]; i++) + values[i-1] = data.block[i]; + return data.block[0]; +} + +/* Returns the number of read bytes */ extern s32 i2c_smbus_read_i2c_block_data(struct i2c_client * client, u8 command, u8 *values) { @@ -1131,8 +1270,8 @@ { union i2c_smbus_data data; int i; - if (length > 32) - length = 32; + if (length > I2C_SMBUS_I2C_BLOCK_MAX) + length = I2C_SMBUS_I2C_BLOCK_MAX; for (i = 1; i <= length; i++) data.block[i] = values[i-1]; data.block[0] = length; @@ -1194,34 +1333,43 @@ break; case I2C_SMBUS_PROC_CALL: num = 2; /* Special case */ + read_write = I2C_SMBUS_READ; msg[0].len = 3; msg[1].len = 2; msgbuf0[1] = data->word & 0xff; msgbuf0[2] = (data->word >> 8) & 0xff; break; case I2C_SMBUS_BLOCK_DATA: + case I2C_SMBUS_BLOCK_DATA_PEC: if (read_write == I2C_SMBUS_READ) { - printk(KERN_ERR "i2c-core.o: Block read not supported under " - "I2C emulation!\n"); - return -1; + printk(KERN_ERR "i2c-core.o: Block read not supported " + "under I2C emulation!\n"); + return -1; } else { msg[0].len = data->block[0] + 2; - if (msg[0].len > 34) { + if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) { printk(KERN_ERR "i2c-core.o: smbus_access called with " "invalid block write size (%d)\n", data->block[0]); return -1; } + if(size == I2C_SMBUS_BLOCK_DATA_PEC) + (msg[0].len)++; for (i = 1; i <= msg[0].len; i++) msgbuf0[i] = data->block[i-1]; } break; + case I2C_SMBUS_BLOCK_PROC_CALL: + case I2C_SMBUS_BLOCK_PROC_CALL_PEC: + printk(KERN_ERR "i2c-core.o: Block process call not supported " + "under I2C emulation!\n"); + return -1; case I2C_SMBUS_I2C_BLOCK_DATA: if (read_write == I2C_SMBUS_READ) { - msg[1].len = 32; + msg[1].len = I2C_SMBUS_I2C_BLOCK_MAX; } else { msg[0].len = data->block[0] + 2; - if (msg[0].len > 34) { + if (msg[0].len > I2C_SMBUS_I2C_BLOCK_MAX + 2) { printk("i2c-core.o: i2c_smbus_xfer_emulated called with " "invalid block write size (%d)\n", data->block[0]); @@ -1254,8 +1402,8 @@ break; case I2C_SMBUS_I2C_BLOCK_DATA: /* fixed at 32 for now */ - data->block[0] = 32; - for (i = 0; i < 32; i++) + data->block[0] = I2C_SMBUS_I2C_BLOCK_MAX; + for (i = 0; i < I2C_SMBUS_I2C_BLOCK_MAX; i++) data->block[i+1] = msgbuf1[i]; break; } @@ -1268,7 +1416,29 @@ union i2c_smbus_data * data) { s32 res; - flags = flags & I2C_M_TEN; + int swpec = 0; + u8 partial = 0; + + flags &= I2C_M_TEN | I2C_CLIENT_PEC; + if((flags & I2C_CLIENT_PEC) && + !(i2c_check_functionality(adapter, I2C_FUNC_SMBUS_HWPEC_CALC))) { + swpec = 1; + if(read_write == I2C_SMBUS_READ && + size == I2C_SMBUS_BLOCK_DATA) + size = I2C_SMBUS_BLOCK_DATA_PEC; + else if(size == I2C_SMBUS_PROC_CALL) + size = I2C_SMBUS_PROC_CALL_PEC; + else if(size == I2C_SMBUS_BLOCK_PROC_CALL) { + i2c_smbus_add_pec(addr, command, + I2C_SMBUS_BLOCK_DATA, data); + partial = data->block[data->block[0] + 1]; + size = I2C_SMBUS_BLOCK_PROC_CALL_PEC; + } else if(read_write == I2C_SMBUS_WRITE && + size != I2C_SMBUS_QUICK && + size != I2C_SMBUS_I2C_BLOCK_DATA) + size = i2c_smbus_add_pec(addr, command, size, data); + } + if (adapter->algo->smbus_xfer) { I2C_LOCK(adapter); res = adapter->algo->smbus_xfer(adapter,addr,flags,read_write, @@ -1277,6 +1447,14 @@ } else res = i2c_smbus_xfer_emulated(adapter,addr,flags,read_write, command,size,data); + + if(res >= 0 && swpec && + size != I2C_SMBUS_QUICK && size != I2C_SMBUS_I2C_BLOCK_DATA && + (read_write == I2C_SMBUS_READ || size == I2C_SMBUS_PROC_CALL_PEC || + size == I2C_SMBUS_BLOCK_PROC_CALL_PEC)) { + if(i2c_smbus_check_pec(addr, command, size, partial, data)) + return -1; + } return res; } diff -Nru a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c --- a/drivers/i2c/i2c-dev.c Fri Aug 16 14:34:59 2002 +++ b/drivers/i2c/i2c-dev.c Fri Aug 16 14:34:59 2002 @@ -28,7 +28,7 @@ /* The devfs code is contributed by Philipp Matthias Hahn */ -/* $Id: i2c-dev.c,v 1.44 2001/11/19 18:45:02 mds Exp $ */ +/* $Id: i2c-dev.c,v 1.46 2002/07/06 02:07:39 mds Exp $ */ #include #include @@ -236,6 +236,12 @@ else client->flags &= ~I2C_M_TEN; return 0; + case I2C_PEC: + if (arg) + client->flags |= I2C_CLIENT_PEC; + else + client->flags &= ~I2C_CLIENT_PEC; + return 0; case I2C_FUNCS: funcs = i2c_get_functionality(client->adapter); return (copy_to_user((unsigned long *)arg,&funcs, @@ -312,7 +318,8 @@ (data_arg.size != I2C_SMBUS_WORD_DATA) && (data_arg.size != I2C_SMBUS_PROC_CALL) && (data_arg.size != I2C_SMBUS_BLOCK_DATA) && - (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA)) { + (data_arg.size != I2C_SMBUS_I2C_BLOCK_DATA) && + (data_arg.size != I2C_SMBUS_BLOCK_PROC_CALL)) { #ifdef DEBUG printk(KERN_DEBUG "i2c-dev.o: size out of range (%x) in ioctl I2C_SMBUS.\n", data_arg.size); @@ -355,10 +362,11 @@ else if ((data_arg.size == I2C_SMBUS_WORD_DATA) || (data_arg.size == I2C_SMBUS_PROC_CALL)) datasize = sizeof(data_arg.data->word); - else /* size == I2C_SMBUS_BLOCK_DATA */ + else /* size == smbus block, i2c block, or block proc. call */ datasize = sizeof(data_arg.data->block); if ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || (data_arg.read_write == I2C_SMBUS_WRITE)) { if (copy_from_user(&temp, data_arg.data, datasize)) return -EFAULT; @@ -367,6 +375,7 @@ data_arg.read_write, data_arg.command,data_arg.size,&temp); if (! res && ((data_arg.size == I2C_SMBUS_PROC_CALL) || + (data_arg.size == I2C_SMBUS_BLOCK_PROC_CALL) || (data_arg.read_write == I2C_SMBUS_READ))) { if (copy_to_user(data_arg.data, &temp, datasize)) return -EFAULT; @@ -534,6 +543,8 @@ } return 0; } + +EXPORT_NO_SYMBOLS; #ifdef MODULE diff -Nru a/drivers/i2c/i2c-elektor.c b/drivers/i2c/i2c-elektor.c --- a/drivers/i2c/i2c-elektor.c Fri Aug 16 14:34:52 2002 +++ b/drivers/i2c/i2c-elektor.c Fri Aug 16 14:34:52 2002 @@ -55,11 +55,7 @@ in some functions, called from the algo-pcf module. Sometimes it's need to be rewriten - but for now just remove this for simpler reading */ -#if (LINUX_VERSION_CODE < 0x020301) -static struct wait_queue *pcf_wait = NULL; -#else static wait_queue_head_t pcf_wait; -#endif static int pcf_pending; /* ----- global defines ----------------------------------------------- */ @@ -280,9 +276,7 @@ base = DEFAULT_BASE; } -#if (LINUX_VERSION_CODE >= 0x020301) init_waitqueue_head(&pcf_wait); -#endif if (pcf_isa_init() == 0) { if (i2c_pcf_add_bus(&pcf_isa_ops) < 0) { pcf_isa_exit(); @@ -297,6 +291,7 @@ return 0; } +EXPORT_NO_SYMBOLS; #ifdef MODULE MODULE_AUTHOR("Hans Berglund "); diff -Nru a/drivers/i2c/i2c-frodo.c b/drivers/i2c/i2c-frodo.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-frodo.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,118 @@ + +/* + * linux/drivers/i2c/i2c-frodo.c + * + * Author: Abraham van der Merwe + * + * An I2C adapter driver for the 2d3D, Inc. StrongARM SA-1110 + * Development board (Frodo). + * + * This source code is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + */ + +#include +#include +#include + +#include +#include +#include + +#include + +#include +#include + +static void frodo_setsda (void *data,int state) +{ + if (state) + FRODO_CPLD_I2C |= FRODO_I2C_SDA_OUT; + else + FRODO_CPLD_I2C &= ~FRODO_I2C_SDA_OUT; +} + +static void frodo_setscl (void *data,int state) +{ + if (state) + FRODO_CPLD_I2C |= FRODO_I2C_SCL_OUT; + else + FRODO_CPLD_I2C &= ~FRODO_I2C_SCL_OUT; +} + +static int frodo_getsda (void *data) +{ + return ((FRODO_CPLD_I2C & FRODO_I2C_SDA_IN) != 0); +} + +static int frodo_getscl (void *data) +{ + return ((FRODO_CPLD_I2C & FRODO_I2C_SCL_IN) != 0); +} + +static struct i2c_algo_bit_data bit_frodo_data = { + setsda: frodo_setsda, + setscl: frodo_setscl, + getsda: frodo_getsda, + getscl: frodo_getscl, + udelay: 80, + mdelay: 80, + timeout: 100 +}; + +static int frodo_client_register (struct i2c_client *client) +{ + return (0); +} + +static int frodo_client_unregister (struct i2c_client *client) +{ + return (0); +} + +static void frodo_inc_use (struct i2c_adapter *adapter) +{ + MOD_INC_USE_COUNT; +} + +static void frodo_dec_use (struct i2c_adapter *adapter) +{ + MOD_DEC_USE_COUNT; +} + +static struct i2c_adapter frodo_ops = { + name: "Frodo adapter driver", + id: I2C_HW_B_FRODO, + algo: NULL, + algo_data: &bit_frodo_data, + inc_use: frodo_inc_use, + dec_use: frodo_dec_use, + client_register: frodo_client_register, + client_unregister: frodo_client_unregister +}; + +static int __init i2c_frodo_init (void) +{ + return (i2c_bit_add_bus (&frodo_ops)); +} + +EXPORT_NO_SYMBOLS; + +static void __exit i2c_frodo_exit (void) +{ + i2c_bit_del_bus (&frodo_ops); +} + +MODULE_AUTHOR ("Abraham van der Merwe "); +MODULE_DESCRIPTION ("I2C-Bus adapter routines for Frodo"); + +#ifdef MODULE_LICENSE +MODULE_LICENSE ("GPL"); +#endif /* #ifdef MODULE_LICENSE */ + +EXPORT_NO_SYMBOLS; + +module_init (i2c_frodo_init); +module_exit (i2c_frodo_exit); + diff -Nru a/drivers/i2c/i2c-proc.c b/drivers/i2c/i2c-proc.c --- a/drivers/i2c/i2c-proc.c Fri Aug 16 14:34:58 2002 +++ b/drivers/i2c/i2c-proc.c Fri Aug 16 14:34:58 2002 @@ -60,6 +60,7 @@ static struct ctl_table_header *i2c_entries[SENSORS_ENTRY_MAX]; static struct i2c_client *i2c_clients[SENSORS_ENTRY_MAX]; +static unsigned short i2c_inodes[SENSORS_ENTRY_MAX]; static ctl_table sysctl_table[] = { {CTL_DEV, "dev", NULL, 0, 0555}, @@ -172,7 +173,7 @@ printk(KERN_ERR "i2c-proc.o: error: sysctl interface not supported by kernel!\n"); kfree(new_table); kfree(name); - return -ENOMEM; + return -EPERM; } i2c_entries[id - 256] = new_header; @@ -188,6 +189,8 @@ return id; } #endif /* DEBUG */ + i2c_inodes[id - 256] = + new_header->ctl_table->child->child->de->low_ino; new_header->ctl_table->child->child->de->owner = controlling_mod; return id; @@ -208,6 +211,49 @@ i2c_entries[id] = NULL; i2c_clients[id] = NULL; } +} + +/* Monitor access for /proc/sys/dev/sensors; make unloading i2c-proc.o + impossible if some process still uses it or some file in it */ +void i2c_fill_inode(struct inode *inode, int fill) +{ + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +} + +/* Monitor access for /proc/sys/dev/sensors/ directories; make unloading + the corresponding module impossible if some process still uses it or + some file in it */ +void i2c_dir_fill_inode(struct inode *inode, int fill) +{ + int i; + struct i2c_client *client; + +#ifdef DEBUG + if (!inode) { + printk(KERN_ERR "i2c-proc.o: Warning: inode NULL in fill_inode()\n"); + return; + } +#endif /* def DEBUG */ + + for (i = 0; i < SENSORS_ENTRY_MAX; i++) + if (i2c_clients[i] + && (i2c_inodes[i] == inode->i_ino)) break; +#ifdef DEBUG + if (i == SENSORS_ENTRY_MAX) { + printk + (KERN_ERR "i2c-proc.o: Warning: inode (%ld) not found in fill_inode()\n", + inode->i_ino); + return; + } +#endif /* def DEBUG */ + client = i2c_clients[i]; + if (fill) + client->driver->inc_use(client); + else + client->driver->dec_use(client); } int i2c_proc_chips(ctl_table * ctl, int write, struct file *filp, diff -Nru a/drivers/i2c/i2c-rpx.c b/drivers/i2c/i2c-rpx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/i2c/i2c-rpx.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,136 @@ +/* + * Embedded Planet RPX Lite MPC8xx CPM I2C interface. + * Copyright (c) 1999 Dan Malek (dmalek@jlc.net). + * + * moved into proper i2c interface; + * Brad Parker (brad@heeltoe.com) + * + * RPX lite specific parts of the i2c interface + * Update: There actually isn't anything RPXLite-specific about this module. + * This should work for most any 8xx board. The console messages have been + * changed to eliminate RPXLite references. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +static void +rpx_iic_init(struct i2c_algo_8xx_data *data) +{ + volatile cpm8xx_t *cp; + volatile immap_t *immap; + + cp = cpmp; /* Get pointer to Communication Processor */ + immap = (immap_t *)IMAP_ADDR; /* and to internal registers */ + + data->iip = (iic_t *)&cp->cp_dparam[PROFF_IIC]; + + /* Check for and use a microcode relocation patch. + */ + if ((data->reloc = data->iip->iic_rpbase)) + data->iip = (iic_t *)&cp->cp_dpmem[data->iip->iic_rpbase]; + + data->i2c = (i2c8xx_t *)&(immap->im_i2c); + data->cp = cp; + + /* Initialize Port B IIC pins. + */ + cp->cp_pbpar |= 0x00000030; + cp->cp_pbdir |= 0x00000030; + cp->cp_pbodr |= 0x00000030; + + /* Allocate space for two transmit and two receive buffer + * descriptors in the DP ram. + */ + data->dp_addr = m8xx_cpm_dpalloc(sizeof(cbd_t) * 4); + + /* ptr to i2c area */ + data->i2c = (i2c8xx_t *)&(((immap_t *)IMAP_ADDR)->im_i2c); +} + +static int rpx_install_isr(int irq, void (*func)(void *, void *), void *data) +{ + /* install interrupt handler */ + cpm_install_handler(irq, (void (*)(void *, struct pt_regs *)) func, data); + + return 0; +} + +static int rpx_reg(struct i2c_client *client) +{ + return 0; +} + +static int rpx_unreg(struct i2c_client *client) +{ + return 0; +} + +static void rpx_inc_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +static void rpx_dec_use(struct i2c_adapter *adap) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +static struct i2c_algo_8xx_data rpx_data = { + setisr: rpx_install_isr +}; + + +static struct i2c_adapter rpx_ops = { + "m8xx", + I2C_HW_MPC8XX_EPON, + NULL, + &rpx_data, + rpx_inc_use, + rpx_dec_use, + rpx_reg, + rpx_unreg, +}; + +int __init i2c_rpx_init(void) +{ + printk("i2c-rpx.o: i2c MPC8xx module version %s (%s)\n", I2C_VERSION, I2C_DATE); + + /* reset hardware to sane state */ + rpx_iic_init(&rpx_data); + + if (i2c_8xx_add_bus(&rpx_ops) < 0) { + printk("i2c-rpx: Unable to register with I2C\n"); + return -ENODEV; + } + + return 0; +} + +void __exit i2c_rpx_exit(void) +{ + i2c_8xx_del_bus(&rpx_ops); +} + +#ifdef MODULE +MODULE_AUTHOR("Dan Malek "); +MODULE_DESCRIPTION("I2C-Bus adapter routines for MPC8xx boards"); + +module_init(i2c_rpx_init); +module_exit(i2c_rpx_exit); +#endif + diff -Nru a/drivers/ide/Config.in b/drivers/ide/Config.in --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/Config.in Fri Aug 16 14:35:01 2002 @@ -0,0 +1,204 @@ +# +# IDE ATA ATAPI Block device driver configuration +# +# Andre Hedrick +# +mainmenu_option next_comment +comment 'IDE, ATA and ATAPI Block devices' + +dep_tristate 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE $CONFIG_IDE +comment 'Please see Documentation/ide.txt for help/info on IDE drives' +if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then + + dep_tristate ' Include IDE/ATA-2 DISK support' CONFIG_BLK_DEV_IDEDISK $CONFIG_BLK_DEV_IDE + dep_mbool ' Use multi-mode by default' CONFIG_IDEDISK_MULTI_MODE $CONFIG_BLK_DEV_IDEDISK + dep_mbool ' Auto-Geometry Resizing support' CONFIG_IDEDISK_STROKE $CONFIG_BLK_DEV_IDEDISK + + define_bool CONFIG_BLK_DEV_IDEDISK_VENDOR n + dep_mbool ' Fujitsu Vendor Specific' CONFIG_BLK_DEV_IDEDISK_FUJITSU $CONFIG_BLK_DEV_IDEDISK_VENDOR + dep_mbool ' IBM Vendor Specific' CONFIG_BLK_DEV_IDEDISK_IBM $CONFIG_BLK_DEV_IDEDISK_VENDOR + dep_mbool ' Maxtor Vendor Specific' CONFIG_BLK_DEV_IDEDISK_MAXTOR $CONFIG_BLK_DEV_IDEDISK_VENDOR + dep_mbool ' Quantum Vendor Specific' CONFIG_BLK_DEV_IDEDISK_QUANTUM $CONFIG_BLK_DEV_IDEDISK_VENDOR + dep_mbool ' Seagate Vendor Specific' CONFIG_BLK_DEV_IDEDISK_SEAGATE $CONFIG_BLK_DEV_IDEDISK_VENDOR + dep_mbool ' Western Digital Vendor Specific' CONFIG_BLK_DEV_IDEDISK_WD $CONFIG_BLK_DEV_IDEDISK_VENDOR + + define_bool CONFIG_BLK_DEV_COMMERIAL n + dep_mbool ' TiVo Commerial Application Specific' CONFIG_BLK_DEV_TIVO $CONFIG_BLK_DEV_COMMERIAL + + dep_tristate ' PCMCIA IDE support' CONFIG_BLK_DEV_IDECS $CONFIG_BLK_DEV_IDE $CONFIG_PCMCIA + dep_tristate ' Include IDE/ATAPI CDROM support' CONFIG_BLK_DEV_IDECD $CONFIG_BLK_DEV_IDE + dep_tristate ' Include IDE/ATAPI TAPE support' CONFIG_BLK_DEV_IDETAPE $CONFIG_BLK_DEV_IDE + dep_tristate ' Include IDE/ATAPI FLOPPY support' CONFIG_BLK_DEV_IDEFLOPPY $CONFIG_BLK_DEV_IDE + dep_tristate ' SCSI emulation support' CONFIG_BLK_DEV_IDESCSI $CONFIG_BLK_DEV_IDE $CONFIG_SCSI + + bool ' IDE Taskfile Access' CONFIG_IDE_TASK_IOCTL +# bool ' IDE Taskfile IO' CONFIG_IDE_TASKFILE_IO + + comment 'IDE chipset support/bugfixes' + if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then + dep_bool ' CMD640 chipset bugfix/support' CONFIG_BLK_DEV_CMD640 $CONFIG_X86 + dep_bool ' CMD640 enhanced support' CONFIG_BLK_DEV_CMD640_ENHANCED $CONFIG_BLK_DEV_CMD640 + dep_bool ' ISA-PNP EIDE support' CONFIG_BLK_DEV_ISAPNP $CONFIG_ISAPNP + if [ "$CONFIG_PCI" = "y" ]; then + dep_bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 $CONFIG_X86 + define_bool CONFIG_BLK_DEV_IDEPCI y + if [ "$CONFIG_BLK_DEV_IDEPCI" = "y" ]; then + bool ' Sharing PCI IDE interrupts support' CONFIG_IDEPCI_SHARE_IRQ + bool ' Generic PCI bus-master DMA support' CONFIG_BLK_DEV_IDEDMA_PCI + bool ' Boot off-board chipsets first support' CONFIG_BLK_DEV_OFFBOARD + dep_bool ' Force enable legacy 2.0.X HOSTS to use DMA' CONFIG_BLK_DEV_IDEDMA_FORCED $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' Use PCI DMA by default when available' CONFIG_IDEDMA_PCI_AUTO $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' Enable DMA only for disks ' CONFIG_IDEDMA_ONLYDISK $CONFIG_IDEDMA_PCI_AUTO + define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' ATA Work(s) In Progress (EXPERIMENTAL)' CONFIG_IDEDMA_PCI_WIP $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL + dep_bool ' Attempt to HACK around Chipsets that TIMEOUT (WIP)' CONFIG_BLK_DEV_IDEDMA_TIMEOUT $CONFIG_IDEDMA_PCI_WIP + dep_bool ' Good-Bad DMA Model-Firmware (WIP)' CONFIG_IDEDMA_NEW_DRIVE_LISTINGS $CONFIG_IDEDMA_PCI_WIP + dep_bool ' AEC62XX chipset support' CONFIG_BLK_DEV_AEC62XX $CONFIG_BLK_DEV_IDEDMA_PCI + dep_mbool ' AEC62XX Tuning support' CONFIG_AEC62XX_TUNING $CONFIG_BLK_DEV_AEC62XX + dep_bool ' ALI M15x3 chipset support' CONFIG_BLK_DEV_ALI15X3 $CONFIG_BLK_DEV_IDEDMA_PCI + dep_mbool ' ALI M15x3 WDC support (DANGEROUS)' CONFIG_WDC_ALI15X3 $CONFIG_BLK_DEV_ALI15X3 + dep_bool ' AMD Viper support' CONFIG_BLK_DEV_AMD74XX $CONFIG_BLK_DEV_IDEDMA_PCI + dep_mbool ' AMD Viper ATA-66 Override (WIP)' CONFIG_AMD74XX_OVERRIDE $CONFIG_BLK_DEV_AMD74XX $CONFIG_IDEDMA_PCI_WIP + dep_bool ' CMD64X chipset support' CONFIG_BLK_DEV_CMD64X $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' CMD680 chipset tuning support' CONFIG_BLK_DEV_CMD680 $CONFIG_BLK_DEV_CMD64X + dep_bool ' CY82C693 chipset support' CONFIG_BLK_DEV_CY82C693 $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' Cyrix CS5530 MediaGX chipset support' CONFIG_BLK_DEV_CS5530 $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' HPT34X chipset support' CONFIG_BLK_DEV_HPT34X $CONFIG_BLK_DEV_IDEDMA_PCI + dep_mbool ' HPT34X AUTODMA support (WIP)' CONFIG_HPT34X_AUTODMA $CONFIG_BLK_DEV_HPT34X $CONFIG_IDEDMA_PCI_WIP + dep_bool ' HPT366/368/370 chipset support' CONFIG_BLK_DEV_HPT366 $CONFIG_BLK_DEV_IDEDMA_PCI + if [ "$CONFIG_X86" = "y" -o "$CONFIG_IA64" = "y" ]; then + dep_mbool ' Intel PIIXn chipsets support' CONFIG_BLK_DEV_PIIX $CONFIG_BLK_DEV_IDEDMA_PCI + dep_mbool ' PIIXn Tuning support' CONFIG_PIIX_TUNING $CONFIG_BLK_DEV_PIIX $CONFIG_IDEDMA_PCI_AUTO + fi + if [ "$CONFIG_MIPS_ITE8172" = "y" -o "$CONFIG_MIPS_IVR" = "y" ]; then + dep_mbool ' IT8172 IDE support' CONFIG_BLK_DEV_IT8172 $CONFIG_BLK_DEV_IDEDMA_PCI + dep_mbool ' IT8172 IDE Tuning support' CONFIG_IT8172_TUNING $CONFIG_BLK_DEV_IT8172 $CONFIG_IDEDMA_PCI_AUTO + fi + dep_bool ' NS87415 chipset support (EXPERIMENTAL)' CONFIG_BLK_DEV_NS87415 $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_EXPERIMENTAL + dep_bool ' OPTi 82C621 chipset enhanced support (EXPERIMENTAL)' CONFIG_BLK_DEV_OPTI621 $CONFIG_EXPERIMENTAL + dep_bool ' Pacific Digital ADMA100 basic support' CONFIG_BLK_DEV_ADMA100 $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' PROMISE PDC202{46|62|65|67|68|69|70} support' CONFIG_BLK_DEV_PDC202XX $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' Special UDMA Feature' CONFIG_PDC202XX_BURST $CONFIG_BLK_DEV_PDC202XX + dep_bool ' Special FastTrak Feature' CONFIG_PDC202XX_FORCE $CONFIG_BLK_DEV_PDC202XX + dep_bool ' RZ1000 chipset bugfix/support' CONFIG_BLK_DEV_RZ1000 $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86 + dep_bool ' ServerWorks OSB4/CSB5/CSB6 chipsets support' CONFIG_BLK_DEV_SVWKS $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' SiS5513 chipset support' CONFIG_BLK_DEV_SIS5513 $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86 + dep_bool ' SLC90E66 chipset support' CONFIG_BLK_DEV_SLC90E66 $CONFIG_BLK_DEV_IDEDMA_PCI $CONFIG_X86 + dep_bool ' Tekram TRM290 chipset support' CONFIG_BLK_DEV_TRM290 $CONFIG_BLK_DEV_IDEDMA_PCI + dep_bool ' VIA82CXXX chipset support' CONFIG_BLK_DEV_VIA82CXXX $CONFIG_BLK_DEV_IDEDMA_PCI + if [ "$CONFIG_PPC" = "y" -o "$CONFIG_ARM" = "y" ]; then + bool ' Winbond SL82c105 support' CONFIG_BLK_DEV_SL82C105 + fi + fi + fi + if [ "$CONFIG_ALL_PPC" = "y" ]; then + bool ' Builtin PowerMac IDE support' CONFIG_BLK_DEV_IDE_PMAC + dep_bool ' PowerMac IDE DMA support' CONFIG_BLK_DEV_IDEDMA_PMAC $CONFIG_BLK_DEV_IDE_PMAC + dep_bool ' Use DMA by default' CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO $CONFIG_BLK_DEV_IDEDMA_PMAC + if [ "$CONFIG_BLK_DEV_IDE_PMAC" = "y" ]; then + define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_PMAC + fi + if [ "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" ]; then + define_bool CONFIG_BLK_DEV_IDEPCI $CONFIG_BLK_DEV_IDEDMA_PMAC + fi + fi + if [ "$CONFIG_SIBYTE_SWARM" = "y" ]; then + bool ' SWARM onboard IDE support' CONFIG_BLK_DEV_IDE_SWARM + fi + if [ "$CONFIG_ARCH_ACORN" = "y" ]; then + dep_bool ' ICS IDE interface support' CONFIG_BLK_DEV_IDE_ICSIDE $CONFIG_ARCH_ACORN + dep_bool ' ICS DMA support' CONFIG_BLK_DEV_IDEDMA_ICS $CONFIG_BLK_DEV_IDE_ICSIDE + dep_bool ' Use ICS DMA by default' CONFIG_IDEDMA_ICS_AUTO $CONFIG_BLK_DEV_IDEDMA_ICS + define_bool CONFIG_BLK_DEV_IDEDMA $CONFIG_BLK_DEV_IDEDMA_ICS + dep_bool ' RapIDE interface support' CONFIG_BLK_DEV_IDE_RAPIDE $CONFIG_ARCH_ACORN + fi + if [ "$CONFIG_AMIGA" = "y" ]; then + dep_bool ' Amiga Gayle IDE interface support' CONFIG_BLK_DEV_GAYLE $CONFIG_AMIGA + dep_mbool ' Amiga IDE Doubler support (EXPERIMENTAL)' CONFIG_BLK_DEV_IDEDOUBLER $CONFIG_BLK_DEV_GAYLE $CONFIG_EXPERIMENTAL + fi + if [ "$CONFIG_ZORRO" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_mbool ' Buddha/Catweasel/X-Surf IDE interface support (EXPERIMENTAL)' CONFIG_BLK_DEV_BUDDHA $CONFIG_ZORRO $CONFIG_EXPERIMENTAL + fi + if [ "$CONFIG_ATARI" = "y" ]; then + dep_bool ' Falcon IDE interface support' CONFIG_BLK_DEV_FALCON_IDE $CONFIG_ATARI + fi + if [ "$CONFIG_MAC" = "y" ]; then + dep_bool ' Macintosh Quadra/Powerbook IDE interface support' CONFIG_BLK_DEV_MAC_IDE $CONFIG_MAC + fi + if [ "$CONFIG_Q40" = "y" ]; then + dep_bool ' Q40/Q60 IDE interface support' CONFIG_BLK_DEV_Q40IDE $CONFIG_Q40 + fi + if [ "$CONFIG_8xx" = "y" ]; then + dep_bool ' MPC8xx IDE support' CONFIG_BLK_DEV_MPC8xx_IDE $CONFIG_8xx + fi + + if [ "$CONFIG_BLK_DEV_MPC8xx_IDE" = "y" ]; then + choice 'Type of MPC8xx IDE interface' \ + "8xx_PCCARD CONFIG_IDE_8xx_PCCARD \ + 8xx_DIRECT CONFIG_IDE_8xx_DIRECT \ + EXT_DIRECT CONFIG_IDE_EXT_DIRECT" 8xx_PCCARD + fi + + bool ' Other IDE chipset support' CONFIG_IDE_CHIPSETS + if [ "$CONFIG_IDE_CHIPSETS" = "y" ]; then + comment 'Note: most of these also require special kernel boot parameters' + bool ' Generic 4 drives/port support' CONFIG_BLK_DEV_4DRIVES + bool ' ALI M14xx support' CONFIG_BLK_DEV_ALI14XX + bool ' DTC-2278 support' CONFIG_BLK_DEV_DTC2278 + bool ' Holtek HT6560B support' CONFIG_BLK_DEV_HT6560B + if [ "$CONFIG_BLK_DEV_IDEDISK" = "y" -a "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' PROMISE DC4030 support (EXPERIMENTAL)' CONFIG_BLK_DEV_PDC4030 + fi + bool ' QDI QD65xx support' CONFIG_BLK_DEV_QD65XX + bool ' UMC-8672 support' CONFIG_BLK_DEV_UMC8672 + fi + fi +fi + +if [ "$CONFIG_IDEDMA_PCI_AUTO" = "y" -o \ + "$CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO" = "y" -o \ + "$CONFIG_IDEDMA_ICS_AUTO" = "y" ]; then + define_bool CONFIG_IDEDMA_AUTO y +else + define_bool CONFIG_IDEDMA_AUTO n +fi + +if [ "$CONFIG_BLK_DEV_IDEDMA_PCI" = "y" -o \ + "$CONFIG_BLK_DEV_IDEDMA_PMAC" = "y" -o \ + "$CONFIG_BLK_DEV_IDEDMA_ICS" = "y" ]; then + bool ' IGNORE word93 Validation BITS' CONFIG_IDEDMA_IVB +fi + +if [ "$CONFIG_BLK_DEV_TIVO" = "y" ]; then + define_bool CONFIG_DMA_NONPCI y +else + define_bool CONFIG_DMA_NONPCI n +fi + +if [ "$CONFIG_IDE_CHIPSETS" = "y" -o \ + "$CONFIG_BLK_DEV_AEC62XX" = "y" -o \ + "$CONFIG_BLK_DEV_ALI15X3" = "y" -o \ + "$CONFIG_BLK_DEV_AMD74XX" = "y" -o \ + "$CONFIG_BLK_DEV_CMD640" = "y" -o \ + "$CONFIG_BLK_DEV_CMD64X" = "y" -o \ + "$CONFIG_BLK_DEV_CS5530" = "y" -o \ + "$CONFIG_BLK_DEV_CY82C693" = "y" -o \ + "$CONFIG_BLK_DEV_HPT34X" = "y" -o \ + "$CONFIG_BLK_DEV_HPT366" = "y" -o \ + "$CONFIG_BLK_DEV_IDE_PMAC" = "y" -o \ + "$CONFIG_BLK_DEV_OPTI621" = "y" -o \ + "$CONFIG_BLK_DEV_SVWKS" = "y" -o \ + "$CONFIG_BLK_DEV_PDC202XX" = "y" -o \ + "$CONFIG_BLK_DEV_PIIX" = "y" -o \ + "$CONFIG_BLK_DEV_IT8172" = "y" -o \ + "$CONFIG_BLK_DEV_SIS5513" = "y" -o \ + "$CONFIG_BLK_DEV_SLC90E66" = "y" -o \ + "$CONFIG_BLK_DEV_SL82C105" = "y" -o \ + "$CONFIG_BLK_DEV_VIA82CXXX" = "y" -o \ + "$CONFIG_BLK_DEV_MPC8xx_IDE" = "y" ]; then + define_bool CONFIG_BLK_DEV_IDE_MODES y +else + define_bool CONFIG_BLK_DEV_IDE_MODES n +fi + +endmenu diff -Nru a/drivers/ide/Makefile b/drivers/ide/Makefile --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/Makefile Fri Aug 16 14:35:01 2002 @@ -0,0 +1,67 @@ +# +# Makefile for the kernel ata, atapi, and ide block device drivers. +# +# 12 September 2000, Bartlomiej Zolnierkiewicz +# Rewritten to use lists instead of if-statements. +# +# Note : at this point, these files are compiled on all systems. +# In the future, some of these should be built conditionally. +# +export-objs := ide-taskfile.o ide.o ide-probe.o ataraid.o + +obj-$(CONFIG_BLK_DEV_IDE) += ide-mod.o ide-probe-mod.o +obj-$(CONFIG_BLK_DEV_IDECS) += ide-cs.o +obj-$(CONFIG_BLK_DEV_IDEDISK) += ide-disk.o +obj-$(CONFIG_BLK_DEV_IDECD) += ide-cd.o +obj-$(CONFIG_BLK_DEV_IDETAPE) += ide-tape.o +obj-$(CONFIG_BLK_DEV_IDEFLOPPY) += ide-floppy.o + +obj-$(CONFIG_BLK_DEV_IT8172) += it8172.o + +ide-obj-$(CONFIG_BLK_DEV_ADMA100) += adma100.o +ide-obj-$(CONFIG_BLK_DEV_AEC62XX) += aec62xx.o +ide-obj-$(CONFIG_BLK_DEV_ALI14XX) += ali14xx.o +ide-obj-$(CONFIG_BLK_DEV_ALI15X3) += alim15x3.o +ide-obj-$(CONFIG_BLK_DEV_AMD74XX) += amd74xx.o +ide-obj-$(CONFIG_BLK_DEV_BUDDHA) += buddha.o +ide-obj-$(CONFIG_BLK_DEV_CMD640) += cmd640.o +ide-obj-$(CONFIG_BLK_DEV_CMD64X) += cmd64x.o +ide-obj-$(CONFIG_BLK_DEV_CS5530) += cs5530.o +ide-obj-$(CONFIG_BLK_DEV_CY82C693) += cy82c693.o +ide-obj-$(CONFIG_BLK_DEV_DTC2278) += dtc2278.o +ide-obj-$(CONFIG_BLK_DEV_FALCON_IDE) += falconide.o +ide-obj-$(CONFIG_BLK_DEV_GAYLE) += gayle.o +ide-obj-$(CONFIG_BLK_DEV_Q40IDE) += q40ide.o +ide-obj-$(CONFIG_BLK_DEV_HPT34X) += hpt34x.o +ide-obj-$(CONFIG_BLK_DEV_HPT366) += hpt366.o +ide-obj-$(CONFIG_BLK_DEV_HT6560B) += ht6560b.o +ide-obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o +ide-obj-$(CONFIG_BLK_DEV_IDEDMA_PCI) += ide-dma.o +ide-obj-$(CONFIG_BLK_DEV_MPC8xx_IDE) += ide-m8xx.o +ide-obj-$(CONFIG_BLK_DEV_IDEPCI) += ide-pci.o +ide-obj-$(CONFIG_BLK_DEV_ISAPNP) += ide-pnp.o +ide-obj-$(CONFIG_BLK_DEV_IDE_PMAC) += ide-pmac.o +ide-obj-$(CONFIG_BLK_DEV_IDE_SWARM) += ide-swarm.o +ide-obj-$(CONFIG_BLK_DEV_MAC_IDE) += macide.o +ide-obj-$(CONFIG_BLK_DEV_NS87415) += ns87415.o +ide-obj-$(CONFIG_BLK_DEV_OPTI621) += opti621.o +ide-obj-$(CONFIG_BLK_DEV_SVWKS) += serverworks.o +ide-obj-$(CONFIG_BLK_DEV_PDC202XX) += pdc202xx.o +ide-obj-$(CONFIG_BLK_DEV_PDC4030) += pdc4030.o +ide-obj-$(CONFIG_BLK_DEV_PIIX) += piix.o +ide-obj-$(CONFIG_BLK_DEV_QD65XX) += qd65xx.o +ide-obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o +ide-obj-$(CONFIG_BLK_DEV_RZ1000) += rz1000.o +ide-obj-$(CONFIG_BLK_DEV_SIS5513) += sis5513.o +ide-obj-$(CONFIG_BLK_DEV_SLC90E66) += slc90e66.o +ide-obj-$(CONFIG_BLK_DEV_SL82C105) += sl82c105.o +ide-obj-$(CONFIG_BLK_DEV_TRM290) += trm290.o +ide-obj-$(CONFIG_BLK_DEV_UMC8672) += umc8672.o +ide-obj-$(CONFIG_BLK_DEV_VIA82CXXX) += via82cxxx.o + +ide-obj-$(CONFIG_PROC_FS) += ide-proc.o + +ide-mod-objs := ide-taskfile.o ide.o $(ide-obj-y) +ide-probe-mod-objs := ide-probe.o ide-geometry.o + +include $(TOPDIR)/Rules.make diff -Nru a/drivers/ide/adma100.c b/drivers/ide/adma100.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/adma100.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,30 @@ +/* + * linux/drivers/ide/adma100.c -- basic support for Pacific Digital ADMA-100 boards + * + * Created 09 Apr 2002 by Mark Lord + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +void __init ide_init_adma100 (ide_hwif_t *hwif) +{ + u32 phy_admctl = pci_resource_start(hwif->pci_dev, 4) + 0x80 + (hwif->channel * 0x20); + void *v_admctl; + + hwif->autodma = 0; // not compatible with normal IDE DMA transfers + hwif->dma_base = 0; // disable DMA completely + hwif->io_ports[IDE_CONTROL_OFFSET] += 4; // chip needs offset of 6 instead of 2 + v_admctl = ioremap_nocache(phy_admctl, 1024); // map config regs, so we can turn on drive IRQs + *((unsigned short *)v_admctl) &= 3; // enable aIEN; preserve PIO mode + iounmap(v_admctl); // all done; unmap config regs +} diff -Nru a/drivers/ide/aec62xx.c b/drivers/ide/aec62xx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/aec62xx.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,670 @@ +/* + * linux/drivers/ide/aec62xx.c Version 0.11 March 27, 2002 + * + * Copyright (C) 1999-2002 Andre Hedrick + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "ide_modes.h" + +#define DISPLAY_AEC62XX_TIMINGS + +#ifndef HIGH_4 +#define HIGH_4(H) ((H)=(H>>4)) +#endif +#ifndef LOW_4 +#define LOW_4(L) ((L)=(L-((L>>4)<<4))) +#endif +#ifndef SPLIT_BYTE +#define SPLIT_BYTE(B,H,L) ((H)=(B>>4), (L)=(B-((B>>4)<<4))) +#endif +#ifndef MAKE_WORD +#define MAKE_WORD(W,HB,LB) ((W)=((HB<<8)+LB)) +#endif + + +#if defined(DISPLAY_AEC62XX_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +static int aec62xx_get_info(char *, char **, off_t, int); +extern int (*aec62xx_display_info)(char *, char **, off_t, int); /* ide-proc.c */ + +#define AEC_MAX_DEVS 5 + +static struct pci_dev *aec_devs[AEC_MAX_DEVS]; +static int n_aec_devs; + +#undef DEBUG_AEC_REGS + +static int aec62xx_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + char *chipset_nums[] = {"error", "error", "error", "error", + "error", "error", "850UF", "860", + "860R", "865", "865R", "error" }; +// char *modes_33[] = {}; +// char *modes_34[] = {}; + int i; + + for (i = 0; i < n_aec_devs; i++) { + struct pci_dev *dev = aec_devs[i]; + // u32 iobase = dev->resource[4].start; + u32 iobase = pci_resource_start(dev, 4); + u8 c0 = inb_p(iobase + 0x02); + u8 c1 = inb_p(iobase + 0x0a); + u8 art = 0; +#ifdef DEBUG_AEC_REGS + u8 uart = 0; +#endif /* DEBUG_AEC_REGS */ + + p += sprintf(p, "\nController: %d\n", i); + p += sprintf(p, "Chipset: AEC%s\n", chipset_nums[dev->device]); + + p += sprintf(p, "--------------- Primary Channel " + "---------------- Secondary Channel " + "-------------\n"); + (void) pci_read_config_byte(dev, 0x4a, &art); + p += sprintf(p, " %sabled ", + (art&0x02)?" en":"dis"); + p += sprintf(p, " %sabled\n", + (art&0x04)?" en":"dis"); + p += sprintf(p, "--------------- drive0 --------- drive1 " + "-------- drive0 ---------- drive1 ------\n"); + p += sprintf(p, "DMA enabled: %s %s ", + (c0&0x20)?"yes":"no ",(c0&0x40)?"yes":"no "); + p += sprintf(p, " %s %s\n", + (c1&0x20)?"yes":"no ",(c1&0x40)?"yes":"no "); + + if (dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) { + (void) pci_read_config_byte(dev, 0x54, &art); + p += sprintf(p, "DMA Mode: %s(%s)", + (c0&0x20)?((art&0x03)?"UDMA":" DMA"):" PIO", + (art&0x02)?"2":(art&0x01)?"1":"0"); + p += sprintf(p, " %s(%s)", + (c0&0x40)?((art&0x0c)?"UDMA":" DMA"):" PIO", + (art&0x08)?"2":(art&0x04)?"1":"0"); + p += sprintf(p, " %s(%s)", + (c1&0x20)?((art&0x30)?"UDMA":" DMA"):" PIO", + (art&0x20)?"2":(art&0x10)?"1":"0"); + p += sprintf(p, " %s(%s)\n", + (c1&0x40)?((art&0xc0)?"UDMA":" DMA"):" PIO", + (art&0x80)?"2":(art&0x40)?"1":"0"); +#ifdef DEBUG_AEC_REGS + (void) pci_read_config_byte(dev, 0x40, &art); + p += sprintf(p, "Active: 0x%02x", art); + (void) pci_read_config_byte(dev, 0x42, &art); + p += sprintf(p, " 0x%02x", art); + (void) pci_read_config_byte(dev, 0x44, &art); + p += sprintf(p, " 0x%02x", art); + (void) pci_read_config_byte(dev, 0x46, &art); + p += sprintf(p, " 0x%02x\n", art); + (void) pci_read_config_byte(dev, 0x41, &art); + p += sprintf(p, "Recovery: 0x%02x", art); + (void) pci_read_config_byte(dev, 0x43, &art); + p += sprintf(p, " 0x%02x", art); + (void) pci_read_config_byte(dev, 0x45, &art); + p += sprintf(p, " 0x%02x", art); + (void) pci_read_config_byte(dev, 0x47, &art); + p += sprintf(p, " 0x%02x\n", art); +#endif /* DEBUG_AEC_REGS */ + } else { + /* + * case PCI_DEVICE_ID_ARTOP_ATP860: + * case PCI_DEVICE_ID_ARTOP_ATP860R: + * case PCI_DEVICE_ID_ARTOP_ATP865: + * case PCI_DEVICE_ID_ARTOP_ATP865R: + */ + (void) pci_read_config_byte(dev, 0x44, &art); + p += sprintf(p, "DMA Mode: %s(%s)", + (c0&0x20)?((art&0x07)?"UDMA":" DMA"):" PIO", + ((art&0x07)==0x07)?"6": + ((art&0x06)==0x06)?"5": + ((art&0x05)==0x05)?"4": + ((art&0x04)==0x04)?"3": + ((art&0x03)==0x03)?"2": + ((art&0x02)==0x02)?"1": + ((art&0x01)==0x01)?"0":"?"); + p += sprintf(p, " %s(%s)", + (c0&0x40)?((art&0x70)?"UDMA":" DMA"):" PIO", + ((art&0x70)==0x70)?"6": + ((art&0x60)==0x60)?"5": + ((art&0x50)==0x50)?"4": + ((art&0x40)==0x40)?"3": + ((art&0x30)==0x30)?"2": + ((art&0x20)==0x20)?"1": + ((art&0x10)==0x10)?"0":"?"); + (void) pci_read_config_byte(dev, 0x45, &art); + p += sprintf(p, " %s(%s)", + (c1&0x20)?((art&0x07)?"UDMA":" DMA"):" PIO", + ((art&0x07)==0x07)?"6": + ((art&0x06)==0x06)?"5": + ((art&0x05)==0x05)?"4": + ((art&0x04)==0x04)?"3": + ((art&0x03)==0x03)?"2": + ((art&0x02)==0x02)?"1": + ((art&0x01)==0x01)?"0":"?"); + p += sprintf(p, " %s(%s)\n", + (c1&0x40)?((art&0x70)?"UDMA":" DMA"):" PIO", + ((art&0x70)==0x70)?"6": + ((art&0x60)==0x60)?"5": + ((art&0x50)==0x50)?"4": + ((art&0x40)==0x40)?"3": + ((art&0x30)==0x30)?"2": + ((art&0x20)==0x20)?"1": + ((art&0x10)==0x10)?"0":"?"); +#ifdef DEBUG_AEC_REGS + (void) pci_read_config_byte(dev, 0x40, &art); + p += sprintf(p, "Active: 0x%02x", HIGH_4(art)); + (void) pci_read_config_byte(dev, 0x41, &art); + p += sprintf(p, " 0x%02x", HIGH_4(art)); + (void) pci_read_config_byte(dev, 0x42, &art); + p += sprintf(p, " 0x%02x", HIGH_4(art)); + (void) pci_read_config_byte(dev, 0x43, &art); + p += sprintf(p, " 0x%02x\n", HIGH_4(art)); + (void) pci_read_config_byte(dev, 0x40, &art); + p += sprintf(p, "Recovery: 0x%02x", LOW_4(art)); + (void) pci_read_config_byte(dev, 0x41, &art); + p += sprintf(p, " 0x%02x", LOW_4(art)); + (void) pci_read_config_byte(dev, 0x42, &art); + p += sprintf(p, " 0x%02x", LOW_4(art)); + (void) pci_read_config_byte(dev, 0x43, &art); + p += sprintf(p, " 0x%02x\n", LOW_4(art)); + (void) pci_read_config_byte(dev, 0x49, &uart); + p += sprintf(p, "reg49h = 0x%02x ", uart); + (void) pci_read_config_byte(dev, 0x4a, &uart); + p += sprintf(p, "reg4ah = 0x%02x\n", uart); +#endif /* DEBUG_AEC_REGS */ + } + } + return p-buffer;/* => must be less than 4k! */ +} +#endif /* defined(DISPLAY_AEC62xx_TIMINGS) && defined(CONFIG_PROC_FS) */ + +byte aec62xx_proc = 0; + +struct chipset_bus_clock_list_entry { + byte xfer_speed; + byte chipset_settings; + byte ultra_settings; +}; + +struct chipset_bus_clock_list_entry aec6xxx_33_base [] = { +#ifdef CONFIG_BLK_DEV_IDEDMA + { XFER_UDMA_6, 0x31, 0x07 }, + { XFER_UDMA_5, 0x31, 0x06 }, + { XFER_UDMA_4, 0x31, 0x05 }, + { XFER_UDMA_3, 0x31, 0x04 }, + { XFER_UDMA_2, 0x31, 0x03 }, + { XFER_UDMA_1, 0x31, 0x02 }, + { XFER_UDMA_0, 0x31, 0x01 }, + + { XFER_MW_DMA_2, 0x31, 0x00 }, + { XFER_MW_DMA_1, 0x31, 0x00 }, + { XFER_MW_DMA_0, 0x0a, 0x00 }, +#endif /* CONFIG_BLK_DEV_IDEDMA */ + { XFER_PIO_4, 0x31, 0x00 }, + { XFER_PIO_3, 0x33, 0x00 }, + { XFER_PIO_2, 0x08, 0x00 }, + { XFER_PIO_1, 0x0a, 0x00 }, + { XFER_PIO_0, 0x00, 0x00 }, + { 0, 0x00, 0x00 } +}; + +struct chipset_bus_clock_list_entry aec6xxx_34_base [] = { +#ifdef CONFIG_BLK_DEV_IDEDMA + { XFER_UDMA_6, 0x41, 0x06 }, + { XFER_UDMA_5, 0x41, 0x05 }, + { XFER_UDMA_4, 0x41, 0x04 }, + { XFER_UDMA_3, 0x41, 0x03 }, + { XFER_UDMA_2, 0x41, 0x02 }, + { XFER_UDMA_1, 0x41, 0x01 }, + { XFER_UDMA_0, 0x41, 0x01 }, + + { XFER_MW_DMA_2, 0x41, 0x00 }, + { XFER_MW_DMA_1, 0x42, 0x00 }, + { XFER_MW_DMA_0, 0x7a, 0x00 }, +#endif /* CONFIG_BLK_DEV_IDEDMA */ + { XFER_PIO_4, 0x41, 0x00 }, + { XFER_PIO_3, 0x43, 0x00 }, + { XFER_PIO_2, 0x78, 0x00 }, + { XFER_PIO_1, 0x7a, 0x00 }, + { XFER_PIO_0, 0x70, 0x00 }, + { 0, 0x00, 0x00 } +}; + +/* + * TO DO: active tuning and correction of cards without a bios. + */ + +static byte pci_bus_clock_list (byte speed, struct chipset_bus_clock_list_entry * chipset_table) +{ + for ( ; chipset_table->xfer_speed ; chipset_table++) + if (chipset_table->xfer_speed == speed) { + return chipset_table->chipset_settings; + } + return chipset_table->chipset_settings; +} + +static byte pci_bus_clock_list_ultra (byte speed, struct chipset_bus_clock_list_entry * chipset_table) +{ + for ( ; chipset_table->xfer_speed ; chipset_table++) + if (chipset_table->xfer_speed == speed) { + return chipset_table->ultra_settings; + } + return chipset_table->ultra_settings; +} + +static byte aec62xx_ratemask (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + byte mode = 0x00; + + if (dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) { + mode |= 0x01; + } else if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP860) || + (dev->device == PCI_DEVICE_ID_ARTOP_ATP860R)) { + mode |= 0x02; + } else if ((dev->device == PCI_DEVICE_ID_ARTOP_ATP865) || + (dev->device == PCI_DEVICE_ID_ARTOP_ATP865R)) { + u32 bmide = pci_resource_start(dev, 4); + if (IN_BYTE(bmide+2) & 0x10) + mode |= 0x04; + else + mode |= 0x03; + } + if (!eighty_ninty_three(drive)) { + mode &= ~0xFE; + mode |= 0x01; + } + return (mode &= ~0xF8); +} + +static byte aec62xx_ratefilter (ide_drive_t *drive, byte speed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + byte mode = aec62xx_ratemask(drive); + + switch(mode) { + case 0x04: while (speed > XFER_UDMA_6) speed--; break; + case 0x03: while (speed > XFER_UDMA_5) speed--; break; + case 0x02: while (speed > XFER_UDMA_4) speed--; break; + case 0x01: while (speed > XFER_UDMA_2) speed--; break; + case 0x00: + default: while (speed > XFER_MW_DMA_2) speed--; break; + break; + } +#else + while (speed > XFER_PIO_4) speed--; +#endif /* CONFIG_BLK_DEV_IDEDMA */ +// printk("%s: mode == %02x speed == %02x\n", drive->name, mode, speed); + return speed; +} + +static int aec6210_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte speed = aec62xx_ratefilter(drive, xferspeed); + unsigned short d_conf = 0x0000; + byte ultra = 0x00; + byte ultra_conf = 0x00; + byte tmp0 = 0x00; + byte tmp1 = 0x00; + byte tmp2 = 0x00; + unsigned long flags; + + local_irq_save(flags); + pci_read_config_word(dev, 0x40|(2*drive->dn), &d_conf); + tmp0 = pci_bus_clock_list(speed, + (struct chipset_bus_clock_list_entry *) dev->driver_data); + SPLIT_BYTE(tmp0,tmp1,tmp2); + MAKE_WORD(d_conf,tmp1,tmp2); + pci_write_config_word(dev, 0x40|(2*drive->dn), d_conf); + + tmp1 = 0x00; + tmp2 = 0x00; + pci_read_config_byte(dev, 0x54, &ultra); + tmp1 = ((0x00 << (2*drive->dn)) | (ultra & ~(3 << (2*drive->dn)))); + ultra_conf = pci_bus_clock_list_ultra(speed, + (struct chipset_bus_clock_list_entry *) dev->driver_data); + tmp2 = ((ultra_conf << (2*drive->dn)) | (tmp1 & ~(3 << (2*drive->dn)))); + pci_write_config_byte(dev, 0x54, tmp2); + local_irq_restore(flags); + return(ide_config_drive_speed(drive, speed)); +} + +static int aec6260_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte unit = (drive->select.b.unit & 0x01); + byte ultra_pci = hwif->channel ? 0x45 : 0x44; + byte speed = aec62xx_ratefilter(drive, xferspeed); + byte drive_conf = 0x00; + byte ultra_conf = 0x00; + byte ultra = 0x00; + byte tmp1 = 0x00; + byte tmp2 = 0x00; + unsigned long flags; + + local_irq_save(flags); + pci_read_config_byte(dev, 0x40|drive->dn, &drive_conf); + drive_conf = pci_bus_clock_list(speed, + (struct chipset_bus_clock_list_entry *) dev->driver_data); + pci_write_config_byte(dev, 0x40|drive->dn, drive_conf); + + pci_read_config_byte(dev, ultra_pci, &ultra); + tmp1 = ((0x00 << (4*unit)) | (ultra & ~(7 << (4*unit)))); + ultra_conf = pci_bus_clock_list_ultra(speed, + (struct chipset_bus_clock_list_entry *) dev->driver_data); + tmp2 = ((ultra_conf << (4*unit)) | (tmp1 & ~(7 << (4*unit)))); + pci_write_config_byte(dev, ultra_pci, tmp2); + local_irq_restore(flags); + return(ide_config_drive_speed(drive, speed)); +} + +static int aec62xx_tune_chipset (ide_drive_t *drive, byte speed) +{ + switch (HWIF(drive)->pci_dev->device) { + case PCI_DEVICE_ID_ARTOP_ATP865: + case PCI_DEVICE_ID_ARTOP_ATP865R: + case PCI_DEVICE_ID_ARTOP_ATP860: + case PCI_DEVICE_ID_ARTOP_ATP860R: + return ((int) aec6260_tune_chipset(drive, speed)); + case PCI_DEVICE_ID_ARTOP_ATP850UF: + return ((int) aec6210_tune_chipset(drive, speed)); + default: + return -1; + } +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static int config_chipset_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + byte mode = aec62xx_ratemask(drive); + byte speed; + + if (drive->media != ide_disk) + return ((int) ide_dma_off_quietly); + + switch(mode) { + case 0x04: + if (id->dma_ultra & 0x0040) + { speed = XFER_UDMA_6; break; } + case 0x03: + if (id->dma_ultra & 0x0020) + { speed = XFER_UDMA_5; break; } + case 0x02: + if (id->dma_ultra & 0x0010) + { speed = XFER_UDMA_4; break; } + if (id->dma_ultra & 0x0008) + { speed = XFER_UDMA_3; break; } + case 0x01: + if (id->dma_ultra & 0x0004) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0002) + { speed = XFER_UDMA_1; break; } + if (id->dma_ultra & 0x0001) + { speed = XFER_UDMA_0; break; } + case 0x00: + if (id->dma_mword & 0x0004) + { speed = XFER_MW_DMA_2; break; } + if (id->dma_mword & 0x0002) + { speed = XFER_MW_DMA_1; break; } + if (id->dma_mword & 0x0001) + { speed = XFER_MW_DMA_0; break; } + if (id->dma_1word & 0x0004) + { speed = XFER_SW_DMA_2; break; } + if (id->dma_1word & 0x0002) + { speed = XFER_SW_DMA_1; break; } + if (id->dma_1word & 0x0001) + { speed = XFER_SW_DMA_0; break; } + default: + return ((int) ide_dma_off_quietly); + } + + (void) aec62xx_tune_chipset(drive, speed); + + return ((int) ((id->dma_ultra >> 11) & 15) ? ide_dma_on : + ((id->dma_ultra >> 8) & 7) ? ide_dma_on : + ((id->dma_mword >> 8) & 7) ? ide_dma_on : + ((id->dma_1word >> 8) & 7) ? ide_dma_on : + ide_dma_off_quietly); +// return ((int) ide_dma_on); +} + +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +static void aec62xx_tune_drive (ide_drive_t *drive, byte pio) +{ + byte speed; + byte new_pio = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL); + + switch(pio) { + case 5: speed = new_pio; break; + case 4: speed = XFER_PIO_4; break; + case 3: speed = XFER_PIO_3; break; + case 2: speed = XFER_PIO_2; break; + case 1: speed = XFER_PIO_1; break; + default: speed = XFER_PIO_0; break; + } + (void) aec62xx_tune_chipset(drive, speed); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static int config_drive_xfer_rate (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_dma_action_t dma_func = ide_dma_on; + + if (id && (id->capability & 1) && HWIF(drive)->autodma) { + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + goto fast_ata_pio; + } + dma_func = ide_dma_off_quietly; + if (id->field_valid & 4) { + if (id->dma_ultra & 0x007F) { + /* Force if Capable UltraDMA */ + dma_func = config_chipset_for_dma(drive); + if ((id->field_valid & 2) && + (dma_func != ide_dma_on)) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & 0x0007) || + (id->dma_1word & 0x0007)) { + /* Force if Capable regular DMA modes */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } + } else if (ide_dmaproc(ide_dma_good_drive, drive)) { + if (id->eide_dma_time > 150) { + goto no_dma_set; + } + /* Consult the list of known "good" drives */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + dma_func = ide_dma_off_quietly; +no_dma_set: + aec62xx_tune_drive(drive, 5); + } + return HWIF(drive)->dmaproc(dma_func, drive); +} + +/* + * aec62xx_dmaproc() initiates/aborts (U)DMA read/write operations on a drive. + */ +int aec62xx_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + + switch (func) { + case ide_dma_check: + return config_drive_xfer_rate(drive); + case ide_dma_lostirq: + case ide_dma_timeout: + switch(dev->device) { + case PCI_DEVICE_ID_ARTOP_ATP860: + case PCI_DEVICE_ID_ARTOP_ATP860R: + case PCI_DEVICE_ID_ARTOP_ATP865: + case PCI_DEVICE_ID_ARTOP_ATP865R: + printk(" AEC62XX time out "); +#if 0 + { + int i = 0; + byte reg49h = 0; + pci_read_config_byte(HWIF(drive)->pci_dev, 0x49, ®49h); + for (i=0;i<256;i++) + pci_write_config_byte(HWIF(drive)->pci_dev, 0x49, reg49h|0x10); + pci_write_config_byte(HWIF(drive)->pci_dev, 0x49, reg49h & ~0x10); + } + return 0; +#endif + default: + break; + } + default: + break; + } +#if 0 + { + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + unsigned long dma_base = hwif->dma_base; + byte tmp1 = 0x00; + byte tmp2 = 0x00; + + pci_read_config_byte(dev, 0x44, &tmp1); + pci_read_config_byte(dev, 0x45, &tmp2); + printk(" AEC6280 r44=%x r45=%x ",tmp1,tmp2); + if (hwif->channel) + dma_base -= 0x08; + tmp1=IN_BYTE(dma_base+2) & 0x10; + printk(" AEC6280 133=%x ",tmp1); + } +#endif + return ide_dmaproc(func, drive); /* use standard DMA stuff */ +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +unsigned int __init pci_init_aec62xx (struct pci_dev *dev, const char *name) +{ + int bus_speed = system_bus_clock(); + + if (dev->resource[PCI_ROM_RESOURCE].start) { + pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); + printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start); + } + + aec_devs[n_aec_devs++] = dev; + +#if defined(DISPLAY_AEC62XX_TIMINGS) && defined(CONFIG_PROC_FS) + if (!aec62xx_proc) { + aec62xx_proc = 1; + aec62xx_display_info = &aec62xx_get_info; + } +#endif /* DISPLAY_AEC62XX_TIMINGS && CONFIG_PROC_FS */ + + if (bus_speed <= 33) + dev->driver_data = (void *) aec6xxx_33_base; + else + dev->driver_data = (void *) aec6xxx_34_base; + + return dev->irq; +} + +unsigned int __init ata66_aec62xx (ide_hwif_t *hwif) +{ + byte mask = hwif->channel ? 0x02 : 0x01; + byte ata66 = 0; + + pci_read_config_byte(hwif->pci_dev, 0x49, &ata66); + return ((ata66 & mask) ? 0 : 1); +} + +void __init ide_init_aec62xx (ide_hwif_t *hwif) +{ + hwif->autodma = 0; + hwif->tuneproc = &aec62xx_tune_drive; + hwif->speedproc = &aec62xx_tune_chipset; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + + if (!hwif->dma_base) + return; + +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->dmaproc = &aec62xx_dmaproc; +#ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +#endif /* CONFIG_IDEDMA_AUTO */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + +} + +void __init ide_dmacapable_aec62xx (ide_hwif_t *hwif, unsigned long dmabase) +{ + struct pci_dev *dev = hwif->pci_dev; + byte reg54h = 0; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + pci_read_config_byte(dev, 0x54, ®54h); + pci_write_config_byte(dev, 0x54, reg54h & ~(hwif->channel ? 0xF0 : 0x0F)); + spin_unlock_irqrestore(&ide_lock, flags); + ide_setup_dma(hwif, dmabase, 8); +} + +extern void ide_setup_pci_device(struct pci_dev *, ide_pci_device_t *); + +void __init fixup_device_aec6x80 (struct pci_dev *dev, ide_pci_device_t *d) +{ + u32 bar4reg = pci_resource_start(dev, 4); + + if (IN_BYTE(bar4reg+2) & 0x10) { + strcpy(d->name, "AEC6880"); + if (dev->device == PCI_DEVICE_ID_ARTOP_ATP865R) + strcpy(d->name, "AEC6880R"); + } else { + strcpy(d->name, "AEC6280"); + if (dev->device == PCI_DEVICE_ID_ARTOP_ATP865R) + strcpy(d->name, "AEC6280R"); + } + + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); +} diff -Nru a/drivers/ide/ali14xx.c b/drivers/ide/ali14xx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ali14xx.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,223 @@ +/* + * linux/drivers/ide/ali14xx.c Version 0.03 Feb 09, 1996 + * + * Copyright (C) 1996 Linus Torvalds & author (see below) + */ + +/* + * ALI M14xx chipset EIDE controller + * + * Works for ALI M1439/1443/1445/1487/1489 chipsets. + * + * Adapted from code developed by derekn@vw.ece.cmu.edu. -ml + * Derek's notes follow: + * + * I think the code should be pretty understandable, + * but I'll be happy to (try to) answer questions. + * + * The critical part is in the setupDrive function. The initRegisters + * function doesn't seem to be necessary, but the DOS driver does it, so + * I threw it in. + * + * I've only tested this on my system, which only has one disk. I posted + * it to comp.sys.linux.hardware, so maybe some other people will try it + * out. + * + * Derek Noonburg (derekn@ece.cmu.edu) + * 95-sep-26 + * + * Update 96-jul-13: + * + * I've since upgraded to two disks and a CD-ROM, with no trouble, and + * I've also heard from several others who have used it successfully. + * This driver appears to work with both the 1443/1445 and the 1487/1489 + * chipsets. I've added support for PIO mode 4 for the 1487. This + * seems to work just fine on the 1443 also, although I'm not sure it's + * advertised as supporting mode 4. (I've been running a WDC AC21200 in + * mode 4 for a while now with no trouble.) -Derek + */ + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ide_modes.h" + +/* port addresses for auto-detection */ +#define ALI_NUM_PORTS 4 +static int ports[ALI_NUM_PORTS] __initdata = {0x074, 0x0f4, 0x034, 0x0e4}; + +/* register initialization data */ +typedef struct { byte reg, data; } RegInitializer; + +static RegInitializer initData[] __initdata = { + {0x01, 0x0f}, {0x02, 0x00}, {0x03, 0x00}, {0x04, 0x00}, + {0x05, 0x00}, {0x06, 0x00}, {0x07, 0x2b}, {0x0a, 0x0f}, + {0x25, 0x00}, {0x26, 0x00}, {0x27, 0x00}, {0x28, 0x00}, + {0x29, 0x00}, {0x2a, 0x00}, {0x2f, 0x00}, {0x2b, 0x00}, + {0x2c, 0x00}, {0x2d, 0x00}, {0x2e, 0x00}, {0x30, 0x00}, + {0x31, 0x00}, {0x32, 0x00}, {0x33, 0x00}, {0x34, 0xff}, + {0x35, 0x03}, {0x00, 0x00} +}; + +#define ALI_MAX_PIO 4 + +/* timing parameter registers for each drive */ +static struct { byte reg1, reg2, reg3, reg4; } regTab[4] = { + {0x03, 0x26, 0x04, 0x27}, /* drive 0 */ + {0x05, 0x28, 0x06, 0x29}, /* drive 1 */ + {0x2b, 0x30, 0x2c, 0x31}, /* drive 2 */ + {0x2d, 0x32, 0x2e, 0x33}, /* drive 3 */ +}; + +static int basePort; /* base port address */ +static int regPort; /* port for register number */ +static int dataPort; /* port for register data */ +static byte regOn; /* output to base port to access registers */ +static byte regOff; /* output to base port to close registers */ + +/*------------------------------------------------------------------------*/ + +/* + * Read a controller register. + */ +static inline byte inReg (byte reg) +{ + outb_p(reg, regPort); + return IN_BYTE(dataPort); +} + +/* + * Write a controller register. + */ +static void outReg (byte data, byte reg) +{ + outb_p(reg, regPort); + outb_p(data, dataPort); +} + +/* + * Set PIO mode for the specified drive. + * This function computes timing parameters + * and sets controller registers accordingly. + */ +static void ali14xx_tune_drive (ide_drive_t *drive, byte pio) +{ + int driveNum; + int time1, time2; + byte param1, param2, param3, param4; + unsigned long flags; + ide_pio_data_t d; + int bus_speed = system_bus_clock(); + + pio = ide_get_best_pio_mode(drive, pio, ALI_MAX_PIO, &d); + + /* calculate timing, according to PIO mode */ + time1 = d.cycle_time; + time2 = ide_pio_timings[pio].active_time; + param3 = param1 = (time2 * bus_speed + 999) / 1000; + param4 = param2 = (time1 * bus_speed + 999) / 1000 - param1; + if (pio < 3) { + param3 += 8; + param4 += 8; + } + printk("%s: PIO mode%d, t1=%dns, t2=%dns, cycles = %d+%d, %d+%d\n", + drive->name, pio, time1, time2, param1, param2, param3, param4); + + /* stuff timing parameters into controller registers */ + driveNum = (HWIF(drive)->index << 1) + drive->select.b.unit; + spin_lock_irqsave(&ide_lock, flags); + outb_p(regOn, basePort); + outReg(param1, regTab[driveNum].reg1); + outReg(param2, regTab[driveNum].reg2); + outReg(param3, regTab[driveNum].reg3); + outReg(param4, regTab[driveNum].reg4); + outb_p(regOff, basePort); + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * Auto-detect the IDE controller port. + */ +static int __init findPort (void) +{ + int i; + byte t; + unsigned long flags; + + local_irq_save(flags); + for (i = 0; i < ALI_NUM_PORTS; ++i) { + basePort = ports[i]; + regOff = IN_BYTE(basePort); + for (regOn = 0x30; regOn <= 0x33; ++regOn) { + outb_p(regOn, basePort); + if (IN_BYTE(basePort) == regOn) { + regPort = basePort + 4; + dataPort = basePort + 8; + t = inReg(0) & 0xf0; + outb_p(regOff, basePort); + local_irq_restore(flags); + if (t != 0x50) + return 0; + return 1; /* success */ + } + } + outb_p(regOff, basePort); + } + local_irq_restore(flags); + return 0; +} + +/* + * Initialize controller registers with default values. + */ +static int __init initRegisters (void) { + RegInitializer *p; + byte t; + unsigned long flags; + + local_irq_save(flags); + outb_p(regOn, basePort); + for (p = initData; p->reg != 0; ++p) + outReg(p->data, p->reg); + outb_p(0x01, regPort); + t = IN_BYTE(regPort) & 0x01; + outb_p(regOff, basePort); + local_irq_restore(flags); + return t; +} + +void __init init_ali14xx (void) +{ + /* auto-detect IDE controller port */ + if (!findPort()) { + printk("\nali14xx: not found"); + return; + } + + printk("\nali14xx: base= 0x%03x, regOn = 0x%02x", basePort, regOn); + ide_hwifs[0].chipset = ide_ali14xx; + ide_hwifs[1].chipset = ide_ali14xx; + ide_hwifs[0].tuneproc = &ali14xx_tune_drive; + ide_hwifs[1].tuneproc = &ali14xx_tune_drive; + ide_hwifs[0].mate = &ide_hwifs[1]; + ide_hwifs[1].mate = &ide_hwifs[0]; + ide_hwifs[1].channel = 1; + + /* initialize controller registers */ + if (!initRegisters()) { + printk("\nali14xx: Chip initialization failed"); + return; + } +} diff -Nru a/drivers/ide/alim15x3.c b/drivers/ide/alim15x3.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/alim15x3.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,825 @@ +/* + * linux/drivers/ide/alim15x3.c Version 0.10 Jun. 9, 2000 + * + * Copyright (C) 1998-2000 Michel Aubry, Maintainer + * Copyright (C) 1998-2000 Andrzej Krzysztofowicz, Maintainer + * Copyright (C) 1999-2000 CJ, cjtsai@ali.com.tw, Maintainer + * + * Copyright (C) 1998-2000 Andre Hedrick (andre@linux-ide.org) + * May be copied or modified under the terms of the GNU General Public License + * + * (U)DMA capable version of ali 1533/1543(C), 1535(D) + * + ********************************************************************** + * 9/7/99 --Parts from the above author are included and need to be + * converted into standard interface, once I finish the thought. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ide_modes.h" + +#define DISPLAY_ALI_TIMINGS + +#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +static int ali_get_info(char *buffer, char **addr, off_t offset, int count); +extern int (*ali_display_info)(char *, char **, off_t, int); /* ide-proc.c */ +static struct pci_dev *bmide_dev; + +char *fifo[4] = { + "FIFO Off", + "FIFO On ", + "DMA mode", + "PIO mode" }; + +char *udmaT[8] = { + "1.5T", + " 2T", + "2.5T", + " 3T", + "3.5T", + " 4T", + " 6T", + " 8T" +}; + +char *channel_status[8] = { + "OK ", + "busy ", + "DRQ ", + "DRQ busy ", + "error ", + "error busy ", + "error DRQ ", + "error DRQ busy" +}; + +static int ali_get_info (char *buffer, char **addr, off_t offset, int count) +{ + byte reg53h, reg5xh, reg5yh, reg5xh1, reg5yh1; + unsigned int bibma; + byte c0, c1; + byte rev, tmp; + char *p = buffer; + char *q; + + /* fetch rev. */ + pci_read_config_byte(bmide_dev, 0x08, &rev); + if (rev >= 0xc1) /* M1543C or newer */ + udmaT[7] = " ???"; + else + fifo[3] = " ??? "; + + /* first fetch bibma: */ + pci_read_config_dword(bmide_dev, 0x20, &bibma); + bibma = (bibma & 0xfff0) ; + /* + * at that point bibma+0x2 et bibma+0xa are byte + * registers to investigate: + */ + c0 = IN_BYTE((unsigned short)bibma + 0x02); + c1 = IN_BYTE((unsigned short)bibma + 0x0a); + + p += sprintf(p, + "\n Ali M15x3 Chipset.\n"); + p += sprintf(p, + " ------------------\n"); + pci_read_config_byte(bmide_dev, 0x78, ®53h); + p += sprintf(p, "PCI Clock: %d.\n", reg53h); + + pci_read_config_byte(bmide_dev, 0x53, ®53h); + p += sprintf(p, + "CD_ROM FIFO:%s, CD_ROM DMA:%s\n", + (reg53h & 0x02) ? "Yes" : "No ", + (reg53h & 0x01) ? "Yes" : "No " ); + pci_read_config_byte(bmide_dev, 0x74, ®53h); + p += sprintf(p, + "FIFO Status: contains %d Words, runs%s%s\n\n", + (reg53h & 0x3f), + (reg53h & 0x40) ? " OVERWR" : "", + (reg53h & 0x80) ? " OVERRD." : "." ); + + p += sprintf(p, + "-------------------primary channel" + "-------------------secondary channel" + "---------\n\n"); + + pci_read_config_byte(bmide_dev, 0x09, ®53h); + p += sprintf(p, + "channel status: %s" + " %s\n", + (reg53h & 0x20) ? "On " : "Off", + (reg53h & 0x10) ? "On " : "Off" ); + + p += sprintf(p, + "both channels togth: %s" + " %s\n", + (c0&0x80) ? "No " : "Yes", + (c1&0x80) ? "No " : "Yes" ); + + pci_read_config_byte(bmide_dev, 0x76, ®53h); + p += sprintf(p, + "Channel state: %s %s\n", + channel_status[reg53h & 0x07], + channel_status[(reg53h & 0x70) >> 4] ); + + pci_read_config_byte(bmide_dev, 0x58, ®5xh); + pci_read_config_byte(bmide_dev, 0x5c, ®5yh); + p += sprintf(p, + "Add. Setup Timing: %dT" + " %dT\n", + (reg5xh & 0x07) ? (reg5xh & 0x07) : 8, + (reg5yh & 0x07) ? (reg5yh & 0x07) : 8 ); + + pci_read_config_byte(bmide_dev, 0x59, ®5xh); + pci_read_config_byte(bmide_dev, 0x5d, ®5yh); + p += sprintf(p, + "Command Act. Count: %dT" + " %dT\n" + "Command Rec. Count: %dT" + " %dT\n\n", + (reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8, + (reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8, + (reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16, + (reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16 ); + + p += sprintf(p, + "----------------drive0-----------drive1" + "------------drive0-----------drive1------\n\n"); + p += sprintf(p, + "DMA enabled: %s %s" + " %s %s\n", + (c0&0x20) ? "Yes" : "No ", + (c0&0x40) ? "Yes" : "No ", + (c1&0x20) ? "Yes" : "No ", + (c1&0x40) ? "Yes" : "No " ); + + pci_read_config_byte(bmide_dev, 0x54, ®5xh); + pci_read_config_byte(bmide_dev, 0x55, ®5yh); + q = "FIFO threshold: %2d Words %2d Words" + " %2d Words %2d Words\n"; + if (rev < 0xc1) { + if ((rev == 0x20) && (pci_read_config_byte(bmide_dev, 0x4f, &tmp), (tmp &= 0x20))) { + p += sprintf(p, q, 8, 8, 8, 8); + } else { + p += sprintf(p, q, + (reg5xh & 0x03) + 12, + ((reg5xh & 0x30)>>4) + 12, + (reg5yh & 0x03) + 12, + ((reg5yh & 0x30)>>4) + 12 ); + } + } else { + int t1 = (tmp = (reg5xh & 0x03)) ? (tmp << 3) : 4; + int t2 = (tmp = ((reg5xh & 0x30)>>4)) ? (tmp << 3) : 4; + int t3 = (tmp = (reg5yh & 0x03)) ? (tmp << 3) : 4; + int t4 = (tmp = ((reg5yh & 0x30)>>4)) ? (tmp << 3) : 4; + p += sprintf(p, q, t1, t2, t3, t4); + } + +#if 0 + p += sprintf(p, + "FIFO threshold: %2d Words %2d Words" + " %2d Words %2d Words\n", + (reg5xh & 0x03) + 12, + ((reg5xh & 0x30)>>4) + 12, + (reg5yh & 0x03) + 12, + ((reg5yh & 0x30)>>4) + 12 ); +#endif + + p += sprintf(p, + "FIFO mode: %s %s %s %s\n", + fifo[((reg5xh & 0x0c) >> 2)], + fifo[((reg5xh & 0xc0) >> 6)], + fifo[((reg5yh & 0x0c) >> 2)], + fifo[((reg5yh & 0xc0) >> 6)] ); + + pci_read_config_byte(bmide_dev, 0x5a, ®5xh); + pci_read_config_byte(bmide_dev, 0x5b, ®5xh1); + pci_read_config_byte(bmide_dev, 0x5e, ®5yh); + pci_read_config_byte(bmide_dev, 0x5f, ®5yh1); + + p += sprintf(p,/* + "------------------drive0-----------drive1" + "------------drive0-----------drive1------\n")*/ + "Dt RW act. Cnt %2dT %2dT" + " %2dT %2dT\n" + "Dt RW rec. Cnt %2dT %2dT" + " %2dT %2dT\n\n", + (reg5xh & 0x70) ? ((reg5xh & 0x70) >> 4) : 8, + (reg5xh1 & 0x70) ? ((reg5xh1 & 0x70) >> 4) : 8, + (reg5yh & 0x70) ? ((reg5yh & 0x70) >> 4) : 8, + (reg5yh1 & 0x70) ? ((reg5yh1 & 0x70) >> 4) : 8, + (reg5xh & 0x0f) ? (reg5xh & 0x0f) : 16, + (reg5xh1 & 0x0f) ? (reg5xh1 & 0x0f) : 16, + (reg5yh & 0x0f) ? (reg5yh & 0x0f) : 16, + (reg5yh1 & 0x0f) ? (reg5yh1 & 0x0f) : 16 ); + + p += sprintf(p, + "-----------------------------------UDMA Timings" + "--------------------------------\n\n"); + + pci_read_config_byte(bmide_dev, 0x56, ®5xh); + pci_read_config_byte(bmide_dev, 0x57, ®5yh); + p += sprintf(p, + "UDMA: %s %s" + " %s %s\n" + "UDMA timings: %s %s" + " %s %s\n\n", + (reg5xh & 0x08) ? "OK" : "No", + (reg5xh & 0x80) ? "OK" : "No", + (reg5yh & 0x08) ? "OK" : "No", + (reg5yh & 0x80) ? "OK" : "No", + udmaT[(reg5xh & 0x07)], + udmaT[(reg5xh & 0x70) >> 4], + udmaT[reg5yh & 0x07], + udmaT[(reg5yh & 0x70) >> 4] ); + + return p-buffer; /* => must be less than 4k! */ +} +#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */ + +static byte m5229_revision; +static byte chip_is_1543c_e; + +byte ali_proc = 0; +static struct pci_dev *isa_dev; + +static void ali15x3_tune_drive (ide_drive_t *drive, byte pio) +{ + ide_pio_data_t d; + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + int s_time, a_time, c_time; + byte s_clc, a_clc, r_clc; + unsigned long flags; + int bus_speed = system_bus_clock(); + int port = hwif->channel ? 0x5c : 0x58; + int portFIFO = hwif->channel ? 0x55 : 0x54; + byte cd_dma_fifo = 0; + + pio = ide_get_best_pio_mode(drive, pio, 5, &d); + s_time = ide_pio_timings[pio].setup_time; + a_time = ide_pio_timings[pio].active_time; + if ((s_clc = (s_time * bus_speed + 999) / 1000) >= 8) + s_clc = 0; + if ((a_clc = (a_time * bus_speed + 999) / 1000) >= 8) + a_clc = 0; + c_time = ide_pio_timings[pio].cycle_time; + +#if 0 + if ((r_clc = ((c_time - s_time - a_time) * bus_speed + 999) / 1000) >= 16) + r_clc = 0; +#endif + + if (!(r_clc = (c_time * bus_speed + 999) / 1000 - a_clc - s_clc)) { + r_clc = 1; + } else { + if (r_clc >= 16) + r_clc = 0; + } + local_irq_save(flags); + + /* + * PIO mode => ATA FIFO on, ATAPI FIFO off + */ + pci_read_config_byte(dev, portFIFO, &cd_dma_fifo); + if (drive->media==ide_disk) { + if (hwif->channel) { + pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0x0F) | 0x50); + } else { + pci_write_config_byte(dev, portFIFO, (cd_dma_fifo & 0xF0) | 0x05); + } + } else { + if (hwif->channel) { + pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0x0F); + } else { + pci_write_config_byte(dev, portFIFO, cd_dma_fifo & 0xF0); + } + } + + pci_write_config_byte(dev, port, s_clc); + pci_write_config_byte(dev, port+drive->select.b.unit+2, (a_clc << 4) | r_clc); + local_irq_restore(flags); + + /* + * setup active rec + * { 70, 165, 365 }, PIO Mode 0 + * { 50, 125, 208 }, PIO Mode 1 + * { 30, 100, 110 }, PIO Mode 2 + * { 30, 80, 70 }, PIO Mode 3 with IORDY + * { 25, 70, 25 }, PIO Mode 4 with IORDY ns + * { 20, 50, 30 } PIO Mode 5 with IORDY (nonstandard) + */ + +} + +static byte ali15x3_can_ultra (ide_drive_t *drive) +{ +#ifndef CONFIG_WDC_ALI15X3 + struct hd_driveid *id = drive->id; +#endif /* CONFIG_WDC_ALI15X3 */ + + if (m5229_revision <= 0x20) { + return 0; + } else if ((m5229_revision < 0xC2) && +#ifndef CONFIG_WDC_ALI15X3 + ((chip_is_1543c_e && strstr(id->model, "WDC ")) || + (drive->media!=ide_disk))) { +#else /* CONFIG_WDC_ALI15X3 */ + (drive->media!=ide_disk)) { +#endif /* CONFIG_WDC_ALI15X3 */ + return 0; + } else { + return 1; + } +} + +static byte ali15x3_ratemask (ide_drive_t *drive) +{ +// struct pci_dev *dev = HWIF(drive)->pci_dev; + byte mode = 0x00; + byte can_ultra = ali15x3_can_ultra(drive); + + if ((m5229_revision >= 0xC4) && (can_ultra)) { + mode |= 0x03; + } else if ((m5229_revision >= 0xC2) && (can_ultra)) { + mode |= 0x02; + } else if (can_ultra) { + mode |= 0x01; + } else { + return (mode &= ~0xFF); + } + + if (!eighty_ninty_three(drive)) { + mode &= ~0xFE; + mode |= 0x01; + } + return (mode &= ~0xF8); +} + +static byte ali15x3_ratefilter (ide_drive_t *drive, byte speed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + byte mode = ali15x3_ratemask(drive); + + switch(mode) { + case 0x04: while (speed > XFER_UDMA_6) speed--; break; + case 0x03: while (speed > XFER_UDMA_5) speed--; break; + case 0x02: while (speed > XFER_UDMA_4) speed--; break; + case 0x01: while (speed > XFER_UDMA_2) speed--; break; + case 0x00: + default: while (speed > XFER_MW_DMA_2) speed--; break; + break; + } +#else + while (speed > XFER_PIO_4) speed--; +#endif /* CONFIG_BLK_DEV_IDEDMA */ +// printk("%s: mode == %02x speed == %02x\n", drive->name, mode, speed); + return speed; +} + +static int ali15x3_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte speed = ali15x3_ratefilter(drive, xferspeed); + byte unit = (drive->select.b.unit & 0x01); + byte tmpbyte = 0x00; + int m5229_udma = (hwif->channel) ? 0x57 : 0x56; + + if (speed < XFER_UDMA_0) { + byte ultra_enable = (unit) ? 0x7f : 0xf7; + /* + * clear "ultra enable" bit + */ + pci_read_config_byte(dev, m5229_udma, &tmpbyte); + tmpbyte &= ultra_enable; + pci_write_config_byte(dev, m5229_udma, tmpbyte); + + if (speed < XFER_SW_DMA_0) + ali15x3_tune_drive(drive, speed); +#ifdef CONFIG_BLK_DEV_IDEDMA + } else { + pci_read_config_byte(dev, m5229_udma, &tmpbyte); + tmpbyte &= (0x0f << ((1-unit) << 2)); + /* + * enable ultra dma and set timing + */ + tmpbyte |= ((0x08 | ((4-speed)&0x07)) << (unit << 2)); + pci_write_config_byte(dev, m5229_udma, tmpbyte); + if (speed >= XFER_UDMA_3) { + pci_read_config_byte(dev, 0x4b, &tmpbyte); + tmpbyte |= 1; + pci_write_config_byte(dev, 0x4b, tmpbyte); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + } + return (ide_config_drive_speed(drive, speed)); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static int config_chipset_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + byte mode = ali15x3_ratemask(drive); + byte speed = 0; + + switch(mode) { + case 0x03: + if (id->dma_ultra & 0x0020) + { speed = XFER_UDMA_5; break; } + case 0x02: + if (id->dma_ultra & 0x0010) + { speed = XFER_UDMA_4; break; } + if (id->dma_ultra & 0x0008) + { speed = XFER_UDMA_3; break; } + case 0x01: + if (id->dma_ultra & 0x0004) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0002) + { speed = XFER_UDMA_1; break; } + if (id->dma_ultra & 0x0001) + { speed = XFER_UDMA_0; break; } + case 0x00: + if (id->dma_mword & 0x0004) + { speed = XFER_MW_DMA_2; break; } + if (id->dma_mword & 0x0002) + { speed = XFER_MW_DMA_1; break; } + if (id->dma_mword & 0x0001) + { speed = XFER_MW_DMA_0; break; } + if (id->dma_1word & 0x0004) + { speed = XFER_SW_DMA_2; break; } + if (id->dma_1word & 0x0002) + { speed = XFER_SW_DMA_1; break; } + if (id->dma_1word & 0x0001) + { speed = XFER_SW_DMA_0; break; } + default: + return ((int) ide_dma_off_quietly); + } + + (void) ali15x3_tune_chipset(drive, speed); +// return ((int) (dma) ? ide_dma_on : ide_dma_off_quietly); + return ((int) ((id->dma_ultra >> 11) & 7) ? ide_dma_on : + ((id->dma_ultra >> 8) & 7) ? ide_dma_on : + ((id->dma_mword >> 8) & 7) ? ide_dma_on : + ((id->dma_1word >> 8) & 7) ? ide_dma_on : + ide_dma_off_quietly); +} + +static int ali15x3_config_drive_for_dma(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + ide_dma_action_t dma_func = ide_dma_on; + + if ((m5229_revision<=0x20) && (drive->media!=ide_disk)) + return hwif->dmaproc(ide_dma_off_quietly, drive); + + drive->init_speed = 0; + + if ((id != NULL) && ((id->capability & 1) != 0) && hwif->autodma) { + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + goto fast_ata_pio; + } + dma_func = ide_dma_off_quietly; + if ((id->field_valid & 4) && (m5229_revision >= 0xC2)) { + if (id->dma_ultra & 0x003F) { + /* Force if Capable UltraDMA */ + dma_func = config_chipset_for_dma(drive); + if ((id->field_valid & 2) && + (dma_func != ide_dma_on)) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & 0x0007) || + (id->dma_1word & 0x0007)) { + /* Force if Capable regular DMA modes */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } + } else if (ide_dmaproc(ide_dma_good_drive, drive)) { + if (id->eide_dma_time > 150) { + goto no_dma_set; + } + /* Consult the list of known "good" drives */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + dma_func = ide_dma_off_quietly; +no_dma_set: + hwif->tuneproc(drive, 5); + } + return hwif->dmaproc(dma_func, drive); +} + +static int ali15x3_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + switch(func) { + case ide_dma_check: + return ali15x3_config_drive_for_dma(drive); + case ide_dma_write: + if ((m5229_revision < 0xC2) && + (drive->media != ide_disk)) + return 1; /* try PIO instead of DMA */ + break; + default: + break; + } + return ide_dmaproc(func, drive); /* use standard DMA stuff */ +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +#define ALI_INIT_CODE_TEST + +unsigned int __init pci_init_ali15x3 (struct pci_dev *dev, const char *name) +{ + unsigned long fixdma_base = pci_resource_start(dev, 4); + +#ifdef ALI_INIT_CODE_TEST + unsigned long flags; + byte tmpbyte; +#endif /* ALI_INIT_CODE_TEST */ + + pci_read_config_byte(dev, PCI_REVISION_ID, &m5229_revision); + + isa_dev = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL); + + if (!fixdma_base) { + /* + * + */ + } else { + /* + * enable DMA capable bit, and "not" simplex only + */ + OUT_BYTE(IN_BYTE(fixdma_base+2) & 0x60, fixdma_base+2); + + if (IN_BYTE(fixdma_base+2) & 0x80) + printk("%s: simplex device: DMA will fail!!\n", name); + } + +#if defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) + if (!ali_proc) { + ali_proc = 1; + bmide_dev = dev; + ali_display_info = &ali_get_info; + } +#endif /* defined(DISPLAY_ALI_TIMINGS) && defined(CONFIG_PROC_FS) */ + +#ifdef ALI_INIT_CODE_TEST + local_irq_save(flags); + + if (m5229_revision >= 0xC2) { + /* + * 1543C-B?, 1535, 1535D, 1553 + * Note 1: not all "motherboard" support this detection + * Note 2: if no udma 66 device, the detection may "error". + * but in this case, we will not set the device to + * ultra 66, the detection result is not important + */ + + /* + * enable "Cable Detection", m5229, 0x4b, bit3 + */ + pci_read_config_byte(dev, 0x4b, &tmpbyte); + pci_write_config_byte(dev, 0x4b, tmpbyte | 0x08); + + /* + * set south-bridge's enable bit, m1533, 0x79 + */ + pci_read_config_byte(isa_dev, 0x79, &tmpbyte); + if (m5229_revision == 0xC2) { + /* + * 1543C-B0 (m1533, 0x79, bit 2) + */ + pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x04); + } else if (m5229_revision >= 0xC3) { + /* + * 1553/1535 (m1533, 0x79, bit 1) + */ + pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x02); + } + } else { + /* + * revision 0x20 (1543-E, 1543-F) + * revision 0xC0, 0xC1 (1543C-C, 1543C-D, 1543C-E) + * clear CD-ROM DMA write bit, m5229, 0x4b, bit 7 + */ + pci_read_config_byte(dev, 0x4b, &tmpbyte); + /* + * clear bit 7 + */ + pci_write_config_byte(dev, 0x4b, tmpbyte & 0x7F); + } + + local_irq_save(flags); +#endif /* ALI_INIT_CODE_TEST */ + + return 0; +} + +/* + * This checks if the controller and the cable are capable + * of UDMA66 transfers. It doesn't check the drives. + * But see note 2 below! + */ +unsigned int __init ata66_ali15x3 (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + unsigned int ata66 = 0; + byte cable_80_pin[2] = { 0, 0 }; + + unsigned long flags; + byte tmpbyte; + + local_irq_save(flags); + + if (m5229_revision >= 0xC2) { +#ifndef ALI_INIT_CODE_TEST + /* + * 1543C-B?, 1535, 1535D, 1553 + * Note 1: not all "motherboard" support this detection + * Note 2: if no udma 66 device, the detection may "error". + * but in this case, we will not set the device to + * ultra 66, the detection result is not important + */ + + /* + * enable "Cable Detection", m5229, 0x4b, bit3 + */ + pci_read_config_byte(dev, 0x4b, &tmpbyte); + pci_write_config_byte(dev, 0x4b, tmpbyte | 0x08); + + /* + * set south-bridge's enable bit, m1533, 0x79 + */ + pci_read_config_byte(isa_dev, 0x79, &tmpbyte); + if (m5229_revision == 0xC2) { + /* + * 1543C-B0 (m1533, 0x79, bit 2) + */ + pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x04); + } else if (m5229_revision >= 0xC3) { + /* + * 1553/1535 (m1533, 0x79, bit 1) + */ + pci_write_config_byte(isa_dev, 0x79, tmpbyte | 0x02); + } +#endif /* ALI_INIT_CODE_TEST */ + /* + * Ultra66 cable detection (from Host View) + * m5229, 0x4a, bit0: primary, bit1: secondary 80 pin + */ + pci_read_config_byte(dev, 0x4a, &tmpbyte); + /* + * 0x4a, bit0 is 0 => primary channel + * has 80-pin (from host view) + */ + if (!(tmpbyte & 0x01)) cable_80_pin[0] = 1; + /* + * 0x4a, bit1 is 0 => secondary channel + * has 80-pin (from host view) + */ + if (!(tmpbyte & 0x02)) cable_80_pin[1] = 1; + /* + * Allow ata66 if cable of current channel has 80 pins + */ + ata66 = (hwif->channel)?cable_80_pin[1]:cable_80_pin[0]; + } else { +#ifndef ALI_INIT_CODE_TEST + /* + * revision 0x20 (1543-E, 1543-F) + * revision 0xC0, 0xC1 (1543C-C, 1543C-D, 1543C-E) + * clear CD-ROM DMA write bit, m5229, 0x4b, bit 7 + */ + pci_read_config_byte(dev, 0x4b, &tmpbyte); + /* + * clear bit 7 + */ + pci_write_config_byte(dev, 0x4b, tmpbyte & 0x7F); +#endif /* ALI_INIT_CODE_TEST */ + /* + * check m1533, 0x5e, bit 1~4 == 1001 => & 00011110 = 00010010 + */ + pci_read_config_byte(isa_dev, 0x5e, &tmpbyte); + chip_is_1543c_e = ((tmpbyte & 0x1e) == 0x12) ? 1: 0; + } + + /* + * CD_ROM DMA on (m5229, 0x53, bit0) + * Enable this bit even if we want to use PIO + * PIO FIFO off (m5229, 0x53, bit1) + * The hardware will use 0x54h and 0x55h to control PIO FIFO + */ + pci_read_config_byte(dev, 0x53, &tmpbyte); + tmpbyte = (tmpbyte & (~0x02)) | 0x01; + + pci_write_config_byte(dev, 0x53, tmpbyte); + + local_irq_restore(flags); + + return(ata66); +} + +void __init ide_init_ali15x3 (ide_hwif_t *hwif) +{ +#ifndef CONFIG_SPARC64 + byte ideic, inmir; + byte irq_routing_table[] = { -1, 9, 3, 10, 4, 5, 7, 6, + 1, 11, 0, 12, 0, 14, 0, 15 }; + + hwif->irq = hwif->channel ? 15 : 14; + + if (isa_dev) { + /* + * read IDE interface control + */ + pci_read_config_byte(isa_dev, 0x58, &ideic); + + /* bit0, bit1 */ + ideic = ideic & 0x03; + + /* get IRQ for IDE Controller */ + if ((hwif->channel && ideic == 0x03) || + (!hwif->channel && !ideic)) { + /* + * get SIRQ1 routing table + */ + pci_read_config_byte(isa_dev, 0x44, &inmir); + inmir = inmir & 0x0f; + hwif->irq = irq_routing_table[inmir]; + } else if (hwif->channel && !(ideic & 0x01)) { + /* + * get SIRQ2 routing table + */ + pci_read_config_byte(isa_dev, 0x75, &inmir); + inmir = inmir & 0x0f; + hwif->irq = irq_routing_table[inmir]; + } + } +#endif /* CONFIG_SPARC64 */ + + hwif->autodma = 0; + hwif->tuneproc = &ali15x3_tune_drive; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + hwif->speedproc = &ali15x3_tune_chipset; + + if (!hwif->dma_base) + return; + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (m5229_revision >= 0x20) { + /* + * M1543C or newer for DMAing + */ + hwif->dmaproc = &ali15x3_dmaproc; +#ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +#endif /* CONFIG_IDEDMA_AUTO */ + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ +} + +void __init ide_dmacapable_ali15x3 (ide_hwif_t *hwif, unsigned long dmabase) +{ + if ((dmabase) && (m5229_revision < 0x20)) { + return; + } + ide_setup_dma(hwif, dmabase, 8); +} + +extern void ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d); + +void __init fixup_device_ali15x3 (struct pci_dev *dev, ide_pci_device_t *d) +{ + if (dev->resource[0].start != 0x01F1) + ide_register_xp_fix(dev); + + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); +} + diff -Nru a/drivers/ide/amd74xx.c b/drivers/ide/amd74xx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/amd74xx.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,519 @@ +/* + * linux/drivers/ide/amd74xx.c Version 0.05 June 9, 2000 + * + * Copyright (C) 1999-2000 Andre Hedrick + * May be copied or modified under the terms of the GNU General Public License + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "ide_modes.h" + +#define DISPLAY_VIPER_TIMINGS + +#if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +static int amd74xx_get_info(char *, char **, off_t, int); +extern int (*amd74xx_display_info)(char *, char **, off_t, int); /* ide-proc.c */ +static struct pci_dev *bmide_dev; + +static int amd74xx_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + u32 bibma = pci_resource_start(bmide_dev, 4); + u8 c0 = 0, c1 = 0; + + /* + * at that point bibma+0x2 et bibma+0xa are byte registers + * to investigate: + */ + c0 = IN_BYTE((unsigned short)bibma + 0x02); + c1 = IN_BYTE((unsigned short)bibma + 0x0a); + + p += sprintf(p, "\n " + "AMD %04X VIPER Chipset.\n", bmide_dev->device); + p += sprintf(p, "--------------- Primary Channel " + "---------------- Secondary Channel " + "-------------\n"); + p += sprintf(p, " %sabled " + " %sabled\n", + (c0&0x80) ? "dis" : " en", + (c1&0x80) ? "dis" : " en"); + p += sprintf(p, "--------------- drive0 --------- drive1 " + "-------- drive0 ---------- drive1 ------\n"); + p += sprintf(p, "DMA enabled: %s %s " + " %s %s\n", + (c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ", + (c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " ); + p += sprintf(p, "UDMA\n"); + p += sprintf(p, "DMA\n"); + p += sprintf(p, "PIO\n"); + + return p-buffer; /* => must be less than 4k! */ +} +#endif /* defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS) */ + +byte amd74xx_proc = 0; + +static int amd74xx_mode5_check (struct pci_dev *dev) +{ + switch(dev->device) { + case PCI_DEVICE_ID_AMD_VIPER_7411: + case PCI_DEVICE_ID_AMD_OPUS_7441: + return 1; + default: + return 0; + } +} + +static unsigned int amd74xx_swdma_check (struct pci_dev *dev) +{ + unsigned int class_rev; + + if (amd74xx_mode5_check(dev)) + return 1; + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + return ((int) (class_rev >= 7) ? 1 : 0); +} + +static int amd74xx_swdma_error (ide_drive_t *drive) +{ + printk("%s: single-word DMA not support (revision < C4)\n", drive->name); + return 0; +} + +static byte amd74xx_ratemask (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + byte mode = 0x00; + + switch(dev->device) { + case PCI_DEVICE_ID_AMD_OPUS_7441: + case PCI_DEVICE_ID_AMD_VIPER_7411: { mode |= 0x03; break; } + case PCI_DEVICE_ID_AMD_VIPER_7409: { mode |= 0x02; break; } + case PCI_DEVICE_ID_AMD_COBRA_7401: { mode |= 0x01; break; } + default: + return (mode &= ~0xFF); + } + + if (!eighty_ninty_three(drive)) { + mode &= ~0xFE; + mode |= 0x01; + } + return (mode &= ~0xF8); +} + +static byte amd74xx_ratefilter (ide_drive_t *drive, byte speed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + byte mode = amd74xx_ratemask(drive); + + switch(mode) { + case 0x04: // while (speed > XFER_UDMA_6) speed--; break; + case 0x03: while (speed > XFER_UDMA_5) speed--; break; + case 0x02: while (speed > XFER_UDMA_4) speed--; break; + case 0x01: while (speed > XFER_UDMA_2) speed--; break; + case 0x00: + default: while (speed > XFER_MW_DMA_2) speed--; break; + break; + } +#else + while (speed > XFER_PIO_4) speed--; +#endif /* CONFIG_BLK_DEV_IDEDMA */ +// printk("%s: mode == %02x speed == %02x\n", drive->name, mode, speed); + return speed; +} + +/* + * Here is where all the hard work goes to program the chipset. + */ +static int amd74xx_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte speed = amd74xx_ratefilter(drive, xferspeed); + byte drive_pci = 0x00; + byte drive_pci2 = 0x00; + byte ultra_timing = 0x00; + byte dma_pio_timing = 0x00; + byte pio_timing = 0x00; + + switch (drive->dn) { + case 0: drive_pci = 0x53; drive_pci2 = 0x4b; break; + case 1: drive_pci = 0x52; drive_pci2 = 0x4a; break; + case 2: drive_pci = 0x51; drive_pci2 = 0x49; break; + case 3: drive_pci = 0x50; drive_pci2 = 0x48; break; + default: + return -1; + } + + pci_read_config_byte(dev, drive_pci, &ultra_timing); + pci_read_config_byte(dev, drive_pci2, &dma_pio_timing); + pci_read_config_byte(dev, 0x4c, &pio_timing); + + ultra_timing &= ~0xC7; + dma_pio_timing &= ~0xFF; + pio_timing &= ~(0x03 << drive->dn); + + switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA + case XFER_UDMA_7: + case XFER_UDMA_6: + speed = XFER_UDMA_5; + case XFER_UDMA_5: + ultra_timing |= 0x46; + dma_pio_timing |= 0x20; + break; + case XFER_UDMA_4: + ultra_timing |= 0x45; + dma_pio_timing |= 0x20; + break; + case XFER_UDMA_3: + ultra_timing |= 0x44; + dma_pio_timing |= 0x20; + break; + case XFER_UDMA_2: + ultra_timing |= 0x40; + dma_pio_timing |= 0x20; + break; + case XFER_UDMA_1: + ultra_timing |= 0x41; + dma_pio_timing |= 0x20; + break; + case XFER_UDMA_0: + ultra_timing |= 0x42; + dma_pio_timing |= 0x20; + break; + case XFER_MW_DMA_2: + dma_pio_timing |= 0x20; + break; + case XFER_MW_DMA_1: + dma_pio_timing |= 0x21; + break; + case XFER_MW_DMA_0: + dma_pio_timing |= 0x77; + break; + case XFER_SW_DMA_2: + if (!amd74xx_swdma_check(dev)) + return amd74xx_swdma_error(drive); + dma_pio_timing |= 0x42; + break; + case XFER_SW_DMA_1: + if (!amd74xx_swdma_check(dev)) + return amd74xx_swdma_error(drive); + dma_pio_timing |= 0x65; + break; + case XFER_SW_DMA_0: + if (!amd74xx_swdma_check(dev)) + return amd74xx_swdma_error(drive); + dma_pio_timing |= 0xA8; + break; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: + dma_pio_timing |= 0x20; + break; + case XFER_PIO_3: + dma_pio_timing |= 0x22; + break; + case XFER_PIO_2: + dma_pio_timing |= 0x42; + break; + case XFER_PIO_1: + dma_pio_timing |= 0x65; + break; + case XFER_PIO_0: + default: + dma_pio_timing |= 0xA8; + break; + } + + pio_timing |= (0x03 << drive->dn); + +#ifdef CONFIG_BLK_DEV_IDEDMA + pci_write_config_byte(dev, drive_pci, ultra_timing); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + pci_write_config_byte(dev, drive_pci2, dma_pio_timing); + pci_write_config_byte(dev, 0x4c, pio_timing); + + return (ide_config_drive_speed(drive, speed)); +} + +static void amd74xx_tune_drive (ide_drive_t *drive, byte pio) +{ + byte speed; + pio = ide_get_best_pio_mode(drive, pio, 5, NULL); + switch(pio) { + case 4: speed = XFER_PIO_4;break; + case 3: speed = XFER_PIO_3;break; + case 2: speed = XFER_PIO_2;break; + case 1: speed = XFER_PIO_1;break; + default: speed = XFER_PIO_0;break; + } + (void) amd74xx_tune_chipset(drive, speed); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +/* + * This allows the configuration of ide_pci chipset registers + * for cards that learn about the drive's UDMA, DMA, PIO capabilities + * after the drive is reported by the OS. + */ +static int config_chipset_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + byte mode = amd74xx_ratemask(drive); + byte swdma = amd74xx_swdma_check(HWIF(drive)->pci_dev); + byte speed = 0; + int rval; + + amd74xx_tune_drive(drive, 5); + + switch(mode) { + case 0x04: + if (id->dma_ultra & 0x0040) + { speed = XFER_UDMA_6; break; } + case 0x03: + if (id->dma_ultra & 0x0020) + { speed = XFER_UDMA_5; break; } + case 0x02: + if (id->dma_ultra & 0x0010) + { speed = XFER_UDMA_4; break; } + if (id->dma_ultra & 0x0008) + { speed = XFER_UDMA_3; break; } + case 0x01: + if (id->dma_ultra & 0x0004) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0002) + { speed = XFER_UDMA_1; break; } + if (id->dma_ultra & 0x0001) + { speed = XFER_UDMA_0; break; } + case 0x00: + if (id->dma_mword & 0x0004) + { speed = XFER_MW_DMA_2; break; } + if (id->dma_mword & 0x0002) + { speed = XFER_MW_DMA_1; break; } + if (id->dma_mword & 0x0001) + { speed = XFER_MW_DMA_0; break; } + if ((id->dma_1word & 0x0004) && (swdma)) + { speed = XFER_SW_DMA_2; break; } + if ((id->dma_1word & 0x0002) && (swdma)) + { speed = XFER_SW_DMA_1; break; } + if ((id->dma_1word & 0x0001) && (swdma)) + { speed = XFER_SW_DMA_0; break; } + default: + return ((int) ide_dma_off_quietly); + } + + (void) amd74xx_tune_chipset(drive, speed); +// return ((int) (dma) ? ide_dma_on : ide_dma_off_quietly); + rval = (int)( ((id->dma_ultra >> 11) & 7) ? ide_dma_on : + ((id->dma_ultra >> 8) & 7) ? ide_dma_on : + ((id->dma_mword >> 8) & 7) ? ide_dma_on : + (((id->dma_1word >> 8) & 7) && (swdma)) ? ide_dma_on : + ide_dma_off_quietly); + return rval; +} + +static int config_drive_xfer_rate (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + ide_dma_action_t dma_func = ide_dma_on; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && hwif->autodma) { + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + goto fast_ata_pio; + } + dma_func = ide_dma_off_quietly; + if (id->field_valid & 4) { + if (id->dma_ultra & 0x003F) { + /* Force if Capable UltraDMA */ + dma_func = config_chipset_for_dma(drive); + if ((id->field_valid & 2) && + (dma_func != ide_dma_on)) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & 0x0007) || + ((id->dma_1word & 0x007) && + (amd74xx_swdma_check(HWIF(drive)->pci_dev)))) { + /* Force if Capable regular DMA modes */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } + + } else if (ide_dmaproc(ide_dma_good_drive, drive)) { + if (id->eide_dma_time > 150) { + goto no_dma_set; + } + /* Consult the list of known "good" drives */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + dma_func = ide_dma_off_quietly; +no_dma_set: + amd74xx_tune_drive(drive, 5); + } + return HWIF(drive)->dmaproc(dma_func, drive); +} + +/* + * amd74xx_dmaproc() initiates/aborts (U)DMA read/write operations on a drive. + */ + +int amd74xx_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + return config_drive_xfer_rate(drive); + default: + break; + } + return ide_dmaproc(func, drive); /* use standard DMA stuff */ +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +unsigned int __init pci_init_amd74xx (struct pci_dev *dev, const char *name) +{ + unsigned long fixdma_base = pci_resource_start(dev, 4); + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (!amd74xx_swdma_check(dev)) + printk("%s: disabling single-word DMA support (revision < C4)\n", name); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + if (!fixdma_base) { + /* + * + */ + } else { + /* + * enable DMA capable bit, and "not" simplex only + */ + OUT_BYTE(IN_BYTE(fixdma_base+2) & 0x60, fixdma_base+2); + + if (IN_BYTE(fixdma_base+2) & 0x80) + printk("%s: simplex device: DMA will fail!!\n", name); + } +#if defined(DISPLAY_VIPER_TIMINGS) && defined(CONFIG_PROC_FS) + if (!amd74xx_proc) { + amd74xx_proc = 1; + bmide_dev = dev; + amd74xx_display_info = &amd74xx_get_info; + } +#endif /* DISPLAY_VIPER_TIMINGS && CONFIG_PROC_FS */ + + return 0; +} + +unsigned int __init ata66_amd74xx (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + byte cable_80_pin[2] = { 0, 0 }; + byte ata66 = 0; + byte tmpbyte; + + /* + * Ultra66 cable detection (from Host View) + * 7411, 7441, 0x42, bit0: primary, bit2: secondary 80 pin + */ + pci_read_config_byte(dev, 0x42, &tmpbyte); + + /* + * 0x42, bit0 is 1 => primary channel + * has 80-pin (from host view) + */ + if (tmpbyte & 0x01) cable_80_pin[0] = 1; + + /* + * 0x42, bit2 is 1 => secondary channel + * has 80-pin (from host view) + */ + if (tmpbyte & 0x04) cable_80_pin[1] = 1; + + switch(dev->device) { + case PCI_DEVICE_ID_AMD_OPUS_7441: + case PCI_DEVICE_ID_AMD_VIPER_7411: + ata66 = (hwif->channel) ? + cable_80_pin[1] : + cable_80_pin[0]; + default: + break; + } +#ifdef CONFIG_AMD74XX_OVERRIDE + return(1); +#else + return (unsigned int) ata66; +#endif /* CONFIG_AMD74XX_OVERRIDE */ +} + +void __init ide_init_amd74xx (ide_hwif_t *hwif) +{ + hwif->tuneproc = &amd74xx_tune_drive; + hwif->speedproc = &amd74xx_tune_chipset; + + if (!hwif->dma_base) { + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + hwif->autodma = 0; + return; + } + +#ifndef CONFIG_BLK_DEV_IDEDMA + hwif->dmaproc = &amd74xx_dmaproc; +#ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +#endif /* CONFIG_IDEDMA_AUTO */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ +} + +void __init ide_dmacapable_amd74xx (ide_hwif_t *hwif, unsigned long dmabase) +{ + ide_setup_dma(hwif, dmabase, 8); +} + +extern void ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d); + +void __init fixup_device_amd74xx (struct pci_dev *dev, ide_pci_device_t *d) +{ + if (dev->resource[0].start != 0x01F1) + ide_register_xp_fix(dev); + + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); +} + diff -Nru a/drivers/ide/buddha.c b/drivers/ide/buddha.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/buddha.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,225 @@ +/* + * linux/drivers/ide/buddha.c -- Amiga Buddha, Catweasel and X-Surf IDE Driver + * + * Copyright (C) 1997, 2001 by Geert Uytterhoeven and others + * + * This driver was written based on the specifications in README.buddha and + * the X-Surf info from Inside_XSurf.txt available at + * http://www.jschoenfeld.com + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + * TODO: + * - test it :-) + * - tune the timings using the speed-register + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + + /* + * The Buddha has 2 IDE interfaces, the Catweasel has 3, X-Surf has 2 + */ + +#define BUDDHA_NUM_HWIFS 2 +#define CATWEASEL_NUM_HWIFS 3 +#define XSURF_NUM_HWIFS 2 + + /* + * Bases of the IDE interfaces (relative to the board address) + */ + +#define BUDDHA_BASE1 0x800 +#define BUDDHA_BASE2 0xa00 +#define BUDDHA_BASE3 0xc00 + +#define XSURF_BASE1 0xb000 /* 2.5" Interface */ +#define XSURF_BASE2 0xd000 /* 3.5" Interface */ + +static u_int buddha_bases[CATWEASEL_NUM_HWIFS] __initdata = { + BUDDHA_BASE1, BUDDHA_BASE2, BUDDHA_BASE3 +}; + +static u_int xsurf_bases[XSURF_NUM_HWIFS] __initdata = { + XSURF_BASE1, XSURF_BASE2 +}; + + + /* + * Offsets from one of the above bases + */ + +#define BUDDHA_DATA 0x00 +#define BUDDHA_ERROR 0x06 /* see err-bits */ +#define BUDDHA_NSECTOR 0x0a /* nr of sectors to read/write */ +#define BUDDHA_SECTOR 0x0e /* starting sector */ +#define BUDDHA_LCYL 0x12 /* starting cylinder */ +#define BUDDHA_HCYL 0x16 /* high byte of starting cyl */ +#define BUDDHA_SELECT 0x1a /* 101dhhhh , d=drive, hhhh=head */ +#define BUDDHA_STATUS 0x1e /* see status-bits */ +#define BUDDHA_CONTROL 0x11a +#define XSURF_CONTROL -1 /* X-Surf has no CS1* (Control/AltStat) */ + +static int buddha_offsets[IDE_NR_PORTS] __initdata = { + BUDDHA_DATA, BUDDHA_ERROR, BUDDHA_NSECTOR, BUDDHA_SECTOR, BUDDHA_LCYL, + BUDDHA_HCYL, BUDDHA_SELECT, BUDDHA_STATUS, BUDDHA_CONTROL, -1 +}; + +static int xsurf_offsets[IDE_NR_PORTS] __initdata = { + BUDDHA_DATA, BUDDHA_ERROR, BUDDHA_NSECTOR, BUDDHA_SECTOR, BUDDHA_LCYL, + BUDDHA_HCYL, BUDDHA_SELECT, BUDDHA_STATUS, XSURF_CONTROL, -1 +}; + + /* + * Other registers + */ + +#define BUDDHA_IRQ1 0xf00 /* MSB = 1, Harddisk is source of */ +#define BUDDHA_IRQ2 0xf40 /* interrupt */ +#define BUDDHA_IRQ3 0xf80 + +#define XSURF_IRQ1 0x7e +#define XSURF_IRQ2 0x7e + +static int buddha_irqports[CATWEASEL_NUM_HWIFS] __initdata = { + BUDDHA_IRQ1, BUDDHA_IRQ2, BUDDHA_IRQ3 +}; + +static int xsurf_irqports[XSURF_NUM_HWIFS] __initdata = { + XSURF_IRQ1, XSURF_IRQ2 +}; + +#define BUDDHA_IRQ_MR 0xfc0 /* master interrupt enable */ + + + /* + * Board information + */ + +typedef enum BuddhaType_Enum { + BOARD_BUDDHA, BOARD_CATWEASEL, BOARD_XSURF +} BuddhaType; + + + /* + * Check and acknowledge the interrupt status + */ + +static int buddha_ack_intr(ide_hwif_t *hwif) +{ + unsigned char ch; + + ch = z_readb(hwif->io_ports[IDE_IRQ_OFFSET]); + if (!(ch & 0x80)) + return 0; + return 1; +} + +static int xsurf_ack_intr(ide_hwif_t *hwif) +{ + unsigned char ch; + + ch = z_readb(hwif->io_ports[IDE_IRQ_OFFSET]); + /* X-Surf needs a 0 written to IRQ register to ensure ISA bit A11 stays at 0 */ + z_writeb(0, hwif->io_ports[IDE_IRQ_OFFSET]); + if (!(ch & 0x80)) + return 0; + return 1; +} + + /* + * Probe for a Buddha or Catweasel IDE interface + */ + +void __init buddha_init(void) +{ + hw_regs_t hw; + int i, index; + + struct zorro_dev *z = NULL; + u_long buddha_board = 0; + BuddhaType type; + int buddha_num_hwifs; + + while ((z = zorro_find_device(ZORRO_WILDCARD, z))) { + unsigned long board; + if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_BUDDHA) { + buddha_num_hwifs = BUDDHA_NUM_HWIFS; + type=BOARD_BUDDHA; + } else if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_CATWEASEL) { + buddha_num_hwifs = CATWEASEL_NUM_HWIFS; + type=BOARD_CATWEASEL; + } else if (z->id == ZORRO_PROD_INDIVIDUAL_COMPUTERS_X_SURF) { + buddha_num_hwifs = XSURF_NUM_HWIFS; + type=BOARD_XSURF; + } else + continue; + + board = z->resource.start; + + if(type != BOARD_XSURF) { + if (!request_mem_region(board+BUDDHA_BASE1, 0x800, "IDE")) + continue; + } else { + if (!request_mem_region(board+XSURF_BASE1, 0x1000, "IDE")) + continue; + if (!request_mem_region(board+XSURF_BASE2, 0x1000, "IDE")) + goto fail_base2; + if (!request_mem_region(board+XSURF_IRQ1, 0x8, "IDE")) { + release_mem_region(board+XSURF_BASE2, 0x1000); +fail_base2: + release_mem_region(board+XSURF_BASE1, 0x1000); + continue; + } + } + buddha_board = ZTWO_VADDR(board); + + /* write to BUDDHA_IRQ_MR to enable the board IRQ */ + /* X-Surf doesn't have this. IRQs are always on */ + if (type != BOARD_XSURF) + z_writeb(0, buddha_board+BUDDHA_IRQ_MR); + + for(i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ide_modes.h" + +/* + * This flag is set in ide.c by the parameter: ide0=cmd640_vlb + */ +int cmd640_vlb = 0; + +/* + * CMD640 specific registers definition. + */ + +#define VID 0x00 +#define DID 0x02 +#define PCMD 0x04 +#define PCMD_ENA 0x01 +#define PSTTS 0x06 +#define REVID 0x08 +#define PROGIF 0x09 +#define SUBCL 0x0a +#define BASCL 0x0b +#define BaseA0 0x10 +#define BaseA1 0x14 +#define BaseA2 0x18 +#define BaseA3 0x1c +#define INTLINE 0x3c +#define INPINE 0x3d + +#define CFR 0x50 +#define CFR_DEVREV 0x03 +#define CFR_IDE01INTR 0x04 +#define CFR_DEVID 0x18 +#define CFR_AT_VESA_078h 0x20 +#define CFR_DSA1 0x40 +#define CFR_DSA0 0x80 + +#define CNTRL 0x51 +#define CNTRL_DIS_RA0 0x40 +#define CNTRL_DIS_RA1 0x80 +#define CNTRL_ENA_2ND 0x08 + +#define CMDTIM 0x52 +#define ARTTIM0 0x53 +#define DRWTIM0 0x54 +#define ARTTIM1 0x55 +#define DRWTIM1 0x56 +#define ARTTIM23 0x57 +#define ARTTIM23_DIS_RA2 0x04 +#define ARTTIM23_DIS_RA3 0x08 +#define DRWTIM23 0x58 +#define BRST 0x59 + +/* + * Registers and masks for easy access by drive index: + */ +static byte prefetch_regs[4] = {CNTRL, CNTRL, ARTTIM23, ARTTIM23}; +static byte prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3}; + +#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED + +static byte arttim_regs[4] = {ARTTIM0, ARTTIM1, ARTTIM23, ARTTIM23}; +static byte drwtim_regs[4] = {DRWTIM0, DRWTIM1, DRWTIM23, DRWTIM23}; + +/* + * Current cmd640 timing values for each drive. + * The defaults for each are the slowest possible timings. + */ +static byte setup_counts[4] = {4, 4, 4, 4}; /* Address setup count (in clocks) */ +static byte active_counts[4] = {16, 16, 16, 16}; /* Active count (encoded) */ +static byte recovery_counts[4] = {16, 16, 16, 16}; /* Recovery count (encoded) */ + +#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */ + +/* + * These are initialized to point at the devices we control + */ +static ide_hwif_t *cmd_hwif0, *cmd_hwif1; +static ide_drive_t *cmd_drives[4]; + +/* + * Interface to access cmd640x registers + */ +static unsigned int cmd640_key; +static void (*put_cmd640_reg)(unsigned short reg, byte val); +static byte (*get_cmd640_reg)(unsigned short reg); + +/* + * This is read from the CFR reg, and is used in several places. + */ +static unsigned int cmd640_chip_version; + +/* + * The CMD640x chip does not support DWORD config write cycles, but some + * of the BIOSes use them to implement the config services. + * Therefore, we must use direct IO instead. + */ + +/* PCI method 1 access */ + +static void put_cmd640_reg_pci1 (unsigned short reg, byte val) +{ + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + outb_p((reg & 0xfc) | cmd640_key, 0xcf8); + outb_p(val, (reg & 3) | 0xcfc); + spin_unlock_irqrestore(&ide_lock, flags); +} + +static byte get_cmd640_reg_pci1 (unsigned short reg) +{ + byte b; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + outb_p((reg & 0xfc) | cmd640_key, 0xcf8); + b = inb_p((reg & 3) | 0xcfc); + spin_unlock_irqrestore(&ide_lock, flags); + return b; +} + +/* PCI method 2 access (from CMD datasheet) */ + +static void put_cmd640_reg_pci2 (unsigned short reg, byte val) +{ + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + outb_p(0x10, 0xcf8); + outb_p(val, cmd640_key + reg); + outb_p(0, 0xcf8); + spin_unlock_irqrestore(&ide_lock, flags); +} + +static byte get_cmd640_reg_pci2 (unsigned short reg) +{ + byte b; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + outb_p(0x10, 0xcf8); + b = inb_p(cmd640_key + reg); + outb_p(0, 0xcf8); + spin_unlock_irqrestore(&ide_lock, flags); + return b; +} + +/* VLB access */ + +static void put_cmd640_reg_vlb (unsigned short reg, byte val) +{ + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + outb_p(reg, cmd640_key); + outb_p(val, cmd640_key + 4); + spin_unlock_irqrestore(&ide_lock, flags); +} + +static byte get_cmd640_reg_vlb (unsigned short reg) +{ + byte b; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + outb_p(reg, cmd640_key); + b = inb_p(cmd640_key + 4); + spin_unlock_irqrestore(&ide_lock, flags); + return b; +} + +static int __init match_pci_cmd640_device (void) +{ + const byte ven_dev[4] = {0x95, 0x10, 0x40, 0x06}; + unsigned int i; + for (i = 0; i < 4; i++) { + if (get_cmd640_reg(i) != ven_dev[i]) + return 0; + } +#ifdef STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT + if ((get_cmd640_reg(PCMD) & PCMD_ENA) == 0) { + printk("ide: cmd640 on PCI disabled by BIOS\n"); + return 0; + } +#endif /* STUPIDLY_TRUST_BROKEN_PCMD_ENA_BIT */ + return 1; /* success */ +} + +/* + * Probe for CMD640x -- pci method 1 + */ +static int __init probe_for_cmd640_pci1 (void) +{ + get_cmd640_reg = get_cmd640_reg_pci1; + put_cmd640_reg = put_cmd640_reg_pci1; + for (cmd640_key = 0x80000000; + cmd640_key <= 0x8000f800; + cmd640_key += 0x800) { + if (match_pci_cmd640_device()) + return 1; /* success */ + } + return 0; +} + +/* + * Probe for CMD640x -- pci method 2 + */ +static int __init probe_for_cmd640_pci2 (void) +{ + get_cmd640_reg = get_cmd640_reg_pci2; + put_cmd640_reg = put_cmd640_reg_pci2; + for (cmd640_key = 0xc000; cmd640_key <= 0xcf00; cmd640_key += 0x100) { + if (match_pci_cmd640_device()) + return 1; /* success */ + } + return 0; +} + +/* + * Probe for CMD640x -- vlb + */ +static int __init probe_for_cmd640_vlb (void) +{ + byte b; + + get_cmd640_reg = get_cmd640_reg_vlb; + put_cmd640_reg = put_cmd640_reg_vlb; + cmd640_key = 0x178; + b = get_cmd640_reg(CFR); + if (b == 0xff || b == 0x00 || (b & CFR_AT_VESA_078h)) { + cmd640_key = 0x78; + b = get_cmd640_reg(CFR); + if (b == 0xff || b == 0x00 || !(b & CFR_AT_VESA_078h)) + return 0; + } + return 1; /* success */ +} + +/* + * Returns 1 if an IDE interface/drive exists at 0x170, + * Returns 0 otherwise. + */ +static int __init secondary_port_responding (void) +{ + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + + outb_p(0x0a, 0x170 + IDE_SELECT_OFFSET); /* select drive0 */ + udelay(100); + if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x0a) { + outb_p(0x1a, 0x170 + IDE_SELECT_OFFSET); /* select drive1 */ + udelay(100); + if ((inb_p(0x170 + IDE_SELECT_OFFSET) & 0x1f) != 0x1a) { + spin_unlock_irqrestore(&ide_lock, flags); + return 0; /* nothing responded */ + } + } + spin_unlock_irqrestore(&ide_lock, flags); + return 1; /* success */ +} + +#ifdef CMD640_DUMP_REGS +/* + * Dump out all cmd640 registers. May be called from ide.c + */ +void cmd640_dump_regs (void) +{ + unsigned int reg = cmd640_vlb ? 0x50 : 0x00; + + /* Dump current state of chip registers */ + printk("ide: cmd640 internal register dump:"); + for (; reg <= 0x59; reg++) { + if (!(reg & 0x0f)) + printk("\n%04x:", reg); + printk(" %02x", get_cmd640_reg(reg)); + } + printk("\n"); +} +#endif + +/* + * Check whether prefetch is on for a drive, + * and initialize the unmask flags for safe operation. + */ +static void __init check_prefetch (unsigned int index) +{ + ide_drive_t *drive = cmd_drives[index]; + byte b = get_cmd640_reg(prefetch_regs[index]); + + if (b & prefetch_masks[index]) { /* is prefetch off? */ + drive->no_unmask = 0; + drive->no_io_32bit = 1; + drive->io_32bit = 0; + } else { +#if CMD640_PREFETCH_MASKS + drive->no_unmask = 1; + drive->unmask = 0; +#endif + drive->no_io_32bit = 0; + } +} + +/* + * Figure out which devices we control + */ +static void __init setup_device_ptrs (void) +{ + unsigned int i; + + cmd_hwif0 = &ide_hwifs[0]; /* default, if not found below */ + cmd_hwif1 = &ide_hwifs[1]; /* default, if not found below */ + for (i = 0; i < MAX_HWIFS; i++) { + ide_hwif_t *hwif = &ide_hwifs[i]; + if (hwif->chipset == ide_unknown || hwif->chipset == ide_generic) { + if (hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0) + cmd_hwif0 = hwif; + else if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170) + cmd_hwif1 = hwif; + } + } + cmd_drives[0] = &cmd_hwif0->drives[0]; + cmd_drives[1] = &cmd_hwif0->drives[1]; + cmd_drives[2] = &cmd_hwif1->drives[0]; + cmd_drives[3] = &cmd_hwif1->drives[1]; +} + +#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED + +/* + * Sets prefetch mode for a drive. + */ +static void set_prefetch_mode (unsigned int index, int mode) +{ + ide_drive_t *drive = cmd_drives[index]; + int reg = prefetch_regs[index]; + byte b; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + b = get_cmd640_reg(reg); + if (mode) { /* want prefetch on? */ +#if CMD640_PREFETCH_MASKS + drive->no_unmask = 1; + drive->unmask = 0; +#endif + drive->no_io_32bit = 0; + b &= ~prefetch_masks[index]; /* enable prefetch */ + } else { + drive->no_unmask = 0; + drive->no_io_32bit = 1; + drive->io_32bit = 0; + b |= prefetch_masks[index]; /* disable prefetch */ + } + put_cmd640_reg(reg, b); + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * Dump out current drive clocks settings + */ +static void display_clocks (unsigned int index) +{ + byte active_count, recovery_count; + + active_count = active_counts[index]; + if (active_count == 1) + ++active_count; + recovery_count = recovery_counts[index]; + if (active_count > 3 && recovery_count == 1) + ++recovery_count; + if (cmd640_chip_version > 1) + recovery_count += 1; /* cmd640b uses (count + 1)*/ + printk(", clocks=%d/%d/%d\n", setup_counts[index], active_count, recovery_count); +} + +/* + * Pack active and recovery counts into single byte representation + * used by controller + */ +inline static byte pack_nibbles (byte upper, byte lower) +{ + return ((upper & 0x0f) << 4) | (lower & 0x0f); +} + +/* + * This routine retrieves the initial drive timings from the chipset. + */ +static void __init retrieve_drive_counts (unsigned int index) +{ + byte b; + + /* + * Get the internal setup timing, and convert to clock count + */ + b = get_cmd640_reg(arttim_regs[index]) & ~0x3f; + switch (b) { + case 0x00: b = 4; break; + case 0x80: b = 3; break; + case 0x40: b = 2; break; + default: b = 5; break; + } + setup_counts[index] = b; + + /* + * Get the active/recovery counts + */ + b = get_cmd640_reg(drwtim_regs[index]); + active_counts[index] = (b >> 4) ? (b >> 4) : 0x10; + recovery_counts[index] = (b & 0x0f) ? (b & 0x0f) : 0x10; +} + + +/* + * This routine writes the prepared setup/active/recovery counts + * for a drive into the cmd640 chipset registers to active them. + */ +static void program_drive_counts (unsigned int index) +{ + unsigned long flags; + byte setup_count = setup_counts[index]; + byte active_count = active_counts[index]; + byte recovery_count = recovery_counts[index]; + + /* + * Set up address setup count and drive read/write timing registers. + * Primary interface has individual count/timing registers for + * each drive. Secondary interface has one common set of registers, + * so we merge the timings, using the slowest value for each timing. + */ + if (index > 1) { + unsigned int mate; + if (cmd_drives[mate = index ^ 1]->present) { + if (setup_count < setup_counts[mate]) + setup_count = setup_counts[mate]; + if (active_count < active_counts[mate]) + active_count = active_counts[mate]; + if (recovery_count < recovery_counts[mate]) + recovery_count = recovery_counts[mate]; + } + } + + /* + * Convert setup_count to internal chipset representation + */ + switch (setup_count) { + case 4: setup_count = 0x00; break; + case 3: setup_count = 0x80; break; + case 1: + case 2: setup_count = 0x40; break; + default: setup_count = 0xc0; /* case 5 */ + } + + /* + * Now that everything is ready, program the new timings + */ + spin_lock_irqsave(&ide_lock, flags); + /* + * Program the address_setup clocks into ARTTIM reg, + * and then the active/recovery counts into the DRWTIM reg + * (this converts counts of 16 into counts of zero -- okay). + */ + setup_count |= get_cmd640_reg(arttim_regs[index]) & 0x3f; + put_cmd640_reg(arttim_regs[index], setup_count); + put_cmd640_reg(drwtim_regs[index], pack_nibbles(active_count, recovery_count)); + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * Set a specific pio_mode for a drive + */ +static void cmd640_set_mode (unsigned int index, byte pio_mode, unsigned int cycle_time) +{ + int setup_time, active_time, recovery_time, clock_time; + byte setup_count, active_count, recovery_count, recovery_count2, cycle_count; + int bus_speed = system_bus_clock(); + + if (pio_mode > 5) + pio_mode = 5; + setup_time = ide_pio_timings[pio_mode].setup_time; + active_time = ide_pio_timings[pio_mode].active_time; + recovery_time = cycle_time - (setup_time + active_time); + clock_time = 1000 / bus_speed; + cycle_count = (cycle_time + clock_time - 1) / clock_time; + + setup_count = (setup_time + clock_time - 1) / clock_time; + + active_count = (active_time + clock_time - 1) / clock_time; + if (active_count < 2) + active_count = 2; /* minimum allowed by cmd640 */ + + recovery_count = (recovery_time + clock_time - 1) / clock_time; + recovery_count2 = cycle_count - (setup_count + active_count); + if (recovery_count2 > recovery_count) + recovery_count = recovery_count2; + if (recovery_count < 2) + recovery_count = 2; /* minimum allowed by cmd640 */ + if (recovery_count > 17) { + active_count += recovery_count - 17; + recovery_count = 17; + } + if (active_count > 16) + active_count = 16; /* maximum allowed by cmd640 */ + if (cmd640_chip_version > 1) + recovery_count -= 1; /* cmd640b uses (count + 1)*/ + if (recovery_count > 16) + recovery_count = 16; /* maximum allowed by cmd640 */ + + setup_counts[index] = setup_count; + active_counts[index] = active_count; + recovery_counts[index] = recovery_count; + + /* + * In a perfect world, we might set the drive pio mode here + * (using WIN_SETFEATURE) before continuing. + * + * But we do not, because: + * 1) this is the wrong place to do it (proper is do_special() in ide.c) + * 2) in practice this is rarely, if ever, necessary + */ + program_drive_counts (index); +} + +/* + * Drive PIO mode selection: + */ +static void cmd640_tune_drive (ide_drive_t *drive, byte mode_wanted) +{ + byte b; + ide_pio_data_t d; + unsigned int index = 0; + + while (drive != cmd_drives[index]) { + if (++index > 3) { + printk("%s: bad news in cmd640_tune_drive\n", drive->name); + return; + } + } + switch (mode_wanted) { + case 6: /* set fast-devsel off */ + case 7: /* set fast-devsel on */ + mode_wanted &= 1; + b = get_cmd640_reg(CNTRL) & ~0x27; + if (mode_wanted) + b |= 0x27; + put_cmd640_reg(CNTRL, b); + printk("%s: %sabled cmd640 fast host timing (devsel)\n", drive->name, mode_wanted ? "en" : "dis"); + return; + + case 8: /* set prefetch off */ + case 9: /* set prefetch on */ + mode_wanted &= 1; + set_prefetch_mode(index, mode_wanted); + printk("%s: %sabled cmd640 prefetch\n", drive->name, mode_wanted ? "en" : "dis"); + return; + } + + (void) ide_get_best_pio_mode (drive, mode_wanted, 5, &d); + cmd640_set_mode (index, d.pio_mode, d.cycle_time); + + printk ("%s: selected cmd640 PIO mode%d (%dns)%s", + drive->name, + d.pio_mode, + d.cycle_time, + d.overridden ? " (overriding vendor mode)" : ""); + display_clocks(index); + return; +} + +#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */ + +static int pci_conf1(void) +{ + u32 tmp; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + OUT_BYTE(0x01, 0xCFB); + tmp = inl(0xCF8); + outl(0x80000000, 0xCF8); + if (inl(0xCF8) == 0x80000000) { + outl(tmp, 0xCF8); + spin_unlock_irqrestore(&ide_lock, flags); + return 1; + } + outl(tmp, 0xCF8); + spin_unlock_irqrestore(&ide_lock, flags); + return 0; +} + +static int pci_conf2(void) +{ + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + OUT_BYTE(0x00, 0xCFB); + OUT_BYTE(0x00, 0xCF8); + OUT_BYTE(0x00, 0xCFA); + if (IN_BYTE(0xCF8) == 0x00 && IN_BYTE(0xCF8) == 0x00) { + spin_unlock_irqrestore(&ide_lock, flags); + return 1; + } + spin_unlock_irqrestore(&ide_lock, flags); + return 0; +} + +/* + * Probe for a cmd640 chipset, and initialize it if found. Called from ide.c + */ +int __init ide_probe_for_cmd640x (void) +{ +#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED + int second_port_toggled = 0; +#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */ + int second_port_cmd640 = 0; + const char *bus_type, *port2; + unsigned int index; + byte b, cfr; + + if (cmd640_vlb && probe_for_cmd640_vlb()) { + bus_type = "VLB"; + } else { + cmd640_vlb = 0; + /* Find out what kind of PCI probing is supported otherwise + Justin Gibbs will sulk.. */ + if (pci_conf1() && probe_for_cmd640_pci1()) + bus_type = "PCI (type1)"; + else if (pci_conf2() && probe_for_cmd640_pci2()) + bus_type = "PCI (type2)"; + else + return 0; + } + /* + * Undocumented magic (there is no 0x5b reg in specs) + */ + put_cmd640_reg(0x5b, 0xbd); + if (get_cmd640_reg(0x5b) != 0xbd) { + printk("ide: cmd640 init failed: wrong value in reg 0x5b\n"); + return 0; + } + put_cmd640_reg(0x5b, 0); + +#ifdef CMD640_DUMP_REGS + CMD640_DUMP_REGS; +#endif + + /* + * Documented magic begins here + */ + cfr = get_cmd640_reg(CFR); + cmd640_chip_version = cfr & CFR_DEVREV; + if (cmd640_chip_version == 0) { + printk ("ide: bad cmd640 revision: %d\n", cmd640_chip_version); + return 0; + } + + /* + * Initialize data for primary port + */ + setup_device_ptrs (); + printk("%s: buggy cmd640%c interface on %s, config=0x%02x\n", + cmd_hwif0->name, 'a' + cmd640_chip_version - 1, bus_type, cfr); + cmd_hwif0->chipset = ide_cmd640; +#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED + cmd_hwif0->tuneproc = &cmd640_tune_drive; +#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */ + + /* + * Ensure compatibility by always using the slowest timings + * for access to the drive's command register block, + * and reset the prefetch burstsize to default (512 bytes). + * + * Maybe we need a way to NOT do these on *some* systems? + */ + put_cmd640_reg(CMDTIM, 0); + put_cmd640_reg(BRST, 0x40); + + /* + * Try to enable the secondary interface, if not already enabled + */ + if (cmd_hwif1->noprobe) { + port2 = "not probed"; + } else { + b = get_cmd640_reg(CNTRL); + if (secondary_port_responding()) { + if ((b & CNTRL_ENA_2ND)) { + second_port_cmd640 = 1; + port2 = "okay"; + } else if (cmd640_vlb) { + second_port_cmd640 = 1; + port2 = "alive"; + } else + port2 = "not cmd640"; + } else { + put_cmd640_reg(CNTRL, b ^ CNTRL_ENA_2ND); /* toggle the bit */ + if (secondary_port_responding()) { + second_port_cmd640 = 1; +#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED + second_port_toggled = 1; +#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */ + port2 = "enabled"; + } else { + put_cmd640_reg(CNTRL, b); /* restore original setting */ + port2 = "not responding"; + } + } + } + + /* + * Initialize data for secondary cmd640 port, if enabled + */ + if (second_port_cmd640) { + cmd_hwif0->serialized = 1; + cmd_hwif1->serialized = 1; + cmd_hwif1->chipset = ide_cmd640; + cmd_hwif0->mate = cmd_hwif1; + cmd_hwif1->mate = cmd_hwif0; + cmd_hwif1->channel = 1; +#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED + cmd_hwif1->tuneproc = &cmd640_tune_drive; +#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */ + } + printk("%s: %sserialized, secondary interface %s\n", cmd_hwif1->name, + cmd_hwif0->serialized ? "" : "not ", port2); + + /* + * Establish initial timings/prefetch for all drives. + * Do not unnecessarily disturb any prior BIOS setup of these. + */ + for (index = 0; index < (2 + (second_port_cmd640 << 1)); index++) { + ide_drive_t *drive = cmd_drives[index]; +#ifdef CONFIG_BLK_DEV_CMD640_ENHANCED + if (drive->autotune || ((index > 1) && second_port_toggled)) { + /* + * Reset timing to the slowest speed and turn off prefetch. + * This way, the drive identify code has a better chance. + */ + setup_counts [index] = 4; /* max possible */ + active_counts [index] = 16; /* max possible */ + recovery_counts [index] = 16; /* max possible */ + program_drive_counts (index); + set_prefetch_mode (index, 0); + printk("cmd640: drive%d timings/prefetch cleared\n", index); + } else { + /* + * Record timings/prefetch without changing them. + * This preserves any prior BIOS setup. + */ + retrieve_drive_counts (index); + check_prefetch (index); + printk("cmd640: drive%d timings/prefetch(%s) preserved", + index, drive->no_io_32bit ? "off" : "on"); + display_clocks(index); + } +#else + /* + * Set the drive unmask flags to match the prefetch setting + */ + check_prefetch (index); + printk("cmd640: drive%d timings/prefetch(%s) preserved\n", + index, drive->no_io_32bit ? "off" : "on"); +#endif /* CONFIG_BLK_DEV_CMD640_ENHANCED */ + } + +#ifdef CMD640_DUMP_REGS + CMD640_DUMP_REGS; +#endif + return 1; +} + diff -Nru a/drivers/ide/cmd64x.c b/drivers/ide/cmd64x.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/cmd64x.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,1236 @@ +/* $Id: cmd64x.c,v 1.21 2000/01/30 23:23:16 + * + * linux/drivers/ide/cmd64x.c Version 1.22 June 9, 2000 + * + * cmd64x.c: Enable interrupts at initialization time on Ultra/PCI machines. + * Note, this driver is not used at all on other systems because + * there the "BIOS" has done all of the following already. + * Due to massive hardware bugs, UltraDMA is only supported + * on the 646U2 and not on the 646U. + * + * Copyright (C) 1998 Eddie C. Dost (ecd@skynet.be) + * Copyright (C) 1998 David S. Miller (davem@redhat.com) + * + * Copyright (C) 1999-2002 Andre Hedrick + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ide_modes.h" + +#ifndef SPLIT_BYTE +#define SPLIT_BYTE(B,H,L) ((H)=(B>>4), (L)=(B-((B>>4)<<4))) +#endif + +#define CMD_DEBUG 0 + +#if CMD_DEBUG +#define cmdprintk(x...) printk(x) +#else +#define cmdprintk(x...) +#endif + +/* + * CMD64x specific registers definition. + */ + +#define CFR 0x50 +#define CFR_INTR_CH0 0x02 +#define CNTRL 0x51 +#define CNTRL_DIS_RA0 0x40 +#define CNTRL_DIS_RA1 0x80 +#define CNTRL_ENA_2ND 0x08 + +#define CMDTIM 0x52 +#define ARTTIM0 0x53 +#define DRWTIM0 0x54 +#define ARTTIM1 0x55 +#define DRWTIM1 0x56 +#define ARTTIM23 0x57 +#define ARTTIM23_DIS_RA2 0x04 +#define ARTTIM23_DIS_RA3 0x08 +#define ARTTIM23_INTR_CH1 0x10 +#define ARTTIM2 0x57 +#define ARTTIM3 0x57 +#define DRWTIM23 0x58 +#define DRWTIM2 0x58 +#define BRST 0x59 +#define DRWTIM3 0x5b + +#define BMIDECR0 0x70 +#define MRDMODE 0x71 +#define MRDMODE_INTR_CH0 0x04 +#define MRDMODE_INTR_CH1 0x08 +#define MRDMODE_BLK_CH0 0x10 +#define MRDMODE_BLK_CH1 0x20 +#define BMIDESR0 0x72 +#define UDIDETCR0 0x73 +#define DTPR0 0x74 +#define BMIDECR1 0x78 +#define BMIDECSR 0x79 +#define BMIDESR1 0x7A +#define UDIDETCR1 0x7B +#define DTPR1 0x7C + +#define DISPLAY_CMD64X_TIMINGS + +#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +static char * print_cmd64x_get_info(char *, struct pci_dev *, int); +static char * print_sii_get_info(char *, struct pci_dev *, int); +static int cmd64x_get_info(char *, char **, off_t, int); +extern int (*cmd64x_display_info)(char *, char **, off_t, int); /* ide-proc.c */ + +byte cmd64x_proc = 0; + +#define CMD_MAX_DEVS 5 + +static struct pci_dev *cmd_devs[CMD_MAX_DEVS]; +static int n_cmd_devs; + +#undef DEBUG_CMD_REGS + +static char * print_cmd64x_get_info (char *buf, struct pci_dev *dev, int index) +{ + char *p = buf; + + u8 reg53 = 0, reg54 = 0, reg55 = 0, reg56 = 0; /* primary */ + u8 reg57 = 0, reg58 = 0, reg5b; /* secondary */ + u8 reg72 = 0, reg73 = 0; /* primary */ + u8 reg7a = 0, reg7b = 0; /* secondary */ + u8 reg50 = 0, reg71 = 0; /* extra */ +#ifdef DEBUG_CMD_REGS + u8 hi_byte = 0, lo_byte = 0; +#endif /* DEBUG_CMD_REGS */ + + p += sprintf(p, "\nController: %d\n", index); + p += sprintf(p, "CMD%x Chipset.\n", dev->device); + (void) pci_read_config_byte(dev, CFR, ®50); + (void) pci_read_config_byte(dev, ARTTIM0, ®53); + (void) pci_read_config_byte(dev, DRWTIM0, ®54); + (void) pci_read_config_byte(dev, ARTTIM1, ®55); + (void) pci_read_config_byte(dev, DRWTIM1, ®56); + (void) pci_read_config_byte(dev, ARTTIM2, ®57); + (void) pci_read_config_byte(dev, DRWTIM2, ®58); + (void) pci_read_config_byte(dev, DRWTIM3, ®5b); + (void) pci_read_config_byte(dev, MRDMODE, ®71); + (void) pci_read_config_byte(dev, BMIDESR0, ®72); + (void) pci_read_config_byte(dev, UDIDETCR0, ®73); + (void) pci_read_config_byte(dev, BMIDESR1, ®7a); + (void) pci_read_config_byte(dev, UDIDETCR1, ®7b); + + p += sprintf(p, "--------------- Primary Channel " + "---------------- Secondary Channel " + "-------------\n"); + p += sprintf(p, " %sabled " + " %sabled\n", + (reg72&0x80)?"dis":" en", + (reg7a&0x80)?"dis":" en"); + p += sprintf(p, "--------------- drive0 " + "--------- drive1 -------- drive0 " + "---------- drive1 ------\n"); + p += sprintf(p, "DMA enabled: %s %s" + " %s %s\n", + (reg72&0x20)?"yes":"no ", (reg72&0x40)?"yes":"no ", + (reg7a&0x20)?"yes":"no ", (reg7a&0x40)?"yes":"no "); + + p += sprintf(p, "DMA Mode: %s(%s) %s(%s)", + (reg72&0x20)?((reg73&0x01)?"UDMA":" DMA"):" PIO", + (reg72&0x20)?( + ((reg73&0x30)==0x30)?(((reg73&0x35)==0x35)?"3":"0"): + ((reg73&0x20)==0x20)?(((reg73&0x25)==0x25)?"3":"1"): + ((reg73&0x10)==0x10)?(((reg73&0x15)==0x15)?"4":"2"): + ((reg73&0x00)==0x00)?(((reg73&0x05)==0x05)?"5":"2"): + "X"):"?", + (reg72&0x40)?((reg73&0x02)?"UDMA":" DMA"):" PIO", + (reg72&0x40)?( + ((reg73&0xC0)==0xC0)?(((reg73&0xC5)==0xC5)?"3":"0"): + ((reg73&0x80)==0x80)?(((reg73&0x85)==0x85)?"3":"1"): + ((reg73&0x40)==0x40)?(((reg73&0x4A)==0x4A)?"4":"2"): + ((reg73&0x00)==0x00)?(((reg73&0x0A)==0x0A)?"5":"2"): + "X"):"?"); + p += sprintf(p, " %s(%s) %s(%s)\n", + (reg7a&0x20)?((reg7b&0x01)?"UDMA":" DMA"):" PIO", + (reg7a&0x20)?( + ((reg7b&0x30)==0x30)?(((reg7b&0x35)==0x35)?"3":"0"): + ((reg7b&0x20)==0x20)?(((reg7b&0x25)==0x25)?"3":"1"): + ((reg7b&0x10)==0x10)?(((reg7b&0x15)==0x15)?"4":"2"): + ((reg7b&0x00)==0x00)?(((reg7b&0x05)==0x05)?"5":"2"): + "X"):"?", + (reg7a&0x40)?((reg7b&0x02)?"UDMA":" DMA"):" PIO", + (reg7a&0x40)?( + ((reg7b&0xC0)==0xC0)?(((reg7b&0xC5)==0xC5)?"3":"0"): + ((reg7b&0x80)==0x80)?(((reg7b&0x85)==0x85)?"3":"1"): + ((reg7b&0x40)==0x40)?(((reg7b&0x4A)==0x4A)?"4":"2"): + ((reg7b&0x00)==0x00)?(((reg7b&0x0A)==0x0A)?"5":"2"): + "X"):"?" ); + p += sprintf(p, "PIO Mode: %s %s" + " %s %s\n", + "?", "?", "?", "?"); + p += sprintf(p, " %s %s\n", + (reg50 & CFR_INTR_CH0) ? "interrupting" : "polling ", + (reg57 & ARTTIM23_INTR_CH1) ? "interrupting" : "polling"); + p += sprintf(p, " %s %s\n", + (reg71 & MRDMODE_INTR_CH0) ? "pending" : "clear ", + (reg71 & MRDMODE_INTR_CH1) ? "pending" : "clear"); + p += sprintf(p, " %s %s\n", + (reg71 & MRDMODE_BLK_CH0) ? "blocked" : "enabled", + (reg71 & MRDMODE_BLK_CH1) ? "blocked" : "enabled"); + +#ifdef DEBUG_CMD_REGS + SPLIT_BYTE(reg50, hi_byte, lo_byte); + p += sprintf(p, "CFR = 0x%02x, HI = 0x%02x, " + "LOW = 0x%02x\n", reg50, hi_byte, lo_byte); + SPLIT_BYTE(reg57, hi_byte, lo_byte); + p += sprintf(p, "ARTTIM23 = 0x%02x, HI = 0x%02x, " + "LOW = 0x%02x\n", reg57, hi_byte, lo_byte); + SPLIT_BYTE(reg71, hi_byte, lo_byte); + p += sprintf(p, "MRDMODE = 0x%02x, HI = 0x%02x, " + "LOW = 0x%02x\n", reg71, hi_byte, lo_byte); +#endif /* DEBUG_CMD_REGS */ + + return (char *)p; +} + +static char * print_sii_get_info (char *buf, struct pci_dev *dev, int index) +{ + char *p = buf; + + p += sprintf(p, "\nController: %d\n", index); + p += sprintf(p, "SII%x Chipset.\n", dev->device); + + p += sprintf(p, "--------------- Primary Channel " + "---------------- Secondary Channel " + "-------------\n"); + p += sprintf(p, "--------------- drive0 --------- drive1 " + "-------- drive0 ---------- drive1 ------\n"); + p += sprintf(p, "PIO Mode: %s %s" + " %s %s\n", + "?", "?", "?", "?"); + return (char *)p; +} + +static int cmd64x_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + int i; + + p += sprintf(p, "\n"); + for (i = 0; i < n_cmd_devs; i++) { + struct pci_dev *dev = cmd_devs[i]; + + if (dev->device <= PCI_DEVICE_ID_CMD_649) + p = print_cmd64x_get_info(p, dev, i); + else + p = print_sii_get_info(p, dev, i); + } + return p-buffer; /* => must be less than 4k! */ +} + +#endif /* defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) */ + +/* + * Registers and masks for easy access by drive index: + */ +#if 0 +static byte prefetch_regs[4] = {CNTRL, CNTRL, ARTTIM23, ARTTIM23}; +static byte prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3}; +#endif + +/* + * This routine writes the prepared setup/active/recovery counts + * for a drive into the cmd646 chipset registers to active them. + */ +static void program_drive_counts (ide_drive_t *drive, int setup_count, int active_count, int recovery_count) +{ + unsigned long flags; + struct pci_dev *dev = HWIF(drive)->pci_dev; + ide_drive_t *drives = HWIF(drive)->drives; + byte temp_b; + static const byte setup_counts[] = {0x40, 0x40, 0x40, 0x80, 0, 0xc0}; + static const byte recovery_counts[] = + {15, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0}; + static const byte arttim_regs[2][2] = { + { ARTTIM0, ARTTIM1 }, + { ARTTIM23, ARTTIM23 } + }; + static const byte drwtim_regs[2][2] = { + { DRWTIM0, DRWTIM1 }, + { DRWTIM2, DRWTIM3 } + }; + int channel = (int) HWIF(drive)->channel; + int slave = (drives != drive); /* Is this really the best way to determine this?? */ + + cmdprintk("program_drive_count parameters = s(%d),a(%d),r(%d),p(%d)\n", setup_count, + active_count, recovery_count, drive->present); + /* + * Set up address setup count registers. + * Primary interface has individual count/timing registers for + * each drive. Secondary interface has one common set of registers, + * for address setup so we merge these timings, using the slowest + * value. + */ + if (channel) { + drive->drive_data = setup_count; + setup_count = IDE_MAX(drives[0].drive_data, drives[1].drive_data); + cmdprintk("Secondary interface, setup_count = %d\n", setup_count); + } + + /* + * Convert values to internal chipset representation + */ + setup_count = (setup_count > 5) ? 0xc0 : (int) setup_counts[setup_count]; + active_count &= 0xf; /* Remember, max value is 16 */ + recovery_count = (int) recovery_counts[recovery_count]; + + cmdprintk("Final values = %d,%d,%d\n", + setup_count, active_count, recovery_count); + + /* + * Now that everything is ready, program the new timings + */ + local_irq_save(flags); + /* + * Program the address_setup clocks into ARTTIM reg, + * and then the active/recovery counts into the DRWTIM reg + */ + (void) pci_read_config_byte(dev, arttim_regs[channel][slave], &temp_b); + (void) pci_write_config_byte(dev, arttim_regs[channel][slave], + ((byte) setup_count) | (temp_b & 0x3f)); + (void) pci_write_config_byte(dev, drwtim_regs[channel][slave], + (byte) ((active_count << 4) | recovery_count)); + cmdprintk ("Write %x to %x\n", + ((byte) setup_count) | (temp_b & 0x3f), + arttim_regs[channel][slave]); + cmdprintk ("Write %x to %x\n", + (byte) ((active_count << 4) | recovery_count), + drwtim_regs[channel][slave]); + local_irq_restore(flags); +} + +/* + * Attempts to set the interface PIO mode. + * The preferred method of selecting PIO modes (e.g. mode 4) is + * "echo 'piomode:4' > /proc/ide/hdx/settings". Special cases are + * 8: prefetch off, 9: prefetch on, 255: auto-select best mode. + * Called with 255 at boot time. + */ +static void cmd64x_tuneproc (ide_drive_t *drive, byte mode_wanted) +{ + int setup_time, active_time, recovery_time, clock_time, pio_mode, cycle_time; + byte recovery_count2, cycle_count; + int setup_count, active_count, recovery_count; + int bus_speed = system_bus_clock(); + /*byte b;*/ + ide_pio_data_t d; + + switch (mode_wanted) { + case 8: /* set prefetch off */ + case 9: /* set prefetch on */ + mode_wanted &= 1; + /*set_prefetch_mode(index, mode_wanted);*/ + cmdprintk("%s: %sabled cmd640 prefetch\n", + drive->name, mode_wanted ? "en" : "dis"); + return; + } + + mode_wanted = ide_get_best_pio_mode (drive, mode_wanted, 5, &d); + pio_mode = d.pio_mode; + cycle_time = d.cycle_time; + + /* + * I copied all this complicated stuff from cmd640.c and made a few + * minor changes. For now I am just going to pray that it is correct. + */ + if (pio_mode > 5) + pio_mode = 5; + setup_time = ide_pio_timings[pio_mode].setup_time; + active_time = ide_pio_timings[pio_mode].active_time; + recovery_time = cycle_time - (setup_time + active_time); + clock_time = 1000 / bus_speed; + cycle_count = (cycle_time + clock_time - 1) / clock_time; + + setup_count = (setup_time + clock_time - 1) / clock_time; + + active_count = (active_time + clock_time - 1) / clock_time; + + recovery_count = (recovery_time + clock_time - 1) / clock_time; + recovery_count2 = cycle_count - (setup_count + active_count); + if (recovery_count2 > recovery_count) + recovery_count = recovery_count2; + if (recovery_count > 16) { + active_count += recovery_count - 16; + recovery_count = 16; + } + if (active_count > 16) + active_count = 16; /* maximum allowed by cmd646 */ + + /* + * In a perfect world, we might set the drive pio mode here + * (using WIN_SETFEATURE) before continuing. + * + * But we do not, because: + * 1) this is the wrong place to do it + * (proper is do_special() in ide.c) + * 2) in practice this is rarely, if ever, necessary + */ + program_drive_counts (drive, setup_count, active_count, recovery_count); + + cmdprintk("%s: selected cmd646 PIO mode%d : %d (%dns)%s, " + "clocks=%d/%d/%d\n", + drive->name, pio_mode, mode_wanted, cycle_time, + d.overridden ? " (overriding vendor mode)" : "", + setup_count, active_count, recovery_count); +} + +static byte cmd64x_ratemask (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + byte mode = 0x00; + + switch(dev->device) { + case PCI_DEVICE_ID_CMD_680: { mode |= 0x04; break; } + case PCI_DEVICE_ID_CMD_649: { mode |= 0x03; break; } + case PCI_DEVICE_ID_CMD_648: { mode |= 0x02; break; } + case PCI_DEVICE_ID_CMD_643: { mode |= 0x01; break; } + + case PCI_DEVICE_ID_CMD_646: + { + unsigned int class_rev = 0; + pci_read_config_dword(dev, + PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + /* + * UltraDMA only supported on PCI646U and PCI646U2, which + * correspond to revisions 0x03, 0x05 and 0x07 respectively. + * Actually, although the CMD tech support people won't + * tell me the details, the 0x03 revision cannot support + * UDMA correctly without hardware modifications, and even + * then it only works with Quantum disks due to some + * hold time assumptions in the 646U part which are fixed + * in the 646U2. + * + * So we only do UltraDMA on revision 0x05 and 0x07 chipsets. + */ + switch(class_rev) { + case 0x07: + case 0x05: { mode |= 0x01; break; } + case 0x03: + case 0x01: + default: { mode |= 0x00; break; } + } + } + } + if (!eighty_ninty_three(drive)) { + mode &= ~0xFE; + mode |= 0x01; + } + return (mode &= ~0xF8); +} + +static byte cmd64x_ratefilter (ide_drive_t *drive, byte speed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + byte mode = cmd64x_ratemask(drive); + + switch(mode) { + case 0x04: while (speed > XFER_UDMA_6) speed--; break; + case 0x03: while (speed > XFER_UDMA_5) speed--; break; + case 0x02: while (speed > XFER_UDMA_4) speed--; break; + case 0x01: while (speed > XFER_UDMA_2) speed--; break; + case 0x00: + default: while (speed > XFER_MW_DMA_2) speed--; break; + break; + } +#else + while (speed > XFER_PIO_4) speed--; +#endif /* CONFIG_BLK_DEV_IDEDMA */ +// printk("%s: mode == %02x speed == %02x\n", drive->name, mode, speed); + return speed; +} + +static byte cmd680_taskfile_timing (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + byte addr_mask = (hwif->channel) ? 0xB2 : 0xA2; + unsigned short timing; + + pci_read_config_word(dev, addr_mask, &timing); + + switch (timing) { + case 0x10c1: return 4; + case 0x10c3: return 3; + case 0x1281: return 2; + case 0x2283: return 1; + case 0x328a: + default: return 0; + } +} + +static void cmd680_tuneproc (ide_drive_t *drive, byte mode_wanted) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte drive_pci; + unsigned short speedt; + + switch (drive->dn) { + case 0: drive_pci = 0xA4; break; + case 1: drive_pci = 0xA6; break; + case 2: drive_pci = 0xB4; break; + case 3: drive_pci = 0xB6; break; + default: return; + } + + pci_read_config_word(dev, drive_pci, &speedt); + + /* cheat for now and use the docs */ +// switch(cmd680_taskfile_timing(hwif)) { + switch(mode_wanted) { + case 4: speedt = 0x10c1; break; + case 3: speedt = 0x10C3; break; + case 2: speedt = 0x1104; break; + case 1: speedt = 0x2283; break; + case 0: + default: speedt = 0x328A; break; + } + pci_write_config_word(dev, drive_pci, speedt); +} + +static void config_cmd64x_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + byte speed = 0x00; + byte set_pio = ide_get_best_pio_mode(drive, 4, 5, NULL); + + cmd64x_tuneproc(drive, set_pio); + speed = XFER_PIO_0 + set_pio; + if (set_speed) + (void) ide_config_drive_speed(drive, speed); +} + +static void config_cmd680_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 unit = (drive->select.b.unit & 0x01); + u8 addr_mask = (hwif->channel) ? 0x84 : 0x80; + u8 speed = 0x00; + u8 mode_pci = 0x00; + u8 channel_timings = cmd680_taskfile_timing(hwif); + u8 set_pio = ide_get_best_pio_mode(drive, 4, 5, NULL); + + pci_read_config_byte(dev, addr_mask, &mode_pci); + mode_pci &= ~((unit) ? 0x30 : 0x03); + + /* WARNING PIO timing mess is going to happen b/w devices, argh */ + if ((channel_timings != set_pio) && (set_pio > channel_timings)) + set_pio = channel_timings; + + cmd680_tuneproc(drive, set_pio); + speed = XFER_PIO_0 + set_pio; + if (set_speed) + (void) ide_config_drive_speed(drive, speed); +} + +static void config_chipset_for_pio (ide_drive_t *drive, byte set_speed) +{ + switch(HWIF(drive)->pci_dev->device) { + case PCI_DEVICE_ID_CMD_680: + config_cmd680_chipset_for_pio(drive, set_speed); + return; + default: + break; + } + config_cmd64x_chipset_for_pio(drive, set_speed); +} + +static int cmd64x_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + + u8 unit = (drive->select.b.unit & 0x01); + u8 pciU = (hwif->channel) ? UDIDETCR1 : UDIDETCR0; + u8 pciD = (hwif->channel) ? BMIDESR1 : BMIDESR0; + u8 regU = 0; + u8 regD = 0; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + u8 speed = cmd64x_ratefilter(drive, xferspeed); + +#ifdef CONFIG_BLK_DEV_IDEDMA + if ((drive->media != ide_disk) && (speed < XFER_SW_DMA_0)) + return 1; + + (void) pci_read_config_byte(dev, pciD, ®D); + (void) pci_read_config_byte(dev, pciU, ®U); + regD &= ~(unit ? 0x40 : 0x20); + regU &= ~(unit ? 0xCA : 0x35); + (void) pci_write_config_byte(dev, pciD, regD); + (void) pci_write_config_byte(dev, pciU, regU); + (void) pci_read_config_byte(dev, pciD, ®D); + (void) pci_read_config_byte(dev, pciU, ®U); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA + case XFER_UDMA_5: regU |= (unit ? 0x0A : 0x05); break; + case XFER_UDMA_4: regU |= (unit ? 0x4A : 0x15); break; + case XFER_UDMA_3: regU |= (unit ? 0x8A : 0x25); break; + case XFER_UDMA_2: regU |= (unit ? 0x42 : 0x11); break; + case XFER_UDMA_1: regU |= (unit ? 0x82 : 0x21); break; + case XFER_UDMA_0: regU |= (unit ? 0xC2 : 0x31); break; + case XFER_MW_DMA_2: regD |= (unit ? 0x40 : 0x10); break; + case XFER_MW_DMA_1: regD |= (unit ? 0x80 : 0x20); break; + case XFER_MW_DMA_0: regD |= (unit ? 0xC0 : 0x30); break; + case XFER_SW_DMA_2: regD |= (unit ? 0x40 : 0x10); break; + case XFER_SW_DMA_1: regD |= (unit ? 0x80 : 0x20); break; + case XFER_SW_DMA_0: regD |= (unit ? 0xC0 : 0x30); break; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: cmd64x_tuneproc(drive, 4); break; + case XFER_PIO_3: cmd64x_tuneproc(drive, 3); break; + case XFER_PIO_2: cmd64x_tuneproc(drive, 2); break; + case XFER_PIO_1: cmd64x_tuneproc(drive, 1); break; + case XFER_PIO_0: cmd64x_tuneproc(drive, 0); break; + + default: + return 1; + } + +#ifdef CONFIG_BLK_DEV_IDEDMA + (void) pci_write_config_byte(dev, pciU, regU); + regD |= (unit ? 0x40 : 0x20); + (void) pci_write_config_byte(dev, pciD, regD); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + return (ide_config_drive_speed(drive, speed)); +} + +static int cmd680_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + u8 addr_mask = (hwif->channel) ? 0x84 : 0x80; + u8 unit = (drive->select.b.unit & 0x01); + u8 speed = cmd64x_ratefilter(drive, xferspeed); + u8 dma_pci = 0; + u8 udma_pci = 0; + u8 mode_pci = 0; + u8 scsc = 0; + u16 ultra = 0; + u16 multi = 0; + + pci_read_config_byte(dev, addr_mask, &mode_pci); + pci_read_config_byte(dev, 0x8A, &scsc); + + switch (drive->dn) { + case 0: dma_pci = 0xA8; udma_pci = 0xAC; break; + case 1: dma_pci = 0xAA; udma_pci = 0xAE; break; + case 2: dma_pci = 0xB8; udma_pci = 0xBC; break; + case 3: dma_pci = 0xBA; udma_pci = 0xBE; break; + default: return 1; + } + + pci_read_config_byte(dev, addr_mask, &mode_pci); + mode_pci &= ~((unit) ? 0x30 : 0x03); + pci_read_config_word(dev, dma_pci, &multi); + pci_read_config_word(dev, udma_pci, &ultra); + + if ((speed == XFER_UDMA_6) && (scsc & 0x30) == 0x00) { + pci_write_config_byte(dev, 0x8A, scsc|0x01); + pci_read_config_byte(dev, 0x8A, &scsc); +#if 0 + /* if 133 clock fails, switch to 2xbus clock */ + if (!(scsc & 0x01)) + pci_write_config_byte(dev, 0x8A, scsc|0x10); +#endif + } + + switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA + case XFER_UDMA_6: + if ((scsc & 0x30) == 0x00) + goto speed_break; + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= 0x01; + break; +speed_break : + speed = XFER_UDMA_5; + case XFER_UDMA_5: + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= (((scsc & 0x30) == 0x00) ? 0x01 : 0x02); + break; + case XFER_UDMA_4: + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= (((scsc & 0x30) == 0x00) ? 0x02 : 0x03); + break; + case XFER_UDMA_3: + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= (((scsc & 0x30) == 0x00) ? 0x04 : 0x05); + break; + case XFER_UDMA_2: + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= (((scsc & 0x30) == 0x00) ? 0x05 : 0x07); + break; + case XFER_UDMA_1: + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= (((scsc & 0x30) == 0x00) ? 0x07 : 0x0B); + break; + case XFER_UDMA_0: + multi = 0x10C1; + ultra &= ~0x3F; + ultra |= (((scsc & 0x30) == 0x00) ? 0x0C : 0x0F); + break; + case XFER_MW_DMA_2: + multi = 0x10C1; + break; + case XFER_MW_DMA_1: + multi = 0x10C2; + break; + case XFER_MW_DMA_0: + multi = 0x2208; + break; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: cmd680_tuneproc(drive, 4); break; + case XFER_PIO_3: cmd680_tuneproc(drive, 3); break; + case XFER_PIO_2: cmd680_tuneproc(drive, 2); break; + case XFER_PIO_1: cmd680_tuneproc(drive, 1); break; + case XFER_PIO_0: cmd680_tuneproc(drive, 0); break; + default: + return 1; + } + + if (speed >= XFER_MW_DMA_0) + config_cmd680_chipset_for_pio(drive, 0); + + if (speed >= XFER_UDMA_0) + mode_pci |= ((unit) ? 0x30 : 0x03); + else if (speed >= XFER_MW_DMA_0) + mode_pci |= ((unit) ? 0x20 : 0x02); + else + mode_pci |= ((unit) ? 0x10 : 0x01); + + pci_write_config_byte(dev, addr_mask, mode_pci); + pci_write_config_word(dev, dma_pci, multi); + pci_write_config_word(dev, udma_pci, ultra); + + return (ide_config_drive_speed(drive, speed)); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static int config_chipset_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + byte mode = cmd64x_ratemask(drive); + byte speed = 0x00; + byte set_pio = 0x00; + int rval; + + if (drive->media != ide_disk) { + cmdprintk("CMD64X: drive->media != ide_disk at double check," + " inital check failed!!\n"); + return ((int) ide_dma_off); + } + + switch(mode) { + case 0x04: + if (id->dma_ultra & 0x0040) + { speed = XFER_UDMA_6; break; } + case 0x03: + if (id->dma_ultra & 0x0020) + { speed = XFER_UDMA_5; break; } + case 0x02: + if (id->dma_ultra & 0x0010) + { speed = XFER_UDMA_4; break; } + if (id->dma_ultra & 0x0008) + { speed = XFER_UDMA_3; break; } + case 0x01: + if (id->dma_ultra & 0x0004) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0002) + { speed = XFER_UDMA_1; break; } + if (id->dma_ultra & 0x0001) + { speed = XFER_UDMA_0; break; } + case 0x00: + if (id->dma_mword & 0x0004) + { speed = XFER_MW_DMA_2; break; } + if (id->dma_mword & 0x0002) + { speed = XFER_MW_DMA_1; break; } + if (id->dma_mword & 0x0001) + { speed = XFER_MW_DMA_0; break; } + if (id->dma_1word & 0x0004) + { speed = XFER_SW_DMA_2; break; } + if (id->dma_1word & 0x0002) + { speed = XFER_SW_DMA_1; break; } + if (id->dma_1word & 0x0001) + { speed = XFER_SW_DMA_0; break; } + default: + { set_pio = 1; break; } + } + + if (!drive->init_speed) + drive->init_speed = speed; + + config_chipset_for_pio(drive, set_pio); + + if (set_pio) + return ((int) ide_dma_off_quietly); + + if (hwif->speedproc(drive, speed)) + return ((int) ide_dma_off); + + rval = (int)( ((id->dma_ultra >> 14) & 3) ? ide_dma_on : + ((id->dma_ultra >> 11) & 7) ? ide_dma_on : + ((id->dma_ultra >> 8) & 7) ? ide_dma_on : + ((id->dma_mword >> 8) & 7) ? ide_dma_on : + ((id->dma_1word >> 8) & 7) ? ide_dma_on : + ide_dma_off_quietly); + + return rval; +} + +static int cmd64x_config_drive_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + ide_dma_action_t dma_func = ide_dma_on; + + if ((id != NULL) && ((id->capability & 1) != 0) && + hwif->autodma && (drive->media == ide_disk)) { + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + goto fast_ata_pio; + } + dma_func = ide_dma_off_quietly; + if ((id->field_valid & 4) && cmd64x_ratemask(drive)) { + if (id->dma_ultra & 0x007F) { + /* Force if Capable UltraDMA */ + dma_func = config_chipset_for_dma(drive); + if ((id->field_valid & 2) && + (dma_func != ide_dma_on)) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & 0x0007) || + (id->dma_1word & 0x0007)) { + /* Force if Capable regular DMA modes */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } + } else if (ide_dmaproc(ide_dma_good_drive, drive)) { + if (id->eide_dma_time > 150) { + goto no_dma_set; + } + /* Consult the list of known "good" drives */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + dma_func = ide_dma_off_quietly; +no_dma_set: + config_chipset_for_pio(drive, 1); + } + return HWIF(drive)->dmaproc(dma_func, drive); +} + +static int cmd680_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + return cmd64x_config_drive_for_dma(drive); + default: + break; + } + /* Other cases are done by generic IDE-DMA code. */ + return ide_dmaproc(func, drive); +} + +static int cmd64x_alt_dma_status (struct pci_dev *dev) +{ + switch(dev->device) { + case PCI_DEVICE_ID_CMD_648: + case PCI_DEVICE_ID_CMD_649: + return 1; + default: + break; + } + return 0; +} + +static int cmd64x_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + byte dma_stat = 0; + byte dma_alt_stat = 0; + ide_hwif_t *hwif = HWIF(drive); + byte mask = (hwif->channel) ? MRDMODE_INTR_CH1 : MRDMODE_INTR_CH0; + unsigned long dma_base = hwif->dma_base; + struct pci_dev *dev = hwif->pci_dev; + byte alt_dma_stat = cmd64x_alt_dma_status(dev); + + switch (func) { + case ide_dma_check: + return cmd64x_config_drive_for_dma(drive); + case ide_dma_end: /* returns 1 on error, 0 otherwise */ + drive->waiting_for_dma = 0; + /* stop DMA */ + OUT_BYTE(IN_BYTE(dma_base)&~1, dma_base); + /* get DMA status */ + dma_stat = IN_BYTE(dma_base+2); + /* clear the INTR & ERROR bits */ + OUT_BYTE(dma_stat|6, dma_base+2); + if (alt_dma_stat) { + byte dma_intr = 0; + byte dma_mask = (hwif->channel) ? ARTTIM23_INTR_CH1 : CFR_INTR_CH0; + byte dma_reg = (hwif->channel) ? ARTTIM2 : CFR; + (void) pci_read_config_byte(dev, dma_reg, &dma_intr); + /* clear the INTR bit */ + (void) pci_write_config_byte(dev, dma_reg, dma_intr|dma_mask); + } + /* purge DMA mappings */ + ide_destroy_dmatable(drive); + /* verify good DMA status */ + return (dma_stat & 7) != 4; + case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */ + dma_stat = IN_BYTE(dma_base+2); + (void) pci_read_config_byte(dev, MRDMODE, &dma_alt_stat); +#ifdef DEBUG + printk("%s: dma_stat: 0x%02x dma_alt_stat: " + "0x%02x mask: 0x%02x\n", drive->name, + dma_stat, dma_alt_stat, mask); +#endif + if (!(dma_alt_stat & mask)) { + return 0; + } + /* return 1 if INTR asserted */ + return (dma_stat & 4) == 4; + default: + break; + } + /* Other cases are done by generic IDE-DMA code. */ + return ide_dmaproc(func, drive); +} + +/* + * ASUS P55T2P4D with CMD646 chipset revision 0x01 requires the old + * event order for DMA transfers. + */ +static int cmd646_1_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned long dma_base = hwif->dma_base; + byte dma_stat; + + switch (func) { + case ide_dma_check: + return cmd64x_config_drive_for_dma(drive); + case ide_dma_end: + drive->waiting_for_dma = 0; + /* get DMA status */ + dma_stat = IN_BYTE(dma_base+2); + /* stop DMA */ + OUT_BYTE(IN_BYTE(dma_base)&~1, dma_base); + /* clear the INTR & ERROR bits */ + OUT_BYTE(dma_stat|6, dma_base+2); + /* and free any DMA resources */ + ide_destroy_dmatable(drive); + /* verify good DMA status */ + return (dma_stat & 7) != 4; + default: + break; + } + + /* Other cases are done by generic IDE-DMA code. */ + return ide_dmaproc(func, drive); +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +static int cmd680_busproc (ide_drive_t * drive, int state) +{ +#if 0 + ide_hwif_t *hwif = HWIF(drive); + u8 addr_mask = (hwif->channel) ? 0xB0 : 0xA0; + u32 stat_config = 0; + + pci_read_config_dword(hwif->pci_dev, addr_mask, &stat_config); + + if (!hwif) + return -EINVAL; + + switch (state) { + case BUSSTATE_ON: + hwif->drives[0].failures = 0; + hwif->drives[1].failures = 0; + break; + case BUSSTATE_OFF: + hwif->drives[0].failures = hwif->drives[0].max_failures + 1; + hwif->drives[1].failures = hwif->drives[1].max_failures + 1; + break; + case BUSSTATE_TRISTATE: + hwif->drives[0].failures = hwif->drives[0].max_failures + 1; + hwif->drives[1].failures = hwif->drives[1].max_failures + 1; + break; + default: + return 0; + } + hwif->bus_state = state; +#endif + return 0; +} + +void cmd680_reset (ide_drive_t *drive) +{ +#if 0 + ide_hwif_t *hwif = HWIF(drive); + u8 addr_mask = (hwif->channel) ? 0xB0 : 0xA0; + byte reset = 0; + + pci_read_config_byte(hwif->pci_dev, addr_mask, &reset); + pci_write_config_byte(hwif->pci_dev, addr_mask, reset|0x03); +#endif +} + +unsigned int cmd680_pci_init (struct pci_dev *dev, const char *name) +{ + u8 tmpbyte = 0; + pci_write_config_byte(dev, 0x80, 0x00); + pci_write_config_byte(dev, 0x84, 0x00); + pci_read_config_byte(dev, 0x8A, &tmpbyte); + pci_write_config_byte(dev, 0x8A, tmpbyte|0x01); +#if 0 + /* if 133 clock fails, switch to 2xbus clock */ + if (!(tmpbyte & 0x01)) { + pci_read_config_byte(dev, 0x8A, &tmpbyte); + pci_write_config_byte(dev, 0x8A, tmpbyte|0x10); + } +#endif + pci_write_config_word(dev, 0xA2, 0x328A); + pci_write_config_dword(dev, 0xA4, 0x328A); + pci_write_config_dword(dev, 0xA8, 0x4392); + pci_write_config_dword(dev, 0xAC, 0x4009); + pci_write_config_word(dev, 0xB2, 0x328A); + pci_write_config_dword(dev, 0xB4, 0x328A); + pci_write_config_dword(dev, 0xB8, 0x4392); + pci_write_config_dword(dev, 0xBC, 0x4009); + + cmd_devs[n_cmd_devs++] = dev; + +#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) + if (!cmd64x_proc) { + cmd64x_proc = 1; + cmd64x_display_info = &cmd64x_get_info; + } +#endif /* DISPLAY_CMD64X_TIMINGS && CONFIG_PROC_FS */ + + return 0; +} + +unsigned int cmd64x_pci_init (struct pci_dev *dev, const char *name) +{ + unsigned char mrdmode; + unsigned int class_rev; + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + +#ifdef __i386__ + if (dev->resource[PCI_ROM_RESOURCE].start) { + pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); + printk("%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start); + } +#endif + + switch(dev->device) { + case PCI_DEVICE_ID_CMD_643: + break; + case PCI_DEVICE_ID_CMD_646: + printk("%s: chipset revision 0x%02X, ", name, class_rev); + switch(class_rev) { + case 0x07: + case 0x05: + printk("UltraDMA Capable"); + break; + case 0x03: + printk("MultiWord DMA Force Limited"); + break; + case 0x01: + default: + printk("MultiWord DMA Limited, IRQ workaround enabled"); + break; + } + printk("\n"); + break; + case PCI_DEVICE_ID_CMD_648: + case PCI_DEVICE_ID_CMD_649: + break; + default: + break; + } + + /* Set a good latency timer and cache line size value. */ + (void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); +#ifdef __sparc_v9__ + (void) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x10); +#endif + + + /* Setup interrupts. */ + (void) pci_read_config_byte(dev, MRDMODE, &mrdmode); + mrdmode &= ~(0x30); + (void) pci_write_config_byte(dev, MRDMODE, mrdmode); + + /* Use MEMORY READ LINE for reads. + * NOTE: Although not mentioned in the PCI0646U specs, + * these bits are write only and won't be read + * back as set or not. The PCI0646U2 specs clarify + * this point. + */ + (void) pci_write_config_byte(dev, MRDMODE, mrdmode | 0x02); + + /* Set reasonable active/recovery/address-setup values. */ + (void) pci_write_config_byte(dev, ARTTIM0, 0x40); + (void) pci_write_config_byte(dev, DRWTIM0, 0x3f); + (void) pci_write_config_byte(dev, ARTTIM1, 0x40); + (void) pci_write_config_byte(dev, DRWTIM1, 0x3f); +#ifdef __i386__ + (void) pci_write_config_byte(dev, ARTTIM23, 0x1c); +#else + (void) pci_write_config_byte(dev, ARTTIM23, 0x5c); +#endif + (void) pci_write_config_byte(dev, DRWTIM23, 0x3f); + (void) pci_write_config_byte(dev, DRWTIM3, 0x3f); +#ifdef CONFIG_PPC + (void) pci_write_config_byte(dev, UDIDETCR0, 0xf0); +#endif /* CONFIG_PPC */ + + cmd_devs[n_cmd_devs++] = dev; + +#if defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) + if (!cmd64x_proc) { + cmd64x_proc = 1; + cmd64x_display_info = &cmd64x_get_info; + } +#endif /* DISPLAY_CMD64X_TIMINGS && CONFIG_PROC_FS */ + + return 0; +} + +unsigned int __init pci_init_cmd64x (struct pci_dev *dev, const char *name) +{ + switch(dev->device) { + case PCI_DEVICE_ID_CMD_680: + return cmd680_pci_init (dev, name); + default: + break; + } + return cmd64x_pci_init (dev, name); +} + +unsigned int cmd680_ata66 (ide_hwif_t *hwif) +{ + byte ata66 = 0; + byte addr_mask = (hwif->channel) ? 0xB0 : 0xA0; + + pci_read_config_byte(hwif->pci_dev, addr_mask, &ata66); + return (ata66 & 0x01) ? 1 : 0; +} + +unsigned int cmd64x_ata66 (ide_hwif_t *hwif) +{ + byte ata66 = 0; + byte mask = (hwif->channel) ? 0x02 : 0x01; + + pci_read_config_byte(hwif->pci_dev, BMIDECSR, &ata66); + return (ata66 & mask) ? 1 : 0; +} + +unsigned int __init ata66_cmd64x (ide_hwif_t *hwif) +{ + switch(hwif->pci_dev->device) { + case PCI_DEVICE_ID_CMD_680: + return cmd680_ata66(hwif); + default: + break; + } + return cmd64x_ata66(hwif); +} + +void __init ide_init_cmd64x (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + unsigned int class_rev; + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + + switch(dev->device) { + case PCI_DEVICE_ID_CMD_680: + hwif->busproc = &cmd680_busproc; +#ifdef CONFIG_BLK_DEV_IDEDMA + if (hwif->dma_base) + hwif->dmaproc = &cmd680_dmaproc; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + hwif->resetproc = &cmd680_reset; + hwif->speedproc = &cmd680_tune_chipset; + hwif->tuneproc = &cmd680_tuneproc; + break; + case PCI_DEVICE_ID_CMD_649: + case PCI_DEVICE_ID_CMD_648: + case PCI_DEVICE_ID_CMD_643: +#ifdef CONFIG_BLK_DEV_IDEDMA + if (hwif->dma_base) + hwif->dmaproc = &cmd64x_dmaproc; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + hwif->tuneproc = &cmd64x_tuneproc; + hwif->speedproc = &cmd64x_tune_chipset; + break; + case PCI_DEVICE_ID_CMD_646: + hwif->chipset = ide_cmd646; +#ifdef CONFIG_BLK_DEV_IDEDMA + if (hwif->dma_base) { + if (class_rev == 0x01) + hwif->dmaproc = &cmd646_1_dmaproc; + else + hwif->dmaproc = &cmd64x_dmaproc; + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + hwif->tuneproc = &cmd64x_tuneproc; + hwif->speedproc = &cmd64x_tune_chipset; + break; + default: + break; + } + +#if defined(CONFIG_BLK_DEV_IDEDMA) && defined(CONFIG_IDEDMA_AUTO) + if (hwif->dma_base) + if (!noautodma) + hwif->autodma = 1; +#endif /* CONFIG_BLK_DEV_IDEDMA && CONFIG_IDEDMA_AUTO*/ +} diff -Nru a/drivers/ide/cs5530.c b/drivers/ide/cs5530.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/cs5530.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,386 @@ +/* + * linux/drivers/ide/cs5530.c Version 0.6 Mar. 18, 2000 + * + * Copyright (C) 2000 Andre Hedrick + * Ditto of GNU General Public License. + * + * Copyright (C) 2000 Mark Lord + * May be copied or modified under the terms of the GNU General Public License + * + * Development of this chipset driver was funded + * by the nice folks at National Semiconductor. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ide_modes.h" + +#define DISPLAY_CS5530_TIMINGS + +#if defined(DISPLAY_CS5530_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +static int cs5530_get_info(char *, char **, off_t, int); +extern int (*cs5530_display_info)(char *, char **, off_t, int); /* ide-proc.c */ +static struct pci_dev *bmide_dev; + +static int cs5530_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + u32 bibma = pci_resource_start(bmide_dev, 4); + u8 c0 = 0, c1 = 0; + + /* + * at that point bibma+0x2 et bibma+0xa are byte registers + * to investigate: + */ + + c0 = inb_p((unsigned short)bibma + 0x02); + c1 = inb_p((unsigned short)bibma + 0x0a); + + p += sprintf(p, "\n " + "Cyrix 5530 Chipset.\n"); + p += sprintf(p, "--------------- Primary Channel " + "---------------- Secondary Channel " + "-------------\n"); + p += sprintf(p, " %sabled " + " %sabled\n", + (c0&0x80) ? "dis" : " en", + (c1&0x80) ? "dis" : " en"); + p += sprintf(p, "--------------- drive0 --------- drive1 " + "-------- drive0 ---------- drive1 ------\n"); + p += sprintf(p, "DMA enabled: %s %s " + " %s %s\n", + (c0&0x20) ? "yes" : "no ", (c0&0x40) ? "yes" : "no ", + (c1&0x20) ? "yes" : "no ", (c1&0x40) ? "yes" : "no " ); + + p += sprintf(p, "UDMA\n"); + p += sprintf(p, "DMA\n"); + p += sprintf(p, "PIO\n"); + + return p-buffer; +} +#endif /* DISPLAY_CS5530_TIMINGS && CONFIG_PROC_FS */ + +byte cs5530_proc = 0; + +/* + * Set a new transfer mode at the drive + */ +int cs5530_set_xfer_mode (ide_drive_t *drive, byte mode) +{ + printk("%s: cs5530_set_xfer_mode(%s)\n", + drive->name, ide_xfer_verbose(mode)); + return (ide_config_drive_speed(drive, mode)); +} + +/* + * Here are the standard PIO mode 0-4 timings for each "format". + * Format-0 uses fast data reg timings, with slower command reg timings. + * Format-1 uses fast timings for all registers, but won't work with all drives. + */ +static unsigned int cs5530_pio_timings[2][5] = + {{0x00009172, 0x00012171, 0x00020080, 0x00032010, 0x00040010}, + {0xd1329172, 0x71212171, 0x30200080, 0x20102010, 0x00100010}}; + +/* + * After chip reset, the PIO timings are set to 0x0000e132, which is not valid. + */ +#define CS5530_BAD_PIO(timings) (((timings)&~0x80000000)==0x0000e132) +#define CS5530_BASEREG(hwif) (((hwif)->dma_base & ~0xf) + ((hwif)->channel ? 0x30 : 0x20)) + +/* + * cs5530_tuneproc() handles selection/setting of PIO modes + * for both the chipset and drive. + * + * The ide_init_cs5530() routine guarantees that all drives + * will have valid default PIO timings set up before we get here. + */ +static void cs5530_tuneproc (ide_drive_t *drive, byte pio) /* pio=255 means "autotune" */ +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int format, basereg = CS5530_BASEREG(hwif); + static byte modes[5] = { XFER_PIO_0, XFER_PIO_1, XFER_PIO_2, XFER_PIO_3, XFER_PIO_4}; + + pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + if (!cs5530_set_xfer_mode(drive, modes[pio])) { + format = (inl(basereg+4) >> 31) & 1; + outl(cs5530_pio_timings[format][pio], + basereg+(drive->select.b.unit<<3)); + } +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +/* + * cs5530_config_dma() handles selection/setting of DMA/UDMA modes + * for both the chipset and drive. + */ +static int cs5530_config_dma (ide_drive_t *drive) +{ + int udma_ok = 1, mode = 0; + ide_hwif_t *hwif = HWIF(drive); + int unit = drive->select.b.unit; + ide_drive_t *mate = &hwif->drives[unit^1]; + struct hd_driveid *id = drive->id; + unsigned int basereg, reg, timings; + + /* + * Default to DMA-off in case we run into trouble here. + */ + (void)hwif->dmaproc(ide_dma_off_quietly, drive); + /* turn off DMA while we fiddle */ + (void)hwif->dmaproc(ide_dma_host_off, drive); + /* clear DMA_capable bit */ + + /* + * The CS5530 specifies that two drives sharing a cable cannot + * mix UDMA/MDMA. It has to be one or the other, for the pair, + * though different timings can still be chosen for each drive. + * We could set the appropriate timing bits on the fly, + * but that might be a bit confusing. So, for now we statically + * handle this requirement by looking at our mate drive to see + * what it is capable of, before choosing a mode for our own drive. + */ + if (mate->present) { + struct hd_driveid *mateid = mate->id; + if (mateid && (mateid->capability & 1) && + !hwif->dmaproc(ide_dma_bad_drive, mate)) { + if ((mateid->field_valid & 4) && + (mateid->dma_ultra & 7)) + udma_ok = 1; + else if ((mateid->field_valid & 2) && + (mateid->dma_mword & 7)) + udma_ok = 0; + else + udma_ok = 1; + } + } + + /* + * Now see what the current drive is capable of, + * selecting UDMA only if the mate said it was ok. + */ + if (id && (id->capability & 1) && hwif->autodma && + !hwif->dmaproc(ide_dma_bad_drive, drive)) { + if (udma_ok && (id->field_valid & 4) && (id->dma_ultra & 7)) { + if (id->dma_ultra & 4) + mode = XFER_UDMA_2; + else if (id->dma_ultra & 2) + mode = XFER_UDMA_1; + else if (id->dma_ultra & 1) + mode = XFER_UDMA_0; + } + if (!mode && (id->field_valid & 2) && (id->dma_mword & 7)) { + if (id->dma_mword & 4) + mode = XFER_MW_DMA_2; + else if (id->dma_mword & 2) + mode = XFER_MW_DMA_1; + else if (id->dma_mword & 1) + mode = XFER_MW_DMA_0; + } + } + + /* + * Tell the drive to switch to the new mode; abort on failure. + */ + if (!mode || cs5530_set_xfer_mode(drive, mode)) + return 1; /* failure */ + + /* + * Now tune the chipset to match the drive: + */ + switch (mode) { + case XFER_UDMA_0: timings = 0x00921250; break; + case XFER_UDMA_1: timings = 0x00911140; break; + case XFER_UDMA_2: timings = 0x00911030; break; + case XFER_MW_DMA_0: timings = 0x00077771; break; + case XFER_MW_DMA_1: timings = 0x00012121; break; + case XFER_MW_DMA_2: timings = 0x00002020; break; + default: + printk("%s: cs5530_config_dma: huh? mode=%02x\n", + drive->name, mode); + return 1; /* failure */ + } + basereg = CS5530_BASEREG(hwif); + reg = inl(basereg+4); /* get drive0 config register */ + timings |= reg & 0x80000000; /* preserve PIO format bit */ + if (unit == 0) { /* are we configuring drive0? */ + outl(timings, basereg+4); /* write drive0 config register */ + } else { + if (timings & 0x00100000) + reg |= 0x00100000; /* enable UDMA timings for both drives */ + else + reg &= ~0x00100000; /* disable UDMA timings for both drives */ + outl(reg, basereg+4); /* write drive0 config register */ + outl(timings, basereg+12); /* write drive1 config register */ + } + (void)hwif->dmaproc(ide_dma_host_on, drive); + /* set DMA_capable bit */ + + /* + * Finally, turn DMA on in software, and exit. + */ + return hwif->dmaproc(ide_dma_on, drive); /* success */ +} + +/* + * This is a CS5530-specific wrapper for the standard ide_dmaproc(). + * We need it for our custom "ide_dma_check" function. + * All other requests are forwarded to the standard ide_dmaproc(). + */ +int cs5530_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + return cs5530_config_dma(drive); + default: + break; + } + /* Other cases are done by generic IDE-DMA code. */ + return ide_dmaproc(func, drive); +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +/* + * Initialize the cs5530 bridge for reliable IDE DMA operation. + */ +unsigned int __init pci_init_cs5530 (struct pci_dev *dev, const char *name) +{ + struct pci_dev *master_0 = NULL, *cs5530_0 = NULL; + unsigned short pcicmd = 0; + unsigned long flags; + +#if defined(DISPLAY_CS5530_TIMINGS) && defined(CONFIG_PROC_FS) + if (!cs5530_proc) { + cs5530_proc = 1; + bmide_dev = dev; + cs5530_display_info = &cs5530_get_info; + } +#endif /* DISPLAY_CS5530_TIMINGS && CONFIG_PROC_FS */ + + pci_for_each_dev (dev) { + if (dev->vendor == PCI_VENDOR_ID_CYRIX) { + switch (dev->device) { + case PCI_DEVICE_ID_CYRIX_PCI_MASTER: + master_0 = dev; + break; + case PCI_DEVICE_ID_CYRIX_5530_LEGACY: + cs5530_0 = dev; + break; + } + } + } + if (!master_0) { + printk("%s: unable to locate PCI MASTER function\n", name); + return 0; + } + if (!cs5530_0) { + printk("%s: unable to locate CS5530 LEGACY function\n", name); + return 0; + } + + spin_lock_irqsave(&ide_lock, flags); + /* all CPUs (there should only be one CPU with this chipset) */ + + /* + * Enable BusMaster and MemoryWriteAndInvalidate for the cs5530: + * --> OR 0x14 into 16-bit PCI COMMAND reg of function 0 of the cs5530 + */ + pci_read_config_word (cs5530_0, PCI_COMMAND, &pcicmd); + pci_write_config_word(cs5530_0, PCI_COMMAND, pcicmd | PCI_COMMAND_MASTER | PCI_COMMAND_INVALIDATE); + + /* + * Set PCI CacheLineSize to 16-bytes: + * --> Write 0x04 into 8-bit PCI CACHELINESIZE reg of function 0 of the cs5530 + */ + pci_write_config_byte(cs5530_0, PCI_CACHE_LINE_SIZE, 0x04); + + /* + * Disable trapping of UDMA register accesses (Win98 hack): + * --> Write 0x5006 into 16-bit reg at offset 0xd0 of function 0 of the cs5530 + */ + pci_write_config_word(cs5530_0, 0xd0, 0x5006); + + /* + * Bit-1 at 0x40 enables MemoryWriteAndInvalidate on internal X-bus: + * The other settings are what is necessary to get the register + * into a sane state for IDE DMA operation. + */ + pci_write_config_byte(master_0, 0x40, 0x1e); + + /* + * Set max PCI burst size (16-bytes seems to work best): + * 16bytes: set bit-1 at 0x41 (reg value of 0x16) + * all others: clear bit-1 at 0x41, and do: + * 128bytes: OR 0x00 at 0x41 + * 256bytes: OR 0x04 at 0x41 + * 512bytes: OR 0x08 at 0x41 + * 1024bytes: OR 0x0c at 0x41 + */ + pci_write_config_byte(master_0, 0x41, 0x14); + + /* + * These settings are necessary to get the chip + * into a sane state for IDE DMA operation. + */ + pci_write_config_byte(master_0, 0x42, 0x00); + pci_write_config_byte(master_0, 0x43, 0xc1); + + spin_unlock_irqrestore(&ide_lock, flags); + + return 0; +} + +/* + * This gets invoked by the IDE driver once for each channel, + * and performs channel-specific pre-initialization before drive probing. + */ +void __init ide_init_cs5530 (ide_hwif_t *hwif) +{ + unsigned int basereg, d0_timings; + hwif->autodma = 0; + + if (hwif->mate) + hwif->serialized = hwif->mate->serialized = 1; + + hwif->tuneproc = &cs5530_tuneproc; + basereg = CS5530_BASEREG(hwif); + d0_timings = inl(basereg+0); + if (CS5530_BAD_PIO(d0_timings)) { + /* PIO timings not initialized? */ + outl(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+0); + if (!hwif->drives[0].autotune) + hwif->drives[0].autotune = 1; + /* needs autotuning later */ + } + if (CS5530_BAD_PIO(inl(basereg+8))) { + /* PIO timings not initialized? */ + outl(cs5530_pio_timings[(d0_timings>>31)&1][0], basereg+8); + if (!hwif->drives[1].autotune) + hwif->drives[1].autotune = 1; + /* needs autotuning later */ + } + +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->dmaproc = &cs5530_dmaproc; +#ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +#endif /* CONFIG_IDEDMA_AUTO */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ +} diff -Nru a/drivers/ide/cy82c693.c b/drivers/ide/cy82c693.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/cy82c693.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,478 @@ +/* + * linux/drivers/ide/cy82c693.c Version 0.34 Dec. 13, 1999 + * + * Copyright (C) 1998-2000 Andreas S. Krebs (akrebs@altavista.net), Maintainer + * Copyright (C) 1998-2000 Andre Hedrick , Integrater + * + * CYPRESS CY82C693 chipset IDE controller + * + * The CY82C693 chipset is used on Digital's PC-Alpha 164SX boards. + * Writting the driver was quite simple, since most of the job is + * done by the generic pci-ide support. + * The hard part was finding the CY82C693's datasheet on Cypress's + * web page :-(. But Altavista solved this problem :-). + * + * + * Notes: + * - I recently got a 16.8G IBM DTTA, so I was able to test it with + * a large and fast disk - the results look great, so I'd say the + * driver is working fine :-) + * hdparm -t reports 8.17 MB/sec at about 6% CPU usage for the DTTA + * - this is my first linux driver, so there's probably a lot of room + * for optimizations and bug fixing, so feel free to do it. + * - use idebus=xx parameter to set PCI bus speed - needed to calc + * timings for PIO modes (default will be 40) + * - if using PIO mode it's a good idea to set the PIO mode and + * 32-bit I/O support (if possible), e.g. hdparm -p2 -c1 /dev/hda + * - I had some problems with my IBM DHEA with PIO modes < 2 + * (lost interrupts) ????? + * - first tests with DMA look okay, they seem to work, but there is a + * problem with sound - the BusMaster IDE TimeOut should fixed this + * + * + * History: + * AMH@1999-08-24: v0.34 init_cy82c693_chip moved to pci_init_cy82c693 + * ASK@1999-01-23: v0.33 made a few minor code clean ups + * removed DMA clock speed setting by default + * added boot message + * ASK@1998-11-01: v0.32 added support to set BusMaster IDE TimeOut + * added support to set DMA Controller Clock Speed + * ASK@1998-10-31: v0.31 fixed problem with setting to high DMA modes on some drive + * ASK@1998-10-29: v0.3 added support to set DMA modes + * ASK@1998-10-28: v0.2 added support to set PIO modes + * ASK@1998-10-27: v0.1 first version - chipset detection + * + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "ide_modes.h" + +/* the current version */ +#define CY82_VERSION "CY82C693U driver v0.34 99-13-12 Andreas S. Krebs (akrebs@altavista.net)" + +/* + * The following are used to debug the driver. + */ +#define CY82C693_DEBUG_LOGS 0 +#define CY82C693_DEBUG_INFO 0 + +/* define CY82C693_SETDMA_CLOCK to set DMA Controller Clock Speed to ATCLK */ +#undef CY82C693_SETDMA_CLOCK + +/* + * note: the value for busmaster timeout is tricky and i got it by trial and error ! + * using a to low value will cause DMA timeouts and drop IDE performance + * using a to high value will cause audio playback to scatter + * if you know a better value or how to calc it, please let me know + */ +#define BUSMASTER_TIMEOUT 0x50 /* twice the value written in cy82c693ub datasheet */ +/* + * the value above was tested on my machine and it seems to work okay + */ + +/* here are the offset definitions for the registers */ +#define CY82_IDE_CMDREG 0x04 +#define CY82_IDE_ADDRSETUP 0x48 +#define CY82_IDE_MASTER_IOR 0x4C +#define CY82_IDE_MASTER_IOW 0x4D +#define CY82_IDE_SLAVE_IOR 0x4E +#define CY82_IDE_SLAVE_IOW 0x4F +#define CY82_IDE_MASTER_8BIT 0x50 +#define CY82_IDE_SLAVE_8BIT 0x51 + +#define CY82_INDEX_PORT 0x22 +#define CY82_DATA_PORT 0x23 + +#define CY82_INDEX_CTRLREG1 0x01 +#define CY82_INDEX_CHANNEL0 0x30 +#define CY82_INDEX_CHANNEL1 0x31 +#define CY82_INDEX_TIMEOUT 0x32 + +/* the max PIO mode - from datasheet */ +#define CY82C693_MAX_PIO 4 + +/* the min and max PCI bus speed in MHz - from datasheet */ +#define CY82C963_MIN_BUS_SPEED 25 +#define CY82C963_MAX_BUS_SPEED 33 + +/* the struct for the PIO mode timings */ +typedef struct pio_clocks_s { + byte address_time; /* Address setup (clocks) */ + byte time_16r; /* clocks for 16bit IOR (0xF0=Active/data, 0x0F=Recovery) */ + byte time_16w; /* clocks for 16bit IOW (0xF0=Active/data, 0x0F=Recovery) */ + byte time_8; /* clocks for 8bit (0xF0=Active/data, 0x0F=Recovery) */ +} pio_clocks_t; + +/* + * calc clocks using bus_speed + * returns (rounded up) time in bus clocks for time in ns + */ +static int calc_clk (int time, int bus_speed) +{ + int clocks; + + clocks = (time*bus_speed+999)/1000 -1; + + if (clocks < 0) + clocks = 0; + + if (clocks > 0x0F) + clocks = 0x0F; + + return clocks; +} + +/* + * compute the values for the clock registers for PIO + * mode and pci_clk [MHz] speed + * + * NOTE: for mode 0,1 and 2 drives 8-bit IDE command control registers are used + * for mode 3 and 4 drives 8 and 16-bit timings are the same + * + */ +static void compute_clocks (byte pio, pio_clocks_t *p_pclk) +{ + int clk1, clk2; + int bus_speed = system_bus_clock(); /* get speed of PCI bus */ + + /* we don't check against CY82C693's min and max speed, + * so you can play with the idebus=xx parameter + */ + + if (pio > CY82C693_MAX_PIO) + pio = CY82C693_MAX_PIO; + + /* let's calc the address setup time clocks */ + p_pclk->address_time = (byte)calc_clk(ide_pio_timings[pio].setup_time, bus_speed); + + /* let's calc the active and recovery time clocks */ + clk1 = calc_clk(ide_pio_timings[pio].active_time, bus_speed); + + /* calc recovery timing */ + clk2 = ide_pio_timings[pio].cycle_time - + ide_pio_timings[pio].active_time - + ide_pio_timings[pio].setup_time; + + clk2 = calc_clk(clk2, bus_speed); + + clk1 = (clk1<<4)|clk2; /* combine active and recovery clocks */ + + /* note: we use the same values for 16bit IOR and IOW + * those are all the same, since I don't have other + * timings than those from ide_modes.h + */ + + p_pclk->time_16r = (byte)clk1; + p_pclk->time_16w = (byte)clk1; + + /* what are good values for 8bit ?? */ + p_pclk->time_8 = (byte)clk1; +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +/* + * set DMA mode a specific channel for CY82C693 + */ +static void cy82c693_dma_enable (ide_drive_t *drive, int mode, int single) +{ + byte index; + byte data; + + if (mode>2) /* make sure we set a valid mode */ + mode = 2; + + if (mode > drive->id->tDMA) /* to be absolutly sure we have a valid mode */ + mode = drive->id->tDMA; + + index = (HWIF(drive)->channel==0) ? CY82_INDEX_CHANNEL0 : CY82_INDEX_CHANNEL1; + +#if CY82C693_DEBUG_LOGS + /* for debug let's show the previous values */ + + OUT_BYTE(index, CY82_INDEX_PORT); + data = IN_BYTE(CY82_DATA_PORT); + + printk (KERN_INFO "%s (ch=%d, dev=%d): DMA mode is %d (single=%d)\n", + drive->name, HWIF(drive)->channel, drive->select.b.unit, + (data&0x3), ((data>>2)&1)); +#endif /* CY82C693_DEBUG_LOGS */ + + data = (byte)mode|(byte)(single<<2); + + OUT_BYTE(index, CY82_INDEX_PORT); + OUT_BYTE(data, CY82_DATA_PORT); + +#if CY82C693_DEBUG_INFO + printk(KERN_INFO "%s (ch=%d, dev=%d): set DMA mode to %d (single=%d)\n", + drive->name, HWIF(drive)->channel, drive->select.b.unit, + mode, single); +#endif /* CY82C693_DEBUG_INFO */ + + /* + * note: below we set the value for Bus Master IDE TimeOut Register + * I'm not absolutly sure what this does, but it solved my problem + * with IDE DMA and sound, so I now can play sound and work with + * my IDE driver at the same time :-) + * + * If you know the correct (best) value for this register please + * let me know - ASK + */ + + data = BUSMASTER_TIMEOUT; + OUT_BYTE(CY82_INDEX_TIMEOUT, CY82_INDEX_PORT); + OUT_BYTE(data, CY82_DATA_PORT); + +#if CY82C693_DEBUG_INFO + printk (KERN_INFO "%s: Set IDE Bus Master TimeOut Register to 0x%X\n", + drive->name, data); +#endif /* CY82C693_DEBUG_INFO */ +} + +/* + * used to set DMA mode for CY82C693 (single and multi modes) + */ +static int cy82c693_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + /* + * if the function is dma on, set dma mode for drive everything + * else is done by the defaul func + */ + if (func == ide_dma_on) { + struct hd_driveid *id = drive->id; + +#if CY82C693_DEBUG_INFO + printk (KERN_INFO "dma_on: %s\n", drive->name); +#endif /* CY82C693_DEBUG_INFO */ + + if (id != NULL) { + /* Enable DMA on any drive that has DMA (multi or single) enabled */ + if (id->field_valid & 2) { /* regular DMA */ + int mmode, smode; + + mmode = id->dma_mword & (id->dma_mword >> 8); + smode = id->dma_1word & (id->dma_1word >> 8); + + if (mmode != 0) + cy82c693_dma_enable(drive, (mmode >> 1), 0); /* enable multi */ + else if (smode != 0) + cy82c693_dma_enable(drive, (smode >> 1), 1); /* enable single */ + } + } + } + return ide_dmaproc(func, drive); +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +/* + * tune ide drive - set PIO mode + */ +static void cy82c693_tune_drive (ide_drive_t *drive, byte pio) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + pio_clocks_t pclk; + unsigned int addrCtrl; + + /* select primary or secondary channel */ + if (hwif->index > 0) { /* drive is on the secondary channel */ + dev = pci_find_slot(dev->bus->number, dev->devfn+1); + if (!dev) { + printk(KERN_ERR "%s: tune_drive: Cannot find secondary interface!\n", drive->name); + return; + } + } + +#if CY82C693_DEBUG_LOGS + /* for debug let's show the register values */ + + if (drive->select.b.unit == 0) { + /* + * get master drive registers + * address setup control register + * is 32 bit !!! + */ + pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl); + addrCtrl &= 0x0F; + + /* now let's get the remaining registers */ + pci_read_config_byte(dev, CY82_IDE_MASTER_IOR, &pclk.time_16r); + pci_read_config_byte(dev, CY82_IDE_MASTER_IOW, &pclk.time_16w); + pci_read_config_byte(dev, CY82_IDE_MASTER_8BIT, &pclk.time_8); + } else { + /* + * set slave drive registers + * address setup control register + * is 32 bit !!! + */ + pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl); + + addrCtrl &= 0xF0; + addrCtrl >>= 4; + + /* now let's get the remaining registers */ + pci_read_config_byte(dev, CY82_IDE_SLAVE_IOR, &pclk.time_16r); + pci_read_config_byte(dev, CY82_IDE_SLAVE_IOW, &pclk.time_16w); + pci_read_config_byte(dev, CY82_IDE_SLAVE_8BIT, &pclk.time_8); + } + + printk(KERN_INFO "%s (ch=%d, dev=%d): PIO timing is " + "(addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n", + drive->name, hwif->channel, drive->select.b.unit, + addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8); +#endif /* CY82C693_DEBUG_LOGS */ + + /* first let's calc the pio modes */ + pio = ide_get_best_pio_mode(drive, pio, CY82C693_MAX_PIO, NULL); + +#if CY82C693_DEBUG_INFO + printk (KERN_INFO "%s: Selected PIO mode %d\n", drive->name, pio); +#endif /* CY82C693_DEBUG_INFO */ + + compute_clocks(pio, &pclk); /* let's calc the values for this PIO mode */ + + /* now let's write the clocks registers */ + if (drive->select.b.unit == 0) { + /* + * set master drive + * address setup control register + * is 32 bit !!! + */ + pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl); + + addrCtrl &= (~0xF); + addrCtrl |= (unsigned int)pclk.address_time; + pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl); + + /* now let's set the remaining registers */ + pci_write_config_byte(dev, CY82_IDE_MASTER_IOR, pclk.time_16r); + pci_write_config_byte(dev, CY82_IDE_MASTER_IOW, pclk.time_16w); + pci_write_config_byte(dev, CY82_IDE_MASTER_8BIT, pclk.time_8); + + addrCtrl &= 0xF; + } else { + /* + * set slave drive + * address setup control register + * is 32 bit !!! + */ + pci_read_config_dword(dev, CY82_IDE_ADDRSETUP, &addrCtrl); + + addrCtrl &= (~0xF0); + addrCtrl |= ((unsigned int)pclk.address_time<<4); + pci_write_config_dword(dev, CY82_IDE_ADDRSETUP, addrCtrl); + + /* now let's set the remaining registers */ + pci_write_config_byte(dev, CY82_IDE_SLAVE_IOR, pclk.time_16r); + pci_write_config_byte(dev, CY82_IDE_SLAVE_IOW, pclk.time_16w); + pci_write_config_byte(dev, CY82_IDE_SLAVE_8BIT, pclk.time_8); + + addrCtrl >>= 4; + addrCtrl &= 0xF; + } + +#if CY82C693_DEBUG_INFO + printk(KERN_INFO "%s (ch=%d, dev=%d): set PIO timing to " + "(addr=0x%X, ior=0x%X, iow=0x%X, 8bit=0x%X)\n", + drive->name, hwif->channel, drive->select.b.unit, + addrCtrl, pclk.time_16r, pclk.time_16w, pclk.time_8); +#endif /* CY82C693_DEBUG_INFO */ +} + +/* + * this function is called during init and is used to setup the cy82c693 chip + */ +/* + * FIXME! "pci_init_cy82c693" really should replace + * the "init_cy82c693_chip", it is the correct location to tinker/setup + * the device prior to INIT. + */ + +unsigned int __init pci_init_cy82c693(struct pci_dev *dev, const char *name) +{ +#ifdef CY82C693_SETDMA_CLOCK + byte data; +#endif /* CY82C693_SETDMA_CLOCK */ + + /* write info about this verion of the driver */ + printk(KERN_INFO CY82_VERSION "\n"); + +#ifdef CY82C693_SETDMA_CLOCK + /* okay let's set the DMA clock speed */ + + OUT_BYTE(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT); + data = IN_BYTE(CY82_DATA_PORT); + +#if CY82C693_DEBUG_INFO + printk(KERN_INFO "%s: Peripheral Configuration Register: 0x%X\n", + name, data); +#endif /* CY82C693_DEBUG_INFO */ + + /* + * for some reason sometimes the DMA controller + * speed is set to ATCLK/2 ???? - we fix this here + * + * note: i don't know what causes this strange behaviour, + * but even changing the dma speed doesn't solve it :-( + * the ide performance is still only half the normal speed + * + * if anybody knows what goes wrong with my machine, please + * let me know - ASK + */ + + data |= 0x03; + + OUT_BYTE(CY82_INDEX_CTRLREG1, CY82_INDEX_PORT); + OUT_BYTE(data, CY82_DATA_PORT); + +#if CY82C693_DEBUG_INFO + printk (KERN_INFO "%s: New Peripheral Configuration Register: 0x%X\n", + name, data); +#endif /* CY82C693_DEBUG_INFO */ + +#endif /* CY82C693_SETDMA_CLOCK */ + return 0; +} + +/* + * the init function - called for each ide channel once + */ +void __init ide_init_cy82c693(ide_hwif_t *hwif) +{ + hwif->chipset = ide_cy82c693; + hwif->tuneproc = &cy82c693_tune_drive; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + hwif->autodma = 0; + + if (!hwif->dma_base) + return; +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->dmaproc = &cy82c693_dmaproc; +#ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +#endif /* CONFIG_IDEDMA_AUTO */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ +} + +extern void ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d); + +void __init fixup_device_cy82c693 (struct pci_dev *dev, ide_pci_device_t *d) +{ + if ((!(PCI_FUNC(dev->devfn) & 1) || + (!((dev->class >> 8) == PCI_CLASS_STORAGE_IDE)))) + return; /* CY82C693 is more than only a IDE controller */ + + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); +} + diff -Nru a/drivers/ide/dtc2278.c b/drivers/ide/dtc2278.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/dtc2278.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,132 @@ +/* + * linux/drivers/ide/dtc2278.c Version 0.02 Feb 10, 1996 + * + * Copyright (C) 1996 Linus Torvalds & author (see below) + */ + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ide_modes.h" + +/* + * Changing this #undef to #define may solve start up problems in some systems. + */ +#undef ALWAYS_SET_DTC2278_PIO_MODE + +/* + * From: andy@cercle.cts.com (Dyan Wile) + * + * Below is a patch for DTC-2278 - alike software-programmable controllers + * The code enables the secondary IDE controller and the PIO4 (3?) timings on + * the primary (EIDE). You may probably have to enable the 32-bit support to + * get the full speed. You better get the disk interrupts disabled ( hdparm -u0 + * /dev/hd.. ) for the drives connected to the EIDE interface. (I get my + * filesystem corrupted with -u1, but under heavy disk load only :-) + * + * This card is now forced to use the "serialize" feature, + * and irq-unmasking is disallowed. If io_32bit is enabled, + * it must be done for BOTH drives on each interface. + * + * This code was written for the DTC2278E, but might work with any of these: + * + * DTC2278S has only a single IDE interface. + * DTC2278D has two IDE interfaces and is otherwise identical to the S version. + * DTC2278E also has serial ports and a printer port + * DTC2278EB: has onboard BIOS, and "works like a charm" -- Kent Bradford + * + * There may be a fourth controller type. The S and D versions use the + * Winbond chip, and I think the E version does also. + * + */ + +static void sub22 (char b, char c) +{ + int i; + + for(i = 0; i < 3; ++i) { + IN_BYTE(0x3f6); + outb_p(b,0xb0); + IN_BYTE(0x3f6); + outb_p(c,0xb4); + IN_BYTE(0x3f6); + if(IN_BYTE(0xb4) == c) { + outb_p(7,0xb0); + IN_BYTE(0x3f6); + return; /* success */ + } + } +} + +static void tune_dtc2278 (ide_drive_t *drive, byte pio) +{ + unsigned long flags; + + pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + + if (pio >= 3) { + spin_lock_irqsave(&ide_lock, flags); + /* + * This enables PIO mode4 (3?) on the first interface + */ + sub22(1,0xc3); + sub22(0,0xa0); + spin_unlock_irqrestore(&ide_lock, flags); + } else { + /* we don't know how to set it back again.. */ + } + + /* + * 32bit I/O has to be enabled for *both* drives at the same time. + */ + drive->io_32bit = 1; + HWIF(drive)->drives[!drive->select.b.unit].io_32bit = 1; +} + +void __init init_dtc2278 (void) +{ + unsigned long flags; + + local_irq_save(flags); + /* + * This enables the second interface + */ + outb_p(4,0xb0); + IN_BYTE(0x3f6); + outb_p(0x20,0xb4); + IN_BYTE(0x3f6); +#ifdef ALWAYS_SET_DTC2278_PIO_MODE + /* + * This enables PIO mode4 (3?) on the first interface + * and may solve start-up problems for some people. + */ + sub22(1,0xc3); + sub22(0,0xa0); +#endif + local_irq_restore(flags); + + ide_hwifs[0].serialized = 1; + ide_hwifs[1].serialized = 1; + ide_hwifs[0].chipset = ide_dtc2278; + ide_hwifs[1].chipset = ide_dtc2278; + ide_hwifs[0].tuneproc = &tune_dtc2278; + ide_hwifs[0].drives[0].no_unmask = 1; + ide_hwifs[0].drives[1].no_unmask = 1; + ide_hwifs[1].drives[0].no_unmask = 1; + ide_hwifs[1].drives[1].no_unmask = 1; + ide_hwifs[0].mate = &ide_hwifs[1]; + ide_hwifs[1].mate = &ide_hwifs[0]; + ide_hwifs[1].channel = 1; +} diff -Nru a/drivers/ide/falconide.c b/drivers/ide/falconide.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/falconide.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,68 @@ +/* + * linux/drivers/ide/falconide.c -- Atari Falcon IDE Driver + * + * Created 12 Jul 1997 by Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + + /* + * Base of the IDE interface + */ + +#define ATA_HD_BASE 0xfff00000 + + /* + * Offsets from the above base + */ + +#define ATA_HD_DATA 0x00 +#define ATA_HD_ERROR 0x05 /* see err-bits */ +#define ATA_HD_NSECTOR 0x09 /* nr of sectors to read/write */ +#define ATA_HD_SECTOR 0x0d /* starting sector */ +#define ATA_HD_LCYL 0x11 /* starting cylinder */ +#define ATA_HD_HCYL 0x15 /* high byte of starting cyl */ +#define ATA_HD_SELECT 0x19 /* 101dhhhh , d=drive, hhhh=head */ +#define ATA_HD_STATUS 0x1d /* see status-bits */ +#define ATA_HD_CONTROL 0x39 + +static int falconide_offsets[IDE_NR_PORTS] __initdata = { + ATA_HD_DATA, ATA_HD_ERROR, ATA_HD_NSECTOR, ATA_HD_SECTOR, ATA_HD_LCYL, + ATA_HD_HCYL, ATA_HD_SELECT, ATA_HD_STATUS, ATA_HD_CONTROL, -1 +}; + + + /* + * Probe for a Falcon IDE interface + */ + +void __init falconide_init(void) +{ + if (MACH_IS_ATARI && ATARIHW_PRESENT(IDE)) { + hw_regs_t hw; + int index; + + ide_setup_ports(&hw, (ide_ioreg_t)ATA_HD_BASE, falconide_offsets, + 0, 0, NULL, IRQ_MFP_IDE); + index = ide_register_hw(&hw, NULL); + + if (index != -1) + printk("ide%d: Falcon IDE interface\n", index); + } +} diff -Nru a/drivers/ide/gayle.c b/drivers/ide/gayle.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/gayle.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,181 @@ +/* + * linux/drivers/ide/gayle.c -- Amiga Gayle IDE Driver + * + * Created 9 Jul 1997 by Geert Uytterhoeven + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + + + /* + * Bases of the IDE interfaces + */ + +#define GAYLE_BASE_4000 0xdd2020 /* A4000/A4000T */ +#define GAYLE_BASE_1200 0xda0000 /* A1200/A600 */ + + /* + * Offsets from one of the above bases + */ + +#define GAYLE_DATA 0x00 +#define GAYLE_ERROR 0x06 /* see err-bits */ +#define GAYLE_NSECTOR 0x0a /* nr of sectors to read/write */ +#define GAYLE_SECTOR 0x0e /* starting sector */ +#define GAYLE_LCYL 0x12 /* starting cylinder */ +#define GAYLE_HCYL 0x16 /* high byte of starting cyl */ +#define GAYLE_SELECT 0x1a /* 101dhhhh , d=drive, hhhh=head */ +#define GAYLE_STATUS 0x1e /* see status-bits */ +#define GAYLE_CONTROL 0x101a + +static int gayle_offsets[IDE_NR_PORTS] __initdata = { + GAYLE_DATA, GAYLE_ERROR, GAYLE_NSECTOR, GAYLE_SECTOR, GAYLE_LCYL, + GAYLE_HCYL, GAYLE_SELECT, GAYLE_STATUS, -1, -1 +}; + + + /* + * These are at different offsets from the base + */ + +#define GAYLE_IRQ_4000 0xdd3020 /* MSB = 1, Harddisk is source of */ +#define GAYLE_IRQ_1200 0xda9000 /* interrupt */ + + + /* + * Offset of the secondary port for IDE doublers + * Note that GAYLE_CONTROL is NOT available then! + */ + +#define GAYLE_NEXT_PORT 0x1000 + +#ifndef CONFIG_BLK_DEV_IDEDOUBLER +#define GAYLE_NUM_HWIFS 1 +#define GAYLE_NUM_PROBE_HWIFS GAYLE_NUM_HWIFS +#define GAYLE_HAS_CONTROL_REG 1 +#define GAYLE_IDEREG_SIZE 0x2000 +#else /* CONFIG_BLK_DEV_IDEDOUBLER */ +#define GAYLE_NUM_HWIFS 2 +#define GAYLE_NUM_PROBE_HWIFS (ide_doubler ? GAYLE_NUM_HWIFS : \ + GAYLE_NUM_HWIFS-1) +#define GAYLE_HAS_CONTROL_REG (!ide_doubler) +#define GAYLE_IDEREG_SIZE (ide_doubler ? 0x1000 : 0x2000) +int ide_doubler = 0; /* support IDE doublers? */ +#endif /* CONFIG_BLK_DEV_IDEDOUBLER */ + + + /* + * Check and acknowledge the interrupt status + */ + +static int gayle_ack_intr_a4000(ide_hwif_t *hwif) +{ + unsigned char ch; + + ch = z_readb(hwif->io_ports[IDE_IRQ_OFFSET]); + if (!(ch & GAYLE_IRQ_IDE)) + return 0; + return 1; +} + +static int gayle_ack_intr_a1200(ide_hwif_t *hwif) +{ + unsigned char ch; + + ch = z_readb(hwif->io_ports[IDE_IRQ_OFFSET]); + if (!(ch & GAYLE_IRQ_IDE)) + return 0; + (void)z_readb(hwif->io_ports[IDE_STATUS_OFFSET]); + z_writeb(0x7c, hwif->io_ports[IDE_IRQ_OFFSET]); + return 1; +} + + /* + * Probe for a Gayle IDE interface (and optionally for an IDE doubler) + */ + +void __init gayle_init(void) +{ + int a4000, i; + + if (!MACH_IS_AMIGA) + return; + + if (!(a4000 = AMIGAHW_PRESENT(A4000_IDE)) && !AMIGAHW_PRESENT(A1200_IDE)) + return; + + for (i = 0; i < GAYLE_NUM_PROBE_HWIFS; i++) { + ide_ioreg_t base, ctrlport, irqport; + ide_ack_intr_t *ack_intr; + hw_regs_t hw; + int index; + unsigned long phys_base, res_start, res_n; + + if (a4000) { + phys_base = GAYLE_BASE_4000; + irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_4000); + ack_intr = gayle_ack_intr_a4000; + } else { + phys_base = GAYLE_BASE_1200; + irqport = (ide_ioreg_t)ZTWO_VADDR(GAYLE_IRQ_1200); + ack_intr = gayle_ack_intr_a1200; + } + phys_base += i*GAYLE_NEXT_PORT; + + res_start = ((unsigned long)phys_base) & ~(GAYLE_NEXT_PORT-1); + res_n = GAYLE_IDEREG_SIZE; + + if (!request_mem_region(res_start, res_n, "IDE")) + continue; + + base = (ide_ioreg_t)ZTWO_VADDR(phys_base); + ctrlport = GAYLE_HAS_CONTROL_REG ? (base + GAYLE_CONTROL) : 0; + + ide_setup_ports(&hw, base, gayle_offsets, + ctrlport, irqport, ack_intr, IRQ_AMIGA_PORTS); + + index = ide_register_hw(&hw, NULL); + if (index != -1) { + switch (i) { + case 0: + printk("ide%d: Gayle IDE interface (A%d style)\n", index, + a4000 ? 4000 : 1200); + break; +#ifdef CONFIG_BLK_DEV_IDEDOUBLER + case 1: + printk("ide%d: IDE doubler\n", index); + break; +#endif /* CONFIG_BLK_DEV_IDEDOUBLER */ + } + } else + release_mem_region(res_start, res_n); + +#if 1 /* TESTING */ + if (i == 1) { + volatile u_short *addr = (u_short *)base; + u_short data; + printk("+++ Probing for IDE doubler... "); + *addr = 0xffff; + data = *addr; + printk("probe returned 0x%02x (PLEASE REPORT THIS!!)\n", data); + } +#endif /* TESTING */ + } +} diff -Nru a/drivers/ide/hd.c b/drivers/ide/hd.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/hd.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,884 @@ +/* + * Copyright (C) 1991, 1992 Linus Torvalds + * + * This is the low-level hd interrupt support. It traverses the + * request-list, using interrupts to jump between functions. As + * all the functions are called within interrupts, we may not + * sleep. Special care is recommended. + * + * modified by Drew Eckhardt to check nr of hd's from the CMOS. + * + * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug + * in the early extended-partition checks and added DM partitions + * + * IRQ-unmask, drive-id, multiple-mode, support for ">16 heads", + * and general streamlining by Mark Lord. + * + * Removed 99% of above. Use Mark's ide driver for those options. + * This is now a lightweight ST-506 driver. (Paul Gortmaker) + * + * Modified 1995 Russell King for ARM processor. + * + * Bugfix: max_sectors must be <= 255 or the wheels tend to come + * off in a hurry once you queue things up - Paul G. 02/2001 + */ + +/* Uncomment the following if you want verbose error reports. */ +/* #define VERBOSE_ERRORS */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* CMOS defines */ +#include +#include +#include + +#define REALLY_SLOW_IO +#include +#include +#include + +#define MAJOR_NR HD_MAJOR +#define DEVICE_NR(device) (minor(device)>>6) +#include + +/* ATA commands we use. + */ +#define WIN_SPECIFY 0x91 /* set drive geometry translation */ +#define WIN_RESTORE 0x10 +#define WIN_READ 0x20 /* 28-Bit */ +#define WIN_WRITE 0x30 /* 28-Bit */ + +#define HD_IRQ 14 /* the standard disk interrupt */ + +#ifdef __arm__ +#undef HD_IRQ +#endif +#include +#ifdef __arm__ +#define HD_IRQ IRQ_HARDDISK +#endif + +/* Hd controller regster ports */ + +#define HD_DATA 0x1f0 /* _CTL when writing */ +#define HD_ERROR 0x1f1 /* see err-bits */ +#define HD_NSECTOR 0x1f2 /* nr of sectors to read/write */ +#define HD_SECTOR 0x1f3 /* starting sector */ +#define HD_LCYL 0x1f4 /* starting cylinder */ +#define HD_HCYL 0x1f5 /* high byte of starting cyl */ +#define HD_CURRENT 0x1f6 /* 101dhhhh , d=drive, hhhh=head */ +#define HD_STATUS 0x1f7 /* see status-bits */ +#define HD_FEATURE HD_ERROR /* same io address, read=error, write=feature */ +#define HD_PRECOMP HD_FEATURE /* obsolete use of this port - predates IDE */ +#define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */ + +#define HD_CMD 0x3f6 /* used for resets */ +#define HD_ALTSTATUS 0x3f6 /* same as HD_STATUS but doesn't clear irq */ + +/* Bits of HD_STATUS */ +#define ERR_STAT 0x01 +#define INDEX_STAT 0x02 +#define ECC_STAT 0x04 /* Corrected error */ +#define DRQ_STAT 0x08 +#define SEEK_STAT 0x10 +#define SERVICE_STAT SEEK_STAT +#define WRERR_STAT 0x20 +#define READY_STAT 0x40 +#define BUSY_STAT 0x80 + +/* Bits for HD_ERROR */ +#define MARK_ERR 0x01 /* Bad address mark */ +#define TRK0_ERR 0x02 /* couldn't find track 0 */ +#define ABRT_ERR 0x04 /* Command aborted */ +#define MCR_ERR 0x08 /* media change request */ +#define ID_ERR 0x10 /* ID field not found */ +#define MC_ERR 0x20 /* media changed */ +#define ECC_ERR 0x40 /* Uncorrectable ECC error */ +#define BBD_ERR 0x80 /* pre-EIDE meaning: block marked bad */ +#define ICRC_ERR 0x80 /* new meaning: CRC error during transfer */ + +static spinlock_t hd_lock = SPIN_LOCK_UNLOCKED; + +#define TIMEOUT_VALUE (6*HZ) +#define HD_DELAY 0 + +#define MAX_ERRORS 16 /* Max read/write errors/sector */ +#define RESET_FREQ 8 /* Reset controller every 8th retry */ +#define RECAL_FREQ 4 /* Recalibrate every 4th retry */ +#define MAX_HD 2 + +#define STAT_OK (READY_STAT|SEEK_STAT) +#define OK_STATUS(s) (((s)&(STAT_OK|(BUSY_STAT|WRERR_STAT|ERR_STAT)))==STAT_OK) + +static void recal_intr(void); +static void bad_rw_intr(void); + +static char recalibrate[MAX_HD]; +static char special_op[MAX_HD]; + +static int reset; +static int hd_error; + +#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0) + +/* + * This struct defines the HD's and their types. + */ +struct hd_i_struct { + unsigned int head,sect,cyl,wpcom,lzone,ctl; +}; + +#ifdef HD_TYPE +static struct hd_i_struct hd_info[] = { HD_TYPE }; +static int NR_HD = ((sizeof (hd_info))/(sizeof (struct hd_i_struct))); +#else +static struct hd_i_struct hd_info[MAX_HD]; +static int NR_HD; +#endif + +static struct hd_struct hd[MAX_HD<<6]; + +static struct timer_list device_timer; + +#define TIMEOUT_VALUE (6*HZ) + +#define SET_TIMER \ + do { \ + mod_timer(&device_timer, jiffies + TIMEOUT_VALUE); \ + } while (0) + +static void (*do_hd)(void) = NULL; +#define SET_HANDLER(x) \ +if ((do_hd = (x)) != NULL) \ + SET_TIMER; \ +else \ + del_timer(&device_timer); + + +#if (HD_DELAY > 0) +unsigned long last_req; + +unsigned long read_timer(void) +{ + extern spinlock_t i8253_lock; + unsigned long t, flags; + int i; + + spin_lock_irqsave(&i8253_lock, flags); + t = jiffies * 11932; + outb_p(0, 0x43); + i = inb_p(0x40); + i |= inb(0x40) << 8; + spin_unlock_irqrestore(&i8253_lock, flags); + return(t - i); +} +#endif + +void __init hd_setup(char *str, int *ints) +{ + int hdind = 0; + + if (ints[0] != 3) + return; + if (hd_info[0].head != 0) + hdind=1; + hd_info[hdind].head = ints[2]; + hd_info[hdind].sect = ints[3]; + hd_info[hdind].cyl = ints[1]; + hd_info[hdind].wpcom = 0; + hd_info[hdind].lzone = ints[1]; + hd_info[hdind].ctl = (ints[2] > 8 ? 8 : 0); + NR_HD = hdind+1; +} + +static void dump_status (const char *msg, unsigned int stat) +{ + char devc; + + devc = !blk_queue_empty(QUEUE) ? 'a' + DEVICE_NR(CURRENT->rq_dev) : '?'; +#ifdef VERBOSE_ERRORS + printk("hd%c: %s: status=0x%02x { ", devc, msg, stat & 0xff); + if (stat & BUSY_STAT) printk("Busy "); + if (stat & READY_STAT) printk("DriveReady "); + if (stat & WRERR_STAT) printk("WriteFault "); + if (stat & SEEK_STAT) printk("SeekComplete "); + if (stat & DRQ_STAT) printk("DataRequest "); + if (stat & ECC_STAT) printk("CorrectedError "); + if (stat & INDEX_STAT) printk("Index "); + if (stat & ERR_STAT) printk("Error "); + printk("}\n"); + if ((stat & ERR_STAT) == 0) { + hd_error = 0; + } else { + hd_error = inb(HD_ERROR); + printk("hd%c: %s: error=0x%02x { ", devc, msg, hd_error & 0xff); + if (hd_error & BBD_ERR) printk("BadSector "); + if (hd_error & ECC_ERR) printk("UncorrectableError "); + if (hd_error & ID_ERR) printk("SectorIdNotFound "); + if (hd_error & ABRT_ERR) printk("DriveStatusError "); + if (hd_error & TRK0_ERR) printk("TrackZeroNotFound "); + if (hd_error & MARK_ERR) printk("AddrMarkNotFound "); + printk("}"); + if (hd_error & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) { + printk(", CHS=%d/%d/%d", (inb(HD_HCYL)<<8) + inb(HD_LCYL), + inb(HD_CURRENT) & 0xf, inb(HD_SECTOR)); + if (!blk_queue_empty(QUEUE)) + printk(", sector=%ld", CURRENT->sector); + } + printk("\n"); + } +#else + printk("hd%c: %s: status=0x%02x.\n", devc, msg, stat & 0xff); + if ((stat & ERR_STAT) == 0) { + hd_error = 0; + } else { + hd_error = inb(HD_ERROR); + printk("hd%c: %s: error=0x%02x.\n", devc, msg, hd_error & 0xff); + } +#endif +} + +void check_status(void) +{ + int i = inb_p(HD_STATUS); + + if (!OK_STATUS(i)) { + dump_status("check_status", i); + bad_rw_intr(); + } +} + +static int controller_busy(void) +{ + int retries = 100000; + unsigned char status; + + do { + status = inb_p(HD_STATUS); + } while ((status & BUSY_STAT) && --retries); + return status; +} + +static int status_ok(void) +{ + unsigned char status = inb_p(HD_STATUS); + + if (status & BUSY_STAT) + return 1; /* Ancient, but does it make sense??? */ + if (status & WRERR_STAT) + return 0; + if (!(status & READY_STAT)) + return 0; + if (!(status & SEEK_STAT)) + return 0; + return 1; +} + +static int controller_ready(unsigned int drive, unsigned int head) +{ + int retry = 100; + + do { + if (controller_busy() & BUSY_STAT) + return 0; + outb_p(0xA0 | (drive<<4) | head, HD_CURRENT); + if (status_ok()) + return 1; + } while (--retry); + return 0; +} + +static void hd_out(unsigned int drive,unsigned int nsect,unsigned int sect, + unsigned int head,unsigned int cyl,unsigned int cmd, + void (*intr_addr)(void)) +{ + unsigned short port; + +#if (HD_DELAY > 0) + while (read_timer() - last_req < HD_DELAY) + /* nothing */; +#endif + if (reset) + return; + if (!controller_ready(drive, head)) { + reset = 1; + return; + } + SET_HANDLER(intr_addr); + outb_p(hd_info[drive].ctl,HD_CMD); + port=HD_DATA; + outb_p(hd_info[drive].wpcom>>2,++port); + outb_p(nsect,++port); + outb_p(sect,++port); + outb_p(cyl,++port); + outb_p(cyl>>8,++port); + outb_p(0xA0|(drive<<4)|head,++port); + outb_p(cmd,++port); +} + +static void hd_request (void); + +static int drive_busy(void) +{ + unsigned int i; + unsigned char c; + + for (i = 0; i < 500000 ; i++) { + c = inb_p(HD_STATUS); + if ((c & (BUSY_STAT | READY_STAT | SEEK_STAT)) == STAT_OK) + return 0; + } + dump_status("reset timed out", c); + return 1; +} + +static void reset_controller(void) +{ + int i; + + outb_p(4,HD_CMD); + for(i = 0; i < 1000; i++) barrier(); + outb_p(hd_info[0].ctl & 0x0f,HD_CMD); + for(i = 0; i < 1000; i++) barrier(); + if (drive_busy()) + printk("hd: controller still busy\n"); + else if ((hd_error = inb(HD_ERROR)) != 1) + printk("hd: controller reset failed: %02x\n",hd_error); +} + +static void reset_hd(void) +{ + static int i; + +repeat: + if (reset) { + reset = 0; + i = -1; + reset_controller(); + } else { + check_status(); + if (reset) + goto repeat; + } + if (++i < NR_HD) { + special_op[i] = recalibrate[i] = 1; + hd_out(i,hd_info[i].sect,hd_info[i].sect,hd_info[i].head-1, + hd_info[i].cyl,WIN_SPECIFY,&reset_hd); + if (reset) + goto repeat; + } else + hd_request(); +} + +/* + * Ok, don't know what to do with the unexpected interrupts: on some machines + * doing a reset and a retry seems to result in an eternal loop. Right now I + * ignore it, and just set the timeout. + * + * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the + * drive enters "idle", "standby", or "sleep" mode, so if the status looks + * "good", we just ignore the interrupt completely. + */ +void unexpected_hd_interrupt(void) +{ + unsigned int stat = inb_p(HD_STATUS); + + if (stat & (BUSY_STAT|DRQ_STAT|ECC_STAT|ERR_STAT)) { + dump_status ("unexpected interrupt", stat); + SET_TIMER; + } +} + +/* + * bad_rw_intr() now tries to be a bit smarter and does things + * according to the error returned by the controller. + * -Mika Liljeberg (liljeber@cs.Helsinki.FI) + */ +static void bad_rw_intr(void) +{ + int dev; + + if (blk_queue_empty(QUEUE)) + return; + dev = DEVICE_NR(CURRENT->rq_dev); + if (++CURRENT->errors >= MAX_ERRORS || (hd_error & BBD_ERR)) { + end_request(CURRENT, 0); + special_op[dev] = recalibrate[dev] = 1; + } else if (CURRENT->errors % RESET_FREQ == 0) + reset = 1; + else if ((hd_error & TRK0_ERR) || CURRENT->errors % RECAL_FREQ == 0) + special_op[dev] = recalibrate[dev] = 1; + /* Otherwise just retry */ +} + +static inline int wait_DRQ(void) +{ + int retries = 100000, stat; + + while (--retries > 0) + if ((stat = inb_p(HD_STATUS)) & DRQ_STAT) + return 0; + dump_status("wait_DRQ", stat); + return -1; +} + +static void read_intr(void) +{ + int i, retries = 100000; + + do { + i = (unsigned) inb_p(HD_STATUS); + if (i & BUSY_STAT) + continue; + if (!OK_STATUS(i)) + break; + if (i & DRQ_STAT) + goto ok_to_read; + } while (--retries > 0); + dump_status("read_intr", i); + bad_rw_intr(); + hd_request(); + return; +ok_to_read: + insw(HD_DATA,CURRENT->buffer,256); + CURRENT->sector++; + CURRENT->buffer += 512; + CURRENT->errors = 0; + i = --CURRENT->nr_sectors; + --CURRENT->current_nr_sectors; +#ifdef DEBUG + printk("hd%c: read: sector %ld, remaining = %ld, buffer=0x%08lx\n", + dev+'a', CURRENT->sector, CURRENT->nr_sectors, + (unsigned long) CURRENT->buffer+512)); +#endif + if (CURRENT->current_nr_sectors <= 0) + end_request(CURRENT, 1); + if (i > 0) { + SET_HANDLER(&read_intr); + return; + } + (void) inb_p(HD_STATUS); +#if (HD_DELAY > 0) + last_req = read_timer(); +#endif + if (!blk_queue_empty(QUEUE)) + hd_request(); + return; +} + +static void write_intr(void) +{ + int i; + int retries = 100000; + + do { + i = (unsigned) inb_p(HD_STATUS); + if (i & BUSY_STAT) + continue; + if (!OK_STATUS(i)) + break; + if ((CURRENT->nr_sectors <= 1) || (i & DRQ_STAT)) + goto ok_to_write; + } while (--retries > 0); + dump_status("write_intr", i); + bad_rw_intr(); + hd_request(); + return; +ok_to_write: + CURRENT->sector++; + i = --CURRENT->nr_sectors; + --CURRENT->current_nr_sectors; + CURRENT->buffer += 512; + if (!i || (CURRENT->bio && !SUBSECTOR(i))) + end_request(CURRENT, 1); + if (i > 0) { + SET_HANDLER(&write_intr); + outsw(HD_DATA,CURRENT->buffer,256); + local_irq_enable(); + } else { +#if (HD_DELAY > 0) + last_req = read_timer(); +#endif + hd_request(); + } + return; +} + +static void recal_intr(void) +{ + check_status(); +#if (HD_DELAY > 0) + last_req = read_timer(); +#endif + hd_request(); +} + +/* + * This is another of the error-routines I don't know what to do with. The + * best idea seems to just set reset, and start all over again. + */ +static void hd_times_out(unsigned long dummy) +{ + unsigned int dev; + + do_hd = NULL; + + if (blk_queue_empty(QUEUE)) + return; + + disable_irq(HD_IRQ); + local_irq_enable(); + reset = 1; + dev = DEVICE_NR(CURRENT->rq_dev); + printk("hd%c: timeout\n", dev+'a'); + if (++CURRENT->errors >= MAX_ERRORS) { +#ifdef DEBUG + printk("hd%c: too many errors\n", dev+'a'); +#endif + end_request(CURRENT, 0); + } + local_irq_disable(); + hd_request(); + enable_irq(HD_IRQ); +} + +int do_special_op (unsigned int dev) +{ + if (recalibrate[dev]) { + recalibrate[dev] = 0; + hd_out(dev,hd_info[dev].sect,0,0,0,WIN_RESTORE,&recal_intr); + return reset; + } + if (hd_info[dev].head > 16) { + printk ("hd%c: cannot handle device with more than 16 heads - giving up\n", dev+'a'); + end_request(CURRENT, 0); + } + special_op[dev] = 0; + return 1; +} + +/* + * The driver enables interrupts as much as possible. In order to do this, + * (a) the device-interrupt is disabled before entering hd_request(), + * and (b) the timeout-interrupt is disabled before the sti(). + * + * Interrupts are still masked (by default) whenever we are exchanging + * data/cmds with a drive, because some drives seem to have very poor + * tolerance for latency during I/O. The IDE driver has support to unmask + * interrupts for non-broken hardware, so use that driver if required. + */ +static void hd_request(void) +{ + unsigned int dev, block, nsect, sec, track, head, cyl; + + if (do_hd) + return; +repeat: + del_timer(&device_timer); + local_irq_enable(); + + if (blk_queue_empty(QUEUE)) { + do_hd = NULL; + return; + } + + if (reset) { + local_irq_disable(); + reset_hd(); + return; + } + dev = minor(CURRENT->rq_dev); + block = CURRENT->sector; + nsect = CURRENT->nr_sectors; + if (dev >= (NR_HD<<6) || (dev & 0x3f) || + block >= hd[dev].nr_sects || ((block+nsect) > hd[dev].nr_sects)) { + if (dev >= (NR_HD<<6) || (dev & 0x3f)) + printk("hd: bad minor number: device=%s\n", + kdevname(CURRENT->rq_dev)); + else + printk("hd%c: bad access: block=%d, count=%d\n", + (minor(CURRENT->rq_dev)>>6)+'a', block, nsect); + end_request(CURRENT, 0); + goto repeat; + } + + dev >>= 6; + if (special_op[dev]) { + if (do_special_op(dev)) + goto repeat; + return; + } + sec = block % hd_info[dev].sect + 1; + track = block / hd_info[dev].sect; + head = track % hd_info[dev].head; + cyl = track / hd_info[dev].head; +#ifdef DEBUG + printk("hd%c: %sing: CHS=%d/%d/%d, sectors=%d, buffer=0x%08lx\n", + dev+'a', (CURRENT->cmd == READ)?"read":"writ", + cyl, head, sec, nsect, (unsigned long) CURRENT->buffer); +#endif + if(CURRENT->flags & REQ_CMD) { + switch (rq_data_dir(CURRENT)) { + case READ: + hd_out(dev,nsect,sec,head,cyl,WIN_READ,&read_intr); + if (reset) + goto repeat; + break; + case WRITE: + hd_out(dev,nsect,sec,head,cyl,WIN_WRITE,&write_intr); + if (reset) + goto repeat; + if (wait_DRQ()) { + bad_rw_intr(); + goto repeat; + } + outsw(HD_DATA,CURRENT->buffer,256); + break; + default: + printk("unknown hd-command\n"); + end_request(CURRENT, 0); + break; + } + } +} + +static void do_hd_request (request_queue_t * q) +{ + disable_irq(HD_IRQ); + hd_request(); + enable_irq(HD_IRQ); +} + +static int hd_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct hd_geometry *loc = (struct hd_geometry *) arg; + int dev; + + if ((!inode) || kdev_none(inode->i_rdev)) + return -EINVAL; + dev = DEVICE_NR(inode->i_rdev); + if (dev >= NR_HD) + return -EINVAL; + switch (cmd) { + case HDIO_GETGEO: + { + struct hd_geometry g; + if (!loc) return -EINVAL; + g.heads = hd_info[dev].head; + g.sectors = hd_info[dev].sect; + g.cylinders = hd_info[dev].cyl; + g.start = get_start_sect(inode->i_bdev); + return copy_to_user(loc, &g, sizeof g) ? -EFAULT : 0; + } + + default: + return -EINVAL; + } +} + +static int hd_open(struct inode * inode, struct file * filp) +{ + int target = DEVICE_NR(inode->i_rdev); + if (target >= NR_HD) + return -ENODEV; + return 0; +} + +/* + * Releasing a block device means we sync() it, so that it can safely + * be forgotten about... + */ + +extern struct block_device_operations hd_fops; + +static struct gendisk hd_gendisk[2] = { +{ + .major = MAJOR_NR, + .first_minor = 0, + .major_name = "hda", + .minor_shift = 6, + .part = hd, + .fops = &hd_fops, +},{ + .major = MAJOR_NR, + .first_minor = 64, + .major_name = "hdb", + .minor_shift = 6, + .part = hd + 64, + .fops = &hd_fops, +} +}; + +static void hd_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + void (*handler)(void) = do_hd; + + do_hd = NULL; + del_timer(&device_timer); + if (!handler) + handler = unexpected_hd_interrupt; + handler(); + local_irq_enable(); +} + +static struct block_device_operations hd_fops = { + .open = hd_open, + .ioctl = hd_ioctl, +}; + +/* + * This is the hard disk IRQ description. The SA_INTERRUPT in sa_flags + * means we run the IRQ-handler with interrupts disabled: this is bad for + * interrupt latency, but anything else has led to problems on some + * machines. + * + * We enable interrupts in some of the routines after making sure it's + * safe. + */ +static void __init hd_geninit(void) +{ + int drive; + + blk_queue_hardsect_size(QUEUE, 512); + +#ifdef __i386__ + if (!NR_HD) { + extern struct drive_info drive_info; + unsigned char *BIOS = (unsigned char *) &drive_info; + unsigned long flags; + int cmos_disks; + + for (drive=0 ; drive<2 ; drive++) { + hd_info[drive].cyl = *(unsigned short *) BIOS; + hd_info[drive].head = *(2+BIOS); + hd_info[drive].wpcom = *(unsigned short *) (5+BIOS); + hd_info[drive].ctl = *(8+BIOS); + hd_info[drive].lzone = *(unsigned short *) (12+BIOS); + hd_info[drive].sect = *(14+BIOS); +#ifdef does_not_work_for_everybody_with_scsi_but_helps_ibm_vp + if (hd_info[drive].cyl && NR_HD == drive) + NR_HD++; +#endif + BIOS += 16; + } + + /* + We query CMOS about hard disks : it could be that + we have a SCSI/ESDI/etc controller that is BIOS + compatible with ST-506, and thus showing up in our + BIOS table, but not register compatible, and therefore + not present in CMOS. + + Furthermore, we will assume that our ST-506 drives + 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. + + Currently the rtc_lock is a bit academic since this + driver is non-modular, but someday... ? Paul G. + */ + + spin_lock_irqsave(&rtc_lock, flags); + cmos_disks = CMOS_READ(0x12); + spin_unlock_irqrestore(&rtc_lock, flags); + + if (cmos_disks & 0xf0) { + if (cmos_disks & 0x0f) + NR_HD = 2; + else + NR_HD = 1; + } + } +#endif /* __i386__ */ +#ifdef __arm__ + if (!NR_HD) { + /* We don't know anything about the drive. This means + * that you *MUST* specify the drive parameters to the + * kernel yourself. + */ + printk("hd: no drives specified - use hd=cyl,head,sectors" + " on kernel command line\n"); + } +#endif + + for (drive=0 ; drive < NR_HD ; drive++) { + hd[drive<<6].nr_sects = hd_info[drive].head * + hd_info[drive].sect * hd_info[drive].cyl; + printk ("hd%c: %ldMB, CHS=%d/%d/%d\n", drive+'a', + hd[drive<<6].nr_sects / 2048, hd_info[drive].cyl, + hd_info[drive].head, hd_info[drive].sect); + } + if (!NR_HD) + return; + + if (request_irq(HD_IRQ, hd_interrupt, SA_INTERRUPT, "hd", NULL)) { + printk("hd: unable to get IRQ%d for the hard disk driver\n", + HD_IRQ); + NR_HD = 0; + return; + } + if (!request_region(HD_DATA, 8, "hd")) { + printk(KERN_WARNING "hd: port 0x%x busy\n", HD_DATA); + NR_HD = 0; + free_irq(HD_IRQ, NULL); + return; + } + if (!request_region(HD_CMD, 1, "hd(cmd)")) { + printk(KERN_WARNING "hd: port 0x%x busy\n", HD_CMD); + NR_HD = 0; + free_irq(HD_IRQ, NULL); + release_region(HD_DATA, 8); + return; + } + + for(drive=0; drive < NR_HD; drive++) { + hd_gendisk[drive].nr_real = 1; + add_gendisk(hd_gendisk + drive); + register_disk(hd_gendisk + drive, + mk_kdev(MAJOR_NR,drive<<6), 1<<6, + &hd_fops, hd_info[drive].head * hd_info[drive].sect * + hd_info[drive].cyl); + } +} + +int __init hd_init(void) +{ + if (register_blkdev(MAJOR_NR,"hd",&hd_fops)) { + printk("hd: unable to get major %d for hard disk\n",MAJOR_NR); + return -1; + } + blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), do_hd_request, &hd_lock); + blk_queue_max_sectors(BLK_DEFAULT_QUEUE(MAJOR_NR), 255); + init_timer(&device_timer); + device_timer.function = hd_times_out; + hd_geninit(); + return 0; +} + +static int parse_hd_setup (char *line) { + int ints[6]; + + (void) get_options(line, ARRAY_SIZE(ints), ints); + hd_setup(NULL, ints); + + return 1; +} +__setup("hd=", parse_hd_setup); + diff -Nru a/drivers/ide/hpt34x.c b/drivers/ide/hpt34x.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/hpt34x.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,491 @@ +/* + * linux/drivers/ide/hpt34x.c Version 0.31 June. 9, 2000 + * + * Copyright (C) 1998-2000 Andre Hedrick + * May be copied or modified under the terms of the GNU General Public License + * + * + * 00:12.0 Unknown mass storage controller: + * Triones Technologies, Inc. + * Unknown device 0003 (rev 01) + * + * hde: UDMA 2 (0x0000 0x0002) (0x0000 0x0010) + * hdf: UDMA 2 (0x0002 0x0012) (0x0010 0x0030) + * hde: DMA 2 (0x0000 0x0002) (0x0000 0x0010) + * hdf: DMA 2 (0x0002 0x0012) (0x0010 0x0030) + * hdg: DMA 1 (0x0012 0x0052) (0x0030 0x0070) + * hdh: DMA 1 (0x0052 0x0252) (0x0070 0x00f0) + * + * ide-pci.c reference + * + * Since there are two cards that report almost identically, + * the only discernable difference is the values reported in pcicmd. + * Booting-BIOS card or HPT363 :: pcicmd == 0x07 + * Non-bootable card or HPT343 :: pcicmd == 0x05 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "ide_modes.h" + +#ifndef SPLIT_BYTE +#define SPLIT_BYTE(B,H,L) ((H)=(B>>4), (L)=(B-((B>>4)<<4))) +#endif + +#define HPT343_DEBUG_DRIVE_INFO 0 + +#undef DISPLAY_HPT34X_TIMINGS + +#define HPT34X_MAX_DEVS 8 +static struct pci_dev *hpt34x_devs[HPT34X_MAX_DEVS]; +static int n_hpt34x_devs; + +#if defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +static int hpt34x_get_info(char *, char **, off_t, int); +extern int (*hpt34x_display_info)(char *, char **, off_t, int); /* ide-proc.c */ + +static int hpt34x_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + int i; + + p += sprintf(p, "\n " + "HPT34X Chipset.\n"); + for (i = 0; i < n_hpt34x_devs; i++) { + struct pci_dev *dev = hpt34x_devs[i]; + u32 bibma = pci_resource_start(dev, 4); + u8 c0 = 0, c1 = 0; + + /* + * at that point bibma+0x2 et bibma+0xa are byte registers + * to investigate: + */ + c0 = inb_p((unsigned short)bibma + 0x02); + c1 = inb_p((unsigned short)bibma + 0x0a); + p += sprintf(p, "\nController: %d\n", i); + p += sprintf(p, "--------------- Primary Channel " + "---------------- Secondary Channel " + "-------------\n"); + p += sprintf(p, " %sabled " + " %sabled\n", + (c0&0x80) ? "dis" : " en", + (c1&0x80) ? "dis" : " en"); + p += sprintf(p, "--------------- drive0 --------- drive1 " + "-------- drive0 ---------- drive1 ------\n"); + p += sprintf(p, "DMA enabled: %s %s" + " %s %s\n", + (c0&0x20) ? "yes" : "no ", + (c0&0x40) ? "yes" : "no ", + (c1&0x20) ? "yes" : "no ", + (c1&0x40) ? "yes" : "no " ); + + p += sprintf(p, "UDMA\n"); + p += sprintf(p, "DMA\n"); + p += sprintf(p, "PIO\n"); + } + p += sprintf(p, "\n"); + + return p-buffer; /* => must be less than 4k! */ +} +#endif /* defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS) */ + +byte hpt34x_proc = 0; + +static byte hpt34x_ratemask (ide_drive_t *drive) +{ + byte mode = 0x00; + + mode |= 0x01; + + if (!eighty_ninty_three(drive)) { + mode &= ~0xFE; + mode |= 0x01; + } + return (mode &= ~0xF8); +} + +static byte hpt34x_ratefilter (ide_drive_t *drive, byte speed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA +# ifdef CONFIG_HPT34X_AUTODMA +byte mode = hpt34x_ratemask(drive); + + switch(mode) { + case 0x04: // while (speed > XFER_UDMA_6) speed--; break; + case 0x03: // while (speed > XFER_UDMA_5) speed--; break; + case 0x02: // while (speed > XFER_UDMA_4) speed--; break; + case 0x01: while (speed > XFER_UDMA_2) speed--; break; + case 0x00: + default: while (speed > XFER_MW_DMA_2) speed--; break; + break; + } +# else /* !CONFIG_HPT34X_AUTODMA */ + while (speed > XFER_PIO_4) speed--; +# endif /* CONFIG_HPT34X_AUTODMA */ +#else + while (speed > XFER_PIO_4) speed--; +#endif /* CONFIG_BLK_DEV_IDEDMA */ +// printk("%s: mode == %02x speed == %02x\n", drive->name, mode, speed); + return speed; +} + +static void hpt34x_clear_chipset (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + unsigned int reg1 = 0, tmp1 = 0; + unsigned int reg2 = 0, tmp2 = 0; + + pci_read_config_dword(dev, 0x44, ®1); + pci_read_config_dword(dev, 0x48, ®2); + tmp1 = ((0x00 << (3*drive->dn)) | (reg1 & ~(7 << (3*drive->dn)))); + tmp2 = (reg2 & ~(0x11 << drive->dn)); + pci_write_config_dword(dev, 0x44, tmp1); + pci_write_config_dword(dev, 0x48, tmp2); +} + +static int hpt34x_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + byte speed = hpt34x_ratefilter(drive, xferspeed); + unsigned int reg1 = 0, tmp1 = 0; + unsigned int reg2 = 0, tmp2 = 0; + byte hi_speed, lo_speed; + + SPLIT_BYTE(speed, hi_speed, lo_speed); + + if (hi_speed & 7) { + hi_speed = (hi_speed & 4) ? 0x01 : 0x10; + } else { + lo_speed <<= 5; + lo_speed >>= 5; + } + + pci_read_config_dword(dev, 0x44, ®1); + pci_read_config_dword(dev, 0x48, ®2); + tmp1 = ((lo_speed << (3*drive->dn)) | (reg1 & ~(7 << (3*drive->dn)))); + tmp2 = ((hi_speed << drive->dn) | reg2); + pci_write_config_dword(dev, 0x44, tmp1); + pci_write_config_dword(dev, 0x48, tmp2); + +#if HPT343_DEBUG_DRIVE_INFO + printk("%s: %s drive%d (0x%04x 0x%04x) (0x%04x 0x%04x)" \ + " (0x%02x 0x%02x)\n", + drive->name, ide_xfer_verbose(speed), + drive->dn, reg1, tmp1, reg2, tmp2, + hi_speed, lo_speed); +#endif /* HPT343_DEBUG_DRIVE_INFO */ + + return(ide_config_drive_speed(drive, speed)); +} + +static void hpt34x_tune_drive (ide_drive_t *drive, byte pio) +{ + byte speed; + pio = ide_get_best_pio_mode(drive, pio, 5, NULL); + + switch(pio) { + case 4: speed = XFER_PIO_4; break; + case 3: speed = XFER_PIO_3; break; + case 2: speed = XFER_PIO_2; break; + case 1: speed = XFER_PIO_1; break; + default: speed = XFER_PIO_0; break; + } + hpt34x_clear_chipset(drive); + (void) hpt34x_tune_chipset(drive, speed); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +/* + * This allows the configuration of ide_pci chipset registers + * for cards that learn about the drive's UDMA, DMA, PIO capabilities + * after the drive is reported by the OS. Initally for designed for + * HPT343 UDMA chipset by HighPoint|Triones Technologies, Inc. + */ +static int config_chipset_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + byte mode = hpt34x_ratemask(drive); + byte speed = 0x00; + + if (drive->media != ide_disk) + return ((int) ide_dma_off_quietly); + + switch(mode) { + case 0x01: + if (id->dma_ultra & 0x0004) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0002) + { speed = XFER_UDMA_1; break; } + if (id->dma_ultra & 0x0001) + { speed = XFER_UDMA_0; break; } + case 0x00: + if (id->dma_mword & 0x0004) + { speed = XFER_MW_DMA_2; break; } + if (id->dma_mword & 0x0002) + { speed = XFER_MW_DMA_1; break; } + if (id->dma_mword & 0x0001) + { speed = XFER_MW_DMA_0; break; } + if (id->dma_1word & 0x0004) + { speed = XFER_SW_DMA_2; break; } + if (id->dma_1word & 0x0002) + { speed = XFER_SW_DMA_1; break; } + if (id->dma_1word & 0x0001) + { speed = XFER_SW_DMA_0; break; } + default: + return ((int) ide_dma_off_quietly); + } + + hpt34x_clear_chipset(drive); + (void) hpt34x_tune_chipset(drive, speed); + + return ((int) ((id->dma_ultra >> 11) & 3) ? ide_dma_off : + ((id->dma_ultra >> 8) & 7) ? ide_dma_on : + ((id->dma_mword >> 8) & 7) ? ide_dma_on : + ((id->dma_1word >> 8) & 7) ? ide_dma_on : + ide_dma_off_quietly); +} + +static int config_drive_xfer_rate (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_dma_action_t dma_func = ide_dma_on; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && HWIF(drive)->autodma) { + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + goto fast_ata_pio; + } + dma_func = ide_dma_off_quietly; + if (id->field_valid & 4) { + if (id->dma_ultra & 0x0007) { + /* Force if Capable UltraDMA */ + dma_func = config_chipset_for_dma(drive); + if ((id->field_valid & 2) && + (dma_func != ide_dma_on)) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & 0x0007) || + (id->dma_1word & 0x0007)) { + /* Force if Capable regular DMA modes */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } + } else if (ide_dmaproc(ide_dma_good_drive, drive)) { + if (id->eide_dma_time > 150) { + goto no_dma_set; + } + /* Consult the list of known "good" drives */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + dma_func = ide_dma_off_quietly; +no_dma_set: + hpt34x_tune_drive(drive, 255); + } + +#ifndef CONFIG_HPT34X_AUTODMA + if (dma_func == ide_dma_on) + dma_func = ide_dma_off; +#endif /* CONFIG_HPT34X_AUTODMA */ + + return HWIF(drive)->dmaproc(dma_func, drive); +} + +/* + * hpt34x_dmaproc() initiates/aborts (U)DMA read/write operations on a drive. + * + * This is specific to the HPT343 UDMA bios-less chipset + * and HPT345 UDMA bios chipset (stamped HPT363) + * by HighPoint|Triones Technologies, Inc. + */ + +int hpt34x_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); +// ide_task_t *args = HWGROUP(drive)->rq->special; + unsigned long dma_base = hwif->dma_base; + unsigned int count, reading = 0; + byte dma_stat; + + switch (func) { + case ide_dma_check: + return config_drive_xfer_rate(drive); + case ide_dma_read: + reading = 1 << 3; + case ide_dma_write: + if (!(count = ide_build_dmatable(drive, func))) + return 1; + /* try PIO instead of DMA */ + outl(hwif->dmatable_dma, dma_base + 4); + /* PRD table */ + reading |= 0x01; + OUT_BYTE(reading, dma_base); + /* specify r/w */ + OUT_BYTE(IN_BYTE(dma_base+2)|6, dma_base+2); + /* clear INTR & ERROR flags */ + drive->waiting_for_dma = 1; + if (drive->media != ide_disk) + return 0; + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); + /* issue cmd to drive */ + /* + * FIX ME to use only ACB ide_task_t args Struct + */ +#if 0 + { + ide_task_t *args = HWGROUP(drive)->rq->special; + OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + { +#else + if (HWGROUP(drive)->rq->flags == REQ_DRIVE_TASKFILE) { + ide_task_t *args = HWGROUP(drive)->rq->special; + OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } else if (drive->addressing == 1) + OUT_BYTE((reading == 9) ? WIN_READDMA_EXT : WIN_WRITEDMA_EXT, IDE_COMMAND_REG); + else + OUT_BYTE((reading == 9) ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); +#endif + return HWIF(drive)->dmaproc(ide_dma_begin, drive); + case ide_dma_end: /* returns 1 on error, 0 otherwise */ + drive->waiting_for_dma = 0; + /* stop DMA */ + OUT_BYTE(IN_BYTE(dma_base)&~1, dma_base); + /* get DMA status */ + dma_stat = IN_BYTE(dma_base+2); + /* clear the INTR & ERROR bits */ + OUT_BYTE(dma_stat|6, dma_base+2); + /* purge DMA mappings */ + ide_destroy_dmatable(drive); + /* verify good DMA status */ + return (dma_stat & 7) != 4; + default: + break; + } + return ide_dmaproc(func, drive); /* use standard DMA stuff */ +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +/* + * If the BIOS does not set the IO base addaress to XX00, 343 will fail. + */ +#define HPT34X_PCI_INIT_REG 0x80 + +unsigned int __init pci_init_hpt34x (struct pci_dev *dev, const char *name) +{ + int i = 0; + unsigned long hpt34xIoBase = pci_resource_start(dev, 4); + unsigned long hpt_addr[4] = { 0x20, 0x34, 0x28, 0x3c }; + unsigned short cmd; + unsigned long flags; + + local_irq_save(flags); + + pci_write_config_byte(dev, HPT34X_PCI_INIT_REG, 0x00); + pci_read_config_word(dev, PCI_COMMAND, &cmd); + + if (cmd & PCI_COMMAND_MEMORY) { + if (pci_resource_start(dev, PCI_ROM_RESOURCE)) { + pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); + printk(KERN_INFO "HPT345: ROM enabled at 0x%08lx\n", + dev->resource[PCI_ROM_RESOURCE].start); + } + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xF0); + } else { + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x20); + } + + /* + * Since 20-23 can be assigned and are R/W, we correct them. + */ + pci_write_config_word(dev, PCI_COMMAND, cmd & ~PCI_COMMAND_IO); + for(i=0; i<4; i++) { + dev->resource[i].start = (hpt34xIoBase + hpt_addr[i]); + dev->resource[i].flags |= PCI_BASE_ADDRESS_SPACE_IO; + pci_write_config_dword(dev, + (PCI_BASE_ADDRESS_0 + (i * 4)), + dev->resource[i].start); + } + pci_write_config_word(dev, PCI_COMMAND, cmd); + + local_irq_restore(flags); + + hpt34x_devs[n_hpt34x_devs++] = dev; + +#if defined(DISPLAY_HPT34X_TIMINGS) && defined(CONFIG_PROC_FS) + if (!hpt34x_proc) { + hpt34x_proc = 1; + hpt34x_display_info = &hpt34x_get_info; + } +#endif /* DISPLAY_HPT34X_TIMINGS && CONFIG_PROC_FS */ + + return dev->irq; +} + +void __init ide_init_hpt34x (ide_hwif_t *hwif) +{ + unsigned short pcicmd = 0; + hwif->tuneproc = &hpt34x_tune_drive; + hwif->speedproc = &hpt34x_tune_chipset; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + hwif->autodma = 0; + + pci_read_config_word(hwif->pci_dev, PCI_COMMAND, &pcicmd); + + if (!hwif->dma_base) + return; + +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->dmaproc = &hpt34x_dmaproc; +#ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = (pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0; +#endif /* CONFIG_IDEDMA_AUTO */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ +} + +extern void ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d); + +void __init fixup_device_hpt343 (struct pci_dev *dev, ide_pci_device_t *d) +{ + char *chipset_names[] = {"HPT343", "HPT345"}; + unsigned short pcicmd = 0; + + pci_read_config_word(dev, PCI_COMMAND, &pcicmd); + + strcpy(d->name, chipset_names[(pcicmd & PCI_COMMAND_MEMORY) ? 1 : 0]); + d->bootable = (pcicmd & PCI_COMMAND_MEMORY) ? OFF_BOARD : NEVER_BOARD; + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); +} diff -Nru a/drivers/ide/hpt366.c b/drivers/ide/hpt366.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/hpt366.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,1695 @@ +/* + * linux/drivers/ide/hpt366.c Version 0.33 April 17, 2002 + * + * Copyright (C) 1999-2002 Andre Hedrick + * Portions Copyright (C) 2001 Sun Microsystems, Inc. + * + * Thanks to HighPoint Technologies for their assistance, and hardware. + * Special Thanks to Jon Burchmore in SanDiego for the deep pockets, his + * donation of an ABit BP6 mainboard, processor, and memory acellerated + * development and support. + * + * Note that final HPT370 support was done by force extraction of GPL. + * + * - add function for getting/setting power status of drive + * - the HPT370's state machine can get confused. reset it before each dma + * xfer to prevent that from happening. + * - reset state engine whenever we get an error. + * - check for busmaster state at end of dma. + * - use new highpoint timings. + * - detect bus speed using highpoint register. + * - use pll if we don't have a clock table. added a 66MHz table that's + * just 2x the 33MHz table. + * - removed turnaround. NOTE: we never want to switch between pll and + * pci clocks as the chip can glitch in those cases. the highpoint + * approved workaround slows everything down too much to be useful. in + * addition, we would have to serialize access to each chip. + * Adrian Sun + * + * add drive timings for 66MHz PCI bus, + * fix ATA Cable signal detection, fix incorrect /proc info + * add /proc display for per-drive PIO/DMA/UDMA mode and + * per-channel ATA-33/66 Cable detect. + * Duncan Laurie + * + * fixup /proc output for multiple controllers + * Tim Hockin + * + * On hpt366: + * Reset the hpt366 on error, reset on dma + * Fix disabling Fast Interrupt hpt366. + * Mike Waychison + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include "ide_modes.h" + +#define DISPLAY_HPT366_TIMINGS + +/* various tuning parameters */ +#define HPT_RESET_STATE_ENGINE +#undef HPT_DELAY_INTERRUPT +#undef HPT_SERIALIZE_IO + +#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include +#endif /* defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) */ + +const char *quirk_drives[] = { + "QUANTUM FIREBALLlct08 08", + "QUANTUM FIREBALLP KA6.4", + "QUANTUM FIREBALLP LM20.4", + "QUANTUM FIREBALLP LM20.5", + NULL +}; + +const char *bad_ata100_5[] = { + "IBM-DTLA-307075", + "IBM-DTLA-307060", + "IBM-DTLA-307045", + "IBM-DTLA-307030", + "IBM-DTLA-307020", + "IBM-DTLA-307015", + "IBM-DTLA-305040", + "IBM-DTLA-305030", + "IBM-DTLA-305020", + "IC35L010AVER07-0", + "IC35L020AVER07-0", + "IC35L030AVER07-0", + "IC35L040AVER07-0", + "IC35L060AVER07-0", + "WDC AC310200R", + NULL +}; + +const char *bad_ata66_4[] = { + "IBM-DTLA-307075", + "IBM-DTLA-307060", + "IBM-DTLA-307045", + "IBM-DTLA-307030", + "IBM-DTLA-307020", + "IBM-DTLA-307015", + "IBM-DTLA-305040", + "IBM-DTLA-305030", + "IBM-DTLA-305020", + "IC35L010AVER07-0", + "IC35L020AVER07-0", + "IC35L030AVER07-0", + "IC35L040AVER07-0", + "IC35L060AVER07-0", + "WDC AC310200R", + NULL +}; + +const char *bad_ata66_3[] = { + "WDC AC310200R", + NULL +}; + +const char *bad_ata33[] = { + "Maxtor 92720U8", "Maxtor 92040U6", "Maxtor 91360U4", "Maxtor 91020U3", "Maxtor 90845U3", "Maxtor 90650U2", + "Maxtor 91360D8", "Maxtor 91190D7", "Maxtor 91020D6", "Maxtor 90845D5", "Maxtor 90680D4", "Maxtor 90510D3", "Maxtor 90340D2", + "Maxtor 91152D8", "Maxtor 91008D7", "Maxtor 90845D6", "Maxtor 90840D6", "Maxtor 90720D5", "Maxtor 90648D5", "Maxtor 90576D4", + "Maxtor 90510D4", + "Maxtor 90432D3", "Maxtor 90288D2", "Maxtor 90256D2", + "Maxtor 91000D8", "Maxtor 90910D8", "Maxtor 90875D7", "Maxtor 90840D7", "Maxtor 90750D6", "Maxtor 90625D5", "Maxtor 90500D4", + "Maxtor 91728D8", "Maxtor 91512D7", "Maxtor 91303D6", "Maxtor 91080D5", "Maxtor 90845D4", "Maxtor 90680D4", "Maxtor 90648D3", "Maxtor 90432D2", + NULL +}; + +struct chipset_bus_clock_list_entry { + byte xfer_speed; + unsigned int chipset_settings; +}; + +/* key for bus clock timings + * bit + * 0:3 data_high_time. inactive time of DIOW_/DIOR_ for PIO and MW + * DMA. cycles = value + 1 + * 4:8 data_low_time. active time of DIOW_/DIOR_ for PIO and MW + * DMA. cycles = value + 1 + * 9:12 cmd_high_time. inactive time of DIOW_/DIOR_ during task file + * register access. + * 13:17 cmd_low_time. active time of DIOW_/DIOR_ during task file + * register access. + * 18:21 udma_cycle_time. clock freq and clock cycles for UDMA xfer. + * during task file register access. + * 22:24 pre_high_time. time to initialize 1st cycle for PIO and MW DMA + * xfer. + * 25:27 cmd_pre_high_time. time to initialize 1st PIO cycle for task + * register access. + * 28 UDMA enable + * 29 DMA enable + * 30 PIO_MST enable. if set, the chip is in bus master mode during + * PIO. + * 31 FIFO enable. + */ +struct chipset_bus_clock_list_entry forty_base_hpt366[] = { + { XFER_UDMA_4, 0x900fd943 }, + { XFER_UDMA_3, 0x900ad943 }, + { XFER_UDMA_2, 0x900bd943 }, + { XFER_UDMA_1, 0x9008d943 }, + { XFER_UDMA_0, 0x9008d943 }, + + { XFER_MW_DMA_2, 0xa008d943 }, + { XFER_MW_DMA_1, 0xa010d955 }, + { XFER_MW_DMA_0, 0xa010d9fc }, + + { XFER_PIO_4, 0xc008d963 }, + { XFER_PIO_3, 0xc010d974 }, + { XFER_PIO_2, 0xc010d997 }, + { XFER_PIO_1, 0xc010d9c7 }, + { XFER_PIO_0, 0xc018d9d9 }, + { 0, 0x0120d9d9 } +}; + +struct chipset_bus_clock_list_entry thirty_three_base_hpt366[] = { + { XFER_UDMA_4, 0x90c9a731 }, + { XFER_UDMA_3, 0x90cfa731 }, + { XFER_UDMA_2, 0x90caa731 }, + { XFER_UDMA_1, 0x90cba731 }, + { XFER_UDMA_0, 0x90c8a731 }, + + { XFER_MW_DMA_2, 0xa0c8a731 }, + { XFER_MW_DMA_1, 0xa0c8a732 }, /* 0xa0c8a733 */ + { XFER_MW_DMA_0, 0xa0c8a797 }, + + { XFER_PIO_4, 0xc0c8a731 }, + { XFER_PIO_3, 0xc0c8a742 }, + { XFER_PIO_2, 0xc0d0a753 }, + { XFER_PIO_1, 0xc0d0a7a3 }, /* 0xc0d0a793 */ + { XFER_PIO_0, 0xc0d0a7aa }, /* 0xc0d0a7a7 */ + { 0, 0x0120a7a7 } +}; + +struct chipset_bus_clock_list_entry twenty_five_base_hpt366[] = { + + { XFER_UDMA_4, 0x90c98521 }, + { XFER_UDMA_3, 0x90cf8521 }, + { XFER_UDMA_2, 0x90cf8521 }, + { XFER_UDMA_1, 0x90cb8521 }, + { XFER_UDMA_0, 0x90cb8521 }, + + { XFER_MW_DMA_2, 0xa0ca8521 }, + { XFER_MW_DMA_1, 0xa0ca8532 }, + { XFER_MW_DMA_0, 0xa0ca8575 }, + + { XFER_PIO_4, 0xc0ca8521 }, + { XFER_PIO_3, 0xc0ca8532 }, + { XFER_PIO_2, 0xc0ca8542 }, + { XFER_PIO_1, 0xc0d08572 }, + { XFER_PIO_0, 0xc0d08585 }, + { 0, 0x01208585 } +}; + +/* from highpoint documentation. these are old values */ +struct chipset_bus_clock_list_entry thirty_three_base_hpt370[] = { +/* { XFER_UDMA_5, 0x1A85F442, 0x16454e31 }, */ + { XFER_UDMA_5, 0x16454e31 }, + { XFER_UDMA_4, 0x16454e31 }, + { XFER_UDMA_3, 0x166d4e31 }, + { XFER_UDMA_2, 0x16494e31 }, + { XFER_UDMA_1, 0x164d4e31 }, + { XFER_UDMA_0, 0x16514e31 }, + + { XFER_MW_DMA_2, 0x26514e21 }, + { XFER_MW_DMA_1, 0x26514e33 }, + { XFER_MW_DMA_0, 0x26514e97 }, + + { XFER_PIO_4, 0x06514e21 }, + { XFER_PIO_3, 0x06514e22 }, + { XFER_PIO_2, 0x06514e33 }, + { XFER_PIO_1, 0x06914e43 }, + { XFER_PIO_0, 0x06914e57 }, + { 0, 0x06514e57 } +}; + +struct chipset_bus_clock_list_entry sixty_six_base_hpt370[] = { + { XFER_UDMA_5, 0x14846231 }, + { XFER_UDMA_4, 0x14886231 }, + { XFER_UDMA_3, 0x148c6231 }, + { XFER_UDMA_2, 0x148c6231 }, + { XFER_UDMA_1, 0x14906231 }, + { XFER_UDMA_0, 0x14986231 }, + + { XFER_MW_DMA_2, 0x26514e21 }, + { XFER_MW_DMA_1, 0x26514e33 }, + { XFER_MW_DMA_0, 0x26514e97 }, + + { XFER_PIO_4, 0x06514e21 }, + { XFER_PIO_3, 0x06514e22 }, + { XFER_PIO_2, 0x06514e33 }, + { XFER_PIO_1, 0x06914e43 }, + { XFER_PIO_0, 0x06914e57 }, + { 0, 0x06514e57 } +}; + +/* these are the current (4 sep 2001) timings from highpoint */ +struct chipset_bus_clock_list_entry thirty_three_base_hpt370a[] = { + { XFER_UDMA_5, 0x12446231 }, + { XFER_UDMA_4, 0x12446231 }, + { XFER_UDMA_3, 0x126c6231 }, + { XFER_UDMA_2, 0x12486231 }, + { XFER_UDMA_1, 0x124c6233 }, + { XFER_UDMA_0, 0x12506297 }, + + { XFER_MW_DMA_2, 0x22406c31 }, + { XFER_MW_DMA_1, 0x22406c33 }, + { XFER_MW_DMA_0, 0x22406c97 }, + + { XFER_PIO_4, 0x06414e31 }, + { XFER_PIO_3, 0x06414e42 }, + { XFER_PIO_2, 0x06414e53 }, + { XFER_PIO_1, 0x06814e93 }, + { XFER_PIO_0, 0x06814ea7 }, + { 0, 0x06814ea7 } +}; + +/* 2x 33MHz timings */ +struct chipset_bus_clock_list_entry sixty_six_base_hpt370a[] = { + { XFER_UDMA_5, 0x1488e673 }, + { XFER_UDMA_4, 0x1488e673 }, + { XFER_UDMA_3, 0x1498e673 }, + { XFER_UDMA_2, 0x1490e673 }, + { XFER_UDMA_1, 0x1498e677 }, + { XFER_UDMA_0, 0x14a0e73f }, + + { XFER_MW_DMA_2, 0x2480fa73 }, + { XFER_MW_DMA_1, 0x2480fa77 }, + { XFER_MW_DMA_0, 0x2480fb3f }, + + { XFER_PIO_4, 0x0c82be73 }, + { XFER_PIO_3, 0x0c82be95 }, + { XFER_PIO_2, 0x0c82beb7 }, + { XFER_PIO_1, 0x0d02bf37 }, + { XFER_PIO_0, 0x0d02bf5f }, + { 0, 0x0d02bf5f } +}; + +struct chipset_bus_clock_list_entry fifty_base_hpt370a[] = { + { XFER_UDMA_5, 0x12848242 }, + { XFER_UDMA_4, 0x12ac8242 }, + { XFER_UDMA_3, 0x128c8242 }, + { XFER_UDMA_2, 0x120c8242 }, + { XFER_UDMA_1, 0x12148254 }, + { XFER_UDMA_0, 0x121882ea }, + + { XFER_MW_DMA_2, 0x22808242 }, + { XFER_MW_DMA_1, 0x22808254 }, + { XFER_MW_DMA_0, 0x228082ea }, + + { XFER_PIO_4, 0x0a81f442 }, + { XFER_PIO_3, 0x0a81f443 }, + { XFER_PIO_2, 0x0a81f454 }, + { XFER_PIO_1, 0x0ac1f465 }, + { XFER_PIO_0, 0x0ac1f48a }, + { 0, 0x0ac1f48a } +}; + +struct chipset_bus_clock_list_entry thirty_three_base_hpt372[] = { + { XFER_UDMA_6, 0x1c81dc62 }, + { XFER_UDMA_5, 0x1c6ddc62 }, + { XFER_UDMA_4, 0x1c8ddc62 }, + { XFER_UDMA_3, 0x1c8edc62 }, /* checkme */ + { XFER_UDMA_2, 0x1c91dc62 }, + { XFER_UDMA_1, 0x1c9adc62 }, /* checkme */ + { XFER_UDMA_0, 0x1c82dc62 }, /* checkme */ + + { XFER_MW_DMA_2, 0x2c829262 }, + { XFER_MW_DMA_1, 0x2c829266 }, /* checkme */ + { XFER_MW_DMA_0, 0x2c82922e }, /* checkme */ + + { XFER_PIO_4, 0x0c829c62 }, + { XFER_PIO_3, 0x0c829c84 }, + { XFER_PIO_2, 0x0c829ca6 }, + { XFER_PIO_1, 0x0d029d26 }, + { XFER_PIO_0, 0x0d029d5e }, + { 0, 0x0d029d5e } +}; + +struct chipset_bus_clock_list_entry fifty_base_hpt372[] = { + { XFER_UDMA_5, 0x12848242 }, + { XFER_UDMA_4, 0x12ac8242 }, + { XFER_UDMA_3, 0x128c8242 }, + { XFER_UDMA_2, 0x120c8242 }, + { XFER_UDMA_1, 0x12148254 }, + { XFER_UDMA_0, 0x121882ea }, + + { XFER_MW_DMA_2, 0x22808242 }, + { XFER_MW_DMA_1, 0x22808254 }, + { XFER_MW_DMA_0, 0x228082ea }, + + { XFER_PIO_4, 0x0a81f442 }, + { XFER_PIO_3, 0x0a81f443 }, + { XFER_PIO_2, 0x0a81f454 }, + { XFER_PIO_1, 0x0ac1f465 }, + { XFER_PIO_0, 0x0ac1f48a }, + { 0, 0x0a81f443 } +}; + +struct chipset_bus_clock_list_entry sixty_six_base_hpt372[] = { + { XFER_UDMA_6, 0x1c869c62 }, + { XFER_UDMA_5, 0x1cae9c62 }, + { XFER_UDMA_4, 0x1c8a9c62 }, + { XFER_UDMA_3, 0x1c8e9c62 }, + { XFER_UDMA_2, 0x1c929c62 }, + { XFER_UDMA_1, 0x1c9a9c62 }, + { XFER_UDMA_0, 0x1c829c62 }, + + { XFER_MW_DMA_2, 0x2c829c62 }, + { XFER_MW_DMA_1, 0x2c829c66 }, + { XFER_MW_DMA_0, 0x2c829d2e }, + + { XFER_PIO_4, 0x0c829c62 }, + { XFER_PIO_3, 0x0c829c84 }, + { XFER_PIO_2, 0x0c829ca6 }, + { XFER_PIO_1, 0x0d029d26 }, + { XFER_PIO_0, 0x0d029d5e }, + { 0, 0x0d029d26 } +}; + +struct chipset_bus_clock_list_entry thirty_three_base_hpt374[] = { + { XFER_UDMA_6, 0x12808242 }, + { XFER_UDMA_5, 0x12848242 }, + { XFER_UDMA_4, 0x12ac8242 }, + { XFER_UDMA_3, 0x128c8242 }, + { XFER_UDMA_2, 0x120c8242 }, + { XFER_UDMA_1, 0x12148254 }, + { XFER_UDMA_0, 0x121882ea }, + + { XFER_MW_DMA_2, 0x22808242 }, + { XFER_MW_DMA_1, 0x22808254 }, + { XFER_MW_DMA_0, 0x228082ea }, + + { XFER_PIO_4, 0x0a81f442 }, + { XFER_PIO_3, 0x0a81f443 }, + { XFER_PIO_2, 0x0a81f454 }, + { XFER_PIO_1, 0x0ac1f465 }, + { XFER_PIO_0, 0x0ac1f48a }, + { 0, 0x06814e93 } +}; + +#if 0 +struct chipset_bus_clock_list_entry fifty_base_hpt374[] = { + { XFER_UDMA_6, }, + { XFER_UDMA_5, }, + { XFER_UDMA_4, }, + { XFER_UDMA_3, }, + { XFER_UDMA_2, }, + { XFER_UDMA_1, }, + { XFER_UDMA_0, }, + { XFER_MW_DMA_2, }, + { XFER_MW_DMA_1, }, + { XFER_MW_DMA_0, }, + { XFER_PIO_4, }, + { XFER_PIO_3, }, + { XFER_PIO_2, }, + { XFER_PIO_1, }, + { XFER_PIO_0, }, + { 0, } +}; +#endif +#if 0 +struct chipset_bus_clock_list_entry sixty_six_base_hpt374[] = { + { XFER_UDMA_6, 0x12406231 }, /* checkme */ + { XFER_UDMA_5, 0x12446231 }, + 0x14846231 + { XFER_UDMA_4, 0x16814ea7 }, + 0x14886231 + { XFER_UDMA_3, 0x16814ea7 }, + 0x148c6231 + { XFER_UDMA_2, 0x16814ea7 }, + 0x148c6231 + { XFER_UDMA_1, 0x16814ea7 }, + 0x14906231 + { XFER_UDMA_0, 0x16814ea7 }, + 0x14986231 + { XFER_MW_DMA_2, 0x16814ea7 }, + 0x26514e21 + { XFER_MW_DMA_1, 0x16814ea7 }, + 0x26514e97 + { XFER_MW_DMA_0, 0x16814ea7 }, + 0x26514e97 + { XFER_PIO_4, 0x06814ea7 }, + 0x06514e21 + { XFER_PIO_3, 0x06814ea7 }, + 0x06514e22 + { XFER_PIO_2, 0x06814ea7 }, + 0x06514e33 + { XFER_PIO_1, 0x06814ea7 }, + 0x06914e43 + { XFER_PIO_0, 0x06814ea7 }, + 0x06914e57 + { 0, 0x06814ea7 } +}; +#endif + +#define HPT366_DEBUG_DRIVE_INFO 0 +#define HPT374_ALLOW_ATA133_6 0 +#define HPT371_ALLOW_ATA133_6 0 +#define HPT302_ALLOW_ATA133_6 0 +#define HPT372_ALLOW_ATA133_6 1 +#define HPT370_ALLOW_ATA100_5 1 +#define HPT366_ALLOW_ATA66_4 1 +#define HPT366_ALLOW_ATA66_3 1 +#define HPT366_MAX_DEVS 8 + +#define F_LOW_PCI_33 0x23 +#define F_LOW_PCI_40 0x29 +#define F_LOW_PCI_50 0x2d +#define F_LOW_PCI_66 0x42 + +static struct pci_dev *hpt_devs[HPT366_MAX_DEVS]; +static int n_hpt_devs; + +static unsigned int hpt_revision(struct pci_dev *dev); +static unsigned int hpt_minimum_revision(struct pci_dev *dev, int revision); + +byte hpt366_proc = 0; +byte hpt363_shared_irq; +byte hpt363_shared_pin; + +#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) +static int hpt366_get_info(char *, char **, off_t, int); +extern int (*hpt366_display_info)(char *, char **, off_t, int); /* ide-proc.c */ + +static int hpt366_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + char *chipset_nums[] = {"366", "366", "368", + "370", "370A", "372", + "302", "371", "374" }; + int i; + + p += sprintf(p, "\n " + "HighPoint HPT366/368/370/372/374\n"); + for (i = 0; i < n_hpt_devs; i++) { + struct pci_dev *dev = hpt_devs[i]; + unsigned long iobase = dev->resource[4].start; + u32 class_rev = hpt_revision(dev); + u8 c0, c1; + + p += sprintf(p, "\nController: %d\n", i); + p += sprintf(p, "Chipset: HPT%s\n", chipset_nums[class_rev]); + p += sprintf(p, "--------------- Primary Channel " + "--------------- Secondary Channel " + "--------------\n"); + + /* get the bus master status registers */ + c0 = inb_p(iobase + 0x2); + c1 = inb_p(iobase + 0xa); + p += sprintf(p, "Enabled: %s" + " %s\n", + (c0 & 0x80) ? "no" : "yes", + (c1 & 0x80) ? "no" : "yes"); + + if (hpt_minimum_revision(dev, 3)) { + u8 cbl; + cbl = inb_p(iobase + 0x7b); + outb_p(cbl | 1, iobase + 0x7b); + outb_p(cbl & ~1, iobase + 0x7b); + cbl = inb_p(iobase + 0x7a); + p += sprintf(p, "Cable: ATA-%d" + " ATA-%d\n", + (cbl & 0x02) ? 33 : 66, + (cbl & 0x01) ? 33 : 66); + p += sprintf(p, "\n"); + } + + p += sprintf(p, "--------------- drive0 --------- drive1 " + "------- drive0 ---------- drive1 -------\n"); + p += sprintf(p, "DMA capable: %s %s" + " %s %s\n", + (c0 & 0x20) ? "yes" : "no ", + (c0 & 0x40) ? "yes" : "no ", + (c1 & 0x20) ? "yes" : "no ", + (c1 & 0x40) ? "yes" : "no "); + + { + u8 c2, c3; + /* older revs don't have these registers mapped + * into io space */ + pci_read_config_byte(dev, 0x43, &c0); + pci_read_config_byte(dev, 0x47, &c1); + pci_read_config_byte(dev, 0x4b, &c2); + pci_read_config_byte(dev, 0x4f, &c3); + + p += sprintf(p, "Mode: %s %s" + " %s %s\n", + (c0 & 0x10) ? "UDMA" : (c0 & 0x20) ? "DMA " : + (c0 & 0x80) ? "PIO " : "off ", + (c1 & 0x10) ? "UDMA" : (c1 & 0x20) ? "DMA " : + (c1 & 0x80) ? "PIO " : "off ", + (c2 & 0x10) ? "UDMA" : (c2 & 0x20) ? "DMA " : + (c2 & 0x80) ? "PIO " : "off ", + (c3 & 0x10) ? "UDMA" : (c3 & 0x20) ? "DMA " : + (c3 & 0x80) ? "PIO " : "off "); + } + } + p += sprintf(p, "\n"); + + return p-buffer;/* => must be less than 4k! */ +} +#endif /* defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) */ + +static unsigned int hpt_revision (struct pci_dev *dev) +{ + unsigned int class_rev; + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + + switch(dev->device) { + case PCI_DEVICE_ID_TTI_HPT374: + class_rev = PCI_DEVICE_ID_TTI_HPT374; break; + case PCI_DEVICE_ID_TTI_HPT371: + class_rev = PCI_DEVICE_ID_TTI_HPT371; break; + case PCI_DEVICE_ID_TTI_HPT302: + class_rev = PCI_DEVICE_ID_TTI_HPT302; break; + case PCI_DEVICE_ID_TTI_HPT372: + class_rev = PCI_DEVICE_ID_TTI_HPT372; break; + default: + break; + } + return class_rev; +} + +static unsigned int hpt_minimum_revision (struct pci_dev *dev, int revision) +{ + unsigned int class_rev = hpt_revision(dev); + revision--; + return ((int) (class_rev > revision) ? 1 : 0); +} + +static int check_in_drive_lists(ide_drive_t *drive, const char **list); + +static byte hpt3xx_ratemask (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + byte mode = 0x00; + + if (hpt_minimum_revision(dev, 8)) { /* HPT374 */ + mode |= (HPT374_ALLOW_ATA133_6) ? 0x04 : 0x03; + } else if (hpt_minimum_revision(dev, 7)) { /* HPT371 */ + mode |= (HPT371_ALLOW_ATA133_6) ? 0x04 : 0x03; + } else if (hpt_minimum_revision(dev, 6)) { /* HPT302 */ + mode |= (HPT302_ALLOW_ATA133_6) ? 0x04 : 0x03; + } else if (hpt_minimum_revision(dev, 5)) { /* HPT372 */ + mode |= (HPT372_ALLOW_ATA133_6) ? 0x04 : 0x03; + } else if (hpt_minimum_revision(dev, 4)) { /* HPT370A */ + mode |= (HPT370_ALLOW_ATA100_5) ? 0x03 : 0x02; + } else if (hpt_minimum_revision(dev, 3)) { /* HPT370 */ + mode |= (HPT370_ALLOW_ATA100_5) ? 0x03 : 0x02; + if (check_in_drive_lists(drive, bad_ata33)) + return (mode &= ~0xFF); + } else { /* HPT366 and HPT368 */ + mode |= 0x02; + if (check_in_drive_lists(drive, bad_ata33)) + return (mode &= ~0xFF); + } + + if (!eighty_ninty_three(drive)) { + mode &= ~0xFE; + mode |= 0x01; + } + return (mode &= ~0xF8); +} + +static byte hpt3xx_ratefilter (ide_drive_t *drive, byte speed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + struct pci_dev *dev = HWIF(drive)->pci_dev; + byte mode = hpt3xx_ratemask(drive); + + if (drive->media != ide_disk) + while (speed > XFER_PIO_4) speed--; + + switch(mode) { + case 0x04: + while (speed > XFER_UDMA_6) speed--; + break; + case 0x03: + while (speed > XFER_UDMA_5) speed--; + if (hpt_minimum_revision(dev, 5)) + break; + if (check_in_drive_lists(drive, bad_ata100_5)) + while (speed > XFER_UDMA_4) speed--; + break; + case 0x02: + while (speed > XFER_UDMA_4) speed--; + /* + * CHECK ME, Does this need to be set to 5 ?? + */ + if (hpt_minimum_revision(dev, 3)) + break; + if ((check_in_drive_lists(drive, bad_ata66_4)) || + (!(HPT366_ALLOW_ATA66_4))) + while (speed > XFER_UDMA_3) speed--; + if ((check_in_drive_lists(drive, bad_ata66_3)) || + (!(HPT366_ALLOW_ATA66_3))) + while (speed > XFER_UDMA_2) speed--; + break; + case 0x01: + while (speed > XFER_UDMA_2) speed--; + /* + * CHECK ME, Does this need to be set to 5 ?? + */ + if (hpt_minimum_revision(dev, 3)) + break; + if (check_in_drive_lists(drive, bad_ata33)) + while (speed > XFER_MW_DMA_2) speed--; + break; + case 0x00: + default: while (speed > XFER_MW_DMA_2) speed--; break; + break; + } +#else + while (speed > XFER_PIO_4) speed--; +#endif /* CONFIG_BLK_DEV_IDEDMA */ +// printk("%s: mode == %02x speed == %02x\n", drive->name, mode, speed); + return speed; +} + +static int check_in_drive_lists (ide_drive_t *drive, const char **list) +{ + struct hd_driveid *id = drive->id; + + if (quirk_drives == list) { + while (*list) { + if (strstr(id->model, *list++)) { + return 1; + } + } + } else { + while (*list) { + if (!strcmp(*list++,id->model)) { + return 1; + } + } + } + return 0; +} + +static unsigned int pci_bus_clock_list (byte speed, struct chipset_bus_clock_list_entry * chipset_table) +{ + for ( ; chipset_table->xfer_speed ; chipset_table++) + if (chipset_table->xfer_speed == speed) { + return chipset_table->chipset_settings; + } + return chipset_table->chipset_settings; +} + +static void hpt366_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + byte speed = hpt3xx_ratefilter(drive, xferspeed); + byte regtime = (drive->select.b.unit & 0x01) ? 0x44 : 0x40; + byte regfast = (HWIF(drive)->channel) ? 0x55 : 0x51; + byte drive_fast = 0; + unsigned int reg1 = 0; + unsigned int reg2 = 0; + + /* + * Disable the "fast interrupt" prediction. + */ + pci_read_config_byte(dev, regfast, &drive_fast); +#if 0 + if (drive_fast & 0x02) + pci_write_config_byte(dev, regfast, drive_fast & ~0x20); +#else + if (drive_fast & 0x80) + pci_write_config_byte(dev, regfast, drive_fast & ~0x80); +#endif + + reg2 = pci_bus_clock_list(speed, + (struct chipset_bus_clock_list_entry *) dev->driver_data); + /* + * Disable on-chip PIO FIFO/buffer + * (to avoid problems handling I/O errors later) + */ + pci_read_config_dword(dev, regtime, ®1); + if (speed >= XFER_MW_DMA_0) { + reg2 = (reg2 & ~0xc0000000) | (reg1 & 0xc0000000); + } else { + reg2 = (reg2 & ~0x30070000) | (reg1 & 0x30070000); + } + reg2 &= ~0x80000000; + + pci_write_config_dword(dev, regtime, reg2); +} + +static void hpt368_tune_chipset (ide_drive_t *drive, byte speed) +{ + hpt366_tune_chipset(drive, speed); +} + +static void hpt370_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + byte speed = hpt3xx_ratefilter(drive, xferspeed); + byte regfast = (HWIF(drive)->channel) ? 0x55 : 0x51; + unsigned int list_conf = 0; + unsigned int drive_conf = 0; + unsigned int conf_mask = (speed >= XFER_MW_DMA_0) ? 0xc0000000 : 0x30070000; + byte drive_pci = 0x40 + (drive->dn * 4); + byte new_fast, drive_fast = 0; + struct pci_dev *dev = HWIF(drive)->pci_dev; + + /* + * Disable the "fast interrupt" prediction. + * don't holdoff on interrupts. (== 0x01 despite what the docs say) + */ + pci_read_config_byte(dev, regfast, &drive_fast); + new_fast = drive_fast; + if (new_fast & 0x02) + new_fast &= ~0x02; + +#ifdef HPT_DELAY_INTERRUPT + if (new_fast & 0x01) + new_fast &= ~0x01; +#else + if ((new_fast & 0x01) == 0) + new_fast |= 0x01; +#endif + if (new_fast != drive_fast) + pci_write_config_byte(dev, regfast, new_fast); + + list_conf = pci_bus_clock_list(speed, + (struct chipset_bus_clock_list_entry *) + dev->driver_data); + + pci_read_config_dword(dev, drive_pci, &drive_conf); + list_conf = (list_conf & ~conf_mask) | (drive_conf & conf_mask); + + if (speed < XFER_MW_DMA_0) { + list_conf &= ~0x80000000; /* Disable on-chip PIO FIFO/buffer */ + } + + pci_write_config_dword(dev, drive_pci, list_conf); +} + +static void hpt372_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + byte speed = hpt3xx_ratefilter(drive, xferspeed); + byte regfast = (HWIF(drive)->channel) ? 0x55 : 0x51; + unsigned int list_conf = 0; + unsigned int drive_conf = 0; + unsigned int conf_mask = (speed >= XFER_MW_DMA_0) ? 0xc0000000 : 0x30070000; + byte drive_pci = 0x40 + (drive->dn * 4); + byte drive_fast = 0; + struct pci_dev *dev = HWIF(drive)->pci_dev; + + /* + * Disable the "fast interrupt" prediction. + * don't holdoff on interrupts. (== 0x01 despite what the docs say) + */ + pci_read_config_byte(dev, regfast, &drive_fast); + drive_fast &= ~0x07; + pci_write_config_byte(dev, regfast, drive_fast); + + list_conf = pci_bus_clock_list(speed, + (struct chipset_bus_clock_list_entry *) + dev->driver_data); + pci_read_config_dword(dev, drive_pci, &drive_conf); + list_conf = (list_conf & ~conf_mask) | (drive_conf & conf_mask); + if (speed < XFER_MW_DMA_0) + list_conf &= ~0x80000000; /* Disable on-chip PIO FIFO/buffer */ + pci_write_config_dword(dev, drive_pci, list_conf); +} + +static void hpt374_tune_chipset (ide_drive_t *drive, byte speed) +{ + hpt372_tune_chipset(drive, speed); +} + +static int hpt3xx_tune_chipset (ide_drive_t *drive, byte speed) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + + if (hpt_minimum_revision(dev, 8)) + hpt374_tune_chipset(drive, speed); +#if 0 + else if (hpt_minimum_revision(dev, 7)) + hpt371_tune_chipset(drive, speed); + else if (hpt_minimum_revision(dev, 6)) + hpt302_tune_chipset(drive, speed); +#endif + else if (hpt_minimum_revision(dev, 5)) + hpt372_tune_chipset(drive, speed); + else if (hpt_minimum_revision(dev, 3)) + hpt370_tune_chipset(drive, speed); + else if (hpt_minimum_revision(dev, 2)) + hpt368_tune_chipset(drive, speed); + else + hpt366_tune_chipset(drive, speed); + + return ((int) ide_config_drive_speed(drive, speed)); +} + +static void hpt3xx_tune_drive (ide_drive_t *drive, byte pio) +{ + byte speed; + + pio = ide_get_best_pio_mode(drive, pio, 5, NULL); + switch(pio) { + case 4: speed = XFER_PIO_4; break; + case 3: speed = XFER_PIO_3; break; + case 2: speed = XFER_PIO_2; break; + case 1: speed = XFER_PIO_1; break; + default: speed = XFER_PIO_0; break; + } + (void) hpt3xx_tune_chipset(drive, speed); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +/* + * This allows the configuration of ide_pci chipset registers + * for cards that learn about the drive's UDMA, DMA, PIO capabilities + * after the drive is reported by the OS. Initally for designed for + * HPT366 UDMA chipset by HighPoint|Triones Technologies, Inc. + * + * check_in_drive_lists(drive, bad_ata66_4) + * check_in_drive_lists(drive, bad_ata66_3) + * check_in_drive_lists(drive, bad_ata33) + * + */ +static int config_chipset_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + byte mode = hpt3xx_ratemask(drive); + byte speed = 0x00; + + if (drive->media != ide_disk) + mode |= 0x08; + + switch(mode) { + case 0x04: + if (id->dma_ultra & 0x0040) + { speed = XFER_UDMA_6; break; } + case 0x03: + if (id->dma_ultra & 0x0020) + { speed = XFER_UDMA_5; break; } + case 0x02: + if (id->dma_ultra & 0x0010) + { speed = XFER_UDMA_4; break; } + if (id->dma_ultra & 0x0008) + { speed = XFER_UDMA_3; break; } + case 0x01: + if (id->dma_ultra & 0x0004) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0002) + { speed = XFER_UDMA_1; break; } + if (id->dma_ultra & 0x0001) + { speed = XFER_UDMA_0; break; } + case 0x00: + if (id->dma_mword & 0x0004) + { speed = XFER_MW_DMA_2; break; } + if (id->dma_mword & 0x0002) + { speed = XFER_MW_DMA_1; break; } + if (id->dma_mword & 0x0001) + { speed = XFER_MW_DMA_0; break; } + default: + return ((int) ide_dma_off_quietly); + } + + (void) hpt3xx_tune_chipset(drive, speed); + + return ((int) ((id->dma_ultra >> 14) & 3) ? ide_dma_on : + ((id->dma_ultra >> 11) & 7) ? ide_dma_on : + ((id->dma_ultra >> 8) & 7) ? ide_dma_on : + ((id->dma_mword >> 8) & 7) ? ide_dma_on : + ide_dma_off_quietly); +} + +int hpt3xx_quirkproc (ide_drive_t *drive) +{ + return ((int) check_in_drive_lists(drive, quirk_drives)); +} + +void hpt3xx_intrproc (ide_drive_t *drive) +{ + if (drive->quirk_list) + return; + /* drives in the quirk_list may not like intr setups/cleanups */ + OUT_BYTE((drive)->ctl|2, HWIF(drive)->io_ports[IDE_CONTROL_OFFSET]); +} + +void hpt3xx_maskproc (ide_drive_t *drive, int mask) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + + if (drive->quirk_list) { + if (hpt_minimum_revision(dev,3)) { + byte reg5a = 0; + pci_read_config_byte(dev, 0x5a, ®5a); + if (((reg5a & 0x10) >> 4) != mask) + pci_write_config_byte(dev, 0x5a, mask ? (reg5a | 0x10) : (reg5a & ~0x10)); + } else { + if (mask) { + disable_irq(HWIF(drive)->irq); + } else { + enable_irq(HWIF(drive)->irq); + } + } + } else { + if (IDE_CONTROL_REG) + OUT_BYTE(mask ? (drive->ctl | 2) : (drive->ctl & ~2), IDE_CONTROL_REG); + } +} + +static int config_drive_xfer_rate (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_dma_action_t dma_func = ide_dma_on; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && HWIF(drive)->autodma) { + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + goto fast_ata_pio; + } + dma_func = ide_dma_off_quietly; + if (id->field_valid & 4) { + if (id->dma_ultra & 0x007F) { + /* Force if Capable UltraDMA */ + dma_func = config_chipset_for_dma(drive); + if ((id->field_valid & 2) && + (dma_func != ide_dma_on)) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if (id->dma_mword & 0x0007) { + /* Force if Capable regular DMA modes */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } + } else if (ide_dmaproc(ide_dma_good_drive, drive)) { + if (id->eide_dma_time > 150) { + goto no_dma_set; + } + /* Consult the list of known "good" drives */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + dma_func = ide_dma_off_quietly; +no_dma_set: + + hpt3xx_tune_drive(drive, 5); + } + return HWIF(drive)->dmaproc(dma_func, drive); +} + +/* + * hpt366_dmaproc() initiates/aborts (U)DMA read/write operations on a drive. + * + * This is specific to the HPT366 UDMA bios chipset + * by HighPoint|Triones Technologies, Inc. + */ +int hpt366_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + unsigned long dma_base = HWIF(drive)->dma_base; + byte reg50h = 0, reg52h = 0, reg5ah = 0, dma_stat = 0; + + switch (func) { + case ide_dma_check: + return config_drive_xfer_rate(drive); + case ide_dma_test_irq: + /* returns 1 if dma irq issued, 0 otherwise */ + dma_stat = IN_BYTE(dma_base+2); + /* return 1 if INTR asserted */ + return (dma_stat & 4) == 4; + case ide_dma_lostirq: + pci_read_config_byte(dev, 0x50, ®50h); + pci_read_config_byte(dev, 0x52, ®52h); + pci_read_config_byte(dev, 0x5a, ®5ah); + printk("%s: (%s) reg50h=0x%02x, reg52h=0x%02x," + " reg5ah=0x%02x\n", + drive->name, + ide_dmafunc_verbose(func), + reg50h, reg52h, reg5ah); + if (reg5ah & 0x10) + pci_write_config_byte(dev, 0x5a, reg5ah & ~0x10); +#if 0 + /* how about we flush and reset, mmmkay? */ + pci_write_config_byte(dev, 0x51, 0x1F); + /* fall through to a reset */ + case ide_dma_begin: + case ide_dma_end: + /* reset the chips state over and over.. */ + pci_write_config_byte(dev, 0x51, 0x13); +#endif + break; + case ide_dma_timeout: + default: + break; + } + /* use standard DMA stuff */ + return ide_dmaproc(func, drive); +} + +int hpt370_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + ide_hwif_t *hwif = HWIF(drive); + unsigned long dma_base = hwif->dma_base; + byte regstate = hwif->channel ? 0x54 : 0x50; + byte reginfo = hwif->channel ? 0x56 : 0x52; + byte dma_stat; + + switch (func) { + case ide_dma_check: + return config_drive_xfer_rate(drive); + case ide_dma_test_irq: + /* returns 1 if dma irq issued, 0 otherwise */ + dma_stat = IN_BYTE(dma_base+2); + /* return 1 if INTR asserted */ + return (dma_stat & 4) == 4; + + case ide_dma_end: + dma_stat = IN_BYTE(dma_base + 2); + if (dma_stat & 0x01) { + /* wait a little */ + udelay(20); + dma_stat = IN_BYTE(dma_base + 2); + } + if ((dma_stat & 0x01) == 0) + break; + + func = ide_dma_timeout; + /* fallthrough */ + + case ide_dma_timeout: + case ide_dma_lostirq: + pci_read_config_byte(dev, reginfo, &dma_stat); + printk("%s: %d bytes in FIFO\n", drive->name, + dma_stat); + pci_write_config_byte(dev, regstate, 0x37); + udelay(10); + dma_stat = IN_BYTE(dma_base); + /* stop dma */ + OUT_BYTE(dma_stat & ~0x1, dma_base); + dma_stat = IN_BYTE(dma_base + 2); + /* clear errors */ + OUT_BYTE(dma_stat | 0x6, dma_base+2); + /* fallthrough */ + +#ifdef HPT_RESET_STATE_ENGINE + case ide_dma_begin: +#endif + pci_write_config_byte(dev, regstate, 0x37); + udelay(10); + break; + + default: + break; + } + /* use standard DMA stuff */ + return ide_dmaproc(func, drive); +} + +int hpt374_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + ide_hwif_t *hwif = HWIF(drive); + unsigned long dma_base = hwif->dma_base; + byte mscreg = hwif->channel ? 0x54 : 0x50; +// byte reginfo = hwif->channel ? 0x56 : 0x52; + byte dma_stat; + + switch (func) { + case ide_dma_check: + return config_drive_xfer_rate(drive); + case ide_dma_test_irq: + /* returns 1 if dma irq issued, 0 otherwise */ + dma_stat = IN_BYTE(dma_base+2); +#if 0 /* do not set unless you know what you are doing */ + if (dma_stat & 4) { + byte stat = GET_STAT(); + OUT_BYTE(dma_base+2, dma_stat & 0xE4); + } +#endif + /* return 1 if INTR asserted */ + return (dma_stat & 4) == 4; + case ide_dma_end: + { + byte bwsr_mask = hwif->channel ? 0x02 : 0x01; + byte bwsr_stat, msc_stat; + pci_read_config_byte(dev, 0x6a, &bwsr_stat); + pci_read_config_byte(dev, mscreg, &msc_stat); + if ((bwsr_stat & bwsr_mask) == bwsr_mask) + pci_write_config_byte(dev, mscreg, msc_stat|0x30); + } + default: + break; + } + /* use standard DMA stuff */ + return ide_dmaproc(func, drive); +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +/* + * Since SUN Cobalt is attempting to do this operation, I should disclose + * this has been a long time ago Thu Jul 27 16:40:57 2000 was the patch date + * HOTSWAP ATA Infrastructure. + */ +void hpt3xx_reset (ide_drive_t *drive) +{ +#if 0 + unsigned long high_16 = pci_resource_start(HWIF(drive)->pci_dev, 4); + byte reset = (HWIF(drive)->channel) ? 0x80 : 0x40; + byte reg59h = 0; + + pci_read_config_byte(HWIF(drive)->pci_dev, 0x59, ®59h); + pci_write_config_byte(HWIF(drive)->pci_dev, 0x59, reg59h|reset); + pci_write_config_byte(HWIF(drive)->pci_dev, 0x59, reg59h); +#endif +} + +static int hpt3xx_tristate (ide_drive_t * drive, int state) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte reset = (hwif->channel) ? 0x80 : 0x40; + byte state_reg = (hwif->channel) ? 0x57 : 0x53; + byte reg59h = 0; + byte regXXh = 0; + + if (!hwif) + return -EINVAL; + +// hwif->bus_state = state; + + pci_read_config_byte(dev, 0x59, ®59h); + pci_read_config_byte(dev, state_reg, ®XXh); + + if (state) { + (void) ide_do_reset(drive); + pci_write_config_byte(dev, state_reg, regXXh|0x80); + pci_write_config_byte(dev, 0x59, reg59h|reset); + } else { + pci_write_config_byte(dev, 0x59, reg59h & ~(reset)); + pci_write_config_byte(dev, state_reg, regXXh & ~(0x80)); + (void) ide_do_reset(drive); + } + return 0; +} + +/* + * set/get power state for a drive. + * turning the power off does the following things: + * 1) soft-reset the drive + * 2) tri-states the ide bus + * + * when we turn things back on, we need to re-initialize things. + */ +#define TRISTATE_BIT 0x8000 +static int hpt370_busproc(ide_drive_t * drive, int state) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte tristate, resetmask, bus_reg; + u16 tri_reg; + + if (!hwif) + return -EINVAL; + + hwif->bus_state = state; + + if (hwif->channel) { + /* secondary channel */ + tristate = 0x56; + resetmask = 0x80; + } else { + /* primary channel */ + tristate = 0x52; + resetmask = 0x40; + } + + /* grab status */ + pci_read_config_word(dev, tristate, &tri_reg); + pci_read_config_byte(dev, 0x59, &bus_reg); + + /* set the state. we don't set it if we don't need to do so. + * make sure that the drive knows that it has failed if it's off */ + switch (state) { + case BUSSTATE_ON: + hwif->drives[0].failures = 0; + hwif->drives[1].failures = 0; + if ((bus_reg & resetmask) == 0) + return 0; + tri_reg &= ~TRISTATE_BIT; + bus_reg &= ~resetmask; + break; + case BUSSTATE_OFF: + hwif->drives[0].failures = hwif->drives[0].max_failures + 1; + hwif->drives[1].failures = hwif->drives[1].max_failures + 1; + if ((tri_reg & TRISTATE_BIT) == 0 && (bus_reg & resetmask)) + return 0; + tri_reg &= ~TRISTATE_BIT; + bus_reg |= resetmask; + break; + case BUSSTATE_TRISTATE: + hwif->drives[0].failures = hwif->drives[0].max_failures + 1; + hwif->drives[1].failures = hwif->drives[1].max_failures + 1; + if ((tri_reg & TRISTATE_BIT) && (bus_reg & resetmask)) + return 0; + tri_reg |= TRISTATE_BIT; + bus_reg |= resetmask; + break; + } + pci_write_config_byte(dev, 0x59, bus_reg); + pci_write_config_word(dev, tristate, tri_reg); + + return 0; +} + +static void __init init_hpt37x(struct pci_dev *dev) +{ + int adjust, i; + u16 freq; + u32 pll; + byte reg5bh; + +#if 1 + byte reg5ah = 0; + pci_read_config_byte(dev, 0x5a, ®5ah); + /* interrupt force enable */ + pci_write_config_byte(dev, 0x5a, (reg5ah & ~0x10)); +#endif + + /* + * default to pci clock. make sure MA15/16 are set to output + * to prevent drives having problems with 40-pin cables. + */ + pci_write_config_byte(dev, 0x5b, 0x23); + + /* + * set up the PLL. we need to adjust it so that it's stable. + * freq = Tpll * 192 / Tpci + */ + pci_read_config_word(dev, 0x78, &freq); + freq &= 0x1FF; + if (freq < 0x9c) { + pll = F_LOW_PCI_33; + if (hpt_minimum_revision(dev,8)) + dev->driver_data = (void *) thirty_three_base_hpt374; + else if (hpt_minimum_revision(dev,5)) + dev->driver_data = (void *) thirty_three_base_hpt372; + else if (hpt_minimum_revision(dev,4)) + dev->driver_data = (void *) thirty_three_base_hpt370a; + else + dev->driver_data = (void *) thirty_three_base_hpt370; + printk("HPT37X: using 33MHz PCI clock\n"); + } else if (freq < 0xb0) { + pll = F_LOW_PCI_40; + } else if (freq < 0xc8) { + pll = F_LOW_PCI_50; + if (hpt_minimum_revision(dev,8)) + BUG(); + else if (hpt_minimum_revision(dev,5)) + dev->driver_data = (void *) fifty_base_hpt372; + else if (hpt_minimum_revision(dev,4)) + dev->driver_data = (void *) fifty_base_hpt370a; + else + dev->driver_data = (void *) fifty_base_hpt370a; + printk("HPT37X: using 50MHz PCI clock\n"); + } else { + pll = F_LOW_PCI_66; + if (hpt_minimum_revision(dev,8)) + BUG(); + else if (hpt_minimum_revision(dev,5)) + dev->driver_data = (void *) sixty_six_base_hpt372; + else if (hpt_minimum_revision(dev,4)) + dev->driver_data = (void *) sixty_six_base_hpt370a; + else + dev->driver_data = (void *) sixty_six_base_hpt370; + printk("HPT37X: using 66MHz PCI clock\n"); + } + + /* + * only try the pll if we don't have a table for the clock + * speed that we're running at. NOTE: the internal PLL will + * result in slow reads when using a 33MHz PCI clock. we also + * don't like to use the PLL because it will cause glitches + * on PRST/SRST when the HPT state engine gets reset. + */ + if (dev->driver_data) + goto init_hpt37X_done; + + /* + * adjust PLL based upon PCI clock, enable it, and wait for + * stabilization. + */ + adjust = 0; + freq = (pll < F_LOW_PCI_50) ? 2 : 4; + while (adjust++ < 6) { + pci_write_config_dword(dev, 0x5c, (freq + pll) << 16 | + pll | 0x100); + + /* wait for clock stabilization */ + for (i = 0; i < 0x50000; i++) { + pci_read_config_byte(dev, 0x5b, ®5bh); + if (reg5bh & 0x80) { + /* spin looking for the clock to destabilize */ + for (i = 0; i < 0x1000; ++i) { + pci_read_config_byte(dev, 0x5b, + ®5bh); + if ((reg5bh & 0x80) == 0) + goto pll_recal; + } + pci_read_config_dword(dev, 0x5c, &pll); + pci_write_config_dword(dev, 0x5c, + pll & ~0x100); + pci_write_config_byte(dev, 0x5b, 0x21); + if (hpt_minimum_revision(dev,8)) + BUG(); + else if (hpt_minimum_revision(dev,5)) + dev->driver_data = (void *) fifty_base_hpt372; + else if (hpt_minimum_revision(dev,4)) + dev->driver_data = (void *) fifty_base_hpt370a; + else + dev->driver_data = (void *) fifty_base_hpt370a; + printk("HPT37X: using 50MHz internal PLL\n"); + goto init_hpt37X_done; + } + } +pll_recal: + if (adjust & 1) + pll -= (adjust >> 1); + else + pll += (adjust >> 1); + } + +init_hpt37X_done: + /* reset state engine */ + pci_write_config_byte(dev, 0x50, 0x37); + pci_write_config_byte(dev, 0x54, 0x37); + udelay(100); +} + +static void __init init_hpt366 (struct pci_dev *dev) +{ + unsigned int reg1 = 0; + byte drive_fast = 0; + + /* + * Disable the "fast interrupt" prediction. + */ + pci_read_config_byte(dev, 0x51, &drive_fast); + if (drive_fast & 0x80) + pci_write_config_byte(dev, 0x51, drive_fast & ~0x80); + pci_read_config_dword(dev, 0x40, ®1); + + /* detect bus speed by looking at control reg timing: */ + switch((reg1 >> 8) & 7) { + case 5: + dev->driver_data = (void *) forty_base_hpt366; + break; + case 9: + dev->driver_data = (void *) twenty_five_base_hpt366; + break; + case 7: + default: + dev->driver_data = (void *) thirty_three_base_hpt366; + break; + } + + if (!dev->driver_data) + BUG(); +} + +unsigned int __init pci_init_hpt366 (struct pci_dev *dev, const char *name) +{ + byte test = 0; + + if (dev->resource[PCI_ROM_RESOURCE].start) + pci_write_config_byte(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); + + pci_read_config_byte(dev, PCI_CACHE_LINE_SIZE, &test); + if (test != (L1_CACHE_BYTES / 4)) + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, (L1_CACHE_BYTES / 4)); + + pci_read_config_byte(dev, PCI_LATENCY_TIMER, &test); + if (test != 0x78) + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x78); + + pci_read_config_byte(dev, PCI_MIN_GNT, &test); + if (test != 0x08) + pci_write_config_byte(dev, PCI_MIN_GNT, 0x08); + + pci_read_config_byte(dev, PCI_MAX_LAT, &test); + if (test != 0x08) + pci_write_config_byte(dev, PCI_MAX_LAT, 0x08); + + if (hpt_minimum_revision(dev, 3)) { + init_hpt37x(dev); + hpt_devs[n_hpt_devs++] = dev; + } else { + init_hpt366(dev); + hpt_devs[n_hpt_devs++] = dev; + } + +#if defined(DISPLAY_HPT366_TIMINGS) && defined(CONFIG_PROC_FS) + if (!hpt366_proc) { + hpt366_proc = 1; + hpt366_display_info = &hpt366_get_info; + } +#endif /* DISPLAY_HPT366_TIMINGS && CONFIG_PROC_FS */ + + return dev->irq; +} + +unsigned int __init ata66_hpt366 (ide_hwif_t *hwif) +{ + byte ata66 = 0; + byte regmask = (hwif->channel) ? 0x01 : 0x02; + + pci_read_config_byte(hwif->pci_dev, 0x5a, &ata66); +#ifdef DEBUG + printk("HPT366: reg5ah=0x%02x ATA-%s Cable Port%d\n", + ata66, (ata66 & regmask) ? "33" : "66", + PCI_FUNC(hwif->pci_dev->devfn)); +#endif /* DEBUG */ + return ((ata66 & regmask) ? 0 : 1); +} + +void __init ide_init_hpt366 (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + hwif->tuneproc = &hpt3xx_tune_drive; + hwif->speedproc = &hpt3xx_tune_chipset; + hwif->quirkproc = &hpt3xx_quirkproc; + hwif->intrproc = &hpt3xx_intrproc; + hwif->maskproc = &hpt3xx_maskproc; + +#ifdef HPT_SERIALIZE_IO + /* serialize access to this device */ + if (hwif->mate) + hwif->serialized = hwif->mate->serialized = 1; +#endif + + if (hpt_minimum_revision(dev,3)) { + byte reg5ah = 0; + pci_write_config_byte(dev, 0x5a, reg5ah & ~0x10); + /* + * set up ioctl for power status. + * note: power affects both + * drives on each channel + */ + hwif->resetproc = &hpt3xx_reset; + hwif->busproc = &hpt370_busproc; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + } else if (hpt_minimum_revision(dev,2)) { + hwif->resetproc = &hpt3xx_reset; + hwif->busproc = &hpt3xx_tristate; + } else { + hwif->resetproc = &hpt3xx_reset; + hwif->busproc = &hpt3xx_tristate; + } + + if (!hwif->dma_base) + return; + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (hpt_minimum_revision(dev,8)) + hwif->dmaproc = &hpt374_dmaproc; + else if (hpt_minimum_revision(dev,5)) + hwif->dmaproc = &hpt374_dmaproc; + else if (hpt_minimum_revision(dev,3)) + hwif->dmaproc = &hpt370_dmaproc; + else if (hpt_minimum_revision(dev,2)) + hwif->dmaproc = &hpt366_dmaproc; + else + hwif->dmaproc = &hpt366_dmaproc; + + +#ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +#endif /* CONFIG_IDEDMA_AUTO */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ +} + +void __init ide_dmacapable_hpt366 (ide_hwif_t *hwif, unsigned long dmabase) +{ + byte masterdma = 0, slavedma = 0; + byte dma_new = 0, dma_old = IN_BYTE(dmabase+2); + byte primary = hwif->channel ? 0x4b : 0x43; + byte secondary = hwif->channel ? 0x4f : 0x47; + unsigned long flags; + + local_irq_save(flags); + + dma_new = dma_old; + pci_read_config_byte(hwif->pci_dev, primary, &masterdma); + pci_read_config_byte(hwif->pci_dev, secondary, &slavedma); + + if (masterdma & 0x30) dma_new |= 0x20; + if (slavedma & 0x30) dma_new |= 0x40; + if (dma_new != dma_old) OUT_BYTE(dma_new, dmabase+2); + + local_irq_restore(flags); + + ide_setup_dma(hwif, dmabase, 8); +} + +extern void ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d); + +void __init fixup_device_hpt374 (struct pci_dev *dev, ide_pci_device_t *d) +{ + struct pci_dev *dev2 = NULL, *findev; + ide_pci_device_t *d2; + + if (PCI_FUNC(dev->devfn) & 1) + return; + + pci_for_each_dev(findev) { + if ((findev->vendor == dev->vendor) && + (findev->device == dev->device) && + ((findev->devfn - dev->devfn) == 1) && + (PCI_FUNC(findev->devfn) & 1)) { + dev2 = findev; + break; + } + } + + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); + if (!dev2) { + return; + } else { + byte irq = 0, irq2 = 0; + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); + pci_read_config_byte(dev2, PCI_INTERRUPT_LINE, &irq2); + if (irq != irq2) { + pci_write_config_byte(dev2, PCI_INTERRUPT_LINE, irq); + dev2->irq = dev->irq; + printk("%s: pci-config space interrupt fixed.\n", + d->name); + } + } + d2 = d; + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d2->name, dev2->bus->number, dev2->devfn); + ide_setup_pci_device(dev2, d2); + +} + +void __init fixup_device_hpt366 (struct pci_dev *dev, ide_pci_device_t *d) +{ + struct pci_dev *dev2 = NULL, *findev; + ide_pci_device_t *d2; + unsigned char pin1 = 0, pin2 = 0; + unsigned int class_rev; + char *chipset_names[] = {"HPT366", "HPT366", "HPT368", + "HPT370", "HPT370A", "HPT372"}; + + if (PCI_FUNC(dev->devfn) & 1) + return; + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + + strcpy(d->name, chipset_names[class_rev]); + + switch(class_rev) { + case 5: + case 4: + case 3: printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); + return; + default: break; + } + + pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin1); + pci_for_each_dev(findev) { + if ((findev->vendor == dev->vendor) && + (findev->device == dev->device) && + ((findev->devfn - dev->devfn) == 1) && + (PCI_FUNC(findev->devfn) & 1)) { + dev2 = findev; + pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin2); + hpt363_shared_pin = (pin1 != pin2) ? 1 : 0; + hpt363_shared_irq = (dev->irq == dev2->irq) ? 1 : 0; + if (hpt363_shared_pin && hpt363_shared_irq) { + d->bootable = ON_BOARD; + printk("%s: onboard version of chipset, " + "pin1=%d pin2=%d\n", d->name, + pin1, pin2); +#if 0 + /* + * This is the third undocumented detection + * method and is generally required for the + * ABIT-BP6 boards. + */ + pci_write_config_byte(dev2, PCI_INTERRUPT_PIN, dev->irq); + printk("PCI: %s: Fixing interrupt %d pin %d " + "to ZERO \n", d->name, dev2->irq, pin2); + pci_write_config_byte(dev2, PCI_INTERRUPT_LINE, 0); +#endif + } + break; + } + } + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); + if (!dev2) + return; + d2 = d; + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d2->name, dev2->bus->number, dev2->devfn); + ide_setup_pci_device(dev2, d2); +} + diff -Nru a/drivers/ide/ht6560b.c b/drivers/ide/ht6560b.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ht6560b.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,341 @@ +/* + * linux/drivers/ide/ht6560b.c Version 0.07 Feb 1, 2000 + * + * Copyright (C) 1995-2000 Linus Torvalds & author (see below) + */ + +/* + * + * Version 0.01 Initial version hacked out of ide.c + * + * Version 0.02 Added support for PIO modes, auto-tune + * + * Version 0.03 Some cleanups + * + * Version 0.05 PIO mode cycle timings auto-tune using bus-speed + * + * Version 0.06 Prefetch mode now defaults no OFF. To set + * prefetch mode OFF/ON use "hdparm -p8/-p9". + * Unmask irq is disabled when prefetch mode + * is enabled. + * + * Version 0.07 Trying to fix CD-ROM detection problem. + * "Prefetch" mode bit OFF for ide disks and + * ON for anything else. + * + * + * HT-6560B EIDE-controller support + * To activate controller support use kernel parameter "ide0=ht6560b". + * Use hdparm utility to enable PIO mode support. + * + * Author: Mikko Ala-Fossi + * Jan Evert van Grootheest + * + * Try: http://www.maf.iki.fi/~maf/ht6560b/ + */ + +#define HT6560B_VERSION "v0.07" + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ide_modes.h" + +/* #define DEBUG */ /* remove comments for DEBUG messages */ + +/* + * The special i/o-port that HT-6560B uses to configuration: + * bit0 (0x01): "1" selects secondary interface + * bit2 (0x04): "1" enables FIFO function + * bit5 (0x20): "1" enables prefetched data read function (???) + * + * The special i/o-port that HT-6560A uses to configuration: + * bit0 (0x01): "1" selects secondary interface + * bit1 (0x02): "1" enables prefetched data read function + * bit2 (0x04): "0" enables multi-master system (?) + * bit3 (0x08): "1" 3 cycle time, "0" 2 cycle time (?) + */ +#define HT_CONFIG_PORT 0x3e6 +#define HT_CONFIG(drivea) (byte)(((drivea)->drive_data & 0xff00) >> 8) +/* + * FIFO + PREFETCH (both a/b-model) + */ +#define HT_CONFIG_DEFAULT 0x1c /* no prefetch */ +/* #define HT_CONFIG_DEFAULT 0x3c */ /* with prefetch */ +#define HT_SECONDARY_IF 0x01 +#define HT_PREFETCH_MODE 0x20 + +/* + * ht6560b Timing values: + * + * I reviewed some assembler source listings of htide drivers and found + * out how they setup those cycle time interfacing values, as they at Holtek + * call them. IDESETUP.COM that is supplied with the drivers figures out + * optimal values and fetches those values to drivers. I found out that + * they use IDE_SELECT_REG to fetch timings to the ide board right after + * interface switching. After that it was quite easy to add code to + * ht6560b.c. + * + * IDESETUP.COM gave me values 0x24, 0x45, 0xaa, 0xff that worked fine + * for hda and hdc. But hdb needed higher values to work, so I guess + * that sometimes it is necessary to give higher value than IDESETUP + * gives. [see cmd640.c for an extreme example of this. -ml] + * + * Perhaps I should explain something about these timing values: + * The higher nibble of value is the Recovery Time (rt) and the lower nibble + * of the value is the Active Time (at). Minimum value 2 is the fastest and + * the maximum value 15 is the slowest. Default values should be 15 for both. + * So 0x24 means 2 for rt and 4 for at. Each of the drives should have + * both values, and IDESETUP gives automatically rt=15 st=15 for CDROMs or + * similar. If value is too small there will be all sorts of failures. + * + * Timing byte consists of + * High nibble: Recovery Cycle Time (rt) + * The valid values range from 2 to 15. The default is 15. + * + * Low nibble: Active Cycle Time (at) + * The valid values range from 2 to 15. The default is 15. + * + * You can obtain optimized timing values by running Holtek IDESETUP.COM + * for DOS. DOS drivers get their timing values from command line, where + * the first value is the Recovery Time and the second value is the + * Active Time for each drive. Smaller value gives higher speed. + * In case of failures you should probably fall back to a higher value. + */ +#define HT_TIMING(drivea) (byte)((drivea)->drive_data & 0x00ff) +#define HT_TIMING_DEFAULT 0xff + +/* + * This routine handles interface switching for the peculiar hardware design + * on the F.G.I./Holtek HT-6560B VLB IDE interface. + * The HT-6560B can only enable one IDE port at a time, and requires a + * silly sequence (below) whenever we switch between primary and secondary. + */ + +/* + * This routine is invoked from ide.c to prepare for access to a given drive. + */ +static void ht6560b_selectproc (ide_drive_t *drive) +{ + unsigned long flags; + static byte current_select = 0; + static byte current_timing = 0; + byte select, timing; + + local_irq_save(flags); + + select = HT_CONFIG(drive); + timing = HT_TIMING(drive); + + if (select != current_select || timing != current_timing) { + current_select = select; + current_timing = timing; + if (drive->media != ide_disk || !drive->present) + select |= HT_PREFETCH_MODE; + (void) IN_BYTE(HT_CONFIG_PORT); + (void) IN_BYTE(HT_CONFIG_PORT); + (void) IN_BYTE(HT_CONFIG_PORT); + (void) IN_BYTE(HT_CONFIG_PORT); + OUT_BYTE(select, HT_CONFIG_PORT); + /* + * Set timing for this drive: + */ + OUT_BYTE(timing, IDE_SELECT_REG); + (void) IN_BYTE(IDE_STATUS_REG); +#ifdef DEBUG + printk("ht6560b: %s: select=%#x timing=%#x\n", + drive->name, select, timing); +#endif + } + local_irq_restore(flags); +} + +/* + * Autodetection and initialization of ht6560b + */ +static int __init try_to_init_ht6560b(void) +{ + byte orig_value; + int i; + + /* Autodetect ht6560b */ + if ((orig_value = IN_BYTE(HT_CONFIG_PORT)) == 0xff) + return 0; + + for (i=3;i>0;i--) { + OUT_BYTE(0x00, HT_CONFIG_PORT); + if (!( (~IN_BYTE(HT_CONFIG_PORT)) & 0x3f )) { + OUT_BYTE(orig_value, HT_CONFIG_PORT); + return 0; + } + } + OUT_BYTE(0x00, HT_CONFIG_PORT); + if ((~IN_BYTE(HT_CONFIG_PORT))& 0x3f) { + OUT_BYTE(orig_value, HT_CONFIG_PORT); + return 0; + } + /* + * Ht6560b autodetected + */ + OUT_BYTE(HT_CONFIG_DEFAULT, HT_CONFIG_PORT); + OUT_BYTE(HT_TIMING_DEFAULT, 0x1f6); /* IDE_SELECT_REG */ + (void) IN_BYTE(0x1f7); /* IDE_STATUS_REG */ + + printk("\nht6560b " HT6560B_VERSION + ": chipset detected and initialized" +#ifdef DEBUG + " with debug enabled" +#endif + ); + return 1; +} + +static byte ht_pio2timings(ide_drive_t *drive, byte pio) +{ + int active_time, recovery_time; + int active_cycles, recovery_cycles; + ide_pio_data_t d; + int bus_speed = system_bus_clock(); + + if (pio) { + pio = ide_get_best_pio_mode(drive, pio, 5, &d); + + /* + * Just like opti621.c we try to calculate the + * actual cycle time for recovery and activity + * according system bus speed. + */ + active_time = ide_pio_timings[pio].active_time; + recovery_time = d.cycle_time + - active_time + - ide_pio_timings[pio].setup_time; + /* + * Cycle times should be Vesa bus cycles + */ + active_cycles = (active_time * bus_speed + 999) / 1000; + recovery_cycles = (recovery_time * bus_speed + 999) / 1000; + /* + * Upper and lower limits + */ + if (active_cycles < 2) active_cycles = 2; + if (recovery_cycles < 2) recovery_cycles = 2; + if (active_cycles > 15) active_cycles = 15; + if (recovery_cycles > 15) recovery_cycles = 0; /* 0==16 */ + +#ifdef DEBUG + printk("ht6560b: drive %s setting pio=%d recovery=%d (%dns) active=%d (%dns)\n", drive->name, pio, recovery_cycles, recovery_time, active_cycles, active_time); +#endif + + return (byte)((recovery_cycles << 4) | active_cycles); + } else { + +#ifdef DEBUG + printk("ht6560b: drive %s setting pio=0\n", drive->name); +#endif + + return HT_TIMING_DEFAULT; /* default setting */ + } +} + +/* + * Enable/Disable so called prefetch mode + */ +static void ht_set_prefetch(ide_drive_t *drive, byte state) +{ + unsigned long flags; + int t = HT_PREFETCH_MODE << 8; + + spin_lock_irqsave(&ide_lock, flags); + + /* + * Prefetch mode and unmask irq seems to conflict + */ + if (state) { + drive->drive_data |= t; /* enable prefetch mode */ + drive->no_unmask = 1; + drive->unmask = 0; + } else { + drive->drive_data &= ~t; /* disable prefetch mode */ + drive->no_unmask = 0; + } + + spin_unlock_irqrestore(&ide_lock, flags); + +#ifdef DEBUG + printk("ht6560b: drive %s prefetch mode %sabled\n", drive->name, (state ? "en" : "dis")); +#endif +} + +static void tune_ht6560b (ide_drive_t *drive, byte pio) +{ + unsigned long flags; + byte timing; + + switch (pio) { + case 8: /* set prefetch off */ + case 9: /* set prefetch on */ + ht_set_prefetch(drive, pio & 1); + return; + } + + timing = ht_pio2timings(drive, pio); + + spin_lock_irqsave(&ide_lock, flags); + + drive->drive_data &= 0xff00; + drive->drive_data |= timing; + + spin_unlock_irqrestore(&ide_lock, flags); + +#ifdef DEBUG + printk("ht6560b: drive %s tuned to pio mode %#x timing=%#x\n", drive->name, pio, timing); +#endif +} + +void __init init_ht6560b (void) +{ + int t; + + if (check_region(HT_CONFIG_PORT,1)) { + printk(KERN_ERR "ht6560b: PORT %#x ALREADY IN USE\n", HT_CONFIG_PORT); + } else { + if (try_to_init_ht6560b()) { + request_region(HT_CONFIG_PORT, 1, ide_hwifs[0].name); + ide_hwifs[0].chipset = ide_ht6560b; + ide_hwifs[1].chipset = ide_ht6560b; + ide_hwifs[0].selectproc = &ht6560b_selectproc; + ide_hwifs[1].selectproc = &ht6560b_selectproc; + ide_hwifs[0].tuneproc = &tune_ht6560b; + ide_hwifs[1].tuneproc = &tune_ht6560b; + ide_hwifs[0].serialized = 1; /* is this needed? */ + ide_hwifs[1].serialized = 1; /* is this needed? */ + ide_hwifs[0].mate = &ide_hwifs[1]; + ide_hwifs[1].mate = &ide_hwifs[0]; + ide_hwifs[1].channel = 1; + + /* + * Setting default configurations for drives + */ + t = (HT_CONFIG_DEFAULT << 8); + t |= HT_TIMING_DEFAULT; + ide_hwifs[0].drives[0].drive_data = t; + ide_hwifs[0].drives[1].drive_data = t; + t |= (HT_SECONDARY_IF << 8); + ide_hwifs[1].drives[0].drive_data = t; + ide_hwifs[1].drives[1].drive_data = t; + } else + printk(KERN_ERR "ht6560b: not found\n"); + } +} diff -Nru a/drivers/ide/icside.c b/drivers/ide/icside.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/icside.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,782 @@ +/* + * linux/drivers/ide/icside.c + * + * Copyright (c) 1996,1997 Russell King. + * + * Changelog: + * 08-Jun-1996 RMK Created + * 12-Sep-1997 RMK Added interrupt enable/disable + * 17-Apr-1999 RMK Added support for V6 EASI + * 22-May-1999 RMK Added support for V6 DMA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* + * Maximum number of interfaces per card + */ +#define MAX_IFS 2 + +#define ICS_IDENT_OFFSET 0x8a0 + +#define ICS_ARCIN_V5_INTRSTAT 0x000 +#define ICS_ARCIN_V5_INTROFFSET 0x001 +#define ICS_ARCIN_V5_IDEOFFSET 0xa00 +#define ICS_ARCIN_V5_IDEALTOFFSET 0xae0 +#define ICS_ARCIN_V5_IDESTEPPING 4 + +#define ICS_ARCIN_V6_IDEOFFSET_1 0x800 +#define ICS_ARCIN_V6_INTROFFSET_1 0x880 +#define ICS_ARCIN_V6_INTRSTAT_1 0x8a4 +#define ICS_ARCIN_V6_IDEALTOFFSET_1 0x8e0 +#define ICS_ARCIN_V6_IDEOFFSET_2 0xc00 +#define ICS_ARCIN_V6_INTROFFSET_2 0xc80 +#define ICS_ARCIN_V6_INTRSTAT_2 0xca4 +#define ICS_ARCIN_V6_IDEALTOFFSET_2 0xce0 +#define ICS_ARCIN_V6_IDESTEPPING 4 + +struct cardinfo { + unsigned int dataoffset; + unsigned int ctrloffset; + unsigned int stepping; +}; + +static struct cardinfo icside_cardinfo_v5 = { + ICS_ARCIN_V5_IDEOFFSET, + ICS_ARCIN_V5_IDEALTOFFSET, + ICS_ARCIN_V5_IDESTEPPING +}; + +static struct cardinfo icside_cardinfo_v6_1 = { + ICS_ARCIN_V6_IDEOFFSET_1, + ICS_ARCIN_V6_IDEALTOFFSET_1, + ICS_ARCIN_V6_IDESTEPPING +}; + +static struct cardinfo icside_cardinfo_v6_2 = { + ICS_ARCIN_V6_IDEOFFSET_2, + ICS_ARCIN_V6_IDEALTOFFSET_2, + ICS_ARCIN_V6_IDESTEPPING +}; + +static const card_ids icside_cids[] = { + { MANU_ICS, PROD_ICS_IDE }, + { MANU_ICS2, PROD_ICS2_IDE }, + { 0xffff, 0xffff } +}; + +typedef enum { + ics_if_unknown, + ics_if_arcin_v5, + ics_if_arcin_v6 +} iftype_t; + +/* ---------------- Version 5 PCB Support Functions --------------------- */ +/* Prototype: icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) + * Purpose : enable interrupts from card + */ +static void icside_irqenable_arcin_v5 (struct expansion_card *ec, int irqnr) +{ + unsigned int memc_port = (unsigned int)ec->irq_data; + OUT_BYTE(0, memc_port + ICS_ARCIN_V5_INTROFFSET); +} + +/* Prototype: icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) + * Purpose : disable interrupts from card + */ +static void icside_irqdisable_arcin_v5 (struct expansion_card *ec, int irqnr) +{ + unsigned int memc_port = (unsigned int)ec->irq_data; + IN_BYTE(memc_port + ICS_ARCIN_V5_INTROFFSET); +} + +static const expansioncard_ops_t icside_ops_arcin_v5 = { + icside_irqenable_arcin_v5, + icside_irqdisable_arcin_v5, + NULL, + NULL, + NULL, + NULL +}; + + +/* ---------------- Version 6 PCB Support Functions --------------------- */ +/* Prototype: icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) + * Purpose : enable interrupts from card + */ +static void icside_irqenable_arcin_v6 (struct expansion_card *ec, int irqnr) +{ + unsigned int ide_base_port = (unsigned int)ec->irq_data; + + OUT_BYTE(0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); + OUT_BYTE(0, ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); +} + +/* Prototype: icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) + * Purpose : disable interrupts from card + */ +static void icside_irqdisable_arcin_v6 (struct expansion_card *ec, int irqnr) +{ + unsigned int ide_base_port = (unsigned int)ec->irq_data; + + IN_BYTE(ide_base_port + ICS_ARCIN_V6_INTROFFSET_1); + IN_BYTE(ide_base_port + ICS_ARCIN_V6_INTROFFSET_2); +} + +/* Prototype: icside_irqprobe(struct expansion_card *ec) + * Purpose : detect an active interrupt from card + */ +static int icside_irqpending_arcin_v6(struct expansion_card *ec) +{ + unsigned int ide_base_port = (unsigned int)ec->irq_data; + + return IN_BYTE(ide_base_port + ICS_ARCIN_V6_INTRSTAT_1) & 1 || + IN_BYTE(ide_base_port + ICS_ARCIN_V6_INTRSTAT_2) & 1; +} + +static const expansioncard_ops_t icside_ops_arcin_v6 = { + icside_irqenable_arcin_v6, + icside_irqdisable_arcin_v6, + icside_irqpending_arcin_v6, + NULL, + NULL, + NULL +}; + +/* Prototype: icside_identifyif (struct expansion_card *ec) + * Purpose : identify IDE interface type + * Notes : checks the description string + */ +static iftype_t __init icside_identifyif (struct expansion_card *ec) +{ + unsigned int addr; + iftype_t iftype; + int id = 0; + + iftype = ics_if_unknown; + + addr = ecard_address (ec, ECARD_IOC, ECARD_FAST) + ICS_IDENT_OFFSET; + + id = IN_BYTE(addr) & 1; + id |= (IN_BYTE(addr + 1) & 1) << 1; + id |= (IN_BYTE(addr + 2) & 1) << 2; + id |= (IN_BYTE(addr + 3) & 1) << 3; + + switch (id) { + case 0: /* A3IN */ + printk("icside: A3IN unsupported\n"); + break; + + case 1: /* A3USER */ + printk("icside: A3USER unsupported\n"); + break; + + case 3: /* ARCIN V6 */ + printk(KERN_DEBUG "icside: detected ARCIN V6 in slot %d\n", ec->slot_no); + iftype = ics_if_arcin_v6; + break; + + case 15:/* ARCIN V5 (no id) */ + printk(KERN_DEBUG "icside: detected ARCIN V5 in slot %d\n", ec->slot_no); + iftype = ics_if_arcin_v5; + break; + + default:/* we don't know - complain very loudly */ + printk("icside: ***********************************\n"); + printk("icside: *** UNKNOWN ICS INTERFACE id=%d ***\n", id); + printk("icside: ***********************************\n"); + printk("icside: please report this to linux@arm.linux.org.uk\n"); + printk("icside: defaulting to ARCIN V5\n"); + iftype = ics_if_arcin_v5; + break; + } + + return iftype; +} + +#ifdef CONFIG_BLK_DEV_IDEDMA_ICS +/* + * SG-DMA support. + * + * Similar to the BM-DMA, but we use the RiscPCs IOMD DMA controllers. + * There is only one DMA controller per card, which means that only + * one drive can be accessed at one time. NOTE! We do not enforce that + * here, but we rely on the main IDE driver spotting that both + * interfaces use the same IRQ, which should guarantee this. + */ +#define NR_ENTRIES 256 +#define TABLE_SIZE (NR_ENTRIES * 8) + +static int ide_build_sglist(ide_hwif_t *hwif, struct request *rq) +{ + struct buffer_head *bh; + struct scatterlist *sg = hwif->sg_table; + int nents = 0; + + if (rq->cmd == READ) + hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; + else + hwif->sg_dma_direction = PCI_DMA_TODEVICE; + bh = rq->bh; + do { + unsigned char *virt_addr = bh->b_data; + unsigned int size = bh->b_size; + + while ((bh = bh->b_reqnext) != NULL) { + if ((virt_addr + size) != (unsigned char *)bh->b_data) + break; + size += bh->b_size; + } + memset(&sg[nents], 0, sizeof(*sg)); + sg[nents].address = virt_addr; + sg[nents].length = size; + nents++; + } while (bh != NULL); + + return pci_map_sg(NULL, sg, nents, hwif->sg_dma_direction); +} + +static int +icside_build_dmatable(ide_drive_t *drive, int reading) +{ + return HWIF(drive)->sg_nents = ide_build_sglist(HWIF(drive), HWGROUP(drive)->rq); +} + +/* Teardown mappings after DMA has completed. */ +static void icside_destroy_dmatable(ide_drive_t *drive) +{ + struct scatterlist *sg = HWIF(drive)->sg_table; + int nents = HWIF(drive)->sg_nents; + + pci_unmap_sg(NULL, sg, nents, HWIF(drive)->sg_dma_direction); +} + +static int +icside_config_if(ide_drive_t *drive, int xfer_mode) +{ + int func = ide_dma_off; + + switch (xfer_mode) { + case XFER_MW_DMA_2: + /* + * The cycle time is limited to 250ns by the r/w + * pulse width (90ns), however we should still + * have a maximum burst transfer rate of 8MB/s. + */ + drive->drive_data = 250; + break; + + case XFER_MW_DMA_1: + drive->drive_data = 250; + break; + + case XFER_MW_DMA_0: + drive->drive_data = 480; + break; + + default: + drive->drive_data = 0; + break; + } + + if (!drive->init_speed) + drive->init_speed = (byte) xfer_mode; + + if (drive->drive_data && + ide_config_drive_speed(drive, (byte) xfer_mode) == 0) + func = ide_dma_on; + else + drive->drive_data = 480; + + printk("%s: %s selected (peak %dMB/s)\n", drive->name, + ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data); + + drive->current_speed = (byte) xfer_mode; + + return func; +} + +static int +icside_set_speed(ide_drive_t *drive, byte speed) +{ + return icside_config_if(drive, speed); +} + +/* + * dma_intr() is the handler for disk read/write DMA interrupts + */ +static ide_startstop_t icside_dmaintr(ide_drive_t *drive) +{ + int i; + byte stat, dma_stat; + + dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive); + stat = GET_STAT(); /* get drive status */ + if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { + if (!dma_stat) { + struct request *rq = HWGROUP(drive)->rq; + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + DRIVER(drive)->end_request(drive, 1); + } + return ide_stopped; + } + printk("%s: dma_intr: bad DMA status (dma_stat=%x)\n", + drive->name, dma_stat); + } + return DRIVER(drive)->error(drive, "dma_intr", stat); +} + +/* + * The following is a sick duplication from ide-dma.c ;( + * + * This should be defined in one place only. + */ +struct drive_list_entry { + char * id_model; + char * id_firmware; +}; + +static struct drive_list_entry drive_whitelist [] = { + { "Micropolis 2112A", "ALL" }, + { "CONNER CTMA 4000", "ALL" }, + { "CONNER CTT8000-A", "ALL" }, + { "ST34342A", "ALL" }, + { NULL, 0 } +}; + +static struct drive_list_entry drive_blacklist [] = { + { "WDC AC11000H", "ALL" }, + { "WDC AC22100H", "ALL" }, + { "WDC AC32500H", "ALL" }, + { "WDC AC33100H", "ALL" }, + { "WDC AC31600H", "ALL" }, + { "WDC AC32100H", "24.09P07" }, + { "WDC AC23200L", "21.10N21" }, + { "Compaq CRD-8241B", "ALL" }, + { "CRD-8400B", "ALL" }, + { "CRD-8480B", "ALL" }, + { "CRD-8480C", "ALL" }, + { "CRD-8482B", "ALL" }, + { "CRD-84", "ALL" }, + { "SanDisk SDP3B", "ALL" }, + { "SanDisk SDP3B-64", "ALL" }, + { "SANYO CD-ROM CRD", "ALL" }, + { "HITACHI CDR-8", "ALL" }, + { "HITACHI CDR-8335", "ALL" }, + { "HITACHI CDR-8435", "ALL" }, + { "Toshiba CD-ROM XM-6202B", "ALL" }, + { "CD-532E-A", "ALL" }, + { "E-IDE CD-ROM CR-840", "ALL" }, + { "CD-ROM Drive/F5A", "ALL" }, + { "RICOH CD-R/RW MP7083A", "ALL" }, + { "WPI CDD-820", "ALL" }, + { "SAMSUNG CD-ROM SC-148C", "ALL" }, + { "SAMSUNG CD-ROM SC-148F", "ALL" }, + { "SAMSUNG CD-ROM SC", "ALL" }, + { "SanDisk SDP3B-64", "ALL" }, + { "SAMSUNG CD-ROM SN-124", "ALL" }, + { "PLEXTOR CD-R PX-W8432T", "ALL" }, + { "ATAPI CD-ROM DRIVE 40X MAXIMUM", "ALL" }, + { "_NEC DV5800A", "ALL" }, + { NULL, 0 } +}; + +static int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table) +{ + for ( ; drive_table->id_model ; drive_table++) + if ((!strcmp(drive_table->id_model, id->model)) && + ((!strstr(drive_table->id_firmware, id->fw_rev)) || + (!strcmp(drive_table->id_firmware, "ALL")))) + return 1; + return 0; +} + +/* + * For both Blacklisted and Whitelisted drives. + * This is setup to be called as an extern for future support + * to other special driver code. + */ +static int check_drive_lists(ide_drive_t *drive, int good_bad) +{ + struct hd_driveid *id = drive->id; + + if (good_bad) { + return in_drive_list(id, drive_whitelist); + } else { + int blacklist = in_drive_list(id, drive_blacklist); + if (blacklist) + printk("%s: Disabling DMA for %s\n", drive->name, id->model); + return(blacklist); + } + return 0; +} + +static int +icside_dma_check(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + int autodma = hwif->autodma; + int xfer_mode = XFER_PIO_2; + int func = ide_dma_off_quietly; + + if (!id || !(id->capability & 1) || !autodma) + goto out; + + /* + * Consult the list of known "bad" drives + */ + if (check_drive_lists(drive, 0)) { + func = ide_dma_off; + goto out; + } + + /* + * Enable DMA on any drive that has multiword DMA + */ + if (id->field_valid & 2) { + if (id->dma_mword & 4) { + xfer_mode = XFER_MW_DMA_2; + func = ide_dma_on; + } else if (id->dma_mword & 2) { + xfer_mode = XFER_MW_DMA_1; + func = ide_dma_on; + } else if (id->dma_mword & 1) { + xfer_mode = XFER_MW_DMA_0; + func = ide_dma_on; + } + goto out; + } + + /* + * Consult the list of known "good" drives + */ + if (check_drive_lists(drive, 1)) { + if (id->eide_dma_time > 150) + goto out; + xfer_mode = XFER_MW_DMA_1; + func = ide_dma_on; + } + +out: + func = icside_config_if(drive, xfer_mode); + + return hwif->dmaproc(func, drive); +} + +static int +icside_dma_verbose(ide_drive_t *drive) +{ + printk(", DMA"); + return 1; +} + +static int +icside_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); +// ide_task_t *args = HWGROUP(drive)->rq->special; + int count, reading = 0; + + switch (func) { + case ide_dma_off: + printk("%s: DMA disabled\n", drive->name); + /*FALLTHROUGH*/ + + case ide_dma_off_quietly: + case ide_dma_on: + drive->using_dma = (func == ide_dma_on); + return 0; + + case ide_dma_check: + return icside_dma_check(drive); + + case ide_dma_read: + reading = 1; + case ide_dma_write: + count = icside_build_dmatable(drive, reading); + if (!count) + return 1; + disable_dma(hwif->hw.dma); + + /* Route the DMA signals to + * to the correct interface. + */ + OUT_BYTE(hwif->select_data, hwif->config_data); + + /* Select the correct timing + * for this drive + */ + set_dma_speed(hwif->hw.dma, drive->drive_data); + + set_dma_sg(hwif->hw.dma, HWIF(drive)->sg_table, count); + set_dma_mode(hwif->hw.dma, reading ? DMA_MODE_READ + : DMA_MODE_WRITE); + + drive->waiting_for_dma = 1; + if (drive->media != ide_disk) + return 0; + + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &icside_dmaintr, WAIT_CMD, NULL); + /* + * FIX ME to use only ACB ide_task_t args Struct + */ +#if 0 + { + ide_task_t *args = HWGROUP(drive)->rq->special; + OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } +#else + if (HWGROUP(drive)->rq->flags == REQ_DRIVE_TASKFILE) { + ide_task_t *args = HWGROUP(drive)->rq->special; + OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } else if (drive->addressing == 1) + OUT_BYTE(reading ? WIN_READDMA_EXT : WIN_WRITEDMA_EXT, IDE_COMMAND_REG); + else + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); +#endif + return HWIF(drive)->dmaproc(ide_dma_begin, drive); + case ide_dma_begin: + enable_dma(hwif->hw.dma); + return 0; + + case ide_dma_end: + drive->waiting_for_dma = 0; + disable_dma(hwif->hw.dma); + icside_destroy_dmatable(drive); + return get_dma_residue(hwif->hw.dma) != 0; + + case ide_dma_test_irq: + return IN_BYTE((unsigned long)hwif->hw.priv) & 1; + + case ide_dma_bad_drive: + case ide_dma_good_drive: + return check_drive_lists(drive, (func == ide_dma_good_drive)); + + case ide_dma_verbose: + return icside_dma_verbose(drive); + + case ide_dma_timeout: + default: + printk("icside_dmaproc: unsupported %s func: %d\n", + ide_dmafunc_verbose(func), func); + } + return 1; +} + +static int +icside_setup_dma(ide_hwif_t *hwif, int autodma) +{ + printk(" %s: SG-DMA", hwif->name); + + hwif->sg_table = kmalloc(sizeof(struct scatterlist) * NR_ENTRIES, + GFP_KERNEL); + if (!hwif->sg_table) + goto failed; + + hwif->dmatable_cpu = NULL; + hwif->dmatable_dma = 0; + hwif->speedproc = icside_set_speed; + hwif->dmaproc = icside_dmaproc; + hwif->autodma = autodma; + + printk(" capable%s\n", autodma ? + ", auto-enable" : ""); + + return 1; + +failed: + printk(" -- ERROR, unable to allocate DMA table\n"); + return 0; +} +#endif + +static ide_hwif_t * +icside_find_hwif(unsigned long dataport) +{ + ide_hwif_t *hwif; + int index; + + for (index = 0; index < MAX_HWIFS; ++index) { + hwif = &ide_hwifs[index]; + if (hwif->io_ports[IDE_DATA_OFFSET] == (ide_ioreg_t)dataport) + goto found; + } + + for (index = 0; index < MAX_HWIFS; ++index) { + hwif = &ide_hwifs[index]; + if (!hwif->io_ports[IDE_DATA_OFFSET]) + goto found; + } + + return NULL; +found: + return hwif; +} + +static ide_hwif_t * +icside_setup(unsigned long base, struct cardinfo *info, int irq) +{ + unsigned long port = base + info->dataoffset; + ide_hwif_t *hwif; + + hwif = icside_find_hwif(base); + if (hwif) { + int i; + + memset(&hwif->hw, 0, sizeof(hw_regs_t)); + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hwif->hw.io_ports[i] = (ide_ioreg_t)port; + hwif->io_ports[i] = (ide_ioreg_t)port; + port += 1 << info->stepping; + } + hwif->hw.io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset; + hwif->io_ports[IDE_CONTROL_OFFSET] = base + info->ctrloffset; + hwif->hw.irq = irq; + hwif->irq = irq; + hwif->hw.dma = NO_DMA; + hwif->noprobe = 0; + hwif->chipset = ide_acorn; + } + + return hwif; +} + +static int __init icside_register_v5(struct expansion_card *ec, int autodma) +{ + unsigned long slot_port; + ide_hwif_t *hwif; + + slot_port = ecard_address(ec, ECARD_MEMC, 0); + + ec->irqaddr = (unsigned char *)ioaddr(slot_port + ICS_ARCIN_V5_INTRSTAT); + ec->irqmask = 1; + ec->irq_data = (void *)slot_port; + ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v5; + + /* + * Be on the safe side - disable interrupts + */ + IN_BYTE(slot_port + ICS_ARCIN_V5_INTROFFSET); + + hwif = icside_setup(slot_port, &icside_cardinfo_v5, ec->irq); + + return hwif ? 0 : -1; +} + +static int __init icside_register_v6(struct expansion_card *ec, int autodma) +{ + unsigned long slot_port, port; + ide_hwif_t *hwif, *mate; + int sel = 0; + + slot_port = ecard_address(ec, ECARD_IOC, ECARD_FAST); + port = ecard_address(ec, ECARD_EASI, ECARD_FAST); + + if (port == 0) + port = slot_port; + else + sel = 1 << 5; + + OUT_BYTE(sel, slot_port); + + ec->irq_data = (void *)port; + ec->ops = (expansioncard_ops_t *)&icside_ops_arcin_v6; + + /* + * Be on the safe side - disable interrupts + */ + IN_BYTE(port + ICS_ARCIN_V6_INTROFFSET_1); + IN_BYTE(port + ICS_ARCIN_V6_INTROFFSET_2); + + hwif = icside_setup(port, &icside_cardinfo_v6_1, ec->irq); + mate = icside_setup(port, &icside_cardinfo_v6_2, ec->irq); + +#ifdef CONFIG_BLK_DEV_IDEDMA_ICS + if (ec->dma != NO_DMA) { + if (request_dma(ec->dma, hwif->name)) + goto no_dma; + + if (hwif) { + hwif->config_data = slot_port; + hwif->select_data = sel; + hwif->hw.dma = ec->dma; + hwif->hw.priv = (void *) + (port + ICS_ARCIN_V6_INTRSTAT_1); + hwif->channel = 0; + icside_setup_dma(hwif, autodma); + } + if (mate) { + mate->config_data = slot_port; + mate->select_data = sel | 1; + mate->hw.dma = ec->dma; + mate->hw.priv = (void *) + (port + ICS_ARCIN_V6_INTRSTAT_2); + mate->channel = 1; + icside_setup_dma(mate, autodma); + } + } +no_dma: +#endif + return hwif || mate ? 0 : -1; +} + +int __init icside_init(void) +{ + int autodma = 0; + +#ifdef CONFIG_IDEDMA_ICS_AUTO + autodma = 1; +#endif + + ecard_startfind (); + + do { + struct expansion_card *ec; + int result; + + ec = ecard_find(0, icside_cids); + if (ec == NULL) + break; + + ecard_claim(ec); + + switch (icside_identifyif(ec)) { + case ics_if_arcin_v5: + result = icside_register_v5(ec, autodma); + break; + + case ics_if_arcin_v6: + result = icside_register_v6(ec, autodma); + break; + + default: + result = -1; + break; + } + + if (result) + ecard_release(ec); + } while (1); + + return 0; +} diff -Nru a/drivers/ide/ide-adma.c b/drivers/ide/ide-adma.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-adma.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,9 @@ +/* + * linux/drivers/ide/ide-adma.c Version 0.00 June 24, 2001 + * + * Copyright (c) 2001 Andre Hedrick + * + * Asynchronous DMA -- TBA, this is a holding file. + * + */ + diff -Nru a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-cd.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,3195 @@ +/* + * linux/drivers/ide/ide-cd.c + * + * Copyright (C) 1994, 1995, 1996 scott snyder + * Copyright (C) 1996-1998 Erik Andersen + * Copyright (C) 1998-2000 Jens Axboe + * + * May be copied or modified under the terms of the GNU General Public + * License. See linux/COPYING for more information. + * + * ATAPI CD-ROM driver. To be used with ide.c. + * See Documentation/cdrom/ide-cd for usage information. + * + * Suggestions are welcome. Patches that work are more welcome though. ;-) + * For those wishing to work on this driver, please be sure you download + * and comply with the latest Mt. Fuji (SFF8090 version 4) and ATAPI + * (SFF-8020i rev 2.6) standards. These documents can be obtained by + * anonymous ftp from: + * ftp://fission.dt.wdc.com/pub/standards/SFF_atapi/spec/SFF8020-r2.6/PS/8020r26.ps + * ftp://ftp.avc-pioneer.com/Mtfuji4/Spec/Fuji4r10.pdf + * + * Drives that deviate from these standards will be accomodated as much + * as possible via compile time or command-line options. Since I only have + * a few drives, you generally need to send me patches... + * + * ---------------------------------- + * TO DO LIST: + * -Make it so that Pioneer CD DR-A24X and friends don't get screwed up on + * boot + * + * ---------------------------------- + * 1.00 Oct 31, 1994 -- Initial version. + * 1.01 Nov 2, 1994 -- Fixed problem with starting request in + * cdrom_check_status. + * 1.03 Nov 25, 1994 -- leaving unmask_intr[] as a user-setting (as for disks) + * (from mlord) -- minor changes to cdrom_setup() + * -- renamed ide_dev_s to ide_drive_t, enable irq on command + * 2.00 Nov 27, 1994 -- Generalize packet command interface; + * add audio ioctls. + * 2.01 Dec 3, 1994 -- Rework packet command interface to handle devices + * which send an interrupt when ready for a command. + * 2.02 Dec 11, 1994 -- Cache the TOC in the driver. + * Don't use SCMD_PLAYAUDIO_TI; it's not included + * in the current version of ATAPI. + * Try to use LBA instead of track or MSF addressing + * when possible. + * Don't wait for READY_STAT. + * 2.03 Jan 10, 1995 -- Rewrite block read routines to handle block sizes + * other than 2k and to move multiple sectors in a + * single transaction. + * 2.04 Apr 21, 1995 -- Add work-around for Creative Labs CD220E drives. + * Thanks to Nick Saw for + * help in figuring this out. Ditto for Acer and + * Aztech drives, which seem to have the same problem. + * 2.04b May 30, 1995 -- Fix to match changes in ide.c version 3.16 -ml + * 2.05 Jun 8, 1995 -- Don't attempt to retry after an illegal request + * or data protect error. + * Use HWIF and DEV_HWIF macros as in ide.c. + * Always try to do a request_sense after + * a failed command. + * Include an option to give textual descriptions + * of ATAPI errors. + * Fix a bug in handling the sector cache which + * showed up if the drive returned data in 512 byte + * blocks (like Pioneer drives). Thanks to + * Richard Hirst for diagnosing this. + * Properly supply the page number field in the + * MODE_SELECT command. + * PLAYAUDIO12 is broken on the Aztech; work around it. + * 2.05x Aug 11, 1995 -- lots of data structure renaming/restructuring in ide.c + * (my apologies to Scott, but now ide-cd.c is independent) + * 3.00 Aug 22, 1995 -- Implement CDROMMULTISESSION ioctl. + * Implement CDROMREADAUDIO ioctl (UNTESTED). + * Use input_ide_data() and output_ide_data(). + * Add door locking. + * Fix usage count leak in cdrom_open, which happened + * when a read-write mount was attempted. + * Try to load the disk on open. + * Implement CDROMEJECT_SW ioctl (off by default). + * Read total cdrom capacity during open. + * Rearrange logic in cdrom_decode_status. Issue + * request sense commands for failed packet commands + * from here instead of from cdrom_queue_packet_command. + * Fix a race condition in retrieving error information. + * Suppress printing normal unit attention errors and + * some drive not ready errors. + * Implement CDROMVOLREAD ioctl. + * Implement CDROMREADMODE1/2 ioctls. + * Fix race condition in setting up interrupt handlers + * when the `serialize' option is used. + * 3.01 Sep 2, 1995 -- Fix ordering of reenabling interrupts in + * cdrom_queue_request. + * Another try at using ide_[input,output]_data. + * 3.02 Sep 16, 1995 -- Stick total disk capacity in partition table as well. + * Make VERBOSE_IDE_CD_ERRORS dump failed command again. + * Dump out more information for ILLEGAL REQUEST errs. + * Fix handling of errors occurring before the + * packet command is transferred. + * Fix transfers with odd bytelengths. + * 3.03 Oct 27, 1995 -- Some Creative drives have an id of just `CD'. + * `DCI-2S10' drives are broken too. + * 3.04 Nov 20, 1995 -- So are Vertos drives. + * 3.05 Dec 1, 1995 -- Changes to go with overhaul of ide.c and ide-tape.c + * 3.06 Dec 16, 1995 -- Add support needed for partitions. + * More workarounds for Vertos bugs (based on patches + * from Holger Dietze ). + * Try to eliminate byteorder assumptions. + * Use atapi_cdrom_subchnl struct definition. + * Add STANDARD_ATAPI compilation option. + * 3.07 Jan 29, 1996 -- More twiddling for broken drives: Sony 55D, + * Vertos 300. + * Add NO_DOOR_LOCKING configuration option. + * Handle drive_cmd requests w/NULL args (for hdparm -t). + * Work around sporadic Sony55e audio play problem. + * 3.07a Feb 11, 1996 -- check drive->id for NULL before dereferencing, to fix + * problem with "hde=cdrom" with no drive present. -ml + * 3.08 Mar 6, 1996 -- More Vertos workarounds. + * 3.09 Apr 5, 1996 -- Add CDROMCLOSETRAY ioctl. + * Switch to using MSF addressing for audio commands. + * Reformat to match kernel tabbing style. + * Add CDROM_GET_UPC ioctl. + * 3.10 Apr 10, 1996 -- Fix compilation error with STANDARD_ATAPI. + * 3.11 Apr 29, 1996 -- Patch from Heiko Eissfeldt + * to remove redundant verify_area calls. + * 3.12 May 7, 1996 -- Rudimentary changer support. Based on patches + * from Gerhard Zuber . + * Let open succeed even if there's no loaded disc. + * 3.13 May 19, 1996 -- Fixes for changer code. + * 3.14 May 29, 1996 -- Add work-around for Vertos 600. + * (From Hennus Bergman .) + * 3.15 July 2, 1996 -- Added support for Sanyo 3 CD changers + * from Ben Galliart with + * special help from Jeff Lightfoot + * + * 3.15a July 9, 1996 -- Improved Sanyo 3 CD changer identification + * 3.16 Jul 28, 1996 -- Fix from Gadi to reduce kernel stack usage for ioctl. + * 3.17 Sep 17, 1996 -- Tweak audio reads for some drives. + * Start changing CDROMLOADFROMSLOT to CDROM_SELECT_DISC. + * 3.18 Oct 31, 1996 -- Added module and DMA support. + * + * + * 4.00 Nov 5, 1996 -- New ide-cd maintainer, + * Erik B. Andersen + * -- Newer Creative drives don't always set the error + * register correctly. Make sure we see media changes + * regardless. + * -- Integrate with generic cdrom driver. + * -- CDROMGETSPINDOWN and CDROMSETSPINDOWN ioctls, based on + * a patch from Ciro Cattuto <>. + * -- Call set_device_ro. + * -- Implement CDROMMECHANISMSTATUS and CDROMSLOTTABLE + * ioctls, based on patch by Erik Andersen + * -- Add some probes of drive capability during setup. + * + * 4.01 Nov 11, 1996 -- Split into ide-cd.c and ide-cd.h + * -- Removed CDROMMECHANISMSTATUS and CDROMSLOTTABLE + * ioctls in favor of a generalized approach + * using the generic cdrom driver. + * -- Fully integrated with the 2.1.X kernel. + * -- Other stuff that I forgot (lots of changes) + * + * 4.02 Dec 01, 1996 -- Applied patch from Gadi Oxman + * to fix the drive door locking problems. + * + * 4.03 Dec 04, 1996 -- Added DSC overlap support. + * 4.04 Dec 29, 1996 -- Added CDROMREADRAW ioclt based on patch + * by Ales Makarov (xmakarov@sun.felk.cvut.cz) + * + * 4.05 Nov 20, 1997 -- Modified to print more drive info on init + * Minor other changes + * Fix errors on CDROMSTOP (If you have a "Dolphin", + * you must define IHAVEADOLPHIN) + * Added identifier so new Sanyo CD-changer works + * Better detection if door locking isn't supported + * + * 4.06 Dec 17, 1997 -- fixed endless "tray open" messages -ml + * 4.07 Dec 17, 1997 -- fallback to set pc->stat on "tray open" + * 4.08 Dec 18, 1997 -- spew less noise when tray is empty + * -- fix speed display for ACER 24X, 18X + * 4.09 Jan 04, 1998 -- fix handling of the last block so we return + * an end of file instead of an I/O error (Gadi) + * 4.10 Jan 24, 1998 -- fixed a bug so now changers can change to a new + * slot when there is no disc in the current slot. + * -- Fixed a memory leak where info->changer_info was + * malloc'ed but never free'd when closing the device. + * -- Cleaned up the global namespace a bit by making more + * functions static that should already have been. + * 4.11 Mar 12, 1998 -- Added support for the CDROM_SELECT_SPEED ioctl + * based on a patch for 2.0.33 by Jelle Foks + * , a patch for 2.0.33 + * by Toni Giorgino , the SCSI + * version, and my own efforts. -erik + * -- Fixed a stupid bug which egcs was kind enough to + * inform me of where "Illegal mode for this track" + * was never returned due to a comparison on data + * types of limited range. + * 4.12 Mar 29, 1998 -- Fixed bug in CDROM_SELECT_SPEED so write speed is + * now set ionly for CD-R and CD-RW drives. I had + * removed this support because it produced errors. + * It produced errors _only_ for non-writers. duh. + * 4.13 May 05, 1998 -- Suppress useless "in progress of becoming ready" + * messages, since this is not an error. + * -- Change error messages to be const + * -- Remove a "\t" which looks ugly in the syslogs + * 4.14 July 17, 1998 -- Change to pointing to .ps version of ATAPI spec + * since the .pdf version doesn't seem to work... + * -- Updated the TODO list to something more current. + * + * 4.15 Aug 25, 1998 -- Updated ide-cd.h to respect mechine endianess, + * patch thanks to "Eddie C. Dost" + * + * 4.50 Oct 19, 1998 -- New maintainers! + * Jens Axboe + * Chris Zwilling + * + * 4.51 Dec 23, 1998 -- Jens Axboe + * - ide_cdrom_reset enabled since the ide subsystem + * handles resets fine now. + * - Transfer size fix for Samsung CD-ROMs, thanks to + * "Ville Hallik" . + * - other minor stuff. + * + * 4.52 Jan 19, 1999 -- Jens Axboe + * - Detect DVD-ROM/RAM drives + * + * 4.53 Feb 22, 1999 - Include other model Samsung and one Goldstar + * drive in transfer size limit. + * - Fix the I/O error when doing eject without a medium + * loaded on some drives. + * - CDROMREADMODE2 is now implemented through + * CDROMREADRAW, since many drives don't support + * MODE2 (even though ATAPI 2.6 says they must). + * - Added ignore parameter to ide-cd (as a module), eg + * insmod ide-cd ignore='hda hdb' + * Useful when using ide-cd in conjunction with + * ide-scsi. TODO: non-modular way of doing the + * same. + * + * 4.54 Aug 5, 1999 - Support for MMC2 class commands through the generic + * packet interface to cdrom.c. + * - Unified audio ioctl support, most of it. + * - cleaned up various deprecated verify_area(). + * - Added ide_cdrom_packet() as the interface for + * the Uniform generic_packet(). + * - bunch of other stuff, will fill in logs later. + * - report 1 slot for non-changers, like the other + * cd-rom drivers. don't report select disc for + * non-changers as well. + * - mask out audio playing, if the device can't do it. + * + * 4.55 Sep 1, 1999 - Eliminated the rest of the audio ioctls, except + * for CDROMREADTOC[ENTRY|HEADER]. Some of the drivers + * use this independently of the actual audio handling. + * They will disappear later when I get the time to + * do it cleanly. + * - Minimize the TOC reading - only do it when we + * know a media change has occurred. + * - Moved all the CDROMREADx ioctls to the Uniform layer. + * - Heiko Eissfeldt supplied + * some fixes for CDI. + * - CD-ROM leaving door locked fix from Andries + * Brouwer + * - Erik Andersen unified + * commands across the various drivers and how + * sense errors are handled. + * + * 4.56 Sep 12, 1999 - Removed changer support - it is now in the + * Uniform layer. + * - Added partition based multisession handling. + * - Mode sense and mode select moved to the + * Uniform layer. + * - Fixed a problem with WPI CDS-32X drive - it + * failed the capabilities + * + * 4.57 Apr 7, 2000 - Fixed sense reporting. + * - Fixed possible oops in ide_cdrom_get_last_session() + * - Fix locking mania and make ide_cdrom_reset relock + * - Stop spewing errors to log when magicdev polls with + * TEST_UNIT_READY on some drives. + * - Various fixes from Tobias Ringstrom: + * tray if it was locked prior to the reset. + * - cdrom_read_capacity returns one frame too little. + * - Fix real capacity reporting. + * + * 4.58 May 1, 2000 - Clean up ACER50 stuff. + * - Fix small problem with ide_cdrom_capacity + * + * 4.59 Aug 11, 2000 - Fix changer problem in cdrom_read_toc, we weren't + * correctly sensing a disc change. + * - Rearranged some code + * - Use extended sense on drives that support it for + * correctly reporting tray status -- from + * Michael D Johnson + * + *************************************************************************/ + +#define IDECD_VERSION "4.59" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ide-cd.h" + +/**************************************************************************** + * Generic packet command support and error handling routines. + */ + +/* Mark that we've seen a media change, and invalidate our internal + buffers. */ +static void cdrom_saw_media_change (ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + + CDROM_STATE_FLAGS (drive)->media_changed = 1; + CDROM_STATE_FLAGS (drive)->toc_valid = 0; + info->nsectors_buffered = 0; +} + +static int cdrom_log_sense(ide_drive_t *drive, struct packet_command *pc, + struct request_sense *sense) +{ + int log = 0; + + if (sense == NULL || pc == NULL || pc->quiet) + return 0; + + switch (sense->sense_key) { + case NO_SENSE: case RECOVERED_ERROR: + break; + case NOT_READY: + /* + * don't care about tray state messages for + * e.g. capacity commands or in-progress or + * becoming ready + */ + if (sense->asc == 0x3a || sense->asc == 0x04) + break; + log = 1; + break; + case UNIT_ATTENTION: + /* + * Make good and sure we've seen this potential media + * change. Some drives (i.e. Creative) fail to present + * the correct sense key in the error register. + */ + cdrom_saw_media_change(drive); + break; + default: + log = 1; + break; + } + return log; +} + +static +void cdrom_analyze_sense_data(ide_drive_t *drive, + struct packet_command *failed_command, + struct request_sense *sense) +{ + + if (!cdrom_log_sense(drive, failed_command, sense)) + return; + + /* + * If a read toc is executed for a CD-R or CD-RW medium where + * the first toc has not been recorded yet, it will fail with + * 05/24/00 (which is a confusing error) + */ + if (failed_command && failed_command->c[0] == GPCMD_READ_TOC_PMA_ATIP) + if (sense->sense_key == 0x05 && sense->asc == 0x24) + return; + +#if VERBOSE_IDE_CD_ERRORS + { + int i; + const char *s; + char buf[80]; + + printk ("ATAPI device %s:\n", drive->name); + if (sense->error_code==0x70) + printk(" Error: "); + else if (sense->error_code==0x71) + printk(" Deferred Error: "); + else if (sense->error_code == 0x7f) + printk(" Vendor-specific Error: "); + else + printk(" Unknown Error Type: "); + + if (sense->sense_key < ARY_LEN(sense_key_texts)) + s = sense_key_texts[sense->sense_key]; + else + s = "bad sense key!"; + + printk("%s -- (Sense key=0x%02x)\n", s, sense->sense_key); + + if (sense->asc == 0x40) { + sprintf(buf, "Diagnostic failure on component 0x%02x", + sense->ascq); + s = buf; + } else { + int lo = 0, mid, hi = ARY_LEN(sense_data_texts); + unsigned long key = (sense->sense_key << 16); + key |= (sense->asc << 8); + if (!(sense->ascq >= 0x80 && sense->ascq <= 0xdd)) + key |= sense->ascq; + s = NULL; + + while (hi > lo) { + mid = (lo + hi) / 2; + if (sense_data_texts[mid].asc_ascq == key || + sense_data_texts[mid].asc_ascq == (0xff0000|key)) { + s = sense_data_texts[mid].text; + break; + } + else if (sense_data_texts[mid].asc_ascq > key) + hi = mid; + else + lo = mid+1; + } + } + + if (s == NULL) { + if (sense->asc > 0x80) + s = "(vendor-specific error)"; + else + s = "(reserved error code)"; + } + + printk(" %s -- (asc=0x%02x, ascq=0x%02x)\n", + s, sense->asc, sense->ascq); + + if (failed_command != NULL) { + + int lo=0, mid, hi= ARY_LEN (packet_command_texts); + s = NULL; + + while (hi > lo) { + mid = (lo + hi) / 2; + if (packet_command_texts[mid].packet_command == + failed_command->c[0]) { + s = packet_command_texts[mid].text; + break; + } + if (packet_command_texts[mid].packet_command > + failed_command->c[0]) + hi = mid; + else + lo = mid+1; + } + + printk (" The failed \"%s\" packet command was: \n \"", s); + for (i=0; ic); i++) + printk ("%02x ", failed_command->c[i]); + printk ("\"\n"); + } + + /* The SKSV bit specifies validity of the sense_key_specific + * in the next two commands. It is bit 7 of the first byte. + * In the case of NOT_READY, if SKSV is set the drive can + * give us nice ETA readings. + */ + if (sense->sense_key == NOT_READY && (sense->sks[0] & 0x80)) { + int progress = (sense->sks[1] << 8 | sense->sks[2]) * 100; + printk(" Command is %02d%% complete\n", progress / 0xffff); + + } + + if (sense->sense_key == ILLEGAL_REQUEST && + (sense->sks[0] & 0x80) != 0) { + printk(" Error in %s byte %d", + (sense->sks[0] & 0x40) != 0 ? + "command packet" : "command data", + (sense->sks[1] << 8) + sense->sks[2]); + + if ((sense->sks[0] & 0x40) != 0) + printk (" bit %d", sense->sks[0] & 0x07); + + printk ("\n"); + } + } + +#else /* not VERBOSE_IDE_CD_ERRORS */ + + /* Suppress printing unit attention and `in progress of becoming ready' + errors when we're not being verbose. */ + + if (sense->sense_key == UNIT_ATTENTION || + (sense->sense_key == NOT_READY && (sense->asc == 4 || + sense->asc == 0x3a))) + return; + + printk("%s: error code: 0x%02x sense_key: 0x%02x asc: 0x%02x ascq: 0x%02x\n", + drive->name, + sense->error_code, sense->sense_key, + sense->asc, sense->ascq); +#endif /* not VERBOSE_IDE_CD_ERRORS */ +} + +static void cdrom_queue_request_sense(ide_drive_t *drive, + struct completion *wait, + struct request_sense *sense, + struct packet_command *failed_command) +{ + struct cdrom_info *info = drive->driver_data; + struct packet_command *pc = &info->request_sense_pc; + struct request *rq; + + if (sense == NULL) + sense = &info->sense_data; + + memset(pc, 0, sizeof(struct packet_command)); + pc->c[0] = GPCMD_REQUEST_SENSE; + pc->c[4] = pc->buflen = 18; + pc->buffer = (char *) sense; + pc->sense = (struct request_sense *) failed_command; + + /* stuff the sense request in front of our current request */ + rq = &info->request_sense_request; + ide_init_drive_cmd(rq); + rq->flags = REQ_SENSE; + rq->buffer = (char *) pc; + rq->waiting = wait; + (void) ide_do_drive_cmd(drive, rq, ide_preempt); +} + + +/* + * This is our end_request replacement function. + */ +static int ide_cdrom_end_request (ide_drive_t *drive, int uptodate) +{ + struct request *rq; + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&ide_lock, flags); + rq = HWGROUP(drive)->rq; + + /* + * decide whether to reenable DMA -- 3 is a random magic for now, + * if we DMA timeout more than 3 times, just stay in PIO + */ + if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) { + drive->state = 0; + HWGROUP(drive)->hwif->dmaproc(ide_dma_on, drive); + } + + if (!end_that_request_first(rq, uptodate, rq->hard_cur_sectors)) { + add_blkdev_randomness(major(rq->rq_dev)); + blkdev_dequeue_request(rq); + HWGROUP(drive)->rq = NULL; + end_that_request_last(rq); + ret = 0; + } + spin_unlock_irqrestore(&ide_lock, flags); + return ret; +} + +/* + * Error reporting, in human readable form (luxurious, but a memory hog). + */ +byte ide_cdrom_dump_status (ide_drive_t *drive, const char *msg, byte stat) +{ + unsigned long flags; + byte err = 0; + + local_irq_set(flags); + printk("%s: %s: status=0x%02x", drive->name, msg, stat); +#if FANCY_STATUS_DUMPS + printk(" { "); + if (stat & BUSY_STAT) + printk("Busy "); + else { + if (stat & READY_STAT) printk("DriveReady "); + if (stat & WRERR_STAT) printk("DeviceFault "); + if (stat & SEEK_STAT) printk("SeekComplete "); + if (stat & DRQ_STAT) printk("DataRequest "); + if (stat & ECC_STAT) printk("CorrectedError "); + if (stat & INDEX_STAT) printk("Index "); + if (stat & ERR_STAT) printk("Error "); + } + printk("}"); +#endif /* FANCY_STATUS_DUMPS */ + printk("\n"); + if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) { + err = GET_ERR(); + printk("%s: %s: error=0x%02x", drive->name, msg, err); +#if FANCY_STATUS_DUMPS +#endif /* FANCY_STATUS_DUMPS */ + printk("\n"); + } + local_irq_restore(flags); + return err; +} + +/* + * ide_error() takes action based on the error returned by the drive. + */ +ide_startstop_t ide_cdrom_error (ide_drive_t *drive, const char *msg, byte stat) +{ + struct request *rq; + byte err; + + err = ide_cdrom_dump_status(drive, msg, stat); + if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) + return ide_stopped; + /* retry only "normal" I/O: */ + if ((rq->flags & REQ_DRIVE_CMD) || (rq->flags & REQ_DRIVE_TASK)) { + rq->errors = 1; + ide_end_drive_cmd(drive, stat, err); + return ide_stopped; + } + + if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { + /* other bits are useless when BUSY */ + rq->errors |= ERROR_RESET; + } + if (GET_STAT() & (BUSY_STAT|DRQ_STAT)) + /* force an abort */ + OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); + if (rq->errors >= ERROR_MAX) { + DRIVER(drive)->end_request(drive, 0); + } else { + if ((rq->errors & ERROR_RESET) == ERROR_RESET) { + ++rq->errors; + return ide_do_reset(drive); + } + ++rq->errors; + } + return ide_stopped; +} + +static void cdrom_end_request (ide_drive_t *drive, int uptodate) +{ + struct request *rq = HWGROUP(drive)->rq; + + if ((rq->flags & REQ_SENSE) && uptodate) { + struct packet_command *pc = (struct packet_command *) rq->buffer; + cdrom_analyze_sense_data(drive, + (struct packet_command *) pc->sense, + (struct request_sense *) (pc->buffer - pc->c[4])); + } + if ((rq->flags & REQ_CMD) && !rq->current_nr_sectors) + uptodate = 1; + + ide_cdrom_end_request(drive, uptodate); +} + + +/* Returns 0 if the request should be continued. + Returns 1 if the request was ended. */ +static int cdrom_decode_status (ide_startstop_t *startstop, ide_drive_t *drive, + int good_stat, int *stat_ret) +{ + struct request *rq = HWGROUP(drive)->rq; + int stat, err, sense_key; + struct packet_command *pc; + + /* Check for errors. */ + stat = GET_STAT(); + *stat_ret = stat; + + if (OK_STAT (stat, good_stat, BAD_R_STAT)) + return 0; + + /* Get the IDE error register. */ + err = GET_ERR(); + sense_key = err >> 4; + + if (rq == NULL) { + printk("%s: missing rq in cdrom_decode_status\n", drive->name); + *startstop = ide_stopped; + return 1; + } + + if (rq->flags & REQ_SENSE) { + /* We got an error trying to get sense info + from the drive (probably while trying + to recover from a former error). Just give up. */ + + pc = (struct packet_command *) rq->buffer; + pc->stat = 1; + cdrom_end_request(drive, 1); + *startstop = DRIVER(drive)->error(drive, "request sense failure", stat); + return 1; + + } else if (rq->flags & REQ_PC) { + /* All other functions, except for READ. */ + struct completion *wait = NULL; + pc = (struct packet_command *) rq->buffer; + + /* Check for tray open. */ + if (sense_key == NOT_READY) { + cdrom_saw_media_change (drive); + } else if (sense_key == UNIT_ATTENTION) { + /* Check for media change. */ + cdrom_saw_media_change (drive); + /*printk("%s: media changed\n",drive->name);*/ + return 0; + } else if (!pc->quiet) { + /* Otherwise, print an error. */ + ide_dump_status(drive, "packet command error", stat); + } + + /* Set the error flag and complete the request. + Then, if we have a CHECK CONDITION status, + queue a request sense command. We must be careful, + though: we don't want the thread in + cdrom_queue_packet_command to wake up until + the request sense has completed. We do this + by transferring the semaphore from the packet + command request to the request sense request. */ + + if ((stat & ERR_STAT) != 0) { + wait = rq->waiting; + rq->waiting = NULL; + } + + pc->stat = 1; + cdrom_end_request(drive, 1); + + if ((stat & ERR_STAT) != 0) + cdrom_queue_request_sense(drive, wait, pc->sense, pc); + } else if (rq->flags & REQ_CMD) { + /* Handle errors from READ and WRITE requests. */ + + if (sense_key == NOT_READY) { + /* Tray open. */ + cdrom_saw_media_change (drive); + + /* Fail the request. */ + printk ("%s: tray open\n", drive->name); + cdrom_end_request(drive, 0); + } else if (sense_key == UNIT_ATTENTION) { + /* Media change. */ + cdrom_saw_media_change (drive); + + /* Arrange to retry the request. + But be sure to give up if we've retried + too many times. */ + if (++rq->errors > ERROR_MAX) + cdrom_end_request(drive, 0); + } else if (sense_key == ILLEGAL_REQUEST || + sense_key == DATA_PROTECT) { + /* No point in retrying after an illegal + request or data protect error.*/ + ide_dump_status (drive, "command error", stat); + cdrom_end_request(drive, 0); + } else if ((err & ~ABRT_ERR) != 0) { + /* Go to the default handler + for other errors. */ + *startstop = DRIVER(drive)->error(drive, "cdrom_decode_status", stat); + return 1; + } else if ((++rq->errors > ERROR_MAX)) { + /* We've racked up too many retries. Abort. */ + cdrom_end_request(drive, 0); + } + + /* If we got a CHECK_CONDITION status, + queue a request sense command. */ + if ((stat & ERR_STAT) != 0) + cdrom_queue_request_sense(drive, NULL, NULL, NULL); + } else + blk_dump_rq_flags(rq, "ide-cd bad flags"); + + /* Retry, or handle the next request. */ + *startstop = ide_stopped; + return 1; +} + +static int cdrom_timer_expiry(ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + struct packet_command *pc = (struct packet_command *) rq->buffer; + unsigned long wait = 0; + + /* + * Some commands are *slow* and normally take a long time to + * complete. Usually we can use the ATAPI "disconnect" to bypass + * this, but not all commands/drives support that. Let + * ide_timer_expiry keep polling us for these. + */ + switch (pc->c[0]) { + case GPCMD_BLANK: + case GPCMD_FORMAT_UNIT: + case GPCMD_RESERVE_RZONE_TRACK: + wait = WAIT_CMD; + break; + default: + wait = 0; + break; + } + return wait; +} + +/* Set up the device registers for transferring a packet command on DEV, + expecting to later transfer XFERLEN bytes. HANDLER is the routine + which actually transfers the command to the drive. If this is a + drq_interrupt device, this routine will arrange for HANDLER to be + called when the interrupt from the drive arrives. Otherwise, HANDLER + will be called immediately after the drive is prepared for the transfer. */ + +static ide_startstop_t cdrom_start_packet_command(ide_drive_t *drive, + int xferlen, + ide_handler_t *handler) +{ + ide_startstop_t startstop; + struct cdrom_info *info = drive->driver_data; + + /* Wait for the controller to be idle. */ + if (ide_wait_stat(&startstop, drive, 0, BUSY_STAT, WAIT_READY)) + return startstop; + + if (info->dma) { + if (info->cmd == READ) { + info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); + } else if (info->cmd == WRITE) { + info->dma = !HWIF(drive)->dmaproc(ide_dma_write, drive); + } else { + printk("ide-cd: DMA set, but not allowed\n"); + } + } + + /* Set up the controller registers. */ + OUT_BYTE(info->dma, IDE_FEATURE_REG); + OUT_BYTE(0, IDE_NSECTOR_REG); + OUT_BYTE(0, IDE_SECTOR_REG); + + OUT_BYTE(xferlen & 0xff, IDE_LCYL_REG); + OUT_BYTE(xferlen >> 8 , IDE_HCYL_REG); + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl, IDE_CONTROL_REG); + + if (info->dma) + (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); + + if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry); + OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ + return ide_started; + } else { + OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ + return (*handler) (drive); + } +} + +/* Send a packet command to DRIVE described by CMD_BUF and CMD_LEN. + The device registers must have already been prepared + by cdrom_start_packet_command. + HANDLER is the interrupt handler to call when the command completes + or there's data ready. */ +/* + * changed 5 parameters to 3 for dvd-ram + * struct packet_command *pc; now packet_command_t *pc; + */ +static ide_startstop_t cdrom_transfer_packet_command (ide_drive_t *drive, + struct packet_command *pc, + ide_handler_t *handler) +{ + unsigned char *cmd_buf = pc->c; + int cmd_len = sizeof(pc->c); + unsigned int timeout = pc->timeout; + ide_startstop_t startstop; + + if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { + /* Here we should have been called after receiving an interrupt + from the device. DRQ should how be set. */ + int stat_dum; + + /* Check for errors. */ + if (cdrom_decode_status (&startstop, drive, DRQ_STAT, &stat_dum)) + return startstop; + } else { + /* Otherwise, we must wait for DRQ to get set. */ + if (ide_wait_stat (&startstop, drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) + return startstop; + } + + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + + /* Arm the interrupt handler. */ + ide_set_handler (drive, handler, timeout, cdrom_timer_expiry); + + /* Send the command to the device. */ + atapi_output_bytes (drive, cmd_buf, cmd_len); + return ide_started; +} + +/**************************************************************************** + * Block read functions. + */ + +/* + * Buffer up to SECTORS_TO_TRANSFER sectors from the drive in our sector + * buffer. Once the first sector is added, any subsequent sectors are + * assumed to be continuous (until the buffer is cleared). For the first + * sector added, SECTOR is its sector number. (SECTOR is then ignored until + * the buffer is cleared.) + */ +static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector, + int sectors_to_transfer) +{ + struct cdrom_info *info = drive->driver_data; + + /* Number of sectors to read into the buffer. */ + int sectors_to_buffer = MIN (sectors_to_transfer, + (SECTOR_BUFFER_SIZE >> SECTOR_BITS) - + info->nsectors_buffered); + + char *dest; + + /* If we couldn't get a buffer, don't try to buffer anything... */ + if (info->buffer == NULL) + sectors_to_buffer = 0; + + /* If this is the first sector in the buffer, remember its number. */ + if (info->nsectors_buffered == 0) + info->sector_buffered = sector; + + /* Read the data into the buffer. */ + dest = info->buffer + info->nsectors_buffered * SECTOR_SIZE; + while (sectors_to_buffer > 0) { + atapi_input_bytes (drive, dest, SECTOR_SIZE); + --sectors_to_buffer; + --sectors_to_transfer; + ++info->nsectors_buffered; + dest += SECTOR_SIZE; + } + + /* Throw away any remaining data. */ + while (sectors_to_transfer > 0) { + char dum[SECTOR_SIZE]; + atapi_input_bytes (drive, dum, sizeof (dum)); + --sectors_to_transfer; + } +} + +/* + * Check the contents of the interrupt reason register from the cdrom + * and attempt to recover if there are problems. Returns 0 if everything's + * ok; nonzero if the request has been terminated. + */ +static inline +int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason) +{ + ireason &= 3; + if (ireason == 2) return 0; + + if (ireason == 0) { + /* Whoops... The drive is expecting to receive data from us! */ + printk ("%s: cdrom_read_intr: " + "Drive wants to transfer data the wrong way!\n", + drive->name); + + /* Throw some data at the drive so it doesn't hang + and quit this request. */ + while (len > 0) { + int dum = 0; + atapi_output_bytes (drive, &dum, sizeof (dum)); + len -= sizeof (dum); + } + } else if (ireason == 1) { + /* Some drives (ASUS) seem to tell us that status + * info is available. just get it and ignore. + */ + GET_STAT(); + return 0; + } else { + /* Drive wants a command packet, or invalid ireason... */ + printk ("%s: cdrom_read_intr: bad interrupt reason %d\n", + drive->name, ireason); + } + + cdrom_end_request(drive, 0); + return -1; +} + +/* + * Interrupt routine. Called when a read request has completed. + */ +static ide_startstop_t cdrom_read_intr (ide_drive_t *drive) +{ + int stat; + int ireason, len, sectors_to_transfer, nskip; + struct cdrom_info *info = drive->driver_data; + int i, dma = info->dma, dma_error = 0; + ide_startstop_t startstop; + + struct request *rq = HWGROUP(drive)->rq; + + /* Check for errors. */ + if (dma) { + info->dma = 0; + if ((dma_error = HWIF(drive)->dmaproc(ide_dma_end, drive))) + HWIF(drive)->dmaproc(ide_dma_off, drive); + } + + if (cdrom_decode_status (&startstop, drive, 0, &stat)) + return startstop; + + if (dma) { + if (!dma_error) { + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_cdrom_end_request(drive, 1); + } + return ide_stopped; + } else + return DRIVER(drive)->error(drive, "dma error", stat); + } + + /* Read the interrupt reason and the transfer length. */ + ireason = IN_BYTE(IDE_NSECTOR_REG); + len = IN_BYTE(IDE_LCYL_REG) + 256 * IN_BYTE(IDE_HCYL_REG); + + /* If DRQ is clear, the command has completed. */ + if ((stat & DRQ_STAT) == 0) { + /* If we're not done filling the current buffer, complain. + Otherwise, complete the command normally. */ + if (rq->current_nr_sectors > 0) { + printk ("%s: cdrom_read_intr: data underrun (%d blocks)\n", + drive->name, rq->current_nr_sectors); + cdrom_end_request(drive, 0); + } else + cdrom_end_request(drive, 1); + return ide_stopped; + } + + /* Check that the drive is expecting to do the same thing we are. */ + if (cdrom_read_check_ireason (drive, len, ireason)) + return ide_stopped; + + /* Assume that the drive will always provide data in multiples + of at least SECTOR_SIZE, as it gets hairy to keep track + of the transfers otherwise. */ + if ((len % SECTOR_SIZE) != 0) { + printk ("%s: cdrom_read_intr: Bad transfer size %d\n", + drive->name, len); + if (CDROM_CONFIG_FLAGS (drive)->limit_nframes) + printk (" This drive is not supported by this version of the driver\n"); + else { + printk (" Trying to limit transfer sizes\n"); + CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1; + } + cdrom_end_request(drive, 0); + return ide_stopped; + } + + /* The number of sectors we need to read from the drive. */ + sectors_to_transfer = len / SECTOR_SIZE; + + /* First, figure out if we need to bit-bucket + any of the leading sectors. */ + nskip = MIN((int)(rq->current_nr_sectors - bio_sectors(rq->bio)), sectors_to_transfer); + + while (nskip > 0) { + /* We need to throw away a sector. */ + char dum[SECTOR_SIZE]; + atapi_input_bytes (drive, dum, sizeof (dum)); + + --rq->current_nr_sectors; + --nskip; + --sectors_to_transfer; + } + + /* Now loop while we still have data to read from the drive. */ + while (sectors_to_transfer > 0) { + int this_transfer; + + /* If we've filled the present buffer but there's another + chained buffer after it, move on. */ + if (rq->current_nr_sectors == 0 && rq->nr_sectors) + cdrom_end_request(drive, 1); + + /* If the buffers are full, cache the rest of the data in our + internal buffer. */ + if (rq->current_nr_sectors == 0) { + cdrom_buffer_sectors(drive, rq->sector, sectors_to_transfer); + sectors_to_transfer = 0; + } else { + /* Transfer data to the buffers. + Figure out how many sectors we can transfer + to the current buffer. */ + this_transfer = MIN (sectors_to_transfer, + rq->current_nr_sectors); + + /* Read this_transfer sectors + into the current buffer. */ + while (this_transfer > 0) { + atapi_input_bytes(drive, rq->buffer, SECTOR_SIZE); + rq->buffer += SECTOR_SIZE; + --rq->nr_sectors; + --rq->current_nr_sectors; + ++rq->sector; + --this_transfer; + --sectors_to_transfer; + } + } + } + + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + + /* Done moving data! Wait for another interrupt. */ + ide_set_handler(drive, &cdrom_read_intr, WAIT_CMD, NULL); + return ide_started; +} + +/* + * Try to satisfy some of the current read request from our cached data. + * Returns nonzero if the request has been completed, zero otherwise. + */ +static int cdrom_read_from_buffer (ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + struct request *rq = HWGROUP(drive)->rq; + + /* Can't do anything if there's no buffer. */ + if (info->buffer == NULL) return 0; + + /* Loop while this request needs data and the next block is present + in our cache. */ + while (rq->nr_sectors > 0 && + rq->sector >= info->sector_buffered && + rq->sector < info->sector_buffered + info->nsectors_buffered) { + if (rq->current_nr_sectors == 0) + cdrom_end_request(drive, 1); + + memcpy (rq->buffer, + info->buffer + + (rq->sector - info->sector_buffered) * SECTOR_SIZE, + SECTOR_SIZE); + rq->buffer += SECTOR_SIZE; + --rq->current_nr_sectors; + --rq->nr_sectors; + ++rq->sector; + } + + /* If we've satisfied the current request, + terminate it successfully. */ + if (rq->nr_sectors == 0) { + cdrom_end_request(drive, 1); + return -1; + } + + /* Move on to the next buffer if needed. */ + if (rq->current_nr_sectors == 0) + cdrom_end_request(drive, 1); + + /* If this condition does not hold, then the kluge i use to + represent the number of sectors to skip at the start of a transfer + will fail. I think that this will never happen, but let's be + paranoid and check. */ + if (rq->current_nr_sectors < bio_sectors(rq->bio) && + (rq->sector % SECTORS_PER_FRAME) != 0) { + printk ("%s: cdrom_read_from_buffer: buffer botch (%ld)\n", + drive->name, rq->sector); + cdrom_end_request(drive, 0); + return -1; + } + + return 0; +} + +/* + * Routine to send a read packet command to the drive. + * This is usually called directly from cdrom_start_read. + * However, for drq_interrupt devices, it is called from an interrupt + * when the drive is ready to accept the command. + */ +static ide_startstop_t cdrom_start_read_continuation (ide_drive_t *drive) +{ + struct packet_command pc; + struct request *rq = HWGROUP(drive)->rq; + int nsect, sector, nframes, frame, nskip; + + /* Number of sectors to transfer. */ + nsect = rq->nr_sectors; + + /* Starting sector. */ + sector = rq->sector; + + /* If the requested sector doesn't start on a cdrom block boundary, + we must adjust the start of the transfer so that it does, + and remember to skip the first few sectors. + If the CURRENT_NR_SECTORS field is larger than the size + of the buffer, it will mean that we're to skip a number + of sectors equal to the amount by which CURRENT_NR_SECTORS + is larger than the buffer size. */ + nskip = (sector % SECTORS_PER_FRAME); + if (nskip > 0) { + /* Sanity check... */ + if (rq->current_nr_sectors != bio_sectors(rq->bio) && + (rq->sector % CD_FRAMESIZE != 0)) { + printk ("%s: cdrom_start_read_continuation: buffer botch (%u)\n", + drive->name, rq->current_nr_sectors); + cdrom_end_request(drive, 0); + return ide_stopped; + } + sector -= nskip; + nsect += nskip; + rq->current_nr_sectors += nskip; + } + + /* Convert from sectors to cdrom blocks, rounding up the transfer + length if needed. */ + nframes = (nsect + SECTORS_PER_FRAME-1) / SECTORS_PER_FRAME; + frame = sector / SECTORS_PER_FRAME; + + /* Largest number of frames was can transfer at once is 64k-1. For + some drives we need to limit this even more. */ + nframes = MIN (nframes, (CDROM_CONFIG_FLAGS (drive)->limit_nframes) ? + (65534 / CD_FRAMESIZE) : 65535); + + /* Set up the command */ + memcpy(pc.c, rq->cmd, sizeof(pc.c)); + pc.timeout = WAIT_CMD; + + /* Send the command to the drive and return. */ + return cdrom_transfer_packet_command(drive, &pc, &cdrom_read_intr); +} + + +#define IDECD_SEEK_THRESHOLD (1000) /* 1000 blocks */ +#define IDECD_SEEK_TIMER (5 * WAIT_MIN_SLEEP) /* 100 ms */ +#define IDECD_SEEK_TIMEOUT WAIT_CMD /* 10 sec */ + +static ide_startstop_t cdrom_seek_intr (ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + int stat; + static int retry = 10; + ide_startstop_t startstop; + + if (cdrom_decode_status (&startstop, drive, 0, &stat)) + return startstop; + CDROM_CONFIG_FLAGS(drive)->seeking = 1; + + if (retry && time_after(jiffies, info->start_seek + IDECD_SEEK_TIMER)) { + if (--retry == 0) { + /* + * this condition is far too common, to bother + * users about it + */ +#if 0 + printk("%s: disabled DSC seek overlap\n", drive->name); +#endif + drive->dsc_overlap = 0; + } + } + return ide_stopped; +} + +static ide_startstop_t cdrom_start_seek_continuation (ide_drive_t *drive) +{ + struct packet_command pc; + struct request *rq = HWGROUP(drive)->rq; + int sector, frame, nskip; + + sector = rq->sector; + nskip = (sector % SECTORS_PER_FRAME); + if (nskip > 0) + sector -= nskip; + frame = sector / SECTORS_PER_FRAME; + + memset(rq->cmd, 0, sizeof(rq->cmd)); + pc.c[0] = GPCMD_SEEK; + put_unaligned(cpu_to_be32(frame), (unsigned int *) &pc.c[2]); + + pc.timeout = WAIT_CMD; + return cdrom_transfer_packet_command(drive, &pc, &cdrom_seek_intr); +} + +static ide_startstop_t cdrom_start_seek (ide_drive_t *drive, unsigned int block) +{ + struct cdrom_info *info = drive->driver_data; + + info->dma = 0; + info->cmd = 0; + info->start_seek = jiffies; + return cdrom_start_packet_command (drive, 0, cdrom_start_seek_continuation); +} + +/* Fix up a possibly partially-processed request so that we can + start it over entirely, or even put it back on the request queue. */ +static void restore_request (struct request *rq) +{ + if (rq->buffer != bio_data(rq->bio)) { + sector_t n = (rq->buffer - (char *) bio_data(rq->bio)) / SECTOR_SIZE; + rq->buffer = bio_data(rq->bio); + rq->nr_sectors += n; + rq->sector -= n; + } + rq->hard_cur_sectors = rq->current_nr_sectors = bio_sectors(rq->bio); + rq->hard_nr_sectors = rq->nr_sectors; + rq->hard_sector = rq->sector; + rq->q->prep_rq_fn(rq->q, rq); +} + +/* + * Start a read request from the CD-ROM. + */ +static ide_startstop_t cdrom_start_read (ide_drive_t *drive, unsigned int block) +{ + struct cdrom_info *info = drive->driver_data; + struct request *rq = HWGROUP(drive)->rq; + + /* We may be retrying this request after an error. Fix up + any weirdness which might be present in the request packet. */ + restore_request(rq); + + /* Satisfy whatever we can of this request from our cached sector. */ + if (cdrom_read_from_buffer(drive)) + return ide_stopped; + + blk_attempt_remerge(&drive->queue, rq); + + /* Clear the local sector buffer. */ + info->nsectors_buffered = 0; + + /* use dma, if possible. */ + if (drive->using_dma && (rq->sector % SECTORS_PER_FRAME == 0) && + (rq->nr_sectors % SECTORS_PER_FRAME == 0)) + info->dma = 1; + else + info->dma = 0; + + info->cmd = READ; + /* Start sending the read request to the drive. */ + return cdrom_start_packet_command(drive, 32768, cdrom_start_read_continuation); +} + +/**************************************************************************** + * Execute all other packet commands. + */ + +/* Forward declarations. */ +static int cdrom_lockdoor(ide_drive_t *drive, int lockflag, + struct request_sense *sense); + +/* Interrupt routine for packet command completion. */ +static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive) +{ + int ireason, len, stat, thislen; + struct request *rq = HWGROUP(drive)->rq; + struct packet_command *pc = (struct packet_command *)rq->buffer; + ide_startstop_t startstop; + + /* Check for errors. */ + if (cdrom_decode_status (&startstop, drive, 0, &stat)) + return startstop; + + /* Read the interrupt reason and the transfer length. */ + ireason = IN_BYTE (IDE_NSECTOR_REG); + len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG); + + /* If DRQ is clear, the command has completed. + Complain if we still have data left to transfer. */ + if ((stat & DRQ_STAT) == 0) { + /* Some of the trailing request sense fields are optional, and + some drives don't send them. Sigh. */ + if (pc->c[0] == GPCMD_REQUEST_SENSE && + pc->buflen > 0 && + pc->buflen <= 5) { + while (pc->buflen > 0) { + *pc->buffer++ = 0; + --pc->buflen; + } + } + + if (pc->buflen == 0) + cdrom_end_request(drive, 1); + else { + /* Comment this out, because this always happens + right after a reset occurs, and it is annoying to + always print expected stuff. */ + /* + printk ("%s: cdrom_pc_intr: data underrun %d\n", + drive->name, pc->buflen); + */ + pc->stat = 1; + cdrom_end_request(drive, 1); + } + return ide_stopped; + } + + /* Figure out how much data to transfer. */ + thislen = pc->buflen; + if (thislen > len) thislen = len; + + /* The drive wants to be written to. */ + if ((ireason & 3) == 0) { + /* Transfer the data. */ + atapi_output_bytes (drive, pc->buffer, thislen); + + /* If we haven't moved enough data to satisfy the drive, + add some padding. */ + while (len > thislen) { + int dum = 0; + atapi_output_bytes (drive, &dum, sizeof (dum)); + len -= sizeof (dum); + } + + /* Keep count of how much data we've moved. */ + pc->buffer += thislen; + pc->buflen -= thislen; + } + + /* Same drill for reading. */ + else if ((ireason & 3) == 2) { + + /* Transfer the data. */ + atapi_input_bytes (drive, pc->buffer, thislen); + + /* If we haven't moved enough data to satisfy the drive, + add some padding. */ + while (len > thislen) { + int dum = 0; + atapi_input_bytes (drive, &dum, sizeof (dum)); + len -= sizeof (dum); + } + + /* Keep count of how much data we've moved. */ + pc->buffer += thislen; + pc->buflen -= thislen; + } else { + printk ("%s: cdrom_pc_intr: The drive " + "appears confused (ireason = 0x%2x)\n", + drive->name, ireason); + pc->stat = 1; + } + + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + + /* Now we wait for another interrupt. */ + ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD, cdrom_timer_expiry); + return ide_started; +} + + +static ide_startstop_t cdrom_do_pc_continuation (ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + struct packet_command *pc = (struct packet_command *)rq->buffer; + + if (!pc->timeout) + pc->timeout = WAIT_CMD; + + /* Send the command to the drive and return. */ + return cdrom_transfer_packet_command(drive, pc, &cdrom_pc_intr); +} + + +static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive) +{ + int len; + struct request *rq = HWGROUP(drive)->rq; + struct packet_command *pc = (struct packet_command *)rq->buffer; + struct cdrom_info *info = drive->driver_data; + + info->dma = 0; + info->cmd = 0; + pc->stat = 0; + len = pc->buflen; + + /* Start sending the command to the drive. */ + return cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation); +} + + +/* Sleep for TIME jiffies. + Not to be called from an interrupt handler. */ +static +void cdrom_sleep (int time) +{ + int sleep = time; + + do { + set_current_state(TASK_INTERRUPTIBLE); + sleep = schedule_timeout(sleep); + } while (sleep); +} + +static +int cdrom_queue_packet_command(ide_drive_t *drive, struct packet_command *pc) +{ + struct request_sense sense; + struct request req; + int retries = 10; + + if (pc->sense == NULL) + pc->sense = &sense; + + /* Start of retry loop. */ + do { + ide_init_drive_cmd (&req); + req.flags = REQ_PC; + req.buffer = (char *)pc; + ide_do_drive_cmd (drive, &req, ide_wait); + /* FIXME: we should probably abort/retry or something + * in case of failure */ + if (pc->stat != 0) { + /* The request failed. Retry if it was due to a unit + attention status + (usually means media was changed). */ + struct request_sense *reqbuf = pc->sense; + + if (reqbuf->sense_key == UNIT_ATTENTION) + cdrom_saw_media_change (drive); + else if (reqbuf->sense_key == NOT_READY && + reqbuf->asc == 4 && reqbuf->ascq != 4) { + /* The drive is in the process of loading + a disk. Retry, but wait a little to give + the drive time to complete the load. */ + cdrom_sleep(2 * HZ); + } else { + /* Otherwise, don't retry. */ + retries = 0; + } + --retries; + } + + /* End of retry loop. */ + } while (pc->stat != 0 && retries >= 0); + + /* Return an error if the command failed. */ + return pc->stat ? -EIO : 0; +} + +/* + * Write handling + */ +static inline int cdrom_write_check_ireason(ide_drive_t *drive, int len, int ireason) +{ + /* Two notes about IDE interrupt reason here - 0 means that + * the drive wants to receive data from us, 2 means that + * the drive is expecting data from us. + */ + ireason &= 3; + + if (ireason == 2) { + /* Whoops... The drive wants to send data. */ + printk("%s: cdrom_write_intr: wrong transfer direction!\n", + drive->name); + + /* Throw some data at the drive so it doesn't hang + and quit this request. */ + while (len > 0) { + int dum = 0; + atapi_output_bytes(drive, &dum, sizeof(dum)); + len -= sizeof(dum); + } + } else { + /* Drive wants a command packet, or invalid ireason... */ + printk("%s: cdrom_write_intr: bad interrupt reason %d\n", + drive->name, ireason); + } + + cdrom_end_request(drive, 0); + return 1; +} + +static ide_startstop_t cdrom_write_intr(ide_drive_t *drive) +{ + int stat, ireason, len, sectors_to_transfer, uptodate; + struct cdrom_info *info = drive->driver_data; + int i, dma_error = 0, dma = info->dma; + ide_startstop_t startstop; + + struct request *rq = HWGROUP(drive)->rq; + + /* Check for errors. */ + if (dma) { + info->dma = 0; + if ((dma_error = HWIF(drive)->dmaproc(ide_dma_end, drive))) { + printk("ide-cd: write dma error\n"); + HWIF(drive)->dmaproc(ide_dma_off, drive); + } + } + + if (cdrom_decode_status(&startstop, drive, 0, &stat)) { + printk("ide-cd: write_intr decode_status bad\n"); + return startstop; + } + + /* + * using dma, transfer is complete now + */ + if (dma) { + if (dma_error) + return DRIVER(drive)->error(drive, "dma error", stat); + + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + ide_cdrom_end_request(drive, 1); + } + return ide_stopped; + } + + /* Read the interrupt reason and the transfer length. */ + ireason = IN_BYTE(IDE_NSECTOR_REG); + len = IN_BYTE(IDE_LCYL_REG) + 256 * IN_BYTE(IDE_HCYL_REG); + + /* If DRQ is clear, the command has completed. */ + if ((stat & DRQ_STAT) == 0) { + /* If we're not done writing, complain. + * Otherwise, complete the command normally. + */ + uptodate = 1; + if (rq->current_nr_sectors > 0) { + printk("%s: write_intr: data underrun (%d blocks)\n", + drive->name, rq->current_nr_sectors); + uptodate = 0; + } + cdrom_end_request(drive, uptodate); + return ide_stopped; + } + + /* Check that the drive is expecting to do the same thing we are. */ + if (ireason & 3) + if (cdrom_write_check_ireason(drive, len, ireason)) + return ide_stopped; + + sectors_to_transfer = len / SECTOR_SIZE; + + /* + * now loop and write out the data + */ + while (sectors_to_transfer > 0) { + int this_transfer; + + if (!rq->current_nr_sectors) { + printk("ide-cd: write_intr: oops\n"); + break; + } + + /* + * Figure out how many sectors we can transfer + */ + this_transfer = MIN(sectors_to_transfer,rq->current_nr_sectors); + + while (this_transfer > 0) { + atapi_output_bytes(drive, rq->buffer, SECTOR_SIZE); + rq->buffer += SECTOR_SIZE; + --rq->nr_sectors; + --rq->current_nr_sectors; + ++rq->sector; + --this_transfer; + --sectors_to_transfer; + } + + /* + * current buffer complete, move on + */ + if (rq->current_nr_sectors == 0 && rq->nr_sectors) + cdrom_end_request(drive, 1); + } + + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + + /* re-arm handler */ + ide_set_handler(drive, &cdrom_write_intr, 5 * WAIT_CMD, NULL); + return ide_started; +} + +static ide_startstop_t cdrom_start_write_cont(ide_drive_t *drive) +{ + struct packet_command pc; /* packet_command_t pc; */ + struct request *rq = HWGROUP(drive)->rq; + unsigned nframes, frame; + + nframes = rq->nr_sectors >> 2; + frame = rq->sector >> 2; + + memcpy(pc.c, rq->cmd, sizeof(pc.c)); +#if 0 /* the immediate bit */ + pc.c[1] = 1 << 3; +#endif + pc.timeout = 2 * WAIT_CMD; + + return cdrom_transfer_packet_command(drive, &pc, cdrom_write_intr); +} + +static ide_startstop_t cdrom_start_write(ide_drive_t *drive, struct request *rq) +{ + struct cdrom_info *info = drive->driver_data; + + /* + * writes *must* be 2kB frame aligned + */ + if ((rq->nr_sectors & 3) || (rq->sector & 3)) { + cdrom_end_request(drive, 0); + return ide_stopped; + } + + /* + * for dvd-ram and such media, it's a really big deal to get + * big writes all the time. so scour the queue and attempt to + * remerge requests, often the plugging will not have had time + * to do this properly + */ + blk_attempt_remerge(&drive->queue, rq); + + info->nsectors_buffered = 0; + + /* use dma, if possible. we don't need to check more, since we + * know that the transfer is always (at least!) 2KB aligned */ + info->dma = drive->using_dma ? 1 : 0; + info->cmd = WRITE; + + /* Start sending the read request to the drive. */ + return cdrom_start_packet_command(drive, 32768, cdrom_start_write_cont); +} + +static ide_startstop_t cdrom_do_block_pc(ide_drive_t *drive, struct request *rq) +{ + struct packet_command pc; + ide_startstop_t startstop; + + memset(&pc, 0, sizeof(pc)); + memcpy(pc.c, rq->cmd, sizeof(pc.c)); + pc.quiet = 1; + pc.timeout = 60 * HZ; + rq->buffer = (char *) &pc; + + startstop = cdrom_do_packet_command(drive); + if (pc.stat) + rq->errors++; + + return startstop; +} + +/**************************************************************************** + * cdrom driver request routine. + */ +static ide_startstop_t +ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + ide_startstop_t action; + struct cdrom_info *info = drive->driver_data; + + if (rq->flags & REQ_CMD) { + if (CDROM_CONFIG_FLAGS(drive)->seeking) { + unsigned long elpased = jiffies - info->start_seek; + int stat = GET_STAT(); + + if ((stat & SEEK_STAT) != SEEK_STAT) { + if (elpased < IDECD_SEEK_TIMEOUT) { + ide_stall_queue(drive, IDECD_SEEK_TIMER); + return ide_stopped; + } + printk ("%s: DSC timeout\n", drive->name); + } + CDROM_CONFIG_FLAGS(drive)->seeking = 0; + } + if (IDE_LARGE_SEEK(info->last_block, block, IDECD_SEEK_THRESHOLD) && drive->dsc_overlap) { + action = cdrom_start_seek(drive, block); + } else { + if (rq_data_dir(rq) == READ) + action = cdrom_start_read(drive, block); + else + action = cdrom_start_write(drive, rq); + } + info->last_block = block; + return action; + } else if (rq->flags & (REQ_PC | REQ_SENSE)) { + return cdrom_do_packet_command(drive); + } else if (rq->flags & REQ_SPECIAL) { + /* + * right now this can only be a reset... + */ + cdrom_end_request(drive, 1); + return ide_stopped; + } else if (rq->flags & REQ_BLOCK_PC) { + return cdrom_do_block_pc(drive, rq); + } + + blk_dump_rq_flags(rq, "ide-cd bad flags"); + cdrom_end_request(drive, 0); + return ide_stopped; +} + + + +/**************************************************************************** + * Ioctl handling. + * + * Routines which queue packet commands take as a final argument a pointer + * to a request_sense struct. If execution of the command results + * in an error with a CHECK CONDITION status, this structure will be filled + * with the results of the subsequent request sense command. The pointer + * can also be NULL, in which case no sense information is returned. + */ + +#if ! STANDARD_ATAPI +static inline +int bin2bcd (int x) +{ + return (x%10) | ((x/10) << 4); +} + + +static inline +int bcd2bin (int x) +{ + return (x >> 4) * 10 + (x & 0x0f); +} + +static +void msf_from_bcd (struct atapi_msf *msf) +{ + msf->minute = bcd2bin (msf->minute); + msf->second = bcd2bin (msf->second); + msf->frame = bcd2bin (msf->frame); +} + +#endif /* not STANDARD_ATAPI */ + + +static inline +void lba_to_msf (int lba, byte *m, byte *s, byte *f) +{ + lba += CD_MSF_OFFSET; + lba &= 0xffffff; /* negative lbas use only 24 bits */ + *m = lba / (CD_SECS * CD_FRAMES); + lba %= (CD_SECS * CD_FRAMES); + *s = lba / CD_FRAMES; + *f = lba % CD_FRAMES; +} + + +static inline +int msf_to_lba (byte m, byte s, byte f) +{ + return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_MSF_OFFSET; +} + +static int cdrom_check_status(ide_drive_t *drive, struct request_sense *sense) +{ + struct packet_command pc; + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *cdi = &info->devinfo; + + memset(&pc, 0, sizeof(pc)); + pc.sense = sense; + + pc.c[0] = GPCMD_TEST_UNIT_READY; + +#if ! STANDARD_ATAPI + /* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to + switch CDs instead of supporting the LOAD_UNLOAD opcode */ + + pc.c[7] = cdi->sanyo_slot % 3; +#endif /* not STANDARD_ATAPI */ + + return cdrom_queue_packet_command(drive, &pc); +} + + +/* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */ +static int +cdrom_lockdoor(ide_drive_t *drive, int lockflag, struct request_sense *sense) +{ + struct request_sense my_sense; + struct packet_command pc; + int stat; + + if (sense == NULL) + sense = &my_sense; + + /* If the drive cannot lock the door, just pretend. */ + if (CDROM_CONFIG_FLAGS(drive)->no_doorlock) { + stat = 0; + } else { + memset(&pc, 0, sizeof(pc)); + pc.sense = sense; + pc.c[0] = GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL; + pc.c[4] = lockflag ? 1 : 0; + stat = cdrom_queue_packet_command (drive, &pc); + } + + /* If we got an illegal field error, the drive + probably cannot lock the door. */ + if (stat != 0 && + sense->sense_key == ILLEGAL_REQUEST && + (sense->asc == 0x24 || sense->asc == 0x20)) { + printk ("%s: door locking not supported\n", + drive->name); + CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1; + stat = 0; + } + + /* no medium, that's alright. */ + if (stat != 0 && sense->sense_key == NOT_READY && sense->asc == 0x3a) + stat = 0; + + if (stat == 0) + CDROM_STATE_FLAGS (drive)->door_locked = lockflag; + + return stat; +} + + +/* Eject the disk if EJECTFLAG is 0. + If EJECTFLAG is 1, try to reload the disk. */ +static int cdrom_eject(ide_drive_t *drive, int ejectflag, + struct request_sense *sense) +{ + struct packet_command pc; + + if (CDROM_CONFIG_FLAGS(drive)->no_eject && !ejectflag) + return -EDRIVE_CANT_DO_THIS; + + /* reload fails on some drives, if the tray is locked */ + if (CDROM_STATE_FLAGS(drive)->door_locked && ejectflag) + return 0; + + memset(&pc, 0, sizeof (pc)); + pc.sense = sense; + + pc.c[0] = GPCMD_START_STOP_UNIT; + pc.c[4] = 0x02 + (ejectflag != 0); + return cdrom_queue_packet_command (drive, &pc); +} + +static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity, + struct request_sense *sense) +{ + struct { + __u32 lba; + __u32 blocklen; + } capbuf; + + int stat; + struct packet_command pc; + + memset(&pc, 0, sizeof(pc)); + pc.sense = sense; + + pc.c[0] = GPCMD_READ_CDVD_CAPACITY; + pc.buffer = (char *)&capbuf; + pc.buflen = sizeof(capbuf); + + stat = cdrom_queue_packet_command(drive, &pc); + if (stat == 0) + *capacity = 1 + be32_to_cpu(capbuf.lba); + + return stat; +} + +static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag, + int format, char *buf, int buflen, + struct request_sense *sense) +{ + struct packet_command pc; + + memset(&pc, 0, sizeof(pc)); + pc.sense = sense; + + pc.buffer = buf; + pc.buflen = buflen; + pc.quiet = 1; + pc.c[0] = GPCMD_READ_TOC_PMA_ATIP; + pc.c[6] = trackno; + pc.c[7] = (buflen >> 8); + pc.c[8] = (buflen & 0xff); + pc.c[9] = (format << 6); + + if (msf_flag) + pc.c[1] = 2; + + return cdrom_queue_packet_command (drive, &pc); +} + + +/* Try to read the entire TOC for the disk into our internal buffer. */ +static int cdrom_read_toc(ide_drive_t *drive, struct request_sense *sense) +{ + int stat, ntracks, i; + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *cdi = &info->devinfo; + struct atapi_toc *toc = info->toc; + struct { + struct atapi_toc_header hdr; + struct atapi_toc_entry ent; + } ms_tmp; + + if (toc == NULL) { + /* Try to allocate space. */ + toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc), + GFP_KERNEL); + info->toc = toc; + if (toc == NULL) { + printk ("%s: No cdrom TOC buffer!\n", drive->name); + return -ENOMEM; + } + } + + /* Check to see if the existing data is still valid. + If it is, just return. */ + (void) cdrom_check_status(drive, sense); + + if (CDROM_STATE_FLAGS(drive)->toc_valid) + return 0; + + /* First read just the header, so we know how long the TOC is. */ + stat = cdrom_read_tocentry(drive, 0, 1, 0, (char *) &toc->hdr, + sizeof(struct atapi_toc_header), sense); + if (stat) return stat; + +#if ! STANDARD_ATAPI + if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) { + toc->hdr.first_track = bcd2bin (toc->hdr.first_track); + toc->hdr.last_track = bcd2bin (toc->hdr.last_track); + } +#endif /* not STANDARD_ATAPI */ + + ntracks = toc->hdr.last_track - toc->hdr.first_track + 1; + if (ntracks <= 0) + return -EIO; + if (ntracks > MAX_TRACKS) + ntracks = MAX_TRACKS; + + /* Now read the whole schmeer. */ + stat = cdrom_read_tocentry(drive, toc->hdr.first_track, 1, 0, + (char *)&toc->hdr, + sizeof(struct atapi_toc_header) + + (ntracks + 1) * + sizeof(struct atapi_toc_entry), sense); + + if (stat && toc->hdr.first_track > 1) { + /* Cds with CDI tracks only don't have any TOC entries, + despite of this the returned values are + first_track == last_track = number of CDI tracks + 1, + so that this case is indistinguishable from the same + layout plus an additional audio track. + If we get an error for the regular case, we assume + a CDI without additional audio tracks. In this case + the readable TOC is empty (CDI tracks are not included) + and only holds the Leadout entry. Heiko Eißfeldt */ + ntracks = 0; + stat = cdrom_read_tocentry(drive, CDROM_LEADOUT, 1, 0, + (char *)&toc->hdr, + sizeof(struct atapi_toc_header) + + (ntracks + 1) * + sizeof(struct atapi_toc_entry), + sense); + if (stat) { + return stat; + } +#if ! STANDARD_ATAPI + if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) { + toc->hdr.first_track = bin2bcd(CDROM_LEADOUT); + toc->hdr.last_track = bin2bcd(CDROM_LEADOUT); + } else +#endif /* not STANDARD_ATAPI */ + { + toc->hdr.first_track = CDROM_LEADOUT; + toc->hdr.last_track = CDROM_LEADOUT; + } + } + + if (stat) + return stat; + + toc->hdr.toc_length = ntohs (toc->hdr.toc_length); + +#if ! STANDARD_ATAPI + if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) { + toc->hdr.first_track = bcd2bin (toc->hdr.first_track); + toc->hdr.last_track = bcd2bin (toc->hdr.last_track); + } +#endif /* not STANDARD_ATAPI */ + + for (i=0; i<=ntracks; i++) { +#if ! STANDARD_ATAPI + if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd) { + if (CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd) + toc->ent[i].track = bcd2bin (toc->ent[i].track); + msf_from_bcd (&toc->ent[i].addr.msf); + } +#endif /* not STANDARD_ATAPI */ + toc->ent[i].addr.lba = msf_to_lba (toc->ent[i].addr.msf.minute, + toc->ent[i].addr.msf.second, + toc->ent[i].addr.msf.frame); + } + + /* Read the multisession information. */ + if (toc->hdr.first_track != CDROM_LEADOUT) { + /* Read the multisession information. */ + stat = cdrom_read_tocentry(drive, 0, 1, 1, (char *)&ms_tmp, + sizeof(ms_tmp), sense); + if (stat) return stat; + } else { + ms_tmp.ent.addr.msf.minute = 0; + ms_tmp.ent.addr.msf.second = 2; + ms_tmp.ent.addr.msf.frame = 0; + ms_tmp.hdr.first_track = ms_tmp.hdr.last_track = CDROM_LEADOUT; + } + +#if ! STANDARD_ATAPI + if (CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd) + msf_from_bcd (&ms_tmp.ent.addr.msf); +#endif /* not STANDARD_ATAPI */ + + toc->last_session_lba = msf_to_lba (ms_tmp.ent.addr.msf.minute, + ms_tmp.ent.addr.msf.second, + ms_tmp.ent.addr.msf.frame); + + toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track); + + /* Now try to get the total cdrom capacity. */ + stat = cdrom_get_last_written(cdi, (long *) &toc->capacity); + if (stat) + stat = cdrom_read_capacity(drive, &toc->capacity, sense); + if (stat) + toc->capacity = 0x1fffff; + + drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME; + + /* Remember that we've read this stuff. */ + CDROM_STATE_FLAGS (drive)->toc_valid = 1; + + return 0; +} + + +static int cdrom_read_subchannel(ide_drive_t *drive, int format, char *buf, + int buflen, struct request_sense *sense) +{ + struct packet_command pc; + + memset(&pc, 0, sizeof(pc)); + pc.sense = sense; + + pc.buffer = buf; + pc.buflen = buflen; + pc.c[0] = GPCMD_READ_SUBCHANNEL; + pc.c[1] = 2; /* MSF addressing */ + pc.c[2] = 0x40; /* request subQ data */ + pc.c[3] = format; + pc.c[7] = (buflen >> 8); + pc.c[8] = (buflen & 0xff); + return cdrom_queue_packet_command(drive, &pc); +} + +/* ATAPI cdrom drives are free to select the speed you request or any slower + rate :-( Requesting too fast a speed will _not_ produce an error. */ +static int cdrom_select_speed(ide_drive_t *drive, int speed, + struct request_sense *sense) +{ + struct packet_command pc; + memset(&pc, 0, sizeof(pc)); + pc.sense = sense; + + if (speed == 0) + speed = 0xffff; /* set to max */ + else + speed *= 177; /* Nx to kbytes/s */ + + pc.c[0] = GPCMD_SET_SPEED; + /* Read Drive speed in kbytes/second MSB */ + pc.c[2] = (speed >> 8) & 0xff; + /* Read Drive speed in kbytes/second LSB */ + pc.c[3] = speed & 0xff; + if (CDROM_CONFIG_FLAGS(drive)->cd_r || + CDROM_CONFIG_FLAGS(drive)->cd_rw || + CDROM_CONFIG_FLAGS(drive)->dvd_r) { + /* Write Drive speed in kbytes/second MSB */ + pc.c[4] = (speed >> 8) & 0xff; + /* Write Drive speed in kbytes/second LSB */ + pc.c[5] = speed & 0xff; + } + + return cdrom_queue_packet_command(drive, &pc); +} + +static int cdrom_play_audio(ide_drive_t *drive, int lba_start, int lba_end) +{ + struct request_sense sense; + struct packet_command pc; + + memset(&pc, 0, sizeof (pc)); + pc.sense = &sense; + + pc.c[0] = GPCMD_PLAY_AUDIO_MSF; + lba_to_msf(lba_start, &pc.c[3], &pc.c[4], &pc.c[5]); + lba_to_msf(lba_end-1, &pc.c[6], &pc.c[7], &pc.c[8]); + + return cdrom_queue_packet_command(drive, &pc); +} + +static int cdrom_get_toc_entry(ide_drive_t *drive, int track, + struct atapi_toc_entry **ent) +{ + struct cdrom_info *info = drive->driver_data; + struct atapi_toc *toc = info->toc; + int ntracks; + + /* + * don't serve cached data, if the toc isn't valid + */ + if (!CDROM_STATE_FLAGS(drive)->toc_valid) + return -EINVAL; + + /* Check validity of requested track number. */ + ntracks = toc->hdr.last_track - toc->hdr.first_track + 1; + if (toc->hdr.first_track == CDROM_LEADOUT) ntracks = 0; + if (track == CDROM_LEADOUT) + *ent = &toc->ent[ntracks]; + else if (track < toc->hdr.first_track || + track > toc->hdr.last_track) + return -EINVAL; + else + *ent = &toc->ent[track - toc->hdr.first_track]; + + return 0; +} + +/* the generic packet interface to cdrom.c */ +static int ide_cdrom_packet(struct cdrom_device_info *cdi, + struct cdrom_generic_command *cgc) +{ + struct packet_command pc; + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + + if (cgc->timeout <= 0) + cgc->timeout = WAIT_CMD; + + /* here we queue the commands from the uniform CD-ROM + layer. the packet must be complete, as we do not + touch it at all. */ + memset(&pc, 0, sizeof(pc)); + memcpy(pc.c, cgc->cmd, CDROM_PACKET_SIZE); + pc.buffer = cgc->buffer; + pc.buflen = cgc->buflen; + pc.quiet = cgc->quiet; + pc.timeout = cgc->timeout; + pc.sense = cgc->sense; + cgc->stat = cdrom_queue_packet_command(drive, &pc); + if (!cgc->stat) + cgc->buflen -= pc.buflen; + return cgc->stat; +} + +static +int ide_cdrom_dev_ioctl (struct cdrom_device_info *cdi, + unsigned int cmd, unsigned long arg) +{ + struct cdrom_generic_command cgc; + char buffer[16]; + int stat; + + init_cdrom_command(&cgc, buffer, sizeof(buffer), CGC_DATA_UNKNOWN); + + /* These will be moved into the Uniform layer shortly... */ + switch (cmd) { + case CDROMSETSPINDOWN: { + char spindown; + + if (copy_from_user(&spindown, (void *) arg, sizeof(char))) + return -EFAULT; + + if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0))) + return stat; + + buffer[11] = (buffer[11] & 0xf0) | (spindown & 0x0f); + + return cdrom_mode_select(cdi, &cgc); + } + + case CDROMGETSPINDOWN: { + char spindown; + + if ((stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CDROM_PAGE, 0))) + return stat; + + spindown = buffer[11] & 0x0f; + + if (copy_to_user((void *) arg, &spindown, sizeof (char))) + return -EFAULT; + + return 0; + } + + default: + return -EINVAL; + } + +} + +static +int ide_cdrom_audio_ioctl (struct cdrom_device_info *cdi, + unsigned int cmd, void *arg) + +{ + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + struct cdrom_info *info = drive->driver_data; + int stat; + + switch (cmd) { + /* + * emulate PLAY_AUDIO_TI command with PLAY_AUDIO_10, since + * atapi doesn't support it + */ + case CDROMPLAYTRKIND: { + unsigned long lba_start, lba_end; + struct cdrom_ti *ti = (struct cdrom_ti *)arg; + struct atapi_toc_entry *first_toc, *last_toc; + + stat = cdrom_get_toc_entry(drive, ti->cdti_trk0, &first_toc); + if (stat) + return stat; + + stat = cdrom_get_toc_entry(drive, ti->cdti_trk1, &last_toc); + if (stat) + return stat; + + if (ti->cdti_trk1 != CDROM_LEADOUT) + ++last_toc; + lba_start = first_toc->addr.lba; + lba_end = last_toc->addr.lba; + + if (lba_end <= lba_start) + return -EINVAL; + + return cdrom_play_audio(drive, lba_start, lba_end); + } + + case CDROMREADTOCHDR: { + struct cdrom_tochdr *tochdr = (struct cdrom_tochdr *) arg; + struct atapi_toc *toc; + + /* Make sure our saved TOC is valid. */ + stat = cdrom_read_toc(drive, NULL); + if (stat) return stat; + + toc = info->toc; + tochdr->cdth_trk0 = toc->hdr.first_track; + tochdr->cdth_trk1 = toc->hdr.last_track; + + return 0; + } + + case CDROMREADTOCENTRY: { + struct cdrom_tocentry *tocentry = (struct cdrom_tocentry*) arg; + struct atapi_toc_entry *toce; + + stat = cdrom_get_toc_entry (drive, tocentry->cdte_track, &toce); + if (stat) return stat; + + tocentry->cdte_ctrl = toce->control; + tocentry->cdte_adr = toce->adr; + if (tocentry->cdte_format == CDROM_MSF) { + lba_to_msf (toce->addr.lba, + &tocentry->cdte_addr.msf.minute, + &tocentry->cdte_addr.msf.second, + &tocentry->cdte_addr.msf.frame); + } else + tocentry->cdte_addr.lba = toce->addr.lba; + + return 0; + } + + default: + return -EINVAL; + } +} + +static +int ide_cdrom_reset (struct cdrom_device_info *cdi) +{ + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + struct request_sense sense; + struct request req; + int ret; + + ide_init_drive_cmd (&req); + req.flags = REQ_SPECIAL; + ret = ide_do_drive_cmd(drive, &req, ide_wait); + + /* + * A reset will unlock the door. If it was previously locked, + * lock it again. + */ + if (CDROM_STATE_FLAGS(drive)->door_locked) + (void) cdrom_lockdoor(drive, 1, &sense); + + return ret; +} + + +static +int ide_cdrom_tray_move (struct cdrom_device_info *cdi, int position) +{ + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + struct request_sense sense; + + if (position) { + int stat = cdrom_lockdoor(drive, 0, &sense); + if (stat) return stat; + } + + return cdrom_eject(drive, !position, &sense); +} + +static +int ide_cdrom_lock_door (struct cdrom_device_info *cdi, int lock) +{ + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + return cdrom_lockdoor(drive, lock, NULL); +} + +static +int ide_cdrom_select_speed (struct cdrom_device_info *cdi, int speed) +{ + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + struct request_sense sense; + int stat; + + if ((stat = cdrom_select_speed (drive, speed, &sense)) < 0) + return stat; + + cdi->speed = CDROM_STATE_FLAGS (drive)->current_speed; + return 0; +} + +static +int ide_cdrom_drive_status (struct cdrom_device_info *cdi, int slot_nr) +{ + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + + if (slot_nr == CDSL_CURRENT) { + struct request_sense sense; + int stat = cdrom_check_status(drive, &sense); + if (stat == 0 || sense.sense_key == UNIT_ATTENTION) + return CDS_DISC_OK; + + if (sense.sense_key == NOT_READY && sense.asc == 0x04 && + sense.ascq == 0x04) + return CDS_DISC_OK; + + + /* + * If not using Mt Fuji extended media tray reports, + * just return TRAY_OPEN since ATAPI doesn't provide + * any other way to detect this... + */ + if (sense.sense_key == NOT_READY) { + if (sense.asc == 0x3a && sense.ascq == 1) + return CDS_NO_DISC; + else + return CDS_TRAY_OPEN; + } + + return CDS_DRIVE_NOT_READY; + } + return -EINVAL; +} + +static +int ide_cdrom_get_last_session (struct cdrom_device_info *cdi, + struct cdrom_multisession *ms_info) +{ + struct atapi_toc *toc; + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + struct cdrom_info *info = drive->driver_data; + struct request_sense sense; + int ret; + + if (!CDROM_STATE_FLAGS(drive)->toc_valid || info->toc == NULL) + if ((ret = cdrom_read_toc(drive, &sense))) + return ret; + + toc = info->toc; + ms_info->addr.lba = toc->last_session_lba; + ms_info->xa_flag = toc->xa_flag; + + return 0; +} + +static +int ide_cdrom_get_mcn (struct cdrom_device_info *cdi, + struct cdrom_mcn *mcn_info) +{ + int stat; + char mcnbuf[24]; + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + +/* get MCN */ + if ((stat = cdrom_read_subchannel(drive, 2, mcnbuf, sizeof (mcnbuf), NULL))) + return stat; + + memcpy (mcn_info->medium_catalog_number, mcnbuf+9, + sizeof (mcn_info->medium_catalog_number)-1); + mcn_info->medium_catalog_number[sizeof (mcn_info->medium_catalog_number)-1] + = '\0'; + + return 0; +} + + + +/**************************************************************************** + * Other driver requests (open, close, check media change). + */ + +static +int ide_cdrom_check_media_change_real (struct cdrom_device_info *cdi, + int slot_nr) +{ + ide_drive_t *drive = (ide_drive_t*) cdi->handle; + int retval; + + if (slot_nr == CDSL_CURRENT) { + (void) cdrom_check_status(drive, NULL); + retval = CDROM_STATE_FLAGS (drive)->media_changed; + CDROM_STATE_FLAGS (drive)->media_changed = 0; + return retval; + } else { + return -EINVAL; + } +} + + +static +int ide_cdrom_open_real (struct cdrom_device_info *cdi, int purpose) +{ + return 0; +} + + +/* + * Close down the device. Invalidate all cached blocks. + */ + +static +void ide_cdrom_release_real (struct cdrom_device_info *cdi) +{ +} + + + +/**************************************************************************** + * Device initialization. + */ +static struct cdrom_device_ops ide_cdrom_dops = { + open: ide_cdrom_open_real, + release: ide_cdrom_release_real, + drive_status: ide_cdrom_drive_status, + media_changed: ide_cdrom_check_media_change_real, + tray_move: ide_cdrom_tray_move, + lock_door: ide_cdrom_lock_door, + select_speed: ide_cdrom_select_speed, + get_last_session: ide_cdrom_get_last_session, + get_mcn: ide_cdrom_get_mcn, + reset: ide_cdrom_reset, + audio_ioctl: ide_cdrom_audio_ioctl, + dev_ioctl: ide_cdrom_dev_ioctl, + capability: CDC_CLOSE_TRAY | CDC_OPEN_TRAY | CDC_LOCK | + CDC_SELECT_SPEED | CDC_SELECT_DISC | + CDC_MULTI_SESSION | CDC_MCN | + CDC_MEDIA_CHANGED | CDC_PLAY_AUDIO | CDC_RESET | + CDC_IOCTLS | CDC_DRIVE_STATUS | CDC_CD_R | + CDC_CD_RW | CDC_DVD | CDC_DVD_R| CDC_DVD_RAM | + CDC_GENERIC_PACKET, + generic_packet: ide_cdrom_packet, +}; + +static int ide_cdrom_register (ide_drive_t *drive, int nslots) +{ + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *devinfo = &info->devinfo; + int minor = (drive->select.b.unit) << PARTN_BITS; + + devinfo->dev = mk_kdev(HWIF(drive)->major, minor); + devinfo->ops = &ide_cdrom_dops; + devinfo->mask = 0; + *(int *)&devinfo->speed = CDROM_STATE_FLAGS (drive)->current_speed; + *(int *)&devinfo->capacity = nslots; + devinfo->handle = (void *) drive; + strcpy(devinfo->name, drive->name); + + /* set capability mask to match the probe. */ + if (!CDROM_CONFIG_FLAGS (drive)->cd_r) + devinfo->mask |= CDC_CD_R; + if (!CDROM_CONFIG_FLAGS (drive)->cd_rw) + devinfo->mask |= CDC_CD_RW; + if (!CDROM_CONFIG_FLAGS (drive)->dvd) + devinfo->mask |= CDC_DVD; + if (!CDROM_CONFIG_FLAGS (drive)->dvd_r) + devinfo->mask |= CDC_DVD_R; + if (!CDROM_CONFIG_FLAGS (drive)->dvd_ram) + devinfo->mask |= CDC_DVD_RAM; + if (!CDROM_CONFIG_FLAGS (drive)->is_changer) + devinfo->mask |= CDC_SELECT_DISC; + if (!CDROM_CONFIG_FLAGS (drive)->audio_play) + devinfo->mask |= CDC_PLAY_AUDIO; + if (!CDROM_CONFIG_FLAGS (drive)->close_tray) + devinfo->mask |= CDC_CLOSE_TRAY; + + devinfo->de = devfs_register(drive->de, "cd", DEVFS_FL_DEFAULT, + HWIF(drive)->major, minor, + S_IFBLK | S_IRUGO | S_IWUGO, + ide_fops, NULL); + + return register_cdrom(devinfo); +} + +static +int ide_cdrom_get_capabilities(ide_drive_t *drive, struct atapi_capabilities_page *cap) +{ + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *cdi = &info->devinfo; + struct cdrom_generic_command cgc; + int stat, attempts = 3, size = sizeof(*cap); + + /* + * ACER50 (and others?) require the full spec length mode sense + * page capabilities size, but older drives break. + */ + if (drive->id) { + if (!(!strcmp(drive->id->model, "ATAPI CD ROM DRIVE 50X MAX") || + !strcmp(drive->id->model, "WPI CDS-32X"))) + size -= sizeof(cap->pad); + } + + /* we have to cheat a little here. the packet will eventually + * be queued with ide_cdrom_packet(), which extracts the + * drive from cdi->handle. Since this device hasn't been + * registered with the Uniform layer yet, it can't do this. + * Same goes for cdi->ops. + */ + cdi->handle = (ide_drive_t *) drive; + cdi->ops = &ide_cdrom_dops; + init_cdrom_command(&cgc, cap, size, CGC_DATA_UNKNOWN); + do { /* we seem to get stat=0x01,err=0x00 the first time (??) */ + stat = cdrom_mode_sense(cdi, &cgc, GPMODE_CAPABILITIES_PAGE, 0); + if (!stat) + break; + } while (--attempts); + return stat; +} + +static +int ide_cdrom_probe_capabilities (ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *cdi = &info->devinfo; + struct atapi_capabilities_page cap; + int nslots = 1; + + if (CDROM_CONFIG_FLAGS (drive)->nec260) { + CDROM_CONFIG_FLAGS (drive)->no_eject = 0; + CDROM_CONFIG_FLAGS (drive)->audio_play = 1; + return nslots; + } + + if (ide_cdrom_get_capabilities(drive, &cap)) + return 0; + + if (cap.lock == 0) + CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1; + if (cap.eject) + CDROM_CONFIG_FLAGS (drive)->no_eject = 0; + if (cap.cd_r_write) + CDROM_CONFIG_FLAGS (drive)->cd_r = 1; + if (cap.cd_rw_write) + CDROM_CONFIG_FLAGS (drive)->cd_rw = 1; + if (cap.test_write) + CDROM_CONFIG_FLAGS (drive)->test_write = 1; + if (cap.dvd_ram_read || cap.dvd_r_read || cap.dvd_rom) + CDROM_CONFIG_FLAGS (drive)->dvd = 1; + if (cap.dvd_ram_write) + CDROM_CONFIG_FLAGS (drive)->dvd_ram = 1; + if (cap.dvd_r_write) + CDROM_CONFIG_FLAGS (drive)->dvd_r = 1; + if (cap.audio_play) + CDROM_CONFIG_FLAGS (drive)->audio_play = 1; + if (cap.mechtype == mechtype_caddy || cap.mechtype == mechtype_popup) + CDROM_CONFIG_FLAGS (drive)->close_tray = 0; + + /* Some drives used by Apple don't advertise audio play + * but they do support reading TOC & audio datas + */ + if (strcmp (drive->id->model, "MATSHITADVD-ROM SR-8187") == 0 || + strcmp (drive->id->model, "MATSHITADVD-ROM SR-8186") == 0 || + strcmp (drive->id->model, "MATSHITADVD-ROM SR-8176") == 0 || + strcmp (drive->id->model, "MATSHITADVD-ROM SR-8174") == 0) + CDROM_CONFIG_FLAGS (drive)->audio_play = 1; + +#if ! STANDARD_ATAPI + if (cdi->sanyo_slot > 0) { + CDROM_CONFIG_FLAGS (drive)->is_changer = 1; + nslots = 3; + } + + else +#endif /* not STANDARD_ATAPI */ + if (cap.mechtype == mechtype_individual_changer || + cap.mechtype == mechtype_cartridge_changer) { + if ((nslots = cdrom_number_of_slots(cdi)) > 1) { + CDROM_CONFIG_FLAGS (drive)->is_changer = 1; + CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 1; + } + } + + /* The ACER/AOpen 24X cdrom has the speed fields byte-swapped */ + if (drive->id && !drive->id->model[0] && !strncmp(drive->id->fw_rev, "241N", 4)) { + CDROM_STATE_FLAGS (drive)->current_speed = + (((unsigned int)cap.curspeed) + (176/2)) / 176; + CDROM_CONFIG_FLAGS (drive)->max_speed = + (((unsigned int)cap.maxspeed) + (176/2)) / 176; + } else { + CDROM_STATE_FLAGS (drive)->current_speed = + (ntohs(cap.curspeed) + (176/2)) / 176; + CDROM_CONFIG_FLAGS (drive)->max_speed = + (ntohs(cap.maxspeed) + (176/2)) / 176; + } + + /* don't print speed if the drive reported 0. + */ + printk("%s: ATAPI", drive->name); + if (CDROM_CONFIG_FLAGS(drive)->max_speed) + printk(" %dX", CDROM_CONFIG_FLAGS(drive)->max_speed); + printk(" %s", CDROM_CONFIG_FLAGS(drive)->dvd ? "DVD-ROM" : "CD-ROM"); + + if (CDROM_CONFIG_FLAGS (drive)->dvd_r|CDROM_CONFIG_FLAGS (drive)->dvd_ram) + printk (" DVD%s%s", + (CDROM_CONFIG_FLAGS (drive)->dvd_r)? "-R" : "", + (CDROM_CONFIG_FLAGS (drive)->dvd_ram)? "-RAM" : ""); + + if (CDROM_CONFIG_FLAGS (drive)->cd_r|CDROM_CONFIG_FLAGS (drive)->cd_rw) + printk (" CD%s%s", + (CDROM_CONFIG_FLAGS (drive)->cd_r)? "-R" : "", + (CDROM_CONFIG_FLAGS (drive)->cd_rw)? "/RW" : ""); + + if (CDROM_CONFIG_FLAGS (drive)->is_changer) + printk (" changer w/%d slots", nslots); + else + printk (" drive"); + + printk (", %dkB Cache", be16_to_cpu(cap.buffer_size)); + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (drive->using_dma) + (void) HWIF(drive)->dmaproc(ide_dma_verbose, drive); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + printk("\n"); + + return nslots; +} + +static void ide_cdrom_add_settings(ide_drive_t *drive) +{ + ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL); +} + +/* + * standard prep_rq_fn that builds 10 byte cmds + */ +static int ll_10byte_cmd_build(request_queue_t *q, struct request *rq) +{ + int hard_sect = queue_hardsect_size(q); + sector_t block = rq->hard_sector / (hard_sect >> 9); + unsigned long blocks = rq->hard_nr_sectors / (hard_sect >> 9); + + if (!(rq->flags & REQ_CMD)) + return 0; + + if (rq->hard_nr_sectors != rq->nr_sectors) { + printk(KERN_ERR "ide-cd: hard_nr_sectors differs from nr_sectors! %lu %lu\n", + rq->nr_sectors, rq->hard_nr_sectors); + } + memset(rq->cmd, 0, sizeof(rq->cmd)); + + if (rq_data_dir(rq) == READ) + rq->cmd[0] = GPCMD_READ_10; + else + rq->cmd[0] = GPCMD_WRITE_10; + + /* + * fill in lba + */ + rq->cmd[2] = (block >> 24) & 0xff; + rq->cmd[3] = (block >> 16) & 0xff; + rq->cmd[4] = (block >> 8) & 0xff; + rq->cmd[5] = block & 0xff; + + /* + * and transfer length + */ + rq->cmd[7] = (blocks >> 8) & 0xff; + rq->cmd[8] = blocks & 0xff; + + return 0; +} + +static +int ide_cdrom_setup (ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *cdi = &info->devinfo; + int minor = drive->select.b.unit << PARTN_BITS; + int nslots; + + /* + * default to read-only always and fix latter at the bottom + */ + set_device_ro(mk_kdev(HWIF(drive)->major, minor), 1); + blk_queue_hardsect_size(&drive->queue, CD_FRAMESIZE); + + blk_queue_prep_rq(&drive->queue, ll_10byte_cmd_build); + + drive->special.all = 0; + drive->ready_stat = 0; + + CDROM_STATE_FLAGS (drive)->media_changed = 1; + CDROM_STATE_FLAGS (drive)->toc_valid = 0; + CDROM_STATE_FLAGS (drive)->door_locked = 0; + +#if NO_DOOR_LOCKING + CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1; +#else + CDROM_CONFIG_FLAGS (drive)->no_doorlock = 0; +#endif + + if (drive->id != NULL) + CDROM_CONFIG_FLAGS (drive)->drq_interrupt = + ((drive->id->config & 0x0060) == 0x20); + else + CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0; + + CDROM_CONFIG_FLAGS (drive)->is_changer = 0; + CDROM_CONFIG_FLAGS (drive)->cd_r = 0; + CDROM_CONFIG_FLAGS (drive)->cd_rw = 0; + CDROM_CONFIG_FLAGS (drive)->test_write = 0; + CDROM_CONFIG_FLAGS (drive)->dvd = 0; + CDROM_CONFIG_FLAGS (drive)->dvd_r = 0; + CDROM_CONFIG_FLAGS (drive)->dvd_ram = 0; + CDROM_CONFIG_FLAGS (drive)->no_eject = 1; + CDROM_CONFIG_FLAGS (drive)->supp_disc_present = 0; + CDROM_CONFIG_FLAGS (drive)->audio_play = 0; + CDROM_CONFIG_FLAGS (drive)->close_tray = 1; + + /* limit transfer size per interrupt. */ + CDROM_CONFIG_FLAGS (drive)->limit_nframes = 0; + if (drive->id != NULL) { + /* a testament to the nice quality of Samsung drives... */ + if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2430")) + CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1; + else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-2432")) + CDROM_CONFIG_FLAGS (drive)->limit_nframes = 1; + /* the 3231 model does not support the SET_CD_SPEED command */ + else if (!strcmp(drive->id->model, "SAMSUNG CD-ROM SCR-3231")) + cdi->mask |= CDC_SELECT_SPEED; + } + +#if ! STANDARD_ATAPI + /* by default Sanyo 3 CD changer support is turned off and + ATAPI Rev 2.2+ standard support for CD changers is used */ + cdi->sanyo_slot = 0; + + CDROM_CONFIG_FLAGS (drive)->nec260 = 0; + CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 0; + CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 0; + CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 0; + CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 0; + + if (drive->id != NULL) { + if (strcmp (drive->id->model, "V003S0DS") == 0 && + drive->id->fw_rev[4] == '1' && + drive->id->fw_rev[6] <= '2') { + /* Vertos 300. + Some versions of this drive like to talk BCD. */ + CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1; + CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1; + CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1; + CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1; + } + + else if (strcmp (drive->id->model, "V006E0DS") == 0 && + drive->id->fw_rev[4] == '1' && + drive->id->fw_rev[6] <= '2') { + /* Vertos 600 ESD. */ + CDROM_CONFIG_FLAGS (drive)->toctracks_as_bcd = 1; + } + + else if (strcmp (drive->id->model, + "NEC CD-ROM DRIVE:260") == 0 && + strncmp (drive->id->fw_rev, "1.01", 4) == 0) { /* FIXME */ + /* Old NEC260 (not R). + This drive was released before the 1.2 version + of the spec. */ + CDROM_CONFIG_FLAGS (drive)->tocaddr_as_bcd = 1; + CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1; + CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1; + CDROM_CONFIG_FLAGS (drive)->nec260 = 1; + } + + else if (strcmp (drive->id->model, "WEARNES CDD-120") == 0 && + strncmp (drive->id->fw_rev, "A1.1", 4) == 0) { /* FIXME */ + /* Wearnes */ + CDROM_CONFIG_FLAGS (drive)->playmsf_as_bcd = 1; + CDROM_CONFIG_FLAGS (drive)->subchan_as_bcd = 1; + } + + /* Sanyo 3 CD changer uses a non-standard command + for CD changing */ + else if ((strcmp(drive->id->model, "CD-ROM CDR-C3 G") == 0) || + (strcmp(drive->id->model, "CD-ROM CDR-C3G") == 0) || + (strcmp(drive->id->model, "CD-ROM CDR_C36") == 0)) { + /* uses CD in slot 0 when value is set to 3 */ + cdi->sanyo_slot = 3; + } + + + } +#endif /* not STANDARD_ATAPI */ + + info->toc = NULL; + info->buffer = NULL; + info->sector_buffered = 0; + info->nsectors_buffered = 0; + info->changer_info = NULL; + info->last_block = 0; + info->start_seek = 0; + + nslots = ide_cdrom_probe_capabilities (drive); + + if (CDROM_CONFIG_FLAGS(drive)->dvd_ram) + set_device_ro(mk_kdev(HWIF(drive)->major, minor), 0); + + if (ide_cdrom_register (drive, nslots)) { + printk ("%s: ide_cdrom_setup failed to register device with the cdrom driver.\n", drive->name); + info->devinfo.handle = NULL; + return 1; + } + ide_cdrom_add_settings(drive); + return 0; +} + +/* Forwarding functions to generic routines. */ +static +int ide_cdrom_ioctl (ide_drive_t *drive, + struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return cdrom_ioctl (inode, file, cmd, arg); +} + +static +int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + int rc = -ENOMEM; + + MOD_INC_USE_COUNT; + if (info->buffer == NULL) + info->buffer = (char *) kmalloc(SECTOR_BUFFER_SIZE, GFP_KERNEL); + if ((info->buffer == NULL) || (rc = cdrom_open(ip, fp))) { + drive->usage--; + MOD_DEC_USE_COUNT; + } + return rc; +} + +static +void ide_cdrom_release (struct inode *inode, struct file *file, + ide_drive_t *drive) +{ + cdrom_release (inode, file); + MOD_DEC_USE_COUNT; +} + +static +int ide_cdrom_check_media_change (ide_drive_t *drive) +{ + return cdrom_media_changed(mk_kdev(HWIF (drive)->major, + (drive->select.b.unit) << PARTN_BITS)); +} + +static +void ide_cdrom_revalidate (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + int unit = drive - hwif->drives; + struct gendisk *g = hwif->gd[unit]; + struct request_sense sense; + + cdrom_read_toc(drive, &sense); + g->minor_shift = 0; + grok_partitions(mk_kdev(g->major, drive->select.b.unit), current_capacity(drive)); +} + +static +unsigned long ide_cdrom_capacity (ide_drive_t *drive) +{ + unsigned long capacity; + + if (cdrom_read_capacity(drive, &capacity, NULL)) + return 0; + + return capacity * SECTORS_PER_FRAME; +} + +static +int ide_cdrom_cleanup(ide_drive_t *drive) +{ + struct cdrom_info *info = drive->driver_data; + struct cdrom_device_info *devinfo = &info->devinfo; + + if (ide_unregister_subdriver (drive)) + return 1; + if (info->buffer != NULL) + kfree(info->buffer); + if (info->toc != NULL) + kfree(info->toc); + if (info->changer_info != NULL) + kfree(info->changer_info); + if (devinfo->handle == drive && unregister_cdrom (devinfo)) + printk ("%s: ide_cdrom_cleanup failed to unregister device from the cdrom driver.\n", drive->name); + kfree(info); + drive->driver_data = NULL; + return 0; +} + +int ide_cdrom_init(void); +int ide_cdrom_reinit (ide_drive_t *drive); + +static ide_driver_t ide_cdrom_driver = { + name: "ide-cdrom", + version: IDECD_VERSION, + media: ide_cdrom, + busy: 0, +#ifdef CONFIG_IDEDMA_ONLYDISK + supports_dma: 0, +#else + supports_dma: 1, +#endif + supports_dsc_overlap: 1, + cleanup: ide_cdrom_cleanup, + standby: NULL, + suspend: NULL, + resume: NULL, + flushcache: NULL, + do_request: ide_do_rw_cdrom, + end_request: ide_cdrom_end_request, + sense: ide_cdrom_dump_status, + error: ide_cdrom_error, + ioctl: ide_cdrom_ioctl, + open: ide_cdrom_open, + release: ide_cdrom_release, + media_change: ide_cdrom_check_media_change, + revalidate: ide_cdrom_revalidate, + pre_reset: NULL, + capacity: ide_cdrom_capacity, + special: NULL, + proc: NULL, + init: ide_cdrom_init, + reinit: ide_cdrom_reinit, + ata_prebuilder: NULL, + atapi_prebuilder: NULL, +}; + +static ide_module_t ide_cdrom_module = { + IDE_DRIVER_MODULE, + ide_cdrom_init, + &ide_cdrom_driver, + NULL +}; + +/* options */ +char *ignore = NULL; + +MODULE_PARM(ignore, "s"); +MODULE_DESCRIPTION("ATAPI CD-ROM Driver"); + +int ide_cdrom_reinit (ide_drive_t *drive) +{ + struct cdrom_info *info; + int failed = 0; + + MOD_INC_USE_COUNT; + info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL); + if (info == NULL) { + printk ("%s: Can't allocate a cdrom structure\n", drive->name); + return 1; + } + if (ide_register_subdriver (drive, &ide_cdrom_driver, IDE_SUBDRIVER_VERSION)) { + printk ("%s: Failed to register the driver with ide.c\n", drive->name); + kfree (info); + return 1; + } + memset (info, 0, sizeof (struct cdrom_info)); + drive->driver_data = info; + DRIVER(drive)->busy++; + if (ide_cdrom_setup (drive)) { + DRIVER(drive)->busy--; + if (ide_cdrom_cleanup (drive)) + printk ("%s: ide_cdrom_cleanup failed in ide_cdrom_init\n", drive->name); + return 1; + } + DRIVER(drive)->busy--; + failed--; + + ide_register_module(&ide_cdrom_module); + MOD_DEC_USE_COUNT; + return 0; +} + +static void __exit ide_cdrom_exit(void) +{ + ide_drive_t *drive; + int failed = 0; + + while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, &ide_cdrom_driver, failed)) != NULL) + if (ide_cdrom_cleanup (drive)) { + printk ("%s: cleanup_module() called while still busy\n", drive->name); + failed++; + } + ide_unregister_module (&ide_cdrom_module); +} + +int ide_cdrom_init(void) +{ + ide_drive_t *drive; + struct cdrom_info *info; + int failed = 0; + + MOD_INC_USE_COUNT; + while ((drive = ide_scan_devices (ide_cdrom, ide_cdrom_driver.name, NULL, failed++)) != NULL) { + /* skip drives that we were told to ignore */ + if (ignore != NULL) { + if (strstr(ignore, drive->name)) { + printk("ide-cd: ignoring drive %s\n", drive->name); + continue; + } + } + if (drive->scsi) { + printk("ide-cd: passing drive %s to ide-scsi emulation.\n", drive->name); + continue; + } + info = (struct cdrom_info *) kmalloc (sizeof (struct cdrom_info), GFP_KERNEL); + if (info == NULL) { + printk ("%s: Can't allocate a cdrom structure\n", drive->name); + continue; + } + if (ide_register_subdriver (drive, &ide_cdrom_driver, IDE_SUBDRIVER_VERSION)) { + printk ("%s: Failed to register the driver with ide.c\n", drive->name); + kfree (info); + continue; + } + memset (info, 0, sizeof (struct cdrom_info)); + drive->driver_data = info; + DRIVER(drive)->busy++; + if (ide_cdrom_setup (drive)) { + DRIVER(drive)->busy--; + if (ide_cdrom_cleanup (drive)) + printk ("%s: ide_cdrom_cleanup failed in ide_cdrom_init\n", drive->name); + continue; + } + DRIVER(drive)->busy--; + failed--; + } + ide_register_module(&ide_cdrom_module); + MOD_DEC_USE_COUNT; + return 0; +} + +module_init(ide_cdrom_init); +module_exit(ide_cdrom_exit); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/ide/ide-cd.h b/drivers/ide/ide-cd.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-cd.h Fri Aug 16 14:35:01 2002 @@ -0,0 +1,744 @@ +/* + * linux/drivers/ide/ide_cd.h + * + * Copyright (C) 1996-98 Erik Andersen + * Copyright (C) 1998-2000 Jens Axboe + */ +#ifndef _IDE_CD_H +#define _IDE_CD_H + +#include +#include + +/* Turn this on to have the driver print out the meanings of the + ATAPI error codes. This will use up additional kernel-space + memory, though. */ + +#ifndef VERBOSE_IDE_CD_ERRORS +#define VERBOSE_IDE_CD_ERRORS 1 +#endif + + +/* Turning this on will remove code to work around various nonstandard + ATAPI implementations. If you know your drive follows the standard, + this will give you a slightly smaller kernel. */ + +#ifndef STANDARD_ATAPI +#define STANDARD_ATAPI 0 +#endif + + +/* Turning this on will disable the door-locking functionality. + This is apparently needed for supermount. */ + +#ifndef NO_DOOR_LOCKING +#define NO_DOOR_LOCKING 0 +#endif + +/************************************************************************/ + +#define SECTOR_BITS 9 +#ifndef SECTOR_SIZE +#define SECTOR_SIZE (1 << SECTOR_BITS) +#endif +#define SECTORS_PER_FRAME (CD_FRAMESIZE >> SECTOR_BITS) +#define SECTOR_BUFFER_SIZE (CD_FRAMESIZE * 32) +#define SECTORS_BUFFER (SECTOR_BUFFER_SIZE >> SECTOR_BITS) +#define SECTORS_MAX (131072 >> SECTOR_BITS) + +#define BLOCKS_PER_FRAME (CD_FRAMESIZE / BLOCK_SIZE) + +#define MIN(a,b) ((a) < (b) ? (a) : (b)) + +/* special command codes for strategy routine. */ +#define PACKET_COMMAND 4315 +#define REQUEST_SENSE_COMMAND 4316 +#define RESET_DRIVE_COMMAND 4317 + + +/* Configuration flags. These describe the capabilities of the drive. + They generally do not change after initialization, unless we learn + more about the drive from stuff failing. */ +struct ide_cd_config_flags { + __u8 drq_interrupt : 1; /* Device sends an interrupt when ready + for a packet command. */ + __u8 no_doorlock : 1; /* Drive cannot lock the door. */ + __u8 no_eject : 1; /* Drive cannot eject the disc. */ + __u8 nec260 : 1; /* Drive is a pre-1.2 NEC 260 drive. */ + __u8 playmsf_as_bcd : 1; /* PLAYMSF command takes BCD args. */ + __u8 tocaddr_as_bcd : 1; /* TOC addresses are in BCD. */ + __u8 toctracks_as_bcd : 1; /* TOC track numbers are in BCD. */ + __u8 subchan_as_bcd : 1; /* Subchannel info is in BCD. */ + __u8 is_changer : 1; /* Drive is a changer. */ + __u8 cd_r : 1; /* Drive can write to CD-R media . */ + __u8 cd_rw : 1; /* Drive can write to CD-R/W media . */ + __u8 dvd : 1; /* Drive is a DVD-ROM */ + __u8 dvd_r : 1; /* Drive can write DVD-R */ + __u8 dvd_ram : 1; /* Drive can write DVD-RAM */ + __u8 test_write : 1; /* Drive can fake writes */ + __u8 supp_disc_present : 1; /* Changer can report exact contents + of slots. */ + __u8 limit_nframes : 1; /* Drive does not provide data in + multiples of SECTOR_SIZE when more + than one interrupt is needed. */ + __u8 seeking : 1; /* Seeking in progress */ + __u8 audio_play : 1; /* can do audio related commands */ + __u8 close_tray : 1; /* can close the tray */ + __u8 writing : 1; /* pseudo write in progress */ + __u8 reserved : 3; + byte max_speed; /* Max speed of the drive */ +}; +#define CDROM_CONFIG_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->config_flags)) + + +/* State flags. These give information about the current state of the + drive, and will change during normal operation. */ +struct ide_cd_state_flags { + __u8 media_changed : 1; /* Driver has noticed a media change. */ + __u8 toc_valid : 1; /* Saved TOC information is current. */ + __u8 door_locked : 1; /* We think that the drive door is locked. */ + __u8 writing : 1; /* the drive is currently writing */ + __u8 reserved : 4; + byte current_speed; /* Current speed of the drive */ +}; + +#define CDROM_STATE_FLAGS(drive) (&(((struct cdrom_info *)(drive->driver_data))->state_flags)) + +struct packet_command { + char *buffer; + int buflen; + int stat; + int quiet; + int timeout; + struct request_sense *sense; + unsigned char c[12]; +}; + +/* Structure of a MSF cdrom address. */ +struct atapi_msf { + byte reserved; + byte minute; + byte second; + byte frame; +}; + +/* Space to hold the disk TOC. */ +#define MAX_TRACKS 99 +struct atapi_toc_header { + unsigned short toc_length; + byte first_track; + byte last_track; +}; + +struct atapi_toc_entry { + byte reserved1; +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 adr : 4; + __u8 control : 4; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + __u8 control : 4; + __u8 adr : 4; +#else +#error "Please fix " +#endif + byte track; + byte reserved2; + union { + unsigned lba; + struct atapi_msf msf; + } addr; +}; + +struct atapi_toc { + int last_session_lba; + int xa_flag; + unsigned long capacity; + struct atapi_toc_header hdr; + struct atapi_toc_entry ent[MAX_TRACKS+1]; + /* One extra for the leadout. */ +}; + + +/* This structure is annoyingly close to, but not identical with, + the cdrom_subchnl structure from cdrom.h. */ +struct atapi_cdrom_subchnl { + u_char acdsc_reserved; + u_char acdsc_audiostatus; + u_short acdsc_length; + u_char acdsc_format; + +#if defined(__BIG_ENDIAN_BITFIELD) + u_char acdsc_ctrl: 4; + u_char acdsc_adr: 4; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + u_char acdsc_adr: 4; + u_char acdsc_ctrl: 4; +#else +#error "Please fix " +#endif + u_char acdsc_trk; + u_char acdsc_ind; + union { + struct atapi_msf msf; + int lba; + } acdsc_absaddr; + union { + struct atapi_msf msf; + int lba; + } acdsc_reladdr; +}; + + + +/* This should probably go into cdrom.h along with the other + * generic stuff now in the Mt. Fuji spec. + */ +struct atapi_capabilities_page { + struct mode_page_header header; +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 parameters_saveable : 1; + __u8 reserved1 : 1; + __u8 page_code : 6; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + __u8 page_code : 6; + __u8 reserved1 : 1; + __u8 parameters_saveable : 1; +#else +#error "Please fix " +#endif + + byte page_length; + +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 reserved2 : 2; + /* Drive supports reading of DVD-RAM discs */ + __u8 dvd_ram_read : 1; + /* Drive supports reading of DVD-R discs */ + __u8 dvd_r_read : 1; + /* Drive supports reading of DVD-ROM discs */ + __u8 dvd_rom : 1; + /* Drive supports reading CD-R discs with addressing method 2 */ + __u8 method2 : 1; /* reserved in 1.2 */ + /* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */ + __u8 cd_rw_read : 1; /* reserved in 1.2 */ + /* Drive supports read from CD-R discs (orange book, part II) */ + __u8 cd_r_read : 1; /* reserved in 1.2 */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + /* Drive supports read from CD-R discs (orange book, part II) */ + __u8 cd_r_read : 1; /* reserved in 1.2 */ + /* Drive can read from CD-R/W (CD-E) discs (orange book, part III) */ + __u8 cd_rw_read : 1; /* reserved in 1.2 */ + /* Drive supports reading CD-R discs with addressing method 2 */ + __u8 method2 : 1; + /* Drive supports reading of DVD-ROM discs */ + __u8 dvd_rom : 1; + /* Drive supports reading of DVD-R discs */ + __u8 dvd_r_read : 1; + /* Drive supports reading of DVD-RAM discs */ + __u8 dvd_ram_read : 1; + __u8 reserved2 : 2; +#else +#error "Please fix " +#endif + +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 reserved3 : 2; + /* Drive can write DVD-RAM discs */ + __u8 dvd_ram_write : 1; + /* Drive can write DVD-R discs */ + __u8 dvd_r_write : 1; + __u8 reserved3a : 1; + /* Drive can fake writes */ + __u8 test_write : 1; + /* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */ + __u8 cd_rw_write : 1; /* reserved in 1.2 */ + /* Drive supports write to CD-R discs (orange book, part II) */ + __u8 cd_r_write : 1; /* reserved in 1.2 */ +#elif defined(__LITTLE_ENDIAN_BITFIELD) + /* Drive can write to CD-R discs (orange book, part II) */ + __u8 cd_r_write : 1; /* reserved in 1.2 */ + /* Drive can write to CD-R/W (CD-E) discs (orange book, part III) */ + __u8 cd_rw_write : 1; /* reserved in 1.2 */ + /* Drive can fake writes */ + __u8 test_write : 1; + __u8 reserved3a : 1; + /* Drive can write DVD-R discs */ + __u8 dvd_r_write : 1; + /* Drive can write DVD-RAM discs */ + __u8 dvd_ram_write : 1; + __u8 reserved3 : 2; +#else +#error "Please fix " +#endif + +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 reserved4 : 1; + /* Drive can read multisession discs. */ + __u8 multisession : 1; + /* Drive can read mode 2, form 2 data. */ + __u8 mode2_form2 : 1; + /* Drive can read mode 2, form 1 (XA) data. */ + __u8 mode2_form1 : 1; + /* Drive supports digital output on port 2. */ + __u8 digport2 : 1; + /* Drive supports digital output on port 1. */ + __u8 digport1 : 1; + /* Drive can deliver a composite audio/video data stream. */ + __u8 composite : 1; + /* Drive supports audio play operations. */ + __u8 audio_play : 1; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + /* Drive supports audio play operations. */ + __u8 audio_play : 1; + /* Drive can deliver a composite audio/video data stream. */ + __u8 composite : 1; + /* Drive supports digital output on port 1. */ + __u8 digport1 : 1; + /* Drive supports digital output on port 2. */ + __u8 digport2 : 1; + /* Drive can read mode 2, form 1 (XA) data. */ + __u8 mode2_form1 : 1; + /* Drive can read mode 2, form 2 data. */ + __u8 mode2_form2 : 1; + /* Drive can read multisession discs. */ + __u8 multisession : 1; + __u8 reserved4 : 1; +#else +#error "Please fix " +#endif + +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 reserved5 : 1; + /* Drive can return Media Catalog Number (UPC) info. */ + __u8 upc : 1; + /* Drive can return International Standard Recording Code info. */ + __u8 isrc : 1; + /* Drive supports C2 error pointers. */ + __u8 c2_pointers : 1; + /* R-W data will be returned deinterleaved and error corrected. */ + __u8 rw_corr : 1; + /* Subchannel reads can return combined R-W information. */ + __u8 rw_supported : 1; + /* Drive can continue a read cdda operation from a loss of streaming.*/ + __u8 cdda_accurate : 1; + /* Drive can read Red Book audio data. */ + __u8 cdda : 1; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + /* Drive can read Red Book audio data. */ + __u8 cdda : 1; + /* Drive can continue a read cdda operation from a loss of streaming.*/ + __u8 cdda_accurate : 1; + /* Subchannel reads can return combined R-W information. */ + __u8 rw_supported : 1; + /* R-W data will be returned deinterleaved and error corrected. */ + __u8 rw_corr : 1; + /* Drive supports C2 error pointers. */ + __u8 c2_pointers : 1; + /* Drive can return International Standard Recording Code info. */ + __u8 isrc : 1; + /* Drive can return Media Catalog Number (UPC) info. */ + __u8 upc : 1; + __u8 reserved5 : 1; +#else +#error "Please fix " +#endif + +#if defined(__BIG_ENDIAN_BITFIELD) + /* Drive mechanism types. */ + mechtype_t mechtype : 3; + __u8 reserved6 : 1; + /* Drive can eject a disc or changer cartridge. */ + __u8 eject : 1; + /* State of prevent/allow jumper. */ + __u8 prevent_jumper : 1; + /* Present state of door lock. */ + __u8 lock_state : 1; + /* Drive can lock the door. */ + __u8 lock : 1; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + + /* Drive can lock the door. */ + __u8 lock : 1; + /* Present state of door lock. */ + __u8 lock_state : 1; + /* State of prevent/allow jumper. */ + __u8 prevent_jumper : 1; + /* Drive can eject a disc or changer cartridge. */ + __u8 eject : 1; + __u8 reserved6 : 1; + /* Drive mechanism types. */ + mechtype_t mechtype : 3; +#else +#error "Please fix " +#endif + +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 reserved7 : 4; + /* Drive supports software slot selection. */ + __u8 sss : 1; /* reserved in 1.2 */ + /* Changer can report exact contents of slots. */ + __u8 disc_present : 1; /* reserved in 1.2 */ + /* Audio for each channel can be muted independently. */ + __u8 separate_mute : 1; + /* Audio level for each channel can be controlled independently. */ + __u8 separate_volume : 1; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + + /* Audio level for each channel can be controlled independently. */ + __u8 separate_volume : 1; + /* Audio for each channel can be muted independently. */ + __u8 separate_mute : 1; + /* Changer can report exact contents of slots. */ + __u8 disc_present : 1; /* reserved in 1.2 */ + /* Drive supports software slot selection. */ + __u8 sss : 1; /* reserved in 1.2 */ + __u8 reserved7 : 4; +#else +#error "Please fix " +#endif + + /* Note: the following four fields are returned in big-endian form. */ + /* Maximum speed (in kB/s). */ + unsigned short maxspeed; + /* Number of discrete volume levels. */ + unsigned short n_vol_levels; + /* Size of cache in drive, in kB. */ + unsigned short buffer_size; + /* Current speed (in kB/s). */ + unsigned short curspeed; + char pad[4]; +}; + + +struct atapi_mechstat_header { +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 fault : 1; + __u8 changer_state : 2; + __u8 curslot : 5; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + __u8 curslot : 5; + __u8 changer_state : 2; + __u8 fault : 1; +#else +#error "Please fix " +#endif + +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 mech_state : 3; + __u8 door_open : 1; + __u8 reserved1 : 4; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + __u8 reserved1 : 4; + __u8 door_open : 1; + __u8 mech_state : 3; +#else +#error "Please fix " +#endif + + byte curlba[3]; + byte nslots; + __u8 short slot_tablelen; +}; + + +struct atapi_slot { +#if defined(__BIG_ENDIAN_BITFIELD) + __u8 disc_present : 1; + __u8 reserved1 : 6; + __u8 change : 1; +#elif defined(__LITTLE_ENDIAN_BITFIELD) + __u8 change : 1; + __u8 reserved1 : 6; + __u8 disc_present : 1; +#else +#error "Please fix " +#endif + + byte reserved2[3]; +}; + +struct atapi_changer_info { + struct atapi_mechstat_header hdr; + struct atapi_slot slots[0]; +}; + +/* Extra per-device info for cdrom drives. */ +struct cdrom_info { + + /* Buffer for table of contents. NULL if we haven't allocated + a TOC buffer for this device yet. */ + + struct atapi_toc *toc; + + unsigned long sector_buffered; + unsigned long nsectors_buffered; + unsigned char *buffer; + + /* The result of the last successful request sense command + on this device. */ + struct request_sense sense_data; + + struct request request_sense_request; + struct packet_command request_sense_pc; + int dma; + int cmd; + unsigned long last_block; + unsigned long start_seek; + /* Buffer to hold mechanism status and changer slot table. */ + struct atapi_changer_info *changer_info; + + struct ide_cd_config_flags config_flags; + struct ide_cd_state_flags state_flags; + + /* Per-device info needed by cdrom.c generic driver. */ + struct cdrom_device_info devinfo; +}; + +/**************************************************************************** + * Descriptions of ATAPI error codes. + */ + +#define ARY_LEN(a) ((sizeof(a) / sizeof(a[0]))) + +/* This stuff should be in cdrom.h, since it is now generic... */ + +/* ATAPI sense keys (from table 140 of ATAPI 2.6) */ +#define NO_SENSE 0x00 +#define RECOVERED_ERROR 0x01 +#define NOT_READY 0x02 +#define MEDIUM_ERROR 0x03 +#define HARDWARE_ERROR 0x04 +#define ILLEGAL_REQUEST 0x05 +#define UNIT_ATTENTION 0x06 +#define DATA_PROTECT 0x07 +#define ABORTED_COMMAND 0x0b +#define MISCOMPARE 0x0e + + + +/* This stuff should be in cdrom.h, since it is now generic... */ +#if VERBOSE_IDE_CD_ERRORS + + /* The generic packet command opcodes for CD/DVD Logical Units, + * From Table 57 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */ +const struct { + unsigned short packet_command; + const char * const text; +} packet_command_texts[] = { + { GPCMD_TEST_UNIT_READY, "Test Unit Ready" }, + { GPCMD_REQUEST_SENSE, "Request Sense" }, + { GPCMD_FORMAT_UNIT, "Format Unit" }, + { GPCMD_INQUIRY, "Inquiry" }, + { GPCMD_START_STOP_UNIT, "Start/Stop Unit" }, + { GPCMD_PREVENT_ALLOW_MEDIUM_REMOVAL, "Prevent/Allow Medium Removal" }, + { GPCMD_READ_FORMAT_CAPACITIES, "Read Format Capacities" }, + { GPCMD_READ_CDVD_CAPACITY, "Read Cd/Dvd Capacity" }, + { GPCMD_READ_10, "Read 10" }, + { GPCMD_WRITE_10, "Write 10" }, + { GPCMD_SEEK, "Seek" }, + { GPCMD_WRITE_AND_VERIFY_10, "Write and Verify 10" }, + { GPCMD_VERIFY_10, "Verify 10" }, + { GPCMD_FLUSH_CACHE, "Flush Cache" }, + { GPCMD_READ_SUBCHANNEL, "Read Subchannel" }, + { GPCMD_READ_TOC_PMA_ATIP, "Read Table of Contents" }, + { GPCMD_READ_HEADER, "Read Header" }, + { GPCMD_PLAY_AUDIO_10, "Play Audio 10" }, + { GPCMD_GET_CONFIGURATION, "Get Configuration" }, + { GPCMD_PLAY_AUDIO_MSF, "Play Audio MSF" }, + { GPCMD_PLAYAUDIO_TI, "Play Audio TrackIndex" }, + { GPCMD_GET_EVENT_STATUS_NOTIFICATION, "Get Event Status Notification" }, + { GPCMD_PAUSE_RESUME, "Pause/Resume" }, + { GPCMD_STOP_PLAY_SCAN, "Stop Play/Scan" }, + { GPCMD_READ_DISC_INFO, "Read Disc Info" }, + { GPCMD_READ_TRACK_RZONE_INFO, "Read Track Rzone Info" }, + { GPCMD_RESERVE_RZONE_TRACK, "Reserve Rzone Track" }, + { GPCMD_SEND_OPC, "Send OPC" }, + { GPCMD_MODE_SELECT_10, "Mode Select 10" }, + { GPCMD_REPAIR_RZONE_TRACK, "Repair Rzone Track" }, + { GPCMD_MODE_SENSE_10, "Mode Sense 10" }, + { GPCMD_CLOSE_TRACK, "Close Track" }, + { GPCMD_BLANK, "Blank" }, + { GPCMD_SEND_EVENT, "Send Event" }, + { GPCMD_SEND_KEY, "Send Key" }, + { GPCMD_REPORT_KEY, "Report Key" }, + { GPCMD_LOAD_UNLOAD, "Load/Unload" }, + { GPCMD_SET_READ_AHEAD, "Set Read-ahead" }, + { GPCMD_READ_12, "Read 12" }, + { GPCMD_GET_PERFORMANCE, "Get Performance" }, + { GPCMD_SEND_DVD_STRUCTURE, "Send DVD Structure" }, + { GPCMD_READ_DVD_STRUCTURE, "Read DVD Structure" }, + { GPCMD_SET_STREAMING, "Set Streaming" }, + { GPCMD_READ_CD_MSF, "Read CD MSF" }, + { GPCMD_SCAN, "Scan" }, + { GPCMD_SET_SPEED, "Set Speed" }, + { GPCMD_PLAY_CD, "Play CD" }, + { GPCMD_MECHANISM_STATUS, "Mechanism Status" }, + { GPCMD_READ_CD, "Read CD" }, +}; + + + +/* From Table 303 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */ +const char * const sense_key_texts[16] = { + "No sense data", + "Recovered error", + "Not ready", + "Medium error", + "Hardware error", + "Illegal request", + "Unit attention", + "Data protect", + "(reserved)", + "(reserved)", + "(reserved)", + "Aborted command", + "(reserved)", + "(reserved)", + "Miscompare", + "(reserved)", +}; + +/* From Table 304 of the SFF8090 Ver. 3 (Mt. Fuji) draft standard. */ +const struct { + unsigned long asc_ascq; + const char * const text; +} sense_data_texts[] = { + { 0x000000, "No additional sense information" }, + { 0x000011, "Play operation in progress" }, + { 0x000012, "Play operation paused" }, + { 0x000013, "Play operation successfully completed" }, + { 0x000014, "Play operation stopped due to error" }, + { 0x000015, "No current audio status to return" }, + { 0x010c0a, "Write error - padding blocks added" }, + { 0x011700, "Recovered data with no error correction applied" }, + { 0x011701, "Recovered data with retries" }, + { 0x011702, "Recovered data with positive head offset" }, + { 0x011703, "Recovered data with negative head offset" }, + { 0x011704, "Recovered data with retries and/or CIRC applied" }, + { 0x011705, "Recovered data using previous sector ID" }, + { 0x011800, "Recovered data with error correction applied" }, + { 0x011801, "Recovered data with error correction and retries applied"}, + { 0x011802, "Recovered data - the data was auto-reallocated" }, + { 0x011803, "Recovered data with CIRC" }, + { 0x011804, "Recovered data with L-EC" }, + { 0x015d00, + "Failure prediction threshold exceeded - Predicted logical unit failure" }, + { 0x015d01, + "Failure prediction threshold exceeded - Predicted media failure" }, + { 0x015dff, "Failure prediction threshold exceeded - False" }, + { 0x017301, "Power calibration area almost full" }, + { 0x020400, "Logical unit not ready - cause not reportable" }, + /* Following is misspelled in ATAPI 2.6, _and_ in Mt. Fuji */ + { 0x020401, + "Logical unit not ready - in progress [sic] of becoming ready" }, + { 0x020402, "Logical unit not ready - initializing command required" }, + { 0x020403, "Logical unit not ready - manual intervention required" }, + { 0x020404, "Logical unit not ready - format in progress" }, + { 0x020407, "Logical unit not ready - operation in progress" }, + { 0x020408, "Logical unit not ready - long write in progress" }, + { 0x020600, "No reference position found (media may be upside down)" }, + { 0x023000, "Incompatible medium installed" }, + { 0x023a00, "Medium not present" }, + { 0x025300, "Media load or eject failed" }, + { 0x025700, "Unable to recover table of contents" }, + { 0x030300, "Peripheral device write fault" }, + { 0x030301, "No write current" }, + { 0x030302, "Excessive write errors" }, + { 0x030c00, "Write error" }, + { 0x030c01, "Write error - Recovered with auto reallocation" }, + { 0x030c02, "Write error - auto reallocation failed" }, + { 0x030c03, "Write error - recommend reassignment" }, + { 0x030c04, "Compression check miscompare error" }, + { 0x030c05, "Data expansion occurred during compress" }, + { 0x030c06, "Block not compressible" }, + { 0x030c07, "Write error - recovery needed" }, + { 0x030c08, "Write error - recovery failed" }, + { 0x030c09, "Write error - loss of streaming" }, + { 0x031100, "Unrecovered read error" }, + { 0x031106, "CIRC unrecovered error" }, + { 0x033101, "Format command failed" }, + { 0x033200, "No defect spare location available" }, + { 0x033201, "Defect list update failure" }, + { 0x035100, "Erase failure" }, + { 0x037200, "Session fixation error" }, + { 0x037201, "Session fixation error writin lead-in" }, + { 0x037202, "Session fixation error writin lead-out" }, + { 0x037300, "CD control error" }, + { 0x037302, "Power calibration area is full" }, + { 0x037303, "Power calibration area error" }, + { 0x037304, "Program memory area / RMA update failure" }, + { 0x037305, "Program memory area / RMA is full" }, + { 0x037306, "Program memory area / RMA is (almost) full" }, + + { 0x040200, "No seek complete" }, + { 0x040300, "Write fault" }, + { 0x040900, "Track following error" }, + { 0x040901, "Tracking servo failure" }, + { 0x040902, "Focus servo failure" }, + { 0x040903, "Spindle servo failure" }, + { 0x041500, "Random positioning error" }, + { 0x041501, "Mechanical positioning or changer error" }, + { 0x041502, "Positioning error detected by read of medium" }, + { 0x043c00, "Mechanical positioning or changer error" }, + { 0x044000, "Diagnostic failure on component (ASCQ)" }, + { 0x044400, "Internal CD/DVD logical unit failure" }, + { 0x04b600, "Media load mechanism failed" }, + { 0x051a00, "Parameter list length error" }, + { 0x052000, "Invalid command operation code" }, + { 0x052100, "Logical block address out of range" }, + { 0x052102, "Invalid address for write" }, + { 0x052400, "Invalid field in command packet" }, + { 0x052600, "Invalid field in parameter list" }, + { 0x052601, "Parameter not supported" }, + { 0x052602, "Parameter value invalid" }, + { 0x052700, "Write protected media" }, + { 0x052c00, "Command sequence error" }, + { 0x052c03, "Current program area is not empty" }, + { 0x052c04, "Current program area is empty" }, + { 0x053001, "Cannot read medium - unknown format" }, + { 0x053002, "Cannot read medium - incompatible format" }, + { 0x053900, "Saving parameters not supported" }, + { 0x054e00, "Overlapped commands attempted" }, + { 0x055302, "Medium removal prevented" }, + { 0x055500, "System resource failure" }, + { 0x056300, "End of user area encountered on this track" }, + { 0x056400, "Illegal mode for this track or incompatible medium" }, + { 0x056f00, "Copy protection key exchange failure - Authentication failure" }, + { 0x056f01, "Copy protection key exchange failure - Key not present" }, + { 0x056f02, "Copy protection key exchange failure - Key not established" }, + { 0x056f03, "Read of scrambled sector without authentication" }, + { 0x056f04, "Media region code is mismatched to logical unit" }, + { 0x056f05, "Drive region must be permanent / region reset count error" }, + { 0x057203, "Session fixation error - incomplete track in session" }, + { 0x057204, "Empty or partially written reserved track" }, + { 0x057205, "No more RZONE reservations are allowed" }, + { 0x05bf00, "Loss of streaming" }, + { 0x062800, "Not ready to ready transition, medium may have changed" }, + { 0x062900, "Power on, reset or hardware reset occurred" }, + { 0x062a00, "Parameters changed" }, + { 0x062a01, "Mode parameters changed" }, + { 0x062e00, "Insufficient time for operation" }, + { 0x063f00, "Logical unit operating conditions have changed" }, + { 0x063f01, "Microcode has been changed" }, + { 0x065a00, "Operator request or state change input (unspecified)" }, + { 0x065a01, "Operator medium removal request" }, + { 0x0bb900, "Play operation aborted" }, + + /* Here we use 0xff for the key (not a valid key) to signify + * that these can have _any_ key value associated with them... */ + { 0xff0401, "Logical unit is in process of becoming ready" }, + { 0xff0400, "Logical unit not ready, cause not reportable" }, + { 0xff0402, "Logical unit not ready, initializing command required" }, + { 0xff0403, "Logical unit not ready, manual intervention required" }, + { 0xff0500, "Logical unit does not respond to selection" }, + { 0xff0800, "Logical unit communication failure" }, + { 0xff0802, "Logical unit communication parity error" }, + { 0xff0801, "Logical unit communication time-out" }, + { 0xff2500, "Logical unit not supported" }, + { 0xff4c00, "Logical unit failed self-configuration" }, + { 0xff3e00, "Logical unit has not self-configured yet" }, +}; +#endif + + +#endif /* _IDE_CD_H */ diff -Nru a/drivers/ide/ide-cs.c b/drivers/ide/ide-cs.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-cs.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,492 @@ +/*====================================================================== + + A driver for PCMCIA IDE/ATA disk cards + + ide_cs.c 1.26 1999/11/16 02:10:49 + + The contents of this file are subject to the Mozilla Public + License Version 1.1 (the "License"); you may not use this file + except in compliance with the License. You may obtain a copy of + the License at http://www.mozilla.org/MPL/ + + Software distributed under the License is distributed on an "AS + IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or + implied. See the License for the specific language governing + rights and limitations under the License. + + The initial developer of the original code is David A. Hinds + . Portions created by David A. Hinds + are Copyright (C) 1999 David A. Hinds. All Rights Reserved. + + Alternatively, the contents of this file may be used under the + terms of the GNU General Public License version 2 (the "GPL"), in which + case the provisions of the GPL are applicable instead of the + above. If you wish to allow the use of your version of this file + only under the terms of the GPL and not to allow others to use + your version of this file under the MPL, indicate your decision + by deleting the provisions above and replace them with the notice + and other provisions required by the GPL. If you do not delete + the provisions above, a recipient may use your version of this + file under either the MPL or the GPL. + +======================================================================*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifdef PCMCIA_DEBUG +static int pc_debug = PCMCIA_DEBUG; +MODULE_PARM(pc_debug, "i"); +#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args) +static char *version = +"ide_cs.c 1.26 1999/11/16 02:10:49 (David Hinds)"; +#else +#define DEBUG(n, args...) +#endif + +/*====================================================================*/ + +/* Parameters that can be set with 'insmod' */ + +/* Bit map of interrupts to choose from */ +static u_int irq_mask = 0xdeb8; +static int irq_list[4] = { -1 }; + +MODULE_PARM(irq_mask, "i"); +MODULE_PARM(irq_list, "1-4i"); + +MODULE_LICENSE("GPL"); + + +/*====================================================================*/ + +static const char ide_major[] = { + IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, +#ifdef IDE4_MAJOR + IDE4_MAJOR, IDE5_MAJOR +#endif +}; + +typedef struct ide_info_t { + dev_link_t link; + int ndev; + dev_node_t node; + int hd; +} ide_info_t; + +static void ide_config(dev_link_t *link); +static void ide_release(u_long arg); +static int ide_event(event_t event, int priority, + event_callback_args_t *args); + +static dev_info_t dev_info = "ide-cs"; + +static dev_link_t *ide_attach(void); +static void ide_detach(dev_link_t *); + +static dev_link_t *dev_list = NULL; + +/*====================================================================*/ + +static void cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + +/*====================================================================== + + ide_attach() creates an "instance" of the driver, allocating + local data structures for one device. The device is registered + with Card Services. + +======================================================================*/ + +static dev_link_t *ide_attach(void) +{ + ide_info_t *info; + dev_link_t *link; + client_reg_t client_reg; + int i, ret; + + DEBUG(0, "ide_attach()\n"); + + /* Create new ide device */ + info = kmalloc(sizeof(*info), GFP_KERNEL); + if (!info) return NULL; + memset(info, 0, sizeof(*info)); + link = &info->link; link->priv = info; + + link->release.function = &ide_release; + link->release.data = (u_long)link; + link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO; + link->io.Attributes2 = IO_DATA_PATH_WIDTH_8; + link->io.IOAddrLines = 3; + link->irq.Attributes = IRQ_TYPE_EXCLUSIVE; + link->irq.IRQInfo1 = IRQ_INFO2_VALID|IRQ_LEVEL_ID; + if (irq_list[0] == -1) + link->irq.IRQInfo2 = irq_mask; + else + for (i = 0; i < 4; i++) + link->irq.IRQInfo2 |= 1 << irq_list[i]; + link->conf.Attributes = CONF_ENABLE_IRQ; + link->conf.Vcc = 50; + link->conf.IntType = INT_MEMORY_AND_IO; + + /* Register with Card Services */ + link->next = dev_list; + dev_list = link; + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &ide_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != CS_SUCCESS) { + cs_error(link->handle, RegisterClient, ret); + ide_detach(link); + return NULL; + } + + return link; +} /* ide_attach */ + +/*====================================================================== + + This deletes a driver "instance". The device is de-registered + with Card Services. If it has been released, all local data + structures are freed. Otherwise, the structures will be freed + when the device is released. + +======================================================================*/ + +static void ide_detach(dev_link_t *link) +{ + dev_link_t **linkp; + int ret; + + DEBUG(0, "ide_detach(0x%p)\n", link); + + /* Locate device structure */ + for (linkp = &dev_list; *linkp; linkp = &(*linkp)->next) + if (*linkp == link) break; + if (*linkp == NULL) + return; + + del_timer(&link->release); + if (link->state & DEV_CONFIG) + ide_release((u_long)link); + + if (link->handle) { + ret = CardServices(DeregisterClient, link->handle); + if (ret != CS_SUCCESS) + cs_error(link->handle, DeregisterClient, ret); + } + + /* Unlink, free device structure */ + *linkp = link->next; + kfree(link->priv); + +} /* ide_detach */ + +/*====================================================================== + + ide_config() is scheduled to run after a CARD_INSERTION event + is received, to configure the PCMCIA socket, and to make the + ide device available to the system. + +======================================================================*/ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +#define CFG_CHECK(fn, args...) \ +if (CardServices(fn, args) != 0) goto next_entry + +int idecs_register (int arg1, int arg2, int irq) +{ + hw_regs_t hw; + ide_init_hwif_ports(&hw, (ide_ioreg_t) arg1, (ide_ioreg_t) arg2, NULL); + hw.irq = irq; + hw.chipset = ide_pci; /* this enables IRQ sharing w/ PCI irqs */ + return ide_register_hw(&hw, NULL); +} + +void ide_config(dev_link_t *link) +{ + client_handle_t handle = link->handle; + ide_info_t *info = link->priv; + tuple_t tuple; + u_short buf[128]; + cisparse_t parse; + config_info_t conf; + cistpl_cftable_entry_t *cfg = &parse.cftable_entry; + cistpl_cftable_entry_t dflt = { 0 }; + int i, pass, last_ret, last_fn, hd=-1, io_base, ctl_base; + + DEBUG(0, "ide_config(0x%p)\n", link); + + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleOffset = 0; tuple.TupleDataMax = 255; + tuple.Attributes = 0; + tuple.DesiredTuple = CISTPL_CONFIG; + CS_CHECK(GetFirstTuple, handle, &tuple); + CS_CHECK(GetTupleData, handle, &tuple); + CS_CHECK(ParseTuple, handle, &tuple, &parse); + link->conf.ConfigBase = parse.config.base; + link->conf.Present = parse.config.rmask[0]; + + /* Configure card */ + link->state |= DEV_CONFIG; + + /* Not sure if this is right... look up the current Vcc */ + CS_CHECK(GetConfigurationInfo, handle, &conf); + link->conf.Vcc = conf.Vcc; + + pass = io_base = ctl_base = 0; + tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; + tuple.Attributes = 0; + CS_CHECK(GetFirstTuple, handle, &tuple); + while (1) { + CFG_CHECK(GetTupleData, handle, &tuple); + CFG_CHECK(ParseTuple, handle, &tuple, &parse); + + /* Check for matching Vcc, unless we're desperate */ + if (!pass) { + if (cfg->vcc.present & (1<vcc.param[CISTPL_POWER_VNOM]/10000) + goto next_entry; + } else if (dflt.vcc.present & (1<vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = + cfg->vpp1.param[CISTPL_POWER_VNOM]/10000; + else if (dflt.vpp1.present & (1<conf.Vpp1 = link->conf.Vpp2 = + dflt.vpp1.param[CISTPL_POWER_VNOM]/10000; + + if ((cfg->io.nwin > 0) || (dflt.io.nwin > 0)) { + cistpl_io_t *io = (cfg->io.nwin) ? &cfg->io : &dflt.io; + link->conf.ConfigIndex = cfg->index; + link->io.BasePort1 = io->win[0].base; + link->io.IOAddrLines = io->flags & CISTPL_IO_LINES_MASK; + if (!(io->flags & CISTPL_IO_16BIT)) + link->io.Attributes1 = IO_DATA_PATH_WIDTH_8; + if (io->nwin == 2) { + link->io.NumPorts1 = 8; + link->io.BasePort2 = io->win[1].base; + link->io.NumPorts2 = 1; + CFG_CHECK(RequestIO, link->handle, &link->io); + io_base = link->io.BasePort1; + ctl_base = link->io.BasePort2; + } else if ((io->nwin == 1) && (io->win[0].len >= 16)) { + link->io.NumPorts1 = io->win[0].len; + link->io.NumPorts2 = 0; + CFG_CHECK(RequestIO, link->handle, &link->io); + io_base = link->io.BasePort1; + ctl_base = link->io.BasePort1+0x0e; + } else goto next_entry; + /* If we've got this far, we're done */ + break; + } + + next_entry: + if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; + if (pass) { + CS_CHECK(GetNextTuple, handle, &tuple); + } else if (CardServices(GetNextTuple, handle, &tuple) != 0) { + CS_CHECK(GetFirstTuple, handle, &tuple); + memset(&dflt, 0, sizeof(dflt)); + pass++; + } + } + + CS_CHECK(RequestIRQ, handle, &link->irq); + CS_CHECK(RequestConfiguration, handle, &link->conf); + + /* deal with brain dead IDE resource management */ + release_region(link->io.BasePort1, link->io.NumPorts1); + if (link->io.NumPorts2) + release_region(link->io.BasePort2, link->io.NumPorts2); + + /* retry registration in case device is still spinning up */ + for (i = 0; i < 10; i++) { + if (ctl_base) + OUT_BYTE(0x02, ctl_base); /* Set nIEN = disable device interrupts */ + hd = idecs_register(io_base, ctl_base, link->irq.AssignedIRQ); + if (hd >= 0) break; + if (link->io.NumPorts1 == 0x20) { + if (ctl_base) + OUT_BYTE(0x02, ctl_base+0x10); + hd = idecs_register(io_base+0x10, ctl_base+0x10, + link->irq.AssignedIRQ); + if (hd >= 0) { + io_base += 0x10; ctl_base += 0x10; + break; + } + } + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/10); + } + + if (hd < 0) { + printk(KERN_NOTICE "ide_cs: ide_register() at 0x%03x & 0x%03x" + ", irq %u failed\n", io_base, ctl_base, + link->irq.AssignedIRQ); + goto failed; + } + + MOD_INC_USE_COUNT; + info->ndev = 1; + sprintf(info->node.dev_name, "hd%c", 'a'+(hd*2)); + info->node.major = ide_major[hd]; + info->node.minor = 0; + info->hd = hd; + link->dev = &info->node; + printk(KERN_INFO "ide_cs: %s: Vcc = %d.%d, Vpp = %d.%d\n", + info->node.dev_name, link->conf.Vcc/10, link->conf.Vcc%10, + link->conf.Vpp1/10, link->conf.Vpp1%10); + + link->state &= ~DEV_CONFIG_PENDING; + return; + +cs_failed: + cs_error(link->handle, last_fn, last_ret); +failed: + ide_release((u_long)link); + +} /* ide_config */ + +/*====================================================================== + + After a card is removed, ide_release() will unregister the net + device, and release the PCMCIA configuration. If the device is + still open, this will be postponed until it is closed. + +======================================================================*/ + +void ide_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + ide_info_t *info = link->priv; + + DEBUG(0, "ide_release(0x%p)\n", link); + + if (info->ndev) { + ide_unregister(info->hd); + MOD_DEC_USE_COUNT; + } + + request_region(link->io.BasePort1, link->io.NumPorts1,"ide-cs"); + if (link->io.NumPorts2) + request_region(link->io.BasePort2, link->io.NumPorts2,"ide-cs"); + + info->ndev = 0; + link->dev = NULL; + + CardServices(ReleaseConfiguration, link->handle); + CardServices(ReleaseIO, link->handle, &link->io); + CardServices(ReleaseIRQ, link->handle, &link->irq); + + link->state &= ~DEV_CONFIG; + +} /* ide_release */ + +/*====================================================================== + + The card status event handler. Mostly, this schedules other + stuff to run after an event is received. A CARD_REMOVAL event + also sets some flags to discourage the ide drivers from + talking to the ports. + +======================================================================*/ + +int ide_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + + DEBUG(1, "ide_event(0x%06x)\n", event); + + switch (event) { + case CS_EVENT_CARD_REMOVAL: + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) + mod_timer(&link->release, jiffies + HZ/20); + break; + case CS_EVENT_CARD_INSERTION: + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + ide_config(link); + break; + case CS_EVENT_PM_SUSPEND: + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + if (link->state & DEV_CONFIG) + CardServices(ReleaseConfiguration, link->handle); + break; + case CS_EVENT_PM_RESUME: + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + if (DEV_OK(link)) + CardServices(RequestConfiguration, link->handle, &link->conf); + break; + } + return 0; +} /* ide_event */ + +/*====================================================================*/ + +static int __init init_ide_cs(void) +{ + servinfo_t serv; + DEBUG(0, "%s\n", version); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + printk(KERN_NOTICE "ide_cs: Card Services release " + "does not match!\n"); + return -1; + } + register_pccard_driver(&dev_info, &ide_attach, &ide_detach); + return 0; +} + +static void __exit exit_ide_cs(void) +{ + DEBUG(0, "ide_cs: unloading\n"); + unregister_pccard_driver(&dev_info); + while (dev_list != NULL) + ide_detach(dev_list); +} + +module_init(init_ide_cs); +module_exit(exit_ide_cs); diff -Nru a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-disk.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,1833 @@ +/* + * linux/drivers/ide/ide-disk.c Version 1.16 April 7, 2002 + * + * Copyright (C) 1998-2002 Linux ATA Developemt + * Andre Hedrick + * + * + * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) + */ + +/* + * Mostly written by Mark Lord + * and Gadi Oxman + * and Andre Hedrick + * + * This is the IDE/ATA disk driver, as evolved from hd.c and ide.c. + * + * Version 1.00 move disk only code from ide.c to ide-disk.c + * support optional byte-swapping of all data + * Version 1.01 fix previous byte-swapping code + * Version 1.02 remove ", LBA" from drive identification msgs + * Version 1.03 fix display of id->buf_size for big-endian + * Version 1.04 add /proc configurable settings and S.M.A.R.T support + * Version 1.05 add capacity support for ATA3 >= 8GB + * Version 1.06 get boot-up messages to show full cyl count + * Version 1.07 disable door-locking if it fails + * Version 1.08 fixed CHS/LBA translations for ATA4 > 8GB, + * process of adding new ATA4 compliance. + * fixed problems in allowing fdisk to see + * the entire disk. + * Version 1.09 added increment of rq->sector in ide_multwrite + * added UDMA 3/4 reporting + * Version 1.10 request queue changes, Ultra DMA 100 + * Version 1.11 added 48-bit lba + * Version 1.12 adding taskfile io access method + * Version 1.13 added standby and flush-cache for notifier + * Version 1.14 added acoustic-wcache + * Version 1.15 convert all calls to ide_raw_taskfile + * since args will return register content. + * Version 1.16 added suspend-resume-checkpower + */ + +#define IDEDISK_VERSION "1.16" + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef CONFIG_BLK_DEV_PDC4030 +#define IS_PDC4030_DRIVE (HWIF(drive)->chipset == ide_pdc4030) +#else +#define IS_PDC4030_DRIVE (0) /* auto-NULLs out pdc4030 code */ +#endif + +static int driver_blocked; + +static inline u32 idedisk_read_24 (ide_drive_t *drive) +{ + return (IN_BYTE(IDE_HCYL_REG)<<16) | + (IN_BYTE(IDE_LCYL_REG)<<8) | + IN_BYTE(IDE_SECTOR_REG); +} + +static int idedisk_end_request(ide_drive_t *drive, int uptodate); + +/* + * lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity" + * value for this drive (from its reported identification information). + * + * Returns: 1 if lba_capacity looks sensible + * 0 otherwise + * + * It is called only once for each drive. + */ +static int lba_capacity_is_ok (struct hd_driveid *id) +{ + unsigned long lba_sects, chs_sects, head, tail; + + if ((id->command_set_2 & 0x0400) && (id->cfs_enable_2 & 0x0400)) { + printk("48-bit Drive: %llu \n", id->lba_capacity_2); + return 1; + } + + /* + * The ATA spec tells large drives to return + * C/H/S = 16383/16/63 independent of their size. + * Some drives can be jumpered to use 15 heads instead of 16. + * Some drives can be jumpered to use 4092 cyls instead of 16383. + */ + if ((id->cyls == 16383 + || (id->cyls == 4092 && id->cur_cyls == 16383)) && + id->sectors == 63 && + (id->heads == 15 || id->heads == 16) && + id->lba_capacity >= 16383*63*id->heads) + return 1; + + lba_sects = id->lba_capacity; + chs_sects = id->cyls * id->heads * id->sectors; + + /* perform a rough sanity check on lba_sects: within 10% is OK */ + if ((lba_sects - chs_sects) < chs_sects/10) + return 1; + + /* some drives have the word order reversed */ + head = ((lba_sects >> 16) & 0xffff); + tail = (lba_sects & 0xffff); + lba_sects = (head | (tail << 16)); + if ((lba_sects - chs_sects) < chs_sects/10) { + id->lba_capacity = lba_sects; + return 1; /* lba_capacity is (now) good */ + } + + return 0; /* lba_capacity value may be bad */ +} + +#ifndef CONFIG_IDE_TASKFILE_IO + +/* + * read_intr() is the handler for disk read/multread interrupts + */ +static ide_startstop_t read_intr (ide_drive_t *drive) +{ + byte stat; + int i; + unsigned int msect, nsect; + struct request *rq; + unsigned long flags; + char *to; + + /* new way for dealing with premature shared PCI interrupts */ + if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) { + if (stat & (ERR_STAT|DRQ_STAT)) { + return DRIVER(drive)->error(drive, "read_intr", stat); + } + /* no data yet, so wait for another interrupt */ + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, &read_intr, WAIT_CMD, NULL); + return ide_started; + } + msect = drive->mult_count; + +read_next: + rq = HWGROUP(drive)->rq; + if (msect) { + if ((nsect = rq->current_nr_sectors) > msect) + nsect = msect; + msect -= nsect; + } else + nsect = 1; + to = ide_map_buffer(rq, &flags); + taskfile_input_data(drive, to, nsect * SECTOR_WORDS); +#ifdef DEBUG + printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n", + drive->name, rq->sector, rq->sector+nsect-1, + (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect); +#endif + ide_unmap_buffer(to, &flags); + rq->sector += nsect; + rq->errors = 0; + i = (rq->nr_sectors -= nsect); + if (((long)(rq->current_nr_sectors -= nsect)) <= 0) + idedisk_end_request(drive, 1); + /* + * Another BH Page walker and DATA INTERGRITY Questioned on ERROR. + * If passed back up on multimode read, BAD DATA could be ACKED + * to FILE SYSTEMS above ... + */ + if (i > 0) { + if (msect) + goto read_next; + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &read_intr, WAIT_CMD, NULL); + return ide_started; + } + return ide_stopped; +} + +/* + * write_intr() is the handler for disk write interrupts + */ +static ide_startstop_t write_intr (ide_drive_t *drive) +{ + byte stat; + int i; + ide_hwgroup_t *hwgroup = HWGROUP(drive); + struct request *rq = hwgroup->rq; + + if (!OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) { + printk("%s: write_intr error1: nr_sectors=%ld, stat=0x%02x\n", drive->name, rq->nr_sectors, stat); + } else { +#ifdef DEBUG + printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n", + drive->name, rq->sector, (unsigned long) rq->buffer, + rq->nr_sectors-1); +#endif + if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) { + rq->sector++; + rq->errors = 0; + i = --rq->nr_sectors; + --rq->current_nr_sectors; + if (((long)rq->current_nr_sectors) <= 0) + idedisk_end_request(drive, 1); + if (i > 0) { + unsigned long flags; + char *to = ide_map_buffer(rq, &flags); + taskfile_output_data(drive, to, SECTOR_WORDS); + ide_unmap_buffer(to, &flags); + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, &write_intr, WAIT_CMD, NULL); + return ide_started; + } + return ide_stopped; + } + return ide_stopped; /* the original code did this here (?) */ + } + return DRIVER(drive)->error(drive, "write_intr", stat); +} + +/* + * ide_multwrite() transfers a block of up to mcount sectors of data + * to a drive as part of a disk multiple-sector write operation. + * + * Returns 0 on success. + * + * Note that we may be called from two contexts - the do_rw_disk context + * and IRQ context. The IRQ can happen any time after we've output the + * full "mcount" number of sectors, so we must make sure we update the + * state _before_ we output the final part of the data! + * + * The update and return to BH is a BLOCK Layer Fakey to get more data + * to satisfy the hardware atomic segment. If the hardware atomic segment + * is shorter or smaller than the BH segment then we should be OKAY. + * This is only valid if we can rewind the rq->current_nr_sectors counter. + */ +int ide_multwrite (ide_drive_t *drive, unsigned int mcount) +{ + ide_hwgroup_t *hwgroup= HWGROUP(drive); + struct request *rq = &hwgroup->wrq; + + do { + char *buffer; + int nsect = rq->current_nr_sectors; + unsigned long flags; + + if (nsect > mcount) + nsect = mcount; + mcount -= nsect; + + buffer = ide_map_buffer(rq, &flags); + rq->sector += nsect; + rq->nr_sectors -= nsect; + rq->current_nr_sectors -= nsect; + + /* Do we move to the next bh after this? */ + if (!rq->current_nr_sectors) { + struct bio *bio = rq->bio; + + /* + * only move to next bio, when we have processed + * all bvecs in this one. + */ + if (++bio->bi_idx >= bio->bi_vcnt) { + bio->bi_idx = 0; + bio = bio->bi_next; + } + + /* end early early we ran out of requests */ + if (!bio) { + mcount = 0; + } else { + rq->bio = bio; + rq->current_nr_sectors = bio_iovec(bio)->bv_len >> 9; + rq->hard_cur_sectors = rq->current_nr_sectors; + } + } + + /* + * Ok, we're all setup for the interrupt + * re-entering us on the last transfer. + */ + taskfile_output_data(drive, buffer, nsect<<7); + ide_unmap_buffer(buffer, &flags); + } while (mcount); + + return 0; +} + +/* + * multwrite_intr() is the handler for disk multwrite interrupts + */ +static ide_startstop_t multwrite_intr (ide_drive_t *drive) +{ + byte stat; + int i; + ide_hwgroup_t *hwgroup = HWGROUP(drive); + struct request *rq = &hwgroup->wrq; + + if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) { + if (stat & DRQ_STAT) { + /* + * The drive wants data. Remember rq is the copy + * of the request + */ + if (rq->nr_sectors) { + if (ide_multwrite(drive, drive->mult_count)) + return ide_stopped; + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, &multwrite_intr, WAIT_CMD, NULL); + return ide_started; + } + } else { + /* + * If the copy has all the blocks completed then + * we can end the original request. + */ + if (!rq->nr_sectors) { /* all done? */ + rq = hwgroup->rq; + for (i = rq->nr_sectors; i > 0;){ + i -= rq->current_nr_sectors; + idedisk_end_request(drive, 1); + } + return ide_stopped; + } + } + return ide_stopped; /* the original code did this here (?) */ + } + return DRIVER(drive)->error(drive, "multwrite_intr", stat); +} +#endif /* CONFIG_IDE_TASKFILE_IO */ + +#ifdef CONFIG_IDE_TASKFILE_IO + +static ide_startstop_t chs_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block); +static ide_startstop_t lba_28_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block); +static ide_startstop_t lba_48_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long long block); + +/* + * do_rw_disk() issues READ and WRITE commands to a disk, + * using LBA if supported, or CHS otherwise, to address sectors. + * It also takes care of issuing special DRIVE_CMDs. + */ +static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + if (!(rq->flags & REQ_CMD)) { + blk_dump_rq_flags(rq, "do_rw_disk - bad command"); + idedisk_end_request(drive, 0); + return ide_stopped; + } + +#ifdef CONFIG_BLK_DEV_PDC4030 + if (IS_PDC4030_DRIVE) { + extern ide_startstop_t promise_rw_disk(ide_drive_t *, struct request *, unsigned long); + return promise_rw_disk(drive, rq, block); + } +#endif /* CONFIG_BLK_DEV_PDC4030 */ + + if ((drive->id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) /* 48-bit LBA */ + return lba_48_rw_disk(drive, rq, (unsigned long long) block); + if (drive->select.b.lba) /* 28-bit LBA */ + return lba_28_rw_disk(drive, rq, (unsigned long) block); + + /* 28-bit CHS : DIE DIE DIE piece of legacy crap!!! */ + return chs_rw_disk(drive, rq, (unsigned long) block); +} + +static task_ioreg_t get_command (ide_drive_t *drive, int cmd) +{ + int lba48bit = (drive->id->cfs_enable_2 & 0x0400) ? 1 : 0; + +#if 1 + lba48bit = (drive->addressing == 1) ? 1 : 0; +#endif + + if ((cmd == READ) && (drive->using_dma)) + return (lba48bit) ? WIN_READDMA_EXT : WIN_READDMA; + else if ((cmd == READ) && (drive->mult_count)) + return (lba48bit) ? WIN_MULTREAD_EXT : WIN_MULTREAD; + else if (cmd == READ) + return (lba48bit) ? WIN_READ_EXT : WIN_READ; + else if ((cmd == WRITE) && (drive->using_dma)) + return (lba48bit) ? WIN_WRITEDMA_EXT : WIN_WRITEDMA; + else if ((cmd == WRITE) && (drive->mult_count)) + return (lba48bit) ? WIN_MULTWRITE_EXT : WIN_MULTWRITE; + else if (cmd == WRITE) + return (lba48bit) ? WIN_WRITE_EXT : WIN_WRITE; + else + return WIN_NOP; +} + +static ide_startstop_t chs_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + ide_task_t args; + int sectors; + task_ioreg_t command = get_command(drive, rq_data_dir(rq)); + unsigned int track = (block / drive->sect); + unsigned int sect = (block % drive->sect) + 1; + unsigned int head = (track % drive->head); + unsigned int cyl = (track / drive->head); + +#ifdef DEBUG + printk("%s: %sing: ", drive->name, (rq_data_dir(rq)==READ) ? "read" : "writ"); + printk("CHS=%d/%d/%d, ", cyl, head, sect); + printk("sectors=%ld, ", rq->nr_sectors); + printk("buffer=0x%08lx\n", (unsigned long) rq->buffer); +#endif + + memset(&args, 0, sizeof(ide_task_t)); + + sectors = (rq->nr_sectors == 256) ? 0x00 : rq->nr_sectors; + args.tfRegister[IDE_NSECTOR_OFFSET] = sectors; + args.tfRegister[IDE_SECTOR_OFFSET] = sect; + args.tfRegister[IDE_LCYL_OFFSET] = cyl; + args.tfRegister[IDE_HCYL_OFFSET] = (cyl>>8); + args.tfRegister[IDE_SELECT_OFFSET] = head; + args.tfRegister[IDE_SELECT_OFFSET] |= drive->select.all; + args.tfRegister[IDE_COMMAND_OFFSET] = command; + args.command_type = ide_cmd_type_parser(&args); + args.rq = (struct request *) rq; + rq->special = (ide_task_t *)&args; + return do_rw_taskfile(drive, &args); +} + +static ide_startstop_t lba_28_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + ide_task_t args; + int sectors; + task_ioreg_t command = get_command(drive, rq_data_dir(rq)); + +#ifdef DEBUG + printk("%s: %sing: ", drive->name, (rq_data_dir(rq)==READ) ? "read" : "writ"); + printk("LBAsect=%lld, ", block); + printk("sectors=%ld, ", rq->nr_sectors); + printk("buffer=0x%08lx\n", (unsigned long) rq->buffer); +#endif + + memset(&args, 0, sizeof(ide_task_t)); + + sectors = (rq->nr_sectors == 256) ? 0x00 : rq->nr_sectors; + args.tfRegister[IDE_NSECTOR_OFFSET] = sectors; + args.tfRegister[IDE_SECTOR_OFFSET] = block; + args.tfRegister[IDE_LCYL_OFFSET] = (block>>=8); + args.tfRegister[IDE_HCYL_OFFSET] = (block>>=8); + args.tfRegister[IDE_SELECT_OFFSET] = ((block>>8)&0x0f); + args.tfRegister[IDE_SELECT_OFFSET] |= drive->select.all; + args.tfRegister[IDE_COMMAND_OFFSET] = command; + args.command_type = ide_cmd_type_parser(&args); + args.rq = (struct request *) rq; + rq->special = (ide_task_t *)&args; + return do_rw_taskfile(drive, &args); +} + +/* + * 268435455 == 137439 MB or 28bit limit + * 320173056 == 163929 MB or 48bit addressing + * 1073741822 == 549756 MB or 48bit addressing fake drive + */ + +static ide_startstop_t lba_48_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long long block) +{ + ide_task_t args; + int sectors; + task_ioreg_t command = get_command(drive, rq_data_dir(rq)); + +#ifdef DEBUG + printk("%s: %sing: ", drive->name, (rq_data_dir(rq)==READ) ? "read" : "writ"); + printk("LBAsect=%lld, ", block); + printk("sectors=%ld, ", rq->nr_sectors); + printk("buffer=0x%08lx\n", (unsigned long) rq->buffer); +#endif + + memset(&args, 0, sizeof(ide_task_t)); + + sectors = (rq->nr_sectors == 65536) ? 0 : rq->nr_sectors; + args.tfRegister[IDE_NSECTOR_OFFSET] = sectors; + args.tfRegister[IDE_SECTOR_OFFSET] = block; /* low lba */ + args.tfRegister[IDE_LCYL_OFFSET] = (block>>=8); /* mid lba */ + args.tfRegister[IDE_HCYL_OFFSET] = (block>>=8); /* hi lba */ + args.tfRegister[IDE_SELECT_OFFSET] = drive->select.all; + args.tfRegister[IDE_COMMAND_OFFSET] = command; + args.hobRegister[IDE_NSECTOR_OFFSET_HOB]= sectors >> 8; + args.hobRegister[IDE_SECTOR_OFFSET_HOB] = (block>>=8); /* low lba */ + args.hobRegister[IDE_LCYL_OFFSET_HOB] = (block>>=8); /* mid lba */ + args.hobRegister[IDE_HCYL_OFFSET_HOB] = (block>>=8); /* hi lba */ + args.hobRegister[IDE_SELECT_OFFSET_HOB] = drive->select.all; + args.hobRegister[IDE_CONTROL_OFFSET_HOB]= (drive->ctl|0x80); + args.command_type = ide_cmd_type_parser(&args); + args.rq = (struct request *) rq; + rq->special = (ide_task_t *)&args; + return do_rw_taskfile(drive, &args); +} + +#else /* !CONFIG_IDE_TASKFILE_IO */ + +/* + * do_rw_disk() issues READ and WRITE commands to a disk, + * using LBA if supported, or CHS otherwise, to address sectors. + * It also takes care of issuing special DRIVE_CMDs. + */ +static ide_startstop_t do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + if (!(rq->flags & REQ_CMD)) { + blk_dump_rq_flags(rq, "do_rw_disk - bad command"); + return ide_stopped; + } + + if (driver_blocked) + panic("Request while ide driver is blocked?"); + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl,IDE_CONTROL_REG); + +#ifdef CONFIG_BLK_DEV_PDC4030 + if (drive->select.b.lba || IS_PDC4030_DRIVE) { +#else /* !CONFIG_BLK_DEV_PDC4030 */ + if (drive->select.b.lba) { +#endif /* CONFIG_BLK_DEV_PDC4030 */ + + if ((drive->id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + task_ioreg_t tasklets[10]; + + tasklets[0] = 0; + tasklets[1] = 0; + tasklets[2] = rq->nr_sectors; + tasklets[3] = (rq->nr_sectors>>8); + if (rq->nr_sectors == 65536) { + tasklets[2] = 0x00; + tasklets[3] = 0x00; + } + tasklets[4] = (task_ioreg_t) block; + tasklets[5] = (task_ioreg_t) (block>>8); + tasklets[6] = (task_ioreg_t) (block>>16); + tasklets[7] = (task_ioreg_t) (block>>24); + tasklets[8] = (task_ioreg_t) 0; + tasklets[9] = (task_ioreg_t) 0; +// tasklets[8] = (task_ioreg_t) (block>>32); +// tasklets[9] = (task_ioreg_t) (block>>40); +#ifdef DEBUG + printk("%s: %sing: LBAsect=%lu, sectors=%ld, buffer=0x%08lx, LBAsect=0x%012lx\n", + drive->name, + (rq_data_dir(rq)==READ)?"read":"writ", + block, + rq->nr_sectors, + (unsigned long) rq->buffer, + block); + printk("%s: 0x%02x%02x 0x%02x%02x%02x%02x%02x%02x\n", + drive->name, tasklets[3], tasklets[2], + tasklets[9], tasklets[8], tasklets[7], + tasklets[6], tasklets[5], tasklets[4]); +#endif + OUT_BYTE(tasklets[1], IDE_FEATURE_REG); + OUT_BYTE(tasklets[3], IDE_NSECTOR_REG); + OUT_BYTE(tasklets[7], IDE_SECTOR_REG); + OUT_BYTE(tasklets[8], IDE_LCYL_REG); + OUT_BYTE(tasklets[9], IDE_HCYL_REG); + + OUT_BYTE(tasklets[0], IDE_FEATURE_REG); + OUT_BYTE(tasklets[2], IDE_NSECTOR_REG); + OUT_BYTE(tasklets[4], IDE_SECTOR_REG); + OUT_BYTE(tasklets[5], IDE_LCYL_REG); + OUT_BYTE(tasklets[6], IDE_HCYL_REG); + OUT_BYTE(0x00|drive->select.all,IDE_SELECT_REG); + } else { +#ifdef DEBUG + printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n", + drive->name, (rq_data_dir(rq)==READ)?"read":"writ", + block, rq->nr_sectors, (unsigned long) rq->buffer); +#endif + OUT_BYTE(0x00, IDE_FEATURE_REG); + OUT_BYTE((rq->nr_sectors==256)?0x00:rq->nr_sectors,IDE_NSECTOR_REG); + OUT_BYTE(block,IDE_SECTOR_REG); + OUT_BYTE(block>>=8,IDE_LCYL_REG); + OUT_BYTE(block>>=8,IDE_HCYL_REG); + OUT_BYTE(((block>>8)&0x0f)|drive->select.all,IDE_SELECT_REG); + } + } else { + unsigned int sect,head,cyl,track; + track = block / drive->sect; + sect = block % drive->sect + 1; + OUT_BYTE(sect,IDE_SECTOR_REG); + head = track % drive->head; + cyl = track / drive->head; + + OUT_BYTE(0x00, IDE_FEATURE_REG); + OUT_BYTE((rq->nr_sectors==256)?0x00:rq->nr_sectors,IDE_NSECTOR_REG); + OUT_BYTE(cyl,IDE_LCYL_REG); + OUT_BYTE(cyl>>8,IDE_HCYL_REG); + OUT_BYTE(head|drive->select.all,IDE_SELECT_REG); +#ifdef DEBUG + printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n", + drive->name, (rq_data_dir(rq)==READ)?"read":"writ", cyl, + head, sect, rq->nr_sectors, (unsigned long) rq->buffer); +#endif + } +#ifdef CONFIG_BLK_DEV_PDC4030 + if (IS_PDC4030_DRIVE) { + extern ide_startstop_t do_pdc4030_io(ide_drive_t *, struct request *); + return do_pdc4030_io (drive, rq); + } +#endif /* CONFIG_BLK_DEV_PDC4030 */ + if (rq_data_dir(rq) == READ) { +#ifdef CONFIG_BLK_DEV_IDEDMA + if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive))) + return ide_started; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, &read_intr, WAIT_CMD, NULL); + if ((drive->id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + OUT_BYTE(drive->mult_count ? WIN_MULTREAD_EXT : WIN_READ_EXT, IDE_COMMAND_REG); + } else { + OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, IDE_COMMAND_REG); + } + return ide_started; + } else if (rq_data_dir(rq) == WRITE) { + ide_startstop_t startstop; +#ifdef CONFIG_BLK_DEV_IDEDMA + if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive))) + return ide_started; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + if ((drive->id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + OUT_BYTE(drive->mult_count ? WIN_MULTWRITE_EXT : WIN_WRITE_EXT, IDE_COMMAND_REG); + } else { + OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, IDE_COMMAND_REG); + } + if (ide_wait_stat(&startstop, drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) { + printk(KERN_ERR "%s: no DRQ after issuing %s\n", drive->name, + drive->mult_count ? "MULTWRITE" : "WRITE"); + return startstop; + } + if (!drive->unmask) + local_irq_disable(); + if (drive->mult_count) { + ide_hwgroup_t *hwgroup = HWGROUP(drive); + /* + * Ugh.. this part looks ugly because we MUST set up + * the interrupt handler before outputting the first block + * of data to be written. If we hit an error (corrupted buffer list) + * in ide_multwrite(), then we need to remove the handler/timer + * before returning. Fortunately, this NEVER happens (right?). + * + * Except when you get an error it seems... + * + * MAJOR DATA INTEGRITY BUG !!! only if we error + */ + hwgroup->wrq = *rq; /* scratchpad */ + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, &multwrite_intr, WAIT_CMD, NULL); + if (ide_multwrite(drive, drive->mult_count)) { + unsigned long flags; + spin_lock_irqsave(&ide_lock, flags); + hwgroup->handler = NULL; + del_timer(&hwgroup->timer); + spin_unlock_irqrestore(&ide_lock, flags); + return ide_stopped; + } + } else { + unsigned long flags; + char *buffer = ide_map_buffer(rq, &flags); + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, &write_intr, WAIT_CMD, NULL); + taskfile_output_data(drive, buffer, SECTOR_WORDS); + ide_unmap_buffer(buffer, &flags); + } + return ide_started; + } + printk(KERN_ERR "%s: bad command: %lx\n", drive->name, rq->flags); + idedisk_end_request(drive, 0); + return ide_stopped; +} + +#endif /* CONFIG_IDE_TASKFILE_IO */ + +static int idedisk_open (struct inode *inode, struct file *filp, ide_drive_t *drive) +{ + MOD_INC_USE_COUNT; + if (drive->removable && drive->usage == 1) { + ide_task_t args; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_DOORLOCK; + args.command_type = ide_cmd_type_parser(&args); + check_disk_change(inode->i_bdev); + /* + * Ignore the return code from door_lock, + * since the open() has already succeeded, + * and the door_lock is irrelevant at this point. + */ + if (drive->doorlocking && ide_raw_taskfile(drive, &args, NULL)) + drive->doorlocking = 0; + } + return 0; +} + +static int do_idedisk_flushcache(ide_drive_t *drive); + +static void idedisk_release (struct inode *inode, struct file *filp, ide_drive_t *drive) +{ + if (drive->removable && !drive->usage) { + ide_task_t args; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_DOORUNLOCK; + args.command_type = ide_cmd_type_parser(&args); + invalidate_bdev(inode->i_bdev, 0); + if (drive->doorlocking && ide_raw_taskfile(drive, &args, NULL)) + drive->doorlocking = 0; + } + if ((drive->id->cfs_enable_2 & 0x3000) && drive->wcache) + if (do_idedisk_flushcache(drive)) + printk (KERN_INFO "%s: Write Cache FAILED Flushing!\n", + drive->name); + MOD_DEC_USE_COUNT; +} + +static int idedisk_media_change (ide_drive_t *drive) +{ + return drive->removable; /* if removable, always assume it was changed */ +} + +static void idedisk_revalidate (ide_drive_t *drive) +{ + ide_revalidate_drive(drive); +} + +static int idedisk_end_request (ide_drive_t *drive, int uptodate) +{ + struct request *rq; + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&ide_lock, flags); + rq = HWGROUP(drive)->rq; + + /* + * decide whether to reenable DMA -- 3 is a random magic for now, + * if we DMA timeout more than 3 times, just stay in PIO + */ + if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) { + drive->state = 0; + HWGROUP(drive)->hwif->dmaproc(ide_dma_on, drive); + } + + if (!end_that_request_first(rq, uptodate, rq->hard_cur_sectors)) { + add_blkdev_randomness(major(rq->rq_dev)); + blkdev_dequeue_request(rq); + HWGROUP(drive)->rq = NULL; + end_that_request_last(rq); + ret = 0; + } + spin_unlock_irqrestore(&ide_lock, flags); + return ret; +} + +static byte idedisk_dump_status (ide_drive_t *drive, const char *msg, byte stat) +{ + unsigned long flags; + byte err = 0; + + local_irq_set(flags); + printk("%s: %s: status=0x%02x", drive->name, msg, stat); +#if FANCY_STATUS_DUMPS + printk(" { "); + if (stat & BUSY_STAT) + printk("Busy "); + else { + if (stat & READY_STAT) printk("DriveReady "); + if (stat & WRERR_STAT) printk("DeviceFault "); + if (stat & SEEK_STAT) printk("SeekComplete "); + if (stat & DRQ_STAT) printk("DataRequest "); + if (stat & ECC_STAT) printk("CorrectedError "); + if (stat & INDEX_STAT) printk("Index "); + if (stat & ERR_STAT) printk("Error "); + } + printk("}"); +#endif /* FANCY_STATUS_DUMPS */ + printk("\n"); + if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) { + err = GET_ERR(); + printk("%s: %s: error=0x%02x", drive->name, msg, err); +#if FANCY_STATUS_DUMPS + printk(" { "); + if (err & ABRT_ERR) printk("DriveStatusError "); + if (err & ICRC_ERR) printk("%s", (err & ABRT_ERR) ? "BadCRC " : "BadSector "); + if (err & ECC_ERR) printk("UncorrectableError "); + if (err & ID_ERR) printk("SectorIdNotFound "); + if (err & TRK0_ERR) printk("TrackZeroNotFound "); + if (err & MARK_ERR) printk("AddrMarkNotFound "); + printk("}"); + if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR || (err & (ECC_ERR|ID_ERR|MARK_ERR))) { + if ((drive->id->command_set_2 & 0x0400) && + (drive->id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + __u64 sectors = 0; + u32 low = 0, high = 0; + low = idedisk_read_24(drive); + OUT_BYTE(drive->ctl|0x80, IDE_CONTROL_REG); + high = idedisk_read_24(drive); + sectors = ((__u64)high << 24) | low; + printk(", LBAsect=%llu, high=%d, low=%d", + (unsigned long long) sectors, + high, low); + } else { + byte cur = IN_BYTE(IDE_SELECT_REG); + if (cur & 0x40) { /* using LBA? */ + printk(", LBAsect=%ld", (unsigned long) + ((cur&0xf)<<24) + |(IN_BYTE(IDE_HCYL_REG)<<16) + |(IN_BYTE(IDE_LCYL_REG)<<8) + | IN_BYTE(IDE_SECTOR_REG)); + } else { + printk(", CHS=%d/%d/%d", + (IN_BYTE(IDE_HCYL_REG)<<8) + + IN_BYTE(IDE_LCYL_REG), + cur & 0xf, + IN_BYTE(IDE_SECTOR_REG)); + } + } + if (HWGROUP(drive) && HWGROUP(drive)->rq) + printk(", sector=%ld", HWGROUP(drive)->rq->sector); + } + } +#endif /* FANCY_STATUS_DUMPS */ + printk("\n"); + local_irq_restore(flags); + return err; +} + +ide_startstop_t idedisk_error (ide_drive_t *drive, const char *msg, byte stat) +{ + struct request *rq; + byte err; + int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS; + + err = idedisk_dump_status(drive, msg, stat); + + if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) + return ide_stopped; + /* retry only "normal" I/O: */ + if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK | REQ_DRIVE_TASKFILE)) { + rq->errors = 1; + ide_end_drive_cmd(drive, stat, err); + return ide_stopped; + } +#if 0 + else if (rq->flags & REQ_DRIVE_TASKFILE) { + rq->errors = 1; + ide_end_taskfile(drive, stat, err); + return ide_stopped; + } +#endif + if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { + /* other bits are useless when BUSY */ + rq->errors |= ERROR_RESET; + } else if (stat & ERR_STAT) { + /* err has different meaning on cdrom and tape */ + if (err == ABRT_ERR) { + if (drive->select.b.lba && + /* some newer drives don't support WIN_SPECIFY */ + IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY) + return ide_stopped; + } else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR)) { + /* UDMA crc error, just retry the operation */ + drive->crc_count++; + } else if (err & (BBD_ERR | ECC_ERR)) + /* retries won't help these */ + rq->errors = ERROR_MAX; + else if (err & TRK0_ERR) + /* help it find track zero */ + rq->errors |= ERROR_RECAL; + } + if ((stat & DRQ_STAT) && rq_data_dir(rq) == READ) { + /* + * try_to_flush_leftover_data() is invoked in response to + * a drive unexpectedly having its DRQ_STAT bit set. As + * an alternative to resetting the drive, this routine + * tries to clear the condition by read a sector's worth + * of data from the drive. Of course, this may not help + * if the drive is *waiting* for data from *us*. + */ + while (i > 0) { + u32 buffer[16]; + unsigned int wcount = (i > 16) ? 16 : i; + i -= wcount; + ata_input_data(drive, buffer, wcount); + } + } + if (GET_STAT() & (BUSY_STAT|DRQ_STAT)) + /* force an abort */ + OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); + if (rq->errors >= ERROR_MAX) + DRIVER(drive)->end_request(drive, 0); + else { + if ((rq->errors & ERROR_RESET) == ERROR_RESET) { + ++rq->errors; + return ide_do_reset(drive); + } + if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) + drive->special.b.recalibrate = 1; + ++rq->errors; + } + return ide_stopped; +} + +/* + * Queries for true maximum capacity of the drive. + * Returns maximum LBA address (> 0) of the drive, 0 if failed. + */ +static unsigned long idedisk_read_native_max_address(ide_drive_t *drive) +{ + ide_task_t args; + unsigned long addr = 0; + +#if 0 + if (!(drive->id->command_set_1 & 0x0400) && + !(drive->id->cfs_enable_2 & 0x0100)) + return addr; +#endif + + /* Create IDE/ATA command request structure */ + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_SELECT_OFFSET] = 0x40; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_READ_NATIVE_MAX; + args.command_type = ide_cmd_type_parser(&args); + /* submit command request */ + ide_raw_taskfile(drive, &args, NULL); + + /* if OK, compute maximum address value */ + if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) { + addr = ((args.tfRegister[IDE_SELECT_OFFSET] & 0x0f) << 24) + | ((args.tfRegister[ IDE_HCYL_OFFSET] ) << 16) + | ((args.tfRegister[ IDE_LCYL_OFFSET] ) << 8) + | ((args.tfRegister[IDE_SECTOR_OFFSET] )); + } + addr++; /* since the return value is (maxlba - 1), we add 1 */ + return addr; +} + +static unsigned long long idedisk_read_native_max_address_ext(ide_drive_t *drive) +{ + ide_task_t args; + unsigned long long addr = 0; + + /* Create IDE/ATA command request structure */ + memset(&args, 0, sizeof(ide_task_t)); + + args.tfRegister[IDE_SELECT_OFFSET] = 0x40; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_READ_NATIVE_MAX_EXT; + args.command_type = ide_cmd_type_parser(&args); + /* submit command request */ + ide_raw_taskfile(drive, &args, NULL); + + /* if OK, compute maximum address value */ + if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) { + u32 high = ((args.hobRegister[IDE_HCYL_OFFSET_HOB])<<16) | + ((args.hobRegister[IDE_LCYL_OFFSET_HOB])<<8) | + (args.hobRegister[IDE_SECTOR_OFFSET_HOB]); + u32 low = ((args.tfRegister[IDE_HCYL_OFFSET])<<16) | + ((args.tfRegister[IDE_LCYL_OFFSET])<<8) | + (args.tfRegister[IDE_SECTOR_OFFSET]); + addr = ((__u64)high << 24) | low; + } + addr++; /* since the return value is (maxlba - 1), we add 1 */ + return addr; +} + +#ifdef CONFIG_IDEDISK_STROKE +/* + * Sets maximum virtual LBA address of the drive. + * Returns new maximum virtual LBA address (> 0) or 0 on failure. + */ +static unsigned long idedisk_set_max_address(ide_drive_t *drive, unsigned long addr_req) +{ + ide_task_t args; + unsigned long addr_set = 0; + + addr_req--; + /* Create IDE/ATA command request structure */ + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_SECTOR_OFFSET] = ((addr_req >> 0) & 0xff); + args.tfRegister[IDE_LCYL_OFFSET] = ((addr_req >> 8) & 0xff); + args.tfRegister[IDE_HCYL_OFFSET] = ((addr_req >> 16) & 0xff); + args.tfRegister[IDE_SELECT_OFFSET] = ((addr_req >> 24) & 0x0f) | 0x40; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SET_MAX; + args.command_type = ide_cmd_type_parser(&args); + /* submit command request */ + ide_raw_taskfile(drive, &args, NULL); + /* if OK, read new maximum address value */ + if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) { + addr_set = ((args.tfRegister[IDE_SELECT_OFFSET] & 0x0f) << 24) + | ((args.tfRegister[ IDE_HCYL_OFFSET] ) << 16) + | ((args.tfRegister[ IDE_LCYL_OFFSET] ) << 8) + | ((args.tfRegister[IDE_SECTOR_OFFSET] )); + } + addr_set++; + return addr_set; +} + +static unsigned long long idedisk_set_max_address_ext(ide_drive_t *drive, unsigned long long addr_req) +{ + ide_task_t args; + unsigned long long addr_set = 0; + + addr_req--; + /* Create IDE/ATA command request structure */ + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_SECTOR_OFFSET] = ((addr_req >> 0) & 0xff); + args.tfRegister[IDE_LCYL_OFFSET] = ((addr_req >>= 8) & 0xff); + args.tfRegister[IDE_HCYL_OFFSET] = ((addr_req >>= 8) & 0xff); + args.tfRegister[IDE_SELECT_OFFSET] = 0x40; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SET_MAX_EXT; + args.hobRegister[IDE_SECTOR_OFFSET_HOB] = ((addr_req >>= 8) & 0xff); + args.hobRegister[IDE_LCYL_OFFSET_HOB] = ((addr_req >>= 8) & 0xff); + args.hobRegister[IDE_HCYL_OFFSET_HOB] = ((addr_req >>= 8) & 0xff); + args.hobRegister[IDE_SELECT_OFFSET_HOB] = 0x40; + args.hobRegister[IDE_CONTROL_OFFSET_HOB]= (drive->ctl|0x80); + args.command_type = ide_cmd_type_parser(&args); + /* submit command request */ + ide_raw_taskfile(drive, &args, NULL); + /* if OK, compute maximum address value */ + if ((args.tfRegister[IDE_STATUS_OFFSET] & 0x01) == 0) { + u32 high = ((args.hobRegister[IDE_HCYL_OFFSET_HOB])<<16) | + ((args.hobRegister[IDE_LCYL_OFFSET_HOB])<<8) | + (args.hobRegister[IDE_SECTOR_OFFSET_HOB]); + u32 low = ((args.tfRegister[IDE_HCYL_OFFSET])<<16) | + ((args.tfRegister[IDE_LCYL_OFFSET])<<8) | + (args.tfRegister[IDE_SECTOR_OFFSET]); + addr_set = ((__u64)high << 24) | low; + } + return addr_set; +} + +#endif /* CONFIG_IDEDISK_STROKE */ + +/* + * Tests if the drive supports Host Protected Area feature. + * Returns true if supported, false otherwise. + */ +static inline int idedisk_supports_host_protected_area(ide_drive_t *drive) +{ + int flag = (drive->id->cfs_enable_1 & 0x0400) ? 1 : 0; + if (flag) + printk("%s: host protected area => %d\n", drive->name, flag); + return flag; +} + +/* + * Compute drive->capacity, the full capacity of the drive + * Called with drive->id != NULL. + * + * To compute capacity, this uses either of + * + * 1. CHS value set by user (whatever user sets will be trusted) + * 2. LBA value from target drive (require new ATA feature) + * 3. LBA value from system BIOS (new one is OK, old one may break) + * 4. CHS value from system BIOS (traditional style) + * + * in above order (i.e., if value of higher priority is available, + * reset will be ignored). + */ +static void init_idedisk_capacity (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + unsigned long capacity = drive->cyl * drive->head * drive->sect; + unsigned long set_max = idedisk_read_native_max_address(drive); + unsigned long long capacity_2 = capacity; + unsigned long long set_max_ext; + + drive->capacity48 = 0; + drive->select.b.lba = 0; + + (void) idedisk_supports_host_protected_area(drive); + + if (id->cfs_enable_2 & 0x0400) { + capacity_2 = id->lba_capacity_2; + drive->cyl = (unsigned int) capacity_2 / (drive->head * drive->sect); + drive->head = drive->bios_head = 255; + drive->sect = drive->bios_sect = 63; + drive->select.b.lba = 1; + set_max_ext = idedisk_read_native_max_address_ext(drive); + if (set_max_ext > capacity_2) { +#ifdef CONFIG_IDEDISK_STROKE + set_max_ext = idedisk_read_native_max_address_ext(drive); + set_max_ext = idedisk_set_max_address_ext(drive, set_max_ext); + if (set_max_ext) { + drive->capacity48 = capacity_2 = set_max_ext; + drive->cyl = (unsigned int) set_max_ext / (drive->head * drive->sect); + drive->select.b.lba = 1; + drive->id->lba_capacity_2 = capacity_2; + } +#else /* !CONFIG_IDEDISK_STROKE */ + printk("%s: setmax_ext LBA %llu, native %llu\n", + drive->name, set_max_ext, capacity_2); +#endif /* CONFIG_IDEDISK_STROKE */ + } + drive->cyl = (unsigned int) capacity_2 / (drive->head * drive->sect); + drive->bios_cyl = drive->cyl; + drive->capacity48 = capacity_2; + drive->capacity = (unsigned long) capacity_2; + return; + /* Determine capacity, and use LBA if the drive properly supports it */ + } else if ((id->capability & 2) && lba_capacity_is_ok(id)) { + capacity = id->lba_capacity; + drive->cyl = capacity / (drive->head * drive->sect); + drive->select.b.lba = 1; + } + + if (set_max > capacity) { +#ifdef CONFIG_IDEDISK_STROKE + set_max = idedisk_read_native_max_address(drive); + set_max = idedisk_set_max_address(drive, set_max); + if (set_max) { + drive->capacity = capacity = set_max; + drive->cyl = set_max / (drive->head * drive->sect); + drive->select.b.lba = 1; + drive->id->lba_capacity = capacity; + } +#else /* !CONFIG_IDEDISK_STROKE */ + printk("%s: setmax LBA %lu, native %lu\n", + drive->name, set_max, capacity); +#endif /* CONFIG_IDEDISK_STROKE */ + } + + drive->capacity = capacity; + + if ((id->command_set_2 & 0x0400) && (id->cfs_enable_2 & 0x0400)) { + drive->capacity48 = id->lba_capacity_2; + drive->head = 255; + drive->sect = 63; + drive->cyl = (unsigned long)(drive->capacity48) / (drive->head * drive->sect); + } +} + +static unsigned long idedisk_capacity (ide_drive_t *drive) +{ + if (drive->id->cfs_enable_2 & 0x0400) + return (drive->capacity48 - drive->sect0); + return (drive->capacity - drive->sect0); +} + +static ide_startstop_t idedisk_special (ide_drive_t *drive) +{ + special_t *s = &drive->special; + + if (s->b.set_geometry) { + s->b.set_geometry = 0; + if (!IS_PDC4030_DRIVE) { + ide_task_t args; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_NSECTOR_OFFSET] = drive->sect; + args.tfRegister[IDE_SECTOR_OFFSET] = drive->sect; + args.tfRegister[IDE_LCYL_OFFSET] = drive->cyl; + args.tfRegister[IDE_HCYL_OFFSET] = drive->cyl>>8; + args.tfRegister[IDE_SELECT_OFFSET] = ((drive->head-1)|drive->select.all)&0xBF; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SPECIFY; + args.command_type = ide_cmd_type_parser(&args); + do_rw_taskfile(drive, &args); + } + } else if (s->b.recalibrate) { + s->b.recalibrate = 0; + if (!IS_PDC4030_DRIVE) { + ide_task_t args; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_NSECTOR_OFFSET] = drive->sect; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_RESTORE; + args.command_type = ide_cmd_type_parser(&args); + do_rw_taskfile(drive, &args); + } + } else if (s->b.set_multmode) { + s->b.set_multmode = 0; + if (drive->id && drive->mult_req > drive->id->max_multsect) + drive->mult_req = drive->id->max_multsect; + if (!IS_PDC4030_DRIVE) { + ide_task_t args; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_NSECTOR_OFFSET] = drive->mult_req; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SETMULT; + args.command_type = ide_cmd_type_parser(&args); + do_rw_taskfile(drive, &args); + } + } else if (s->all) { + int special = s->all; + s->all = 0; + printk(KERN_ERR "%s: bad special flag: 0x%02x\n", drive->name, special); + return ide_stopped; + } + return IS_PDC4030_DRIVE ? ide_stopped : ide_started; +} + +static void idedisk_pre_reset (ide_drive_t *drive) +{ + int legacy = (drive->id->cfs_enable_2 & 0x0400) ? 0 : 1; + + drive->special.all = 0; + drive->special.b.set_geometry = legacy; + drive->special.b.recalibrate = legacy; + if (OK_TO_RESET_CONTROLLER) + drive->mult_count = 0; + if (!drive->keep_settings && !drive->using_dma) + drive->mult_req = 0; + if (drive->mult_req != drive->mult_count) + drive->special.b.set_multmode = 1; +} + +#ifdef CONFIG_PROC_FS + +static int smart_enable(ide_drive_t *drive) +{ + ide_task_t args; + + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_FEATURE_OFFSET] = SMART_ENABLE; + args.tfRegister[IDE_LCYL_OFFSET] = SMART_LCYL_PASS; + args.tfRegister[IDE_HCYL_OFFSET] = SMART_HCYL_PASS; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SMART; + args.command_type = ide_cmd_type_parser(&args); + return ide_raw_taskfile(drive, &args, NULL); +} + +static int get_smart_values(ide_drive_t *drive, byte *buf) +{ + ide_task_t args; + + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_FEATURE_OFFSET] = SMART_READ_VALUES; + args.tfRegister[IDE_NSECTOR_OFFSET] = 0x01; + args.tfRegister[IDE_LCYL_OFFSET] = SMART_LCYL_PASS; + args.tfRegister[IDE_HCYL_OFFSET] = SMART_HCYL_PASS; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SMART; + args.command_type = ide_cmd_type_parser(&args); + (void) smart_enable(drive); + return ide_raw_taskfile(drive, &args, buf); +} + +static int get_smart_thresholds(ide_drive_t *drive, byte *buf) +{ + ide_task_t args; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_FEATURE_OFFSET] = SMART_READ_THRESHOLDS; + args.tfRegister[IDE_NSECTOR_OFFSET] = 0x01; + args.tfRegister[IDE_LCYL_OFFSET] = SMART_LCYL_PASS; + args.tfRegister[IDE_HCYL_OFFSET] = SMART_HCYL_PASS; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SMART; + args.command_type = ide_cmd_type_parser(&args); + (void) smart_enable(drive); + return ide_raw_taskfile(drive, &args, buf); +} + +static int proc_idedisk_read_cache + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + char *out = page; + int len; + + if (drive->id) + len = sprintf(out,"%i\n", drive->id->buf_size / 2); + else + len = sprintf(out,"(none)\n"); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_idedisk_read_smart_thresholds + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *)data; + int len = 0, i = 0; + + if (!get_smart_thresholds(drive, page)) { + unsigned short *val = (unsigned short *) page; + char *out = ((char *)val) + (SECTOR_WORDS * 4); + page = out; + do { + out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n'); + val += 1; + } while (i < (SECTOR_WORDS * 2)); + len = out - page; + } + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_idedisk_read_smart_values + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *)data; + int len = 0, i = 0; + + if (!get_smart_values(drive, page)) { + unsigned short *val = (unsigned short *) page; + char *out = ((char *)val) + (SECTOR_WORDS * 4); + page = out; + do { + out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n'); + val += 1; + } while (i < (SECTOR_WORDS * 2)); + len = out - page; + } + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static ide_proc_entry_t idedisk_proc[] = { + { "cache", S_IFREG|S_IRUGO, proc_idedisk_read_cache, NULL }, + { "geometry", S_IFREG|S_IRUGO, proc_ide_read_geometry, NULL }, + { "smart_values", S_IFREG|S_IRUSR, proc_idedisk_read_smart_values, NULL }, + { "smart_thresholds", S_IFREG|S_IRUSR, proc_idedisk_read_smart_thresholds, NULL }, + { NULL, 0, NULL, NULL } +}; + +#else + +#define idedisk_proc NULL + +#endif /* CONFIG_PROC_FS */ + +/* + * This is tightly woven into the driver->do_special can not touch. + * DON'T do it again until a total personality rewrite is committed. + */ +static int set_multcount(ide_drive_t *drive, int arg) +{ + struct request rq; + + if (drive->special.b.set_multmode) + return -EBUSY; + ide_init_drive_cmd (&rq); + rq.flags = REQ_DRIVE_CMD; + drive->mult_req = arg; + drive->special.b.set_multmode = 1; + (void) ide_do_drive_cmd (drive, &rq, ide_wait); + return (drive->mult_count == arg) ? 0 : -EIO; +} + +static int set_nowerr(ide_drive_t *drive, int arg) +{ + if (ide_spin_wait_hwgroup(drive)) + return -EBUSY; + drive->nowerr = arg; + drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT; + spin_unlock_irq(&ide_lock); + return 0; +} + +static int write_cache (ide_drive_t *drive, int arg) +{ + ide_task_t args; + + if (!(drive->id->cfs_enable_2 & 0x3000)) + return 1; + + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_FEATURE_OFFSET] = (arg) ? + SETFEATURES_EN_WCACHE : SETFEATURES_DIS_WCACHE; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SETFEATURES; + args.command_type = ide_cmd_type_parser(&args); + (void) ide_raw_taskfile(drive, &args, NULL); + + drive->wcache = arg; + return 0; +} + +static int call_idedisk_standby (ide_drive_t *drive, int arg) +{ + ide_task_t args; + byte standby = (arg) ? WIN_STANDBYNOW2 : WIN_STANDBYNOW1; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_COMMAND_OFFSET] = standby; + args.command_type = ide_cmd_type_parser(&args); + return ide_raw_taskfile(drive, &args, NULL); +} + +static int do_idedisk_standby (ide_drive_t *drive) +{ + return call_idedisk_standby(drive, 0); +} + +static int call_idedisk_suspend (ide_drive_t *drive, int arg) +{ + ide_task_t args; + byte suspend = (arg) ? WIN_SLEEPNOW2 : WIN_SLEEPNOW1; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_COMMAND_OFFSET] = suspend; + args.command_type = ide_cmd_type_parser(&args); + return ide_raw_taskfile(drive, &args, NULL); +} + +static int do_idedisk_suspend (ide_drive_t *drive) +{ + if (drive->suspend_reset) + return 1; + + return call_idedisk_suspend(drive, 0); +} + +#if 0 +static int call_idedisk_checkpower (ide_drive_t *drive, int arg) +{ + ide_task_t args; + byte ckpw = (arg) ? WIN_CHECKPOWERMODE2 : WIN_CHECKPOWERMODE1; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_COMMAND_OFFSET] = ckpw; + args.command_type = ide_cmd_type_parser(&args); + ide_raw_taskfile(drive, &args, NULL); +#if 0 +if (errno != EIO || args[0] != 0 || args[1] != 0) + state = "unknown"; +else + state = "sleeping"; +} else { + state = (args[2] == 255) ? "active/idle" : "standby"; +#endif + return 0; +} + +static int do_idedisk_checkpower (ide_drive_t *drive) +{ + return call_idedisk_checkpower(drive, 0); +} +#endif + +static int do_idedisk_resume (ide_drive_t *drive) +{ + if (!drive->suspend_reset) + return 1; + return 0; +} + +static int do_idedisk_flushcache (ide_drive_t *drive) +{ + ide_task_t args; + + memset(&args, 0, sizeof(ide_task_t)); + if (drive->id->cfs_enable_2 & 0x2400) + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE_EXT; + else + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_FLUSH_CACHE; + args.command_type = ide_cmd_type_parser(&args); + return ide_raw_taskfile(drive, &args, NULL); +} + +static int set_acoustic (ide_drive_t *drive, int arg) +{ + ide_task_t args; + + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_FEATURE_OFFSET] = (arg) ? SETFEATURES_EN_AAM : + SETFEATURES_DIS_AAM; + args.tfRegister[IDE_NSECTOR_OFFSET] = arg; + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_SETFEATURES; + args.command_type = ide_cmd_type_parser(&args); + ide_raw_taskfile(drive, &args, NULL); + drive->acoustic = arg; + return 0; +} + +static int probe_lba_addressing (ide_drive_t *drive, int arg) +{ + drive->addressing = 0; + + if (HWIF(drive)->addressing) + return 0; + + if (!(drive->id->cfs_enable_2 & 0x0400)) + return -EIO; + drive->addressing = arg; + return 0; +} + +static int set_lba_addressing (ide_drive_t *drive, int arg) +{ + return (probe_lba_addressing(drive, arg)); +} + +static void idedisk_add_settings(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + + ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->bios_cyl, NULL); + ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); + ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL); + ide_add_setting(drive, "address", SETTING_RW, HDIO_GET_ADDRESS, HDIO_SET_ADDRESS, TYPE_INTA, 0, 2, 1, 1, &drive->addressing, set_lba_addressing); + ide_add_setting(drive, "bswap", SETTING_READ, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->bswap, NULL); + ide_add_setting(drive, "multcount", id ? SETTING_RW : SETTING_READ, HDIO_GET_MULTCOUNT, HDIO_SET_MULTCOUNT, TYPE_BYTE, 0, id ? id->max_multsect : 0, 1, 1, &drive->mult_count, set_multcount); + ide_add_setting(drive, "nowerr", SETTING_RW, HDIO_GET_NOWERR, HDIO_SET_NOWERR, TYPE_BYTE, 0, 1, 1, 1, &drive->nowerr, set_nowerr); + ide_add_setting(drive, "lun", SETTING_RW, -1, -1, TYPE_INT, 0, 7, 1, 1, &drive->lun, NULL); + ide_add_setting(drive, "wcache", SETTING_RW, HDIO_GET_WCACHE, HDIO_SET_WCACHE, TYPE_BYTE, 0, 1, 1, 1, &drive->wcache, write_cache); + ide_add_setting(drive, "acoustic", SETTING_RW, HDIO_GET_ACOUSTIC, HDIO_SET_ACOUSTIC, TYPE_BYTE, 0, 254, 1, 1, &drive->acoustic, set_acoustic); + ide_add_setting(drive, "failures", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->failures, NULL); + ide_add_setting(drive, "max_failures", SETTING_RW, -1, -1, TYPE_INT, 0, 65535, 1, 1, &drive->max_failures, NULL); +} + +static void idedisk_setup (ide_drive_t *drive) +{ + int i; + + struct hd_driveid *id = drive->id; + unsigned long capacity; + + idedisk_add_settings(drive); + + if (id == NULL) + return; + + /* + * CompactFlash cards and their brethern look just like hard drives + * to us, but they are removable and don't have a doorlock mechanism. + */ + if (drive->removable && !drive_is_flashcard(drive)) { + /* + * Removable disks (eg. SYQUEST); ignore 'WD' drives + */ + if (id->model[0] != 'W' || id->model[1] != 'D') { + drive->doorlocking = 1; + } + } + for (i = 0; i < MAX_DRIVES; ++i) { + ide_hwif_t *hwif = HWIF(drive); + + if (drive != &hwif->drives[i]) continue; + hwif->gd[i]->de_arr[i] = drive->de; + if (drive->removable) + hwif->gd[i]->flags[i] |= GENHD_FL_REMOVABLE; + break; + } + +#if 1 + (void) probe_lba_addressing(drive, 1); +#else + /* if using 48-bit addressing bump the request size up */ + if (probe_lba_addressing(drive, 1)) + blk_queue_max_sectors(&drive->queue, 2048); +#endif + + /* Extract geometry if we did not already have one for the drive */ + if (!drive->cyl || !drive->head || !drive->sect) { + drive->cyl = drive->bios_cyl = id->cyls; + drive->head = drive->bios_head = id->heads; + drive->sect = drive->bios_sect = id->sectors; + } + + /* Handle logical geometry translation by the drive */ + if ((id->field_valid & 1) && id->cur_cyls && + id->cur_heads && (id->cur_heads <= 16) && id->cur_sectors) { + drive->cyl = id->cur_cyls; + drive->head = id->cur_heads; + drive->sect = id->cur_sectors; + } + + /* Use physical geometry if what we have still makes no sense */ + if (drive->head > 16 && id->heads && id->heads <= 16) { + drive->cyl = id->cyls; + drive->head = id->heads; + drive->sect = id->sectors; + } + + /* calculate drive capacity, and select LBA if possible */ + init_idedisk_capacity (drive); + + /* + * if possible, give fdisk access to more of the drive, + * by correcting bios_cyls: + */ + capacity = idedisk_capacity (drive); + if ((capacity >= (drive->bios_cyl * drive->bios_sect * drive->bios_head)) && + (!drive->forced_geom) && drive->bios_sect && drive->bios_head) + drive->bios_cyl = (capacity / drive->bios_sect) / drive->bios_head; + printk (KERN_INFO "%s: %ld sectors", drive->name, capacity); + + /* Give size in megabytes (MB), not mebibytes (MiB). */ + /* We compute the exact rounded value, avoiding overflow. */ + printk (" (%ld MB)", (capacity - capacity/625 + 974)/1950); + + /* Only print cache size when it was specified */ + if (id->buf_size) + printk (" w/%dKiB Cache", id->buf_size/2); + + printk(", CHS=%d/%d/%d", + drive->bios_cyl, drive->bios_head, drive->bios_sect); +#ifdef CONFIG_BLK_DEV_IDEDMA + if (drive->using_dma) + (void) HWIF(drive)->dmaproc(ide_dma_verbose, drive); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + printk("\n"); + + drive->mult_count = 0; + if (id->max_multsect) { +#ifdef CONFIG_IDEDISK_MULTI_MODE + id->multsect = ((id->max_multsect/2) > 1) ? id->max_multsect : 0; + id->multsect_valid = id->multsect ? 1 : 0; + drive->mult_req = id->multsect_valid ? id->max_multsect : INITIAL_MULT_COUNT; + drive->special.b.set_multmode = drive->mult_req ? 1 : 0; +#else /* original, pre IDE-NFG, per request of AC */ + drive->mult_req = INITIAL_MULT_COUNT; + if (drive->mult_req > id->max_multsect) + drive->mult_req = id->max_multsect; + if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect)) + drive->special.b.set_multmode = 1; +#endif /* CONFIG_IDEDISK_MULTI_MODE */ + } + drive->no_io_32bit = id->dword_io ? 1 : 0; + if (drive->id->cfs_enable_2 & 0x3000) + write_cache(drive, (id->cfs_enable_2 & 0x3000)); +} + +static int idedisk_cleanup (ide_drive_t *drive) +{ + if ((drive->id->cfs_enable_2 & 0x3000) && drive->wcache) + if (do_idedisk_flushcache(drive)) + printk (KERN_INFO "%s: Write Cache FAILED Flushing!\n", + drive->name); + return ide_unregister_subdriver(drive); +} + +int idedisk_init (void); +int idedisk_reinit(ide_drive_t *drive); + +/* + * IDE subdriver functions, registered with ide.c + */ +static ide_driver_t idedisk_driver = { + name: "ide-disk", + version: IDEDISK_VERSION, + media: ide_disk, + busy: 0, + supports_dma: 1, + supports_dsc_overlap: 0, + cleanup: idedisk_cleanup, + standby: do_idedisk_standby, + suspend: do_idedisk_suspend, + resume: do_idedisk_resume, + flushcache: do_idedisk_flushcache, + do_request: do_rw_disk, + end_request: idedisk_end_request, + sense: idedisk_dump_status, + error: idedisk_error, + ioctl: NULL, + open: idedisk_open, + release: idedisk_release, + media_change: idedisk_media_change, + revalidate: idedisk_revalidate, + pre_reset: idedisk_pre_reset, + capacity: idedisk_capacity, + special: idedisk_special, + proc: idedisk_proc, + init: idedisk_init, + reinit: idedisk_reinit, + ata_prebuilder: NULL, + atapi_prebuilder: NULL, +}; + +static ide_module_t idedisk_module = { + IDE_DRIVER_MODULE, + idedisk_init, + &idedisk_driver, + NULL +}; + +MODULE_DESCRIPTION("ATA DISK Driver"); + +int idedisk_reinit (ide_drive_t *drive) +{ + int failed = 0; + + MOD_INC_USE_COUNT; + + if (ide_register_subdriver (drive, &idedisk_driver, IDE_SUBDRIVER_VERSION)) { + printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name); + return 1; + } + DRIVER(drive)->busy++; + idedisk_setup(drive); + if ((!drive->head || drive->head > 16) && !drive->select.b.lba) { + printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n", + drive->name, drive->head); + (void) idedisk_cleanup(drive); + DRIVER(drive)->busy--; + return 1; + } + DRIVER(drive)->busy--; + failed--; + + ide_register_module(&idedisk_module); + MOD_DEC_USE_COUNT; + return 0; +} + +static void __exit idedisk_exit (void) +{ + ide_drive_t *drive; + int failed = 0; + + while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, &idedisk_driver, failed)) != NULL) { + if (idedisk_cleanup (drive)) { + printk (KERN_ERR "%s: cleanup_module() called while still busy\n", drive->name); + failed++; + } + /* We must remove proc entries defined in this module. + Otherwise we oops while accessing these entries */ +#ifdef CONFIG_PROC_FS + if (drive->proc) + ide_remove_proc_entries(drive->proc, idedisk_proc); +#endif + } + ide_unregister_module(&idedisk_module); +} + +int idedisk_init (void) +{ + ide_drive_t *drive; + int failed = 0; + + MOD_INC_USE_COUNT; + while ((drive = ide_scan_devices (ide_disk, idedisk_driver.name, NULL, failed++)) != NULL) { + if (ide_register_subdriver (drive, &idedisk_driver, IDE_SUBDRIVER_VERSION)) { + printk (KERN_ERR "ide-disk: %s: Failed to register the driver with ide.c\n", drive->name); + continue; + } + DRIVER(drive)->busy++; + idedisk_setup(drive); + if ((!drive->head || drive->head > 16) && !drive->select.b.lba) { + printk(KERN_ERR "%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n", drive->name, drive->head); + (void) idedisk_cleanup(drive); + DRIVER(drive)->busy--; + continue; + } + DRIVER(drive)->busy--; + failed--; + } + ide_register_module(&idedisk_module); + MOD_DEC_USE_COUNT; + return 0; +} + +ide_startstop_t panic_box(ide_drive_t *drive) +{ +#if 0 + panic("%s: Attempted to corrupt something: ide operation " +#else + printk(KERN_ERR "%s: Attempted to corrupt something: ide operation " +#endif + "was pending accross suspend/resume.\n", drive->name); + return ide_stopped; +} + +int ide_disks_busy(void) +{ + int i; + for (i=0; ihandler) && (hwgroup->handler != panic_box)) + return 1; + } + return 0; +} + +void ide_disk_suspend(void) +{ + int i; + while (ide_disks_busy()) { + printk("*"); + schedule(); + } + for (i=0; ihandler_save = hwgroup->handler; + hwgroup->handler = panic_box; + } + driver_blocked = 1; + if (ide_disks_busy()) + panic("How did you get that request through?!"); +} + +/* unsuspend and resume should be equal in the ideal world */ + +void ide_disk_unsuspend(void) +{ + int i; + for (i=0; ihandler = NULL; /* hwgroup->handler_save; */ + hwgroup->handler_save = NULL; + } + driver_blocked = 0; +} + +void ide_disk_resume(void) +{ + int i; + for (i=0; ihandler != panic_box) + panic("Handler was not set to panic?"); + hwgroup->handler_save = NULL; + hwgroup->handler = NULL; + } + driver_blocked = 0; +} + +module_init(idedisk_init); +module_exit(idedisk_exit); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-dma.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,906 @@ +/* + * linux/drivers/ide/ide-dma.c Version 4.10 June 9, 2000 + * + * Copyright (c) 1999-2000 Andre Hedrick + * May be copied or modified under the terms of the GNU General Public License + */ + +/* + * Special Thanks to Mark for his Six years of work. + * + * Copyright (c) 1995-1998 Mark Lord + * May be copied or modified under the terms of the GNU General Public License + */ + +/* + * This module provides support for the bus-master IDE DMA functions + * of various PCI chipsets, including the Intel PIIX (i82371FB for + * the 430 FX chipset), the PIIX3 (i82371SB for the 430 HX/VX and + * 440 chipsets), and the PIIX4 (i82371AB for the 430 TX chipset) + * ("PIIX" stands for "PCI ISA IDE Xcellerator"). + * + * Pretty much the same code works for other IDE PCI bus-mastering chipsets. + * + * DMA is supported for all IDE devices (disk drives, cdroms, tapes, floppies). + * + * By default, DMA support is prepared for use, but is currently enabled only + * for drives which already have DMA enabled (UltraDMA or mode 2 multi/single), + * or which are recognized as "good" (see table below). Drives with only mode0 + * or mode1 (multi/single) DMA should also work with this chipset/driver + * (eg. MC2112A) but are not enabled by default. + * + * Use "hdparm -i" to view modes supported by a given drive. + * + * The hdparm-3.5 (or later) utility can be used for manually enabling/disabling + * DMA support, but must be (re-)compiled against this kernel version or later. + * + * To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting. + * If problems arise, ide.c will disable DMA operation after a few retries. + * This error recovery mechanism works and has been extremely well exercised. + * + * IDE drives, depending on their vintage, may support several different modes + * of DMA operation. The boot-time modes are indicated with a "*" in + * the "hdparm -i" listing, and can be changed with *knowledgeable* use of + * the "hdparm -X" feature. There is seldom a need to do this, as drives + * normally power-up with their "best" PIO/DMA modes enabled. + * + * Testing has been done with a rather extensive number of drives, + * with Quantum & Western Digital models generally outperforming the pack, + * and Fujitsu & Conner (and some Seagate which are really Conner) drives + * showing more lackluster throughput. + * + * Keep an eye on /var/adm/messages for "DMA disabled" messages. + * + * Some people have reported trouble with Intel Zappa motherboards. + * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0, + * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe + * (thanks to Glen Morrell for researching this). + * + * Thanks to "Christopher J. Reimer" for + * fixing the problem with the BIOS on some Acer motherboards. + * + * Thanks to "Benoit Poulot-Cazajous" for testing + * "TX" chipset compatibility and for providing patches for the "TX" chipset. + * + * Thanks to Christian Brunner for taking a good first crack + * at generic DMA -- his patches were referred to when preparing this code. + * + * Most importantly, thanks to Robert Bringman + * for supplying a Promise UDMA board & WD UDMA drive for this work! + * + * And, yes, Intel Zappa boards really *do* use both PIIX IDE ports. + * + * check_drive_lists(ide_drive_t *drive, int good_bad) + * + * ATA-66/100 and recovery functions, I forgot the rest...... + * SELECT_READ_WRITE(hwif,drive,func) for active tuning based on IO direction. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* + * Long lost data from 2.0.34 that is now in 2.0.39 + * + * This was used in ./drivers/block/triton.c to do DMA Base address setup + * when PnP failed. Oh the things we forget. I believe this was part + * of SFF-8038i that has been withdrawn from public access... :-(( + */ +#define DEFAULT_BMIBA 0xe800 /* in case BIOS did not init it */ +#define DEFAULT_BMCRBA 0xcc00 /* VIA's default value */ +#define DEFAULT_BMALIBA 0xd400 /* ALI's default value */ + +#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS + +struct drive_list_entry { + char * id_model; + char * id_firmware; +}; + +struct drive_list_entry drive_whitelist [] = { + + { "Micropolis 2112A" , "ALL" }, + { "CONNER CTMA 4000" , "ALL" }, + { "CONNER CTT8000-A" , "ALL" }, + { "ST34342A" , "ALL" }, + { 0 , 0 } +}; + +struct drive_list_entry drive_blacklist [] = { + + { "WDC AC11000H" , "ALL" }, + { "WDC AC22100H" , "ALL" }, + { "WDC AC32500H" , "ALL" }, + { "WDC AC33100H" , "ALL" }, + { "WDC AC31600H" , "ALL" }, + { "WDC AC32100H" , "24.09P07" }, + { "WDC AC23200L" , "21.10N21" }, + { "Compaq CRD-8241B" , "ALL" }, + { "CRD-8400B" , "ALL" }, + { "CRD-8480B", "ALL" }, + { "CRD-8480C", "ALL" }, + { "CRD-8482B", "ALL" }, + { "CRD-84" , "ALL" }, + { "SanDisk SDP3B" , "ALL" }, + { "SanDisk SDP3B-64" , "ALL" }, + { "SANYO CD-ROM CRD" , "ALL" }, + { "HITACHI CDR-8" , "ALL" }, + { "HITACHI CDR-8335" , "ALL" }, + { "HITACHI CDR-8435" , "ALL" }, + { "Toshiba CD-ROM XM-6202B" , "ALL" }, + { "CD-532E-A" , "ALL" }, + { "E-IDE CD-ROM CR-840", "ALL" }, + { "CD-ROM Drive/F5A", "ALL" }, + { "RICOH CD-R/RW MP7083A", "ALL" }, + { "WPI CDD-820", "ALL" }, + { "SAMSUNG CD-ROM SC-148C", "ALL" }, + { "SAMSUNG CD-ROM SC-148F", "ALL" }, + { "SAMSUNG CD-ROM SC", "ALL" }, + { "SanDisk SDP3B-64" , "ALL" }, + { "SAMSUNG CD-ROM SN-124", "ALL" }, + { "PLEXTOR CD-R PX-W8432T", "ALL" }, + { "ATAPI CD-ROM DRIVE 40X MAXIMUM", "ALL" }, + { "_NEC DV5800A", "ALL" }, + { 0 , 0 } + +}; + +int in_drive_list(struct hd_driveid *id, struct drive_list_entry * drive_table) +{ + for ( ; drive_table->id_model ; drive_table++) + if ((!strcmp(drive_table->id_model, id->model)) && + ((!strstr(drive_table->id_firmware, id->fw_rev)) || + (!strcmp(drive_table->id_firmware, "ALL")))) + return 1; + return 0; +} + +#else /* !CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */ + +/* + * good_dma_drives() lists the model names (from "hdparm -i") + * of drives which do not support mode2 DMA but which are + * known to work fine with this interface under Linux. + */ +const char *good_dma_drives[] = {"Micropolis 2112A", + "CONNER CTMA 4000", + "CONNER CTT8000-A", + "ST34342A", /* for Sun Ultra */ + NULL}; + +/* + * bad_dma_drives() lists the model names (from "hdparm -i") + * of drives which supposedly support (U)DMA but which are + * known to corrupt data with this interface under Linux. + * + * This is an empirical list. Its generated from bug reports. That means + * while it reflects actual problem distributions it doesn't answer whether + * the drive or the controller, or cabling, or software, or some combination + * thereof is the fault. If you don't happen to agree with the kernel's + * opinion of your drive - use hdparm to turn DMA on. + */ +const char *bad_dma_drives[] = {"WDC AC11000H", + "WDC AC22100H", + "WDC AC32100H", + "WDC AC32500H", + "WDC AC33100H", + "WDC AC31600H", + NULL}; + +#endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */ + +/* + * dma_intr() is the handler for disk read/write DMA interrupts + */ +ide_startstop_t ide_dma_intr (ide_drive_t *drive) +{ + int i; + byte stat, dma_stat; + + dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive); + stat = GET_STAT(); /* get drive status */ + if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) { + if (!dma_stat) { + struct request *rq = HWGROUP(drive)->rq; + rq = HWGROUP(drive)->rq; + for (i = rq->nr_sectors; i > 0;) { + i -= rq->current_nr_sectors; + DRIVER(drive)->end_request(drive, 1); + } + return ide_stopped; + } + printk("%s: dma_intr: bad DMA status (dma_stat=%x)\n", + drive->name, dma_stat); + } + return DRIVER(drive)->error(drive, "dma_intr", stat); +} + +static int ide_build_sglist (ide_drive_t *drive, struct request *rq) +{ + ide_hwif_t *hwif = HWIF(drive); + struct scatterlist *sg = hwif->sg_table; + int nents; + + if (hwif->sg_dma_active) + BUG(); + + nents = blk_rq_map_sg(&drive->queue, rq, hwif->sg_table); + + if (rq_data_dir(rq) == READ) + hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; + else + hwif->sg_dma_direction = PCI_DMA_TODEVICE; + + return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction); +} + +static int ide_raw_build_sglist (ide_drive_t *drive, struct request *rq) +{ + ide_hwif_t *hwif = HWIF(drive); + struct scatterlist *sg = hwif->sg_table; + int nents = 0; + ide_task_t *args = rq->special; + unsigned char *virt_addr = rq->buffer; + int sector_count = rq->nr_sectors; + + if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE) + hwif->sg_dma_direction = PCI_DMA_TODEVICE; + else + hwif->sg_dma_direction = PCI_DMA_FROMDEVICE; +#if 1 + if (sector_count > 128) { + memset(&sg[nents], 0, sizeof(*sg)); + sg[nents].page = virt_to_page(virt_addr); + sg[nents].offset = (unsigned long) virt_addr & ~PAGE_MASK; + sg[nents].length = 128 * SECTOR_SIZE; + nents++; + virt_addr = virt_addr + (128 * SECTOR_SIZE); + sector_count -= 128; + } + memset(&sg[nents], 0, sizeof(*sg)); + sg[nents].page = virt_to_page(virt_addr); + sg[nents].offset = (unsigned long) virt_addr & ~PAGE_MASK; + sg[nents].length = sector_count * SECTOR_SIZE; + nents++; +#else + while (sector_count > 128) { + memset(&sg[nents], 0, sizeof(*sg)); + sg[nents].address = virt_to_page(virt_addr); + sg[nents].offset = (unsigned long)virt_addr & ~PAGE_MASK; + sg[nents].length = 128 * SECTOR_SIZE; + nents++; + virt_addr = virt_addr + (128 * SECTOR_SIZE); + sector_count -= 128; + }; + memset(&sg[nents], 0, sizeof(*sg)); + sg[nents].page = virt_to_page(virt_addr); + sg[nents].offset = (unsigned long) virt_addr & ~PAGE_MASK; + sg[nents].length = sector_count * SECTOR_SIZE; + nents++; +#endif + return pci_map_sg(hwif->pci_dev, sg, nents, hwif->sg_dma_direction); +} + +/* + * ide_build_dmatable() prepares a dma request. + * Returns 0 if all went okay, returns 1 otherwise. + * May also be invoked from trm290.c + */ +int ide_build_dmatable (ide_drive_t *drive, ide_dma_action_t func) +{ + unsigned int *table = HWIF(drive)->dmatable_cpu; +#ifdef CONFIG_BLK_DEV_TRM290 + unsigned int is_trm290_chipset = (HWIF(drive)->chipset == ide_trm290); +#else + const int is_trm290_chipset = 0; +#endif + unsigned int count = 0; + int i; + struct scatterlist *sg; + + if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE) + HWIF(drive)->sg_nents = i = ide_raw_build_sglist(drive, HWGROUP(drive)->rq); + else + HWIF(drive)->sg_nents = i = ide_build_sglist(drive, HWGROUP(drive)->rq); + + if (!i) + return 0; + + sg = HWIF(drive)->sg_table; + while (i) { + u32 cur_addr; + u32 cur_len; + + cur_addr = sg_dma_address(sg); + cur_len = sg_dma_len(sg); + + /* + * Fill in the dma table, without crossing any 64kB boundaries. + * Most hardware requires 16-bit alignment of all blocks, + * but the trm290 requires 32-bit alignment. + */ + + while (cur_len) { + if (count++ >= PRD_ENTRIES) { + printk("%s: DMA table too small\n", drive->name); + goto use_pio_instead; + } else { + u32 xcount, bcount = 0x10000 - (cur_addr & 0xffff); + + if (bcount > cur_len) + bcount = cur_len; + *table++ = cpu_to_le32(cur_addr); + xcount = bcount & 0xffff; + if (is_trm290_chipset) + xcount = ((xcount >> 2) - 1) << 16; + if (xcount == 0x0000) { + /* + * Most chipsets correctly interpret a length of 0x0000 as 64KB, + * but at least one (e.g. CS5530) misinterprets it as zero (!). + * So here we break the 64KB entry into two 32KB entries instead. + */ + if (count++ >= PRD_ENTRIES) { + printk("%s: DMA table too small\n", drive->name); + goto use_pio_instead; + } + *table++ = cpu_to_le32(0x8000); + *table++ = cpu_to_le32(cur_addr + 0x8000); + xcount = 0x8000; + } + *table++ = cpu_to_le32(xcount); + cur_addr += bcount; + cur_len -= bcount; + } + } + + sg++; + i--; + } + + if (count) { + if (!is_trm290_chipset) + *--table |= cpu_to_le32(0x80000000); + return count; + } + printk("%s: empty DMA table?\n", drive->name); +use_pio_instead: + pci_unmap_sg(HWIF(drive)->pci_dev, + HWIF(drive)->sg_table, + HWIF(drive)->sg_nents, + HWIF(drive)->sg_dma_direction); + HWIF(drive)->sg_dma_active = 0; + return 0; /* revert to PIO for this request */ +} + +/* Teardown mappings after DMA has completed. */ +void ide_destroy_dmatable (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + struct scatterlist *sg = HWIF(drive)->sg_table; + int nents = HWIF(drive)->sg_nents; + + pci_unmap_sg(dev, sg, nents, HWIF(drive)->sg_dma_direction); + HWIF(drive)->sg_dma_active = 0; +} + +/* + * For both Blacklisted and Whitelisted drives. + * This is setup to be called as an extern for future support + * to other special driver code. + */ +int check_drive_lists (ide_drive_t *drive, int good_bad) +{ + struct hd_driveid *id = drive->id; + +#ifdef CONFIG_IDEDMA_NEW_DRIVE_LISTINGS + if (good_bad) { + return in_drive_list(id, drive_whitelist); + } else { + int blacklist = in_drive_list(id, drive_blacklist); + if (blacklist) + printk("%s: Disabling (U)DMA for %s\n", drive->name, id->model); + return(blacklist); + } +#else /* !CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */ + const char **list; + + if (good_bad) { + /* Consult the list of known "good" drives */ + list = good_dma_drives; + while (*list) { + if (!strcmp(*list++,id->model)) + return 1; + } + } else { + /* Consult the list of known "bad" drives */ + list = bad_dma_drives; + while (*list) { + if (!strcmp(*list++,id->model)) { + printk("%s: Disabling (U)DMA for %s\n", + drive->name, id->model); + return 1; + } + } + } +#endif /* CONFIG_IDEDMA_NEW_DRIVE_LISTINGS */ + return 0; +} + +int report_drive_dmaing (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + + if ((id->field_valid & 4) && (eighty_ninty_three(drive)) && + (id->dma_ultra & (id->dma_ultra >> 14) & 3)) { + if ((id->dma_ultra >> 15) & 1) { + printk(", UDMA(mode 7)"); /* UDMA BIOS-enabled! */ + } else { + printk(", UDMA(133)"); /* UDMA BIOS-enabled! */ + } + } else if ((id->field_valid & 4) && (eighty_ninty_three(drive)) && + (id->dma_ultra & (id->dma_ultra >> 11) & 7)) { + if ((id->dma_ultra >> 13) & 1) { + printk(", UDMA(100)"); /* UDMA BIOS-enabled! */ + } else if ((id->dma_ultra >> 12) & 1) { + printk(", UDMA(66)"); /* UDMA BIOS-enabled! */ + } else { + printk(", UDMA(44)"); /* UDMA BIOS-enabled! */ + } + } else if ((id->field_valid & 4) && + (id->dma_ultra & (id->dma_ultra >> 8) & 7)) { + if ((id->dma_ultra >> 10) & 1) { + printk(", UDMA(33)"); /* UDMA BIOS-enabled! */ + } else if ((id->dma_ultra >> 9) & 1) { + printk(", UDMA(25)"); /* UDMA BIOS-enabled! */ + } else { + printk(", UDMA(16)"); /* UDMA BIOS-enabled! */ + } + } else if (id->field_valid & 4) { + printk(", (U)DMA"); /* Can be BIOS-enabled! */ + } else { + printk(", DMA"); + } + return 1; +} + +static int config_drive_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + + if (id && (id->capability & 1) && hwif->autodma) { + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) + return hwif->dmaproc(ide_dma_off, drive); + + /* Enable DMA on any drive that has UltraDMA (mode 6/7/?) enabled */ + if ((id->field_valid & 4) && (eighty_ninty_three(drive))) + if ((id->dma_ultra & (id->dma_ultra >> 14) & 2)) + return hwif->dmaproc(ide_dma_on, drive); + /* Enable DMA on any drive that has UltraDMA (mode 3/4/5) enabled */ + if ((id->field_valid & 4) && (eighty_ninty_three(drive))) + if ((id->dma_ultra & (id->dma_ultra >> 11) & 7)) + return hwif->dmaproc(ide_dma_on, drive); + /* Enable DMA on any drive that has UltraDMA (mode 0/1/2) enabled */ + if (id->field_valid & 4) /* UltraDMA */ + if ((id->dma_ultra & (id->dma_ultra >> 8) & 7)) + return hwif->dmaproc(ide_dma_on, drive); + /* Enable DMA on any drive that has mode2 DMA (multi or single) enabled */ + if (id->field_valid & 2) /* regular DMA */ + if ((id->dma_mword & 0x404) == 0x404 || (id->dma_1word & 0x404) == 0x404) + return hwif->dmaproc(ide_dma_on, drive); + /* Consult the list of known "good" drives */ + if (ide_dmaproc(ide_dma_good_drive, drive)) + return hwif->dmaproc(ide_dma_on, drive); + } + return hwif->dmaproc(ide_dma_off_quietly, drive); +} + +#ifndef __IDEDMA_TIMEOUT +/* + * 1 dmaing, 2 error, 4 intr + */ +static int dma_timer_expiry (ide_drive_t *drive) +{ + byte dma_stat = IN_BYTE(HWIF(drive)->dma_base+2); + +#ifdef DEBUG + printk("%s: dma_timer_expiry: dma status == 0x%02x\n", drive->name, dma_stat); +#endif /* DEBUG */ + +#if 0 + HWGROUP(drive)->expiry = NULL; /* one free ride for now */ +#endif + + if (dma_stat & 2) { /* ERROR */ + byte stat = GET_STAT(); + return DRIVER(drive)->error(drive, "dma_timer_expiry", stat); + } + if (dma_stat & 1) /* DMAing */ + return WAIT_CMD; + return 0; +} +#else /* __IDEDMA_TIMEOUT */ +static int ide_dma_timeout_recovery (ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + int enable_dma = drive->using_dma; + int speed = drive->current_speed; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + HWGROUP(drive)->handler = NULL; + del_timer(&HWGROUP(drive)->timer); + HWGROUP(drive)->expiry = NULL; + HWGROUP(drive)->rq = NULL; + spin_unlock_irqrestore(&ide_lock, flags); + + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); + drive->waiting_for_dma = 0; + + (void) ide_do_reset(drive); + + if (!(drive_is_ready(drive))) { + /* FIXME: Replace hard-coded 100, error handling? */ + int i; + for (i=0; i<100; i++) { + if (drive_is_ready(drive)) + break; + } + } + + if ((HWIF(drive)->speedproc) != NULL) { + HWIF(drive)->speedproc(drive, speed); + drive->current_speed = speed; + } + + if ((enable_dma) && !(drive->using_dma)) + (void) HWIF(drive)->dmaproc(ide_dma_on, drive); + + return restart_request(drive, rq); +} +#endif /* __IDEDMA_TIMEOUT */ + +static void ide_toggle_bounce(ide_drive_t *drive, int on) +{ + u64 addr = BLK_BOUNCE_HIGH; + + if (on && drive->media == ide_disk && !HWIF(drive)->no_highmem) { + if (!PCI_DMA_BUS_IS_PHYS) + addr = BLK_BOUNCE_ANY; + else + addr = HWIF(drive)->pci_dev->dma_mask; + } + + blk_queue_bounce_limit(&drive->queue, addr); +} + +/* + * ide_dmaproc() initiates/aborts DMA read/write operations on a drive. + * + * The caller is assumed to have selected the drive and programmed the drive's + * sector address using CHS or LBA. All that remains is to prepare for DMA + * and then issue the actual read/write DMA/PIO command to the drive. + * + * For ATAPI devices, we just prepare for DMA and return. The caller should + * then issue the packet command to the drive and call us again with + * ide_dma_begin afterwards. + * + * Returns 0 if all went well. + * Returns 1 if DMA read/write could not be started, in which case + * the caller should revert to PIO for the current request. + * May also be invoked from trm290.c + */ +int ide_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); +// ide_task_t *args = HWGROUP(drive)->rq->special; + unsigned long dma_base = hwif->dma_base; + byte unit = (drive->select.b.unit & 0x01); + unsigned int count, reading = 0; + unsigned int set_high = 1; + byte dma_stat; + + switch (func) { + case ide_dma_off: + printk("%s: DMA disabled\n", drive->name); + case ide_dma_off_quietly: + case ide_dma_host_off: + OUT_BYTE(IN_BYTE(dma_base+2) & ~(1<<(5+unit)), dma_base+2); + if (func == ide_dma_host_off) + return 0; + set_high = 0; + case ide_dma_on: + drive->using_dma = (func == ide_dma_on); + if (!drive->using_dma) + return 0; + case ide_dma_host_on: + if (drive->using_dma) + OUT_BYTE(IN_BYTE(dma_base+2)|(1<<(5+unit)), dma_base+2); + ide_toggle_bounce(drive, set_high); + return 0; + case ide_dma_check: + return config_drive_for_dma (drive); + case ide_dma_read: + reading = 1 << 3; + case ide_dma_write: + SELECT_READ_WRITE(hwif,drive,func); + if (!(count = ide_build_dmatable(drive, func))) + /* try PIO instead of DMA */ + return 1; + /* PRD table */ + outl(hwif->dmatable_dma, dma_base + 4); + /* specify r/w */ + OUT_BYTE(reading, dma_base); + /* clear INTR & ERROR flags */ + OUT_BYTE(IN_BYTE(dma_base+2)|6, dma_base+2); + drive->waiting_for_dma = 1; + if (drive->media != ide_disk) + return 0; + /* paranoia check */ + if (HWGROUP(drive)->handler != NULL) + BUG(); +#ifndef __IDEDMA_TIMEOUT + ide_set_handler(drive, &ide_dma_intr, 2*WAIT_CMD, dma_timer_expiry); +#else /* __IDEDMA_TIMEOUT */ + ide_set_handler(drive, &ide_dma_intr, 2*WAIT_CMD, NULL); +#endif /* __IDEDMA_TIMEOUT */ + /* issue cmd to drive */ + /* + * FIX ME to use only ACB ide_task_t args Struct + */ +#if 0 + { + ide_task_t *args = HWGROUP(drive)->rq->special; + OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } +#else + if (HWGROUP(drive)->rq->flags & REQ_DRIVE_TASKFILE) { + ide_task_t *args = HWGROUP(drive)->rq->special; + OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } else if (drive->addressing == 1) + OUT_BYTE(reading ? WIN_READDMA_EXT : WIN_WRITEDMA_EXT, IDE_COMMAND_REG); + else + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); +#endif + return HWIF(drive)->dmaproc(ide_dma_begin, drive); + case ide_dma_begin: + /* Note that this is done *after* the cmd has + * been issued to the drive, as per the BM-IDE spec. + * The Promise Ultra33 doesn't work correctly when + * we do this part before issuing the drive cmd. + */ + /* start DMA */ + OUT_BYTE(IN_BYTE(dma_base)|1, dma_base); + return 0; + case ide_dma_end: /* returns 1 on error, 0 otherwise */ + drive->waiting_for_dma = 0; + /* stop DMA */ + OUT_BYTE(IN_BYTE(dma_base)&~1, dma_base); + /* get DMA status */ + dma_stat = IN_BYTE(dma_base+2); + /* clear the INTR & ERROR bits */ + OUT_BYTE(dma_stat|6, dma_base+2); + /* purge DMA mappings */ + ide_destroy_dmatable(drive); + /* verify good DMA status */ + return (dma_stat & 7) != 4 ? (0x10 | dma_stat) : 0; + case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */ + dma_stat = IN_BYTE(dma_base+2); +#if 0 /* do not set unless you know what you are doing */ + if (dma_stat & 4) { + byte stat = GET_STAT(); + OUT_BYTE(dma_base+2, dma_stat & 0xE4); + } +#endif + /* return 1 if INTR asserted */ + return (dma_stat & 4) == 4; + case ide_dma_bad_drive: + case ide_dma_good_drive: + return check_drive_lists(drive, (func == ide_dma_good_drive)); + case ide_dma_verbose: + return report_drive_dmaing(drive); + case ide_dma_timeout: + // FIXME: Many IDE chipsets do not permit command file register access + // FIXME: while the bus-master function is still active. + // FIXME: To prevent deadlock with those chipsets, we must be extremely + // FIXME: careful here (and in ide_intr() as well) to NOT access any + // FIXME: registers from the 0x1Fx/0x17x sets before terminating the + // FIXME: bus-master operation via the bus-master control reg. + // FIXME: Otherwise, chipset deadlock will occur, and some systems will + // FIXME: lock up completely!! +#ifdef __IDEDMA_TIMEOUT + /* + * Have to issue an abort and requeue the request + * DMA engine got turned off by a goofy ASIC, and + * we have to clean up the mess, and here is as good + * as any. Do it globally for all chipsets. + */ +#if 0 + dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive); +#else + drive->waiting_for_dma = 0; + /* stop DMA */ + OUT_BYTE(IN_BYTE(dma_base)&~1, dma_base); +// OUT_BYTE(0x00, dma_base); + /* get DMA status */ + dma_stat = IN_BYTE(dma_base+2); + /* clear the INTR & ERROR bits */ + OUT_BYTE(dma_stat|6, dma_base+2); + /* purge DMA mappings */ + ide_destroy_dmatable(drive); +#endif + printk("%s: %s: Lets do it again!" \ + "stat = 0x%02x, dma_stat = 0x%02x\n", + drive->name, ide_dmafunc_verbose(func), + GET_STAT(), dma_stat); + + if (dma_stat & 0xF0) + return ide_dma_timeout_recovery(drive); +#endif /* __IDEDMA_TIMEOUT */ + case ide_dma_retune: + case ide_dma_lostirq: + printk("ide_dmaproc: chipset supported %s " + "func only: %d\n", + ide_dmafunc_verbose(func), func); + return 1; + default: + printk("ide_dmaproc: unsupported %s func: %d\n", + ide_dmafunc_verbose(func), func); + return 1; + } +} + +/* + * Needed for allowing full modular support of ide-driver + */ +int ide_release_dma (ide_hwif_t *hwif) +{ + if (hwif->dmatable_cpu) { + pci_free_consistent(hwif->pci_dev, + PRD_ENTRIES * PRD_BYTES, + hwif->dmatable_cpu, + hwif->dmatable_dma); + hwif->dmatable_cpu = NULL; + } + if (hwif->sg_table) { + kfree(hwif->sg_table); + hwif->sg_table = NULL; + } + if ((hwif->dma_extra) && (hwif->channel == 0)) + release_region((hwif->dma_base + 16), hwif->dma_extra); + release_region(hwif->dma_base, 8); + return 1; +} + +/* + * This can be called for a dynamically installed interface. Don't __init it + */ + +void ide_setup_dma (ide_hwif_t *hwif, unsigned long dma_base, unsigned int num_ports) +{ + printk(" %s: BM-DMA at 0x%04lx-0x%04lx", hwif->name, dma_base, dma_base + num_ports - 1); + if (check_region(dma_base, num_ports)) { + printk(" -- ERROR, PORT ADDRESSES ALREADY IN USE\n"); + return; + } + request_region(dma_base, num_ports, hwif->name); + hwif->dma_base = dma_base; + hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev, + PRD_ENTRIES * PRD_BYTES, + &hwif->dmatable_dma); + if (hwif->dmatable_cpu == NULL) + goto dma_alloc_failure; + + hwif->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES, + GFP_KERNEL); + if (hwif->sg_table == NULL) { + pci_free_consistent(hwif->pci_dev, PRD_ENTRIES * PRD_BYTES, + hwif->dmatable_cpu, hwif->dmatable_dma); + goto dma_alloc_failure; + } + + hwif->dmaproc = &ide_dmaproc; + + if (hwif->chipset != ide_trm290) { + byte dma_stat = IN_BYTE(dma_base+2); + printk(", BIOS settings: %s:%s, %s:%s", + hwif->drives[0].name, (dma_stat & 0x20) ? "DMA" : "pio", + hwif->drives[1].name, (dma_stat & 0x40) ? "DMA" : "pio"); + } + printk("\n"); + return; + +dma_alloc_failure: + printk(" -- ERROR, UNABLE TO ALLOCATE DMA TABLES\n"); +} + +/* + * Fetch the DMA Bus-Master-I/O-Base-Address (BMIBA) from PCI space: + */ +unsigned long __init ide_get_or_set_dma_base (ide_hwif_t *hwif, int extra, const char *name) +{ + unsigned long dma_base = 0; + struct pci_dev *dev = hwif->pci_dev; + +#ifdef CONFIG_BLK_DEV_IDEDMA_FORCED + int second_chance = 0; + +second_chance_to_dma: +#endif /* CONFIG_BLK_DEV_IDEDMA_FORCED */ + + if (hwif->mate && hwif->mate->dma_base) { + dma_base = hwif->mate->dma_base - (hwif->channel ? 0 : 8); + } else { + dma_base = pci_resource_start(dev, 4); + if (!dma_base) { + printk("%s: dma_base is invalid (0x%04lx)\n", name, dma_base); + dma_base = 0; + } + } + +#ifdef CONFIG_BLK_DEV_IDEDMA_FORCED + if ((!dma_base) && (!second_chance)) { + unsigned long set_bmiba = 0; + second_chance++; + switch(dev->vendor) { + case PCI_VENDOR_ID_AL: + set_bmiba = DEFAULT_BMALIBA; break; + case PCI_VENDOR_ID_VIA: + set_bmiba = DEFAULT_BMCRBA; break; + case PCI_VENDOR_ID_INTEL: + set_bmiba = DEFAULT_BMIBA; break; + default: + return dma_base; + } + pci_write_config_dword(dev, 0x20, set_bmiba|1); + goto second_chance_to_dma; + } +#endif /* CONFIG_BLK_DEV_IDEDMA_FORCED */ + + if (dma_base) { + if (extra) /* PDC20246, PDC20262, HPT343, & HPT366 */ + request_region(dma_base+16, extra, name); + dma_base += hwif->channel ? 8 : 0; + hwif->dma_extra = extra; + + switch(dev->device) { + case PCI_DEVICE_ID_AL_M5219: + case PCI_DEVICE_ID_AMD_VIPER_7409: + case PCI_DEVICE_ID_CMD_643: + case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: + OUT_BYTE(IN_BYTE(dma_base+2) & 0x60, dma_base+2); + if (IN_BYTE(dma_base+2) & 0x80) { + printk("%s: simplex device: DMA forced\n", name); + } + break; + default: + /* + * If the device claims "simplex" DMA, + * this means only one of the two interfaces + * can be trusted with DMA at any point in time. + * So we should enable DMA only on one of the + * two interfaces. + */ + if ((IN_BYTE(dma_base+2) & 0x80)) { /* simplex device? */ + if ((!hwif->drives[0].present && !hwif->drives[1].present) || + (hwif->mate && hwif->mate->dma_base)) { + printk("%s: simplex device: DMA disabled\n", name); + dma_base = 0; + } + } + } + } + return dma_base; +} diff -Nru a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-floppy.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,2291 @@ +/* + * linux/drivers/ide/ide-floppy.c Version 0.99 Feb 24 2002 + * + * Copyright (C) 1996 - 1999 Gadi Oxman + * Copyright (C) 2000 - 2002 Paul Bristow + */ + +/* + * IDE ATAPI floppy driver. + * + * The driver currently doesn't have any fancy features, just the bare + * minimum read/write support. + * + * This driver supports the following IDE floppy drives: + * + * LS-120/240 SuperDisk + * Iomega Zip 100/250 + * Iomega PC Card Clik!/PocketZip + * + * Many thanks to Lode Leroy , who tested so many + * ALPHA patches to this driver on an EASYSTOR LS-120 ATAPI floppy drive. + * + * Ver 0.1 Oct 17 96 Initial test version, mostly based on ide-tape.c. + * Ver 0.2 Oct 31 96 Minor changes. + * Ver 0.3 Dec 2 96 Fixed error recovery bug. + * Ver 0.4 Jan 26 97 Add support for the HDIO_GETGEO ioctl. + * Ver 0.5 Feb 21 97 Add partitions support. + * Use the minimum of the LBA and CHS capacities. + * Avoid hwgroup->rq == NULL on the last irq. + * Fix potential null dereferencing with DEBUG_LOG. + * Ver 0.8 Dec 7 97 Increase irq timeout from 10 to 50 seconds. + * Add media write-protect detection. + * Issue START command only if TEST UNIT READY fails. + * Add work-around for IOMEGA ZIP revision 21.D. + * Remove idefloppy_get_capabilities(). + * Ver 0.9 Jul 4 99 Fix a bug which might have caused the number of + * bytes requested on each interrupt to be zero. + * Thanks to for pointing this out. + * Ver 0.9.sv Jan 6 01 Sam Varshavchik + * Implement low level formatting. Reimplemented + * IDEFLOPPY_CAPABILITIES_PAGE, since we need the srfp + * bit. My LS-120 drive barfs on + * IDEFLOPPY_CAPABILITIES_PAGE, but maybe it's just me. + * Compromise by not reporting a failure to get this + * mode page. Implemented four IOCTLs in order to + * implement formatting. IOCTls begin with 0x4600, + * 0x46 is 'F' as in Format. + * Jan 9 01 Userland option to select format verify. + * Added PC_SUPPRESS_ERROR flag - some idefloppy drives + * do not implement IDEFLOPPY_CAPABILITIES_PAGE, and + * return a sense error. Suppress error reporting in + * this particular case in order to avoid spurious + * errors in syslog. The culprit is + * idefloppy_get_capability_page(), so move it to + * idefloppy_begin_format() so that it's not used + * unless absolutely necessary. + * If drive does not support format progress indication + * monitor the dsc bit in the status register. + * Also, O_NDELAY on open will allow the device to be + * opened without a disk available. This can be used to + * open an unformatted disk, or get the device capacity. + * Ver 0.91 Dec 11 99 Added IOMEGA Clik! drive support by + * + * Ver 0.92 Oct 22 00 Paul Bristow became official maintainer for this + * driver. Included Powerbook internal zip kludge. + * Ver 0.93 Oct 24 00 Fixed bugs for Clik! drive + * no disk on insert and disk change now works + * Ver 0.94 Oct 27 00 Tidied up to remove strstr(Clik) everywhere + * Ver 0.95 Nov 7 00 Brought across to kernel 2.4 + * Ver 0.96 Jan 7 01 Actually in line with release version of 2.4.0 + * including set_bit patch from Rusty Russell + * Ver 0.97 Jul 22 01 Merge 0.91-0.96 onto 0.9.sv for ac series + * Ver 0.97.sv Aug 3 01 Backported from 2.4.7-ac3 + * Ver 0.98 Oct 26 01 Split idefloppy_transfer_pc into two pieces to + * fix a lost interrupt problem. It appears the busy + * bit was being deasserted by my IOMEGA ATAPI ZIP 100 + * drive before the drive was actually ready. + * Ver 0.98a Oct 29 01 Expose delay value so we can play. + * Ver 0.99 Feb 24 02 Remove duplicate code, modify clik! detection code + * to support new PocketZip drives + */ + +#define IDEFLOPPY_VERSION "0.99.newide" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/* + * The following are used to debug the driver. + */ +#define IDEFLOPPY_DEBUG_LOG 0 +#define IDEFLOPPY_DEBUG_INFO 0 +#define IDEFLOPPY_DEBUG_BUGS 1 + +/* #define IDEFLOPPY_DEBUG(fmt, args...) printk(KERN_INFO fmt, ## args) */ +#define IDEFLOPPY_DEBUG( fmt, args... ) + + +/* + * Some drives require a longer irq timeout. + */ +#define IDEFLOPPY_WAIT_CMD (5 * WAIT_CMD) + +/* + * After each failed packet command we issue a request sense command + * and retry the packet command IDEFLOPPY_MAX_PC_RETRIES times. + */ +#define IDEFLOPPY_MAX_PC_RETRIES 3 + +/* + * With each packet command, we allocate a buffer of + * IDEFLOPPY_PC_BUFFER_SIZE bytes. + */ +#define IDEFLOPPY_PC_BUFFER_SIZE 256 + +/* + * In various places in the driver, we need to allocate storage + * for packet commands and requests, which will remain valid while + * we leave the driver to wait for an interrupt or a timeout event. + */ +#define IDEFLOPPY_PC_STACK (10 + IDEFLOPPY_MAX_PC_RETRIES) + +/* + * Our view of a packet command. + */ +typedef struct idefloppy_packet_command_s { + u8 c[12]; /* Actual packet bytes */ + int retries; /* On each retry, we increment retries */ + int error; /* Error code */ + int request_transfer; /* Bytes to transfer */ + int actually_transferred; /* Bytes actually transferred */ + int buffer_size; /* Size of our data buffer */ + char *b_data; /* Pointer which runs on the buffers */ + int b_count; /* Missing/Available data on the current buffer */ + struct request *rq; /* The corresponding request */ + byte *buffer; /* Data buffer */ + byte *current_position; /* Pointer into the above buffer */ + void (*callback) (ide_drive_t *); /* Called when this packet command is completed */ + byte pc_buffer[IDEFLOPPY_PC_BUFFER_SIZE]; /* Temporary buffer */ + unsigned long flags; /* Status/Action bit flags: long for set_bit */ +} idefloppy_pc_t; + +/* + * Packet command flag bits. + */ +#define PC_ABORT 0 /* Set when an error is considered normal - We won't retry */ +#define PC_DMA_RECOMMENDED 2 /* 1 when we prefer to use DMA if possible */ +#define PC_DMA_IN_PROGRESS 3 /* 1 while DMA in progress */ +#define PC_DMA_ERROR 4 /* 1 when encountered problem during DMA */ +#define PC_WRITING 5 /* Data direction */ + +#define PC_SUPPRESS_ERROR 6 /* Suppress error reporting */ + +/* + * Removable Block Access Capabilities Page + */ +typedef struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned page_code :6; /* Page code - Should be 0x1b */ + unsigned reserved1_6 :1; /* Reserved */ + unsigned ps :1; /* Should be 0 */ +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned ps :1; /* Should be 0 */ + unsigned reserved1_6 :1; /* Reserved */ + unsigned page_code :6; /* Page code - Should be 0x1b */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + u8 page_length; /* Page Length - Should be 0xa */ +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned reserved2 :6; + unsigned srfp :1; /* Supports reporting progress of format */ + unsigned sflp :1; /* System floppy type device */ + unsigned tlun :3; /* Total logical units supported by the device */ + unsigned reserved3 :3; + unsigned sml :1; /* Single / Multiple lun supported */ + unsigned ncd :1; /* Non cd optical device */ +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned sflp :1; /* System floppy type device */ + unsigned srfp :1; /* Supports reporting progress of format */ + unsigned reserved2 :6; + unsigned ncd :1; /* Non cd optical device */ + unsigned sml :1; /* Single / Multiple lun supported */ + unsigned reserved3 :3; + unsigned tlun :3; /* Total logical units supported by the device */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + u8 reserved[8]; +} idefloppy_capabilities_page_t; + +/* + * Flexible disk page. + */ +typedef struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned page_code :6; /* Page code - Should be 0x5 */ + unsigned reserved1_6 :1; /* Reserved */ + unsigned ps :1; /* The device is capable of saving the page */ +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned ps :1; /* The device is capable of saving the page */ + unsigned reserved1_6 :1; /* Reserved */ + unsigned page_code :6; /* Page code - Should be 0x5 */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + u8 page_length; /* Page Length - Should be 0x1e */ + u16 transfer_rate; /* In kilobits per second */ + u8 heads, sectors; /* Number of heads, Number of sectors per track */ + u16 sector_size; /* Byes per sector */ + u16 cyls; /* Number of cylinders */ + u8 reserved10[10]; + u8 motor_delay; /* Motor off delay */ + u8 reserved21[7]; + u16 rpm; /* Rotations per minute */ + u8 reserved30[2]; +} idefloppy_flexible_disk_page_t; + +/* + * Format capacity + */ +typedef struct { + u8 reserved[3]; + u8 length; /* Length of the following descriptors in bytes */ +} idefloppy_capacity_header_t; + +typedef struct { + u32 blocks; /* Number of blocks */ +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned dc :2; /* Descriptor Code */ + unsigned reserved :6; +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned reserved :6; + unsigned dc :2; /* Descriptor Code */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + u8 length_msb; /* Block Length (MSB)*/ + u16 length; /* Block Length */ +} idefloppy_capacity_descriptor_t; + +#define CAPACITY_INVALID 0x00 +#define CAPACITY_UNFORMATTED 0x01 +#define CAPACITY_CURRENT 0x02 +#define CAPACITY_NO_CARTRIDGE 0x03 + +/* + * Most of our global data which we need to save even as we leave the + * driver due to an interrupt or a timer event is stored in a variable + * of type idefloppy_floppy_t, defined below. + */ +typedef struct { + ide_drive_t *drive; + + idefloppy_pc_t *pc; /* Current packet command */ + idefloppy_pc_t *failed_pc; /* Last failed packet command */ + idefloppy_pc_t pc_stack[IDEFLOPPY_PC_STACK];/* Packet command stack */ + int pc_stack_index; /* Next free packet command storage space */ + struct request rq_stack[IDEFLOPPY_PC_STACK]; + int rq_stack_index; /* We implement a circular array */ + + /* + * Last error information + */ + byte sense_key, asc, ascq; + byte ticks; /* delay this long before sending packet command */ + int progress_indication; + + /* + * Device information + */ + int blocks, block_size, bs_factor; /* Current format */ + idefloppy_capacity_descriptor_t capacity; /* Last format capacity */ + idefloppy_flexible_disk_page_t flexible_disk_page; /* Copy of the flexible disk page */ + int wp; /* Write protect */ + int srfp; /* Supports format progress report */ + unsigned long flags; /* Status/Action flags */ +} idefloppy_floppy_t; + +#define IDEFLOPPY_TICKS_DELAY 3 /* default delay for ZIP 100 */ + +/* + * Floppy flag bits values. + */ +#define IDEFLOPPY_DRQ_INTERRUPT 0 /* DRQ interrupt device */ +#define IDEFLOPPY_MEDIA_CHANGED 1 /* Media may have changed */ +#define IDEFLOPPY_USE_READ12 2 /* Use READ12/WRITE12 or READ10/WRITE10 */ +#define IDEFLOPPY_FORMAT_IN_PROGRESS 3 /* Format in progress */ +#define IDEFLOPPY_CLIK_DRIVE 4 /* Avoid commands not supported in Clik drive */ +#define IDEFLOPPY_ZIP_DRIVE 5 /* Requires BH algorithm for packets */ + +/* + * ATAPI floppy drive packet commands + */ +#define IDEFLOPPY_FORMAT_UNIT_CMD 0x04 +#define IDEFLOPPY_INQUIRY_CMD 0x12 +#define IDEFLOPPY_MODE_SELECT_CMD 0x55 +#define IDEFLOPPY_MODE_SENSE_CMD 0x5a +#define IDEFLOPPY_READ10_CMD 0x28 +#define IDEFLOPPY_READ12_CMD 0xa8 +#define IDEFLOPPY_READ_CAPACITY_CMD 0x23 +#define IDEFLOPPY_REQUEST_SENSE_CMD 0x03 +#define IDEFLOPPY_PREVENT_REMOVAL_CMD 0x1e +#define IDEFLOPPY_SEEK_CMD 0x2b +#define IDEFLOPPY_START_STOP_CMD 0x1b +#define IDEFLOPPY_TEST_UNIT_READY_CMD 0x00 +#define IDEFLOPPY_VERIFY_CMD 0x2f +#define IDEFLOPPY_WRITE10_CMD 0x2a +#define IDEFLOPPY_WRITE12_CMD 0xaa +#define IDEFLOPPY_WRITE_VERIFY_CMD 0x2e + +/* + * Defines for the mode sense command + */ +#define MODE_SENSE_CURRENT 0x00 +#define MODE_SENSE_CHANGEABLE 0x01 +#define MODE_SENSE_DEFAULT 0x02 +#define MODE_SENSE_SAVED 0x03 + +/* + * IOCTLs used in low-level formatting. + */ + +#define IDEFLOPPY_IOCTL_FORMAT_SUPPORTED 0x4600 +#define IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY 0x4601 +#define IDEFLOPPY_IOCTL_FORMAT_START 0x4602 +#define IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS 0x4603 + +#if 0 +/* + * Special requests for our block device strategy routine. + */ +#define IDEFLOPPY_FIRST_RQ 90 + +/* + * IDEFLOPPY_PC_RQ is used to queue a packet command in the request queue. + */ +#define IDEFLOPPY_PC_RQ 90 + +#define IDEFLOPPY_LAST_RQ 90 + +/* + * A macro which can be used to check if a given request command + * originated in the driver or in the buffer cache layer. + */ +#define IDEFLOPPY_RQ_CMD(cmd) ((cmd >= IDEFLOPPY_FIRST_RQ) && (cmd <= IDEFLOPPY_LAST_RQ)) + +#endif + +/* + * Error codes which are returned in rq->errors to the higher part + * of the driver. + */ +#define IDEFLOPPY_ERROR_GENERAL 101 + +/* + * The ATAPI Status Register. + */ +typedef union { + unsigned all :8; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned check :1; /* Error occurred */ + unsigned idx :1; /* Reserved */ + unsigned corr :1; /* Correctable error occurred */ + unsigned drq :1; /* Data is request by the device */ + unsigned dsc :1; /* Media access command finished */ + unsigned reserved5 :1; /* Reserved */ + unsigned drdy :1; /* Ignored for ATAPI commands (ready to accept ATA command) */ + unsigned bsy :1; /* The device has access to the command block */ +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned bsy :1; /* The device has access to the command block */ + unsigned drdy :1; /* Ignored for ATAPI commands (ready to accept ATA command) */ + unsigned reserved5 :1; /* Reserved */ + unsigned dsc :1; /* Media access command finished */ + unsigned drq :1; /* Data is request by the device */ + unsigned corr :1; /* Correctable error occurred */ + unsigned idx :1; /* Reserved */ + unsigned check :1; /* Error occurred */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + } b; +} idefloppy_status_reg_t; + +/* + * The ATAPI error register. + */ +typedef union { + unsigned all :8; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned ili :1; /* Illegal Length Indication */ + unsigned eom :1; /* End Of Media Detected */ + unsigned abrt :1; /* Aborted command - As defined by ATA */ + unsigned mcr :1; /* Media Change Requested - As defined by ATA */ + unsigned sense_key :4; /* Sense key of the last failed packet command */ +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned sense_key :4; /* Sense key of the last failed packet command */ + unsigned mcr :1; /* Media Change Requested - As defined by ATA */ + unsigned abrt :1; /* Aborted command - As defined by ATA */ + unsigned eom :1; /* End Of Media Detected */ + unsigned ili :1; /* Illegal Length Indication */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + } b; +} idefloppy_error_reg_t; + +/* + * ATAPI Feature Register + */ +typedef union { + unsigned all :8; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned dma :1; /* Using DMA or PIO */ + unsigned reserved321 :3; /* Reserved */ + unsigned reserved654 :3; /* Reserved (Tag Type) */ + unsigned reserved7 :1; /* Reserved */ +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned reserved7 :1; /* Reserved */ + unsigned reserved654 :3; /* Reserved (Tag Type) */ + unsigned reserved321 :3; /* Reserved */ + unsigned dma :1; /* Using DMA or PIO */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + } b; +} idefloppy_feature_reg_t; + +/* + * ATAPI Byte Count Register. + */ +typedef union { + unsigned all :16; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned low :8; /* LSB */ + unsigned high :8; /* MSB */ +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned high :8; /* MSB */ + unsigned low :8; /* LSB */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + } b; +} idefloppy_bcount_reg_t; + +/* + * ATAPI Interrupt Reason Register. + */ +typedef union { + unsigned all :8; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned cod :1; /* Information transferred is command (1) or data (0) */ + unsigned io :1; /* The device requests us to read (1) or write (0) */ + unsigned reserved :6; /* Reserved */ +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned reserved :6; /* Reserved */ + unsigned io :1; /* The device requests us to read (1) or write (0) */ + unsigned cod :1; /* Information transferred is command (1) or data (0) */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + } b; +} idefloppy_ireason_reg_t; + +/* + * ATAPI floppy Drive Select Register + */ +typedef union { + unsigned all :8; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned sam_lun :3; /* Logical unit number */ + unsigned reserved3 :1; /* Reserved */ + unsigned drv :1; /* The responding drive will be drive 0 (0) or drive 1 (1) */ + unsigned one5 :1; /* Should be set to 1 */ + unsigned reserved6 :1; /* Reserved */ + unsigned one7 :1; /* Should be set to 1 */ +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned one7 :1; /* Should be set to 1 */ + unsigned reserved6 :1; /* Reserved */ + unsigned one5 :1; /* Should be set to 1 */ + unsigned drv :1; /* The responding drive will be drive 0 (0) or drive 1 (1) */ + unsigned reserved3 :1; /* Reserved */ + unsigned sam_lun :3; /* Logical unit number */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + } b; +} idefloppy_drivesel_reg_t; + +/* + * ATAPI Device Control Register + */ +typedef union { + unsigned all :8; + struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned zero0 :1; /* Should be set to zero */ + unsigned nien :1; /* Device interrupt is disabled (1) or enabled (0) */ + unsigned srst :1; /* ATA software reset. ATAPI devices should use the new ATAPI srst. */ + unsigned one3 :1; /* Should be set to 1 */ + unsigned reserved4567 :4; /* Reserved */ +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned reserved4567 :4; /* Reserved */ + unsigned one3 :1; /* Should be set to 1 */ + unsigned srst :1; /* ATA software reset. ATAPI devices should use the new ATAPI srst. */ + unsigned nien :1; /* Device interrupt is disabled (1) or enabled (0) */ + unsigned zero0 :1; /* Should be set to zero */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + } b; +} idefloppy_control_reg_t; + +/* + * The following is used to format the general configuration word of + * the ATAPI IDENTIFY DEVICE command. + */ +struct idefloppy_id_gcw { +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned packet_size :2; /* Packet Size */ + unsigned reserved234 :3; /* Reserved */ + unsigned drq_type :2; /* Command packet DRQ type */ + unsigned removable :1; /* Removable media */ + unsigned device_type :5; /* Device type */ + unsigned reserved13 :1; /* Reserved */ + unsigned protocol :2; /* Protocol type */ +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned protocol :2; /* Protocol type */ + unsigned reserved13 :1; /* Reserved */ + unsigned device_type :5; /* Device type */ + unsigned removable :1; /* Removable media */ + unsigned drq_type :2; /* Command packet DRQ type */ + unsigned reserved234 :3; /* Reserved */ + unsigned packet_size :2; /* Packet Size */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif +}; + +/* + * INQUIRY packet command - Data Format + */ +typedef struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned device_type :5; /* Peripheral Device Type */ + unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */ + unsigned reserved1_6t0 :7; /* Reserved */ + unsigned rmb :1; /* Removable Medium Bit */ + unsigned ansi_version :3; /* ANSI Version */ + unsigned ecma_version :3; /* ECMA Version */ + unsigned iso_version :2; /* ISO Version */ + unsigned response_format :4; /* Response Data Format */ + unsigned reserved3_45 :2; /* Reserved */ + unsigned reserved3_6 :1; /* TrmIOP - Reserved */ + unsigned reserved3_7 :1; /* AENC - Reserved */ +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */ + unsigned device_type :5; /* Peripheral Device Type */ + unsigned rmb :1; /* Removable Medium Bit */ + unsigned reserved1_6t0 :7; /* Reserved */ + unsigned iso_version :2; /* ISO Version */ + unsigned ecma_version :3; /* ECMA Version */ + unsigned ansi_version :3; /* ANSI Version */ + unsigned reserved3_7 :1; /* AENC - Reserved */ + unsigned reserved3_6 :1; /* TrmIOP - Reserved */ + unsigned reserved3_45 :2; /* Reserved */ + unsigned response_format :4; /* Response Data Format */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + u8 additional_length; /* Additional Length (total_length-4) */ + u8 rsv5, rsv6, rsv7; /* Reserved */ + u8 vendor_id[8]; /* Vendor Identification */ + u8 product_id[16]; /* Product Identification */ + u8 revision_level[4]; /* Revision Level */ + u8 vendor_specific[20]; /* Vendor Specific - Optional */ + u8 reserved56t95[40]; /* Reserved - Optional */ + /* Additional information may be returned */ +} idefloppy_inquiry_result_t; + +/* + * REQUEST SENSE packet command result - Data Format. + */ +typedef struct { +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned error_code :7; /* Current error (0x70) */ + unsigned valid :1; /* The information field conforms to SFF-8070i */ + u8 reserved1 :8; /* Reserved */ + unsigned sense_key :4; /* Sense Key */ + unsigned reserved2_4 :1; /* Reserved */ + unsigned ili :1; /* Incorrect Length Indicator */ + unsigned reserved2_67 :2; +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned valid :1; /* The information field conforms to SFF-8070i */ + unsigned error_code :7; /* Current error (0x70) */ + u8 reserved1 :8; /* Reserved */ + unsigned reserved2_67 :2; + unsigned ili :1; /* Incorrect Length Indicator */ + unsigned reserved2_4 :1; /* Reserved */ + unsigned sense_key :4; /* Sense Key */ +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + u32 information __attribute__ ((packed)); + u8 asl; /* Additional sense length (n-7) */ + u32 command_specific; /* Additional command specific information */ + u8 asc; /* Additional Sense Code */ + u8 ascq; /* Additional Sense Code Qualifier */ + u8 replaceable_unit_code; /* Field Replaceable Unit Code */ + u8 sksv[3]; + u8 pad[2]; /* Padding to 20 bytes */ +} idefloppy_request_sense_result_t; + +/* + * Pages of the SELECT SENSE / MODE SENSE packet commands. + */ +#define IDEFLOPPY_CAPABILITIES_PAGE 0x1b +#define IDEFLOPPY_FLEXIBLE_DISK_PAGE 0x05 + +/* + * Mode Parameter Header for the MODE SENSE packet command + */ +typedef struct { + u16 mode_data_length; /* Length of the following data transfer */ + u8 medium_type; /* Medium Type */ +#if defined(__LITTLE_ENDIAN_BITFIELD) + unsigned reserved3 :7; + unsigned wp :1; /* Write protect */ +#elif defined(__BIG_ENDIAN_BITFIELD) + unsigned wp :1; /* Write protect */ + unsigned reserved3 :7; +#else +#error "Bitfield endianness not defined! Check your byteorder.h" +#endif + u8 reserved[4]; +} idefloppy_mode_parameter_header_t; + +#define IDEFLOPPY_MIN(a,b) ((a)<(b) ? (a):(b)) +#define IDEFLOPPY_MAX(a,b) ((a)>(b) ? (a):(b)) + +/* + * Too bad. The drive wants to send us data which we are not ready to accept. + * Just throw it away. + */ +static void idefloppy_discard_data (ide_drive_t *drive, unsigned int bcount) +{ + while (bcount--) + IN_BYTE(IDE_DATA_REG); +} + +#if IDEFLOPPY_DEBUG_BUGS +static void idefloppy_write_zeros (ide_drive_t *drive, unsigned int bcount) +{ + while (bcount--) + OUT_BYTE(0, IDE_DATA_REG); +} +#endif /* IDEFLOPPY_DEBUG_BUGS */ + + +static int idefloppy_end_request (ide_drive_t *drive, int uptodate) +{ + struct request *rq; + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&ide_lock, flags); + rq = HWGROUP(drive)->rq; + + BUG_ON(!(rq->flags & REQ_STARTED)); + + /* + * decide whether to reenable DMA -- 3 is a random magic for now, + * if we DMA timeout more than 3 times, just stay in PIO + */ + if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) { + drive->state = 0; + HWGROUP(drive)->hwif->dmaproc(ide_dma_on, drive); + } + + if (!end_that_request_first(rq, uptodate, rq->hard_cur_sectors)) { + add_blkdev_randomness(major(rq->rq_dev)); + blkdev_dequeue_request(rq); + HWGROUP(drive)->rq = NULL; + end_that_request_last(rq); + ret = 0; + } + spin_unlock_irqrestore(&ide_lock, flags); + return ret; +} + +/* + * idefloppy_do_end_request is used to finish servicing a request. + * + * For read/write requests, we will call ide_end_request to pass to the + * next buffer. + */ +static int idefloppy_do_end_request (ide_drive_t *drive, int uptodate) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + struct request *rq = HWGROUP(drive)->rq; + int error; + +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "Reached idefloppy_end_request\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + switch (uptodate) { + case 0: error = IDEFLOPPY_ERROR_GENERAL; break; + case 1: error = 0; break; + default: error = uptodate; + } + if (error) + floppy->failed_pc = NULL; + /* Why does this happen? */ + if (!rq) + return 0; + if (!(rq->flags & REQ_SPECIAL)) { //if (!IDEFLOPPY_RQ_CMD (rq->cmd)) { + /* our real local end request function */ + idefloppy_end_request(drive, uptodate); + return 0; + } + rq->errors = error; + /* fixme: need to move this local also */ + ide_end_drive_cmd (drive, 0, 0); + return 0; +} + +static void idefloppy_input_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount) +{ + struct request *rq = pc->rq; + struct bio *bio = rq->bio; + int count; + + while (bcount) { + if (pc->b_count == bio->bi_size) { + rq->sector += rq->current_nr_sectors; + rq->nr_sectors -= rq->current_nr_sectors; + idefloppy_do_end_request(drive, 1); + if ((bio = rq->bio) != NULL) + pc->b_count = 0; + } + if (bio == NULL) { + printk (KERN_ERR "%s: bio == NULL in idefloppy_input_buffers, bcount == %d\n", drive->name, bcount); + idefloppy_discard_data(drive, bcount); + return; + } + count = IDEFLOPPY_MIN(bio->bi_size - pc->b_count, bcount); + atapi_input_bytes(drive, bio_data(bio) + pc->b_count, count); + bcount -= count; pc->b_count += count; + } +} + +static void idefloppy_output_buffers (ide_drive_t *drive, idefloppy_pc_t *pc, unsigned int bcount) +{ + struct request *rq = pc->rq; + struct bio *bio = rq->bio; + int count; + + while (bcount) { + if (!pc->b_count) { + rq->sector += rq->current_nr_sectors; + rq->nr_sectors -= rq->current_nr_sectors; + idefloppy_do_end_request(drive, 1); + if ((bio = rq->bio) != NULL) { + pc->b_data = bio_data(bio); + pc->b_count = bio->bi_size; + } + } + if (bio == NULL) { + printk (KERN_ERR "%s: bio == NULL in idefloppy_output_buffers, bcount == %d\n", drive->name, bcount); + idefloppy_write_zeros (drive, bcount); + return; + } + count = IDEFLOPPY_MIN (pc->b_count, bcount); + atapi_output_bytes (drive, pc->b_data, count); + bcount -= count; pc->b_data += count; pc->b_count -= count; + } +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static void idefloppy_update_buffers (ide_drive_t *drive, idefloppy_pc_t *pc) +{ + struct request *rq = pc->rq; + struct bio *bio = rq->bio; + + while ((bio = rq->bio) != NULL) + idefloppy_do_end_request(drive, 1); +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +/* + * idefloppy_queue_pc_head generates a new packet command request in front + * of the request queue, before the current request, so that it will be + * processed immediately, on the next pass through the driver. + */ +static void idefloppy_queue_pc_head (ide_drive_t *drive,idefloppy_pc_t *pc,struct request *rq) +{ + ide_init_drive_cmd (rq); + rq->buffer = (char *) pc; + rq->flags = REQ_SPECIAL; //rq->cmd = IDEFLOPPY_PC_RQ; + (void) ide_do_drive_cmd (drive, rq, ide_preempt); +} + +static idefloppy_pc_t *idefloppy_next_pc_storage (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + + if (floppy->pc_stack_index==IDEFLOPPY_PC_STACK) + floppy->pc_stack_index=0; + return (&floppy->pc_stack[floppy->pc_stack_index++]); +} + +static struct request *idefloppy_next_rq_storage (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + + if (floppy->rq_stack_index==IDEFLOPPY_PC_STACK) + floppy->rq_stack_index=0; + return (&floppy->rq_stack[floppy->rq_stack_index++]); +} + +/* + * idefloppy_analyze_error is called on each failed packet command retry + * to analyze the request sense. + */ +static void idefloppy_analyze_error (ide_drive_t *drive,idefloppy_request_sense_result_t *result) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + + floppy->sense_key = result->sense_key; floppy->asc = result->asc; floppy->ascq = result->ascq; + floppy->progress_indication= result->sksv[0] & 0x80 ? + (unsigned short)get_unaligned((u16 *)(result->sksv+1)):0x10000; +#if IDEFLOPPY_DEBUG_LOG + if (floppy->failed_pc) + printk (KERN_INFO "ide-floppy: pc = %x, sense key = %x, asc = %x, ascq = %x\n",floppy->failed_pc->c[0],result->sense_key,result->asc,result->ascq); + else + printk (KERN_INFO "ide-floppy: sense key = %x, asc = %x, ascq = %x\n",result->sense_key,result->asc,result->ascq); +#endif /* IDEFLOPPY_DEBUG_LOG */ +} + +static void idefloppy_request_sense_callback (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: Reached idefloppy_request_sense_callback\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + if (!floppy->pc->error) { + idefloppy_analyze_error (drive,(idefloppy_request_sense_result_t *) floppy->pc->buffer); + idefloppy_do_end_request(drive, 1); + } else { + printk (KERN_ERR "Error in REQUEST SENSE itself - Aborting request!\n"); + idefloppy_do_end_request(drive, 0); + } +} + +/* + * General packet command callback function. + */ +static void idefloppy_pc_callback (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_callback\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + idefloppy_do_end_request(drive, floppy->pc->error ? 0 : 1); +} + +/* + * idefloppy_init_pc initializes a packet command. + */ +static void idefloppy_init_pc (idefloppy_pc_t *pc) +{ + memset (pc->c, 0, 12); + pc->retries = 0; + pc->flags = 0; + pc->request_transfer = 0; + pc->buffer = pc->pc_buffer; + pc->buffer_size = IDEFLOPPY_PC_BUFFER_SIZE; + pc->b_data = NULL; +// pc->bio = NULL; + pc->callback = &idefloppy_pc_callback; +} + +static void idefloppy_create_request_sense_cmd (idefloppy_pc_t *pc) +{ + idefloppy_init_pc (pc); + pc->c[0] = IDEFLOPPY_REQUEST_SENSE_CMD; + pc->c[4] = 255; + pc->request_transfer = 18; + pc->callback = &idefloppy_request_sense_callback; +} + +/* + * idefloppy_retry_pc is called when an error was detected during the + * last packet command. We queue a request sense packet command in + * the head of the request list. + */ +static void idefloppy_retry_pc (ide_drive_t *drive) +{ + idefloppy_pc_t *pc; + struct request *rq; + idefloppy_error_reg_t error; + + error.all = IN_BYTE (IDE_ERROR_REG); + pc = idefloppy_next_pc_storage (drive); + rq = idefloppy_next_rq_storage (drive); + idefloppy_create_request_sense_cmd (pc); + idefloppy_queue_pc_head (drive, pc, rq); +} + +/* + * idefloppy_pc_intr is the usual interrupt handler which will be called + * during a packet command. + */ +static ide_startstop_t idefloppy_pc_intr (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_status_reg_t status; + idefloppy_bcount_reg_t bcount; + idefloppy_ireason_reg_t ireason; + idefloppy_pc_t *pc=floppy->pc; + struct request *rq = pc->rq; + unsigned int temp; + +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: Reached idefloppy_pc_intr interrupt handler\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (test_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { + if (HWIF(drive)->dmaproc(ide_dma_end, drive)) { + set_bit (PC_DMA_ERROR, &pc->flags); + } else { + pc->actually_transferred=pc->request_transfer; + idefloppy_update_buffers (drive, pc); + } +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: DMA finished\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + status.all = GET_STAT(); /* Clear the interrupt */ + + if (!status.b.drq) { /* No more interrupts */ +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred); +#endif /* IDEFLOPPY_DEBUG_LOG */ + clear_bit (PC_DMA_IN_PROGRESS, &pc->flags); + + local_irq_enable(); + + if (status.b.check || test_bit (PC_DMA_ERROR, &pc->flags)) { /* Error detected */ +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: %s: I/O error\n",drive->name); +#endif /* IDEFLOPPY_DEBUG_LOG */ + rq->errors++; + if (pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) { + printk (KERN_ERR "ide-floppy: I/O error in request sense command\n"); + return ide_do_reset (drive); + } + idefloppy_retry_pc (drive); /* Retry operation */ + return ide_stopped; /* queued, but not started */ + } + pc->error = 0; + if (floppy->failed_pc == pc) + floppy->failed_pc=NULL; + pc->callback(drive); /* Command finished - Call the callback function */ + return ide_stopped; + } +#ifdef CONFIG_BLK_DEV_IDEDMA + if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { + printk (KERN_ERR "ide-floppy: The floppy wants to issue more interrupts in DMA mode\n"); + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); + return ide_do_reset (drive); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + bcount.b.high=IN_BYTE (IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */ + bcount.b.low=IN_BYTE (IDE_BCOUNTL_REG); /* on this interrupt */ + ireason.all=IN_BYTE (IDE_IREASON_REG); + + if (ireason.b.cod) { + printk (KERN_ERR "ide-floppy: CoD != 0 in idefloppy_pc_intr\n"); + return ide_do_reset (drive); + } + if (ireason.b.io == test_bit (PC_WRITING, &pc->flags)) { /* Hopefully, we will never get here */ + printk (KERN_ERR "ide-floppy: We wanted to %s, ", ireason.b.io ? "Write":"Read"); + printk (KERN_ERR "but the floppy wants us to %s !\n",ireason.b.io ? "Read":"Write"); + return ide_do_reset (drive); + } + if (!test_bit (PC_WRITING, &pc->flags)) { /* Reading - Check that we have enough space */ + temp = pc->actually_transferred + bcount.all; + if ( temp > pc->request_transfer) { + if (temp > pc->buffer_size) { + printk (KERN_ERR "ide-floppy: The floppy wants to send us more data than expected - discarding data\n"); + idefloppy_discard_data (drive,bcount.all); + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL); + return ide_started; + } +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_NOTICE "ide-floppy: The floppy wants to send us more data than expected - allowing transfer\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + } + } + if (test_bit (PC_WRITING, &pc->flags)) { + if (pc->buffer != NULL) + atapi_output_bytes (drive,pc->current_position,bcount.all); /* Write the current buffer */ + else + idefloppy_output_buffers (drive, pc, bcount.all); + } else { + if (pc->buffer != NULL) + atapi_input_bytes (drive,pc->current_position,bcount.all); /* Read the current buffer */ + else + idefloppy_input_buffers (drive, pc, bcount.all); + } + pc->actually_transferred+=bcount.all; /* Update the current position */ + pc->current_position+=bcount.all; + + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL); /* And set the interrupt handler again */ + return ide_started; +} + +/* + * This is the original routine that did the packet transfer. + * It fails at high speeds on the Iomega ZIP drive, so there's a slower version + * for that drive below. The algorithm is chosen based on drive type + */ +static ide_startstop_t idefloppy_transfer_pc (ide_drive_t *drive) +{ + ide_startstop_t startstop; + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_ireason_reg_t ireason; + + if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + printk(KERN_ERR "ide-floppy: Strange, packet command " + "initiated yet DRQ isn't asserted\n"); + return startstop; + } + ireason.all = IN_BYTE(IDE_IREASON_REG); + if (!ireason.b.cod || ireason.b.io) { + printk(KERN_ERR "ide-floppy: (IO,CoD) != (0,1) while " + "issuing a packet command\n"); + return ide_do_reset (drive); + } + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, &idefloppy_pc_intr, IDEFLOPPY_WAIT_CMD, NULL); /* Set the interrupt routine */ + atapi_output_bytes (drive, floppy->pc->c, 12); /* Send the actual packet */ + return ide_started; +} + + +/* + * What we have here is a classic case of a top half / bottom half + * interrupt service routine. In interrupt mode, the device sends + * an interrupt to signal it's ready to receive a packet. However, + * we need to delay about 2-3 ticks before issuing the packet or we + * gets in trouble. + * + * So, follow carefully. transfer_pc1 is called as an interrupt (or + * directly). In either case, when the device says it's ready for a + * packet, we schedule the packet transfer to occur about 2-3 ticks + * later in transfer_pc2. + */ +static int idefloppy_transfer_pc2 (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + + atapi_output_bytes(drive, floppy->pc->c, 12); /* Send the actual packet */ + return IDEFLOPPY_WAIT_CMD; /* Timeout for the packet command */ +} + +static ide_startstop_t idefloppy_transfer_pc1 (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + ide_startstop_t startstop; + idefloppy_ireason_reg_t ireason; + + if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + printk(KERN_ERR "ide-floppy: Strange, packet command " + "initiated yet DRQ isn't asserted\n"); + return startstop; + } + ireason.all = IN_BYTE(IDE_IREASON_REG); + if (!ireason.b.cod || ireason.b.io) { + printk(KERN_ERR "ide-floppy: (IO,CoD) != (0,1) " + "while issuing a packet command\n"); + return ide_do_reset (drive); + } + /* + * The following delay solves a problem with ATAPI Zip 100 drives where the + * Busy flag was apparently being deasserted before the unit was ready to + * receive data. This was happening on a 1200 MHz Athlon system. 10/26/01 + * 25msec is too short, 40 and 50msec work well. idefloppy_pc_intr will + * not be actually used until after the packet is moved in about 50 msec. + */ + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, + &idefloppy_pc_intr, /* service routine for packet command */ + floppy->ticks, /* wait this long before "failing" */ + &idefloppy_transfer_pc2); /* fail == transfer_pc2 */ + return ide_started; +} + +/* + * Issue a packet command + */ +static ide_startstop_t idefloppy_issue_pc (ide_drive_t *drive, idefloppy_pc_t *pc) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_bcount_reg_t bcount; + int dma_ok = 0; + ide_handler_t *pkt_xfer_routine; + +#if IDEFLOPPY_DEBUG_BUGS + if (floppy->pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD && pc->c[0] == IDEFLOPPY_REQUEST_SENSE_CMD) { + printk (KERN_ERR "ide-floppy: possible ide-floppy.c bug - Two request sense in serial were issued\n"); + } +#endif /* IDEFLOPPY_DEBUG_BUGS */ + + if (floppy->failed_pc == NULL && pc->c[0] != IDEFLOPPY_REQUEST_SENSE_CMD) + floppy->failed_pc=pc; + floppy->pc=pc; /* Set the current packet command */ + + if (pc->retries > IDEFLOPPY_MAX_PC_RETRIES || test_bit (PC_ABORT, &pc->flags)) { + /* + * We will "abort" retrying a packet command in case + * a legitimate error code was received. + */ + if (!test_bit (PC_ABORT, &pc->flags)) { + if (!test_bit (PC_SUPPRESS_ERROR, &pc->flags)) { + ; + printk(KERN_ERR "ide-floppy: %s: I/O error, " + "pc = %2x, key = %2x, " + "asc = %2x, ascq = %2x\n", + drive->name, pc->c[0], + floppy->sense_key, + floppy->asc, floppy->ascq); + } + /* Giving up */ + pc->error = IDEFLOPPY_ERROR_GENERAL; + } + floppy->failed_pc=NULL; + pc->callback(drive); + return ide_stopped; + } +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "Retry number - %d\n",pc->retries); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + pc->retries++; + pc->actually_transferred=0; /* We haven't transferred any data yet */ + pc->current_position=pc->buffer; + bcount.all = min(pc->request_transfer, 63 * 1024); + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) { + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); + } + if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) + dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl,IDE_CONTROL_REG); + OUT_BYTE(dma_ok ? 1:0,IDE_FEATURE_REG); /* Use PIO/DMA */ + OUT_BYTE(bcount.b.high,IDE_BCOUNTH_REG); + OUT_BYTE(bcount.b.low,IDE_BCOUNTL_REG); + OUT_BYTE(drive->select.all,IDE_SELECT_REG); + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (dma_ok) { /* Begin DMA, if necessary */ + set_bit (PC_DMA_IN_PROGRESS, &pc->flags); + (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + /* Can we transfer the packet when we get the interrupt or wait? */ + if (test_bit (IDEFLOPPY_ZIP_DRIVE, &floppy->flags)) { + pkt_xfer_routine = &idefloppy_transfer_pc1; /* wait */ + } else { + pkt_xfer_routine = &idefloppy_transfer_pc; /* immediate */ + } + + if (test_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags)) { + if (HWGROUP(drive)->handler != NULL) + BUG(); + ide_set_handler(drive, pkt_xfer_routine, IDEFLOPPY_WAIT_CMD, NULL); + OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */ + return ide_started; + } else { + OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); + return (*pkt_xfer_routine) (drive); + } +} + +static void idefloppy_rw_callback (ide_drive_t *drive) +{ +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: Reached idefloppy_rw_callback\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + idefloppy_do_end_request(drive, 1); + return; +} + +static void idefloppy_create_prevent_cmd (idefloppy_pc_t *pc, int prevent) +{ +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "ide-floppy: creating prevent removal command, prevent = %d\n", prevent); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + idefloppy_init_pc (pc); + pc->c[0] = IDEFLOPPY_PREVENT_REMOVAL_CMD; + pc->c[4] = prevent; +} + +static void idefloppy_create_read_capacity_cmd (idefloppy_pc_t *pc) +{ + idefloppy_init_pc (pc); + pc->c[0] = IDEFLOPPY_READ_CAPACITY_CMD; + pc->c[7] = 255; + pc->c[8] = 255; + pc->request_transfer = 255; +} + +static void idefloppy_create_format_unit_cmd (idefloppy_pc_t *pc, int b, int l, + int flags) +{ + idefloppy_init_pc (pc); + pc->c[0] = IDEFLOPPY_FORMAT_UNIT_CMD; + pc->c[1] = 0x17; + + memset(pc->buffer, 0, 12); + pc->buffer[1] = 0xA2; + /* Default format list header, byte 1: FOV/DCRT/IMM bits set */ + + if (flags & 1) /* Verify bit on... */ + pc->buffer[1] ^= 0x20; /* ... turn off DCRT bit */ + pc->buffer[3] = 8; + + put_unaligned(htonl(b), (unsigned int *)(&pc->buffer[4])); + put_unaligned(htonl(l), (unsigned int *)(&pc->buffer[8])); + pc->buffer_size=12; + set_bit(PC_WRITING, &pc->flags); +} + +/* + * A mode sense command is used to "sense" floppy parameters. + */ +static void idefloppy_create_mode_sense_cmd (idefloppy_pc_t *pc, byte page_code, byte type) +{ + unsigned short length = sizeof (idefloppy_mode_parameter_header_t); + + idefloppy_init_pc (pc); + pc->c[0] = IDEFLOPPY_MODE_SENSE_CMD; + pc->c[1] = 0; + pc->c[2] = page_code + (type << 6); + + switch (page_code) { + case IDEFLOPPY_CAPABILITIES_PAGE: + length += 12; + break; + case IDEFLOPPY_FLEXIBLE_DISK_PAGE: + length += 32; + break; + default: + printk (KERN_ERR "ide-floppy: unsupported page code in create_mode_sense_cmd\n"); + } + put_unaligned (htons (length), (unsigned short *) &pc->c[7]); + pc->request_transfer = length; +} + +static void idefloppy_create_start_stop_cmd (idefloppy_pc_t *pc, int start) +{ + idefloppy_init_pc (pc); + pc->c[0] = IDEFLOPPY_START_STOP_CMD; + pc->c[4] = start; +} + +static void idefloppy_create_test_unit_ready_cmd(idefloppy_pc_t *pc) +{ + idefloppy_init_pc(pc); + pc->c[0] = IDEFLOPPY_TEST_UNIT_READY_CMD; +} + +static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t *pc, struct request *rq, unsigned long sector) +{ + int block = sector / floppy->bs_factor; + int blocks = rq->nr_sectors / floppy->bs_factor; + int cmd = rq_data_dir(rq); + +#if IDEFLOPPY_DEBUG_LOG + printk ("create_rw1%d_cmd: block == %d, blocks == %d\n", + 2 * test_bit (IDEFLOPPY_USE_READ12, &floppy->flags), block, blocks); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + idefloppy_init_pc (pc); + if (test_bit (IDEFLOPPY_USE_READ12, &floppy->flags)) { + pc->c[0] = cmd == READ ? IDEFLOPPY_READ12_CMD : IDEFLOPPY_WRITE12_CMD; + put_unaligned(htonl (blocks), (unsigned int *) &pc->c[6]); + } else { + pc->c[0] = cmd == READ ? IDEFLOPPY_READ10_CMD : IDEFLOPPY_WRITE10_CMD; + put_unaligned(htons (blocks), (unsigned short *) &pc->c[7]); + } + put_unaligned(htonl (block), (unsigned int *) &pc->c[2]); + pc->callback = &idefloppy_rw_callback; + pc->rq = rq; + pc->b_data = rq->buffer; + pc->b_count = cmd == READ ? 0 : rq->bio->bi_size; + if (rq->flags & REQ_RW) + set_bit (PC_WRITING, &pc->flags); + pc->buffer = NULL; + pc->request_transfer = pc->buffer_size = blocks * floppy->block_size; + set_bit (PC_DMA_RECOMMENDED, &pc->flags); +} + +/* + * idefloppy_do_request is our request handling function. + */ +static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_pc_t *pc; + +#if IDEFLOPPY_DEBUG_LOG + printk(KERN_INFO "rq_status: %d, rq_dev: %u, flags: %lx, errors: %d\n", + rq->rq_status, (unsigned int) rq->rq_dev, + rq->flags, rq->errors); + printk(KERN_INFO "sector: %ld, nr_sectors: %ld, " + "current_nr_sectors: %ld\n", rq->sector, + rq->nr_sectors, rq->current_nr_sectors); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + if (rq->errors >= ERROR_MAX) { + if (floppy->failed_pc != NULL) + printk(KERN_ERR "ide-floppy: %s: I/O error, pc = %2x," + " key = %2x, asc = %2x, ascq = %2x\n", + drive->name, floppy->failed_pc->c[0], + floppy->sense_key, floppy->asc, floppy->ascq); + else + printk(KERN_ERR "ide-floppy: %s: I/O error\n", + drive->name); + idefloppy_do_end_request(drive, 0); + return ide_stopped; + } + if (rq->flags & REQ_CMD) { + if ((rq->sector % floppy->bs_factor) || + (rq->nr_sectors % floppy->bs_factor)) { + printk("%s: unsupported r/w request size\n", + drive->name); + idefloppy_do_end_request(drive, 0); + return ide_stopped; + } + pc = idefloppy_next_pc_storage (drive); + idefloppy_create_rw_cmd (floppy, pc, rq, block); + } else if (rq->flags & REQ_SPECIAL) { + pc = (idefloppy_pc_t *) rq->buffer; + } else { + blk_dump_rq_flags(rq, + "ide-floppy: unsupported command in queue"); + idefloppy_do_end_request(drive, 0); + return ide_stopped; + } + + pc->rq = rq; + return idefloppy_issue_pc(drive, pc); +} + +/* + * idefloppy_queue_pc_tail adds a special packet command request to the + * tail of the request queue, and waits for it to be serviced. + */ +static int idefloppy_queue_pc_tail (ide_drive_t *drive,idefloppy_pc_t *pc) +{ + struct request rq; + + ide_init_drive_cmd (&rq); + rq.buffer = (char *) pc; + rq.flags = REQ_SPECIAL; // rq.cmd = IDEFLOPPY_PC_RQ; + + return ide_do_drive_cmd (drive, &rq, ide_wait); +} + +/* + * Look at the flexible disk page parameters. We will ignore the CHS + * capacity parameters and use the LBA parameters instead. + */ +static int idefloppy_get_flexible_disk_page (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_pc_t pc; + idefloppy_mode_parameter_header_t *header; + idefloppy_flexible_disk_page_t *page; + int capacity, lba_capacity; + + idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_FLEXIBLE_DISK_PAGE, MODE_SENSE_CURRENT); + if (idefloppy_queue_pc_tail (drive,&pc)) { + printk (KERN_ERR "ide-floppy: Can't get flexible disk page parameters\n"); + return 1; + } + header = (idefloppy_mode_parameter_header_t *) pc.buffer; + floppy->wp = header->wp; + page = (idefloppy_flexible_disk_page_t *) (header + 1); + + page->transfer_rate = ntohs (page->transfer_rate); + page->sector_size = ntohs (page->sector_size); + page->cyls = ntohs (page->cyls); + page->rpm = ntohs (page->rpm); + capacity = page->cyls * page->heads * page->sectors * page->sector_size; + if (memcmp (page, &floppy->flexible_disk_page, sizeof (idefloppy_flexible_disk_page_t))) + printk(KERN_INFO "%s: %dkB, %d/%d/%d CHS, %d kBps, " + "%d sector size, %d rpm\n", + drive->name, capacity / 1024, page->cyls, + page->heads, page->sectors, + page->transfer_rate / 8, page->sector_size, page->rpm); + + floppy->flexible_disk_page = *page; + drive->bios_cyl = page->cyls; + drive->bios_head = page->heads; + drive->bios_sect = page->sectors; + lba_capacity = floppy->blocks * floppy->block_size; + if (capacity < lba_capacity) { + printk(KERN_NOTICE "%s: The disk reports a capacity of %d " + "bytes, but the drive only handles %d\n", + drive->name, lba_capacity, capacity); + floppy->blocks = floppy->block_size ? capacity / floppy->block_size : 0; + } + return 0; +} + +static int idefloppy_get_capability_page(ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_pc_t pc; + idefloppy_mode_parameter_header_t *header; + idefloppy_capabilities_page_t *page; + + floppy->srfp=0; + idefloppy_create_mode_sense_cmd (&pc, IDEFLOPPY_CAPABILITIES_PAGE, + MODE_SENSE_CURRENT); + + set_bit(PC_SUPPRESS_ERROR, &pc.flags); + if (idefloppy_queue_pc_tail (drive,&pc)) { + return 1; + } + + header = (idefloppy_mode_parameter_header_t *) pc.buffer; + page= (idefloppy_capabilities_page_t *)(header+1); + floppy->srfp=page->srfp; + return (0); +} + +/* + * Determine if a media is present in the floppy drive, and if so, + * its LBA capacity. + */ +static int idefloppy_get_capacity (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_pc_t pc; + idefloppy_capacity_header_t *header; + idefloppy_capacity_descriptor_t *descriptor; + int i, descriptors, rc = 1, blocks, length; + + drive->bios_cyl = 0; + drive->bios_head = drive->bios_sect = 0; + floppy->blocks = floppy->bs_factor = 0; + drive->part[0].nr_sects = 0; + + idefloppy_create_read_capacity_cmd (&pc); + if (idefloppy_queue_pc_tail (drive, &pc)) { + printk (KERN_ERR "ide-floppy: Can't get floppy parameters\n"); + return 1; + } + header = (idefloppy_capacity_header_t *) pc.buffer; + descriptors = header->length / sizeof (idefloppy_capacity_descriptor_t); + descriptor = (idefloppy_capacity_descriptor_t *) (header + 1); + + for (i = 0; i < descriptors; i++, descriptor++) { + blocks = descriptor->blocks = ntohl (descriptor->blocks); + length = descriptor->length = ntohs (descriptor->length); + + if (!i) + { + switch (descriptor->dc) { + case CAPACITY_UNFORMATTED: /* Clik! drive returns this instead of CAPACITY_CURRENT */ + if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)) + break; /* If it is not a clik drive, break out (maintains previous driver behaviour) */ + case CAPACITY_CURRENT: /* Normal Zip/LS-120 disks */ + if (memcmp (descriptor, &floppy->capacity, sizeof (idefloppy_capacity_descriptor_t))) + printk (KERN_INFO "%s: %dkB, %d blocks, %d sector size\n", drive->name, blocks * length / 1024, blocks, length); + floppy->capacity = *descriptor; + if (!length || length % 512) + printk (KERN_NOTICE "%s: %d bytes block size not supported\n", drive->name, length); + else { + floppy->blocks = blocks; + floppy->block_size = length; + if ((floppy->bs_factor = length / 512) != 1) + printk (KERN_NOTICE "%s: warning: non 512 bytes block size not fully supported\n", drive->name); + rc = 0; + } + break; + case CAPACITY_NO_CARTRIDGE: + /* This is a KERN_ERR so it appears on screen for the user to see */ + printk (KERN_ERR "%s: No disk in drive\n", drive->name); + break; + case CAPACITY_INVALID: + printk (KERN_ERR "%s: Invalid capacity for disk in drive\n", drive->name); + break; + } + } + if (!i) { + IDEFLOPPY_DEBUG( "Descriptor 0 Code: %d\n", descriptor->dc); + } + IDEFLOPPY_DEBUG( "Descriptor %d: %dkB, %d blocks, %d sector size\n", i, blocks * length / 1024, blocks, length); + } + + /* Clik! disk does not support get_flexible_disk_page */ + if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)) { + (void) idefloppy_get_flexible_disk_page (drive); + } + + drive->part[0].nr_sects = floppy->blocks * floppy->bs_factor; + return rc; +} + +/* +** Obtain the list of formattable capacities. +** Very similar to idefloppy_get_capacity, except that we push the capacity +** descriptors to userland, instead of our own structures. +** +** Userland gives us the following structure: +** +** struct idefloppy_format_capacities { +** int nformats; +** struct { +** int nblocks; +** int blocksize; +** } formats[]; +** } ; +** +** userland initializes nformats to the number of allocated formats[] +** records. On exit we set nformats to the number of records we've +** actually initialized. +** +*/ + +static int idefloppy_get_format_capacities (ide_drive_t *drive, + struct inode *inode, + struct file *file, + int *arg) /* Cheater */ +{ + idefloppy_pc_t pc; + idefloppy_capacity_header_t *header; + idefloppy_capacity_descriptor_t *descriptor; + int i, descriptors, blocks, length; + int u_array_size; + int u_index; + int *argp; + + if (get_user(u_array_size, arg)) + return (-EFAULT); + + if (u_array_size <= 0) + return (-EINVAL); + + idefloppy_create_read_capacity_cmd (&pc); + if (idefloppy_queue_pc_tail (drive, &pc)) { + printk (KERN_ERR "ide-floppy: Can't get floppy parameters\n"); + return (-EIO); + } + header = (idefloppy_capacity_header_t *) pc.buffer; + descriptors = header->length / + sizeof (idefloppy_capacity_descriptor_t); + descriptor = (idefloppy_capacity_descriptor_t *) (header + 1); + + u_index=0; + argp=arg+1; + + /* + ** We always skip the first capacity descriptor. That's the + ** current capacity. We are interested in the remaining descriptors, + ** the formattable capacities. + */ + + for (i=0; i= u_array_size) + break; /* User-supplied buffer too small */ + if (i == 0) + continue; /* Skip the first descriptor */ + + blocks = ntohl (descriptor->blocks); + length = ntohs (descriptor->length); + + if (put_user(blocks, argp)) + return (-EFAULT); + ++argp; + + if (put_user(length, argp)) + return (-EFAULT); + ++argp; + + ++u_index; + } + + if (put_user(u_index, arg)) + return (-EFAULT); + return (0); +} + +/* +** Send ATAPI_FORMAT_UNIT to the drive. +** +** Userland gives us the following structure: +** +** struct idefloppy_format_command { +** int nblocks; +** int blocksize; +** int flags; +** } ; +** +** flags is a bitmask, currently, the only defined flag is: +** +** 0x01 - verify media after format. +*/ + +static int idefloppy_begin_format(ide_drive_t *drive, + struct inode *inode, + struct file *file, + int *arg) +{ + int blocks; + int length; + int flags; + idefloppy_pc_t pc; + + if (get_user(blocks, arg) + || get_user(length, arg+1) + || get_user(flags, arg+2)) + { + return (-EFAULT); + } + + (void) idefloppy_get_capability_page (drive); /* Get the SFRP bit */ + idefloppy_create_format_unit_cmd(&pc, blocks, length, flags); + if (idefloppy_queue_pc_tail (drive, &pc)) + { + return (-EIO); + } + return (0); +} + +/* +** Get ATAPI_FORMAT_UNIT progress indication. +** +** Userland gives a pointer to an int. The int is set to a progresss +** indicator 0-65536, with 65536=100%. +** +** If the drive does not support format progress indication, we just check +** the dsc bit, and return either 0 or 65536. +*/ + +static int idefloppy_get_format_progress(ide_drive_t *drive, + struct inode *inode, + struct file *file, + int *arg) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_pc_t pc; + int progress_indication=0x10000; + + if (floppy->srfp) + { + idefloppy_create_request_sense_cmd(&pc); + if (idefloppy_queue_pc_tail (drive, &pc)) + { + return (-EIO); + } + + if (floppy->sense_key == 2 && floppy->asc == 4 && + floppy->ascq == 4) + { + progress_indication=floppy->progress_indication; + } + /* Else assume format_unit has finished, and we're + ** at 0x10000 */ + } + else + { + idefloppy_status_reg_t status; + unsigned long flags; + + local_irq_save(flags); + status.all=GET_STAT(); + local_irq_restore(flags); + + progress_indication= !status.b.dsc ? 0:0x10000; + } + if (put_user(progress_indication, arg)) + return (-EFAULT); + + return (0); +} + +/* + * Our special ide-floppy ioctl's. + * + * Currently there aren't any ioctl's. + */ +static int idefloppy_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + idefloppy_pc_t pc; + idefloppy_floppy_t *floppy = drive->driver_data; + int prevent = (arg) ? 1 : 0; + + switch (cmd) { + case CDROMEJECT: + prevent = 0; + /* fall through */ + case CDROM_LOCKDOOR: + if (drive->usage > 1) + return -EBUSY; + + /* The IOMEGA Clik! Drive doesn't support this command - no room for an eject mechanism */ + if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)) { + idefloppy_create_prevent_cmd (&pc, prevent); + (void) idefloppy_queue_pc_tail (drive, &pc); + } + if (cmd == CDROMEJECT) { + idefloppy_create_start_stop_cmd (&pc, 2); + (void) idefloppy_queue_pc_tail (drive, &pc); + } + return 0; + case IDEFLOPPY_IOCTL_FORMAT_SUPPORTED: + return (0); + case IDEFLOPPY_IOCTL_FORMAT_GET_CAPACITY: + return (idefloppy_get_format_capacities(drive, inode, file, + (int *)arg)); + case IDEFLOPPY_IOCTL_FORMAT_START: + + if (!(file->f_mode & 2)) + return (-EPERM); + + { + idefloppy_floppy_t *floppy = drive->driver_data; + + if (drive->usage > 1) + { + /* Don't format if someone is using the disk */ + + clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, + &floppy->flags); + return -EBUSY; + } + else + { + int rc; + + set_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, + &floppy->flags); + + rc=idefloppy_begin_format(drive, inode, + file, + (int *)arg); + + if (rc) + clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, + &floppy->flags); + return (rc); + + /* + ** Note, the bit will be cleared when the device is + ** closed. This is the cleanest way to handle the + ** situation where the drive does not support + ** format progress reporting. + */ + } + } + case IDEFLOPPY_IOCTL_FORMAT_GET_PROGRESS: + return (idefloppy_get_format_progress(drive, inode, file, + (int *)arg)); + } + return -EIO; +} + +/* + * Our open/release functions + */ +static int idefloppy_open (struct inode *inode, struct file *filp, ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_pc_t pc; + +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "Reached idefloppy_open\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + MOD_INC_USE_COUNT; + if (drive->usage == 1) { + clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags); + /* Just in case */ + + idefloppy_create_test_unit_ready_cmd(&pc); + if (idefloppy_queue_pc_tail(drive, &pc)) { + idefloppy_create_start_stop_cmd (&pc, 1); + (void) idefloppy_queue_pc_tail (drive, &pc); + } + + if (idefloppy_get_capacity (drive) + && (filp->f_flags & O_NDELAY) == 0 + /* + ** Allow O_NDELAY to open a drive without a disk, or with + ** an unreadable disk, so that we can get the format + ** capacity of the drive or begin the format - Sam + */ + ) { + drive->usage--; + MOD_DEC_USE_COUNT; + return -EIO; + } + + if (floppy->wp && (filp->f_mode & 2)) { + drive->usage--; + MOD_DEC_USE_COUNT; + return -EROFS; + } + set_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags); + /* IOMEGA Clik! drives do not support lock/unlock commands */ + if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)) { + idefloppy_create_prevent_cmd (&pc, 1); + (void) idefloppy_queue_pc_tail (drive, &pc); + } + check_disk_change(inode->i_bdev); + } + else if (test_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags)) + { + drive->usage--; + MOD_DEC_USE_COUNT; + return -EBUSY; + } + return 0; +} + +static void idefloppy_release (struct inode *inode, struct file *filp, ide_drive_t *drive) +{ + idefloppy_pc_t pc; + +#if IDEFLOPPY_DEBUG_LOG + printk (KERN_INFO "Reached idefloppy_release\n"); +#endif /* IDEFLOPPY_DEBUG_LOG */ + + if (!drive->usage) { + idefloppy_floppy_t *floppy = drive->driver_data; + + /* IOMEGA Clik! drives do not support lock/unlock commands */ + if (!test_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags)) { + idefloppy_create_prevent_cmd (&pc, 0); + (void) idefloppy_queue_pc_tail (drive, &pc); + } + + clear_bit(IDEFLOPPY_FORMAT_IN_PROGRESS, &floppy->flags); + } + MOD_DEC_USE_COUNT; +} + +/* + * Check media change. Use a simple algorithm for now. + */ +static int idefloppy_media_change (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + + return test_and_clear_bit (IDEFLOPPY_MEDIA_CHANGED, &floppy->flags); +} + +/* + * Revalidate the new media. Should set blk_size[] + */ +static void idefloppy_revalidate (ide_drive_t *drive) +{ + ide_revalidate_drive(drive); +} + +/* + * Return the current floppy capacity to ide.c. + */ +static unsigned long idefloppy_capacity (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + unsigned long capacity = floppy->blocks * floppy->bs_factor; + + return capacity; +} + +/* + * idefloppy_identify_device checks if we can support a drive, + * based on the ATAPI IDENTIFY command results. + */ +static int idefloppy_identify_device (ide_drive_t *drive,struct hd_driveid *id) +{ + struct idefloppy_id_gcw gcw; +#if IDEFLOPPY_DEBUG_INFO + unsigned short mask,i; + char buffer[80]; +#endif /* IDEFLOPPY_DEBUG_INFO */ + + *((unsigned short *) &gcw) = id->config; + +#ifdef CONFIG_PPC + /* kludge for Apple PowerBook internal zip */ + if ((gcw.device_type == 5) && !strstr(id->model, "CD-ROM") + && strstr(id->model, "ZIP")) + gcw.device_type = 0; +#endif + +#if IDEFLOPPY_DEBUG_INFO + printk (KERN_INFO "Dumping ATAPI Identify Device floppy parameters\n"); + switch (gcw.protocol) { + case 0: case 1: sprintf (buffer, "ATA");break; + case 2: sprintf (buffer, "ATAPI");break; + case 3: sprintf (buffer, "Reserved (Unknown to ide-floppy)");break; + } + printk (KERN_INFO "Protocol Type: %s\n", buffer); + switch (gcw.device_type) { + case 0: sprintf (buffer, "Direct-access Device");break; + case 1: sprintf (buffer, "Streaming Tape Device");break; + case 2: case 3: case 4: sprintf (buffer, "Reserved");break; + case 5: sprintf (buffer, "CD-ROM Device");break; + case 6: sprintf (buffer, "Reserved"); + case 7: sprintf (buffer, "Optical memory Device");break; + case 0x1f: sprintf (buffer, "Unknown or no Device type");break; + default: sprintf (buffer, "Reserved"); + } + printk (KERN_INFO "Device Type: %x - %s\n", gcw.device_type, buffer); + printk (KERN_INFO "Removable: %s\n",gcw.removable ? "Yes":"No"); + switch (gcw.drq_type) { + case 0: sprintf (buffer, "Microprocessor DRQ");break; + case 1: sprintf (buffer, "Interrupt DRQ");break; + case 2: sprintf (buffer, "Accelerated DRQ");break; + case 3: sprintf (buffer, "Reserved");break; + } + printk (KERN_INFO "Command Packet DRQ Type: %s\n", buffer); + switch (gcw.packet_size) { + case 0: sprintf (buffer, "12 bytes");break; + case 1: sprintf (buffer, "16 bytes");break; + default: sprintf (buffer, "Reserved");break; + } + printk (KERN_INFO "Command Packet Size: %s\n", buffer); + printk (KERN_INFO "Model: %.40s\n",id->model); + printk (KERN_INFO "Firmware Revision: %.8s\n",id->fw_rev); + printk (KERN_INFO "Serial Number: %.20s\n",id->serial_no); + printk (KERN_INFO "Write buffer size(?): %d bytes\n",id->buf_size*512); + printk (KERN_INFO "DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n"); + printk (KERN_INFO "LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n"); + printk (KERN_INFO "IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n"); + printk (KERN_INFO "IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n"); + printk (KERN_INFO "ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n"); + printk (KERN_INFO "PIO Cycle Timing Category: %d\n",id->tPIO); + printk (KERN_INFO "DMA Cycle Timing Category: %d\n",id->tDMA); + printk (KERN_INFO "Single Word DMA supported modes:\n"); + for (i=0,mask=1;i<8;i++,mask=mask << 1) { + if (id->dma_1word & mask) + printk (KERN_INFO " Mode %d%s\n", i, (id->dma_1word & (mask << 8)) ? " (active)" : ""); + } + printk (KERN_INFO "Multi Word DMA supported modes:\n"); + for (i=0,mask=1;i<8;i++,mask=mask << 1) { + if (id->dma_mword & mask) + printk (KERN_INFO " Mode %d%s\n", i, (id->dma_mword & (mask << 8)) ? " (active)" : ""); + } + if (id->field_valid & 0x0002) { + printk (KERN_INFO "Enhanced PIO Modes: %s\n",id->eide_pio_modes & 1 ? "Mode 3":"None"); + if (id->eide_dma_min == 0) + sprintf (buffer, "Not supported"); + else + sprintf (buffer, "%d ns",id->eide_dma_min); + printk (KERN_INFO "Minimum Multi-word DMA cycle per word: %s\n", buffer); + if (id->eide_dma_time == 0) + sprintf (buffer, "Not supported"); + else + sprintf (buffer, "%d ns",id->eide_dma_time); + printk (KERN_INFO "Manufacturer\'s Recommended Multi-word cycle: %s\n", buffer); + if (id->eide_pio == 0) + sprintf (buffer, "Not supported"); + else + sprintf (buffer, "%d ns",id->eide_pio); + printk (KERN_INFO "Minimum PIO cycle without IORDY: %s\n", buffer); + if (id->eide_pio_iordy == 0) + sprintf (buffer, "Not supported"); + else + sprintf (buffer, "%d ns",id->eide_pio_iordy); + printk (KERN_INFO "Minimum PIO cycle with IORDY: %s\n", buffer); + } else + printk (KERN_INFO "According to the device, fields 64-70 are not valid.\n"); +#endif /* IDEFLOPPY_DEBUG_INFO */ + + if (gcw.protocol != 2) + printk (KERN_ERR "ide-floppy: Protocol is not ATAPI\n"); + else if (gcw.device_type != 0) + printk (KERN_ERR "ide-floppy: Device type is not set to floppy\n"); + else if (!gcw.removable) + printk (KERN_ERR "ide-floppy: The removable flag is not set\n"); + else if (gcw.drq_type == 3) { + printk (KERN_ERR "ide-floppy: Sorry, DRQ type %d not supported\n", gcw.drq_type); + } else if (gcw.packet_size != 0) { + printk (KERN_ERR "ide-floppy: Packet size is not 12 bytes long\n"); + } else + return 1; + return 0; +} + +static void idefloppy_add_settings(ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + +/* + * drive setting name read/write ioctl ioctl data type min max mul_factor div_factor data pointer set function + */ + ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 1023, 1, 1, &drive->bios_cyl, NULL); + ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); + ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL); + ide_add_setting(drive, "ticks", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &floppy->ticks, NULL); + +} + +/* + * Driver initialization. + */ +static void idefloppy_setup (ide_drive_t *drive, idefloppy_floppy_t *floppy) +{ + struct idefloppy_id_gcw gcw; + int i; + + *((unsigned short *) &gcw) = drive->id->config; + drive->driver_data = floppy; + drive->ready_stat = 0; + memset (floppy, 0, sizeof (idefloppy_floppy_t)); + floppy->drive = drive; + floppy->pc = floppy->pc_stack; + if (gcw.drq_type == 1) + set_bit (IDEFLOPPY_DRQ_INTERRUPT, &floppy->flags); + /* + * We used to check revisions here. At this point however + * I'm giving up. Just assume they are all broken, its easier. + * + * The actual reason for the workarounds was likely + * a driver bug after all rather than a firmware bug, + * and the workaround below used to hide it. It should + * be fixed as of version 1.9, but to be on the safe side + * we'll leave the limitation below for the 2.2.x tree. + */ + + if (strcmp(drive->id->model, "IOMEGA ZIP 100 ATAPI") == 0) + { + set_bit(IDEFLOPPY_ZIP_DRIVE, &floppy->flags); + /* This value will be visible in the /proc/ide/hdx/settings */ + floppy->ticks = IDEFLOPPY_TICKS_DELAY; + blk_queue_max_sectors(&drive->queue, 64); + } + + /* + * Guess what? The IOMEGA Clik! drive also needs the + * above fix. It makes nasty clicking noises without + * it, so please don't remove this. + */ + if (strncmp(drive->id->model, "IOMEGA Clik!", 11) == 0) + { + blk_queue_max_sectors(&drive->queue, 64); + set_bit(IDEFLOPPY_CLIK_DRIVE, &floppy->flags); + } + + + (void) idefloppy_get_capacity (drive); + idefloppy_add_settings(drive); + for (i = 0; i < MAX_DRIVES; ++i) { + ide_hwif_t *hwif = HWIF(drive); + + if (drive != &hwif->drives[i]) continue; + hwif->gd[i]->de_arr[i] = drive->de; + if (drive->removable) + hwif->gd[i]->flags[i] |= GENHD_FL_REMOVABLE; + break; + } +} + +static int idefloppy_cleanup (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy = drive->driver_data; + + if (ide_unregister_subdriver (drive)) + return 1; + drive->driver_data = NULL; + kfree (floppy); + return 0; +} + +#ifdef CONFIG_PROC_FS + +static ide_proc_entry_t idefloppy_proc[] = { + { "geometry", S_IFREG|S_IRUGO, proc_ide_read_geometry, NULL }, + { NULL, 0, NULL, NULL } +}; + +#else + +#define idefloppy_proc NULL + +#endif /* CONFIG_PROC_FS */ + +int idefloppy_init (void); +int idefloppy_reinit(ide_drive_t *drive); + +/* + * IDE subdriver functions, registered with ide.c + */ +static ide_driver_t idefloppy_driver = { + name: "ide-floppy", + version: IDEFLOPPY_VERSION, + media: ide_floppy, + busy: 0, +#ifdef CONFIG_IDEDMA_ONLYDISK + supports_dma: 0, +#else + supports_dma: 1, +#endif + supports_dsc_overlap: 0, + cleanup: idefloppy_cleanup, + standby: NULL, + suspend: NULL, + resume: NULL, + flushcache: NULL, + do_request: idefloppy_do_request, + end_request: idefloppy_do_end_request, + sense: NULL, + error: NULL, + ioctl: idefloppy_ioctl, + open: idefloppy_open, + release: idefloppy_release, + media_change: idefloppy_media_change, + revalidate: idefloppy_revalidate, + pre_reset: NULL, + capacity: idefloppy_capacity, + special: NULL, + proc: idefloppy_proc, + init: idefloppy_init, + reinit: idefloppy_reinit, + ata_prebuilder: NULL, + atapi_prebuilder: NULL, +}; + +static ide_module_t idefloppy_module = { + IDE_DRIVER_MODULE, + idefloppy_init, + &idefloppy_driver, + NULL +}; + +int idefloppy_reinit (ide_drive_t *drive) +{ + idefloppy_floppy_t *floppy; + int failed = 0; + + MOD_INC_USE_COUNT; + while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, NULL, failed++)) != NULL) { + if (!idefloppy_identify_device (drive, drive->id)) { + printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name); + continue; + } + if (drive->scsi) { + printk("ide-floppy: passing drive %s to ide-scsi emulation.\n", drive->name); + continue; + } + if ((floppy = (idefloppy_floppy_t *) kmalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL) { + printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name); + continue; + } + if (ide_register_subdriver (drive, &idefloppy_driver, IDE_SUBDRIVER_VERSION)) { + printk (KERN_ERR "ide-floppy: %s: Failed to register the driver with ide.c\n", drive->name); + kfree (floppy); + continue; + } + DRIVER(drive)->busy++; + idefloppy_setup (drive, floppy); + DRIVER(drive)->busy--; + failed--; + } + ide_register_module(&idefloppy_module); + MOD_DEC_USE_COUNT; + return 0; +} + +MODULE_DESCRIPTION("ATAPI FLOPPY Driver"); + +static void __exit idefloppy_exit (void) +{ + ide_drive_t *drive; + int failed = 0; + + while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, &idefloppy_driver, failed)) != NULL) { + if (idefloppy_cleanup (drive)) { + printk ("%s: cleanup_module() called while still busy\n", drive->name); + failed++; + } + /* We must remove proc entries defined in this module. + Otherwise we oops while accessing these entries */ +#ifdef CONFIG_PROC_FS + if (drive->proc) + ide_remove_proc_entries(drive->proc, idefloppy_proc); +#endif + } + ide_unregister_module(&idefloppy_module); +} + +/* + * idefloppy_init will register the driver for each floppy. + */ +int idefloppy_init (void) +{ + ide_drive_t *drive; + idefloppy_floppy_t *floppy; + int failed = 0; + + printk("ide-floppy driver " IDEFLOPPY_VERSION "\n"); + MOD_INC_USE_COUNT; + while ((drive = ide_scan_devices (ide_floppy, idefloppy_driver.name, NULL, failed++)) != NULL) { + if (!idefloppy_identify_device (drive, drive->id)) { + printk (KERN_ERR "ide-floppy: %s: not supported by this version of ide-floppy\n", drive->name); + continue; + } + if (drive->scsi) { + printk("ide-floppy: passing drive %s to ide-scsi emulation.\n", drive->name); + continue; + } + if ((floppy = (idefloppy_floppy_t *) kmalloc (sizeof (idefloppy_floppy_t), GFP_KERNEL)) == NULL) { + printk (KERN_ERR "ide-floppy: %s: Can't allocate a floppy structure\n", drive->name); + continue; + } + if (ide_register_subdriver (drive, &idefloppy_driver, IDE_SUBDRIVER_VERSION)) { + printk (KERN_ERR "ide-floppy: %s: Failed to register the driver with ide.c\n", drive->name); + kfree (floppy); + continue; + } + DRIVER(drive)->busy++; + idefloppy_setup (drive, floppy); + DRIVER(drive)->busy--; + failed--; + } + ide_register_module(&idefloppy_module); + MOD_DEC_USE_COUNT; + return 0; +} + +module_init(idefloppy_init); +module_exit(idefloppy_exit); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/ide/ide-geometry.c b/drivers/ide/ide-geometry.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-geometry.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,224 @@ +/* + * linux/drivers/ide/ide-geometry.c + */ +#include +#include +#include +#include + +#ifdef CONFIG_BLK_DEV_IDE + +/* + * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc + * controller that is BIOS compatible with ST-506, and thus showing up in our + * BIOS table, but not register compatible, and therefore not present in CMOS. + * + * Furthermore, we will assume that our ST-506 drives are the primary + * drives in the system -- 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. A non-zero value + * means we have an AT controller hard disk for that drive. + * + * Of course, there is no guarantee that either drive is actually on the + * "primary" IDE interface, but we don't bother trying to sort that out here. + * If a drive is not actually on the primary interface, then these parameters + * will be ignored. This results in the user having to supply the logical + * drive geometry as a boot parameter for each drive not on the primary i/f. + */ +/* + * The only "perfect" way to handle this would be to modify the setup.[cS] code + * to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info + * for us during initialization. I have the necessary docs -- any takers? -ml + */ +/* + * I did this, but it doesnt work - there is no reasonable way to find the + * correspondence between the BIOS numbering of the disks and the Linux + * numbering. -aeb + * + * The code below is bad. One of the problems is that drives 1 and 2 + * may be SCSI disks (even when IDE disks are present), so that + * the geometry we read here from BIOS is attributed to the wrong disks. + * Consequently, also the former "drive->present = 1" below was a mistake. + * + * Eventually the entire routine below should be removed. + * + * 17-OCT-2000 rjohnson@analogic.com Added spin-locks for reading CMOS + * chip. + */ + +void probe_cmos_for_drives (ide_hwif_t *hwif) +{ +#ifdef __i386__ + extern struct drive_info_struct drive_info; + byte cmos_disks, *BIOS = (byte *) &drive_info; + int unit; + unsigned long flags; + +#ifdef CONFIG_BLK_DEV_PDC4030 + if (hwif->chipset == ide_pdc4030 && hwif->channel != 0) + return; +#endif /* CONFIG_BLK_DEV_PDC4030 */ + spin_lock_irqsave(&rtc_lock, flags); + cmos_disks = CMOS_READ(0x12); + spin_unlock_irqrestore(&rtc_lock, flags); + /* Extract drive geometry from CMOS+BIOS if not already setup */ + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + + if ((cmos_disks & (0xf0 >> (unit*4))) + && !drive->present && !drive->nobios) { + unsigned short cyl = *(unsigned short *)BIOS; + unsigned char head = *(BIOS+2); + unsigned char sect = *(BIOS+14); + if (cyl > 0 && head > 0 && sect > 0 && sect < 64) { + drive->cyl = drive->bios_cyl = cyl; + drive->head = drive->bios_head = head; + drive->sect = drive->bios_sect = sect; + drive->ctl = *(BIOS+8); + } else { + printk("hd%c: C/H/S=%d/%d/%d from BIOS ignored\n", + unit+'a', cyl, head, sect); + } + } + + BIOS += 16; + } +#endif +} +#endif /* CONFIG_BLK_DEV_IDE */ + + +#if defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) + +extern ide_drive_t * get_info_ptr(kdev_t); +extern unsigned long current_capacity (ide_drive_t *); + +/* + * If heads is nonzero: find a translation with this many heads and S=63. + * Otherwise: find out how OnTrack Disk Manager would translate the disk. + */ +static void +ontrack(ide_drive_t *drive, int heads, unsigned int *c, int *h, int *s) { + static const byte dm_head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0}; + const byte *headp = dm_head_vals; + unsigned long total; + + /* + * The specs say: take geometry as obtained from Identify, + * compute total capacity C*H*S from that, and truncate to + * 1024*255*63. Now take S=63, H the first in the sequence + * 4, 8, 16, 32, 64, 128, 255 such that 63*H*1024 >= total. + * [Please tell aeb@cwi.nl in case this computes a + * geometry different from what OnTrack uses.] + */ + total = DRIVER(drive)->capacity(drive); + + *s = 63; + + if (heads) { + *h = heads; + *c = total / (63 * heads); + return; + } + + while (63 * headp[0] * 1024 < total && headp[1] != 0) + headp++; + *h = headp[0]; + *c = total / (63 * headp[0]); +} + +/* + * This routine is called from the partition-table code in pt/msdos.c. + * It has two tasks: + * (i) to handle Ontrack DiskManager by offsetting everything by 63 sectors, + * or to handle EZdrive by remapping sector 0 to sector 1. + * (ii) to invent a translated geometry. + * Part (i) is suppressed if the user specifies the "noremap" option + * on the command line. + * Part (ii) is suppressed if the user specifies an explicit geometry. + * + * The ptheads parameter is either 0 or tells about the number of + * heads shown by the end of the first nonempty partition. + * If this is either 16, 32, 64, 128, 240 or 255 we'll believe it. + * + * The xparm parameter has the following meaning: + * 0 = convert to CHS with fewer than 1024 cyls + * using the same method as Ontrack DiskManager. + * 1 = same as "0", plus offset everything by 63 sectors. + * -1 = similar to "0", plus redirect sector 0 to sector 1. + * 2 = convert to a CHS geometry with "ptheads" heads. + * + * Returns 0 if the translation was not possible, if the device was not + * an IDE disk drive, or if a geometry was "forced" on the commandline. + * Returns 1 if the geometry translation was successful. + */ +int ide_xlate_1024 (kdev_t i_rdev, int xparm, int ptheads, const char *msg) +{ + ide_drive_t *drive; + const char *msg1 = ""; + int heads = 0; + int c, h, s; + int transl = 1; /* try translation */ + int ret = 0; + + drive = get_info_ptr(i_rdev); + if (!drive) + return 0; + + /* remap? */ + if (drive->remap_0_to_1 != 2) { + if (xparm == 1) { /* DM */ + drive->sect0 = 63; + msg1 = " [remap +63]"; + ret = 1; + } else if (xparm == -1) { /* EZ-Drive */ + if (drive->remap_0_to_1 == 0) { + drive->remap_0_to_1 = 1; + msg1 = " [remap 0->1]"; + ret = 1; + } + } + } + + /* There used to be code here that assigned drive->id->CHS + to drive->CHS and that to drive->bios_CHS. However, + some disks have id->C/H/S = 4092/16/63 but are larger than 2.1 GB. + In such cases that code was wrong. Moreover, + there seems to be no reason to do any of these things. */ + + /* translate? */ + if (drive->forced_geom) + transl = 0; + + /* does ptheads look reasonable? */ + if (ptheads == 32 || ptheads == 64 || ptheads == 128 || + ptheads == 240 || ptheads == 255) + heads = ptheads; + + if (xparm == 2) { + if (!heads || + (drive->bios_head >= heads && drive->bios_sect == 63)) + transl = 0; + } + if (xparm == -1) { + if (drive->bios_head > 16) + transl = 0; /* we already have a translation */ + } + + if (transl) { + ontrack(drive, heads, &c, &h, &s); + drive->bios_cyl = c; + drive->bios_head = h; + drive->bios_sect = s; + ret = 1; + } + + drive->part[0].nr_sects = current_capacity(drive); + + if (ret) + printk("%s%s [%d/%d/%d]", msg, msg1, + drive->bios_cyl, drive->bios_head, drive->bios_sect); + return ret; +} +#endif /* defined(CONFIG_BLK_DEV_IDE) || defined(CONFIG_BLK_DEV_IDE_MODULE) */ diff -Nru a/drivers/ide/ide-m8xx.c b/drivers/ide/ide-m8xx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-m8xx.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,857 @@ +/* + * linux/drivers/ide/ide-m8xx.c + * + * Copyright (C) 2000, 2001 Wolfgang Denk, wd@denx.de + * Modified for direct IDE interface + * by Thomas Lange, thomas@corelatus.com + * Modified for direct IDE interface on 8xx without using the PCMCIA + * controller + * by Steven.Scholz@imc-berlin.de + * Moved out of arch/ppc/kernel/m8xx_setup.c, other minor cleanups + * by Mathew Locke + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ide_modes.h" +static int identify (volatile unsigned char *p); +static void print_fixed (volatile unsigned char *p); +static void print_funcid (int func); +static int check_ide_device (unsigned long base); + +static void ide_interrupt_ack (void *dev); +static void m8xx_ide_tuneproc(ide_drive_t *drive, byte pio); + +typedef struct ide_ioport_desc { + unsigned long base_off; /* Offset to PCMCIA memory */ + ide_ioreg_t reg_off[IDE_NR_PORTS]; /* controller register offsets */ + int irq; /* IRQ */ +} ide_ioport_desc_t; + +ide_ioport_desc_t ioport_dsc[MAX_HWIFS] = { +#ifdef IDE0_BASE_OFFSET + { IDE0_BASE_OFFSET, + { + IDE0_DATA_REG_OFFSET, + IDE0_ERROR_REG_OFFSET, + IDE0_NSECTOR_REG_OFFSET, + IDE0_SECTOR_REG_OFFSET, + IDE0_LCYL_REG_OFFSET, + IDE0_HCYL_REG_OFFSET, + IDE0_SELECT_REG_OFFSET, + IDE0_STATUS_REG_OFFSET, + IDE0_CONTROL_REG_OFFSET, + IDE0_IRQ_REG_OFFSET, + }, + IDE0_INTERRUPT, + }, +#ifdef IDE1_BASE_OFFSET + { IDE1_BASE_OFFSET, + { + IDE1_DATA_REG_OFFSET, + IDE1_ERROR_REG_OFFSET, + IDE1_NSECTOR_REG_OFFSET, + IDE1_SECTOR_REG_OFFSET, + IDE1_LCYL_REG_OFFSET, + IDE1_HCYL_REG_OFFSET, + IDE1_SELECT_REG_OFFSET, + IDE1_STATUS_REG_OFFSET, + IDE1_CONTROL_REG_OFFSET, + IDE1_IRQ_REG_OFFSET, + }, + IDE1_INTERRUPT, + }, +#endif /* IDE1_BASE_OFFSET */ +#endif /* IDE0_BASE_OFFSET */ +}; + +ide_pio_timings_t ide_pio_clocks[6]; +int hold_time[6] = {30, 20, 15, 10, 10, 10 }; /* PIO Mode 5 with IORDY (nonstandard) */ + +/* + * Warning: only 1 (ONE) PCMCIA slot supported here, + * which must be correctly initialized by the firmware (PPCBoot). + */ +static int _slot_ = -1; /* will be read from PCMCIA registers */ + +/* Make clock cycles and always round up */ +#define PCMCIA_MK_CLKS( t, T ) (( (t) * ((T)/1000000) + 999U ) / 1000U ) + + + +/* + * IDE stuff. + */ +static int +m8xx_ide_default_irq(ide_ioreg_t base) +{ +#ifdef CONFIG_BLK_DEV_MPC8xx_IDE + if (base >= MAX_HWIFS) + return 0; + + printk("[%d] m8xx_ide_default_irq %d\n",__LINE__,ioport_dsc[base].irq); + + return (ioport_dsc[base].irq); +#else + return 9; +#endif +} + +static ide_ioreg_t +m8xx_ide_default_io_base(int index) +{ + return index; +} + +#define M8XX_PCMCIA_CD2(slot) (0x10000000 >> (slot << 4)) +#define M8XX_PCMCIA_CD1(slot) (0x08000000 >> (slot << 4)) + +/* + * The TQM850L hardware has two pins swapped! Grrrrgh! + */ +#ifdef CONFIG_TQM850L +#define __MY_PCMCIA_GCRX_CXRESET PCMCIA_GCRX_CXOE +#define __MY_PCMCIA_GCRX_CXOE PCMCIA_GCRX_CXRESET +#else +#define __MY_PCMCIA_GCRX_CXRESET PCMCIA_GCRX_CXRESET +#define __MY_PCMCIA_GCRX_CXOE PCMCIA_GCRX_CXOE +#endif + +#if defined(CONFIG_BLK_DEV_MPC8xx_IDE) && defined(CONFIG_IDE_8xx_PCCARD) +#define PCMCIA_SCHLVL IDE0_INTERRUPT /* Status Change Interrupt Level */ +static int pcmcia_schlvl = PCMCIA_SCHLVL; +#endif + +/* + * See include/linux/ide.h for definition of hw_regs_t (p, base) + */ + +/* + * m8xx_ide_init_hwif_ports for a direct IDE interface _using_ + */ +#if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT) +static void +m8xx_ide_init_hwif_ports(hw_regs_t *hw, ide_ioreg_t data_port, + ide_ioreg_t ctrl_port, int *irq) +{ + ide_ioreg_t *p = hw->io_ports; + int i; + + typedef struct { + ulong br; + ulong or; + } pcmcia_win_t; + volatile pcmcia_win_t *win; + volatile pcmconf8xx_t *pcmp; + + uint *pgcrx; + u32 pcmcia_phy_base; + u32 pcmcia_phy_end; + static unsigned long pcmcia_base = 0; + unsigned long base; + + *p = 0; + if (irq) + *irq = 0; + + pcmp = (pcmconf8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_pcmcia)); + + if (!pcmcia_base) { + /* + * Read out PCMCIA registers. Since the reset values + * are undefined, we sure hope that they have been + * set up by firmware + */ + + /* Scan all registers for valid settings */ + pcmcia_phy_base = 0xFFFFFFFF; + pcmcia_phy_end = 0; + /* br0 is start of brX and orX regs */ + win = (pcmcia_win_t *) \ + (&(((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pbr0)); + for (i = 0; i < 8; i++) { + if (win->or & 1) { /* This bank is marked as valid */ + if (win->br < pcmcia_phy_base) { + pcmcia_phy_base = win->br; + } + if ((win->br + PCMCIA_MEM_SIZE) > pcmcia_phy_end) { + pcmcia_phy_end = win->br + PCMCIA_MEM_SIZE; + } + /* Check which slot that has been defined */ + _slot_ = (win->or >> 2) & 1; + + } /* Valid bank */ + win++; + } /* for */ + + printk ("PCMCIA slot %c: phys mem %08x...%08x (size %08x)\n", + 'A' + _slot_, + pcmcia_phy_base, pcmcia_phy_end, + pcmcia_phy_end - pcmcia_phy_base); + + pcmcia_base=(unsigned long)ioremap(pcmcia_phy_base, + pcmcia_phy_end-pcmcia_phy_base); + +#ifdef DEBUG + printk ("PCMCIA virt base: %08lx\n", pcmcia_base); +#endif + /* Compute clock cycles for PIO timings */ + for (i=0; i<6; ++i) { + bd_t *binfo = (bd_t *)__res; + + hold_time[i] = + PCMCIA_MK_CLKS (hold_time[i], + binfo->bi_busfreq); + ide_pio_clocks[i].setup_time = + PCMCIA_MK_CLKS (ide_pio_timings[i].setup_time, + binfo->bi_busfreq); + ide_pio_clocks[i].active_time = + PCMCIA_MK_CLKS (ide_pio_timings[i].active_time, + binfo->bi_busfreq); + ide_pio_clocks[i].cycle_time = + PCMCIA_MK_CLKS (ide_pio_timings[i].cycle_time, + binfo->bi_busfreq); +#if 0 + printk ("PIO mode %d timings: %d/%d/%d => %d/%d/%d\n", + i, + ide_pio_clocks[i].setup_time, + ide_pio_clocks[i].active_time, + ide_pio_clocks[i].hold_time, + ide_pio_clocks[i].cycle_time, + ide_pio_timings[i].setup_time, + ide_pio_timings[i].active_time, + ide_pio_timings[i].hold_time, + ide_pio_timings[i].cycle_time); +#endif + } + } + + if (data_port >= MAX_HWIFS) + return; + + if (_slot_ == -1) { + printk ("PCMCIA slot has not been defined! Using A as default\n"); + _slot_ = 0; + } + +#ifdef CONFIG_IDE_8xx_PCCARD + +#ifdef DEBUG + printk ("PIPR = 0x%08X slot %c ==> mask = 0x%X\n", + pcmp->pcmc_pipr, + 'A' + _slot_, + M8XX_PCMCIA_CD1(_slot_) | M8XX_PCMCIA_CD2(_slot_) ); +#endif /* DEBUG */ + + if (pcmp->pcmc_pipr & (M8XX_PCMCIA_CD1(_slot_)|M8XX_PCMCIA_CD2(_slot_))) { + printk ("No card in slot %c: PIPR=%08x\n", + 'A' + _slot_, (u32) pcmp->pcmc_pipr); + return; /* No card in slot */ + } + + check_ide_device (pcmcia_base); + +#endif /* CONFIG_IDE_8xx_PCCARD */ + + base = pcmcia_base + ioport_dsc[data_port].base_off; +#ifdef DEBUG + printk ("base: %08x + %08x = %08x\n", + pcmcia_base, ioport_dsc[data_port].base_off, base); +#endif + + for (i = 0; i < IDE_NR_PORTS; ++i) { +#ifdef DEBUG + printk ("port[%d]: %08x + %08x = %08x\n", + i, + base, + ioport_dsc[data_port].reg_off[i], + i, base + ioport_dsc[data_port].reg_off[i]); +#endif + *p++ = base + ioport_dsc[data_port].reg_off[i]; + } + + if (irq) { +#ifdef CONFIG_IDE_8xx_PCCARD + unsigned int reg; + + *irq = ioport_dsc[data_port].irq; + if (_slot_) + pgcrx = &((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pgcrb; + else + pgcrx = &((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pgcra; + + reg = *pgcrx; + reg |= mk_int_int_mask (pcmcia_schlvl) << 24; + reg |= mk_int_int_mask (pcmcia_schlvl) << 16; + *pgcrx = reg; +#else /* direct connected IDE drive, i.e. external IRQ, not the PCMCIA irq */ + *irq = ioport_dsc[data_port].irq; +#endif /* CONFIG_IDE_8xx_PCCARD */ + } + + /* register routine to tune PIO mode */ + ide_hwifs[data_port].tuneproc = m8xx_ide_tuneproc; + + hw->ack_intr = (ide_ack_intr_t *) ide_interrupt_ack; + /* Enable Harddisk Interrupt, + * and make it edge sensitive + */ + /* (11-18) Set edge detect for irq, no wakeup from low power mode */ + ((immap_t *)IMAP_ADDR)->im_siu_conf.sc_siel |= + (0x80000000 >> ioport_dsc[data_port].irq); + +#ifdef CONFIG_IDE_8xx_PCCARD + /* Make sure we dont get garbage irq */ + ((immap_t *) IMAP_ADDR)->im_pcmcia.pcmc_pscr = 0xFFFF; + + /* Enable falling edge irq */ + pcmp->pcmc_per = 0x100000 >> (16 * _slot_); +#endif /* CONFIG_IDE_8xx_PCCARD */ +} /* m8xx_ide_init_hwif_ports() using 8xx internal PCMCIA interface */ +#endif /* CONFIG_IDE_8xx_PCCARD || CONFIG_IDE_8xx_DIRECT */ + +/* + * m8xx_ide_init_hwif_ports for a direct IDE interface _not_ using + * MPC8xx's internal PCMCIA interface + */ +#if defined(CONFIG_IDE_EXT_DIRECT) +void m8xx_ide_init_hwif_ports (hw_regs_t *hw, + ide_ioreg_t data_port, ide_ioreg_t ctrl_port, int *irq) +{ + ide_ioreg_t *p = hw->io_ports; + int i; + + u32 ide_phy_base; + u32 ide_phy_end; + static unsigned long ide_base = 0; + unsigned long base; + + *p = 0; + if (irq) + *irq = 0; + + if (!ide_base) { + + /* TODO: + * - add code to read ORx, BRx + */ + ide_phy_base = CFG_ATA_BASE_ADDR; + ide_phy_end = CFG_ATA_BASE_ADDR + 0x200; + + printk ("IDE phys mem : %08x...%08x (size %08x)\n", + ide_phy_base, ide_phy_end, + ide_phy_end - ide_phy_base); + + ide_base=(unsigned long)ioremap(ide_phy_base, + ide_phy_end-ide_phy_base); + +#ifdef DEBUG + printk ("IDE virt base: %08lx\n", ide_base); +#endif + } + + if (data_port >= MAX_HWIFS) + return; + + base = ide_base + ioport_dsc[data_port].base_off; +#ifdef DEBUG + printk ("base: %08x + %08x = %08x\n", + ide_base, ioport_dsc[data_port].base_off, base); +#endif + + for (i = 0; i < IDE_NR_PORTS; ++i) { +#ifdef DEBUG + printk ("port[%d]: %08x + %08x = %08x\n", + i, + base, + ioport_dsc[data_port].reg_off[i], + i, base + ioport_dsc[data_port].reg_off[i]); +#endif + *p++ = base + ioport_dsc[data_port].reg_off[i]; + } + + if (irq) { + /* direct connected IDE drive, i.e. external IRQ */ + *irq = ioport_dsc[data_port].irq; + } + + /* register routine to tune PIO mode */ + ide_hwifs[data_port].tuneproc = m8xx_ide_tuneproc; + + hw->ack_intr = (ide_ack_intr_t *) ide_interrupt_ack; + /* Enable Harddisk Interrupt, + * and make it edge sensitive + */ + /* (11-18) Set edge detect for irq, no wakeup from low power mode */ + ((immap_t *) IMAP_ADDR)->im_siu_conf.sc_siel |= + (0x80000000 >> ioport_dsc[data_port].irq); +} /* m8xx_ide_init_hwif_ports() for CONFIG_IDE_8xx_DIRECT */ + +#endif /* CONFIG_IDE_8xx_DIRECT */ + + +/* -------------------------------------------------------------------- */ + + +/* PCMCIA Timing */ +#ifndef PCMCIA_SHT +#define PCMCIA_SHT(t) ((t & 0x0F)<<16) /* Strobe Hold Time */ +#define PCMCIA_SST(t) ((t & 0x0F)<<12) /* Strobe Setup Time */ +#define PCMCIA_SL(t) ((t==32) ? 0 : ((t & 0x1F)<<7)) /* Strobe Length */ +#endif + + +/* Calculate PIO timings */ +static void +m8xx_ide_tuneproc(ide_drive_t *drive, byte pio) +{ + ide_pio_data_t d; +#if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT) + volatile pcmconf8xx_t *pcmp; + ulong timing, mask, reg; +#endif + + pio = ide_get_best_pio_mode(drive, pio, 4, &d); + +#if 1 + printk("%s[%d] %s: best PIO mode: %d\n", + __FILE__,__LINE__,__FUNCTION__, pio); +#endif + +#if defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_DIRECT) + pcmp = (pcmconf8xx_t *)(&(((immap_t *)IMAP_ADDR)->im_pcmcia)); + + mask = ~(PCMCIA_SHT(0xFF) | PCMCIA_SST(0xFF) | PCMCIA_SL(0xFF)); + + timing = PCMCIA_SHT(hold_time[pio] ) + | PCMCIA_SST(ide_pio_clocks[pio].setup_time ) + | PCMCIA_SL (ide_pio_clocks[pio].active_time) + ; + +#if 1 + printk ("Setting timing bits 0x%08lx in PCMCIA controller\n", timing); +#endif + if ((reg = pcmp->pcmc_por0 & mask) != 0) + pcmp->pcmc_por0 = reg | timing; + + if ((reg = pcmp->pcmc_por1 & mask) != 0) + pcmp->pcmc_por1 = reg | timing; + + if ((reg = pcmp->pcmc_por2 & mask) != 0) + pcmp->pcmc_por2 = reg | timing; + + if ((reg = pcmp->pcmc_por3 & mask) != 0) + pcmp->pcmc_por3 = reg | timing; + + if ((reg = pcmp->pcmc_por4 & mask) != 0) + pcmp->pcmc_por4 = reg | timing; + + if ((reg = pcmp->pcmc_por5 & mask) != 0) + pcmp->pcmc_por5 = reg | timing; + + if ((reg = pcmp->pcmc_por6 & mask) != 0) + pcmp->pcmc_por6 = reg | timing; + + if ((reg = pcmp->pcmc_por7 & mask) != 0) + pcmp->pcmc_por7 = reg | timing; + +#elif defined(CONFIG_IDE_EXT_DIRECT) + + printk("%s[%d] %s: not implemented yet!\n", + __FILE__,__LINE__,__FUNCTION__); +#endif /* defined(CONFIG_IDE_8xx_PCCARD) || defined(CONFIG_IDE_8xx_PCMCIA */ +} + +static void +ide_interrupt_ack (void *dev) +{ +#ifdef CONFIG_IDE_8xx_PCCARD + u_int pscr, pipr; + +#if (PCMCIA_SOCKETS_NO == 2) + u_int _slot_; +#endif + + /* get interrupt sources */ + + pscr = ((volatile immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr; + pipr = ((volatile immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pipr; + + /* + * report only if both card detect signals are the same + * not too nice done, + * we depend on that CD2 is the bit to the left of CD1... + */ + + if(_slot_==-1){ + printk("PCMCIA slot has not been defined! Using A as default\n"); + _slot_=0; + } + + if(((pipr & M8XX_PCMCIA_CD2(_slot_)) >> 1) ^ + (pipr & M8XX_PCMCIA_CD1(_slot_)) ) { + printk ("card detect interrupt\n"); + } + /* clear the interrupt sources */ + ((immap_t *)IMAP_ADDR)->im_pcmcia.pcmc_pscr = pscr; + +#else /* ! CONFIG_IDE_8xx_PCCARD */ + /* + * Only CONFIG_IDE_8xx_PCCARD is using the interrupt of the + * MPC8xx's PCMCIA controller, so there is nothing to be done here + * for CONFIG_IDE_8xx_DIRECT and CONFIG_IDE_EXT_DIRECT. + * The interrupt is handled somewhere else. -- Steven + */ +#endif /* CONFIG_IDE_8xx_PCCARD */ +} + + + +/* + * CIS Tupel codes + */ +#define CISTPL_NULL 0x00 +#define CISTPL_DEVICE 0x01 +#define CISTPL_LONGLINK_CB 0x02 +#define CISTPL_INDIRECT 0x03 +#define CISTPL_CONFIG_CB 0x04 +#define CISTPL_CFTABLE_ENTRY_CB 0x05 +#define CISTPL_LONGLINK_MFC 0x06 +#define CISTPL_BAR 0x07 +#define CISTPL_PWR_MGMNT 0x08 +#define CISTPL_EXTDEVICE 0x09 +#define CISTPL_CHECKSUM 0x10 +#define CISTPL_LONGLINK_A 0x11 +#define CISTPL_LONGLINK_C 0x12 +#define CISTPL_LINKTARGET 0x13 +#define CISTPL_NO_LINK 0x14 +#define CISTPL_VERS_1 0x15 +#define CISTPL_ALTSTR 0x16 +#define CISTPL_DEVICE_A 0x17 +#define CISTPL_JEDEC_C 0x18 +#define CISTPL_JEDEC_A 0x19 +#define CISTPL_CONFIG 0x1a +#define CISTPL_CFTABLE_ENTRY 0x1b +#define CISTPL_DEVICE_OC 0x1c +#define CISTPL_DEVICE_OA 0x1d +#define CISTPL_DEVICE_GEO 0x1e +#define CISTPL_DEVICE_GEO_A 0x1f +#define CISTPL_MANFID 0x20 +#define CISTPL_FUNCID 0x21 +#define CISTPL_FUNCE 0x22 +#define CISTPL_SWIL 0x23 +#define CISTPL_END 0xff + +/* + * CIS Function ID codes + */ +#define CISTPL_FUNCID_MULTI 0x00 +#define CISTPL_FUNCID_MEMORY 0x01 +#define CISTPL_FUNCID_SERIAL 0x02 +#define CISTPL_FUNCID_PARALLEL 0x03 +#define CISTPL_FUNCID_FIXED 0x04 +#define CISTPL_FUNCID_VIDEO 0x05 +#define CISTPL_FUNCID_NETWORK 0x06 +#define CISTPL_FUNCID_AIMS 0x07 +#define CISTPL_FUNCID_SCSI 0x08 + +/* + * Fixed Disk FUNCE codes + */ +#define CISTPL_IDE_INTERFACE 0x01 + +#define CISTPL_FUNCE_IDE_IFACE 0x01 +#define CISTPL_FUNCE_IDE_MASTER 0x02 +#define CISTPL_FUNCE_IDE_SLAVE 0x03 + +/* First feature byte */ +#define CISTPL_IDE_SILICON 0x04 +#define CISTPL_IDE_UNIQUE 0x08 +#define CISTPL_IDE_DUAL 0x10 + +/* Second feature byte */ +#define CISTPL_IDE_HAS_SLEEP 0x01 +#define CISTPL_IDE_HAS_STANDBY 0x02 +#define CISTPL_IDE_HAS_IDLE 0x04 +#define CISTPL_IDE_LOW_POWER 0x08 +#define CISTPL_IDE_REG_INHIBIT 0x10 +#define CISTPL_IDE_HAS_INDEX 0x20 +#define CISTPL_IDE_IOIS16 0x40 + + +/* -------------------------------------------------------------------- */ + + +#define MAX_TUPEL_SZ 512 +#define MAX_FEATURES 4 + +static int check_ide_device (unsigned long base) +{ + volatile unsigned char *ident = NULL; + volatile unsigned char *feature_p[MAX_FEATURES]; + volatile unsigned char *p, *start; + int n_features = 0; + unsigned char func_id = ~0; + unsigned char code, len; + unsigned short config_base = 0; + int found = 0; + int i; + +#ifdef DEBUG + printk ("PCMCIA MEM: %08lX\n", base); +#endif + start = p = (volatile unsigned char *) base; + + while ((p - start) < MAX_TUPEL_SZ) { + + code = *p; p += 2; + + if (code == 0xFF) { /* End of chain */ + break; + } + + len = *p; p += 2; +#ifdef DEBUG_PCMCIA + { volatile unsigned char *q = p; + printk ("\nTuple code %02x length %d\n\tData:", + code, len); + + for (i = 0; i < len; ++i) { + printk (" %02x", *q); + q+= 2; + } + } +#endif /* DEBUG_PCMCIA */ + switch (code) { + case CISTPL_VERS_1: + ident = p + 4; + break; + case CISTPL_FUNCID: + func_id = *p; + break; + case CISTPL_FUNCE: + if (n_features < MAX_FEATURES) + feature_p[n_features++] = p; + break; + case CISTPL_CONFIG: + config_base = (*(p+6) << 8) + (*(p+4)); + default: + break; + } + p += 2 * len; + } + + found = identify (ident); + + if (func_id != ((unsigned char)~0)) { + print_funcid (func_id); + + if (func_id == CISTPL_FUNCID_FIXED) + found = 1; + else + return (1); /* no disk drive */ + } + + for (i=0; i id_str) { + if (*t == ' ') + *t = '\0'; + else + break; + } + printk ("Card ID: %s\n", id_str); + + for (card=known_cards; *card; ++card) { + if (strcmp(*card, id_str) == 0) { /* found! */ + return (1); + } + } + + return (0); /* don't know */ +} + +void m8xx_ide_init(void) +{ + ppc_ide_md.default_irq = m8xx_ide_default_irq; + ppc_ide_md.default_io_base = m8xx_ide_default_io_base; + ppc_ide_md.ide_init_hwif = m8xx_ide_init_hwif_ports; +} diff -Nru a/drivers/ide/ide-pci.c b/drivers/ide/ide-pci.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-pci.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,992 @@ +/* + * linux/drivers/ide/ide-pci.c Version 1.05 June 9, 2000 + * + * Copyright (c) 1998-2000 Andre Hedrick + * + * Copyright (c) 1995-1998 Mark Lord + * May be copied or modified under the terms of the GNU General Public License + */ + +/* + * This module provides support for automatic detection and + * configuration of all PCI IDE interfaces present in a system. + */ + +/* + * Chipsets that are on the IDE_IGNORE list because of problems of not being + * set at compile time. + * + * CONFIG_BLK_DEV_PDC202XX + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define DEVID_PIIXa ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_0}) +#define DEVID_PIIXb ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371FB_1}) +#define DEVID_MPIIX ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371MX}) +#define DEVID_PIIX3 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_1}) +#define DEVID_PIIX4 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB}) +#define DEVID_PIIX4E ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_1}) +#define DEVID_PIIX4E2 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82443MX_1}) +#define DEVID_PIIX4U ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_1}) +#define DEVID_PIIX4U2 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82372FB_1}) +#define DEVID_PIIX4NX ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82451NX}) +#define DEVID_PIIX4U3 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_9}) +#define DEVID_PIIX4U4 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_8}) +#define DEVID_PIIX4U5 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_10}) +#define DEVID_PIIX4U6 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_11}) +#define DEVID_PIIX4U7 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_11}) +#define DEVID_PIIX4U8 ((ide_pci_devid_t){PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_11}) +#define DEVID_VIA_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C561}) +#define DEVID_MR_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C576_1}) +#define DEVID_VP_IDE ((ide_pci_devid_t){PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_82C586_1}) +#define DEVID_PDC20246 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20246}) +#define DEVID_PDC20262 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20262}) +#define DEVID_PDC20263 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20263}) +#define DEVID_PDC20265 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20265}) +#define DEVID_PDC20267 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20267}) +#define DEVID_PDC20268 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268}) +#define DEVID_PDC20268R ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20268R}) +#define DEVID_PDC20269 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20269}) +#define DEVID_PDC20271 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20271}) +#define DEVID_PDC20275 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20275}) +#define DEVID_PDC20276 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20276}) +#define DEVID_PDC20277 ((ide_pci_devid_t){PCI_VENDOR_ID_PROMISE, PCI_DEVICE_ID_PROMISE_20277}) +#define DEVID_RZ1000 ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000}) +#define DEVID_RZ1001 ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001}) +#define DEVID_SAMURAI ((ide_pci_devid_t){PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_SAMURAI_IDE}) +#define DEVID_CMD640 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_640}) +#define DEVID_CMD643 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_643}) +#define DEVID_CMD646 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_646}) +#define DEVID_CMD648 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_648}) +#define DEVID_CMD649 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_649}) +#define DEVID_CMD680 ((ide_pci_devid_t){PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_CMD_680}) +#define DEVID_SIS5513 ((ide_pci_devid_t){PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5513}) +#define DEVID_OPTI621 ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C621}) +#define DEVID_OPTI621V ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C558}) +#define DEVID_OPTI621X ((ide_pci_devid_t){PCI_VENDOR_ID_OPTI, PCI_DEVICE_ID_OPTI_82C825}) +#define DEVID_TRM290 ((ide_pci_devid_t){PCI_VENDOR_ID_TEKRAM, PCI_DEVICE_ID_TEKRAM_DC290}) +#define DEVID_NS87410 ((ide_pci_devid_t){PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87410}) +#define DEVID_NS87415 ((ide_pci_devid_t){PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_87415}) +#define DEVID_HT6565 ((ide_pci_devid_t){PCI_VENDOR_ID_HOLTEK, PCI_DEVICE_ID_HOLTEK_6565}) +#define DEVID_AEC6210 ((ide_pci_devid_t){PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF}) +#define DEVID_AEC6260 ((ide_pci_devid_t){PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860}) +#define DEVID_AEC6260R ((ide_pci_devid_t){PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860R}) +#define DEVID_AEC6280 ((ide_pci_devid_t){PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP865}) +#define DEVID_AEC6880 ((ide_pci_devid_t){PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP865R}) +#define DEVID_W82C105 ((ide_pci_devid_t){PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105}) +#define DEVID_UM8673F ((ide_pci_devid_t){PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8673F}) +#define DEVID_UM8886A ((ide_pci_devid_t){PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886A}) +#define DEVID_UM8886BF ((ide_pci_devid_t){PCI_VENDOR_ID_UMC, PCI_DEVICE_ID_UMC_UM8886BF}) +#define DEVID_HPT34X ((ide_pci_devid_t){PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT343}) +#define DEVID_HPT366 ((ide_pci_devid_t){PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT366}) +#define DEVID_HPT372 ((ide_pci_devid_t){PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT372}) +#define DEVID_HPT302 ((ide_pci_devid_t){PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT302}) +#define DEVID_HPT371 ((ide_pci_devid_t){PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT371}) +#define DEVID_HPT374 ((ide_pci_devid_t){PCI_VENDOR_ID_TTI, PCI_DEVICE_ID_TTI_HPT374}) +#define DEVID_ALI15X3 ((ide_pci_devid_t){PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M5229}) +#define DEVID_CY82C693 ((ide_pci_devid_t){PCI_VENDOR_ID_CONTAQ, PCI_DEVICE_ID_CONTAQ_82C693}) +#define DEVID_HINT ((ide_pci_devid_t){0x3388, 0x8013}) +#define DEVID_CS5530 ((ide_pci_devid_t){PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_IDE}) +#define DEVID_AMD7401 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_COBRA_7401}) +#define DEVID_AMD7409 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7409}) +#define DEVID_AMD7411 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7411}) +#define DEVID_AMD7441 ((ide_pci_devid_t){PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_OPUS_7441}) +#define DEVID_PDCADMA ((ide_pci_devid_t){PCI_VENDOR_ID_PDC, PCI_DEVICE_ID_PDC_1841}) +#define DEVID_SLC90E66 ((ide_pci_devid_t){PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_1}) +#define DEVID_OSB4 ((ide_pci_devid_t){PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE}) +#define DEVID_CSB5 ((ide_pci_devid_t){PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE}) +#define DEVID_CSB6 ((ide_pci_devid_t){PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE}) +#define DEVID_ITE8172G ((ide_pci_devid_t){PCI_VENDOR_ID_ITE, PCI_DEVICE_ID_ITE_IT8172G}) + +#define IDE_IGNORE ((void *)-1) +#define IDE_NO_DRIVER ((void *)-2) + +#ifdef CONFIG_BLK_DEV_AEC62XX +extern void fixup_device_aec6x80(struct pci_dev *, ide_pci_device_t *); +extern unsigned int pci_init_aec62xx(struct pci_dev *, const char *); +extern unsigned int ata66_aec62xx(ide_hwif_t *); +extern void ide_init_aec62xx(ide_hwif_t *); +extern void ide_dmacapable_aec62xx(ide_hwif_t *, unsigned long); +#define FIXUP_AEC62XX &fixup_device_aec6x80 +#define PCI_AEC62XX &pci_init_aec62xx +#define ATA66_AEC62XX &ata66_aec62xx +#define INIT_AEC62XX &ide_init_aec62xx +#define DMA_AEC62XX &ide_dmacapable_aec62xx +#else +#define FIXUP_AEC62XX NULL +#define PCI_AEC62XX NULL +#define ATA66_AEC62XX NULL +#define INIT_AEC62XX IDE_NO_DRIVER +#define DMA_AEC62XX NULL +#endif + +#ifdef CONFIG_BLK_DEV_ALI15X3 +extern void fixup_device_ali15x3(struct pci_dev *, ide_pci_device_t *); +extern unsigned int pci_init_ali15x3(struct pci_dev *, const char *); +extern unsigned int ata66_ali15x3(ide_hwif_t *); +extern void ide_init_ali15x3(ide_hwif_t *); +extern void ide_dmacapable_ali15x3(ide_hwif_t *, unsigned long); +#define FIXUP_ALI15X3 &fixup_device_ali15x3 +#define PCI_ALI15X3 &pci_init_ali15x3 +#define ATA66_ALI15X3 &ata66_ali15x3 +#define INIT_ALI15X3 &ide_init_ali15x3 +#define DMA_ALI15X3 &ide_dmacapable_ali15x3 +#else +#define FIXUP_ALI15X3 NULL +#define PCI_ALI15X3 NULL +#define ATA66_ALI15X3 NULL +#define INIT_ALI15X3 IDE_NO_DRIVER +#define DMA_ALI15X3 NULL +#endif + +#ifdef CONFIG_BLK_DEV_AMD74XX +extern void fixup_device_amd74xx(struct pci_dev *, ide_pci_device_t *); +extern unsigned int pci_init_amd74xx(struct pci_dev *, const char *); +extern unsigned int ata66_amd74xx(ide_hwif_t *); +extern void ide_init_amd74xx(ide_hwif_t *); +extern void ide_dmacapable_amd74xx(ide_hwif_t *, unsigned long); +#define FIXUP_AMD74XX &fixup_device_amd74xx +#define PCI_AMD74XX &pci_init_amd74xx +#define ATA66_AMD74XX &ata66_amd74xx +#define INIT_AMD74XX &ide_init_amd74xx +#define DMA_AMD74XX &ide_dmacapable_amd74xx +#else +#define FIXUP_AMD74XX NULL +#define PCI_AMD74XX NULL +#define ATA66_AMD74XX NULL +#define INIT_AMD74XX IDE_NO_DRIVER +#define DMA_AMD74XX NULL +#endif + +#ifdef CONFIG_BLK_DEV_CMD64X +extern unsigned int pci_init_cmd64x(struct pci_dev *, const char *); +extern unsigned int ata66_cmd64x(ide_hwif_t *); +extern void ide_init_cmd64x(ide_hwif_t *); +extern void ide_dmacapable_cmd64x(ide_hwif_t *, unsigned long); +#define PCI_CMD64X &pci_init_cmd64x +#define ATA66_CMD64X &ata66_cmd64x +#define INIT_CMD64X &ide_init_cmd64x +#else +#define PCI_CMD64X NULL +#define ATA66_CMD64X NULL +#ifdef __sparc_v9__ +#define INIT_CMD64X IDE_IGNORE +#else +#define INIT_CMD64X IDE_NO_DRIVER +#endif +#endif + +#ifdef CONFIG_BLK_DEV_CY82C693 +extern void fixup_device_cy82c693(struct pci_dev *, ide_pci_device_t *); +extern unsigned int pci_init_cy82c693(struct pci_dev *, const char *); +extern void ide_init_cy82c693(ide_hwif_t *); +#define FIXUP_CY82C693 &fixup_device_cy82c693 +#define PCI_CY82C693 &pci_init_cy82c693 +#define INIT_CY82C693 &ide_init_cy82c693 +#else +#define FIXUP_CY82C693 NULL +#define PCI_CY82C693 NULL +#define INIT_CY82C693 IDE_NO_DRIVER +#endif + +#ifdef CONFIG_BLK_DEV_CS5530 +extern unsigned int pci_init_cs5530(struct pci_dev *, const char *); +extern void ide_init_cs5530(ide_hwif_t *); +#define PCI_CS5530 &pci_init_cs5530 +#define INIT_CS5530 &ide_init_cs5530 +#else +#define PCI_CS5530 NULL +#define INIT_CS5530 IDE_NO_DRIVER +#endif + +#ifdef CONFIG_BLK_DEV_HPT34X +extern void fixup_device_hpt343(struct pci_dev *, ide_pci_device_t *); +extern unsigned int pci_init_hpt34x(struct pci_dev *, const char *); +extern void ide_init_hpt34x(ide_hwif_t *); +#define FIXUP_HPT34X &fixup_device_hpt343 +#define PCI_HPT34X &pci_init_hpt34x +#define INIT_HPT34X &ide_init_hpt34x +#else +#define FIXUP_HPT34X NULL +#define PCI_HPT34X NULL +#define INIT_HPT34X IDE_IGNORE +#endif + +#ifdef CONFIG_BLK_DEV_HPT366 +extern void fixup_device_hpt366(struct pci_dev *, ide_pci_device_t *); +extern void fixup_device_hpt374(struct pci_dev *, ide_pci_device_t *); +extern unsigned int pci_init_hpt366(struct pci_dev *, const char *); +extern unsigned int ata66_hpt366(ide_hwif_t *); +extern void ide_init_hpt366(ide_hwif_t *); +extern void ide_dmacapable_hpt366(ide_hwif_t *, unsigned long); +#define FIXUP_HPT366 &fixup_device_hpt366 +#define FIXUP_HPT374 &fixup_device_hpt374 +#define PCI_HPT366 &pci_init_hpt366 +#define ATA66_HPT366 &ata66_hpt366 +#define INIT_HPT366 &ide_init_hpt366 +#define DMA_HPT366 &ide_dmacapable_hpt366 +#else +#define FIXUP_HPT366 NULL +#define FIXUP_HPT374 NULL +#define PCI_HPT366 NULL +#define ATA66_HPT366 NULL +#define INIT_HPT366 IDE_NO_DRIVER +#define DMA_HPT366 NULL +#endif + +#ifdef CONFIG_BLK_DEV_NS87415 +extern void ide_init_ns87415(ide_hwif_t *); +#define INIT_NS87415 &ide_init_ns87415 +#else +#define INIT_NS87415 IDE_IGNORE +#endif + +#ifdef CONFIG_BLK_DEV_OPTI621 +extern void fixup_device_opti621(struct pci_dev *, ide_pci_device_t *); +extern void ide_init_opti621(ide_hwif_t *); +#define FIXUP_OPTI621 &fixup_device_opti621 +#define INIT_OPTI621 &ide_init_opti621 +#else +#define FIXUP_OPTI621 NULL +#define INIT_OPTI621 IDE_NO_DRIVER +#endif + +#ifdef CONFIG_BLK_DEV_PDC_ADMA +extern unsigned int pci_init_pdcadma(struct pci_dev *, const char *); +extern unsigned int ata66_pdcadma(ide_hwif_t *); +extern void ide_init_pdcadma(ide_hwif_t *); +extern void ide_dmacapable_pdcadma(ide_hwif_t *, unsigned long); +#define PCI_PDCADMA &pci_init_pdcadma +#define ATA66_PDCADMA &ata66_pdcadma +#define INIT_PDCADMA &ide_init_pdcadma +#define DMA_PDCADMA &ide_dmacapable_pdcadma +#else +#define PCI_PDCADMA IDE_IGNORE +#define ATA66_PDCADMA IDE_IGNORE +#define INIT_PDCADMA IDE_IGNORE +#define DMA_PDCADMA IDE_IGNORE +#endif + +#ifdef CONFIG_BLK_DEV_PDC202XX +extern void fixup_device_pdc20265(struct pci_dev *, ide_pci_device_t *); +extern void fixup_device_pdc20270(struct pci_dev *, ide_pci_device_t *); +extern unsigned int pci_init_pdc202xx(struct pci_dev *, const char *); +extern unsigned int ata66_pdc202xx(ide_hwif_t *); +extern void ide_init_pdc202xx(ide_hwif_t *); +#define FIXUP_PDC20265 &fixup_device_pdc20265 +#define FIXUP_PDC20270 &fixup_device_pdc20270 +#define PCI_PDC202XX &pci_init_pdc202xx +#define ATA66_PDC202XX &ata66_pdc202xx +#define INIT_PDC202XX &ide_init_pdc202xx +#else +#define FIXUP_PDC20265 IDE_IGNORE +#define FIXUP_PDC20270 IDE_IGNORE +#define PCI_PDC202XX IDE_IGNORE +#define ATA66_PDC202XX IDE_IGNORE +#define INIT_PDC202XX IDE_IGNORE +#endif + +#ifdef CONFIG_BLK_DEV_PIIX +extern void fixup_device_piix(struct pci_dev *, ide_pci_device_t *); +extern unsigned int pci_init_piix(struct pci_dev *, const char *); +extern unsigned int ata66_piix(ide_hwif_t *); +extern void ide_init_piix(ide_hwif_t *); +#define FIXUP_PIIX &fixup_device_piix +#define PCI_PIIX &pci_init_piix +#define ATA66_PIIX &ata66_piix +#define INIT_PIIX &ide_init_piix +#else +#define FIXUP_PIIX NULL +#define PCI_PIIX NULL +#define ATA66_PIIX NULL +#define INIT_PIIX IDE_NO_DRIVER +#endif + +#ifdef CONFIG_BLK_DEV_IT8172 +extern void fixup_device_it8172(struct pci_dev *, ide_pci_device_t *); +extern unsigned int pci_init_it8172(struct pci_dev *, const char *); +extern unsigned int ata66_it8172(ide_hwif_t *); +extern void ide_init_it8172(ide_hwif_t *); +#define FIXUP_IT8172 &fixup_device_it8172 +#define PCI_IT8172 &pci_init_it8172 +#define ATA66_IT8172 &ata66_it8172 +#define INIT_IT8172 &ide_init_it8172 +#else +#define FIXUP_IT8172 NULL +#define PCI_IT8172 NULL +#define ATA66_IT8172 NULL +#define INIT_IT8172 IDE_NO_DRIVER +#endif + +#ifdef CONFIG_BLK_DEV_RZ1000 +extern void ide_init_rz1000(ide_hwif_t *); +#define INIT_RZ1000 &ide_init_rz1000 +#else +#define INIT_RZ1000 IDE_IGNORE +#endif + +#define INIT_SAMURAI NULL + +#ifdef CONFIG_BLK_DEV_SVWKS +extern void fixup_device_csb6(struct pci_dev *, ide_pci_device_t *); +extern unsigned int pci_init_svwks(struct pci_dev *, const char *); +extern unsigned int ata66_svwks(ide_hwif_t *); +extern void ide_init_svwks(ide_hwif_t *); +extern void ide_dmacapable_svwks(ide_hwif_t *, unsigned long); +#define FIXUP_CSB6 &fixup_device_csb6 +#define PCI_SVWKS &pci_init_svwks +#define ATA66_SVWKS &ata66_svwks +#define INIT_SVWKS &ide_init_svwks +#define DMA_SVWKS &ide_dmacapable_svwks +#else +#define FIXUP_CSB6 NULL +#define PCI_SVWKS NULL +#define ATA66_SVWKS NULL +#define INIT_SVWKS IDE_NO_DRIVER +#define DMA_SVWKS NULL +#endif + +#ifdef CONFIG_BLK_DEV_SIS5513 +extern void fixup_device_sis5513(struct pci_dev *, ide_pci_device_t *); +extern unsigned int pci_init_sis5513(struct pci_dev *, const char *); +extern unsigned int ata66_sis5513(ide_hwif_t *); +extern void ide_init_sis5513(ide_hwif_t *); +#define FIXUP_SIS5513 &fixup_device_sis5513 +#define PCI_SIS5513 &pci_init_sis5513 +#define ATA66_SIS5513 &ata66_sis5513 +#define INIT_SIS5513 &ide_init_sis5513 +#else +#define FIXUP_SIS5513 NULL +#define PCI_SIS5513 NULL +#define ATA66_SIS5513 NULL +#define INIT_SIS5513 IDE_NO_DRIVER +#endif + +#ifdef CONFIG_BLK_DEV_SLC90E66 +extern unsigned int pci_init_slc90e66(struct pci_dev *, const char *); +extern unsigned int ata66_slc90e66(ide_hwif_t *); +extern void ide_init_slc90e66(ide_hwif_t *); +#define PCI_SLC90E66 &pci_init_slc90e66 +#define ATA66_SLC90E66 &ata66_slc90e66 +#define INIT_SLC90E66 &ide_init_slc90e66 +#else +#define PCI_SLC90E66 NULL +#define ATA66_SLC90E66 NULL +#define INIT_SLC90E66 IDE_NO_DRIVER +#endif + +#ifdef CONFIG_BLK_DEV_SL82C105 +extern unsigned int pci_init_sl82c105(struct pci_dev *, const char *); +extern void dma_init_sl82c105(ide_hwif_t *, unsigned long); +extern void ide_init_sl82c105(ide_hwif_t *); +#define PCI_W82C105 &pci_init_sl82c105 +#define DMA_W82C105 &dma_init_sl82c105 +#define INIT_W82C105 &ide_init_sl82c105 +#else +#define PCI_W82C105 NULL +#define DMA_W82C105 NULL +#define INIT_W82C105 IDE_IGNORE +#endif + +#ifdef CONFIG_BLK_DEV_TRM290 +extern void ide_init_trm290(ide_hwif_t *); +#define INIT_TRM290 &ide_init_trm290 +#else +#define INIT_TRM290 IDE_IGNORE +#endif + +#ifdef CONFIG_BLK_DEV_VIA82CXXX +extern unsigned int pci_init_via82cxxx(struct pci_dev *, const char *); +extern unsigned int ata66_via82cxxx(ide_hwif_t *); +extern void ide_init_via82cxxx(ide_hwif_t *); +extern void ide_dmacapable_via82cxxx(ide_hwif_t *, unsigned long); +#define PCI_VIA82CXXX &pci_init_via82cxxx +#define ATA66_VIA82CXXX &ata66_via82cxxx +#define INIT_VIA82CXXX &ide_init_via82cxxx +#define DMA_VIA82CXXX &ide_dmacapable_via82cxxx +#else +#define PCI_VIA82CXXX NULL +#define ATA66_VIA82CXXX NULL +#define INIT_VIA82CXXX IDE_NO_DRIVER +#define DMA_VIA82CXXX NULL +#endif + +static ide_pci_device_t ide_pci_chipsets[] __initdata = { + {DEVID_PIIXa, "PIIX", FIXUP_PIIX, PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIXb, "PIIX", FIXUP_PIIX, PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_MPIIX, "MPIIX", FIXUP_PIIX, NULL, NULL, INIT_PIIX, NULL, {{0x6D,0x80,0x80}, {0x6F,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX3, "PIIX3", FIXUP_PIIX, PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4, "PIIX4", FIXUP_PIIX, PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4E, "PIIX4", FIXUP_PIIX, PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4E2, "PIIX4", FIXUP_PIIX, PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4U, "PIIX4", FIXUP_PIIX, PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4U2, "PIIX4", FIXUP_PIIX, PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4NX, "PIIX4", FIXUP_PIIX, PCI_PIIX, NULL, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4U3, "PIIX4", FIXUP_PIIX, PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4U4, "PIIX4", FIXUP_PIIX, PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4U5, "PIIX4", FIXUP_PIIX, PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4U6, "PIIX4", FIXUP_PIIX, PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4U7, "PIIX4", FIXUP_PIIX, PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_PIIX4U8, "PIIX4", FIXUP_PIIX, PCI_PIIX, ATA66_PIIX, INIT_PIIX, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_VIA_IDE, "VIA_IDE", NULL, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_MR_IDE, "VP_IDE", NULL, PCI_VIA82CXXX, ATA66_VIA82CXXX,INIT_VIA82CXXX, DMA_VIA82CXXX, {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, ON_BOARD, 0 }, + {DEVID_VP_IDE, "VP_IDE", NULL, PCI_VIA82CXXX, ATA66_VIA82CXXX,INIT_VIA82CXXX, DMA_VIA82CXXX, {{0x40,0x02,0x02}, {0x40,0x01,0x01}}, ON_BOARD, 0 }, +#ifdef CONFIG_PDC202XX_FORCE + {DEVID_PDC20246,"PDC20246", NULL, PCI_PDC202XX, NULL, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 16 }, + {DEVID_PDC20262,"PDC20262", NULL, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 48 }, + {DEVID_PDC20263,"PDC20263", NULL, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 48 }, + {DEVID_PDC20265,"PDC20265", FIXUP_PDC20265, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 48 }, + {DEVID_PDC20267,"PDC20267", NULL, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 48 }, +#else /* !CONFIG_PDC202XX_FORCE */ + {DEVID_PDC20246,"PDC20246", NULL, PCI_PDC202XX, NULL, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 16 }, + {DEVID_PDC20262,"PDC20262", NULL, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48 }, + {DEVID_PDC20263,"PDC20263", NULL, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48 }, + {DEVID_PDC20265,"PDC20265", FIXUP_PDC20265, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48 }, + {DEVID_PDC20267,"PDC20267", NULL, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x50,0x02,0x02}, {0x50,0x04,0x04}}, OFF_BOARD, 48 }, +#endif + {DEVID_PDC20268,"PDC20268", NULL, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + /* + * Promise used a different PCI ident for the raid card apparently + * to try and prevent Linux detecting it and using our own raid code. + * We want to detect it for the ataraid drivers, so we have to list + * both here.. + */ + {DEVID_PDC20268R,"PDC20270", FIXUP_PDC20270, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_PDC20269,"PDC20269", NULL, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_PDC20271,"PDC20271", NULL, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_PDC20275,"PDC20275", NULL, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_PDC20276,"PDC20276", NULL, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_PDC20277,"PDC20277", NULL, PCI_PDC202XX, ATA66_PDC202XX, INIT_PDC202XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_RZ1000, "RZ1000", NULL, NULL, NULL, INIT_RZ1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_RZ1001, "RZ1001", NULL, NULL, NULL, INIT_RZ1000, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_SAMURAI, "SAMURAI", NULL, NULL, NULL, INIT_SAMURAI, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_CMD640, "CMD640", NULL, NULL, NULL, IDE_IGNORE, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_NS87410, "NS87410", NULL, NULL, NULL, NULL, NULL, {{0x43,0x08,0x08}, {0x47,0x08,0x08}}, ON_BOARD, 0 }, + {DEVID_SIS5513, "SIS5513", FIXUP_SIS5513, PCI_SIS5513, ATA66_SIS5513, INIT_SIS5513, NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, ON_BOARD, 0 }, + {DEVID_CMD643, "CMD643", NULL, PCI_CMD64X, NULL, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_CMD646, "CMD646", NULL, PCI_CMD64X, NULL, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x51,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_CMD648, "CMD648", NULL, PCI_CMD64X, ATA66_CMD64X, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_CMD649, "CMD649", NULL, PCI_CMD64X, ATA66_CMD64X, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_CMD680, "CMD680", NULL, PCI_CMD64X, ATA66_CMD64X, INIT_CMD64X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_HT6565, "HT6565", NULL, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_OPTI621, "OPTI621", FIXUP_OPTI621, NULL, NULL, INIT_OPTI621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0 }, + {DEVID_OPTI621X,"OPTI621X", FIXUP_OPTI621, NULL, NULL, INIT_OPTI621, NULL, {{0x45,0x80,0x00}, {0x40,0x08,0x00}}, ON_BOARD, 0 }, + {DEVID_TRM290, "TRM290", NULL, NULL, NULL, INIT_TRM290, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_NS87415, "NS87415", NULL, NULL, NULL, INIT_NS87415, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_AEC6210, "AEC6210", NULL, PCI_AEC62XX, NULL, INIT_AEC62XX, DMA_AEC62XX, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, OFF_BOARD, 0 }, + {DEVID_AEC6260, "AEC6260", NULL, PCI_AEC62XX, ATA66_AEC62XX, INIT_AEC62XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, NEVER_BOARD, 0 }, + {DEVID_AEC6260R,"AEC6260R", NULL, PCI_AEC62XX, ATA66_AEC62XX, INIT_AEC62XX, NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, OFF_BOARD, 0 }, + {DEVID_AEC6280, "AEC6X80", FIXUP_AEC62XX, PCI_AEC62XX, ATA66_AEC62XX, INIT_AEC62XX, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, NEVER_BOARD, 0 }, + {DEVID_AEC6880, "AEC6X80R", FIXUP_AEC62XX, PCI_AEC62XX, ATA66_AEC62XX, INIT_AEC62XX, NULL, {{0x4a,0x02,0x02}, {0x4a,0x04,0x04}}, OFF_BOARD, 0 }, + {DEVID_W82C105, "W82C105", NULL, PCI_W82C105, NULL, INIT_W82C105, DMA_W82C105, {{0x40,0x01,0x01}, {0x40,0x10,0x10}}, ON_BOARD, 0 }, + {DEVID_UM8673F, "UM8673F", NULL, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_UM8886A, "UM8886A", NULL, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_UM8886BF,"UM8886BF", NULL, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_HPT34X, "HPT34X", FIXUP_HPT34X, PCI_HPT34X, NULL, INIT_HPT34X, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, NEVER_BOARD, 16 }, + {DEVID_HPT366, "HPT366", FIXUP_HPT366, PCI_HPT366, ATA66_HPT366, INIT_HPT366, DMA_HPT366, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 240 }, + {DEVID_HPT372, "HPT372A", NULL, PCI_HPT366, ATA66_HPT366, INIT_HPT366, DMA_HPT366, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_HPT302, "HPT302", NULL, PCI_HPT366, ATA66_HPT366, INIT_HPT366, DMA_HPT366, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_HPT371, "HPT371", NULL, PCI_HPT366, ATA66_HPT366, INIT_HPT366, DMA_HPT366, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_HPT374, "HPT374", FIXUP_HPT374, PCI_HPT366, ATA66_HPT366, INIT_HPT366, DMA_HPT366, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_ALI15X3, "ALI15X3", FIXUP_ALI15X3, PCI_ALI15X3, ATA66_ALI15X3, INIT_ALI15X3, DMA_ALI15X3, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_CY82C693,"CY82C693", FIXUP_CY82C693, PCI_CY82C693, NULL, INIT_CY82C693, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_HINT, "HINT_IDE", NULL, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_CS5530, "CS5530", NULL, PCI_CS5530, NULL, INIT_CS5530, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_AMD7401, "AMD7401", FIXUP_AMD74XX, NULL, NULL, NULL, DMA_AMD74XX, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 }, + {DEVID_AMD7409, "AMD7409", FIXUP_AMD74XX, PCI_AMD74XX, ATA66_AMD74XX, INIT_AMD74XX, DMA_AMD74XX, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 }, + {DEVID_AMD7411, "AMD7411", FIXUP_AMD74XX, PCI_AMD74XX, ATA66_AMD74XX, INIT_AMD74XX, DMA_AMD74XX, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 }, + {DEVID_AMD7441, "AMD7441", FIXUP_AMD74XX, PCI_AMD74XX, ATA66_AMD74XX, INIT_AMD74XX, DMA_AMD74XX, {{0x40,0x01,0x01}, {0x40,0x02,0x02}}, ON_BOARD, 0 }, + {DEVID_PDCADMA, "PDCADMA", NULL, PCI_PDCADMA, ATA66_PDCADMA, INIT_PDCADMA, DMA_PDCADMA, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, OFF_BOARD, 0 }, + {DEVID_SLC90E66,"SLC90E66", NULL, PCI_SLC90E66, ATA66_SLC90E66, INIT_SLC90E66, NULL, {{0x41,0x80,0x80}, {0x43,0x80,0x80}}, ON_BOARD, 0 }, + {DEVID_OSB4, "SvrWks OSB4", NULL, PCI_SVWKS, ATA66_SVWKS, INIT_SVWKS, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_CSB5, "SvrWks CSB5", NULL, PCI_SVWKS, ATA66_SVWKS, INIT_SVWKS, DMA_SVWKS, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_CSB6, "SvrWks CSB6", FIXUP_CSB6, PCI_SVWKS, ATA66_SVWKS, INIT_SVWKS, DMA_SVWKS, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }, + {DEVID_ITE8172G,"IT8172G", FIXUP_IT8172, PCI_IT8172, NULL, INIT_IT8172, NULL, {{0x00,0x00,0x00}, {0x40,0x00,0x01}}, ON_BOARD, 0 }, + {IDE_PCI_DEVID_NULL, "PCI_IDE", NULL, NULL, NULL, NULL, NULL, {{0x00,0x00,0x00}, {0x00,0x00,0x00}}, ON_BOARD, 0 }}; + +/* + * This allows offboard ide-pci cards the enable a BIOS, verify interrupt + * settings of split-mirror pci-config space, place chipset into init-mode, + * and/or preserve an interrupt if the card is not native ide support. + */ +static unsigned int __init ide_special_settings (struct pci_dev *dev, const char *name) +{ + switch(dev->device) { + case PCI_DEVICE_ID_TTI_HPT366: + case PCI_DEVICE_ID_TTI_HPT372: + case PCI_DEVICE_ID_TTI_HPT302: + case PCI_DEVICE_ID_TTI_HPT371: + case PCI_DEVICE_ID_TTI_HPT374: + case PCI_DEVICE_ID_PROMISE_20246: + case PCI_DEVICE_ID_PROMISE_20262: + case PCI_DEVICE_ID_PROMISE_20263: + case PCI_DEVICE_ID_PROMISE_20265: + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20268: + case PCI_DEVICE_ID_PROMISE_20268R: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20271: + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20276: + case PCI_DEVICE_ID_PROMISE_20277: + /* + * case PCI_DEVICE_ID_ARTOP_ATP850UF: + * same device ID value as PCI_DEVICE_ID_TTI_HPT372 + * case PCI_DEVICE_ID_ARTOP_ATP860: + * same device ID value as PCI_DEVICE_ID_TTI_HPT302 + * case PCI_DEVICE_ID_ARTOP_ATP860R: + * same device ID value as PCI_DEVICE_ID_TTI_HPT371 + * case PCI_DEVICE_ID_ARTOP_ATP865: + * same device ID value as PCI_DEVICE_ID_TTI_HPT374 + */ + case PCI_DEVICE_ID_ARTOP_ATP865R: + return dev->irq; + default: + break; + } + return 0; +} + +/* + * Match a PCI IDE port against an entry in ide_hwifs[], + * based on io_base port if possible. + */ +static ide_hwif_t __init *ide_match_hwif (unsigned long io_base, byte bootable, const char *name) +{ + int h; + ide_hwif_t *hwif; + + /* + * Look for a hwif with matching io_base specified using + * parameters to ide_setup(). + */ + for (h = 0; h < MAX_HWIFS; ++h) { + hwif = &ide_hwifs[h]; + if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) { + if (hwif->chipset == ide_generic) + return hwif; /* a perfect match */ + } + } + /* + * Look for a hwif with matching io_base default value. + * If chipset is "ide_unknown", then claim that hwif slot. + * Otherwise, some other chipset has already claimed it.. :( + */ + for (h = 0; h < MAX_HWIFS; ++h) { + hwif = &ide_hwifs[h]; + if (hwif->io_ports[IDE_DATA_OFFSET] == io_base) { + if (hwif->chipset == ide_unknown) + return hwif; /* match */ + printk("%s: port 0x%04lx already claimed by %s\n", + name, io_base, hwif->name); + return NULL; /* already claimed */ + } + } + /* + * Okay, there is no hwif matching our io_base, + * so we'll just claim an unassigned slot. + * Give preference to claiming other slots before claiming ide0/ide1, + * just in case there's another interface yet-to-be-scanned + * which uses ports 1f0/170 (the ide0/ide1 defaults). + * + * Unless there is a bootable card that does not use the standard + * ports 1f0/170 (the ide0/ide1 defaults). The (bootable) flag. + */ + if (bootable) { + for (h = 0; h < MAX_HWIFS; ++h) { + hwif = &ide_hwifs[h]; + if (hwif->chipset == ide_unknown) + return hwif; /* pick an unused entry */ + } + } else { + for (h = 2; h < MAX_HWIFS; ++h) { + hwif = ide_hwifs + h; + if (hwif->chipset == ide_unknown) + return hwif; /* pick an unused entry */ + } + } + for (h = 0; h < 2; ++h) { + hwif = ide_hwifs + h; + if (hwif->chipset == ide_unknown) + return hwif; /* pick an unused entry */ + } + printk("%s: too many IDE interfaces, no room in table\n", name); + return NULL; +} + +static int __init ide_setup_pci_baseregs (struct pci_dev *dev, const char *name) +{ + byte reg, progif = 0; + + /* + * Place both IDE interfaces into PCI "native" mode: + */ + if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || + (progif & 5) != 5) { + if ((progif & 0xa) != 0xa) { + printk("%s: device not capable of full " + "native PCI mode\n", name); + return 1; + } + printk("%s: placing both ports into native PCI mode\n", name); + (void) pci_write_config_byte(dev, PCI_CLASS_PROG, progif|5); + if (pci_read_config_byte(dev, PCI_CLASS_PROG, &progif) || (progif & 5) != 5) { + printk("%s: rewrite of PROGIF failed, wanted 0x%04x, got 0x%04x\n", name, progif|5, progif); + return 1; + } + } + /* + * Setup base registers for IDE command/control spaces for each interface: + */ + for (reg = 0; reg < 4; reg++) { + struct resource *res = dev->resource + reg; + if ((res->flags & IORESOURCE_IO) == 0) + continue; + if (!res->start) { + printk("%s: Missing I/O address #%d\n", name, reg); + return 1; + } + } + return 0; +} + +/* + * ide_setup_pci_device() looks at the primary/secondary interfaces + * on a PCI IDE device and, if they are enabled, prepares the IDE driver + * for use with them. This generic code works for most PCI chipsets. + * + * One thing that is not standardized is the location of the + * primary/secondary interface "enable/disable" bits. For chipsets that + * we "know" about, this information is in the ide_pci_device_t struct; + * for all other chipsets, we just assume both interfaces are enabled. + */ +void __init ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d) +{ + unsigned int port, at_least_one_hwif_enabled = 0, autodma = 0, pciirq = 0; + unsigned short pcicmd = 0, tried_config = 0; + byte tmp = 0; + ide_hwif_t *hwif, *mate = NULL; + unsigned int class_rev; + static int secondpdc = 0; + +#ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + autodma = 1; +#endif + + if (d->init_hwif == IDE_NO_DRIVER) { + printk(KERN_WARNING "%s: detected chipset, " + "but driver not compiled in!\n", d->name); + d->init_hwif = NULL; + } + + if (pci_enable_device(dev)) { + printk(KERN_WARNING "%s: (ide_setup_pci_device:) " + "Could not enable device.\n", d->name); + return; + } + +check_if_enabled: + if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd)) { + printk("%s: error accessing PCI regs\n", d->name); + return; + } + if (!(pcicmd & PCI_COMMAND_IO)) { /* is device disabled? */ + /* + * PnP BIOS was *supposed* to have set this device up for us, + * but we can do it ourselves, so long as the BIOS has assigned an IRQ + * (or possibly the device is using a "legacy header" for IRQs). + * Maybe the user deliberately *disabled* the device, + * but we'll eventually ignore it again if no drives respond. + */ + if (tried_config++ + || ide_setup_pci_baseregs(dev, d->name) + || pci_write_config_word(dev, PCI_COMMAND, pcicmd | PCI_COMMAND_IO)) { + printk("%s: device disabled (BIOS)\n", d->name); + return; + } + autodma = 0; /* default DMA off if we had to configure it here */ + goto check_if_enabled; + } + if (tried_config) + printk("%s: device enabled (Linux)\n", d->name); + + pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev); + class_rev &= 0xff; + printk("%s: chipset revision %d\n", d->name, class_rev); + + /* + * Can we trust the reported IRQ? + */ + pciirq = dev->irq; + + if ((dev->class & ~(0xfa)) != ((PCI_CLASS_STORAGE_IDE << 8) | 5)) { + printk("%s: not 100%% native mode: " + "will probe irqs later\n", d->name); + /* + * This allows offboard ide-pci cards the enable a BIOS, + * verify interrupt settings of split-mirror pci-config + * space, place chipset into init-mode, and/or preserve + * an interrupt if the card is not native ide support. + */ + pciirq = (d->init_chipset) ? d->init_chipset(dev, d->name) : ide_special_settings(dev, d->name); + } else if (tried_config) { + printk("%s: will probe irqs later\n", d->name); + pciirq = 0; + } else if (!pciirq) { + printk("%s: bad irq (%d): will probe later\n", d->name, pciirq); + pciirq = 0; + } else { + if (d->init_chipset) + (void) d->init_chipset(dev, d->name); +#ifdef __sparc__ + printk("%s: 100%% native mode on irq %s\n", + d->name, __irq_itoa(pciirq)); +#else + printk("%s: 100%% native mode on irq %d\n", d->name, pciirq); +#endif + } + + /* + * Set up the IDE ports + */ + for (port = 0; port <= 1; ++port) { + unsigned long base = 0, ctl = 0; + ide_pci_enablebit_t *e = &(d->enablebits[port]); + + /* + * If this is a Promise FakeRaid controller, + * the 2nd controller will be marked as + * disabled while it is actually there and enabled + * by the bios for raid purposes. + * Skip the normal "is it enabled" test for those. + */ + if ((IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20265)) && + (secondpdc++==1) && (port==1)) + goto controller_ok; + if ((IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20262)) && + (secondpdc++==1) && (port==1)) + goto controller_ok; + + if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) || + (tmp & e->mask) != e->val)) + continue; /* port not enabled */ +controller_ok: + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) && + (port) && (class_rev < 0x03)) + return; + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT302) && (port)) + return; + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_CSB6) && + (port) && (!(PCI_FUNC(dev->devfn) & 1))) + return; + if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE || + (dev->class & (port ? 4 : 1)) != 0) { + ctl = dev->resource[(2*port)+1].start; + base = dev->resource[2*port].start; + if (!(ctl & PCI_BASE_ADDRESS_IO_MASK) || + !(base & PCI_BASE_ADDRESS_IO_MASK)) { + printk("%s: IO baseregs (BIOS) are reported " + "as MEM, report to " + ".\n", d->name); +#if 0 + /* + * FIXME! This really should check that + * it really gets the IO/MEM part right! + */ + continue; +#endif + } + } + if ((ctl && !base) || (base && !ctl)) { + printk("%s: inconsistent baseregs (BIOS) " + "for port %d, skipping\n", d->name, port); + continue; + } + if (!ctl) + ctl = port ? 0x374 : 0x3f4; /* use default value */ + if (!base) + base = port ? 0x170 : 0x1f0; /* use default value */ + if ((hwif = ide_match_hwif(base, d->bootable, d->name)) == NULL) + continue; /* no room in ide_hwifs[] */ + if (hwif->io_ports[IDE_DATA_OFFSET] != base) { + ide_init_hwif_ports(&hwif->hw, base, (ctl | 2), NULL); + memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports)); + hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET]; + } + hwif->chipset = ide_pci; + hwif->pci_dev = dev; + hwif->pci_devid = d->devid; + hwif->channel = port; + if (!hwif->irq) + hwif->irq = pciirq; + if (mate) { + hwif->mate = mate; + mate->mate = hwif; + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210)) { + hwif->serialized = 1; + mate->serialized = 1; + } + } + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886A) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886BF) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8673F)) { + hwif->irq = hwif->channel ? 15 : 14; + goto bypass_umc_dma; + } + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_MPIIX)) + goto bypass_piix_dma; + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDCADMA)) + goto bypass_legacy_dma; + if (hwif->udma_four) { + printk("%s: ATA-66/100 forced bit set (WARNING)!!\n", + d->name); + } else { + hwif->udma_four = (d->ata66_check) ? d->ata66_check(hwif) : 0; + } +#ifdef CONFIG_BLK_DEV_IDEDMA + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_SIS5513) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6260) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PIIX4NX) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_VIA_IDE) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_MR_IDE) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_VP_IDE)) + autodma = 0; + if (autodma) + hwif->autodma = 1; + + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20246) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20262) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20263) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20265) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20267) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20268) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20268R) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20269) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20271) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20275) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20276) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_PDC20277) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6210) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6260) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6260R) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6280) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_AEC6880) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT34X) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT366) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT372) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT302) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT371) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_HPT374) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_CS5530) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_CY82C693) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD646) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD648) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD649) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_CMD680) || + IDE_PCI_DEVID_EQ(d->devid, DEVID_OSB4) || + ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE && (dev->class & 0x80))) { + unsigned long dma_base = ide_get_or_set_dma_base(hwif, (!mate && d->extra) ? d->extra : 0, d->name); + if (dma_base && !(pcicmd & PCI_COMMAND_MASTER)) { + /* + * Set up BM-DMA capability (PnP BIOS should have done this) + */ + if (!IDE_PCI_DEVID_EQ(d->devid, DEVID_CS5530)) + hwif->autodma = 0; /* default DMA off if we had to configure it here */ + (void) pci_write_config_word(dev, PCI_COMMAND, pcicmd | PCI_COMMAND_MASTER); + if (pci_read_config_word(dev, PCI_COMMAND, &pcicmd) || !(pcicmd & PCI_COMMAND_MASTER)) { + printk("%s: %s error updating PCICMD\n", hwif->name, d->name); + dma_base = 0; + } + } + if (dma_base) { + if (d->dma_init) { + d->dma_init(hwif, dma_base); + } else { + ide_setup_dma(hwif, dma_base, 8); + } + } else { + printk("%s: %s Bus-Master DMA disabled (BIOS)\n", hwif->name, d->name); + } + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ +bypass_legacy_dma: +bypass_piix_dma: +bypass_umc_dma: + if (d->init_hwif) /* Call chipset-specific routine for each enabled hwif */ + d->init_hwif(hwif); + mate = hwif; + at_least_one_hwif_enabled = 1; + } + if (!at_least_one_hwif_enabled) + printk("%s: neither IDE port enabled (BIOS)\n", d->name); +} + +/* + * ide_scan_pcibus() gets invoked at boot time from ide.c. + * It finds all PCI IDE controllers and calls ide_setup_pci_device for them. + */ +void __init ide_scan_pcidev (struct pci_dev *dev) +{ + ide_pci_devid_t devid; + ide_pci_device_t *d; + + devid.vid = dev->vendor; + devid.did = dev->device; + for (d = ide_pci_chipsets; + d->devid.vid && !IDE_PCI_DEVID_EQ(d->devid, devid); ++d); + + if (d->init_hwif == IDE_IGNORE) + printk("%s: ignored by ide_scan_pci_device() " + "(uses own driver)\n", d->name); + else if (d->fixup_device) + d->fixup_device(dev, d); +#if 0 + else if (((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) && + (!(PCI_FUNC(dev->devfn) & 1))) + return; +#endif + else if (IDE_PCI_DEVID_EQ(d->devid, DEVID_UM8886A) && + (!(PCI_FUNC(dev->devfn) & 1))) + return; /* UM8886A/BF pair */ + else if (!IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL) || + (dev->class >> 8) == PCI_CLASS_STORAGE_IDE) { + if (IDE_PCI_DEVID_EQ(d->devid, IDE_PCI_DEVID_NULL)) + printk("%s: unknown IDE controller on PCI bus " + "%02x device %02x, VID=%04x, DID=%04x\n", + d->name, dev->bus->number, dev->devfn, + devid.vid, devid.did); + else + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); + } +} + +void __init ide_scan_pcibus (int scan_direction) +{ + struct pci_dev *dev; + + if (!scan_direction) { + pci_for_each_dev(dev) { + ide_scan_pcidev(dev); + } + } else { + pci_for_each_dev_reverse(dev) { + ide_scan_pcidev(dev); + } + } +} diff -Nru a/drivers/ide/ide-pmac.c b/drivers/ide/ide-pmac.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-pmac.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,1724 @@ +/* + * linux/drivers/ide/ide-pmac.c + * + * Support for IDE interfaces on PowerMacs. + * These IDE interfaces are memory-mapped and have a DBDMA channel + * for doing DMA. + * + * Copyright (C) 1998-2001 Paul Mackerras & Ben. Herrenschmidt + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Some code taken from drivers/ide/ide-dma.c: + * + * Copyright (c) 1995-1998 Mark Lord + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_PMAC_PBOOK +#include +#include +#endif +#include "ide_modes.h" + +extern void ide_do_request(ide_hwgroup_t *hwgroup, int masked_irq); + +#define IDE_PMAC_DEBUG + +#define DMA_WAIT_TIMEOUT 500 + +struct pmac_ide_hwif { + ide_ioreg_t regbase; + int irq; + int kind; + int aapl_bus_id; + struct device_node* node; + u32 timings[2]; +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + /* Those fields are duplicating what is in hwif. We currently + * can't use the hwif ones because of some assumptions that are + * beeing done by the generic code about the kind of dma controller + * and format of the dma table. This will have to be fixed though. + */ + volatile struct dbdma_regs* dma_regs; + struct dbdma_cmd* dma_table_cpu; + dma_addr_t dma_table_dma; + struct scatterlist* sg_table; + int sg_nents; + int sg_dma_direction; +#endif + +} pmac_ide[MAX_HWIFS] __pmacdata; + +static int pmac_ide_count; + +enum { + controller_ohare, /* OHare based */ + controller_heathrow, /* Heathrow/Paddington */ + controller_kl_ata3, /* KeyLargo ATA-3 */ + controller_kl_ata4, /* KeyLargo ATA-4 */ + controller_kl_ata4_80 /* KeyLargo ATA-4 with 80 conductor cable */ +}; + +/* + * Extra registers, both 32-bit little-endian + */ +#define IDE_TIMING_CONFIG 0x200 +#define IDE_INTERRUPT 0x300 + +/* + * Timing configuration register definitions + */ + +/* Number of IDE_SYSCLK_NS ticks, argument is in nanoseconds */ +#define SYSCLK_TICKS(t) (((t) + IDE_SYSCLK_NS - 1) / IDE_SYSCLK_NS) +#define SYSCLK_TICKS_66(t) (((t) + IDE_SYSCLK_66_NS - 1) / IDE_SYSCLK_66_NS) +#define IDE_SYSCLK_NS 30 /* 33Mhz cell */ +#define IDE_SYSCLK_66_NS 15 /* 66Mhz cell */ + +/* 66Mhz cell, found in KeyLargo. Can do ultra mode 0 to 2 on + * 40 connector cable and to 4 on 80 connector one. + * Clock unit is 15ns (66Mhz) + * + * 3 Values can be programmed: + * - Write data setup, which appears to match the cycle time. They + * also call it DIOW setup. + * - Ready to pause time (from spec) + * - Address setup. That one is weird. I don't see where exactly + * it fits in UDMA cycles, I got it's name from an obscure piece + * of commented out code in Darwin. They leave it to 0, we do as + * well, despite a comment that would lead to think it has a + * min value of 45ns. + * Apple also add 60ns to the write data setup (or cycle time ?) on + * reads. I can't explain that, I tried it and it broke everything + * here. + */ +#define TR_66_UDMA_MASK 0xfff00000 +#define TR_66_UDMA_EN 0x00100000 /* Enable Ultra mode for DMA */ +#define TR_66_UDMA_ADDRSETUP_MASK 0xe0000000 /* Address setup */ +#define TR_66_UDMA_ADDRSETUP_SHIFT 29 +#define TR_66_UDMA_RDY2PAUS_MASK 0x1e000000 /* Ready 2 pause time */ +#define TR_66_UDMA_RDY2PAUS_SHIFT 25 +#define TR_66_UDMA_WRDATASETUP_MASK 0x01e00000 /* Write data setup time */ +#define TR_66_UDMA_WRDATASETUP_SHIFT 21 +#define TR_66_MDMA_MASK 0x000ffc00 +#define TR_66_MDMA_RECOVERY_MASK 0x000f8000 +#define TR_66_MDMA_RECOVERY_SHIFT 15 +#define TR_66_MDMA_ACCESS_MASK 0x00007c00 +#define TR_66_MDMA_ACCESS_SHIFT 10 +#define TR_66_PIO_MASK 0x000003ff +#define TR_66_PIO_RECOVERY_MASK 0x000003e0 +#define TR_66_PIO_RECOVERY_SHIFT 5 +#define TR_66_PIO_ACCESS_MASK 0x0000001f +#define TR_66_PIO_ACCESS_SHIFT 0 + +/* 33Mhz cell, found in OHare, Heathrow (& Paddington) and KeyLargo + * Can do pio & mdma modes, clock unit is 30ns (33Mhz) + * + * The access time and recovery time can be programmed. Some older + * Darwin code base limit OHare to 150ns cycle time. I decided to do + * the same here fore safety against broken old hardware ;) + * The HalfTick bit, when set, adds half a clock (15ns) to the access + * time and removes one from recovery. It's not supported on KeyLargo + * implementation afaik. The E bit appears to be set for PIO mode 0 and + * is used to reach long timings used in this mode. + */ +#define TR_33_MDMA_MASK 0x003ff800 +#define TR_33_MDMA_RECOVERY_MASK 0x001f0000 +#define TR_33_MDMA_RECOVERY_SHIFT 16 +#define TR_33_MDMA_ACCESS_MASK 0x0000f800 +#define TR_33_MDMA_ACCESS_SHIFT 11 +#define TR_33_MDMA_HALFTICK 0x00200000 +#define TR_33_PIO_MASK 0x000007ff +#define TR_33_PIO_E 0x00000400 +#define TR_33_PIO_RECOVERY_MASK 0x000003e0 +#define TR_33_PIO_RECOVERY_SHIFT 5 +#define TR_33_PIO_ACCESS_MASK 0x0000001f +#define TR_33_PIO_ACCESS_SHIFT 0 + +/* + * Interrupt register definitions + */ +#define IDE_INTR_DMA 0x80000000 +#define IDE_INTR_DEVICE 0x40000000 + +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + +/* Rounded Multiword DMA timings + * + * I gave up finding a generic formula for all controller + * types and instead, built tables based on timing values + * used by Apple in Darwin's implementation. + */ +struct mdma_timings_t { + int accessTime; + int recoveryTime; + int cycleTime; +}; + +struct mdma_timings_t mdma_timings_33[] __pmacdata = +{ + { 240, 240, 480 }, + { 180, 180, 360 }, + { 135, 135, 270 }, + { 120, 120, 240 }, + { 105, 105, 210 }, + { 90, 90, 180 }, + { 75, 75, 150 }, + { 75, 45, 120 }, + { 0, 0, 0 } +}; + +struct mdma_timings_t mdma_timings_33k[] __pmacdata = +{ + { 240, 240, 480 }, + { 180, 180, 360 }, + { 150, 150, 300 }, + { 120, 120, 240 }, + { 90, 120, 210 }, + { 90, 90, 180 }, + { 90, 60, 150 }, + { 90, 30, 120 }, + { 0, 0, 0 } +}; + +struct mdma_timings_t mdma_timings_66[] __pmacdata = +{ + { 240, 240, 480 }, + { 180, 180, 360 }, + { 135, 135, 270 }, + { 120, 120, 240 }, + { 105, 105, 210 }, + { 90, 90, 180 }, + { 90, 75, 165 }, + { 75, 45, 120 }, + { 0, 0, 0 } +}; + +/* Ultra DMA timings (rounded) */ +struct { + int addrSetup; /* ??? */ + int rdy2pause; + int wrDataSetup; +} udma_timings[] __pmacdata = +{ + { 0, 180, 120 }, /* Mode 0 */ + { 0, 150, 90 }, /* 1 */ + { 0, 120, 60 }, /* 2 */ + { 0, 90, 45 }, /* 3 */ + { 0, 90, 30 } /* 4 */ +}; + +/* allow up to 256 DBDMA commands per xfer */ +#define MAX_DCMDS 256 + +/* Wait 2s for disk to answer on IDE bus after + * enable operation. + * NOTE: There is at least one case I know of a disk that needs about 10sec + * before anwering on the bus. I beleive we could add a kernel command + * line arg to override this delay for such cases. + */ +#define IDE_WAKEUP_DELAY_MS 2000 + +static void pmac_ide_setup_dma(struct device_node *np, int ix); +static int pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive); +static int pmac_ide_build_dmatable(ide_drive_t *drive, int ix, int wr); +static int pmac_ide_tune_chipset(ide_drive_t *drive, byte speed); +static void pmac_ide_tuneproc(ide_drive_t *drive, byte pio); +static void pmac_ide_selectproc(ide_drive_t *drive); + +#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ + +#ifdef CONFIG_PMAC_PBOOK +static int idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when); +struct pmu_sleep_notifier idepmac_sleep_notifier = { + idepmac_notify_sleep, SLEEP_LEVEL_BLOCK, +}; +#endif /* CONFIG_PMAC_PBOOK */ + +static int pmac_ide_notify_reboot(struct notifier_block *, unsigned long, void *); +static struct notifier_block pmac_ide_reboot_notifier = { + pmac_ide_notify_reboot, + NULL, + 0 +}; + +static int __pmac +pmac_ide_find(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + ide_ioreg_t base; + int i; + + for (i=0; iio_ports[0]) + return i; + } + return -1; +} + +/* + * N.B. this can't be an initfunc, because the media-bay task can + * call ide_[un]register at any time. + */ +void __pmac +pmac_ide_init_hwif_ports(hw_regs_t *hw, + ide_ioreg_t data_port, ide_ioreg_t ctrl_port, + int *irq) +{ + int i, ix; + + if (data_port == 0) + return; + + for (ix = 0; ix < MAX_HWIFS; ++ix) + if (data_port == pmac_ide[ix].regbase) + break; + + if (ix >= MAX_HWIFS) { + /* Probably a PCI interface... */ + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; ++i) + hw->io_ports[i] = data_port + i - IDE_DATA_OFFSET; + hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port; + return; + } + + for (i = 0; i < 8; ++i) + hw->io_ports[i] = data_port + i * 0x10; + hw->io_ports[8] = data_port + 0x160; + + if (irq != NULL) + *irq = pmac_ide[ix].irq; + + ide_hwifs[ix].tuneproc = pmac_ide_tuneproc; + ide_hwifs[ix].selectproc = pmac_ide_selectproc; + ide_hwifs[ix].speedproc = &pmac_ide_tune_chipset; + if (pmac_ide[ix].dma_regs && pmac_ide[ix].dma_table_cpu) { + ide_hwifs[ix].dmaproc = &pmac_ide_dmaproc; +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO + if (!noautodma) + ide_hwifs[ix].autodma = 1; +#endif + } +} + +#if 0 +/* This one could be later extended to handle CMD IDE and be used by some kind + * of /proc interface. I want to be able to get the devicetree path of a block + * device for yaboot configuration + */ +struct device_node* +pmac_ide_get_devnode(ide_drive_t *drive) +{ + int i = pmac_ide_find(drive); + if (i < 0) + return NULL; + return pmac_ide[i].node; +} +#endif + +/* Setup timings for the selected drive (master/slave). I still need to verify if this + * is enough, I beleive selectproc will be called whenever an IDE command is started, + * but... */ +static void __pmac +pmac_ide_selectproc(ide_drive_t *drive) +{ + int i = pmac_ide_find(drive); + if (i < 0) + return; + + if (drive->select.b.unit & 0x01) + out_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE), + pmac_ide[i].timings[1]); + else + out_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE), + pmac_ide[i].timings[0]); + (void)in_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE)); +} + + +static int __pmac +pmac_ide_do_setfeature(ide_drive_t *drive, byte command) +{ + int result = 1; + unsigned long flags; + ide_hwif_t *hwif = HWIF(drive); + + disable_irq(hwif->irq); /* disable_irq_nosync ?? */ + udelay(1); + SELECT_DRIVE(HWIF(drive), drive); + SELECT_MASK(HWIF(drive), drive, 0); + udelay(1); + (void)GET_STAT(); /* Get rid of pending error state */ + if(wait_for_ready(drive, 2000)) { /* Timeout bumped for some powerbooks */ + printk(KERN_ERR "pmac_ide_do_setfeature disk not ready before SET_FEATURE!\n"); + goto out; + } + udelay(10); + OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG); + OUT_BYTE(command, IDE_NSECTOR_REG); + OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG); + OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG); + udelay(1); + local_irq_set(flags); + result = wait_for_ready(drive, 2000); /* Timeout bumped for some powerbooks */ + local_irq_restore(flags); + OUT_BYTE(drive->ctl, IDE_CONTROL_REG); + if (result) + printk(KERN_ERR "pmac_ide_do_setfeature disk not ready after SET_FEATURE !\n"); +out: + SELECT_MASK(HWIF(drive), drive, 0); + if (result == 0) { + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_mword &= ~0x0F00; + drive->id->dma_1word &= ~0x0F00; + switch(command) { + case XFER_UDMA_7: drive->id->dma_ultra |= 0x8080; break; + case XFER_UDMA_6: drive->id->dma_ultra |= 0x4040; break; + case XFER_UDMA_5: drive->id->dma_ultra |= 0x2020; break; + case XFER_UDMA_4: drive->id->dma_ultra |= 0x1010; break; + case XFER_UDMA_3: drive->id->dma_ultra |= 0x0808; break; + case XFER_UDMA_2: drive->id->dma_ultra |= 0x0404; break; + case XFER_UDMA_1: drive->id->dma_ultra |= 0x0202; break; + case XFER_UDMA_0: drive->id->dma_ultra |= 0x0101; break; + case XFER_MW_DMA_2: drive->id->dma_mword |= 0x0404; break; + case XFER_MW_DMA_1: drive->id->dma_mword |= 0x0202; break; + case XFER_MW_DMA_0: drive->id->dma_mword |= 0x0101; break; + case XFER_SW_DMA_2: drive->id->dma_1word |= 0x0404; break; + case XFER_SW_DMA_1: drive->id->dma_1word |= 0x0202; break; + case XFER_SW_DMA_0: drive->id->dma_1word |= 0x0101; break; + default: break; + } + } + enable_irq(hwif->irq); + return result; +} + +/* Calculate PIO timings */ +static void __pmac +pmac_ide_tuneproc(ide_drive_t *drive, byte pio) +{ + ide_pio_data_t d; + int i; + u32 *timings; + unsigned accessTicks, recTicks; + unsigned accessTime, recTime; + + i = pmac_ide_find(drive); + if (i < 0) + return; + + pio = ide_get_best_pio_mode(drive, pio, 4, &d); + accessTicks = SYSCLK_TICKS(ide_pio_timings[pio].active_time); + if (drive->select.b.unit & 0x01) + timings = &pmac_ide[i].timings[1]; + else + timings = &pmac_ide[i].timings[0]; + + recTime = d.cycle_time - ide_pio_timings[pio].active_time + - ide_pio_timings[pio].setup_time; + recTime = max(recTime, 150U); + accessTime = ide_pio_timings[pio].active_time; + accessTime = max(accessTime, 150U); + if (pmac_ide[i].kind == controller_kl_ata4 || + pmac_ide[i].kind == controller_kl_ata4_80) { + /* 66Mhz cell */ + accessTicks = SYSCLK_TICKS_66(accessTime); + accessTicks = min(accessTicks, 0x1fU); + recTicks = SYSCLK_TICKS_66(recTime); + recTicks = min(recTicks, 0x1fU); + *timings = ((*timings) & ~TR_66_PIO_MASK) | + (accessTicks << TR_66_PIO_ACCESS_SHIFT) | + (recTicks << TR_66_PIO_RECOVERY_SHIFT); + } else { + /* 33Mhz cell */ + int ebit = 0; + accessTicks = SYSCLK_TICKS(accessTime); + accessTicks = min(accessTicks, 0x1fU); + accessTicks = max(accessTicks, 4U); + recTicks = SYSCLK_TICKS(recTime); + recTicks = min(recTicks, 0x1fU); + recTicks = max(recTicks, 5U) - 4; + if (recTicks > 9) { + recTicks--; /* guess, but it's only for PIO0, so... */ + ebit = 1; + } + *timings = ((*timings) & ~TR_33_PIO_MASK) | + (accessTicks << TR_33_PIO_ACCESS_SHIFT) | + (recTicks << TR_33_PIO_RECOVERY_SHIFT); + if (ebit) + *timings |= TR_33_PIO_E; + } + +#ifdef IDE_PMAC_DEBUG + printk(KERN_ERR "ide_pmac: Set PIO timing for mode %d, reg: 0x%08x\n", + pio, *timings); +#endif + + if (drive->select.all == IN_BYTE(IDE_SELECT_REG)) + pmac_ide_selectproc(drive); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC +static int __pmac +set_timings_udma(u32 *timings, byte speed) +{ + unsigned rdyToPauseTicks, wrDataSetupTicks, addrTicks; + + rdyToPauseTicks = SYSCLK_TICKS_66(udma_timings[speed & 0xf].rdy2pause); + wrDataSetupTicks = SYSCLK_TICKS_66(udma_timings[speed & 0xf].wrDataSetup); + addrTicks = SYSCLK_TICKS_66(udma_timings[speed & 0xf].addrSetup); + + *timings = ((*timings) & ~(TR_66_UDMA_MASK | TR_66_MDMA_MASK)) | + (wrDataSetupTicks << TR_66_UDMA_WRDATASETUP_SHIFT) | + (rdyToPauseTicks << TR_66_UDMA_RDY2PAUS_SHIFT) | + (addrTicks < cycleTime) + cycleTime = drive_cycle_time; + /* OHare limits according to some old Apple sources */ + if ((intf_type == controller_ohare) && (cycleTime < 150)) + cycleTime = 150; + /* Get the proper timing array for this controller */ + switch(intf_type) { + case controller_kl_ata4: + case controller_kl_ata4_80: + tm = mdma_timings_66; + break; + case controller_kl_ata3: + tm = mdma_timings_33k; + break; + default: + tm = mdma_timings_33; + break; + } + /* Lookup matching access & recovery times */ + i = -1; + for (;;) { + if (tm[i+1].cycleTime < cycleTime) + break; + i++; + } + if (i < 0) + return -1; + cycleTime = tm[i].cycleTime; + accessTime = tm[i].accessTime; + recTime = tm[i].recoveryTime; + +#ifdef IDE_PMAC_DEBUG + printk(KERN_ERR "ide_pmac: MDMA, cycleTime: %d, accessTime: %d, recTime: %d\n", + cycleTime, accessTime, recTime); +#endif + if (intf_type == controller_kl_ata4 || intf_type == controller_kl_ata4_80) { + /* 66Mhz cell */ + accessTicks = SYSCLK_TICKS_66(accessTime); + accessTicks = min(accessTicks, 0x1fU); + accessTicks = max(accessTicks, 0x1U); + recTicks = SYSCLK_TICKS_66(recTime); + recTicks = min(recTicks, 0x1fU); + recTicks = max(recTicks, 0x3U); + /* Clear out mdma bits and disable udma */ + *timings = ((*timings) & ~(TR_66_MDMA_MASK | TR_66_UDMA_MASK)) | + (accessTicks << TR_66_MDMA_ACCESS_SHIFT) | + (recTicks << TR_66_MDMA_RECOVERY_SHIFT); + } else if (intf_type == controller_kl_ata3) { + /* 33Mhz cell on KeyLargo */ + accessTicks = SYSCLK_TICKS(accessTime); + accessTicks = max(accessTicks, 1U); + accessTicks = min(accessTicks, 0x1fU); + accessTime = accessTicks * IDE_SYSCLK_NS; + recTicks = SYSCLK_TICKS(recTime); + recTicks = max(recTicks, 1U); + recTicks = min(recTicks, 0x1fU); + *timings = ((*timings) & ~TR_33_MDMA_MASK) | + (accessTicks << TR_33_MDMA_ACCESS_SHIFT) | + (recTicks << TR_33_MDMA_RECOVERY_SHIFT); + } else { + /* 33Mhz cell on others */ + int halfTick = 0; + int origAccessTime = accessTime; + int origRecTime = recTime; + + accessTicks = SYSCLK_TICKS(accessTime); + accessTicks = max(accessTicks, 1U); + accessTicks = min(accessTicks, 0x1fU); + accessTime = accessTicks * IDE_SYSCLK_NS; + recTicks = SYSCLK_TICKS(recTime); + recTicks = max(recTicks, 2U) - 1; + recTicks = min(recTicks, 0x1fU); + recTime = (recTicks + 1) * IDE_SYSCLK_NS; + if ((accessTicks > 1) && + ((accessTime - IDE_SYSCLK_NS/2) >= origAccessTime) && + ((recTime - IDE_SYSCLK_NS/2) >= origRecTime)) { + halfTick = 1; + accessTicks--; + } + *timings = ((*timings) & ~TR_33_MDMA_MASK) | + (accessTicks << TR_33_MDMA_ACCESS_SHIFT) | + (recTicks << TR_33_MDMA_RECOVERY_SHIFT); + if (halfTick) + *timings |= TR_33_MDMA_HALFTICK; + } +#ifdef IDE_PMAC_DEBUG + printk(KERN_ERR "ide_pmac: Set MDMA timing for mode %d, reg: 0x%08x\n", + speed & 0xf, *timings); +#endif + return 0; +} +#endif /* #ifdef CONFIG_BLK_DEV_IDEDMA_PMAC */ + +/* You may notice we don't use this function on normal operation, + * our, normal mdma function is supposed to be more precise + */ +static int __pmac +pmac_ide_tune_chipset (ide_drive_t *drive, byte speed) +{ + int intf = pmac_ide_find(drive); + int unit = (drive->select.b.unit & 0x01); + int ret = 0; + u32 *timings; + + if (intf < 0) + return 1; + + timings = &pmac_ide[intf].timings[unit]; + + switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + case XFER_UDMA_4: + case XFER_UDMA_3: + if (pmac_ide[intf].kind != controller_kl_ata4_80) + return 1; + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + if (pmac_ide[intf].kind != controller_kl_ata4 && + pmac_ide[intf].kind != controller_kl_ata4_80) + return 1; + ret = set_timings_udma(timings, speed); + break; + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + ret = set_timings_mdma(pmac_ide[intf].kind, timings, speed, 0); + break; + case XFER_SW_DMA_2: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + return 1; +#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_1: + case XFER_PIO_0: + pmac_ide_tuneproc(drive, speed & 0x07); + break; + default: + ret = 1; + } + if (ret) + return ret; + + ret = pmac_ide_do_setfeature(drive, speed); + if (ret) + return ret; + + pmac_ide_selectproc(drive); + drive->current_speed = speed; + + return 0; +} + +static void __pmac +sanitize_timings(int i) +{ + unsigned value; + + switch(pmac_ide[i].kind) { + case controller_kl_ata4: + case controller_kl_ata4_80: + value = 0x0008438c; + break; + case controller_kl_ata3: + value = 0x00084526; + break; + case controller_heathrow: + case controller_ohare: + default: + value = 0x00074526; + break; + } + pmac_ide[i].timings[0] = pmac_ide[i].timings[1] = value; +} + +ide_ioreg_t __pmac +pmac_ide_get_base(int index) +{ + return pmac_ide[index].regbase; +} + +int __pmac +pmac_ide_check_base(ide_ioreg_t base) +{ + int ix; + + for (ix = 0; ix < MAX_HWIFS; ++ix) + if (base == pmac_ide[ix].regbase) + return ix; + return -1; +} + +int __pmac +pmac_ide_get_irq(ide_ioreg_t base) +{ + int ix; + + for (ix = 0; ix < MAX_HWIFS; ++ix) + if (base == pmac_ide[ix].regbase) + return pmac_ide[ix].irq; + return 0; +} + +static int ide_majors[] __pmacdata = { 3, 22, 33, 34, 56, 57 }; + +kdev_t __init +pmac_find_ide_boot(char *bootdevice, int n) +{ + int i; + + /* + * Look through the list of IDE interfaces for this one. + */ + for (i = 0; i < pmac_ide_count; ++i) { + char *name; + if (!pmac_ide[i].node || !pmac_ide[i].node->full_name) + continue; + name = pmac_ide[i].node->full_name; + if (memcmp(name, bootdevice, n) == 0 && name[n] == 0) { + /* XXX should cope with the 2nd drive as well... */ + return MKDEV(ide_majors[i], 0); + } + } + + return 0; +} + +void __init +pmac_ide_probe(void) +{ + struct device_node *np; + int i; + struct device_node *atas; + struct device_node *p, **pp, *removables, **rp; + unsigned long base; + int irq, big_delay; + ide_hwif_t *hwif; + + if (_machine != _MACH_Pmac) + return; + pp = &atas; + rp = &removables; + p = find_devices("ATA"); + if (p == NULL) + p = find_devices("IDE"); + if (p == NULL) + p = find_type_devices("ide"); + if (p == NULL) + p = find_type_devices("ata"); + /* Move removable devices such as the media-bay CDROM + on the PB3400 to the end of the list. */ + for (; p != NULL; p = p->next) { + if (p->parent && p->parent->type + && strcasecmp(p->parent->type, "media-bay") == 0) { + *rp = p; + rp = &p->next; + } else { + *pp = p; + pp = &p->next; + } + } + *rp = NULL; + *pp = removables; + big_delay = 0; + + for (i = 0, np = atas; i < MAX_HWIFS && np != NULL; np = np->next) { + struct device_node *tp; + struct pmac_ide_hwif* pmhw; + int *bidp; + int in_bay = 0; + u8 pbus, pid; + struct pci_dev *pdev = NULL; + + /* + * If this node is not under a mac-io or dbdma node, + * leave it to the generic PCI driver. + */ + for (tp = np->parent; tp != 0; tp = tp->parent) + if (tp->type && (strcmp(tp->type, "mac-io") == 0 + || strcmp(tp->type, "dbdma") == 0)) + break; + if (tp == 0) + continue; + + if (np->n_addrs == 0) { + printk(KERN_WARNING "ide: no address for device %s\n", + np->full_name); + continue; + } + + /* We need to find the pci_dev of the mac-io holding the + * IDE interface + */ + if (pci_device_from_OF_node(tp, &pbus, &pid) == 0) + pdev = pci_find_slot(pbus, pid); + if (pdev == NULL) + printk(KERN_WARNING "ide: no PCI host for device %s, DMA disabled\n", + np->full_name); + + /* + * If this slot is taken (e.g. by ide-pci.c) try the next one. + */ + while (i < MAX_HWIFS + && ide_hwifs[i].io_ports[IDE_DATA_OFFSET] != 0) + ++i; + if (i >= MAX_HWIFS) + break; + pmhw = &pmac_ide[i]; + + /* + * Some older OFs have bogus sizes, causing request_OF_resource + * to fail. We fix them up here + */ + if (np->addrs[0].size > 0x1000) + np->addrs[0].size = 0x1000; + if (np->n_addrs > 1 && np->addrs[1].size > 0x100) + np->addrs[1].size = 0x100; + + if (request_OF_resource(np, 0, " (mac-io IDE IO)") == NULL) { + printk(KERN_ERR "ide-pmac(%s): can't request IO resource !\n", np->name); + continue; + } + + base = (unsigned long) ioremap(np->addrs[0].address, 0x400) - _IO_BASE; + + /* XXX This is bogus. Should be fixed in the registry by checking + the kind of host interrupt controller, a bit like gatwick + fixes in irq.c + */ + if (np->n_intrs == 0) { + printk(KERN_WARNING "ide: no intrs for device %s, using 13\n", + np->full_name); + irq = 13; + } else { + irq = np->intrs[0].line; + } + pmhw->regbase = base; + pmhw->irq = irq; + pmhw->node = np; + if (device_is_compatible(np, "keylargo-ata")) { + if (strcmp(np->name, "ata-4") == 0) + pmhw->kind = controller_kl_ata4; + else + pmhw->kind = controller_kl_ata3; + } else if (device_is_compatible(np, "heathrow-ata")) + pmhw->kind = controller_heathrow; + else + pmhw->kind = controller_ohare; + + bidp = (int *)get_property(np, "AAPL,bus-id", NULL); + pmhw->aapl_bus_id = bidp ? *bidp : 0; + + if (pmhw->kind == controller_kl_ata4) { + char* cable = get_property(np, "cable-type", NULL); + if (cable && !strncmp(cable, "80-", 3)) + pmhw->kind = controller_kl_ata4_80; + } + + /* Make sure we have sane timings */ + sanitize_timings(i); + + if (np->parent && np->parent->name + && strcasecmp(np->parent->name, "media-bay") == 0) { +#ifdef CONFIG_PMAC_PBOOK + media_bay_set_ide_infos(np->parent,base,irq,i); +#endif /* CONFIG_PMAC_PBOOK */ + in_bay = 1; + if (!bidp) + pmhw->aapl_bus_id = 1; + } else if (pmhw->kind == controller_ohare) { + /* The code below is having trouble on some ohare machines + * (timing related ?). Until I can put my hand on one of these + * units, I keep the old way + */ + ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, 0, 1); + } else { + /* This is necessary to enable IDE when net-booting */ + printk(KERN_INFO "pmac_ide: enabling IDE bus ID %d\n", + pmhw->aapl_bus_id); + ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmhw->aapl_bus_id, 1); + ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmhw->aapl_bus_id, 1); + mdelay(10); + ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmhw->aapl_bus_id, 0); + big_delay = 1; + } + + hwif = &ide_hwifs[i]; + pmac_ide_init_hwif_ports(&hwif->hw, base, 0, &hwif->irq); + memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports)); + hwif->chipset = ide_pmac; + hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET] || in_bay; + hwif->udma_four = (pmhw->kind == controller_kl_ata4_80); + hwif->pci_dev = pdev; +#ifdef CONFIG_PMAC_PBOOK + if (in_bay && check_media_bay_by_base(base, MB_CD) == 0) + hwif->noprobe = 0; +#endif /* CONFIG_PMAC_PBOOK */ + +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + if (np->n_addrs >= 2) { + /* has a DBDMA controller channel */ + pmac_ide_setup_dma(np, i); + } +#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ + + ++i; + } + pmac_ide_count = i; + if (big_delay) + mdelay(IDE_WAKEUP_DELAY_MS); + +#ifdef CONFIG_PMAC_PBOOK + pmu_register_sleep_notifier(&idepmac_sleep_notifier); +#endif /* CONFIG_PMAC_PBOOK */ + register_reboot_notifier(&pmac_ide_reboot_notifier); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + +static void __init +pmac_ide_setup_dma(struct device_node *np, int ix) +{ + struct pmac_ide_hwif *pmif = &pmac_ide[ix]; + + if (request_OF_resource(np, 1, " (mac-io IDE DMA)") == NULL) { + printk(KERN_ERR "ide-pmac(%s): can't request DMA resource !\n", np->name); + return; + } + + pmif->dma_regs = + (volatile struct dbdma_regs*)ioremap(np->addrs[1].address, 0x200); + + /* + * Allocate space for the DBDMA commands. + * The +2 is +1 for the stop command and +1 to allow for + * aligning the start address to a multiple of 16 bytes. + */ + pmif->dma_table_cpu = (struct dbdma_cmd*)pci_alloc_consistent( + ide_hwifs[ix].pci_dev, + (MAX_DCMDS + 2) * sizeof(struct dbdma_cmd), + &pmif->dma_table_dma); + if (pmif->dma_table_cpu == NULL) { + printk(KERN_ERR "%s: unable to allocate DMA command list\n", + ide_hwifs[ix].name); + return; + } + + pmif->sg_table = kmalloc(sizeof(struct scatterlist) * MAX_DCMDS, + GFP_KERNEL); + if (pmif->sg_table == NULL) { + pci_free_consistent( ide_hwifs[ix].pci_dev, + (MAX_DCMDS + 2) * sizeof(struct dbdma_cmd), + pmif->dma_table_cpu, pmif->dma_table_dma); + return; + } + ide_hwifs[ix].dmaproc = &pmac_ide_dmaproc; +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC_AUTO + if (!noautodma) + ide_hwifs[ix].autodma = 1; +#endif +} + +static int +pmac_ide_build_sglist (int ix, struct request *rq) +{ + ide_hwif_t *hwif = &ide_hwifs[ix]; + struct pmac_ide_hwif *pmif = &pmac_ide[ix]; + struct buffer_head *bh; + struct scatterlist *sg = pmif->sg_table; + int nents = 0; + + if (hwif->sg_dma_active) + BUG(); + + if (rq->cmd == READ) + pmif->sg_dma_direction = PCI_DMA_FROMDEVICE; + else + pmif->sg_dma_direction = PCI_DMA_TODEVICE; + bh = rq->bh; + do { + unsigned char *virt_addr = bh->b_data; + unsigned int size = bh->b_size; + + if (nents >= MAX_DCMDS) + return 0; + + while ((bh = bh->b_reqnext) != NULL) { + if ((virt_addr + size) != (unsigned char *) bh->b_data) + break; + size += bh->b_size; + } + memset(&sg[nents], 0, sizeof(*sg)); + sg[nents].address = virt_addr; + sg[nents].length = size; + nents++; + } while (bh != NULL); + + return pci_map_sg(hwif->pci_dev, sg, nents, pmif->sg_dma_direction); +} + +static int +pmac_ide_raw_build_sglist (int ix, struct request *rq) +{ + ide_hwif_t *hwif = &ide_hwifs[ix]; + struct pmac_ide_hwif *pmif = &pmac_ide[ix]; + struct scatterlist *sg = hwif->sg_table; + int nents = 0; + ide_task_t *args = rq->special; + unsigned char *virt_addr = rq->buffer; + int sector_count = rq->nr_sectors; + + if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE) + pmif->sg_dma_direction = PCI_DMA_TODEVICE; + else + pmif->sg_dma_direction = PCI_DMA_FROMDEVICE; + + if (sector_count > 128) { + memset(&sg[nents], 0, sizeof(*sg)); + sg[nents].address = virt_addr; + sg[nents].length = 128 * SECTOR_SIZE; + nents++; + virt_addr = virt_addr + (128 * SECTOR_SIZE); + sector_count -= 128; + } + memset(&sg[nents], 0, sizeof(*sg)); + sg[nents].address = virt_addr; + sg[nents].length = sector_count * SECTOR_SIZE; + nents++; + + return pci_map_sg(hwif->pci_dev, sg, nents, pmif->sg_dma_direction); +} + +/* + * pmac_ide_build_dmatable builds the DBDMA command list + * for a transfer and sets the DBDMA channel to point to it. + */ +static int +pmac_ide_build_dmatable(ide_drive_t *drive, int ix, int wr) +{ + struct dbdma_cmd *table; + int i, count = 0; + struct request *rq = HWGROUP(drive)->rq; + volatile struct dbdma_regs *dma = pmac_ide[ix].dma_regs; + struct scatterlist *sg; + + /* DMA table is already aligned */ + table = (struct dbdma_cmd *) pmac_ide[ix].dma_table_cpu; + + /* Make sure DMA controller is stopped (necessary ?) */ + out_le32(&dma->control, (RUN|PAUSE|FLUSH|WAKE|DEAD) << 16); + while (in_le32(&dma->status) & RUN) + udelay(1); + + /* Build sglist */ + if (HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) + pmac_ide[ix].sg_nents = i = pmac_ide_raw_build_sglist(ix, rq); + else + pmac_ide[ix].sg_nents = i = pmac_ide_build_sglist(ix, rq); + if (!i) + return 0; + + /* Build DBDMA commands list */ + sg = pmac_ide[ix].sg_table; + while (i) { + u32 cur_addr; + u32 cur_len; + + cur_addr = sg_dma_address(sg); + cur_len = sg_dma_len(sg); + + while (cur_len) { + unsigned int tc = (cur_len < 0xfe00)? cur_len: 0xfe00; + + if (++count >= MAX_DCMDS) { + printk(KERN_WARNING "%s: DMA table too small\n", + drive->name); + return 0; /* revert to PIO for this request */ + } + st_le16(&table->command, wr? OUTPUT_MORE: INPUT_MORE); + st_le16(&table->req_count, tc); + st_le32(&table->phy_addr, cur_addr); + table->cmd_dep = 0; + table->xfer_status = 0; + table->res_count = 0; + cur_addr += tc; + cur_len -= tc; + ++table; + } + sg++; + i--; + } + + /* convert the last command to an input/output last command */ + if (count) + st_le16(&table[-1].command, wr? OUTPUT_LAST: INPUT_LAST); + else + printk(KERN_DEBUG "%s: empty DMA table?\n", drive->name); + + /* add the stop command to the end of the list */ + memset(table, 0, sizeof(struct dbdma_cmd)); + out_le16(&table->command, DBDMA_STOP); + + out_le32(&dma->cmdptr, pmac_ide[ix].dma_table_dma); + return 1; +} + +/* Teardown mappings after DMA has completed. */ +static void +pmac_ide_destroy_dmatable (ide_drive_t *drive, int ix) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + struct scatterlist *sg = pmac_ide[ix].sg_table; + int nents = pmac_ide[ix].sg_nents; + + if (nents) { + pci_unmap_sg(dev, sg, nents, pmac_ide[ix].sg_dma_direction); + pmac_ide[ix].sg_nents = 0; + } +} + +static __inline__ unsigned char +dma_bits_to_command(unsigned char bits) +{ + if(bits & 0x04) + return XFER_MW_DMA_2; + if(bits & 0x02) + return XFER_MW_DMA_1; + if(bits & 0x01) + return XFER_MW_DMA_0; + return 0; +} + +static __inline__ unsigned char +udma_bits_to_command(unsigned char bits, int high_speed) +{ + if (high_speed) { + if(bits & 0x10) + return XFER_UDMA_4; + if(bits & 0x08) + return XFER_UDMA_3; + } + if(bits & 0x04) + return XFER_UDMA_2; + if(bits & 0x02) + return XFER_UDMA_1; + if(bits & 0x01) + return XFER_UDMA_0; + return 0; +} + +/* Calculate MultiWord DMA timings */ +static int __pmac +pmac_ide_mdma_enable(ide_drive_t *drive, int idx) +{ + byte bits = drive->id->dma_mword & 0x07; + byte feature = dma_bits_to_command(bits); + u32 *timings; + int drive_cycle_time; + struct hd_driveid *id = drive->id; + int ret; + + /* Set feature on drive */ + printk(KERN_INFO "%s: Enabling MultiWord DMA %d\n", drive->name, feature & 0xf); + ret = pmac_ide_do_setfeature(drive, feature); + if (ret) { + printk(KERN_WARNING "%s: Failed !\n", drive->name); + return 0; + } + + if (!drive->init_speed) + drive->init_speed = feature; + + /* which drive is it ? */ + if (drive->select.b.unit & 0x01) + timings = &pmac_ide[idx].timings[1]; + else + timings = &pmac_ide[idx].timings[0]; + + /* Check if drive provide explicit cycle time */ + if ((id->field_valid & 2) && (id->eide_dma_time)) + drive_cycle_time = id->eide_dma_time; + else + drive_cycle_time = 0; + + /* Calculate controller timings */ + set_timings_mdma(pmac_ide[idx].kind, timings, feature, drive_cycle_time); + + drive->current_speed = feature; + return 1; +} + +/* Calculate Ultra DMA timings */ +static int __pmac +pmac_ide_udma_enable(ide_drive_t *drive, int idx, int high_speed) +{ + byte bits = drive->id->dma_ultra & 0x1f; + byte feature = udma_bits_to_command(bits, high_speed); + u32 *timings; + int ret; + + /* Set feature on drive */ + printk(KERN_INFO "%s: Enabling Ultra DMA %d\n", drive->name, feature & 0xf); + ret = pmac_ide_do_setfeature(drive, feature); + if (ret) { + printk(KERN_WARNING "%s: Failed !\n", drive->name); + return 0; + } + + if (!drive->init_speed) + drive->init_speed = feature; + + /* which drive is it ? */ + if (drive->select.b.unit & 0x01) + timings = &pmac_ide[idx].timings[1]; + else + timings = &pmac_ide[idx].timings[0]; + + set_timings_udma(timings, feature); + + drive->current_speed = feature; + return 1; +} + +static int __pmac +pmac_ide_check_dma(ide_drive_t *drive) +{ + int ata4, udma, idx; + struct hd_driveid *id = drive->id; + int enable = 1; + + drive->using_dma = 0; + + idx = pmac_ide_find(drive); + if (idx < 0) + return 0; + + if (drive->media == ide_floppy) + enable = 0; + if (((id->capability & 1) == 0) && !check_drive_lists(drive, GOOD_DMA_DRIVE)) + enable = 0; + if (check_drive_lists(drive, BAD_DMA_DRIVE)) + enable = 0; + + udma = 0; + ata4 = (pmac_ide[idx].kind == controller_kl_ata4 || + pmac_ide[idx].kind == controller_kl_ata4_80); + + if(enable) { + if (ata4 && (drive->media == ide_disk) && + (id->field_valid & 0x0004) && (id->dma_ultra & 0x1f)) { + /* UltraDMA modes. */ + drive->using_dma = pmac_ide_udma_enable(drive, idx, + pmac_ide[idx].kind == controller_kl_ata4_80); + } + if (!drive->using_dma && (id->dma_mword & 0x0007)) { + /* Normal MultiWord DMA modes. */ + drive->using_dma = pmac_ide_mdma_enable(drive, idx); + } + OUT_BYTE(0, IDE_CONTROL_REG); + /* Apply settings to controller */ + pmac_ide_selectproc(drive); + } + return 0; +} + +static int __pmac +pmac_ide_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ +// ide_task_t *args = HWGROUP(drive)->rq->special; + int ix, dstat; + volatile struct dbdma_regs *dma; + byte unit = (drive->select.b.unit & 0x01); + byte ata4; + int reading = 0; + + /* Can we stuff a pointer to our intf structure in config_data + * or select_data in hwif ? + */ + ix = pmac_ide_find(drive); + if (ix < 0) + return 0; + dma = pmac_ide[ix].dma_regs; + ata4 = (pmac_ide[ix].kind == controller_kl_ata4 || + pmac_ide[ix].kind == controller_kl_ata4_80); + + switch (func) { + case ide_dma_off: + printk(KERN_INFO "%s: DMA disabled\n", drive->name); + case ide_dma_off_quietly: + drive->using_dma = 0; + break; + case ide_dma_on: + case ide_dma_check: + pmac_ide_check_dma(drive); + break; + case ide_dma_read: + reading = 1; + case ide_dma_write: + SELECT_READ_WRITE(HWIF(drive),drive,func); + if (!pmac_ide_build_dmatable(drive, ix, !reading)) + return 1; + /* Apple adds 60ns to wrDataSetup on reads */ + if (ata4 && (pmac_ide[ix].timings[unit] & TR_66_UDMA_EN)) { + out_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE), + pmac_ide[ix].timings[unit] + + (reading ? 0x00800000UL : 0)); + (void)in_le32((unsigned *)(IDE_DATA_REG + IDE_TIMING_CONFIG + _IO_BASE)); + } + drive->waiting_for_dma = 1; + if (drive->media != ide_disk) + return 0; + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); +#if 0 + { + ide_task_t *args = HWGROUP(drive)->rq->special; + OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } +#else + if (HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) { + ide_task_t *args = HWGROUP(drive)->rq->special; + OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } else if (drive->addressing == 1) + OUT_BYTE(reading ? WIN_READDMA_EXT : WIN_WRITEDMA_EXT, IDE_COMMAND_REG); + else + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); +#endif + case ide_dma_begin: + out_le32(&dma->control, (RUN << 16) | RUN); + /* Make sure it gets to the controller right now */ + (void)in_le32(&dma->control); + break; + case ide_dma_end: /* returns 1 on error, 0 otherwise */ + drive->waiting_for_dma = 0; + dstat = in_le32(&dma->status); + out_le32(&dma->control, ((RUN|WAKE|DEAD) << 16)); + pmac_ide_destroy_dmatable(drive, ix); + /* verify good dma status */ + return (dstat & (RUN|DEAD|ACTIVE)) != RUN; + case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */ + /* We have to things to deal with here: + * + * - The dbdma won't stop if the command was started + * but completed with an error without transfering all + * datas. This happens when bad blocks are met during + * a multi-block transfer. + * + * - The dbdma fifo hasn't yet finished flushing to + * to system memory when the disk interrupt occurs. + * + * The trick here is to increment drive->waiting_for_dma, + * and return as if no interrupt occured. If the counter + * reach a certain timeout value, we then return 1. If + * we really got the interrupt, it will happen right away + * again. + * Apple's solution here may be more elegant. They issue + * a DMA channel interrupt (a separate irq line) via a DBDMA + * NOP command just before the STOP, and wait for both the + * disk and DBDMA interrupts to have completed. + */ + + /* If ACTIVE is cleared, the STOP command have passed and + * transfer is complete. + */ + if (!(in_le32(&dma->status) & ACTIVE)) + return 1; + if (!drive->waiting_for_dma) + printk(KERN_WARNING "ide%d, ide_dma_test_irq \ + called while not waiting\n", ix); + + /* If dbdma didn't execute the STOP command yet, the + * active bit is still set */ + drive->waiting_for_dma++; + if (drive->waiting_for_dma >= DMA_WAIT_TIMEOUT) { + printk(KERN_WARNING "ide%d, timeout waiting \ + for dbdma command stop\n", ix); + return 1; + } + udelay(1); + return 0; + + /* Let's implement tose just in case someone wants them */ + case ide_dma_bad_drive: + case ide_dma_good_drive: + return check_drive_lists(drive, (func == ide_dma_good_drive)); + case ide_dma_verbose: + return report_drive_dmaing(drive); + case ide_dma_retune: + case ide_dma_lostirq: + case ide_dma_timeout: + printk(KERN_WARNING "ide_pmac_dmaproc: chipset supported %s func only: %d\n", ide_dmafunc_verbose(func), func); + return 1; + default: + printk(KERN_WARNING "ide_pmac_dmaproc: unsupported %s func: %d\n", ide_dmafunc_verbose(func), func); + return 1; + } + return 0; +} +#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ + +static void __pmac +idepmac_sleep_device(ide_drive_t *drive, int i, unsigned base) +{ + int j; + + /* FIXME: We only handle the master IDE disk, we shoud + * try to fix CD-ROMs here + */ + switch (drive->media) { + case ide_disk: + /* Spin down the drive */ + OUT_BYTE(drive->select.all, base+0x60); + (void) IN_BYTE(base+0x60); + udelay(100); + OUT_BYTE(0x0, base+0x30); + OUT_BYTE(0x0, base+0x20); + OUT_BYTE(0x0, base+0x40); + OUT_BYTE(0x0, base+0x50); + OUT_BYTE(0xe0, base+0x70); + OUT_BYTE(0x2, base+0x160); + for (j = 0; j < 10; j++) { + int status; + mdelay(100); + status = IN_BYTE(base+0x70); + if (!(status & BUSY_STAT) && (status & DRQ_STAT)) + break; + } + break; + case ide_cdrom: + // todo + break; + case ide_floppy: + // todo + break; + } +} + +#ifdef CONFIG_PMAC_PBOOK +static void __pmac +idepmac_wake_device(ide_drive_t *drive, int used_dma) +{ + /* We force the IDE subdriver to check for a media change + * This must be done first or we may lost the condition + * + * Problem: This can schedule. I moved the block device + * wakeup almost late by priority because of that. + */ + if (DRIVER(drive) && DRIVER(drive)->media_change) + DRIVER(drive)->media_change(drive); + + /* We kick the VFS too (see fix in ide.c revalidate) */ + __check_disk_change(MKDEV(HWIF(drive)->major, (drive->select.b.unit) << PARTN_BITS)); + +#ifdef CONFIG_BLK_DEV_IDEDMA_PMAC + /* We re-enable DMA on the drive if it was active. */ + /* This doesn't work with the CD-ROM in the media-bay, probably + * because of a pending unit attention. The problem if that if I + * clear the error, the filesystem dies. + */ + if (used_dma && !ide_spin_wait_hwgroup(drive)) { + /* Lock HW group */ + HWGROUP(drive)->busy = 1; + pmac_ide_check_dma(drive); + HWGROUP(drive)->busy = 0; + if (!list_empty(&drive->queue.queue_head)) + ide_do_request(HWGROUP(drive), 0); + spin_unlock_irq(&ide_lock); + } +#endif /* CONFIG_BLK_DEV_IDEDMA_PMAC */ +} + +static void __pmac +idepmac_sleep_interface(int i, unsigned base, int mediabay) +{ + struct device_node* np = pmac_ide[i].node; + + /* We clear the timings */ + pmac_ide[i].timings[0] = 0; + pmac_ide[i].timings[1] = 0; + + /* The media bay will handle itself just fine */ + if (mediabay) + return; + + /* Disable the bus */ + ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmac_ide[i].aapl_bus_id, 0); +} + +static void __pmac +idepmac_wake_interface(int i, unsigned long base, int mediabay) +{ + struct device_node* np = pmac_ide[i].node; + + if (!mediabay) { + /* Revive IDE disk and controller */ + ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmac_ide[i].aapl_bus_id, 1); + ppc_md.feature_call(PMAC_FTR_IDE_ENABLE, np, pmac_ide[i].aapl_bus_id, 1); + mdelay(10); + ppc_md.feature_call(PMAC_FTR_IDE_RESET, np, pmac_ide[i].aapl_bus_id, 0); + } +} + +static void +idepmac_sleep_drive(ide_drive_t *drive, int idx, unsigned long base) +{ + int unlock = 0; + + /* Wait for HW group to complete operations */ + if (ide_spin_wait_hwgroup(drive)) { + // What can we do here ? Wake drive we had already + // put to sleep and return an error ? + } else { + unlock = 1; + /* Lock HW group */ + HWGROUP(drive)->busy = 1; + /* Stop the device */ + idepmac_sleep_device(drive, idx, base); + } + if (unlock) + spin_unlock_irq(&ide_lock); +} + +static void +idepmac_wake_drive(ide_drive_t *drive, unsigned long base) +{ + unsigned long flags; + int j; + + /* Reset timings */ + pmac_ide_selectproc(drive); + mdelay(10); + + /* Wait up to 20 seconds for the drive to be ready */ + for (j = 0; j < 200; j++) { + int status; + mdelay(100); + OUT_BYTE(drive->select.all, base + 0x60); + if (IN_BYTE(base + 0x60) != drive->select.all) + continue; + status = IN_BYTE(base + 0x70); + if (!(status & BUSY_STAT)) + break; + } + + /* We resume processing on the HW group */ + spin_lock_irqsave(&ide_lock, flags); + HWGROUP(drive)->busy = 0; + if (!list_empty(&drive->queue.queue_head)) + ide_do_request(HWGROUP(drive), 0); + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* Note: We support only master drives for now. This will have to be + * improved if we want to handle sleep on the iMacDV where the CD-ROM + * is a slave + */ +static int __pmac +idepmac_notify_sleep(struct pmu_sleep_notifier *self, int when) +{ + int i, ret; + unsigned long base; + int big_delay; + + switch (when) { + case PBOOK_SLEEP_REQUEST: + break; + case PBOOK_SLEEP_REJECT: + break; + case PBOOK_SLEEP_NOW: + for (i = 0; i < pmac_ide_count; ++i) { + ide_hwif_t *hwif; + int dn; + + if ((base = pmac_ide[i].regbase) == 0) + continue; + + hwif = &ide_hwifs[i]; + for (dn=0; dndrives[dn].present) + continue; + idepmac_sleep_drive(&hwif->drives[dn], i, base); + } + /* Disable irq during sleep */ + disable_irq(pmac_ide[i].irq); + + /* Check if this is a media bay with an IDE device or not + * a media bay. + */ + ret = check_media_bay_by_base(base, MB_CD); + if ((ret == 0) || (ret == -ENODEV)) + idepmac_sleep_interface(i, base, (ret == 0)); + } + break; + case PBOOK_WAKE: + big_delay = 0; + for (i = 0; i < pmac_ide_count; ++i) { + + if ((base = pmac_ide[i].regbase) == 0) + continue; + + /* Make sure we have sane timings */ + sanitize_timings(i); + + /* Check if this is a media bay with an IDE device or not + * a media bay + */ + ret = check_media_bay_by_base(base, MB_CD); + if ((ret == 0) || (ret == -ENODEV)) { + idepmac_wake_interface(i, base, (ret == 0)); + big_delay = 1; + } + + } + /* Let hardware get up to speed */ + if (big_delay) + mdelay(IDE_WAKEUP_DELAY_MS); + + for (i = 0; i < pmac_ide_count; ++i) { + ide_hwif_t *hwif; + int used_dma, dn; + int irq_on = 0; + + if ((base = pmac_ide[i].regbase) == 0) + continue; + + hwif = &ide_hwifs[i]; + for (dn=0; dndrives[dn]; + if (!drive->present) + continue; + /* We don't have re-configured DMA yet */ + used_dma = drive->using_dma; + drive->using_dma = 0; + idepmac_wake_drive(drive, base); + if (!irq_on) { + enable_irq(pmac_ide[i].irq); + irq_on = 1; + } + idepmac_wake_device(drive, used_dma); + } + if (!irq_on) + enable_irq(pmac_ide[i].irq); + } + break; + } + return PBOOK_SLEEP_OK; +} +#endif /* CONFIG_PMAC_PBOOK */ + +static int __pmac +pmac_ide_notify_reboot(struct notifier_block *this, unsigned long code, void *x) +{ + int i, gotone; + unsigned long base; + + if (code != SYS_HALT && code != SYS_POWER_OFF) + return 0; + + gotone = 0; + for (i = 0; i < pmac_ide_count; ++i) { + ide_hwif_t *hwif; + ide_drive_t *drive; + int unlock = 0; + int dn; + + if ((base = pmac_ide[i].regbase) == 0) + continue; + + hwif = &ide_hwifs[i]; + for (dn=0; dndrives[dn]; + if (drive->present) { + gotone = 1; + /* Wait for HW group to complete operations */ + if (ide_spin_wait_hwgroup(drive)) { + // What can we do here ? Wake drive we had already + // put to sleep and return an error ? + } else { + unlock = 1; + /* Lock HW group */ + HWGROUP(drive)->busy = 1; + + /* Stop the device */ + idepmac_sleep_device(drive, i, base); + } + } + if (unlock) + spin_unlock_irq(&ide_lock); + } + } + if (gotone) + mdelay(1000); + + return NOTIFY_DONE; +} diff -Nru a/drivers/ide/ide-pnp.c b/drivers/ide/ide-pnp.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-pnp.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,161 @@ +/* + * linux/drivers/ide/ide-pnp.c + * + * This file provides autodetection for ISA PnP IDE interfaces. + * It was tested with "ESS ES1868 Plug and Play AudioDrive" IDE interface. + * + * Copyright (C) 2000 Andrey Panin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * You should have received a copy of the GNU General Public License + * (for example /usr/src/linux/COPYING); if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#include + +#ifndef PREPARE_FUNC +#define PREPARE_FUNC(dev) (dev->prepare) +#define ACTIVATE_FUNC(dev) (dev->activate) +#define DEACTIVATE_FUNC(dev) (dev->deactivate) +#endif + +#define DEV_IO(dev, index) (dev->resource[index].start) +#define DEV_IRQ(dev, index) (dev->irq_resource[index].start) + +#define DEV_NAME(dev) (dev->bus->name ? dev->bus->name : "ISA PnP") + +#define GENERIC_HD_DATA 0 +#define GENERIC_HD_ERROR 1 +#define GENERIC_HD_NSECTOR 2 +#define GENERIC_HD_SECTOR 3 +#define GENERIC_HD_LCYL 4 +#define GENERIC_HD_HCYL 5 +#define GENERIC_HD_SELECT 6 +#define GENERIC_HD_STATUS 7 + +static int generic_ide_offsets[IDE_NR_PORTS] __initdata = { + GENERIC_HD_DATA, GENERIC_HD_ERROR, GENERIC_HD_NSECTOR, + GENERIC_HD_SECTOR, GENERIC_HD_LCYL, GENERIC_HD_HCYL, + GENERIC_HD_SELECT, GENERIC_HD_STATUS, -1, -1 +}; + +/* ISA PnP device table entry */ +struct pnp_dev_t { + unsigned short card_vendor, card_device, vendor, device; + int (*init_fn)(struct pci_dev *dev, int enable); +}; + +/* Generic initialisation function for ISA PnP IDE interface */ +static int __init pnpide_generic_init(struct pci_dev *dev, int enable) +{ + hw_regs_t hw; + int index; + + if (!enable) + return 0; + + if (!(DEV_IO(dev, 0) && DEV_IO(dev, 1) && DEV_IRQ(dev, 0))) + return 1; + + ide_setup_ports(&hw, (ide_ioreg_t) DEV_IO(dev, 0), + generic_ide_offsets, (ide_ioreg_t) DEV_IO(dev, 1), + 0, NULL, DEV_IRQ(dev, 0)); + + index = ide_register_hw(&hw, NULL); + + if (index != -1) { + printk("ide%d: %s IDE interface\n", index, DEV_NAME(dev)); + return 0; + } + + return 1; +} + +/* Add your devices here :)) */ +struct pnp_dev_t idepnp_devices[] __initdata = { + /* Generic ESDI/IDE/ATA compatible hard disk controller */ + { ISAPNP_ANY_ID, ISAPNP_ANY_ID, + ISAPNP_VENDOR('P', 'N', 'P'), ISAPNP_DEVICE(0x0600), + pnpide_generic_init }, + { 0 } +}; + +#ifdef MODULE +#define NR_PNP_DEVICES 8 +struct pnp_dev_inst { + struct pci_dev *dev; + struct pnp_dev_t *dev_type; +}; +static struct pnp_dev_inst devices[NR_PNP_DEVICES]; +static int pnp_ide_dev_idx = 0; +#endif + +/* + * Probe for ISA PnP IDE interfaces. + */ +void __init pnpide_init(int enable) +{ + struct pci_dev *dev = NULL; + struct pnp_dev_t *dev_type; + + if (!isapnp_present()) + return; + +#ifdef MODULE + /* Module unload, deactivate all registered devices. */ + if (!enable) { + int i; + for (i = 0; i < pnp_ide_dev_idx; i++) { + devices[i].dev_type->init_fn(dev, 0); + + if (DEACTIVATE_FUNC(devices[i].dev)) + DEACTIVATE_FUNC(devices[i].dev)(devices[i].dev); + } + return; + } +#endif + for (dev_type = idepnp_devices; dev_type->vendor; dev_type++) { + while ((dev = isapnp_find_dev(NULL, dev_type->vendor, + dev_type->device, dev))) { + + if (dev->active) + continue; + + if (PREPARE_FUNC(dev) && (PREPARE_FUNC(dev))(dev) < 0) { + printk("ide: %s prepare failed\n", DEV_NAME(dev)); + continue; + } + + if (ACTIVATE_FUNC(dev) && (ACTIVATE_FUNC(dev))(dev) < 0) { + printk("ide: %s activate failed\n", DEV_NAME(dev)); + continue; + } + + /* Call device initialization function */ + if (dev_type->init_fn(dev, 1)) { + if (DEACTIVATE_FUNC(dev)) + DEACTIVATE_FUNC(dev)(dev); + } else { +#ifdef MODULE + /* + * Register device in the array to + * deactivate it on a module unload. + */ + if (pnp_ide_dev_idx >= NR_PNP_DEVICES) + return; + devices[pnp_ide_dev_idx].dev = dev; + devices[pnp_ide_dev_idx].dev_type = dev_type; + pnp_ide_dev_idx++; +#endif + } + } + } +} diff -Nru a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-probe.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,1027 @@ +/* + * linux/drivers/ide/ide-probe.c Version 1.07 March 18, 2001 + * + * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) + */ + +/* + * Mostly written by Mark Lord + * and Gadi Oxman + * and Andre Hedrick + * + * See linux/MAINTAINERS for address of current maintainer. + * + * This is the IDE probe module, as evolved from hd.c and ide.c. + * + * Version 1.00 move drive probing code from ide.c to ide-probe.c + * Version 1.01 fix compilation problem for m68k + * Version 1.02 increase WAIT_PIDENTIFY to avoid CD-ROM locking at boot + * by Andrea Arcangeli + * Version 1.03 fix for (hwif->chipset == ide_4drives) + * Version 1.04 fixed buggy treatments of known flash memory cards + * + * Version 1.05 fix for (hwif->chipset == ide_pdc4030) + * added ide6/7/8/9 + * allowed for secondary flash card to be detectable + * with new flag : drive->ata_flash : 1; + * Version 1.06 stream line request queue and prep for cascade project. + * Version 1.07 max_sect <= 255; slower disks would get behind and + * then fall over when they get to 256. Paul G. + */ + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +static inline void do_identify (ide_drive_t *drive, byte cmd) +{ + ide_hwif_t *hwif = HWIF(drive); + int bswap = 1; + struct hd_driveid *id; + + id = drive->id = kmalloc (SECTOR_WORDS*4, GFP_ATOMIC); /* called with interrupts disabled! */ + if (!id) { + printk(KERN_WARNING "(ide-probe::do_identify) Out of memory.\n"); + goto err_kmalloc; + } + /* read 512 bytes of id info */ +#if 1 + ata_input_data(drive, id, SECTOR_WORDS); +#else + { + unsigned long *ptr = (unsigned long *)id ; + unsigned long lcount = 256/2 ; + // printk("IDE_DATA_REG = %#lx",IDE_DATA_REG); + while( lcount-- ) + *ptr++ = inl(IDE_DATA_REG); + } +#endif + local_irq_enable(); + ide_fix_driveid(id); + + if (id->word156 == 0x4d42) { + printk("%s: drive->id->word156 == 0x%04x \n", + drive->name, drive->id->word156); + } + + if (!drive->forced_lun) + drive->last_lun = id->last_lun & 0x7; +#if defined (CONFIG_SCSI_EATA_DMA) || defined (CONFIG_SCSI_EATA_PIO) || defined (CONFIG_SCSI_EATA) + /* + * EATA SCSI controllers do a hardware ATA emulation: + * Ignore them if there is a driver for them available. + */ + if ((id->model[0] == 'P' && id->model[1] == 'M') + || (id->model[0] == 'S' && id->model[1] == 'K')) { + printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model); + goto err_misc; + } +#endif /* CONFIG_SCSI_EATA_DMA || CONFIG_SCSI_EATA_PIO */ + + /* + * WIN_IDENTIFY returns little-endian info, + * WIN_PIDENTIFY *usually* returns little-endian info. + */ + if (cmd == WIN_PIDENTIFY) { + if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */ + || (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */ + || (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */ + bswap ^= 1; /* Vertos drives may still be weird */ + } + ide_fixstring (id->model, sizeof(id->model), bswap); + ide_fixstring (id->fw_rev, sizeof(id->fw_rev), bswap); + ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap); + + if (strstr(id->model, "E X A B Y T E N E S T")) + goto err_misc; + + id->model[sizeof(id->model)-1] = '\0'; /* we depend on this a lot! */ + printk("%s: %s, ", drive->name, id->model); + drive->present = 1; + + /* + * Check for an ATAPI device + */ + if (cmd == WIN_PIDENTIFY) { + byte type = (id->config >> 8) & 0x1f; + printk("ATAPI "); +#ifdef CONFIG_BLK_DEV_PDC4030 + if (hwif->channel == 1 && hwif->chipset == ide_pdc4030) { + printk(" -- not supported on 2nd Promise port\n"); + goto err_misc; + } +#endif /* CONFIG_BLK_DEV_PDC4030 */ + switch (type) { + case ide_floppy: + if (!strstr(id->model, "CD-ROM")) { + if (!strstr(id->model, "oppy") && + !strstr(id->model, "poyp") && + !strstr(id->model, "ZIP")) + printk("cdrom or floppy?, assuming "); + if (drive->media != ide_cdrom) { + printk ("FLOPPY"); + break; + } + } + type = ide_cdrom; /* Early cdrom models used zero */ + case ide_cdrom: + drive->removable = 1; +#ifdef CONFIG_PPC + /* kludge for Apple PowerBook internal zip */ + if (!strstr(id->model, "CD-ROM") && + strstr(id->model, "ZIP")) { + printk ("FLOPPY"); + type = ide_floppy; + break; + } +#endif + printk ("CD/DVD-ROM"); + break; + case ide_tape: + printk ("TAPE"); + break; + case ide_optical: + printk ("OPTICAL"); + drive->removable = 1; + break; + default: + printk("UNKNOWN (type %d)", type); + break; + } + printk (" drive\n"); + drive->media = type; + return; + } + + /* + * Not an ATAPI device: looks like a "regular" hard disk + */ + if (id->config & (1<<7)) + drive->removable = 1; + /* + * Prevent long system lockup probing later for non-existant + * slave drive if the hwif is actually a flash memory card of + * some variety: + */ + if (drive_is_flashcard(drive)) { + ide_drive_t *mate = &hwif->drives[1^drive->select.b.unit]; + if (!mate->ata_flash) { + mate->present = 0; + mate->noprobe = 1; + } + } + drive->media = ide_disk; + printk("ATA DISK drive\n"); + QUIRK_LIST(hwif, drive); + return; + +err_misc: + kfree(id); +err_kmalloc: + drive->present = 0; + return; +} + +/* + * try_to_identify() sends an ATA(PI) IDENTIFY request to a drive + * and waits for a response. It also monitors irqs while this is + * happening, in hope of automatically determining which one is + * being used by the interface. + * + * Returns: 0 device was identified + * 1 device timed-out (no response to identify request) + * 2 device aborted the command (refused to identify itself) + */ +static int actual_try_to_identify (ide_drive_t *drive, byte cmd) +{ +// ide_hwif_t *hwif = HWIF(drive); + int rc; + ide_ioreg_t hd_status; + unsigned long timeout; + byte s, a; + + if (IDE_CONTROL_REG) { + /* take a deep breath */ + ide_delay_50ms(); + a = IN_BYTE(IDE_ALTSTATUS_REG); + s = IN_BYTE(IDE_STATUS_REG); + if ((a ^ s) & ~INDEX_STAT) { + printk("%s: probing with STATUS(0x%02x) instead of ALTSTATUS(0x%02x)\n", drive->name, s, a); + hd_status = IDE_STATUS_REG; /* ancient Seagate drives, broken interfaces */ + } else { + hd_status = IDE_ALTSTATUS_REG; /* use non-intrusive polling */ + } + } else { + ide_delay_50ms(); + hd_status = IDE_STATUS_REG; + } + + /* set features register for atapi identify command to be sure of reply */ + if ((cmd == WIN_PIDENTIFY)) + OUT_BYTE(0,IDE_FEATURE_REG); /* disable dma & overlap */ + +#if CONFIG_BLK_DEV_PDC4030 + if (HWIF(drive)->chipset == ide_pdc4030) { + /* DC4030 hosted drives need their own identify... */ + extern int pdc4030_identify(ide_drive_t *); + if (pdc4030_identify(drive)) { + return 1; + } + } else +#endif /* CONFIG_BLK_DEV_PDC4030 */ + OUT_BYTE(cmd,IDE_COMMAND_REG); /* ask drive for ID */ + timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2; + timeout += jiffies; + do { + if (time_after(jiffies, timeout)) { + return 1; /* drive timed-out */ + } + ide_delay_50ms(); /* give drive a breather */ + } while (IN_BYTE(hd_status) & BUSY_STAT); + + ide_delay_50ms(); /* wait for IRQ and DRQ_STAT */ + if (OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) { + unsigned long flags; + local_irq_save(flags); + /* local CPU only; some systems need this */ + do_identify(drive, cmd); /* drive returned ID */ + rc = 0; /* drive responded with ID */ + (void) GET_STAT(); /* clear drive IRQ */ + local_irq_restore(flags); + } else + rc = 2; /* drive refused ID */ + return rc; +} + +static int try_to_identify (ide_drive_t *drive, byte cmd) +{ + ide_hwif_t *hwif = HWIF(drive); + int retval; + int autoprobe = 0; + unsigned long cookie = 0; + + if (IDE_CONTROL_REG && !hwif->irq) { + autoprobe = 1; + cookie = probe_irq_on(); + OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* enable device irq */ + } + + retval = actual_try_to_identify(drive, cmd); + + if (autoprobe) { + int irq; + OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* mask device irq */ + (void) GET_STAT(); /* clear drive IRQ */ + udelay(5); + irq = probe_irq_off(cookie); + if (!hwif->irq) { + if (irq > 0) { + hwif->irq = irq; + } else { /* Mmmm.. multiple IRQs.. don't know which was ours */ + printk("%s: IRQ probe failed (0x%lx)\n", drive->name, cookie); +#ifdef CONFIG_BLK_DEV_CMD640 +#ifdef CMD640_DUMP_REGS + if (hwif->chipset == ide_cmd640) { + printk("%s: Hmmm.. probably a driver problem.\n", drive->name); + CMD640_DUMP_REGS; + } +#endif /* CMD640_DUMP_REGS */ +#endif /* CONFIG_BLK_DEV_CMD640 */ + } + } + } + return retval; +} + + +/* + * do_probe() has the difficult job of finding a drive if it exists, + * without getting hung up if it doesn't exist, without trampling on + * ethernet cards, and without leaving any IRQs dangling to haunt us later. + * + * If a drive is "known" to exist (from CMOS or kernel parameters), + * but does not respond right away, the probe will "hang in there" + * for the maximum wait time (about 30 seconds), otherwise it will + * exit much more quickly. + * + * Returns: 0 device was identified + * 1 device timed-out (no response to identify request) + * 2 device aborted the command (refused to identify itself) + * 3 bad status from device (possible for ATAPI drives) + * 4 probe was not attempted because failure was obvious + */ +static int do_probe (ide_drive_t *drive, byte cmd) +{ + int rc; + ide_hwif_t *hwif = HWIF(drive); + if (drive->present) { /* avoid waiting for inappropriate probes */ + if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY)) + return 4; + } +#ifdef DEBUG + printk("probing for %s: present=%d, media=%d, probetype=%s\n", + drive->name, drive->present, drive->media, + (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI"); +#endif + ide_delay_50ms(); /* needed for some systems (e.g. crw9624 as drive0 with disk as slave) */ + SELECT_DRIVE(hwif,drive); + ide_delay_50ms(); + if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) { + if (drive->select.b.unit != 0) { + SELECT_DRIVE(hwif,&hwif->drives[0]); /* exit with drive0 selected */ + ide_delay_50ms(); /* allow BUSY_STAT to assert & clear */ + } + return 3; /* no i/f present: mmm.. this should be a 4 -ml */ + } + + if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT) || + drive->present || cmd == WIN_PIDENTIFY) { + if ((rc = try_to_identify(drive,cmd))) /* send cmd and wait */ + rc = try_to_identify(drive,cmd); /* failed: try again */ + if (rc == 1 && cmd == WIN_PIDENTIFY && drive->autotune != 2) { + unsigned long timeout; + printk("%s: no response (status = 0x%02x), resetting drive\n", drive->name, GET_STAT()); + ide_delay_50ms(); + OUT_BYTE (drive->select.all, IDE_SELECT_REG); + ide_delay_50ms(); + OUT_BYTE(WIN_SRST, IDE_COMMAND_REG); + timeout = jiffies; + while ((GET_STAT() & BUSY_STAT) && time_before(jiffies, timeout + WAIT_WORSTCASE)) + ide_delay_50ms(); + rc = try_to_identify(drive, cmd); + } + if (rc == 1) + printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT()); + (void) GET_STAT(); /* ensure drive irq is clear */ + } else { + rc = 3; /* not present or maybe ATAPI */ + } + if (drive->select.b.unit != 0) { + SELECT_DRIVE(hwif,&hwif->drives[0]); /* exit with drive0 selected */ + ide_delay_50ms(); + (void) GET_STAT(); /* ensure drive irq is clear */ + } + return rc; +} + +/* + * + */ +static void enable_nest (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned long timeout; + + printk("%s: enabling %s -- ", hwif->name, drive->id->model); + SELECT_DRIVE(hwif, drive); + ide_delay_50ms(); + OUT_BYTE(EXABYTE_ENABLE_NEST, IDE_COMMAND_REG); + timeout = jiffies + WAIT_WORSTCASE; + do { + if (time_after(jiffies, timeout)) { + printk("failed (timeout)\n"); + return; + } + ide_delay_50ms(); + } while (GET_STAT() & BUSY_STAT); + ide_delay_50ms(); + if (!OK_STAT(GET_STAT(), 0, BAD_STAT)) + printk("failed (status = 0x%02x)\n", GET_STAT()); + else + printk("success\n"); + + if (do_probe(drive, WIN_IDENTIFY) >= 2) /* if !(success||timed-out) */ + (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */ +} + +/* + * probe_for_drive() tests for existence of a given drive using do_probe(). + * + * Returns: 0 no device was found + * 1 device was found (note: drive->present might still be 0) + */ +static inline byte probe_for_drive (ide_drive_t *drive) +{ + if (drive->noprobe) /* skip probing? */ + return drive->present; + if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */ + (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */ + } + if (drive->id && strstr(drive->id->model, "E X A B Y T E N E S T")) + enable_nest(drive); + if (!drive->present) + return 0; /* drive not found */ + if (drive->id == NULL) { /* identification failed? */ + if (drive->media == ide_disk) { + printk ("%s: non-IDE drive, CHS=%d/%d/%d\n", + drive->name, drive->cyl, drive->head, drive->sect); + } else if (drive->media == ide_cdrom) { + printk("%s: ATAPI cdrom (?)\n", drive->name); + } else { + drive->present = 0; /* nuke it */ + } + } + return 1; /* drive was found */ +} + +/* + * Calculate the region that this interface occupies, + * handling interfaces where the registers may not be + * ordered sanely. We deal with the CONTROL register + * separately. + */ +static int hwif_check_regions (ide_hwif_t *hwif) +{ + int region_errors = 0; + + hwif->straight8 = 0; + region_errors = ide_check_region(hwif->io_ports[IDE_DATA_OFFSET], 1); + region_errors += ide_check_region(hwif->io_ports[IDE_ERROR_OFFSET], 1); + region_errors += ide_check_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1); + region_errors += ide_check_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1); + region_errors += ide_check_region(hwif->io_ports[IDE_LCYL_OFFSET], 1); + region_errors += ide_check_region(hwif->io_ports[IDE_HCYL_OFFSET], 1); + region_errors += ide_check_region(hwif->io_ports[IDE_SELECT_OFFSET], 1); + region_errors += ide_check_region(hwif->io_ports[IDE_STATUS_OFFSET], 1); + + if (hwif->io_ports[IDE_CONTROL_OFFSET]) + region_errors += ide_check_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1); +#if defined(CONFIG_AMIGA) || defined(CONFIG_MAC) + if (hwif->io_ports[IDE_IRQ_OFFSET]) + region_errors += ide_check_region(hwif->io_ports[IDE_IRQ_OFFSET], 1); +#endif /* (CONFIG_AMIGA) || (CONFIG_MAC) */ + /* + * If any errors are return, we drop the hwif interface. + */ + return(region_errors); +} + +static void hwif_register (ide_hwif_t *hwif) +{ + if (((unsigned long)hwif->io_ports[IDE_DATA_OFFSET] | 7) == + ((unsigned long)hwif->io_ports[IDE_STATUS_OFFSET])) { + ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 8, hwif->name); + hwif->straight8 = 1; + goto jump_straight8; + } + + if (hwif->io_ports[IDE_DATA_OFFSET]) + ide_request_region(hwif->io_ports[IDE_DATA_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_ERROR_OFFSET]) + ide_request_region(hwif->io_ports[IDE_ERROR_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_NSECTOR_OFFSET]) + ide_request_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_SECTOR_OFFSET]) + ide_request_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_LCYL_OFFSET]) + ide_request_region(hwif->io_ports[IDE_LCYL_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_HCYL_OFFSET]) + ide_request_region(hwif->io_ports[IDE_HCYL_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_SELECT_OFFSET]) + ide_request_region(hwif->io_ports[IDE_SELECT_OFFSET], 1, hwif->name); + if (hwif->io_ports[IDE_STATUS_OFFSET]) + ide_request_region(hwif->io_ports[IDE_STATUS_OFFSET], 1, hwif->name); + +jump_straight8: + if (hwif->io_ports[IDE_CONTROL_OFFSET]) + ide_request_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1, hwif->name); +#if defined(CONFIG_AMIGA) || defined(CONFIG_MAC) + if (hwif->io_ports[IDE_IRQ_OFFSET]) + ide_request_region(hwif->io_ports[IDE_IRQ_OFFSET], 1, hwif->name); +#endif /* (CONFIG_AMIGA) || (CONFIG_MAC) */ +} + +/* + * This routine only knows how to look for drive units 0 and 1 + * on an interface, so any setting of MAX_DRIVES > 2 won't work here. + */ +static void probe_hwif (ide_hwif_t *hwif) +{ + unsigned int unit; + unsigned long flags; + + if (hwif->noprobe) + return; +#ifdef CONFIG_BLK_DEV_IDE + if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA) { + extern void probe_cmos_for_drives(ide_hwif_t *); + + probe_cmos_for_drives (hwif); + } +#endif + + if ((hwif->chipset != ide_4drives || !hwif->mate->present) && +#if CONFIG_BLK_DEV_PDC4030 + (hwif->chipset != ide_pdc4030 || hwif->channel == 0) && +#endif /* CONFIG_BLK_DEV_PDC4030 */ + (hwif_check_regions(hwif))) { + int msgout = 0; + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + if (drive->present) { + drive->present = 0; + printk("%s: ERROR, PORTS ALREADY IN USE\n", drive->name); + msgout = 1; + } + } + if (!msgout) + printk("%s: ports already in use, skipping probe\n", hwif->name); + return; + } + + local_irq_set(flags); + /* + * Second drive should only exist if first drive was found, + * but a lot of cdrom drives are configured as single slaves. + */ + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + (void) probe_for_drive (drive); + if (drive->present && !hwif->present) { + hwif->present = 1; + if (hwif->chipset != ide_4drives || + !hwif->mate->present) { + hwif_register(hwif); + } + } + } + if (hwif->io_ports[IDE_CONTROL_OFFSET] && hwif->reset) { + unsigned long timeout = jiffies + WAIT_WORSTCASE; + byte stat; + + printk("%s: reset\n", hwif->name); + OUT_BYTE(12, hwif->io_ports[IDE_CONTROL_OFFSET]); + udelay(10); + OUT_BYTE(8, hwif->io_ports[IDE_CONTROL_OFFSET]); + do { + ide_delay_50ms(); + stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); + } while ((stat & BUSY_STAT) && time_after(timeout, jiffies)); + + } + local_irq_restore(flags); + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + if (drive->present) { + if (hwif->tuneproc != NULL && drive->autotune == 1) + /* auto-tune PIO mode */ + hwif->tuneproc(drive, 255); + } + } +} + +#if MAX_HWIFS > 1 +/* + * save_match() is used to simplify logic in init_irq() below. + * + * A loophole here is that we may not know about a particular + * hwif's irq until after that hwif is actually probed/initialized.. + * This could be a problem for the case where an hwif is on a + * dual interface that requires serialization (eg. cmd640) and another + * hwif using one of the same irqs is initialized beforehand. + * + * This routine detects and reports such situations, but does not fix them. + */ +static void save_match (ide_hwif_t *hwif, ide_hwif_t *new, ide_hwif_t **match) +{ + ide_hwif_t *m = *match; + + if (m && m->hwgroup && m->hwgroup != new->hwgroup) { + if (!new->hwgroup) + return; + printk("%s: potential irq problem with %s and %s\n", + hwif->name, new->name, m->name); + } + if (!m || m->irq != hwif->irq) /* don't undo a prior perfect match */ + *match = new; +} +#endif /* MAX_HWIFS > 1 */ + +/* + * init request queue + */ +static void ide_init_queue(ide_drive_t *drive) +{ + request_queue_t *q = &drive->queue; + int max_sectors; + + q->queuedata = HWGROUP(drive); + blk_init_queue(q, do_ide_request, &ide_lock); + blk_queue_segment_boundary(q, 0xffff); + +#ifdef CONFIG_BLK_DEV_PDC4030 + max_sectors = 127; +#else + max_sectors = 255; +#endif + blk_queue_max_sectors(q, max_sectors); + + /* IDE DMA can do PRD_ENTRIES number of segments. */ + blk_queue_max_hw_segments(q, PRD_ENTRIES); + + /* This is a driver limit and could be eliminated. */ + blk_queue_max_phys_segments(q, PRD_ENTRIES); +} + +/* + * This routine sets up the irq for an ide interface, and creates a new + * hwgroup for the irq/hwif if none was previously assigned. + * + * Much of the code is for correctly detecting/handling irq sharing + * and irq serialization situations. This is somewhat complex because + * it handles static as well as dynamic (PCMCIA) IDE interfaces. + * + * The SA_INTERRUPT in sa_flags means ide_intr() is always entered with + * interrupts completely disabled. This can be bad for interrupt latency, + * but anything else has led to problems on some machines. We re-enable + * interrupts as much as we can safely do in most places. + */ +static int init_irq (ide_hwif_t *hwif) +{ + unsigned long flags; + unsigned int index; + ide_hwgroup_t *hwgroup, *new_hwgroup; + ide_hwif_t *match = NULL; + + + /* Allocate the buffer and potentially sleep first */ + + new_hwgroup = kmalloc(sizeof(ide_hwgroup_t),GFP_KERNEL); + + spin_lock_irqsave(&ide_lock, flags); + + hwif->hwgroup = NULL; +#if MAX_HWIFS > 1 + /* + * Group up with any other hwifs that share our irq(s). + */ + for (index = 0; index < MAX_HWIFS; index++) { + ide_hwif_t *h = &ide_hwifs[index]; + if (h->hwgroup) { /* scan only initialized hwif's */ + if (hwif->irq == h->irq) { + hwif->sharing_irq = h->sharing_irq = 1; + if (hwif->chipset != ide_pci || h->chipset != ide_pci) { + save_match(hwif, h, &match); + } + } + if (hwif->serialized) { + if (hwif->mate && hwif->mate->irq == h->irq) + save_match(hwif, h, &match); + } + if (h->serialized) { + if (h->mate && hwif->irq == h->mate->irq) + save_match(hwif, h, &match); + } + } + } +#endif /* MAX_HWIFS > 1 */ + /* + * If we are still without a hwgroup, then form a new one + */ + if (match) { + hwgroup = match->hwgroup; + if(new_hwgroup) + kfree(new_hwgroup); + } else { + hwgroup = new_hwgroup; + if (!hwgroup) { + spin_unlock_irqrestore(&ide_lock, flags); + return 1; + } + memset(hwgroup, 0, sizeof(ide_hwgroup_t)); + hwgroup->hwif = hwif->next = hwif; + hwgroup->rq = NULL; + hwgroup->handler = NULL; + hwgroup->drive = NULL; + hwgroup->busy = 0; + init_timer(&hwgroup->timer); + hwgroup->timer.function = &ide_timer_expiry; + hwgroup->timer.data = (unsigned long) hwgroup; + } + + /* + * Allocate the irq, if not already obtained for another hwif + */ + if (!match || match->irq != hwif->irq) { +#ifdef CONFIG_IDEPCI_SHARE_IRQ + int sa = IDE_CHIPSET_IS_PCI(hwif->chipset) ? SA_SHIRQ : SA_INTERRUPT; +#else /* !CONFIG_IDEPCI_SHARE_IRQ */ + int sa = IDE_CHIPSET_IS_PCI(hwif->chipset) ? SA_INTERRUPT|SA_SHIRQ : SA_INTERRUPT; +#endif /* CONFIG_IDEPCI_SHARE_IRQ */ + + if (hwif->io_ports[IDE_CONTROL_OFFSET]) + OUT_BYTE(0x08, hwif->io_ports[IDE_CONTROL_OFFSET]); /* clear nIEN */ + + if (ide_request_irq(hwif->irq, &ide_intr, sa, hwif->name, hwgroup)) { + if (!match) + kfree(hwgroup); + spin_unlock_irqrestore(&ide_lock, flags); + return 1; + } + } + + /* + * Everything is okay, so link us into the hwgroup + */ + hwif->hwgroup = hwgroup; + hwif->next = hwgroup->hwif->next; + hwgroup->hwif->next = hwif; + + for (index = 0; index < MAX_DRIVES; ++index) { + ide_drive_t *drive = &hwif->drives[index]; + if (!drive->present) + continue; + if (!hwgroup->drive) + hwgroup->drive = drive; + drive->next = hwgroup->drive->next; + hwgroup->drive->next = drive; + ide_init_queue(drive); + } + if (!hwgroup->hwif) { + hwgroup->hwif = HWIF(hwgroup->drive); +#ifdef DEBUG + printk("%s : Adding missed hwif to hwgroup!!\n", hwif->name); +#endif + } + spin_unlock_irqrestore(&ide_lock, flags); + /* all CPUs; safe now that hwif->hwgroup is set up */ + +#if !defined(__mc68000__) && !defined(CONFIG_APUS) && !defined(__sparc__) + printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name, + hwif->io_ports[IDE_DATA_OFFSET], + hwif->io_ports[IDE_DATA_OFFSET]+7, + hwif->io_ports[IDE_CONTROL_OFFSET], hwif->irq); +#elif defined(__sparc__) + printk("%s at 0x%03lx-0x%03lx,0x%03lx on irq %s", hwif->name, + hwif->io_ports[IDE_DATA_OFFSET], + hwif->io_ports[IDE_DATA_OFFSET]+7, + hwif->io_ports[IDE_CONTROL_OFFSET], __irq_itoa(hwif->irq)); +#else + printk("%s at %p on irq 0x%08x", hwif->name, + hwif->io_ports[IDE_DATA_OFFSET], hwif->irq); +#endif /* __mc68000__ && CONFIG_APUS */ + if (match) + printk(" (%sed with %s)", + hwif->sharing_irq ? "shar" : "serializ", match->name); + printk("\n"); + return 0; +} + +/* + * init_gendisk() (as opposed to ide_geninit) is called for each major device, + * after probing for drives, to allocate partition tables and other data + * structures needed for the routines in genhd.c. ide_geninit() gets called + * somewhat later, during the partition check. + */ +static void init_gendisk (ide_hwif_t *hwif) +{ + struct gendisk *gd; + struct hd_struct *part; + devfs_handle_t *de_arr; + char *flags; + unsigned int unit, units, minors; + extern devfs_handle_t ide_devfs_handle; + char *names; + +#if 1 + units = MAX_DRIVES; +#else + /* figure out maximum drive number on the interface */ + for (units = MAX_DRIVES; units > 0; --units) { + if (hwif->drives[units-1].present) + break; + } +#endif + + minors = units * (1<drives[unit].part = gd[unit].part; + gd[unit].major = hwif->major; + gd[unit].first_minor = unit << PARTN_BITS; + sprintf(names + 4*unit, "hd%c",'a'+hwif->index*MAX_DRIVES+unit); + gd[unit].major_name = names + 4*unit; + gd[unit].minor_shift = PARTN_BITS; + gd[unit].nr_real = 1; + gd[unit].fops = ide_fops; + hwif->gd[unit] = gd + unit; + add_gendisk(gd + unit); + } + + for (unit = 0; unit < units; ++unit) { +#if 1 + char name[64]; + ide_add_generic_settings(hwif->drives + unit); + hwif->drives[unit].dn = ((hwif->channel ? 2 : 0) + unit); + sprintf (name, "host%d/bus%d/target%d/lun%d", + (hwif->channel && hwif->mate) ? + hwif->mate->index : hwif->index, + hwif->channel, unit, hwif->drives[unit].lun); + if (hwif->drives[unit].present) + hwif->drives[unit].de = devfs_mk_dir(ide_devfs_handle, name, NULL); +#else + if (hwif->drives[unit].present) { + char name[64]; + + ide_add_generic_settings(hwif->drives + unit); + hwif->drives[unit].dn = ((hwif->channel ? 2 : 0) + unit); + sprintf (name, "host%d/bus%d/target%d/lun%d", + (hwif->channel && hwif->mate) ? hwif->mate->index : hwif->index, + hwif->channel, unit, hwif->drives[unit].lun); + hwif->drives[unit].de = + devfs_mk_dir (ide_devfs_handle, name, NULL); + } +#endif + } + return; + +err_kmalloc_gd_names: + kfree(names); +err_kmalloc_gd_flags: + kfree(de_arr); +err_kmalloc_gd_de_arr: + kfree(part); +err_kmalloc_gd_part: + kfree(gd); +err_kmalloc_gd: + printk(KERN_WARNING "(ide::init_gendisk) Out of memory\n"); +} + +static int hwif_init (ide_hwif_t *hwif) +{ + if (!hwif->present) + return 0; + if (!hwif->irq) { + if (!(hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET]))) + { + printk("%s: DISABLED, NO IRQ\n", hwif->name); + return (hwif->present = 0); + } + } +#ifdef CONFIG_BLK_DEV_HD + if (hwif->irq == HD_IRQ && hwif->io_ports[IDE_DATA_OFFSET] != HD_DATA) { + printk("%s: CANNOT SHARE IRQ WITH OLD " + "HARDDISK DRIVER (hd.c)\n", hwif->name); + return (hwif->present = 0); + } +#endif /* CONFIG_BLK_DEV_HD */ + + hwif->present = 0; /* we set it back to 1 if all is ok below */ + + if (register_blkdev (hwif->major, hwif->name, ide_fops)) { + printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", + hwif->name, hwif->major); + return (hwif->present = 0); + } + + if (init_irq(hwif)) { + int i = hwif->irq; + /* + * It failed to initialise. Find the default IRQ for + * this port and try that. + */ + if (!(hwif->irq = ide_default_irq(hwif->io_ports[IDE_DATA_OFFSET]))) { + printk("%s: Disabled unable to get IRQ %d.\n", + hwif->name, i); + (void) unregister_blkdev (hwif->major, hwif->name); + return (hwif->present = 0); + } + if (init_irq(hwif)) { + printk("%s: probed IRQ %d and default IRQ %d failed.\n", + hwif->name, i, hwif->irq); + (void) unregister_blkdev (hwif->major, hwif->name); + return (hwif->present = 0); + } + printk("%s: probed IRQ %d failed, using default.\n", + hwif->name, hwif->irq); + } + + init_gendisk(hwif); + blk_dev[hwif->major].data = hwif; + blk_dev[hwif->major].queue = ide_get_queue; + hwif->present = 1; /* success */ + +#if (DEBUG_SPINLOCK > 0) +{ + static int done = 0; + if (!done++) + printk("ide_lock is %p\n", &ide_lock); /* FIXME */ +} +#endif + return hwif->present; +} + +void export_ide_init_queue (ide_drive_t *drive) +{ + ide_init_queue(drive); +} + +byte export_probe_for_drive (ide_drive_t *drive) +{ + return probe_for_drive(drive); +} + +EXPORT_SYMBOL(export_ide_init_queue); +EXPORT_SYMBOL(export_probe_for_drive); + +int ideprobe_init (void); +static ide_module_t ideprobe_module = { + IDE_PROBE_MODULE, + ideprobe_init, + NULL +}; + +int ideprobe_init (void) +{ + unsigned int index; + int probe[MAX_HWIFS]; + + MOD_INC_USE_COUNT; + memset(probe, 0, MAX_HWIFS * sizeof(int)); + for (index = 0; index < MAX_HWIFS; ++index) + probe[index] = !ide_hwifs[index].present; + + /* + * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports + */ + for (index = 0; index < MAX_HWIFS; ++index) + if (probe[index]) + probe_hwif(&ide_hwifs[index]); + for (index = 0; index < MAX_HWIFS; ++index) + if (probe[index]) + hwif_init(&ide_hwifs[index]); + if (!ide_probe) + ide_probe = &ideprobe_module; + MOD_DEC_USE_COUNT; + return 0; +} + +#ifdef MODULE +extern int (*ide_xlate_1024_hook)(kdev_t, int, int, const char *); + +int init_module (void) +{ + unsigned int index; + + for (index = 0; index < MAX_HWIFS; ++index) + ide_unregister(index); + ideprobe_init(); + create_proc_ide_interfaces(); + ide_xlate_1024_hook = ide_xlate_1024; + return 0; +} + +void cleanup_module (void) +{ + ide_probe = NULL; + ide_xlate_1024_hook = 0; +} +MODULE_LICENSE("GPL"); +#endif /* MODULE */ diff -Nru a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-proc.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,961 @@ +/* + * linux/drivers/ide/ide-proc.c Version 1.03 January 2, 1998 + * + * Copyright (C) 1997-1998 Mark Lord + */ + +/* + * This is the /proc/ide/ filesystem implementation. + * + * The major reason this exists is to provide sufficient access + * to driver and config data, such that user-mode programs can + * be developed to handle chipset tuning for most PCI interfaces. + * This should provide better utilities, and less kernel bloat. + * + * The entire pci config space for a PCI interface chipset can be + * retrieved by just reading it. e.g. "cat /proc/ide3/config" + * + * To modify registers *safely*, do something like: + * echo "P40:88" >/proc/ide/ide3/config + * That expression writes 0x88 to pci config register 0x40 + * on the chip which controls ide3. Multiple tuples can be issued, + * and the writes will be completed as an atomic set: + * echo "P40:88 P41:35 P42:00 P43:00" >/proc/ide/ide3/config + * + * All numbers must be specified using pairs of ascii hex digits. + * It is important to note that these writes will be performed + * after waiting for the IDE controller (both interfaces) + * to be completely idle, to ensure no corruption of I/O in progress. + * + * Non-PCI registers can also be written, using "R" in place of "P" + * in the above examples. The size of the port transfer is determined + * by the number of pairs of hex digits given for the data. If a two + * digit value is given, the write will be a byte operation; if four + * digits are used, the write will be performed as a 16-bit operation; + * and if eight digits are specified, a 32-bit "dword" write will be + * performed. Odd numbers of digits are not permitted. + * + * If there is an error *anywhere* in the string of registers/data + * then *none* of the writes will be performed. + * + * Drive/Driver settings can be retrieved by reading the drive's + * "settings" files. e.g. "cat /proc/ide0/hda/settings" + * To write a new value "val" into a specific setting "name", use: + * echo "name:val" >/proc/ide/ide0/hda/settings + * + * Also useful, "cat /proc/ide0/hda/[identify, smart_values, + * smart_thresholds, capabilities]" will issue an IDENTIFY / + * PACKET_IDENTIFY / SMART_READ_VALUES / SMART_READ_THRESHOLDS / + * SENSE CAPABILITIES command to /dev/hda, and then dump out the + * returned data as 256 16-bit words. The "hdparm" utility will + * be updated someday soon to use this mechanism. + * + * Feel free to develop and distribute fancy GUI configuration + * utilities for your favorite PCI chipsets. I'll be working on + * one for the Promise 20246 someday soon. -ml + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifdef CONFIG_BLK_DEV_AEC62XX +extern byte aec62xx_proc; +int (*aec62xx_display_info)(char *, char **, off_t, int) = NULL; +#endif /* CONFIG_BLK_DEV_AEC62XX */ +#ifdef CONFIG_BLK_DEV_ALI15X3 +extern byte ali_proc; +int (*ali_display_info)(char *, char **, off_t, int) = NULL; +#endif /* CONFIG_BLK_DEV_ALI15X3 */ +#ifdef CONFIG_BLK_DEV_AMD74XX +extern byte amd74xx_proc; +int (*amd74xx_display_info)(char *, char **, off_t, int) = NULL; +#endif /* CONFIG_BLK_DEV_AMD74XX */ +#ifdef CONFIG_BLK_DEV_CMD64X +extern byte cmd64x_proc; +int (*cmd64x_display_info)(char *, char **, off_t, int) = NULL; +#endif /* CONFIG_BLK_DEV_CMD64X */ +#ifdef CONFIG_BLK_DEV_CS5530 +extern byte cs5530_proc; +int (*cs5530_display_info)(char *, char **, off_t, int) = NULL; +#endif /* CONFIG_BLK_DEV_CS5530 */ +#ifdef CONFIG_BLK_DEV_HPT34X +extern byte hpt34x_proc; +int (*hpt34x_display_info)(char *, char **, off_t, int) = NULL; +#endif /* CONFIG_BLK_DEV_HPT34X */ +#ifdef CONFIG_BLK_DEV_HPT366 +extern byte hpt366_proc; +int (*hpt366_display_info)(char *, char **, off_t, int) = NULL; +#endif /* CONFIG_BLK_DEV_HPT366 */ +#ifdef CONFIG_BLK_DEV_PDC202XX +extern byte pdc202xx_proc; +int (*pdc202xx_display_info)(char *, char **, off_t, int) = NULL; +#endif /* CONFIG_BLK_DEV_PDC202XX */ +#ifdef CONFIG_BLK_DEV_PIIX +extern byte piix_proc; +int (*piix_display_info)(char *, char **, off_t, int) = NULL; +#endif /* CONFIG_BLK_DEV_PIIX */ +#ifdef CONFIG_BLK_DEV_SVWKS +extern byte svwks_proc; +int (*svwks_display_info)(char *, char **, off_t, int) = NULL; +#endif /* CONFIG_BLK_DEV_SVWKS */ +#ifdef CONFIG_BLK_DEV_SIS5513 +extern byte sis_proc; +int (*sis_display_info)(char *, char **, off_t, int) = NULL; +#endif /* CONFIG_BLK_DEV_SIS5513 */ +#ifdef CONFIG_BLK_DEV_SLC90E66 +extern byte slc90e66_proc; +int (*slc90e66_display_info)(char *, char **, off_t, int) = NULL; +#endif /* CONFIG_BLK_DEV_SLC90E66 */ +#ifdef CONFIG_BLK_DEV_VIA82CXXX +extern byte via_proc; +int (*via_display_info)(char *, char **, off_t, int) = NULL; +#endif /* CONFIG_BLK_DEV_VIA82CXXX */ + +static int ide_getxdigit(char c) +{ + int digit; + if (isdigit(c)) + digit = c - '0'; + else if (isxdigit(c)) + digit = tolower(c) - 'a' + 10; + else + digit = -1; + return digit; +} + +static int xx_xx_parse_error (const char *data, unsigned long len, const char *msg) +{ + char errbuf[16]; + int i; + if (len >= sizeof(errbuf)) + len = sizeof(errbuf) - 1; + for (i = 0; i < len; ++i) { + char c = data[i]; + if (!c || c == '\n') + c = '\0'; + else if (iscntrl(c)) + c = '?'; + errbuf[i] = c; + } + errbuf[i] = '\0'; + printk("proc_ide: error: %s: '%s'\n", msg, errbuf); + return -EINVAL; +} + +static struct proc_dir_entry * proc_ide_root = NULL; + +static int proc_ide_write_config + (struct file *file, const char *buffer, unsigned long count, void *data) +{ + ide_hwif_t *hwif = (ide_hwif_t *)data; + int for_real = 0; + unsigned long startn = 0, n, flags; + const char *start = NULL, *msg = NULL; + + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + /* + * Skip over leading whitespace + */ + while (count && isspace(*buffer)) { + --count; + ++buffer; + } + /* + * Do one full pass to verify all parameters, + * then do another to actually write the regs. + */ + spin_lock_irqsave(&ide_lock, flags); + do { + const char *p; + if (for_real) { + unsigned long timeout = jiffies + (3 * HZ); + ide_hwgroup_t *mygroup = (ide_hwgroup_t *)(hwif->hwgroup); + ide_hwgroup_t *mategroup = NULL; + if (hwif->mate && hwif->mate->hwgroup) + mategroup = (ide_hwgroup_t *)(hwif->mate->hwgroup); + spin_lock_irqsave(&ide_lock, flags); + while (mygroup->busy || + (mategroup && mategroup->busy)) { + spin_unlock_irqrestore(&ide_lock, flags); + if (time_after(jiffies, timeout)) { + printk("/proc/ide/%s/config: channel(s) busy, cannot write\n", hwif->name); + spin_unlock_irqrestore(&ide_lock, flags); + return -EBUSY; + } + spin_lock_irqsave(&ide_lock, flags); + } + } + p = buffer; + n = count; + while (n > 0) { + int d, digits; + unsigned int reg = 0, val = 0, is_pci; + start = p; + startn = n--; + switch (*p++) { + case 'R': is_pci = 0; + break; + case 'P': is_pci = 1; +#ifdef CONFIG_BLK_DEV_IDEPCI + if (hwif->pci_dev && !IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL)) + break; +#endif /* CONFIG_BLK_DEV_IDEPCI */ + msg = "not a PCI device"; + goto parse_error; + default: msg = "expected 'R' or 'P'"; + goto parse_error; + } + digits = 0; + while (n > 0 && (d = ide_getxdigit(*p)) >= 0) { + reg = (reg << 4) | d; + --n; + ++p; + ++digits; + } + if (!digits || (digits > 4) || (is_pci && reg > 0xff)) { + msg = "bad/missing register number"; + goto parse_error; + } + if (n-- == 0 || *p++ != ':') { + msg = "missing ':'"; + goto parse_error; + } + digits = 0; + while (n > 0 && (d = ide_getxdigit(*p)) >= 0) { + val = (val << 4) | d; + --n; + ++p; + ++digits; + } + if (digits != 2 && digits != 4 && digits != 8) { + msg = "bad data, 2/4/8 digits required"; + goto parse_error; + } + if (n > 0 && !isspace(*p)) { + msg = "expected whitespace after data"; + goto parse_error; + } + while (n > 0 && isspace(*p)) { + --n; + ++p; + } +#ifdef CONFIG_BLK_DEV_IDEPCI + if (is_pci && (reg & ((digits >> 1) - 1))) { + msg = "misaligned access"; + goto parse_error; + } +#endif /* CONFIG_BLK_DEV_IDEPCI */ + if (for_real) { +#if 0 + printk("proc_ide_write_config: type=%c, reg=0x%x, val=0x%x, digits=%d\n", is_pci ? "PCI" : "non-PCI", reg, val, digits); +#endif + if (is_pci) { +#ifdef CONFIG_BLK_DEV_IDEPCI + int rc = 0; + struct pci_dev *dev = hwif->pci_dev; + switch (digits) { + case 2: msg = "byte"; + rc = pci_write_config_byte(dev, reg, val); + break; + case 4: msg = "word"; + rc = pci_write_config_word(dev, reg, val); + break; + case 8: msg = "dword"; + rc = pci_write_config_dword(dev, reg, val); + break; + } + if (rc) { + spin_unlock_irqrestore(&ide_lock, flags); + printk("proc_ide_write_config: error writing %s at bus %02x dev %02x reg 0x%x value 0x%x\n", + msg, dev->bus->number, dev->devfn, reg, val); + printk("proc_ide_write_config: error %d\n", rc); + return -EIO; + } +#endif /* CONFIG_BLK_DEV_IDEPCI */ + } else { /* not pci */ +#if !defined(__mc68000__) && !defined(CONFIG_APUS) + +/* + * Geert Uytterhoeven + * + * unless you can explain me what it really does. + * On m68k, we don't have outw() and outl() yet, + * and I need a good reason to implement it. + * + * BTW, IMHO the main remaining portability problem with the IDE driver + * is that it mixes IO (ioport) and MMIO (iomem) access on different platforms. + * + * I think all accesses should be done using + * + * ide_in[bwl](ide_device_instance, offset) + * ide_out[bwl](ide_device_instance, value, offset) + * + * so the architecture specific code can #define ide_{in,out}[bwl] to the + * appropriate function. + * + */ + switch (digits) { + case 2: OUT_BYTE(val, reg); + break; + case 4: OUT_WORD(val, reg); + break; + case 8: outl(val, reg); + break; + } +#endif /* !__mc68000__ && !CONFIG_APUS */ + } + } + } + } while (!for_real++); + spin_unlock_irqrestore(&ide_lock, flags); + return count; +parse_error: + spin_unlock_irqrestore(&ide_lock, flags); + printk("parse error\n"); + return xx_xx_parse_error(start, startn, msg); +} + +static int proc_ide_read_config + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *out = page; + int len; + +#ifdef CONFIG_BLK_DEV_IDEPCI + ide_hwif_t *hwif = (ide_hwif_t *)data; + struct pci_dev *dev = hwif->pci_dev; + if (!IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL) && dev && dev->bus) { + int reg = 0; + + out += sprintf(out, "pci bus %02x device %02x vid %04x did %04x channel %d\n", + dev->bus->number, dev->devfn, hwif->pci_devid.vid, hwif->pci_devid.did, hwif->channel); + do { + byte val; + int rc = pci_read_config_byte(dev, reg, &val); + if (rc) { + printk("proc_ide_read_config: error %d reading bus %02x dev %02x reg 0x%02x\n", + rc, dev->bus->number, dev->devfn, reg); + out += sprintf(out, "??%c", (++reg & 0xf) ? ' ' : '\n'); + } else + out += sprintf(out, "%02x%c", val, (++reg & 0xf) ? ' ' : '\n'); + } while (reg < 0x100); + } else +#endif /* CONFIG_BLK_DEV_IDEPCI */ + out += sprintf(out, "(none)\n"); + len = out - page; + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + + +static int ide_getdigit(char c) +{ + int digit; + if (isdigit(c)) + digit = c - '0'; + else + digit = -1; + return digit; +} + +static int proc_ide_read_drivers + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + char *out = page; + int len; + ide_module_t *p = ide_modules; + ide_driver_t *driver; + + while (p) { + driver = (ide_driver_t *) p->info; + if (driver) + out += sprintf(out, "%s version %s\n", driver->name, driver->version); + p = p->next; + } + len = out - page; + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_imodel + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_hwif_t *hwif = (ide_hwif_t *) data; + int len; + const char *name; + + switch (hwif->chipset) { + case ide_unknown: name = "(none)"; break; + case ide_generic: name = "generic"; break; + case ide_pci: name = "pci"; break; + case ide_cmd640: name = "cmd640"; break; + case ide_dtc2278: name = "dtc2278"; break; + case ide_ali14xx: name = "ali14xx"; break; + case ide_qd65xx: name = "qd65xx"; break; + case ide_umc8672: name = "umc8672"; break; + case ide_ht6560b: name = "ht6560b"; break; + case ide_pdc4030: name = "pdc4030"; break; + case ide_rz1000: name = "rz1000"; break; + case ide_trm290: name = "trm290"; break; + case ide_cmd646: name = "cmd646"; break; + case ide_cy82c693: name = "cy82c693"; break; + case ide_4drives: name = "4drives"; break; + case ide_pmac: name = "mac-io"; break; + default: name = "(unknown)"; break; + } + len = sprintf(page, "%s\n", name); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_mate + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_hwif_t *hwif = (ide_hwif_t *) data; + int len; + + if (hwif && hwif->mate && hwif->mate->present) + len = sprintf(page, "%s\n", hwif->mate->name); + else + len = sprintf(page, "(none)\n"); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_channel + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_hwif_t *hwif = (ide_hwif_t *) data; + int len; + + page[0] = hwif->channel ? '1' : '0'; + page[1] = '\n'; + len = 2; + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_identify + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *)data; + int len = 0, i = 0; + + if (drive && !taskfile_lib_get_identify(drive, page)) { + unsigned short *val = (unsigned short *) page; + char *out = ((char *)val) + (SECTOR_WORDS * 4); + page = out; + do { + out += sprintf(out, "%04x%c", le16_to_cpu(*val), (++i & 7) ? ' ' : '\n'); + val += 1; + } while (i < (SECTOR_WORDS * 2)); + len = out - page; + } + else + len = sprintf(page, "\n"); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_settings + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + ide_settings_t *setting = (ide_settings_t *) drive->settings; + char *out = page; + int len, rc, mul_factor, div_factor; + + out += sprintf(out, "name\t\t\tvalue\t\tmin\t\tmax\t\tmode\n"); + out += sprintf(out, "----\t\t\t-----\t\t---\t\t---\t\t----\n"); + while(setting) { + mul_factor = setting->mul_factor; + div_factor = setting->div_factor; + out += sprintf(out, "%-24s", setting->name); + if ((rc = ide_read_setting(drive, setting)) >= 0) + out += sprintf(out, "%-16d", rc * mul_factor / div_factor); + else + out += sprintf(out, "%-16s", "write-only"); + out += sprintf(out, "%-16d%-16d", (setting->min * mul_factor + div_factor - 1) / div_factor, setting->max * mul_factor / div_factor); + if (setting->rw & SETTING_READ) + out += sprintf(out, "r"); + if (setting->rw & SETTING_WRITE) + out += sprintf(out, "w"); + out += sprintf(out, "\n"); + setting = setting->next; + } + len = out - page; + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +#define MAX_LEN 30 + +static int proc_ide_write_settings + (struct file *file, const char *buffer, unsigned long count, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + char name[MAX_LEN + 1]; + int for_real = 0, len; + unsigned long n; + const char *start = NULL; + ide_settings_t *setting; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + /* + * Skip over leading whitespace + */ + while (count && isspace(*buffer)) { + --count; + ++buffer; + } + /* + * Do one full pass to verify all parameters, + * then do another to actually write the new settings. + */ + do { + const char *p; + p = buffer; + n = count; + while (n > 0) { + int d, digits; + unsigned int val = 0; + start = p; + + while (n > 0 && *p != ':') { + --n; + p++; + } + if (*p != ':') + goto parse_error; + len = IDE_MIN(p - start, MAX_LEN); + strncpy(name, start, IDE_MIN(len, MAX_LEN)); + name[len] = 0; + + if (n > 0) { + --n; + p++; + } else + goto parse_error; + + digits = 0; + while (n > 0 && (d = ide_getdigit(*p)) >= 0) { + val = (val * 10) + d; + --n; + ++p; + ++digits; + } + if (n > 0 && !isspace(*p)) + goto parse_error; + while (n > 0 && isspace(*p)) { + --n; + ++p; + } + setting = ide_find_setting_by_name(drive, name); + if (!setting) + goto parse_error; + + if (for_real) + ide_write_setting(drive, setting, val * setting->div_factor / setting->mul_factor); + } + } while (!for_real++); + return count; +parse_error: + printk("proc_ide_write_settings(): parse error\n"); + return -EINVAL; +} + +int proc_ide_read_capacity + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + ide_driver_t *driver = (ide_driver_t *) drive->driver; + int len; + + if (!driver) + len = sprintf(page, "(none)\n"); + else + len = sprintf(page,"%llu\n", + (unsigned long long) ((ide_driver_t *)drive->driver)->capacity(drive)); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +int proc_ide_read_geometry + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + char *out = page; + int len; + + out += sprintf(out,"physical %d/%d/%d\n", drive->cyl, drive->head, drive->sect); + out += sprintf(out,"logical %d/%d/%d\n", drive->bios_cyl, drive->bios_head, drive->bios_sect); + len = out - page; + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_dmodel + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + struct hd_driveid *id = drive->id; + int len; + + len = sprintf(page, "%.40s\n", (id && id->model[0]) ? (char *)id->model : "(none)"); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_read_driver + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + ide_driver_t *driver = (ide_driver_t *) drive->driver; + int len; + + if (!driver) + len = sprintf(page, "(none)\n"); + else + len = sprintf(page, "%s version %s\n", driver->name, driver->version); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static int proc_ide_write_driver + (struct file *file, const char *buffer, unsigned long count, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (ide_replace_subdriver(drive, buffer)) + return -EINVAL; + return count; +} + +static int proc_ide_read_media + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + const char *media; + int len; + + switch (drive->media) { + case ide_disk: media = "disk\n"; + break; + case ide_cdrom: media = "cdrom\n"; + break; + case ide_tape: media = "tape\n"; + break; + case ide_floppy:media = "floppy\n"; + break; + default: media = "UNKNOWN\n"; + break; + } + strcpy(page,media); + len = strlen(media); + PROC_IDE_READ_RETURN(page,start,off,count,eof,len); +} + +static ide_proc_entry_t generic_drive_entries[] = { + { "driver", S_IFREG|S_IRUGO, proc_ide_read_driver, proc_ide_write_driver }, + { "identify", S_IFREG|S_IRUSR, proc_ide_read_identify, NULL }, + { "media", S_IFREG|S_IRUGO, proc_ide_read_media, NULL }, + { "model", S_IFREG|S_IRUGO, proc_ide_read_dmodel, NULL }, + { "settings", S_IFREG|S_IRUSR|S_IWUSR,proc_ide_read_settings, proc_ide_write_settings }, + { NULL, 0, NULL, NULL } +}; + +void ide_add_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p, void *data) +{ + struct proc_dir_entry *ent; + + if (!dir || !p) + return; + while (p->name != NULL) { + ent = create_proc_entry(p->name, p->mode, dir); + if (!ent) return; + ent->nlink = 1; + ent->data = data; + ent->read_proc = p->read_proc; + ent->write_proc = p->write_proc; + p++; + } +} + +void ide_remove_proc_entries(struct proc_dir_entry *dir, ide_proc_entry_t *p) +{ + if (!dir || !p) + return; + while (p->name != NULL) { + remove_proc_entry(p->name, dir); + p++; + } +} + +static void create_proc_ide_drives(ide_hwif_t *hwif) +{ + int d; + struct proc_dir_entry *ent; + struct proc_dir_entry *parent = hwif->proc; + char name[64]; + + for (d = 0; d < MAX_DRIVES; d++) { + ide_drive_t *drive = &hwif->drives[d]; + ide_driver_t *driver = drive->driver; + + if (!drive->present) + continue; + if (drive->proc) + continue; + + drive->proc = proc_mkdir(drive->name, parent); + if (drive->proc) { + ide_add_proc_entries(drive->proc, generic_drive_entries, drive); + if (driver) { + ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive); + ide_add_proc_entries(drive->proc, driver->proc, drive); + } + } + sprintf(name,"ide%d/%s", (drive->name[2]-'a')/2, drive->name); + ent = proc_symlink(drive->name, proc_ide_root, name); + if (!ent) return; + } +} + +void recreate_proc_ide_device(ide_hwif_t *hwif, ide_drive_t *drive) +{ + struct proc_dir_entry *ent; + struct proc_dir_entry *parent = hwif->proc; + char name[64]; +// ide_driver_t *driver = drive->driver; + + if (drive->present && !drive->proc) { + drive->proc = proc_mkdir(drive->name, parent); + if (drive->proc) + ide_add_proc_entries(drive->proc, generic_drive_entries, drive); + +/* + * assume that we have these already, however, should test FIXME! + * if (driver) { + * ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive); + * ide_add_proc_entries(drive->proc, driver->proc, drive); + * } + * + */ + sprintf(name,"ide%d/%s", (drive->name[2]-'a')/2, drive->name); + ent = proc_symlink(drive->name, proc_ide_root, name); + if (!ent) + return; + } +} + +void destroy_proc_ide_device(ide_hwif_t *hwif, ide_drive_t *drive) +{ + ide_driver_t *driver = drive->driver; + + if (drive->proc) { + if (driver) + ide_remove_proc_entries(drive->proc, driver->proc); + ide_remove_proc_entries(drive->proc, generic_drive_entries); + remove_proc_entry(drive->name, proc_ide_root); + remove_proc_entry(drive->name, hwif->proc); + drive->proc = NULL; + } +} + +void destroy_proc_ide_drives(ide_hwif_t *hwif) +{ + int d; + + for (d = 0; d < MAX_DRIVES; d++) { + ide_drive_t *drive = &hwif->drives[d]; +// ide_driver_t *driver = drive->driver; + + if (drive->proc) + destroy_proc_ide_device(hwif, drive); + } +} + +static ide_proc_entry_t hwif_entries[] = { + { "channel", S_IFREG|S_IRUGO, proc_ide_read_channel, NULL }, + { "config", S_IFREG|S_IRUGO|S_IWUSR,proc_ide_read_config, proc_ide_write_config }, + { "mate", S_IFREG|S_IRUGO, proc_ide_read_mate, NULL }, + { "model", S_IFREG|S_IRUGO, proc_ide_read_imodel, NULL }, + { NULL, 0, NULL, NULL } +}; + +void create_proc_ide_interfaces(void) +{ + int h; + + for (h = 0; h < MAX_HWIFS; h++) { + ide_hwif_t *hwif = &ide_hwifs[h]; + + if (!hwif->present) + continue; + if (!hwif->proc) { + hwif->proc = proc_mkdir(hwif->name, proc_ide_root); + if (!hwif->proc) + return; + ide_add_proc_entries(hwif->proc, hwif_entries, hwif); + } + create_proc_ide_drives(hwif); + } +} + +static void destroy_proc_ide_interfaces(void) +{ + int h; + + for (h = 0; h < MAX_HWIFS; h++) { + ide_hwif_t *hwif = &ide_hwifs[h]; + int exist = (hwif->proc != NULL); +#if 0 + if (!hwif->present) + continue; +#endif + if (exist) { + destroy_proc_ide_drives(hwif); + ide_remove_proc_entries(hwif->proc, hwif_entries); + remove_proc_entry(hwif->name, proc_ide_root); + hwif->proc = NULL; + } else + continue; + } +} + +void proc_ide_create(void) +{ + proc_ide_root = proc_mkdir("ide", 0); + if (!proc_ide_root) return; + + create_proc_ide_interfaces(); + + create_proc_read_entry("drivers", 0, proc_ide_root, + proc_ide_read_drivers, NULL); + +#ifdef CONFIG_BLK_DEV_AEC62XX + if ((aec62xx_display_info) && (aec62xx_proc)) + create_proc_info_entry("aec62xx", 0, proc_ide_root, aec62xx_display_info); +#endif /* CONFIG_BLK_DEV_AEC62XX */ +#ifdef CONFIG_BLK_DEV_ALI15X3 + if ((ali_display_info) && (ali_proc)) + create_proc_info_entry("ali", 0, proc_ide_root, ali_display_info); +#endif /* CONFIG_BLK_DEV_ALI15X3 */ +#ifdef CONFIG_BLK_DEV_AMD74XX + if ((amd74xx_display_info) && (amd74xx_proc)) + create_proc_info_entry("amd74xx", 0, proc_ide_root, amd74xx_display_info); +#endif /* CONFIG_BLK_DEV_AMD74XX */ +#ifdef CONFIG_BLK_DEV_CMD64X + if ((cmd64x_display_info) && (cmd64x_proc)) + create_proc_info_entry("cmd64x", 0, proc_ide_root, cmd64x_display_info); +#endif /* CONFIG_BLK_DEV_CMD64X */ +#ifdef CONFIG_BLK_DEV_CS5530 + if ((cs5530_display_info) && (cs5530_proc)) + create_proc_info_entry("cs5530", 0, proc_ide_root, cs5530_display_info); +#endif /* CONFIG_BLK_DEV_CS5530 */ +#ifdef CONFIG_BLK_DEV_HPT34X + if ((hpt34x_display_info) && (hpt34x_proc)) + create_proc_info_entry("hpt34x", 0, proc_ide_root, hpt34x_display_info); +#endif /* CONFIG_BLK_DEV_HPT34X */ +#ifdef CONFIG_BLK_DEV_HPT366 + if ((hpt366_display_info) && (hpt366_proc)) + create_proc_info_entry("hpt366", 0, proc_ide_root, hpt366_display_info); +#endif /* CONFIG_BLK_DEV_HPT366 */ +#ifdef CONFIG_BLK_DEV_SVWKS + if ((svwks_display_info) && (svwks_proc)) + create_proc_info_entry("svwks", 0, proc_ide_root, svwks_display_info); +#endif /* CONFIG_BLK_DEV_SVWKS */ +#ifdef CONFIG_BLK_DEV_PDC202XX + if ((pdc202xx_display_info) && (pdc202xx_proc)) + create_proc_info_entry("pdc202xx", 0, proc_ide_root, pdc202xx_display_info); +#endif /* CONFIG_BLK_DEV_PDC202XX */ +#ifdef CONFIG_BLK_DEV_PIIX + if ((piix_display_info) && (piix_proc)) + create_proc_info_entry("piix", 0, proc_ide_root, piix_display_info); +#endif /* CONFIG_BLK_DEV_PIIX */ +#ifdef CONFIG_BLK_DEV_SIS5513 + if ((sis_display_info) && (sis_proc)) + create_proc_info_entry("sis", 0, proc_ide_root, sis_display_info); +#endif /* CONFIG_BLK_DEV_SIS5513 */ +#ifdef CONFIG_BLK_DEV_SLC90E66 + if ((slc90e66_display_info) && (slc90e66_proc)) + create_proc_info_entry("slc90e66", 0, proc_ide_root, slc90e66_display_info); +#endif /* CONFIG_BLK_DEV_SLC90E66 */ +#ifdef CONFIG_BLK_DEV_VIA82CXXX + if ((via_display_info) && (via_proc)) + create_proc_info_entry("via", 0, proc_ide_root, via_display_info); +#endif /* CONFIG_BLK_DEV_VIA82CXXX */ +} + +void proc_ide_destroy(void) +{ + /* + * Mmmm.. does this free up all resources, + * or do we need to do a more proper cleanup here ?? + */ +#ifdef CONFIG_BLK_DEV_AEC62XX + if ((aec62xx_display_info) && (aec62xx_proc)) + remove_proc_entry("ide/aec62xx",0); +#endif /* CONFIG_BLK_DEV_AEC62XX */ +#ifdef CONFIG_BLK_DEV_ALI15X3 + if ((ali_display_info) && (ali_proc)) + remove_proc_entry("ide/ali",0); +#endif /* CONFIG_BLK_DEV_ALI15X3 */ +#ifdef CONFIG_BLK_DEV_AMD74XX + if ((amd74xx_display_info) && (amd74xx_proc)) + remove_proc_entry("ide/amd74xx",0); +#endif /* CONFIG_BLK_DEV_AMD74XX */ +#ifdef CONFIG_BLK_DEV_CMD64X + if ((cmd64x_display_info) && (cmd64x_proc)) + remove_proc_entry("ide/cmd64x",0); +#endif /* CONFIG_BLK_DEV_CMD64X */ +#ifdef CONFIG_BLK_DEV_CS5530 + if ((cs5530_display_info) && (cs5530_proc)) + remove_proc_entry("ide/cs5530",0); +#endif /* CONFIG_BLK_DEV_CS5530 */ +#ifdef CONFIG_BLK_DEV_HPT34X + if ((hpt34x_display_info) && (hpt34x_proc)) + remove_proc_entry("ide/hpt34x",0); +#endif /* CONFIG_BLK_DEV_HPT34X */ +#ifdef CONFIG_BLK_DEV_HPT366 + if ((hpt366_display_info) && (hpt366_proc)) + remove_proc_entry("ide/hpt366",0); +#endif /* CONFIG_BLK_DEV_HPT366 */ +#ifdef CONFIG_BLK_DEV_PDC202XX + if ((pdc202xx_display_info) && (pdc202xx_proc)) + remove_proc_entry("ide/pdc202xx",0); +#endif /* CONFIG_BLK_DEV_PDC202XX */ +#ifdef CONFIG_BLK_DEV_PIIX + if ((piix_display_info) && (piix_proc)) + remove_proc_entry("ide/piix",0); +#endif /* CONFIG_BLK_DEV_PIIX */ +#ifdef CONFIG_BLK_DEV_SVWKS + if ((svwks_display_info) && (svwks_proc)) + remove_proc_entry("ide/svwks",0); +#endif /* CONFIG_BLK_DEV_SVWKS */ +#ifdef CONFIG_BLK_DEV_SIS5513 + if ((sis_display_info) && (sis_proc)) + remove_proc_entry("ide/sis", 0); +#endif /* CONFIG_BLK_DEV_SIS5513 */ +#ifdef CONFIG_BLK_DEV_SLC90E66 + if ((slc90e66_display_info) && (slc90e66_proc)) + remove_proc_entry("ide/slc90e66",0); +#endif /* CONFIG_BLK_DEV_SLC90E66 */ +#ifdef CONFIG_BLK_DEV_VIA82CXXX + if ((via_display_info) && (via_proc)) + remove_proc_entry("ide/via",0); +#endif /* CONFIG_BLK_DEV_VIA82CXXX */ + + remove_proc_entry("ide/drivers", 0); + destroy_proc_ide_interfaces(); + remove_proc_entry("ide", 0); +} diff -Nru a/drivers/ide/ide-swarm.c b/drivers/ide/ide-swarm.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-swarm.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2001 Broadcom Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* Derived loosely from ide-pmac.c, so: + * + * Copyright (C) 1998 Paul Mackerras. + * Copyright (C) 1995-1998 Mark Lord + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void __init swarm_ide_probe(void) +{ + int i; + ide_hwif_t *hwif; + /* + * Find the first untaken slot in hwifs + */ + for (i = 0; i < MAX_HWIFS; i++) { + if (!ide_hwifs[i].io_ports[IDE_DATA_OFFSET]) { + break; + } + } + if (i == MAX_HWIFS) { + printk("No space for SWARM onboard IDE driver in ide_hwifs[]. Not enabled.\n"); + return; + } + + /* Set up our stuff */ + hwif = &ide_hwifs[i]; + hwif->hw.io_ports[IDE_DATA_OFFSET] = SWARM_IDE_REG(0x1f0); + hwif->hw.io_ports[IDE_ERROR_OFFSET] = SWARM_IDE_REG(0x1f1); + hwif->hw.io_ports[IDE_NSECTOR_OFFSET] = SWARM_IDE_REG(0x1f2); + hwif->hw.io_ports[IDE_SECTOR_OFFSET] = SWARM_IDE_REG(0x1f3); + hwif->hw.io_ports[IDE_LCYL_OFFSET] = SWARM_IDE_REG(0x1f4); + hwif->hw.io_ports[IDE_HCYL_OFFSET] = SWARM_IDE_REG(0x1f5); + hwif->hw.io_ports[IDE_SELECT_OFFSET] = SWARM_IDE_REG(0x1f6); + hwif->hw.io_ports[IDE_STATUS_OFFSET] = SWARM_IDE_REG(0x1f7); + hwif->hw.io_ports[IDE_CONTROL_OFFSET] = SWARM_IDE_REG(0x3f6); + hwif->hw.io_ports[IDE_IRQ_OFFSET] = SWARM_IDE_REG(0x3f7); +// hwif->hw->ack_intr = swarm_ide_ack_intr; + hwif->hw.irq = SWARM_IDE_INT; + hwif->ideproc = swarm_ideproc; + + memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports)); + hwif->irq = hwif->hw.irq; + printk("SWARM onboard IDE configured as device %i\n", i); +} + diff -Nru a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-tape.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,6558 @@ +/* + * linux/drivers/ide/ide-tape.c Version 1.17a Jan, 2001 + * + * Copyright (C) 1995 - 1999 Gadi Oxman + * + * $Header$ + * + * This driver was constructed as a student project in the software laboratory + * of the faculty of electrical engineering in the Technion - Israel's + * Institute Of Technology, with the guide of Avner Lottem and Dr. Ilana David. + * + * It is hereby placed under the terms of the GNU general public license. + * (See linux/COPYING). + */ + +/* + * IDE ATAPI streaming tape driver. + * + * This driver is a part of the Linux ide driver and works in co-operation + * with linux/drivers/block/ide.c. + * + * The driver, in co-operation with ide.c, basically traverses the + * request-list for the block device interface. The character device + * interface, on the other hand, creates new requests, adds them + * to the request-list of the block device, and waits for their completion. + * + * Pipelined operation mode is now supported on both reads and writes. + * + * The block device major and minor numbers are determined from the + * tape's relative position in the ide interfaces, as explained in ide.c. + * + * The character device interface consists of the following devices: + * + * ht0 major 37, minor 0 first IDE tape, rewind on close. + * ht1 major 37, minor 1 second IDE tape, rewind on close. + * ... + * nht0 major 37, minor 128 first IDE tape, no rewind on close. + * nht1 major 37, minor 129 second IDE tape, no rewind on close. + * ... + * + * Run linux/scripts/MAKEDEV.ide to create the above entries. + * + * The general magnetic tape commands compatible interface, as defined by + * include/linux/mtio.h, is accessible through the character device. + * + * General ide driver configuration options, such as the interrupt-unmask + * flag, can be configured by issuing an ioctl to the block device interface, + * as any other ide device. + * + * Our own ide-tape ioctl's can be issued to either the block device or + * the character device interface. + * + * Maximal throughput with minimal bus load will usually be achieved in the + * following scenario: + * + * 1. ide-tape is operating in the pipelined operation mode. + * 2. No buffering is performed by the user backup program. + * + * Testing was done with a 2 GB CONNER CTMA 4000 IDE ATAPI Streaming Tape Drive. + * + * Ver 0.1 Nov 1 95 Pre-working code :-) + * Ver 0.2 Nov 23 95 A short backup (few megabytes) and restore procedure + * was successful ! (Using tar cvf ... on the block + * device interface). + * A longer backup resulted in major swapping, bad + * overall Linux performance and eventually failed as + * we received non serial read-ahead requests from the + * buffer cache. + * Ver 0.3 Nov 28 95 Long backups are now possible, thanks to the + * character device interface. Linux's responsiveness + * and performance doesn't seem to be much affected + * from the background backup procedure. + * Some general mtio.h magnetic tape operations are + * now supported by our character device. As a result, + * popular tape utilities are starting to work with + * ide tapes :-) + * The following configurations were tested: + * 1. An IDE ATAPI TAPE shares the same interface + * and irq with an IDE ATAPI CDROM. + * 2. An IDE ATAPI TAPE shares the same interface + * and irq with a normal IDE disk. + * Both configurations seemed to work just fine ! + * However, to be on the safe side, it is meanwhile + * recommended to give the IDE TAPE its own interface + * and irq. + * The one thing which needs to be done here is to + * add a "request postpone" feature to ide.c, + * so that we won't have to wait for the tape to finish + * performing a long media access (DSC) request (such + * as a rewind) before we can access the other device + * on the same interface. This effect doesn't disturb + * normal operation most of the time because read/write + * requests are relatively fast, and once we are + * performing one tape r/w request, a lot of requests + * from the other device can be queued and ide.c will + * service all of them after this single tape request. + * Ver 1.0 Dec 11 95 Integrated into Linux 1.3.46 development tree. + * On each read / write request, we now ask the drive + * if we can transfer a constant number of bytes + * (a parameter of the drive) only to its buffers, + * without causing actual media access. If we can't, + * we just wait until we can by polling the DSC bit. + * This ensures that while we are not transferring + * more bytes than the constant referred to above, the + * interrupt latency will not become too high and + * we won't cause an interrupt timeout, as happened + * occasionally in the previous version. + * While polling for DSC, the current request is + * postponed and ide.c is free to handle requests from + * the other device. This is handled transparently to + * ide.c. The hwgroup locking method which was used + * in the previous version was removed. + * Use of new general features which are provided by + * ide.c for use with atapi devices. + * (Programming done by Mark Lord) + * Few potential bug fixes (Again, suggested by Mark) + * Single character device data transfers are now + * not limited in size, as they were before. + * We are asking the tape about its recommended + * transfer unit and send a larger data transfer + * as several transfers of the above size. + * For best results, use an integral number of this + * basic unit (which is shown during driver + * initialization). I will soon add an ioctl to get + * this important parameter. + * Our data transfer buffer is allocated on startup, + * rather than before each data transfer. This should + * ensure that we will indeed have a data buffer. + * Ver 1.1 Dec 14 95 Fixed random problems which occurred when the tape + * shared an interface with another device. + * (poll_for_dsc was a complete mess). + * Removed some old (non-active) code which had + * to do with supporting buffer cache originated + * requests. + * The block device interface can now be opened, so + * that general ide driver features like the unmask + * interrupts flag can be selected with an ioctl. + * This is the only use of the block device interface. + * New fast pipelined operation mode (currently only on + * writes). When using the pipelined mode, the + * throughput can potentially reach the maximum + * tape supported throughput, regardless of the + * user backup program. On my tape drive, it sometimes + * boosted performance by a factor of 2. Pipelined + * mode is enabled by default, but since it has a few + * downfalls as well, you may want to disable it. + * A short explanation of the pipelined operation mode + * is available below. + * Ver 1.2 Jan 1 96 Eliminated pipelined mode race condition. + * Added pipeline read mode. As a result, restores + * are now as fast as backups. + * Optimized shared interface behavior. The new behavior + * typically results in better IDE bus efficiency and + * higher tape throughput. + * Pre-calculation of the expected read/write request + * service time, based on the tape's parameters. In + * the pipelined operation mode, this allows us to + * adjust our polling frequency to a much lower value, + * and thus to dramatically reduce our load on Linux, + * without any decrease in performance. + * Implemented additional mtio.h operations. + * The recommended user block size is returned by + * the MTIOCGET ioctl. + * Additional minor changes. + * Ver 1.3 Feb 9 96 Fixed pipelined read mode bug which prevented the + * use of some block sizes during a restore procedure. + * The character device interface will now present a + * continuous view of the media - any mix of block sizes + * during a backup/restore procedure is supported. The + * driver will buffer the requests internally and + * convert them to the tape's recommended transfer + * unit, making performance almost independent of the + * chosen user block size. + * Some improvements in error recovery. + * By cooperating with ide-dma.c, bus mastering DMA can + * now sometimes be used with IDE tape drives as well. + * Bus mastering DMA has the potential to dramatically + * reduce the CPU's overhead when accessing the device, + * and can be enabled by using hdparm -d1 on the tape's + * block device interface. For more info, read the + * comments in ide-dma.c. + * Ver 1.4 Mar 13 96 Fixed serialize support. + * Ver 1.5 Apr 12 96 Fixed shared interface operation, broken in 1.3.85. + * Fixed pipelined read mode inefficiency. + * Fixed nasty null dereferencing bug. + * Ver 1.6 Aug 16 96 Fixed FPU usage in the driver. + * Fixed end of media bug. + * Ver 1.7 Sep 10 96 Minor changes for the CONNER CTT8000-A model. + * Ver 1.8 Sep 26 96 Attempt to find a better balance between good + * interactive response and high system throughput. + * Ver 1.9 Nov 5 96 Automatically cross encountered filemarks rather + * than requiring an explicit FSF command. + * Abort pending requests at end of media. + * MTTELL was sometimes returning incorrect results. + * Return the real block size in the MTIOCGET ioctl. + * Some error recovery bug fixes. + * Ver 1.10 Nov 5 96 Major reorganization. + * Reduced CPU overhead a bit by eliminating internal + * bounce buffers. + * Added module support. + * Added multiple tape drives support. + * Added partition support. + * Rewrote DSC handling. + * Some portability fixes. + * Removed ide-tape.h. + * Additional minor changes. + * Ver 1.11 Dec 2 96 Bug fix in previous DSC timeout handling. + * Use ide_stall_queue() for DSC overlap. + * Use the maximum speed rather than the current speed + * to compute the request service time. + * Ver 1.12 Dec 7 97 Fix random memory overwriting and/or last block data + * corruption, which could occur if the total number + * of bytes written to the tape was not an integral + * number of tape blocks. + * Add support for INTERRUPT DRQ devices. + * Ver 1.13 Jan 2 98 Add "speed == 0" work-around for HP COLORADO 5GB + * Ver 1.14 Dec 30 98 Partial fixes for the Sony/AIWA tape drives. + * Replace cli()/sti() with hwgroup spinlocks. + * Ver 1.15 Mar 25 99 Fix SMP race condition by replacing hwgroup + * spinlock with private per-tape spinlock. + * Ver 1.16 Sep 1 99 Add OnStream tape support. + * Abort read pipeline on EOD. + * Wait for the tape to become ready in case it returns + * "in the process of becoming ready" on open(). + * Fix zero padding of the last written block in + * case the tape block size is larger than PAGE_SIZE. + * Decrease the default disconnection time to tn. + * Ver 1.16e Oct 3 99 Minor fixes. + * Ver 1.16e1 Oct 13 99 Patches by Arnold Niessen, + * niessen@iae.nl / arnold.niessen@philips.com + * GO-1) Undefined code in idetape_read_position + * according to Gadi's email + * AJN-1) Minor fix asc == 11 should be asc == 0x11 + * in idetape_issue_packet_command (did effect + * debugging output only) + * AJN-2) Added more debugging output, and + * added ide-tape: where missing. I would also + * like to add tape->name where possible + * AJN-3) Added different debug_level's + * via /proc/ide/hdc/settings + * "debug_level" determines amount of debugging output; + * can be changed using /proc/ide/hdx/settings + * 0 : almost no debugging output + * 1 : 0+output errors only + * 2 : 1+output all sensekey/asc + * 3 : 2+follow all chrdev related procedures + * 4 : 3+follow all procedures + * 5 : 4+include pc_stack rq_stack info + * 6 : 5+USE_COUNT updates + * AJN-4) Fixed timeout for retension in idetape_queue_pc_tail + * from 5 to 10 minutes + * AJN-5) Changed maximum number of blocks to skip when + * reading tapes with multiple consecutive write + * errors from 100 to 1000 in idetape_get_logical_blk + * Proposed changes to code: + * 1) output "logical_blk_num" via /proc + * 2) output "current_operation" via /proc + * 3) Either solve or document the fact that `mt rewind' is + * required after reading from /dev/nhtx to be + * able to rmmod the idetape module; + * Also, sometimes an application finishes but the + * device remains `busy' for some time. Same cause ? + * Proposed changes to release-notes: + * 4) write a simple `quickstart' section in the + * release notes; I volunteer if you don't want to + * 5) include a pointer to video4linux in the doc + * to stimulate video applications + * 6) release notes lines 331 and 362: explain what happens + * if the application data rate is higher than 1100 KB/s; + * similar approach to lower-than-500 kB/s ? + * 7) 6.6 Comparison; wouldn't it be better to allow different + * strategies for read and write ? + * Wouldn't it be better to control the tape buffer + * contents instead of the bandwidth ? + * 8) line 536: replace will by would (if I understand + * this section correctly, a hypothetical and unwanted situation + * is being described) + * Ver 1.16f Dec 15 99 Change place of the secondary OnStream header frames. + * Ver 1.17 Nov 2000 / Jan 2001 Marcel Mol, marcel@mesa.nl + * - Add idetape_onstream_mode_sense_tape_parameter_page + * function to get tape capacity in frames: tape->capacity. + * - Add support for DI-50 drives( or any DI- drive). + * - 'workaround' for read error/blank block arround block 3000. + * - Implement Early warning for end of media for Onstream. + * - Cosmetic code changes for readability. + * - Idetape_position_tape should not use SKIP bit during + * Onstream read recovery. + * - Add capacity, logical_blk_num and first/last_frame_position + * to /proc/ide/hd?/settings. + * - Module use count was gone in the Linux 2.4 driver. + * Ver 1.17a Apr 2001 Willem Riede osst@riede.org + * - Get drive's actual block size from mode sense block descriptor + * - Limit size of pipeline + * + * Here are some words from the first releases of hd.c, which are quoted + * in ide.c and apply here as well: + * + * | Special care is recommended. Have Fun! + * + */ + +/* + * An overview of the pipelined operation mode. + * + * In the pipelined write mode, we will usually just add requests to our + * pipeline and return immediately, before we even start to service them. The + * user program will then have enough time to prepare the next request while + * we are still busy servicing previous requests. In the pipelined read mode, + * the situation is similar - we add read-ahead requests into the pipeline, + * before the user even requested them. + * + * The pipeline can be viewed as a "safety net" which will be activated when + * the system load is high and prevents the user backup program from keeping up + * with the current tape speed. At this point, the pipeline will get + * shorter and shorter but the tape will still be streaming at the same speed. + * Assuming we have enough pipeline stages, the system load will hopefully + * decrease before the pipeline is completely empty, and the backup program + * will be able to "catch up" and refill the pipeline again. + * + * When using the pipelined mode, it would be best to disable any type of + * buffering done by the user program, as ide-tape already provides all the + * benefits in the kernel, where it can be done in a more efficient way. + * As we will usually not block the user program on a request, the most + * efficient user code will then be a simple read-write-read-... cycle. + * Any additional logic will usually just slow down the backup process. + * + * Using the pipelined mode, I get a constant over 400 KBps throughput, + * which seems to be the maximum throughput supported by my tape. + * + * However, there are some downfalls: + * + * 1. We use memory (for data buffers) in proportional to the number + * of pipeline stages (each stage is about 26 KB with my tape). + * 2. In the pipelined write mode, we cheat and postpone error codes + * to the user task. In read mode, the actual tape position + * will be a bit further than the last requested block. + * + * Concerning (1): + * + * 1. We allocate stages dynamically only when we need them. When + * we don't need them, we don't consume additional memory. In + * case we can't allocate stages, we just manage without them + * (at the expense of decreased throughput) so when Linux is + * tight in memory, we will not pose additional difficulties. + * + * 2. The maximum number of stages (which is, in fact, the maximum + * amount of memory) which we allocate is limited by the compile + * time parameter IDETAPE_MAX_PIPELINE_STAGES. + * + * 3. The maximum number of stages is a controlled parameter - We + * don't start from the user defined maximum number of stages + * but from the lower IDETAPE_MIN_PIPELINE_STAGES (again, we + * will not even allocate this amount of stages if the user + * program can't handle the speed). We then implement a feedback + * loop which checks if the pipeline is empty, and if it is, we + * increase the maximum number of stages as necessary until we + * reach the optimum value which just manages to keep the tape + * busy with minimum allocated memory or until we reach + * IDETAPE_MAX_PIPELINE_STAGES. + * + * Concerning (2): + * + * In pipelined write mode, ide-tape can not return accurate error codes + * to the user program since we usually just add the request to the + * pipeline without waiting for it to be serviced. In case an error + * occurs, I will report it on the next user request. + * + * In the pipelined read mode, subsequent read requests or forward + * filemark spacing will perform correctly, as we preserve all blocks + * and filemarks which we encountered during our excess read-ahead. + * + * For accurate tape positioning and error reporting, disabling + * pipelined mode might be the best option. + * + * You can enable/disable/tune the pipelined operation mode by adjusting + * the compile time parameters below. + */ + +/* + * Possible improvements. + * + * 1. Support for the ATAPI overlap protocol. + * + * In order to maximize bus throughput, we currently use the DSC + * overlap method which enables ide.c to service requests from the + * other device while the tape is busy executing a command. The + * DSC overlap method involves polling the tape's status register + * for the DSC bit, and servicing the other device while the tape + * isn't ready. + * + * In the current QIC development standard (December 1995), + * it is recommended that new tape drives will *in addition* + * implement the ATAPI overlap protocol, which is used for the + * same purpose - efficient use of the IDE bus, but is interrupt + * driven and thus has much less CPU overhead. + * + * ATAPI overlap is likely to be supported in most new ATAPI + * devices, including new ATAPI cdroms, and thus provides us + * a method by which we can achieve higher throughput when + * sharing a (fast) ATA-2 disk with any (slow) new ATAPI device. + */ + +#define IDETAPE_VERSION "1.17a" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + + +#define NO_LONGER_REQUIRED (1) + +/* + * OnStream support + */ +#define ONSTREAM_DEBUG (0) +#define OS_CONFIG_PARTITION (0xff) +#define OS_DATA_PARTITION (0) +#define OS_PARTITION_VERSION (1) +#define OS_EW 300 +#define OS_ADR_MINREV 2 + +#define OS_DATA_STARTFRAME1 20 +#define OS_DATA_ENDFRAME1 2980 +/* + * partition + */ +typedef struct os_partition_s { + __u8 partition_num; + __u8 par_desc_ver; + __u16 wrt_pass_cntr; + __u32 first_frame_addr; + __u32 last_frame_addr; + __u32 eod_frame_addr; +} os_partition_t; + +/* + * DAT entry + */ +typedef struct os_dat_entry_s { + __u32 blk_sz; + __u16 blk_cnt; + __u8 flags; + __u8 reserved; +} os_dat_entry_t; + +/* + * DAT + */ +#define OS_DAT_FLAGS_DATA (0xc) +#define OS_DAT_FLAGS_MARK (0x1) + +typedef struct os_dat_s { + __u8 dat_sz; + __u8 reserved1; + __u8 entry_cnt; + __u8 reserved3; + os_dat_entry_t dat_list[16]; +} os_dat_t; + +/* + * Frame types + */ +#define OS_FRAME_TYPE_FILL (0) +#define OS_FRAME_TYPE_EOD (1 << 0) +#define OS_FRAME_TYPE_MARKER (1 << 1) +#define OS_FRAME_TYPE_HEADER (1 << 3) +#define OS_FRAME_TYPE_DATA (1 << 7) + +/* + * AUX + */ +typedef struct os_aux_s { + __u32 format_id; /* hardware compability AUX is based on */ + char application_sig[4]; /* driver used to write this media */ + __u32 hdwr; /* reserved */ + __u32 update_frame_cntr; /* for configuration frame */ + __u8 frame_type; + __u8 frame_type_reserved; + __u8 reserved_18_19[2]; + os_partition_t partition; + __u8 reserved_36_43[8]; + __u32 frame_seq_num; + __u32 logical_blk_num_high; + __u32 logical_blk_num; + os_dat_t dat; + __u8 reserved188_191[4]; + __u32 filemark_cnt; + __u32 phys_fm; + __u32 last_mark_addr; + __u8 reserved204_223[20]; + + /* + * __u8 app_specific[32]; + * + * Linux specific fields: + */ + __u32 next_mark_addr; /* when known, points to next marker */ + __u8 linux_specific[28]; + + __u8 reserved_256_511[256]; +} os_aux_t; + +typedef struct os_header_s { + char ident_str[8]; + __u8 major_rev; + __u8 minor_rev; + __u8 reserved10_15[6]; + __u8 par_num; + __u8 reserved1_3[3]; + os_partition_t partition; +} os_header_t; + +/* + * OnStream Tape Parameters Page + */ +typedef struct { + unsigned page_code :6; /* Page code - Should be 0x2b */ + unsigned reserved1_6 :1; + unsigned ps :1; + __u8 reserved2; + __u8 density; /* kbpi */ + __u8 reserved3,reserved4; + __u16 segtrk; /* segment of per track */ + __u16 trks; /* tracks per tape */ + __u8 reserved5,reserved6,reserved7,reserved8,reserved9,reserved10; +} onstream_tape_paramtr_page_t; + +/* + * OnStream ADRL frame + */ +#define OS_FRAME_SIZE (32 * 1024 + 512) +#define OS_DATA_SIZE (32 * 1024) +#define OS_AUX_SIZE (512) + +/* + * internal error codes for onstream + */ +#define OS_PART_ERROR 2 +#define OS_WRITE_ERROR 1 + +#include + +/**************************** Tunable parameters *****************************/ + + +/* + * Pipelined mode parameters. + * + * We try to use the minimum number of stages which is enough to + * keep the tape constantly streaming. To accomplish that, we implement + * a feedback loop around the maximum number of stages: + * + * We start from MIN maximum stages (we will not even use MIN stages + * if we don't need them), increment it by RATE*(MAX-MIN) + * whenever we sense that the pipeline is empty, until we reach + * the optimum value or until we reach MAX. + * + * Setting the following parameter to 0 will disable the pipelined mode. + */ +#define IDETAPE_MIN_PIPELINE_STAGES 200 +#define IDETAPE_MAX_PIPELINE_STAGES 400 +#define IDETAPE_INCREASE_STAGES_RATE 20 + +/* + * The following are used to debug the driver: + * + * Setting IDETAPE_DEBUG_INFO to 1 will report device capabilities. + * Setting IDETAPE_DEBUG_LOG to 1 will log driver flow control. + * Setting IDETAPE_DEBUG_BUGS to 1 will enable self-sanity checks in + * some places. + * + * Setting them to 0 will restore normal operation mode: + * + * 1. Disable logging normal successful operations. + * 2. Disable self-sanity checks. + * 3. Errors will still be logged, of course. + * + * All the #if DEBUG code will be removed some day, when the driver + * is verified to be stable enough. This will make it much more + * esthetic. + */ +#define IDETAPE_DEBUG_INFO 1 +#define IDETAPE_DEBUG_LOG 1 +#define IDETAPE_DEBUG_LOG_VERBOSE 0 +#define IDETAPE_DEBUG_BUGS 1 + +/* + * After each failed packet command we issue a request sense command + * and retry the packet command IDETAPE_MAX_PC_RETRIES times. + * + * Setting IDETAPE_MAX_PC_RETRIES to 0 will disable retries. + */ +#define IDETAPE_MAX_PC_RETRIES 3 + +/* + * With each packet command, we allocate a buffer of + * IDETAPE_PC_BUFFER_SIZE bytes. This is used for several packet + * commands (Not for READ/WRITE commands). + */ +#define IDETAPE_PC_BUFFER_SIZE 256 + +/* + * In various places in the driver, we need to allocate storage + * for packet commands and requests, which will remain valid while + * we leave the driver to wait for an interrupt or a timeout event. + */ +#define IDETAPE_PC_STACK (10 + IDETAPE_MAX_PC_RETRIES) + +/* + * Some tape drives require a long irq timeout + */ +#define IDETAPE_WAIT_CMD (60*HZ) + +/* + * The following parameter is used to select the point in the internal + * tape fifo in which we will start to refill the buffer. Decreasing + * the following parameter will improve the system's latency and + * interactive response, while using a high value might improve sytem + * throughput. + */ +#define IDETAPE_FIFO_THRESHOLD 2 + +/* + * DSC polling parameters. + * + * Polling for DSC (a single bit in the status register) is a very + * important function in ide-tape. There are two cases in which we + * poll for DSC: + * + * 1. Before a read/write packet command, to ensure that we + * can transfer data from/to the tape's data buffers, without + * causing an actual media access. In case the tape is not + * ready yet, we take out our request from the device + * request queue, so that ide.c will service requests from + * the other device on the same interface meanwhile. + * + * 2. After the successful initialization of a "media access + * packet command", which is a command which can take a long + * time to complete (it can be several seconds or even an hour). + * + * Again, we postpone our request in the middle to free the bus + * for the other device. The polling frequency here should be + * lower than the read/write frequency since those media access + * commands are slow. We start from a "fast" frequency - + * IDETAPE_DSC_MA_FAST (one second), and if we don't receive DSC + * after IDETAPE_DSC_MA_THRESHOLD (5 minutes), we switch it to a + * lower frequency - IDETAPE_DSC_MA_SLOW (1 minute). + * + * We also set a timeout for the timer, in case something goes wrong. + * The timeout should be longer then the maximum execution time of a + * tape operation. + */ + +/* + * DSC timings. + */ +#define IDETAPE_DSC_RW_MIN 5*HZ/100 /* 50 msec */ +#define IDETAPE_DSC_RW_MAX 40*HZ/100 /* 400 msec */ +#define IDETAPE_DSC_RW_TIMEOUT 2*60*HZ /* 2 minutes */ +#define IDETAPE_DSC_MA_FAST 2*HZ /* 2 seconds */ +#define IDETAPE_DSC_MA_THRESHOLD 5*60*HZ /* 5 minutes */ +#define IDETAPE_DSC_MA_SLOW 30*HZ /* 30 seconds */ +#define IDETAPE_DSC_MA_TIMEOUT 2*60*60*HZ /* 2 hours */ + +/*************************** End of tunable parameters ***********************/ + +/* + * Debugging/Performance analysis + * + * I/O trace support + */ +#define USE_IOTRACE 0 +#if USE_IOTRACE +#include +#define IO_IDETAPE_FIFO 500 +#endif + +/* + * Read/Write error simulation + */ +#define SIMULATE_ERRORS 0 + +/* + * For general magnetic tape device compatibility. + */ +typedef enum { + idetape_direction_none, + idetape_direction_read, + idetape_direction_write +} idetape_chrdev_direction_t; + +/* + * Our view of a packet command. + */ +typedef struct idetape_packet_command_s { + u8 c[12]; /* Actual packet bytes */ + int retries; /* On each retry, we increment retries */ + int error; /* Error code */ + int request_transfer; /* Bytes to transfer */ + int actually_transferred; /* Bytes actually transferred */ + int buffer_size; /* Size of our data buffer */ + struct bio *bio; + char *b_data; + int b_count; + byte *buffer; /* Data buffer */ + byte *current_position; /* Pointer into the above buffer */ + ide_startstop_t (*callback) (ide_drive_t *); /* Called when this packet command is completed */ + byte pc_buffer[IDETAPE_PC_BUFFER_SIZE]; /* Temporary buffer */ + unsigned long flags; /* Status/Action bit flags: long for set_bit */ +} idetape_pc_t; + +/* + * Packet command flag bits. + */ +#define PC_ABORT 0 /* Set when an error is considered normal - We won't retry */ +#define PC_WAIT_FOR_DSC 1 /* 1 When polling for DSC on a media access command */ +#define PC_DMA_RECOMMENDED 2 /* 1 when we prefer to use DMA if possible */ +#define PC_DMA_IN_PROGRESS 3 /* 1 while DMA in progress */ +#define PC_DMA_ERROR 4 /* 1 when encountered problem during DMA */ +#define PC_WRITING 5 /* Data direction */ + +/* + * Capabilities and Mechanical Status Page + */ +typedef struct { + unsigned page_code :6; /* Page code - Should be 0x2a */ + __u8 reserved0_6 :1; + __u8 ps :1; /* parameters saveable */ + __u8 page_length; /* Page Length - Should be 0x12 */ + __u8 reserved2, reserved3; + unsigned ro :1; /* Read Only Mode */ + unsigned reserved4_1234 :4; + unsigned sprev :1; /* Supports SPACE in the reverse direction */ + unsigned reserved4_67 :2; + unsigned reserved5_012 :3; + unsigned efmt :1; /* Supports ERASE command initiated formatting */ + unsigned reserved5_4 :1; + unsigned qfa :1; /* Supports the QFA two partition formats */ + unsigned reserved5_67 :2; + unsigned lock :1; /* Supports locking the volume */ + unsigned locked :1; /* The volume is locked */ + unsigned prevent :1; /* The device defaults in the prevent state after power up */ + unsigned eject :1; /* The device can eject the volume */ + __u8 disconnect :1; /* The device can break request > ctl */ + __u8 reserved6_5 :1; + unsigned ecc :1; /* Supports error correction */ + unsigned cmprs :1; /* Supports data compression */ + unsigned reserved7_0 :1; + unsigned blk512 :1; /* Supports 512 bytes block size */ + unsigned blk1024 :1; /* Supports 1024 bytes block size */ + unsigned reserved7_3_6 :4; + unsigned blk32768 :1; /* slowb - the device restricts the byte count for PIO */ + /* transfers for slow buffer memory ??? */ + /* Also 32768 block size in some cases */ + __u16 max_speed; /* Maximum speed supported in KBps */ + __u8 reserved10, reserved11; + __u16 ctl; /* Continuous Transfer Limit in blocks */ + __u16 speed; /* Current Speed, in KBps */ + __u16 buffer_size; /* Buffer Size, in 512 bytes */ + __u8 reserved18, reserved19; +} idetape_capabilities_page_t; + +/* + * Block Size Page + */ +typedef struct { + unsigned page_code :6; /* Page code - Should be 0x30 */ + unsigned reserved1_6 :1; + unsigned ps :1; + __u8 page_length; /* Page Length - Should be 2 */ + __u8 reserved2; + unsigned play32 :1; + unsigned play32_5 :1; + unsigned reserved2_23 :2; + unsigned record32 :1; + unsigned record32_5 :1; + unsigned reserved2_6 :1; + unsigned one :1; +} idetape_block_size_page_t; + +/* + * A pipeline stage. + */ +typedef struct idetape_stage_s { + struct request rq; /* The corresponding request */ + struct bio *bio; /* The data buffers */ + struct idetape_stage_s *next; /* Pointer to the next stage */ + os_aux_t *aux; /* OnStream aux ptr */ +} idetape_stage_t; + +/* + * REQUEST SENSE packet command result - Data Format. + */ +typedef struct { + unsigned error_code :7; /* Current of deferred errors */ + unsigned valid :1; /* The information field conforms to QIC-157C */ + __u8 reserved1 :8; /* Segment Number - Reserved */ + unsigned sense_key :4; /* Sense Key */ + unsigned reserved2_4 :1; /* Reserved */ + unsigned ili :1; /* Incorrect Length Indicator */ + unsigned eom :1; /* End Of Medium */ + unsigned filemark :1; /* Filemark */ + __u32 information __attribute__ ((packed)); + __u8 asl; /* Additional sense length (n-7) */ + __u32 command_specific; /* Additional command specific information */ + __u8 asc; /* Additional Sense Code */ + __u8 ascq; /* Additional Sense Code Qualifier */ + __u8 replaceable_unit_code; /* Field Replaceable Unit Code */ + unsigned sk_specific1 :7; /* Sense Key Specific */ + unsigned sksv :1; /* Sense Key Specific information is valid */ + __u8 sk_specific2; /* Sense Key Specific */ + __u8 sk_specific3; /* Sense Key Specific */ + __u8 pad[2]; /* Padding to 20 bytes */ +} idetape_request_sense_result_t; + + +/* + * Most of our global data which we need to save even as we leave the + * driver due to an interrupt or a timer event is stored in a variable + * of type idetape_tape_t, defined below. + */ +typedef struct { + ide_drive_t *drive; + devfs_handle_t de_r, de_n; + + /* + * Since a typical character device operation requires more + * than one packet command, we provide here enough memory + * for the maximum of interconnected packet commands. + * The packet commands are stored in the circular array pc_stack. + * pc_stack_index points to the last used entry, and warps around + * to the start when we get to the last array entry. + * + * pc points to the current processed packet command. + * + * failed_pc points to the last failed packet command, or contains + * NULL if we do not need to retry any packet command. This is + * required since an additional packet command is needed before the + * retry, to get detailed information on what went wrong. + */ + idetape_pc_t *pc; /* Current packet command */ + idetape_pc_t *failed_pc; /* Last failed packet command */ + idetape_pc_t pc_stack[IDETAPE_PC_STACK];/* Packet command stack */ + int pc_stack_index; /* Next free packet command storage space */ + struct request rq_stack[IDETAPE_PC_STACK]; + int rq_stack_index; /* We implement a circular array */ + + /* + * DSC polling variables. + * + * While polling for DSC we use postponed_rq to postpone the + * current request so that ide.c will be able to service + * pending requests on the other device. Note that at most + * we will have only one DSC (usually data transfer) request + * in the device request queue. Additional requests can be + * queued in our internal pipeline, but they will be visible + * to ide.c only one at a time. + */ + struct request *postponed_rq; + unsigned long dsc_polling_start; /* The time in which we started polling for DSC */ + struct timer_list dsc_timer; /* Timer used to poll for dsc */ + unsigned long best_dsc_rw_frequency; /* Read/Write dsc polling frequency */ + unsigned long dsc_polling_frequency; /* The current polling frequency */ + unsigned long dsc_timeout; /* Maximum waiting time */ + + /* + * Read position information + */ + byte partition; + unsigned int first_frame_position; /* Current block */ + unsigned int last_frame_position; + unsigned int blocks_in_buffer; + + /* + * Last error information + */ + byte sense_key, asc, ascq; + + /* + * Character device operation + */ + unsigned int minor; + char name[4]; /* device name */ + idetape_chrdev_direction_t chrdev_direction; /* Current character device data transfer direction */ + + /* + * Device information + */ + unsigned short tape_block_size; /* Usually 512 or 1024 bytes */ + int user_bs_factor; + idetape_capabilities_page_t capabilities; /* Copy of the tape's Capabilities and Mechanical Page */ + + /* + * Active data transfer request parameters. + * + * At most, there is only one ide-tape originated data transfer + * request in the device request queue. This allows ide.c to + * easily service requests from the other device when we + * postpone our active request. In the pipelined operation + * mode, we use our internal pipeline structure to hold + * more data requests. + * + * The data buffer size is chosen based on the tape's + * recommendation. + */ + struct request *active_data_request; /* Pointer to the request which is waiting in the device request queue */ + int stage_size; /* Data buffer size (chosen based on the tape's recommendation */ + idetape_stage_t *merge_stage; + int merge_stage_size; + struct bio *bio; + char *b_data; + int b_count; + + /* + * Pipeline parameters. + * + * To accomplish non-pipelined mode, we simply set the following + * variables to zero (or NULL, where appropriate). + */ + int nr_stages; /* Number of currently used stages */ + int nr_pending_stages; /* Number of pending stages */ + int max_stages, min_pipeline, max_pipeline; /* We will not allocate more than this number of stages */ + idetape_stage_t *first_stage; /* The first stage which will be removed from the pipeline */ + idetape_stage_t *active_stage; /* The currently active stage */ + idetape_stage_t *next_stage; /* Will be serviced after the currently active request */ + idetape_stage_t *last_stage; /* New requests will be added to the pipeline here */ + idetape_stage_t *cache_stage; /* Optional free stage which we can use */ + int pages_per_stage; + int excess_bh_size; /* Wasted space in each stage */ + + unsigned long flags; /* Status/Action flags: long for set_bit */ + spinlock_t spinlock; /* protects the ide-tape queue */ + + /* + * Measures average tape speed + */ + unsigned long avg_time; + int avg_size; + int avg_speed; + + idetape_request_sense_result_t sense; /* last sense information */ + + char vendor_id[10]; + char product_id[18]; + char firmware_revision[6]; + int firmware_revision_num; + + int door_locked; /* the door is currently locked */ + + /* + * OnStream flags + */ + int onstream; /* the tape is an OnStream tape */ + int raw; /* OnStream raw access (32.5KB block size) */ + int cur_frames; /* current number of frames in internal buffer */ + int max_frames; /* max number of frames in internal buffer */ + int logical_blk_num; /* logical block number */ + __u16 wrt_pass_cntr; /* write pass counter */ + __u32 update_frame_cntr; /* update frame counter */ + struct completion *waiting; + int onstream_write_error; /* write error recovery active */ + int header_ok; /* header frame verified ok */ + int linux_media; /* reading linux-specifc media */ + int linux_media_version; + char application_sig[5]; /* application signature */ + int filemark_cnt; + int first_mark_addr; + int last_mark_addr; + int eod_frame_addr; + unsigned long cmd_start_time; + unsigned long max_cmd_time; + unsigned capacity; + + /* + * Optimize the number of "buffer filling" + * mode sense commands. + */ + unsigned long last_buffer_fill; /* last time in which we issued fill cmd */ + int req_buffer_fill; /* buffer fill command requested */ + int writes_since_buffer_fill; + int reads_since_buffer_fill; + + /* + * Limit the number of times a request can + * be postponed, to avoid an infinite postpone + * deadlock. + */ + int postpone_cnt; /* request postpone count limit */ + + /* + * Measures number of frames: + * + * 1. written/read to/from the driver pipeline (pipeline_head). + * 2. written/read to/from the tape buffers (bio). + * 3. written/read by the tape to/from the media (tape_head). + */ + int pipeline_head; + int buffer_head; + int tape_head; + int last_tape_head; + + /* + * Speed control at the tape buffers input/output + */ + unsigned long insert_time; + int insert_size; + int insert_speed; + int max_insert_speed; + int measure_insert_time; + + /* + * Measure tape still time, in milliseconds + */ + unsigned long tape_still_time_begin; + int tape_still_time; + + /* + * Speed regulation negative feedback loop + */ + int speed_control; + int pipeline_head_speed, controlled_pipeline_head_speed, uncontrolled_pipeline_head_speed; + int controlled_last_pipeline_head, uncontrolled_last_pipeline_head; + unsigned long uncontrolled_pipeline_head_time, controlled_pipeline_head_time; + int controlled_previous_pipeline_head, uncontrolled_previous_pipeline_head; + unsigned long controlled_previous_head_time, uncontrolled_previous_head_time; + int restart_speed_control_req; + + /* + * Debug_level determines amount of debugging output; + * can be changed using /proc/ide/hdx/settings + * 0 : almost no debugging output + * 1 : 0+output errors only + * 2 : 1+output all sensekey/asc + * 3 : 2+follow all chrdev related procedures + * 4 : 3+follow all procedures + * 5 : 4+include pc_stack rq_stack info + * 6 : 5+USE_COUNT updates + */ + int debug_level; +} idetape_tape_t; + +/* + * Tape door status + */ +#define DOOR_UNLOCKED 0 +#define DOOR_LOCKED 1 +#define DOOR_EXPLICITLY_LOCKED 2 + +/* + * Tape flag bits values. + */ +#define IDETAPE_IGNORE_DSC 0 +#define IDETAPE_ADDRESS_VALID 1 /* 0 When the tape position is unknown */ +#define IDETAPE_BUSY 2 /* Device already opened */ +#define IDETAPE_PIPELINE_ERROR 3 /* Error detected in a pipeline stage */ +#define IDETAPE_DETECT_BS 4 /* Attempt to auto-detect the current user block size */ +#define IDETAPE_FILEMARK 5 /* Currently on a filemark */ +#define IDETAPE_DRQ_INTERRUPT 6 /* DRQ interrupt device */ +#define IDETAPE_READ_ERROR 7 +#define IDETAPE_PIPELINE_ACTIVE 8 /* pipeline active */ + +/* + * Supported ATAPI tape drives packet commands + */ +#define IDETAPE_TEST_UNIT_READY_CMD 0x00 +#define IDETAPE_REWIND_CMD 0x01 +#define IDETAPE_REQUEST_SENSE_CMD 0x03 +#define IDETAPE_READ_CMD 0x08 +#define IDETAPE_WRITE_CMD 0x0a +#define IDETAPE_WRITE_FILEMARK_CMD 0x10 +#define IDETAPE_SPACE_CMD 0x11 +#define IDETAPE_INQUIRY_CMD 0x12 +#define IDETAPE_ERASE_CMD 0x19 +#define IDETAPE_MODE_SENSE_CMD 0x1a +#define IDETAPE_MODE_SELECT_CMD 0x15 +#define IDETAPE_LOAD_UNLOAD_CMD 0x1b +#define IDETAPE_PREVENT_CMD 0x1e +#define IDETAPE_LOCATE_CMD 0x2b +#define IDETAPE_READ_POSITION_CMD 0x34 +#define IDETAPE_READ_BUFFER_CMD 0x3c +#define IDETAPE_SET_SPEED_CMD 0xbb + +/* + * Some defines for the READ BUFFER command + */ +#define IDETAPE_RETRIEVE_FAULTY_BLOCK 6 + +/* + * Some defines for the SPACE command + */ +#define IDETAPE_SPACE_OVER_FILEMARK 1 +#define IDETAPE_SPACE_TO_EOD 3 + +/* + * Some defines for the LOAD UNLOAD command + */ +#define IDETAPE_LU_LOAD_MASK 1 +#define IDETAPE_LU_RETENSION_MASK 2 +#define IDETAPE_LU_EOT_MASK 4 + +/* + * Special requests for our block device strategy routine. + * + * In order to service a character device command, we add special + * requests to the tail of our block device request queue and wait + * for their completion. + * + */ +#define IDETAPE_FIRST_RQ 90 + +/* + * IDETAPE_PC_RQ is used to queue a packet command in the request queue. + */ +#define IDETAPE_PC_RQ1 90 +#define IDETAPE_PC_RQ2 91 + +/* + * IDETAPE_READ_RQ and IDETAPE_WRITE_RQ are used by our + * character device interface to request read/write operations from + * our block device interface. + */ +#define IDETAPE_READ_RQ 92 +#define IDETAPE_WRITE_RQ 93 +#define IDETAPE_ABORTED_WRITE_RQ 94 +#define IDETAPE_ABORTED_READ_RQ 95 +#define IDETAPE_READ_BUFFER_RQ 96 + +#define IDETAPE_LAST_RQ 96 + +/* + * A macro which can be used to check if a we support a given + * request command. + */ +#define IDETAPE_RQ_CMD(cmd) ((cmd >= IDETAPE_FIRST_RQ) && (cmd <= IDETAPE_LAST_RQ)) + +/* + * Error codes which are returned in rq->errors to the higher part + * of the driver. + */ +#define IDETAPE_ERROR_GENERAL 101 +#define IDETAPE_ERROR_FILEMARK 102 +#define IDETAPE_ERROR_EOD 103 + +/* + * The ATAPI Status Register. + */ +typedef union { + unsigned all :8; + struct { + unsigned check :1; /* Error occurred */ + unsigned idx :1; /* Reserved */ + unsigned corr :1; /* Correctable error occurred */ + unsigned drq :1; /* Data is request by the device */ + unsigned dsc :1; /* Buffer availability / Media access command finished */ + unsigned reserved5 :1; /* Reserved */ + unsigned drdy :1; /* Ignored for ATAPI commands (ready to accept ATA command) */ + unsigned bsy :1; /* The device has access to the command block */ + } b; +} idetape_status_reg_t; + +/* + * The ATAPI error register. + */ +typedef union { + unsigned all :8; + struct { + unsigned ili :1; /* Illegal Length Indication */ + unsigned eom :1; /* End Of Media Detected */ + unsigned abrt :1; /* Aborted command - As defined by ATA */ + unsigned mcr :1; /* Media Change Requested - As defined by ATA */ + unsigned sense_key :4; /* Sense key of the last failed packet command */ + } b; +} idetape_error_reg_t; + +/* + * ATAPI Feature Register + */ +typedef union { + unsigned all :8; + struct { + unsigned dma :1; /* Using DMA or PIO */ + unsigned reserved321 :3; /* Reserved */ + unsigned reserved654 :3; /* Reserved (Tag Type) */ + unsigned reserved7 :1; /* Reserved */ + } b; +} idetape_feature_reg_t; + +/* + * ATAPI Byte Count Register. + */ +typedef union { + unsigned all :16; + struct { + unsigned low :8; /* LSB */ + unsigned high :8; /* MSB */ + } b; +} idetape_bcount_reg_t; + +/* + * ATAPI Interrupt Reason Register. + */ +typedef union { + unsigned all :8; + struct { + unsigned cod :1; /* Information transferred is command (1) or data (0) */ + unsigned io :1; /* The device requests us to read (1) or write (0) */ + unsigned reserved :6; /* Reserved */ + } b; +} idetape_ireason_reg_t; + +/* + * ATAPI Drive Select Register + */ +typedef union { + unsigned all :8; + struct { + unsigned sam_lun :4; /* Should be zero with ATAPI (not used) */ + unsigned drv :1; /* The responding drive will be drive 0 (0) or drive 1 (1) */ + unsigned one5 :1; /* Should be set to 1 */ + unsigned reserved6 :1; /* Reserved */ + unsigned one7 :1; /* Should be set to 1 */ + } b; +} idetape_drivesel_reg_t; + +/* + * ATAPI Device Control Register + */ +typedef union { + unsigned all :8; + struct { + unsigned zero0 :1; /* Should be set to zero */ + unsigned nien :1; /* Device interrupt is disabled (1) or enabled (0) */ + unsigned srst :1; /* ATA software reset. ATAPI devices should use the new ATAPI srst. */ + unsigned one3 :1; /* Should be set to 1 */ + unsigned reserved4567 :4; /* Reserved */ + } b; +} idetape_control_reg_t; + +/* + * idetape_chrdev_t provides the link between out character device + * interface and our block device interface and the corresponding + * ide_drive_t structure. + */ +typedef struct { + ide_drive_t *drive; +} idetape_chrdev_t; + +/* + * The following is used to format the general configuration word of + * the ATAPI IDENTIFY DEVICE command. + */ +struct idetape_id_gcw { + unsigned packet_size :2; /* Packet Size */ + unsigned reserved234 :3; /* Reserved */ + unsigned drq_type :2; /* Command packet DRQ type */ + unsigned removable :1; /* Removable media */ + unsigned device_type :5; /* Device type */ + unsigned reserved13 :1; /* Reserved */ + unsigned protocol :2; /* Protocol type */ +}; + +/* + * INQUIRY packet command - Data Format (From Table 6-8 of QIC-157C) + */ +typedef struct { + unsigned device_type :5; /* Peripheral Device Type */ + unsigned reserved0_765 :3; /* Peripheral Qualifier - Reserved */ + unsigned reserved1_6t0 :7; /* Reserved */ + unsigned rmb :1; /* Removable Medium Bit */ + unsigned ansi_version :3; /* ANSI Version */ + unsigned ecma_version :3; /* ECMA Version */ + unsigned iso_version :2; /* ISO Version */ + unsigned response_format :4; /* Response Data Format */ + unsigned reserved3_45 :2; /* Reserved */ + unsigned reserved3_6 :1; /* TrmIOP - Reserved */ + unsigned reserved3_7 :1; /* AENC - Reserved */ + __u8 additional_length; /* Additional Length (total_length-4) */ + __u8 rsv5, rsv6, rsv7; /* Reserved */ + __u8 vendor_id[8]; /* Vendor Identification */ + __u8 product_id[16]; /* Product Identification */ + __u8 revision_level[4]; /* Revision Level */ + __u8 vendor_specific[20]; /* Vendor Specific - Optional */ + __u8 reserved56t95[40]; /* Reserved - Optional */ + /* Additional information may be returned */ +} idetape_inquiry_result_t; + +/* + * READ POSITION packet command - Data Format (From Table 6-57) + */ +typedef struct { + unsigned reserved0_10 :2; /* Reserved */ + unsigned bpu :1; /* Block Position Unknown */ + unsigned reserved0_543 :3; /* Reserved */ + unsigned eop :1; /* End Of Partition */ + unsigned bop :1; /* Beginning Of Partition */ + u8 partition; /* Partition Number */ + u8 reserved2, reserved3; /* Reserved */ + u32 first_block; /* First Block Location */ + u32 last_block; /* Last Block Location (Optional) */ + u8 reserved12; /* Reserved */ + u8 blocks_in_buffer[3]; /* Blocks In Buffer - (Optional) */ + u32 bytes_in_buffer; /* Bytes In Buffer (Optional) */ +} idetape_read_position_result_t; + +/* + * Follows structures which are related to the SELECT SENSE / MODE SENSE + * packet commands. Those packet commands are still not supported + * by ide-tape. + */ +#define IDETAPE_BLOCK_DESCRIPTOR 0 +#define IDETAPE_CAPABILITIES_PAGE 0x2a +#define IDETAPE_PARAMTR_PAGE 0x2b /* Onstream DI-x0 only */ +#define IDETAPE_BLOCK_SIZE_PAGE 0x30 +#define IDETAPE_BUFFER_FILLING_PAGE 0x33 + +/* + * Mode Parameter Header for the MODE SENSE packet command + */ +typedef struct { + __u8 mode_data_length; /* Length of the following data transfer */ + __u8 medium_type; /* Medium Type */ + __u8 dsp; /* Device Specific Parameter */ + __u8 bdl; /* Block Descriptor Length */ +#if 0 + /* data transfer page */ + __u8 page_code :6; + __u8 reserved0_6 :1; + __u8 ps :1; /* parameters saveable */ + __u8 page_length; /* page Length == 0x02 */ + __u8 reserved2; + __u8 read32k :1; /* 32k blk size (data only) */ + __u8 read32k5 :1; /* 32.5k blk size (data&AUX) */ + __u8 reserved3_23 :2; + __u8 write32k :1; /* 32k blk size (data only) */ + __u8 write32k5 :1; /* 32.5k blk size (data&AUX) */ + __u8 reserved3_6 :1; + __u8 streaming :1; /* streaming mode enable */ +#endif +} idetape_mode_parameter_header_t; + +/* + * Mode Parameter Block Descriptor the MODE SENSE packet command + * + * Support for block descriptors is optional. + */ +typedef struct { + __u8 density_code; /* Medium density code */ + __u8 blocks[3]; /* Number of blocks */ + __u8 reserved4; /* Reserved */ + __u8 length[3]; /* Block Length */ +} idetape_parameter_block_descriptor_t; + +/* + * The Data Compression Page, as returned by the MODE SENSE packet command. + */ +typedef struct { + unsigned page_code :6; /* Page Code - Should be 0xf */ + unsigned reserved0 :1; /* Reserved */ + unsigned ps :1; + __u8 page_length; /* Page Length - Should be 14 */ + unsigned reserved2 :6; /* Reserved */ + unsigned dcc :1; /* Data Compression Capable */ + unsigned dce :1; /* Data Compression Enable */ + unsigned reserved3 :5; /* Reserved */ + unsigned red :2; /* Report Exception on Decompression */ + unsigned dde :1; /* Data Decompression Enable */ + __u32 ca; /* Compression Algorithm */ + __u32 da; /* Decompression Algorithm */ + __u8 reserved[4]; /* Reserved */ +} idetape_data_compression_page_t; + +/* + * The Medium Partition Page, as returned by the MODE SENSE packet command. + */ +typedef struct { + unsigned page_code :6; /* Page Code - Should be 0x11 */ + unsigned reserved1_6 :1; /* Reserved */ + unsigned ps :1; + __u8 page_length; /* Page Length - Should be 6 */ + __u8 map; /* Maximum Additional Partitions - Should be 0 */ + __u8 apd; /* Additional Partitions Defined - Should be 0 */ + unsigned reserved4_012 :3; /* Reserved */ + unsigned psum :2; /* Should be 0 */ + unsigned idp :1; /* Should be 0 */ + unsigned sdp :1; /* Should be 0 */ + unsigned fdp :1; /* Fixed Data Partitions */ + __u8 mfr; /* Medium Format Recognition */ + __u8 reserved[2]; /* Reserved */ +} idetape_medium_partition_page_t; + +/* + * Run time configurable parameters. + */ +typedef struct { + int dsc_rw_frequency; + int dsc_media_access_frequency; + int nr_stages; +} idetape_config_t; + +/* + * The variables below are used for the character device interface. + * Additional state variables are defined in our ide_drive_t structure. + */ +static idetape_chrdev_t idetape_chrdevs[MAX_HWIFS * MAX_DRIVES]; +static int idetape_chrdev_present = 0; + +#if IDETAPE_DEBUG_LOG_VERBOSE + +/* + * DO NOT REMOVE, BUILDING A VERBOSE DEBUG SCHEME FOR ATAPI + */ + +char *idetape_sense_key_verbose (byte idetape_sense_key) +{ + switch (idetape_sense_key) { + default: { + char buf[22]; + sprintf(buf, "IDETAPE_SENSE (0x%02x)", idetape_sense_key); + return(buf); + } + + } +} + +char *idetape_command_key_verbose (byte idetape_command_key) +{ + switch (idetape_command_key) { + case IDETAPE_TEST_UNIT_READY_CMD: return("TEST_UNIT_READY_CMD"); + case IDETAPE_REWIND_CMD: return("REWIND_CMD"); + case IDETAPE_REQUEST_SENSE_CMD: return("REQUEST_SENSE_CMD"); + case IDETAPE_READ_CMD: return("READ_CMD"); + case IDETAPE_WRITE_CMD: return("WRITE_CMD"); + case IDETAPE_WRITE_FILEMARK_CMD: return("WRITE_FILEMARK_CMD"); + case IDETAPE_SPACE_CMD: return("SPACE_CMD"); + case IDETAPE_INQUIRY_CMD: return("INQUIRY_CMD"); + case IDETAPE_ERASE_CMD: return("ERASE_CMD"); + case IDETAPE_MODE_SENSE_CMD: return("MODE_SENSE_CMD"); + case IDETAPE_MODE_SELECT_CMD: return("MODE_SELECT_CMD"); + case IDETAPE_LOAD_UNLOAD_CMD: return("LOAD_UNLOAD_CMD"); + case IDETAPE_PREVENT_CMD: return("PREVENT_CMD"); + case IDETAPE_LOCATE_CMD: return("LOCATE_CMD"); + case IDETAPE_READ_POSITION_CMD: return("READ_POSITION_CMD"); + case IDETAPE_READ_BUFFER_CMD: return("READ_BUFFER_CMD"); + case IDETAPE_SET_SPEED_CMD: return("SET_SPEED_CMD"); + default: { + char buf[20]; + sprintf(buf, "CMD (0x%02x)", idetape_command_key); + return(buf); + } + } +} +#endif /* IDETAPE_DEBUG_LOG_VERBOSE */ + +/* + * Function declarations + * + */ +static void idetape_onstream_mode_sense_tape_parameter_page(ide_drive_t *drive, int debug); +static int idetape_chrdev_release (struct inode *inode, struct file *filp); +static void idetape_write_release (struct inode *inode); + +/* + * Too bad. The drive wants to send us data which we are not ready to accept. + * Just throw it away. + */ +static void idetape_discard_data (ide_drive_t *drive, unsigned int bcount) +{ + while (bcount--) + IN_BYTE(IDE_DATA_REG); +} + +static void idetape_input_buffers (ide_drive_t *drive, idetape_pc_t *pc, unsigned int bcount) +{ + struct bio *bio = pc->bio; + int count; + + while (bcount) { +#if IDETAPE_DEBUG_BUGS + if (bio == NULL) { + printk(KERN_ERR "ide-tape: bio == NULL in " + "idetape_input_buffers\n"); + idetape_discard_data(drive, bcount); + return; + } +#endif /* IDETAPE_DEBUG_BUGS */ + count = min(bio->bi_size - pc->b_count, bcount); + atapi_input_bytes(drive, bio_data(bio) + pc->b_count, count); + bcount -= count; + pc->b_count += bio->bi_size; + if (pc->b_count == bio->bi_size) { + bio = bio->bi_next; + if (bio) + pc->b_count = 0; + } + } + pc->bio = bio; +} + +static void idetape_output_buffers (ide_drive_t *drive, idetape_pc_t *pc, unsigned int bcount) +{ + struct bio *bio = pc->bio; + int count; + + while (bcount) { +#if IDETAPE_DEBUG_BUGS + if (bio == NULL) { + printk(KERN_ERR "ide-tape: bio == NULL in " + "idetape_output_buffers\n"); + return; + } +#endif /* IDETAPE_DEBUG_BUGS */ + count = min((unsigned long) pc->b_count, (unsigned long) bcount); + atapi_output_bytes(drive, /*(void *)*/ bio_data(bio), count); + bcount -= count; + pc->b_data += count; + pc->b_count -= count; + if (!pc->b_count) { + pc->bio = bio = bio->bi_next; + if (bio) { + pc->b_data = bio_data(bio); + pc->b_count = bio->bi_size; + } + } + } +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static void idetape_update_buffers (idetape_pc_t *pc) +{ + struct bio *bio = pc->bio; + int count; + unsigned int bcount = pc->actually_transferred; + + if (test_bit(PC_WRITING, &pc->flags)) + return; + while (bcount) { +#if IDETAPE_DEBUG_BUGS + if (bio == NULL) { + printk(KERN_ERR "ide-tape: bio == NULL in " + "idetape_update_buffers\n"); + return; + } +#endif /* IDETAPE_DEBUG_BUGS */ + count = min((unsigned long) bio->bi_size, (unsigned long) bcount); + pc->b_count = count; + if (pc->b_count == bio->bi_size) + bio = bio->bi_next; + bcount -= count; + } + pc->bio = bio; +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +/* + * idetape_next_pc_storage returns a pointer to a place in which we can + * safely store a packet command, even though we intend to leave the + * driver. A storage space for a maximum of IDETAPE_PC_STACK packet + * commands is allocated at initialization time. + */ +static idetape_pc_t *idetape_next_pc_storage (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 5) + printk(KERN_INFO "ide-tape: pc_stack_index=%d\n", + tape->pc_stack_index); +#endif /* IDETAPE_DEBUG_LOG */ + if (tape->pc_stack_index == IDETAPE_PC_STACK) + tape->pc_stack_index=0; + return (&tape->pc_stack[tape->pc_stack_index++]); +} + +/* + * idetape_next_rq_storage is used along with idetape_next_pc_storage. + * Since we queue packet commands in the request queue, we need to + * allocate a request, along with the allocation of a packet command. + */ + +/************************************************************** + * * + * This should get fixed to use kmalloc(.., GFP_ATOMIC) * + * followed later on by kfree(). -ml * + * * + **************************************************************/ + +static struct request *idetape_next_rq_storage (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 5) + printk(KERN_INFO "ide-tape: rq_stack_index=%d\n", + tape->rq_stack_index); +#endif /* IDETAPE_DEBUG_LOG */ + if (tape->rq_stack_index==IDETAPE_PC_STACK) + tape->rq_stack_index=0; + return (&tape->rq_stack[tape->rq_stack_index++]); +} + +/* + * idetape_init_pc initializes a packet command. + */ +static void idetape_init_pc (idetape_pc_t *pc) +{ + memset(pc->c, 0, 12); + pc->retries = 0; + pc->flags = 0; + pc->request_transfer = 0; + pc->buffer = pc->pc_buffer; + pc->buffer_size = IDETAPE_PC_BUFFER_SIZE; + pc->bio = NULL; + pc->b_data = NULL; +} + +/* + * idetape_analyze_error is called on each failed packet command retry + * to analyze the request sense. We currently do not utilize this + * information. + */ +static void idetape_analyze_error (ide_drive_t *drive, idetape_request_sense_result_t *result) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t *pc = tape->failed_pc; + + tape->sense = *result; + tape->sense_key = result->sense_key; + tape->asc = result->asc; + tape->ascq = result->ascq; +#if IDETAPE_DEBUG_LOG + /* + * Without debugging, we only log an error if we decided to + * give up retrying. + */ + if (tape->debug_level >= 1) + printk(KERN_INFO "ide-tape: pc = %x, sense key = %x, " + "asc = %x, ascq = %x\n", + pc->c[0], result->sense_key, + result->asc, result->ascq); +#if IDETAPE_DEBUG_LOG_VERBOSE + if (tape->debug_level >= 1) + printk(KERN_INFO "ide-tape: pc = %s, sense key = %x, " + "asc = %x, ascq = %x\n", + idetape_command_key_verbose((byte) pc->c[0]), + result->sense_key, + result->asc, + result->ascq); +#endif /* IDETAPE_DEBUG_LOG_VERBOSE */ +#endif /* IDETAPE_DEBUG_LOG */ + + if (tape->onstream && result->sense_key == 2 && + result->asc == 0x53 && result->ascq == 2) { + clear_bit(PC_DMA_ERROR, &pc->flags); + ide_stall_queue(drive, HZ / 2); + return; + } +#ifdef CONFIG_BLK_DEV_IDEDMA + + /* + * Correct pc->actually_transferred by asking the tape. + */ + if (test_bit(PC_DMA_ERROR, &pc->flags)) { + pc->actually_transferred = pc->request_transfer - tape->tape_block_size * ntohl(get_unaligned(&result->information)); + idetape_update_buffers(pc); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + if (pc->c[0] == IDETAPE_READ_CMD && result->filemark) { + pc->error = IDETAPE_ERROR_FILEMARK; + set_bit(PC_ABORT, &pc->flags); + } + if (pc->c[0] == IDETAPE_WRITE_CMD) { + if (result->eom || + (result->sense_key == 0xd && result->asc == 0x0 && + result->ascq == 0x2)) { + pc->error = IDETAPE_ERROR_EOD; + set_bit(PC_ABORT, &pc->flags); + } + } + if (pc->c[0] == IDETAPE_READ_CMD || pc->c[0] == IDETAPE_WRITE_CMD) { + if (result->sense_key == 8) { + pc->error = IDETAPE_ERROR_EOD; + set_bit(PC_ABORT, &pc->flags); + } + if (!test_bit(PC_ABORT, &pc->flags) && + (tape->onstream || pc->actually_transferred)) + pc->retries = IDETAPE_MAX_PC_RETRIES + 1; + } +} + +static void idetape_abort_pipeline (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage = tape->next_stage; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: %s: idetape_abort_pipeline called\n", tape->name); +#endif + while (stage) { + if (stage->rq.flags == IDETAPE_WRITE_RQ) + stage->rq.flags = IDETAPE_ABORTED_WRITE_RQ; + else if (stage->rq.flags == IDETAPE_READ_RQ) + stage->rq.flags = IDETAPE_ABORTED_READ_RQ; + stage = stage->next; + } +} + +/* + * idetape_active_next_stage will declare the next stage as "active". + */ +static void idetape_active_next_stage (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage = tape->next_stage; + struct request *rq = &stage->rq; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: Reached idetape_active_next_stage\n"); +#endif /* IDETAPE_DEBUG_LOG */ +#if IDETAPE_DEBUG_BUGS + if (stage == NULL) { + printk(KERN_ERR "ide-tape: bug: Trying to activate a non existing stage\n"); + return; + } +#endif /* IDETAPE_DEBUG_BUGS */ + + rq->buffer = NULL; + rq->bio = stage->bio; + tape->active_data_request = rq; + tape->active_stage = stage; + tape->next_stage = stage->next; +} + +/* + * idetape_increase_max_pipeline_stages is a part of the feedback + * loop which tries to find the optimum number of stages. In the + * feedback loop, we are starting from a minimum maximum number of + * stages, and if we sense that the pipeline is empty, we try to + * increase it, until we reach the user compile time memory limit. + */ +static void idetape_increase_max_pipeline_stages (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + int increase = (tape->max_pipeline - tape->min_pipeline) / 10; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk (KERN_INFO "ide-tape: Reached idetape_increase_max_pipeline_stages\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + tape->max_stages += increase; + tape->max_stages = max(tape->max_stages, tape->min_pipeline); + tape->max_stages = min(tape->max_stages, tape->max_pipeline); +} + +/* + * idetape_kfree_stage calls kfree to completely free a stage, along with + * its related buffers. + */ +static void __idetape_kfree_stage (idetape_stage_t *stage) +{ + struct bio *prev_bio, *bio = stage->bio; + int size; + + while (bio != NULL) { + if (bio_data(bio) != NULL) { + size = (int) bio->bi_size; + while (size > 0) { + free_page((unsigned long) bio_data(bio)); + size -= PAGE_SIZE; + bio->bi_size += PAGE_SIZE; + } + } + prev_bio = bio; + bio = bio->bi_next; + kfree(prev_bio); + } + kfree(stage); +} + +static void idetape_kfree_stage (idetape_tape_t *tape, idetape_stage_t *stage) +{ + __idetape_kfree_stage(stage); +} + +/* + * idetape_remove_stage_head removes tape->first_stage from the pipeline. + * The caller should avoid race conditions. + */ +static void idetape_remove_stage_head (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: Reached idetape_remove_stage_head\n"); +#endif /* IDETAPE_DEBUG_LOG */ +#if IDETAPE_DEBUG_BUGS + if (tape->first_stage == NULL) { + printk(KERN_ERR "ide-tape: bug: tape->first_stage is NULL\n"); + return; + } + if (tape->active_stage == tape->first_stage) { + printk(KERN_ERR "ide-tape: bug: Trying to free our active pipeline stage\n"); + return; + } +#endif /* IDETAPE_DEBUG_BUGS */ + stage = tape->first_stage; + tape->first_stage = stage->next; + idetape_kfree_stage(tape, stage); + tape->nr_stages--; + if (tape->first_stage == NULL) { + tape->last_stage = NULL; +#if IDETAPE_DEBUG_BUGS + if (tape->next_stage != NULL) + printk(KERN_ERR "ide-tape: bug: tape->next_stage != NULL\n"); + if (tape->nr_stages) + printk(KERN_ERR "ide-tape: bug: nr_stages should be 0 now\n"); +#endif /* IDETAPE_DEBUG_BUGS */ + } +} + +/* + * idetape_end_request is used to finish servicing a request, and to + * insert a pending pipeline request into the main device queue. + */ +static int idetape_end_request (ide_drive_t *drive, int uptodate) +{ + struct request *rq = HWGROUP(drive)->rq; + idetape_tape_t *tape = drive->driver_data; + unsigned long flags; + int error; + int remove_stage = 0; +#if ONSTREAM_DEBUG + idetape_stage_t *stage; + os_aux_t *aux; + unsigned char *p; +#endif + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: Reached idetape_end_request\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + switch (uptodate) { + case 0: error = IDETAPE_ERROR_GENERAL; break; + case 1: error = 0; break; + default: error = uptodate; + } + rq->errors = error; + if (error) + tape->failed_pc = NULL; + + spin_lock_irqsave(&tape->spinlock, flags); + + /* The request was a pipelined data transfer request */ + if (tape->active_data_request == rq) { + tape->active_stage = NULL; + tape->active_data_request = NULL; + tape->nr_pending_stages--; + if (rq->flags == IDETAPE_WRITE_RQ) { +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) { + if (tape->onstream) { + stage = tape->first_stage; + aux = stage->aux; + p = bio_data(stage->bio); + if (ntohl(aux->logical_blk_num) < 11300 && ntohl(aux->logical_blk_num) > 11100) + printk(KERN_INFO "ide-tape: finished writing logical blk %u (data %x %x %x %x)\n", ntohl(aux->logical_blk_num), *p++, *p++, *p++, *p++); + } + } +#endif + if (tape->onstream && !tape->raw) { + if (tape->first_frame_position == OS_DATA_ENDFRAME1) { +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk("ide-tape: %s: skipping over config parition..\n", tape->name); +#endif + tape->onstream_write_error = OS_PART_ERROR; + if (tape->waiting) + complete(tape->waiting); + } + } + remove_stage = 1; + if (error) { + set_bit(IDETAPE_PIPELINE_ERROR, &tape->flags); + if (error == IDETAPE_ERROR_EOD) + idetape_abort_pipeline(drive); + if (tape->onstream && !tape->raw && + error == IDETAPE_ERROR_GENERAL && + tape->sense.sense_key == 3) { + clear_bit(IDETAPE_PIPELINE_ERROR, &tape->flags); + printk(KERN_ERR "ide-tape: %s: write error, enabling error recovery\n", tape->name); + tape->onstream_write_error = OS_WRITE_ERROR; + remove_stage = 0; + tape->nr_pending_stages++; + tape->next_stage = tape->first_stage; + rq->current_nr_sectors = rq->nr_sectors; + if (tape->waiting) + complete(tape->waiting); + } + } + } else if (rq->flags == IDETAPE_READ_RQ) { + if (error == IDETAPE_ERROR_EOD) { + set_bit(IDETAPE_PIPELINE_ERROR, &tape->flags); + idetape_abort_pipeline(drive); + } + } + if (tape->next_stage != NULL && !tape->onstream_write_error) { + idetape_active_next_stage(drive); + + /* + * Insert the next request into the request queue. + */ + (void) ide_do_drive_cmd(drive, tape->active_data_request, ide_end); + } else if (!error) { + if (!tape->onstream) + idetape_increase_max_pipeline_stages (drive); + } + } + ide_end_drive_cmd(drive, 0, 0); +// blkdev_dequeue_request(rq); +// drive->rq = NULL; +// end_that_request_last(rq); + + if (remove_stage) + idetape_remove_stage_head(drive); + if (tape->active_data_request == NULL) + clear_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags); + spin_unlock_irqrestore(&tape->spinlock, flags); + return 0; +} + +static ide_startstop_t idetape_request_sense_callback (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: Reached idetape_request_sense_callback\n"); +#endif /* IDETAPE_DEBUG_LOG */ + if (!tape->pc->error) { + idetape_analyze_error(drive, (idetape_request_sense_result_t *) tape->pc->buffer); + idetape_end_request(drive, 1); + } else { + printk(KERN_ERR "ide-tape: Error in REQUEST SENSE itself - Aborting request!\n"); + idetape_end_request(drive, 0); + } + return ide_stopped; +} + +static void idetape_create_request_sense_cmd (idetape_pc_t *pc) +{ + idetape_init_pc(pc); + pc->c[0] = IDETAPE_REQUEST_SENSE_CMD; + pc->c[4] = 20; + pc->request_transfer = 18; + pc->callback = &idetape_request_sense_callback; +} + +/* + * idetape_queue_pc_head generates a new packet command request in front + * of the request queue, before the current request, so that it will be + * processed immediately, on the next pass through the driver. + * + * idetape_queue_pc_head is called from the request handling part of + * the driver (the "bottom" part). Safe storage for the request should + * be allocated with idetape_next_pc_storage and idetape_next_rq_storage + * before calling idetape_queue_pc_head. + * + * Memory for those requests is pre-allocated at initialization time, and + * is limited to IDETAPE_PC_STACK requests. We assume that we have enough + * space for the maximum possible number of inter-dependent packet commands. + * + * The higher level of the driver - The ioctl handler and the character + * device handling functions should queue request to the lower level part + * and wait for their completion using idetape_queue_pc_tail or + * idetape_queue_rw_tail. + */ +static void idetape_queue_pc_head (ide_drive_t *drive, idetape_pc_t *pc,struct request *rq) +{ + ide_init_drive_cmd(rq); + rq->buffer = (char *) pc; + rq->flags = IDETAPE_PC_RQ1; + (void) ide_do_drive_cmd(drive, rq, ide_preempt); +} + +/* + * idetape_retry_pc is called when an error was detected during the + * last packet command. We queue a request sense packet command in + * the head of the request list. + */ +static ide_startstop_t idetape_retry_pc (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t *pc; + struct request *rq; + idetape_error_reg_t error; + + error.all = IN_BYTE(IDE_ERROR_REG); + pc = idetape_next_pc_storage(drive); + rq = idetape_next_rq_storage(drive); + idetape_create_request_sense_cmd(pc); + set_bit(IDETAPE_IGNORE_DSC, &tape->flags); + idetape_queue_pc_head(drive, pc, rq); + return ide_stopped; +} + +/* + * idetape_postpone_request postpones the current request so that + * ide.c will be able to service requests from another device on + * the same hwgroup while we are polling for DSC. + */ +static void idetape_postpone_request (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: idetape_postpone_request\n"); +#endif + tape->postponed_rq = HWGROUP(drive)->rq; + ide_stall_queue(drive, tape->dsc_polling_frequency); +} + +/* + * idetape_pc_intr is the usual interrupt handler which will be called + * during a packet command. We will transfer some of the data (as + * requested by the drive) and will re-point interrupt handler to us. + * When data transfer is finished, we will act according to the + * algorithm described before idetape_issue_packet_command. + * + */ +static ide_startstop_t idetape_pc_intr (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_status_reg_t status; + idetape_bcount_reg_t bcount; + idetape_ireason_reg_t ireason; + idetape_pc_t *pc = tape->pc; + + unsigned int temp; + unsigned long cmd_time; +#if SIMULATE_ERRORS + static int error_sim_count = 0; +#endif + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: Reached idetape_pc_intr " + "interrupt handler\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + status.all = GET_STAT(); /* Clear the interrupt */ + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (test_bit(PC_DMA_IN_PROGRESS, &pc->flags)) { + if (HWIF(drive)->dmaproc(ide_dma_end, drive)) { + /* + * A DMA error is sometimes expected. For example, + * if the tape is crossing a filemark during a + * READ command, it will issue an irq and position + * itself before the filemark, so that only a partial + * data transfer will occur (which causes the DMA + * error). In that case, we will later ask the tape + * how much bytes of the original request were + * actually transferred (we can't receive that + * information from the DMA engine on most chipsets). + */ + set_bit(PC_DMA_ERROR, &pc->flags); + } else if (!status.b.check) { + pc->actually_transferred = pc->request_transfer; + idetape_update_buffers(pc); + } +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: DMA finished\n"); +#endif /* IDETAPE_DEBUG_LOG */ + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + if (!status.b.drq) { /* No more interrupts */ + cmd_time = (jiffies - tape->cmd_start_time) * 1000 / HZ; + tape->max_cmd_time = max(cmd_time, tape->max_cmd_time); +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: Packet command completed, %d bytes transferred\n", pc->actually_transferred); +#endif /* IDETAPE_DEBUG_LOG */ + clear_bit(PC_DMA_IN_PROGRESS, &pc->flags); + + local_irq_enable(); + +#if SIMULATE_ERRORS + if ((pc->c[0] == IDETAPE_WRITE_CMD || + pc->c[0] == IDETAPE_READ_CMD) && + (++error_sim_count % 100) == 0) { + printk(KERN_INFO "ide-tape: %s: simulating error\n", + tape->name); + status.b.check = 1; + } +#endif + if (status.b.check && pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) + status.b.check = 0; + if (status.b.check || test_bit(PC_DMA_ERROR, &pc->flags)) { /* Error detected */ +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 1) + printk(KERN_INFO "ide-tape: %s: I/O error, ", + tape->name); +#endif /* IDETAPE_DEBUG_LOG */ + if (pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) { + printk(KERN_ERR "ide-tape: I/O error in request sense command\n"); + return ide_do_reset(drive); + } +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 1) + printk(KERN_INFO "ide-tape: [cmd %x]: check condition\n", pc->c[0]); +#endif + return idetape_retry_pc(drive); /* Retry operation */ + } + pc->error = 0; + if (!tape->onstream && + test_bit(PC_WAIT_FOR_DSC, &pc->flags) && !status.b.dsc) { + /* Media access command */ + tape->dsc_polling_start = jiffies; + tape->dsc_polling_frequency = IDETAPE_DSC_MA_FAST; + tape->dsc_timeout = jiffies + IDETAPE_DSC_MA_TIMEOUT; + idetape_postpone_request(drive); /* Allow ide.c to handle other requests */ + return ide_stopped; + } + if (tape->failed_pc == pc) + tape->failed_pc = NULL; + return pc->callback(drive); /* Command finished - Call the callback function */ + } +#ifdef CONFIG_BLK_DEV_IDEDMA + if (test_and_clear_bit(PC_DMA_IN_PROGRESS, &pc->flags)) { + printk(KERN_ERR "ide-tape: The tape wants to issue more " + "interrupts in DMA mode\n"); + printk(KERN_ERR "ide-tape: DMA disabled, reverting to PIO\n"); + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); + return ide_do_reset(drive); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + bcount.b.high = IN_BYTE(IDE_BCOUNTH_REG); /* Get the number of bytes to transfer */ + bcount.b.low = IN_BYTE(IDE_BCOUNTL_REG); /* on this interrupt */ + ireason.all = IN_BYTE(IDE_IREASON_REG); + + if (ireason.b.cod) { + printk(KERN_ERR "ide-tape: CoD != 0 in idetape_pc_intr\n"); + return ide_do_reset(drive); + } + if (ireason.b.io == test_bit(PC_WRITING, &pc->flags)) { /* Hopefully, we will never get here */ + printk(KERN_ERR "ide-tape: We wanted to %s, ", + ireason.b.io ? "Write":"Read"); + printk(KERN_ERR "ide-tape: but the tape wants us to %s !\n", + ireason.b.io ? "Read":"Write"); + return ide_do_reset(drive); + } + if (!test_bit(PC_WRITING, &pc->flags)) { /* Reading - Check that we have enough space */ + temp = pc->actually_transferred + bcount.all; + if (temp > pc->request_transfer) { + if (temp > pc->buffer_size) { + printk(KERN_ERR "ide-tape: The tape wants to send us more data than expected - discarding data\n"); + idetape_discard_data(drive, bcount.all); + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL); + return ide_started; + } +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 2) + printk(KERN_NOTICE "ide-tape: The tape wants to send us more data than expected - allowing transfer\n"); +#endif /* IDETAPE_DEBUG_LOG */ + } + } + if (test_bit(PC_WRITING, &pc->flags)) { + if (pc->bio != NULL) + idetape_output_buffers(drive, pc, bcount.all); + else + atapi_output_bytes(drive, pc->current_position, bcount.all); /* Write the current buffer */ + } else { + if (pc->bio != NULL) + idetape_input_buffers(drive, pc, bcount.all); + else + atapi_input_bytes(drive, pc->current_position, bcount.all); /* Read the current buffer */ + } + pc->actually_transferred += bcount.all; /* Update the current position */ + pc->current_position += bcount.all; +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: [cmd %x] transferred %d bytes on that interrupt\n", pc->c[0], bcount.all); +#endif + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL); /* And set the interrupt handler again */ + return ide_started; +} + +/* + * Packet Command Interface + * + * The current Packet Command is available in tape->pc, and will not + * change until we finish handling it. Each packet command is associated + * with a callback function that will be called when the command is + * finished. + * + * The handling will be done in three stages: + * + * 1. idetape_issue_packet_command will send the packet command to the + * drive, and will set the interrupt handler to idetape_pc_intr. + * + * 2. On each interrupt, idetape_pc_intr will be called. This step + * will be repeated until the device signals us that no more + * interrupts will be issued. + * + * 3. ATAPI Tape media access commands have immediate status with a + * delayed process. In case of a successful initiation of a + * media access packet command, the DSC bit will be set when the + * actual execution of the command is finished. + * Since the tape drive will not issue an interrupt, we have to + * poll for this event. In this case, we define the request as + * "low priority request" by setting rq_status to + * IDETAPE_RQ_POSTPONED, set a timer to poll for DSC and exit + * the driver. + * + * ide.c will then give higher priority to requests which + * originate from the other device, until will change rq_status + * to RQ_ACTIVE. + * + * 4. When the packet command is finished, it will be checked for errors. + * + * 5. In case an error was found, we queue a request sense packet command + * in front of the request queue and retry the operation up to + * IDETAPE_MAX_PC_RETRIES times. + * + * 6. In case no error was found, or we decided to give up and not + * to retry again, the callback function will be called and then + * we will handle the next request. + * + */ +static ide_startstop_t idetape_transfer_pc(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t *pc = tape->pc; + idetape_ireason_reg_t ireason; + int retries = 100; + ide_startstop_t startstop; + + if (ide_wait_stat(&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + printk(KERN_ERR "ide-tape: Strange, packet command initiated yet DRQ isn't asserted\n"); + return startstop; + } + ireason.all = IN_BYTE(IDE_IREASON_REG); + while (retries-- && (!ireason.b.cod || ireason.b.io)) { + printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while issuing " + "a packet command, retrying\n"); + udelay(100); + ireason.all = IN_BYTE(IDE_IREASON_REG); + if (retries == 0) { + printk(KERN_ERR "ide-tape: (IO,CoD != (0,1) while " + "issuing a packet command, ignoring\n"); + ireason.b.cod = 1; + ireason.b.io = 0; + } + } + if (!ireason.b.cod || ireason.b.io) { + printk(KERN_ERR "ide-tape: (IO,CoD) != (0,1) while issuing " + "a packet command\n"); + return ide_do_reset(drive); + } + tape->cmd_start_time = jiffies; + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &idetape_pc_intr, IDETAPE_WAIT_CMD, NULL); /* Set the interrupt routine */ + atapi_output_bytes(drive, pc->c, 12); /* Send the actual packet */ + return ide_started; +} + +static ide_startstop_t idetape_issue_packet_command (ide_drive_t *drive, idetape_pc_t *pc) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_bcount_reg_t bcount; + int dma_ok = 0; + +#if IDETAPE_DEBUG_BUGS + if (tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD && + pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) { + printk(KERN_ERR "ide-tape: possible ide-tape.c bug - " + "Two request sense in serial were issued\n"); + } +#endif /* IDETAPE_DEBUG_BUGS */ + + if (tape->failed_pc == NULL && pc->c[0] != IDETAPE_REQUEST_SENSE_CMD) + tape->failed_pc = pc; + tape->pc = pc; /* Set the current packet command */ + + if (pc->retries > IDETAPE_MAX_PC_RETRIES || + test_bit(PC_ABORT, &pc->flags)) { + /* + * We will "abort" retrying a packet command in case + * a legitimate error code was received (crossing a + * filemark, or DMA error in the end of media, for + * example). + */ + if (!test_bit(PC_ABORT, &pc->flags)) { + if (!(pc->c[0] == IDETAPE_TEST_UNIT_READY_CMD && + tape->sense_key == 2 && tape->asc == 4 && + (tape->ascq == 1 || tape->ascq == 8))) { + printk(KERN_ERR "ide-tape: %s: I/O error, " + "pc = %2x, key = %2x, " + "asc = %2x, ascq = %2x\n", + tape->name, pc->c[0], + tape->sense_key, tape->asc, + tape->ascq); + if (tape->onstream && + pc->c[0] == IDETAPE_READ_CMD && + tape->sense_key == 3 && + tape->asc == 0x11) /* AJN-1: 11 should be 0x11 */ + printk(KERN_ERR "ide-tape: %s: enabling read error recovery\n", tape->name); + } + pc->error = IDETAPE_ERROR_GENERAL; /* Giving up */ + } + tape->failed_pc = NULL; + return pc->callback(drive); + } +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: Retry number - %d\n", pc->retries); +#endif /* IDETAPE_DEBUG_LOG */ + + pc->retries++; + pc->actually_transferred = 0; /* We haven't transferred any data yet */ + pc->current_position = pc->buffer; + bcount.all = pc->request_transfer; /* Request to transfer the entire buffer at once */ + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (test_and_clear_bit(PC_DMA_ERROR, &pc->flags)) { + printk(KERN_WARNING "ide-tape: DMA disabled, " + "reverting to PIO\n"); + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); + } + if (test_bit(PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) + dma_ok = !HWIF(drive)->dmaproc(test_bit(PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl, IDE_CONTROL_REG); + OUT_BYTE(dma_ok ? 1 : 0, IDE_FEATURE_REG); /* Use PIO/DMA */ + OUT_BYTE(bcount.b.high, IDE_BCOUNTH_REG); + OUT_BYTE(bcount.b.low, IDE_BCOUNTL_REG); + OUT_BYTE(drive->select.all, IDE_SELECT_REG); +#ifdef CONFIG_BLK_DEV_IDEDMA + if (dma_ok) { /* Begin DMA, if necessary */ + set_bit(PC_DMA_IN_PROGRESS, &pc->flags); + (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) { + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &idetape_transfer_pc, IDETAPE_WAIT_CMD, NULL); + OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); + return ide_started; + } else { + OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); + return idetape_transfer_pc(drive); + } +} + +/* + * General packet command callback function. + */ +static ide_startstop_t idetape_pc_callback (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: Reached idetape_pc_callback\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + idetape_end_request(drive, tape->pc->error ? 0 : 1); + return ide_stopped; +} + +/* + * A mode sense command is used to "sense" tape parameters. + */ +static void idetape_create_mode_sense_cmd (idetape_pc_t *pc, byte page_code) +{ + idetape_init_pc(pc); + pc->c[0] = IDETAPE_MODE_SENSE_CMD; + if (page_code != IDETAPE_BLOCK_DESCRIPTOR) + pc->c[1] = 8; /* DBD = 1 - Don't return block descriptors */ + pc->c[2] = page_code; + pc->c[3] = 255; /* Don't limit the returned information */ + pc->c[4] = 255; /* (We will just discard data in that case) */ + if (page_code == IDETAPE_BLOCK_DESCRIPTOR) + pc->request_transfer = 12; + else if (page_code == IDETAPE_CAPABILITIES_PAGE) + pc->request_transfer = 24; + else + pc->request_transfer = 50; + pc->callback = &idetape_pc_callback; +} + +static ide_startstop_t idetape_onstream_buffer_fill_callback (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + tape->max_frames = tape->pc->buffer[4 + 2]; + tape->cur_frames = tape->pc->buffer[4 + 3]; + if (tape->chrdev_direction == idetape_direction_write) + tape->tape_head = tape->buffer_head - tape->cur_frames; + else + tape->tape_head = tape->buffer_head + tape->cur_frames; + if (tape->tape_head != tape->last_tape_head) { + tape->last_tape_head = tape->tape_head; + tape->tape_still_time_begin = jiffies; + if (tape->tape_still_time > 200) + tape->measure_insert_time = 1; + } + tape->tape_still_time = (jiffies - tape->tape_still_time_begin) * 1000 / HZ; +#if USE_IOTRACE + IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, + tape->tape_head, tape->minor); +#endif +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 1) + printk(KERN_INFO "ide-tape: buffer fill callback, %d/%d\n", + tape->cur_frames, tape->max_frames); +#endif + idetape_end_request(drive, tape->pc->error ? 0 : 1); + return ide_stopped; +} + +static void idetape_queue_onstream_buffer_fill (ide_drive_t *drive) +{ + idetape_pc_t *pc; + struct request *rq; + + pc = idetape_next_pc_storage(drive); + rq = idetape_next_rq_storage(drive); + idetape_create_mode_sense_cmd(pc, IDETAPE_BUFFER_FILLING_PAGE); + pc->callback = idetape_onstream_buffer_fill_callback; + idetape_queue_pc_head(drive, pc, rq); +} + +static void calculate_speeds(ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + int full = 125, empty = 75; + + if (time_after(jiffies, tape->controlled_pipeline_head_time + 120 * HZ)) { + tape->controlled_previous_pipeline_head = tape->controlled_last_pipeline_head; + tape->controlled_previous_head_time = tape->controlled_pipeline_head_time; + tape->controlled_last_pipeline_head = tape->pipeline_head; + tape->controlled_pipeline_head_time = jiffies; + } + if (time_after(jiffies, tape->controlled_pipeline_head_time + 60 * HZ)) + tape->controlled_pipeline_head_speed = (tape->pipeline_head - tape->controlled_last_pipeline_head) * 32 * HZ / (jiffies - tape->controlled_pipeline_head_time); + else if (time_after(jiffies, tape->controlled_previous_head_time)) + tape->controlled_pipeline_head_speed = (tape->pipeline_head - tape->controlled_previous_pipeline_head) * 32 * HZ / (jiffies - tape->controlled_previous_head_time); + + if (tape->nr_pending_stages < tape->max_stages /*- 1 */) { /* -1 for read mode error recovery */ + if (time_after(jiffies, tape->uncontrolled_previous_head_time + 10 * HZ)) { + tape->uncontrolled_pipeline_head_time = jiffies; + tape->uncontrolled_pipeline_head_speed = (tape->pipeline_head - tape->uncontrolled_previous_pipeline_head) * 32 * HZ / (jiffies - tape->uncontrolled_previous_head_time); + } + } else { + tape->uncontrolled_previous_head_time = jiffies; + tape->uncontrolled_previous_pipeline_head = tape->pipeline_head; + if (time_after(jiffies, tape->uncontrolled_pipeline_head_time + 30 * HZ)) { + tape->uncontrolled_pipeline_head_time = jiffies; + } + } + tape->pipeline_head_speed = max(tape->uncontrolled_pipeline_head_speed, tape->controlled_pipeline_head_speed); + if (tape->speed_control == 0) { + tape->max_insert_speed = 5000; + } else if (tape->speed_control == 1) { + if (tape->nr_pending_stages >= tape->max_stages / 2) + tape->max_insert_speed = tape->pipeline_head_speed + + (1100 - tape->pipeline_head_speed) * 2 * (tape->nr_pending_stages - tape->max_stages / 2) / tape->max_stages; + else + tape->max_insert_speed = 500 + + (tape->pipeline_head_speed - 500) * 2 * tape->nr_pending_stages / tape->max_stages; + if (tape->nr_pending_stages >= tape->max_stages * 99 / 100) + tape->max_insert_speed = 5000; + } else if (tape->speed_control == 2) { + tape->max_insert_speed = tape->pipeline_head_speed * empty / 100 + + (tape->pipeline_head_speed * full / 100 - tape->pipeline_head_speed * empty / 100) * tape->nr_pending_stages / tape->max_stages; + } else + tape->max_insert_speed = tape->speed_control; + tape->max_insert_speed = max(tape->max_insert_speed, 500); +} + +static ide_startstop_t idetape_media_access_finished (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t *pc = tape->pc; + idetape_status_reg_t status; + + if (tape->onstream) + printk(KERN_INFO "ide-tape: bug: onstream, media_access_finished\n"); + status.all = GET_STAT(); + if (status.b.dsc) { + if (status.b.check) { /* Error detected */ + printk(KERN_ERR "ide-tape: %s: I/O error, ",tape->name); + return idetape_retry_pc(drive); /* Retry operation */ + } + pc->error = 0; + if (tape->failed_pc == pc) + tape->failed_pc = NULL; + } else { + pc->error = IDETAPE_ERROR_GENERAL; + tape->failed_pc = NULL; + } + return pc->callback(drive); +} + +static ide_startstop_t idetape_rw_callback (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + struct request *rq = HWGROUP(drive)->rq; + int blocks = tape->pc->actually_transferred / tape->tape_block_size; + + tape->avg_size += blocks * tape->tape_block_size; + tape->insert_size += blocks * tape->tape_block_size; + if (tape->insert_size > 1024 * 1024) + tape->measure_insert_time = 1; + if (tape->measure_insert_time) { + tape->measure_insert_time = 0; + tape->insert_time = jiffies; + tape->insert_size = 0; + } + if (time_after(jiffies, tape->insert_time)) + tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time); + if (jiffies - tape->avg_time >= HZ) { + tape->avg_speed = tape->avg_size * HZ / (jiffies - tape->avg_time) / 1024; + tape->avg_size = 0; + tape->avg_time = jiffies; + } + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: Reached idetape_rw_callback\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + tape->first_frame_position += blocks; + rq->current_nr_sectors -= blocks; + + if (!tape->pc->error) + idetape_end_request(drive, 1); + else + idetape_end_request(drive, tape->pc->error); + return ide_stopped; +} + +static void idetape_create_read_cmd (idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct bio *bio) +{ + struct bio *p = bio; + struct bio_vec *bv = bio_iovec(p); /* effective page bh */ + idetape_init_pc(pc); + pc->c[0] = IDETAPE_READ_CMD; + put_unaligned(htonl(length), (unsigned int *) &pc->c[1]); + pc->c[1] = 1; + pc->callback = &idetape_rw_callback; + pc->bio = bio; + bv->bv_len = 0; + pc->buffer = NULL; + if (tape->onstream) { + while (p) { + bv->bv_len = 0; + p = p->bi_next; + } + } + if (!tape->onstream) { + pc->request_transfer = pc->buffer_size = length * tape->tape_block_size; + if (pc->request_transfer == tape->stage_size) + set_bit(PC_DMA_RECOMMENDED, &pc->flags); + } else { + if (length) { + pc->request_transfer = pc->buffer_size = 32768 + 512; + set_bit(PC_DMA_RECOMMENDED, &pc->flags); + } else + pc->request_transfer = 0; + } +} + +static void idetape_create_read_buffer_cmd(idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct bio *bio) +{ + int size = 32768; + struct bio *p = bio; + + idetape_init_pc(pc); + pc->c[0] = IDETAPE_READ_BUFFER_CMD; + pc->c[1] = IDETAPE_RETRIEVE_FAULTY_BLOCK; + pc->c[7] = size >> 8; + pc->c[8] = size & 0xff; + pc->callback = &idetape_pc_callback; + pc->bio = bio; + atomic_set(&bio->bi_cnt, 0); + pc->buffer = NULL; + while (p) { + p->bi_size = 0; + p = p->bi_next; + } + pc->request_transfer = pc->buffer_size = size; +} + +static void idetape_create_write_cmd (idetape_tape_t *tape, idetape_pc_t *pc, unsigned int length, struct bio *bio) +{ + struct bio *p = bio; + struct bio_vec *bv= bio_iovec(p); + + idetape_init_pc(pc); + pc->c[0] = IDETAPE_WRITE_CMD; + put_unaligned(htonl(length), (unsigned int *) &pc->c[1]); + pc->c[1] = 1; + pc->callback = &idetape_rw_callback; + set_bit(PC_WRITING, &pc->flags); + if (tape->onstream) { + while (p) { + bv->bv_len = p->bi_size; + p = p->bi_next; + } + } + pc->bio = bio; + pc->b_data = bio_data(bio); + pc->b_count = bio->bi_size; + pc->buffer = NULL; + if (!tape->onstream) { + pc->request_transfer = pc->buffer_size = length * tape->tape_block_size; + if (pc->request_transfer == tape->stage_size) + set_bit (PC_DMA_RECOMMENDED, &pc->flags); + } else { + if (length) { + pc->request_transfer = pc->buffer_size = 32768 + 512; + set_bit(PC_DMA_RECOMMENDED, &pc->flags); + } else + pc->request_transfer = 0; + } +} + +/* + * This is our end_request replacement function. + */ +static int idetape_do_end_request (ide_drive_t *drive, int uptodate) +{ + struct request *rq; + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&ide_lock, flags); + rq = HWGROUP(drive)->rq; + + BUG_ON(!(rq->flags & REQ_STARTED)); + + /* + * decide whether to reenable DMA -- 3 is a random magic for now, + * if we DMA timeout more than 3 times, just stay in PIO + */ + if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) { + drive->state = 0; + HWGROUP(drive)->hwif->dmaproc(ide_dma_on, drive); + } + + if (!end_that_request_first(rq, uptodate, rq->hard_cur_sectors)) { + add_blkdev_randomness(major(rq->rq_dev)); + blkdev_dequeue_request(rq); + HWGROUP(drive)->rq = NULL; + end_that_request_last(rq); + ret = 0; + } + spin_unlock_irqrestore(&ide_lock, flags); + return ret; +} + +/* + * idetape_do_request is our request handling function. + */ +static ide_startstop_t idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t *pc; + struct request *postponed_rq = tape->postponed_rq; + idetape_status_reg_t status; + +#if IDETAPE_DEBUG_LOG +#if 0 + if (tape->debug_level >= 5) + printk(KERN_INFO "ide-tape: rq_status: %d, " + "rq_dev: %u, cmd: %ld, errors: %d\n", rq->rq_status, + (unsigned int) rq->rq_dev, rq->flags, rq->errors); +#endif + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: sector: %ld, " + "nr_sectors: %ld, current_nr_sectors: %d\n", + rq->sector, rq->nr_sectors, rq->current_nr_sectors); +#endif /* IDETAPE_DEBUG_LOG */ + + if (!IDETAPE_RQ_CMD(rq->flags)) { + /* + * We do not support buffer cache originated requests. + */ + printk(KERN_NOTICE "ide-tape: %s: Unsupported command in " + "request queue (%ld)\n", drive->name, rq->flags); + idetape_do_end_request(drive, 0); + return ide_stopped; + } + + /* + * Retry a failed packet command + */ + if (tape->failed_pc != NULL && + tape->pc->c[0] == IDETAPE_REQUEST_SENSE_CMD) { + return idetape_issue_packet_command(drive, tape->failed_pc); + } +#if IDETAPE_DEBUG_BUGS + if (postponed_rq != NULL) + if (rq != postponed_rq) { + printk(KERN_ERR "ide-tape: ide-tape.c bug - " + "Two DSC requests were queued\n"); + idetape_end_request(drive, 0); + return ide_stopped; + } +#endif /* IDETAPE_DEBUG_BUGS */ + + tape->postponed_rq = NULL; + + /* + * If the tape is still busy, postpone our request and service + * the other device meanwhile. + */ + status.all = GET_STAT(); + + /* + * The OnStream tape drive doesn't support DSC. Assume + * that DSC is always set. + */ + if (tape->onstream) + status.b.dsc = 1; + if (!drive->dsc_overlap && rq->flags != IDETAPE_PC_RQ2) + set_bit(IDETAPE_IGNORE_DSC, &tape->flags); + + /* + * For the OnStream tape, check the current status of the tape + * internal buffer using data gathered from the buffer fill + * mode page, and postpone our request, effectively "disconnecting" + * from the IDE bus, in case the buffer is full (writing) or + * empty (reading), and there is a danger that our request will + * hold the IDE bus during actual media access. + */ + if (tape->tape_still_time > 100 && tape->tape_still_time < 200) + tape->measure_insert_time = 1; + if (tape->req_buffer_fill && + (rq->flags == IDETAPE_WRITE_RQ || rq->flags == IDETAPE_READ_RQ)) { + tape->req_buffer_fill = 0; + tape->writes_since_buffer_fill = 0; + tape->reads_since_buffer_fill = 0; + tape->last_buffer_fill = jiffies; + idetape_queue_onstream_buffer_fill(drive); + if (time_after(jiffies, tape->insert_time)) + tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time); + return ide_stopped; + } + if (time_after(jiffies, tape->insert_time)) + tape->insert_speed = tape->insert_size / 1024 * HZ / (jiffies - tape->insert_time); + calculate_speeds(drive); + if (tape->onstream && tape->max_frames && + ((rq->flags == IDETAPE_WRITE_RQ && + ( tape->cur_frames == tape->max_frames || + ( tape->speed_control && tape->cur_frames > 5 && + (tape->insert_speed > tape->max_insert_speed || + (0 /* tape->cur_frames > 30 && tape->tape_still_time > 200 */) ) ) ) ) || + (rq->flags == IDETAPE_READ_RQ && + ( tape->cur_frames == 0 || + ( tape->speed_control && (tape->cur_frames < tape->max_frames - 5) && + tape->insert_speed > tape->max_insert_speed ) ) && rq->nr_sectors) ) ) { +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: postponing request, " + "cmd %ld, cur %d, max %d\n", + rq->flags, tape->cur_frames, tape->max_frames); +#endif + if (tape->postpone_cnt++ < 500) { + status.b.dsc = 0; + tape->req_buffer_fill = 1; + } +#if ONSTREAM_DEBUG + else if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: %s: postpone_cnt %d\n", + tape->name, tape->postpone_cnt); +#endif + } + if (!test_and_clear_bit(IDETAPE_IGNORE_DSC, &tape->flags) && !status.b.dsc) { + if (postponed_rq == NULL) { + tape->dsc_polling_start = jiffies; + tape->dsc_polling_frequency = tape->best_dsc_rw_frequency; + tape->dsc_timeout = jiffies + IDETAPE_DSC_RW_TIMEOUT; + } else if ((signed long) (jiffies - tape->dsc_timeout) > 0) { + printk(KERN_ERR "ide-tape: %s: DSC timeout\n", + tape->name); + if (rq->flags == IDETAPE_PC_RQ2) { + idetape_media_access_finished(drive); + return ide_stopped; + } else { + return ide_do_reset(drive); + } + } else if (jiffies - tape->dsc_polling_start > IDETAPE_DSC_MA_THRESHOLD) + tape->dsc_polling_frequency = IDETAPE_DSC_MA_SLOW; + idetape_postpone_request(drive); + return ide_stopped; + } + switch (rq->flags) { + case IDETAPE_READ_RQ: + tape->buffer_head++; +#if USE_IOTRACE + IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor); +#endif + tape->postpone_cnt = 0; + tape->reads_since_buffer_fill++; + if (tape->onstream) { + if (tape->cur_frames - tape->reads_since_buffer_fill <= 0) + tape->req_buffer_fill = 1; + if (time_after(jiffies, tape->last_buffer_fill + 5 * HZ / 100)) + tape->req_buffer_fill = 1; + } + pc = idetape_next_pc_storage(drive); + idetape_create_read_cmd(tape, pc, rq->current_nr_sectors, rq->bio); + break; + case IDETAPE_WRITE_RQ: + tape->buffer_head++; +#if USE_IOTRACE + IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor); +#endif + tape->postpone_cnt = 0; + tape->writes_since_buffer_fill++; + if (tape->onstream) { + if (tape->cur_frames + tape->writes_since_buffer_fill >= tape->max_frames) + tape->req_buffer_fill = 1; + if (time_after(jiffies, tape->last_buffer_fill + 5 * HZ / 100)) + tape->req_buffer_fill = 1; + calculate_speeds(drive); + } + pc = idetape_next_pc_storage(drive); + idetape_create_write_cmd(tape, pc, rq->current_nr_sectors, rq->bio); + break; + case IDETAPE_READ_BUFFER_RQ: + tape->postpone_cnt = 0; + pc = idetape_next_pc_storage(drive); + idetape_create_read_buffer_cmd(tape, pc, rq->current_nr_sectors, rq->bio); + break; + case IDETAPE_ABORTED_WRITE_RQ: + rq->flags = IDETAPE_WRITE_RQ; + idetape_end_request(drive, IDETAPE_ERROR_EOD); + return ide_stopped; + case IDETAPE_ABORTED_READ_RQ: +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: detected aborted read rq\n", tape->name); +#endif + rq->flags = IDETAPE_READ_RQ; + idetape_end_request(drive, IDETAPE_ERROR_EOD); + return ide_stopped; + case IDETAPE_PC_RQ1: + pc = (idetape_pc_t *) rq->buffer; + rq->flags = IDETAPE_PC_RQ2; + break; + case IDETAPE_PC_RQ2: + idetape_media_access_finished(drive); + return ide_stopped; + default: + printk(KERN_ERR "ide-tape: bug in IDETAPE_RQ_CMD macro\n"); + idetape_end_request(drive, 0); + return ide_stopped; + } + return idetape_issue_packet_command(drive, pc); +} + +/* + * Pipeline related functions + */ +static inline int idetape_pipeline_active (idetape_tape_t *tape) +{ + int rc1, rc2; + + rc1 = test_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags); + rc2 = (tape->active_data_request != NULL); + return rc1; +} + +/* + * idetape_kmalloc_stage uses __get_free_page to allocate a pipeline + * stage, along with all the necessary small buffers which together make + * a buffer of size tape->stage_size (or a bit more). We attempt to + * combine sequential pages as much as possible. + * + * Returns a pointer to the new allocated stage, or NULL if we + * can't (or don't want to) allocate a stage. + * + * Pipeline stages are optional and are used to increase performance. + * If we can't allocate them, we'll manage without them. + */ +static idetape_stage_t *__idetape_kmalloc_stage (idetape_tape_t *tape, int full, int clear) +{ + idetape_stage_t *stage; + struct bio *prev_bio, *bio; + int pages = tape->pages_per_stage; + char *b_data = NULL; + struct bio_vec *bv; + + if ((stage = (idetape_stage_t *) kmalloc (sizeof (idetape_stage_t),GFP_KERNEL)) == NULL) + return NULL; + stage->next = NULL; + + bio = stage->bio = bio_alloc(GFP_KERNEL,1); + bv = bio_iovec(bio); + bv->bv_len = 0; + if (bio == NULL) + goto abort; + bio->bi_next = NULL; + if ((bio->bi_io_vec[0].bv_page = alloc_page(GFP_KERNEL)) == NULL) + goto abort; + if (clear) + memset(bio_data(bio), 0, PAGE_SIZE); + bio->bi_size = PAGE_SIZE; + if (bv->bv_len == full) + bv->bv_len = bio->bi_size; + +// set_bit(BH_Lock, &bio->bi_flags); + + while (--pages) { + if ((bio->bi_io_vec[pages].bv_page = alloc_page(GFP_KERNEL)) == NULL) + goto abort; + if (clear) + memset(bio_data(bio), 0, PAGE_SIZE); + if (bio->bi_size == bv->bv_len + PAGE_SIZE) { + bio->bi_size += PAGE_SIZE; + bv->bv_len += PAGE_SIZE; + bv->bv_offset -= PAGE_SIZE; + if (full) + bio->bi_size += PAGE_SIZE; + continue; + } + if (b_data == bio_data(bio) + bio->bi_size) { + bio->bi_size += PAGE_SIZE; + if (full) + bio->bi_size += PAGE_SIZE; + continue; + } + prev_bio = bio; + if ((bio = bio_alloc(GFP_KERNEL,1)) == NULL) { + free_page((unsigned long) bio_data(bio)); + goto abort; + } + bio->bi_next = NULL; + //bio->bi_io_vec[0].bv_offset = b_data; + bio->bi_size = PAGE_SIZE; + atomic_set(&bio->bi_cnt, full ? bio->bi_size : 0); +// set_bit(BH_Lock, &bio->bi_flags); + prev_bio->bi_next = bio; + } + bio->bi_size -= tape->excess_bh_size; + if (full) + atomic_sub(tape->excess_bh_size, &bio->bi_cnt); + if (tape->onstream) + stage->aux = (os_aux_t *) (bio_data(bio) + bio->bi_size - OS_AUX_SIZE); + return stage; +abort: + __idetape_kfree_stage(stage); + return NULL; +} + +static idetape_stage_t *idetape_kmalloc_stage (idetape_tape_t *tape) +{ + idetape_stage_t *cache_stage = tape->cache_stage; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: Reached idetape_kmalloc_stage\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + if (tape->nr_stages >= tape->max_stages) + return NULL; + if (cache_stage != NULL) { + tape->cache_stage = NULL; + return cache_stage; + } + return __idetape_kmalloc_stage(tape, 0, 0); +} + +static void idetape_copy_stage_from_user (idetape_tape_t *tape, idetape_stage_t *stage, const char *buf, int n) +{ + struct bio *bio = tape->bio; + int count; + + while (n) { +#if IDETAPE_DEBUG_BUGS + if (bio == NULL) { + printk(KERN_ERR "ide-tape: bio == NULL in " + "idetape_copy_stage_from_user\n"); + return; + } +#endif /* IDETAPE_DEBUG_BUGS */ + count = min((unsigned long) (bio->bi_size - tape->b_count), (unsigned long) n); + copy_from_user(bio_data(bio) + tape->b_count, buf, count); + n -= count; + bio->bi_size += count; + buf += count; + if (tape->b_count == bio->bi_size) { + bio = bio->bi_next; + if (bio) + tape->b_count = 0; + } + } + tape->bio = bio; +} + +static void idetape_copy_stage_to_user (idetape_tape_t *tape, char *buf, idetape_stage_t *stage, int n) +{ + struct bio *bio = tape->bio; + int count; + + while (n) { +#if IDETAPE_DEBUG_BUGS + if (bio == NULL) { + printk(KERN_ERR "ide-tape: bio == NULL in " + "idetape_copy_stage_to_user\n"); + return; + } +#endif /* IDETAPE_DEBUG_BUGS */ + count = min(tape->b_count, n); + copy_to_user(buf, tape->b_data, count); + n -= count; + tape->b_data += count; + tape->b_count -= count; + buf += count; + if (!tape->b_count) { + tape->bio = bio = bio->bi_next; + if (bio) { + tape->b_data = bio_data(bio); + tape->b_count = bio->bi_size; + } + } + } +} + +static void idetape_init_merge_stage (idetape_tape_t *tape) +{ + struct bio *bio = tape->merge_stage->bio; + + tape->bio = bio; + if (tape->chrdev_direction == idetape_direction_write) + atomic_set(&bio->bi_cnt, 0); + else { + tape->b_data = bio_data(bio); + tape->b_count = atomic_read(&bio->bi_cnt); + } +} + +static void idetape_switch_buffers (idetape_tape_t *tape, idetape_stage_t *stage) +{ + struct bio *tmp; + os_aux_t *tmp_aux; + + tmp = stage->bio; tmp_aux = stage->aux; + stage->bio = tape->merge_stage->bio; + stage->aux = tape->merge_stage->aux; + tape->merge_stage->bio = tmp; + tape->merge_stage->aux = tmp_aux; + idetape_init_merge_stage(tape); +} + +/* + * idetape_add_stage_tail adds a new stage at the end of the pipeline. + */ +static void idetape_add_stage_tail (ide_drive_t *drive,idetape_stage_t *stage) +{ + idetape_tape_t *tape = drive->driver_data; + unsigned long flags; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk (KERN_INFO "ide-tape: Reached idetape_add_stage_tail\n"); +#endif /* IDETAPE_DEBUG_LOG */ + spin_lock_irqsave(&tape->spinlock, flags); + stage->next=NULL; + if (tape->last_stage != NULL) + tape->last_stage->next=stage; + else + tape->first_stage = tape->next_stage=stage; + tape->last_stage = stage; + if (tape->next_stage == NULL) + tape->next_stage = tape->last_stage; + tape->nr_stages++; + tape->nr_pending_stages++; + spin_unlock_irqrestore(&tape->spinlock, flags); +} + +/* + * Initialize the OnStream AUX + */ +static void idetape_init_stage (ide_drive_t *drive, idetape_stage_t *stage, int frame_type, int logical_blk_num) +{ + idetape_tape_t *tape = drive->driver_data; + os_aux_t *aux = stage->aux; + os_partition_t *par = &aux->partition; + os_dat_t *dat = &aux->dat; + + if (!tape->onstream || tape->raw) + return; + memset(aux, 0, sizeof(*aux)); + aux->format_id = htonl(0); + memcpy(aux->application_sig, "LIN3", 4); + aux->hdwr = htonl(0); + aux->frame_type = frame_type; + + if (frame_type == OS_FRAME_TYPE_HEADER) { + aux->update_frame_cntr = htonl(tape->update_frame_cntr); + par->partition_num = OS_CONFIG_PARTITION; + par->par_desc_ver = OS_PARTITION_VERSION; + par->wrt_pass_cntr = htons(0xffff); + par->first_frame_addr = htonl(0); + par->last_frame_addr = htonl(0xbb7); /* 2999 */ + aux->frame_seq_num = htonl(0); + aux->logical_blk_num_high = htonl(0); + aux->logical_blk_num = htonl(0); + aux->next_mark_addr = htonl(tape->first_mark_addr); + } else { + aux->update_frame_cntr = htonl(0); + par->partition_num = OS_DATA_PARTITION; + par->par_desc_ver = OS_PARTITION_VERSION; + par->wrt_pass_cntr = htons(tape->wrt_pass_cntr); + par->first_frame_addr = htonl(OS_DATA_STARTFRAME1); + par->last_frame_addr = htonl(tape->capacity); + aux->frame_seq_num = htonl(logical_blk_num); + aux->logical_blk_num_high = htonl(0); + aux->logical_blk_num = htonl(logical_blk_num); + dat->dat_sz = 8; + dat->reserved1 = 0; + dat->entry_cnt = 1; + dat->reserved3 = 0; + if (frame_type == OS_FRAME_TYPE_DATA) + dat->dat_list[0].blk_sz = htonl(32 * 1024); + else + dat->dat_list[0].blk_sz = 0; + dat->dat_list[0].blk_cnt = htons(1); + if (frame_type == OS_FRAME_TYPE_MARKER) + dat->dat_list[0].flags = OS_DAT_FLAGS_MARK; + else + dat->dat_list[0].flags = OS_DAT_FLAGS_DATA; + dat->dat_list[0].reserved = 0; + } + aux->filemark_cnt = ntohl(tape->filemark_cnt); /* shouldn't this be htonl ?? */ + aux->phys_fm = ntohl(0xffffffff); /* shouldn't this be htonl ?? */ + aux->last_mark_addr = ntohl(tape->last_mark_addr); /* shouldn't this be htonl ?? */ +} + +/* + * idetape_wait_for_request installs a completion in a pending request + * and sleeps until it is serviced. + * + * The caller should ensure that the request will not be serviced + * before we install the completion (usually by disabling interrupts). + */ +static void idetape_wait_for_request (ide_drive_t *drive, struct request *rq) +{ + DECLARE_COMPLETION(wait); + idetape_tape_t *tape = drive->driver_data; + +#if IDETAPE_DEBUG_BUGS + if (rq == NULL || !IDETAPE_RQ_CMD (rq->flags)) { + printk (KERN_ERR "ide-tape: bug: Trying to sleep on non-valid request\n"); + return; + } +#endif /* IDETAPE_DEBUG_BUGS */ + rq->waiting = &wait; + tape->waiting = &wait; + spin_unlock(&tape->spinlock); + wait_for_completion(&wait); + rq->waiting = NULL; + tape->waiting = NULL; + spin_lock_irq(&tape->spinlock); +} + +static ide_startstop_t idetape_read_position_callback (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_read_position_result_t *result; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: Reached idetape_read_position_callback\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + if (!tape->pc->error) { + result = (idetape_read_position_result_t *) tape->pc->buffer; +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: BOP - %s\n",result->bop ? "Yes":"No"); + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: EOP - %s\n",result->eop ? "Yes":"No"); +#endif /* IDETAPE_DEBUG_LOG */ + if (result->bpu) { + printk(KERN_INFO "ide-tape: Block location is unknown to the tape\n"); + clear_bit(IDETAPE_ADDRESS_VALID, &tape->flags); + idetape_end_request(drive, 0); + } else { +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: Block Location - %u\n", ntohl(result->first_block)); +#endif /* IDETAPE_DEBUG_LOG */ + tape->partition = result->partition; + tape->first_frame_position = ntohl(result->first_block); + tape->last_frame_position = ntohl(result->last_block); + tape->blocks_in_buffer = result->blocks_in_buffer[2]; + set_bit(IDETAPE_ADDRESS_VALID, &tape->flags); + idetape_end_request(drive, 1); + } + } else { + idetape_end_request(drive, 0); + } + return ide_stopped; +} + +/* + * idetape_create_write_filemark_cmd will: + * + * 1. Write a filemark if write_filemark=1. + * 2. Flush the device buffers without writing a filemark + * if write_filemark=0. + * + */ +static void idetape_create_write_filemark_cmd (ide_drive_t *drive, idetape_pc_t *pc,int write_filemark) +{ + idetape_tape_t *tape = drive->driver_data; + + idetape_init_pc(pc); + pc->c[0] = IDETAPE_WRITE_FILEMARK_CMD; + if (tape->onstream) + pc->c[1] = 1; /* Immed bit */ + pc->c[4] = write_filemark; /* not used for OnStream ?? */ + set_bit(PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +static void idetape_create_test_unit_ready_cmd(idetape_pc_t *pc) +{ + idetape_init_pc(pc); + pc->c[0] = IDETAPE_TEST_UNIT_READY_CMD; + pc->callback = &idetape_pc_callback; +} + +/* + * idetape_queue_pc_tail is based on the following functions: + * + * ide_do_drive_cmd from ide.c + * cdrom_queue_request and cdrom_queue_packet_command from ide-cd.c + * + * We add a special packet command request to the tail of the request queue, + * and wait for it to be serviced. + * + * This is not to be called from within the request handling part + * of the driver ! We allocate here data in the stack, and it is valid + * until the request is finished. This is not the case for the bottom + * part of the driver, where we are always leaving the functions to wait + * for an interrupt or a timer event. + * + * From the bottom part of the driver, we should allocate safe memory + * using idetape_next_pc_storage and idetape_next_rq_storage, and add + * the request to the request list without waiting for it to be serviced ! + * In that case, we usually use idetape_queue_pc_head. + */ +static int __idetape_queue_pc_tail (ide_drive_t *drive, idetape_pc_t *pc) +{ + struct request rq; + + ide_init_drive_cmd(&rq); + rq.buffer = (char *) pc; + rq.flags = IDETAPE_PC_RQ1; + return ide_do_drive_cmd(drive, &rq, ide_wait); +} + +static void idetape_create_load_unload_cmd (ide_drive_t *drive, idetape_pc_t *pc,int cmd) +{ + idetape_tape_t *tape = drive->driver_data; + + idetape_init_pc(pc); + pc->c[0] = IDETAPE_LOAD_UNLOAD_CMD; + pc->c[4] = cmd; + if (tape->onstream) { + pc->c[1] = 1; + if (cmd == !IDETAPE_LU_LOAD_MASK) + pc->c[4] = 4; + } + set_bit(PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +static int idetape_wait_ready (ide_drive_t *drive, unsigned long long timeout) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + + /* + * Wait for the tape to become ready + */ + timeout += jiffies; + while (time_before(jiffies, timeout)) { + idetape_create_test_unit_ready_cmd(&pc); + if (!__idetape_queue_pc_tail(drive, &pc)) + return 0; + if (tape->sense_key == 2 && tape->asc == 4 && tape->ascq == 2) { + idetape_create_load_unload_cmd(drive, &pc, IDETAPE_LU_LOAD_MASK); + __idetape_queue_pc_tail(drive, &pc); + idetape_create_test_unit_ready_cmd(&pc); + if (!__idetape_queue_pc_tail(drive, &pc)) + return 0; + } + if (!(tape->sense_key == 2 && tape->asc == 4 && + (tape->ascq == 1 || tape->ascq == 8))) + break; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(HZ / 10); + } + return -EIO; +} + +static int idetape_queue_pc_tail (ide_drive_t *drive,idetape_pc_t *pc) +{ + idetape_tape_t *tape = drive->driver_data; + int rc; + + rc = __idetape_queue_pc_tail(drive, pc); + if (rc) + return rc; + if (tape->onstream && test_bit(PC_WAIT_FOR_DSC, &pc->flags)) { + /* AJN-4: Changed from 5 to 10 minutes; + * because retension takes approx. + * 8:20 with Onstream 30GB tape + */ + rc = idetape_wait_ready(drive, 60 * 10 * HZ); + } + return rc; +} + +static int idetape_flush_tape_buffers (ide_drive_t *drive) +{ + idetape_pc_t pc; + int rc; + + idetape_create_write_filemark_cmd(drive, &pc, 0); + if ((rc = idetape_queue_pc_tail(drive, &pc))) + return rc; + idetape_wait_ready(drive, 60 * 5 * HZ); + return 0; +} + +static void idetape_create_read_position_cmd (idetape_pc_t *pc) +{ + idetape_init_pc(pc); + pc->c[0] = IDETAPE_READ_POSITION_CMD; + pc->request_transfer = 20; + pc->callback = &idetape_read_position_callback; +} + +static int idetape_read_position (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + int position; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: Reached idetape_read_position\n"); +#endif /* IDETAPE_DEBUG_LOG */ + +#ifdef NO_LONGER_REQUIRED + idetape_flush_tape_buffers(drive); +#endif + idetape_create_read_position_cmd(&pc); + if (idetape_queue_pc_tail(drive, &pc)) + return -1; + position = tape->first_frame_position; +#ifdef NO_LONGER_REQUIRED + if (tape->onstream) { + if ((position != tape->last_frame_position - tape->blocks_in_buffer) && + (position != tape->last_frame_position + tape->blocks_in_buffer)) { + if (tape->blocks_in_buffer == 0) { + printk("ide-tape: %s: correcting read position %d, %d, %d\n", tape->name, position, tape->last_frame_position, tape->blocks_in_buffer); + position = tape->last_frame_position; + tape->first_frame_position = position; + } + } + } +#endif + return position; +} + +static void idetape_create_locate_cmd (ide_drive_t *drive, idetape_pc_t *pc, unsigned int block, byte partition, int skip) +{ + idetape_tape_t *tape = drive->driver_data; + + idetape_init_pc(pc); + pc->c[0] = IDETAPE_LOCATE_CMD; + if (tape->onstream) + pc->c[1] = 1; /* Immediate bit */ + else + pc->c[1] = 2; + put_unaligned(htonl(block), (unsigned int *) &pc->c[3]); + pc->c[8] = partition; + if (tape->onstream) + /* + * Set SKIP bit. + * In case of write error this will write buffered + * data in the drive to this new position! + */ + pc->c[9] = skip << 7; + set_bit(PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +static int idetape_create_prevent_cmd (ide_drive_t *drive, idetape_pc_t *pc, int prevent) +{ + idetape_tape_t *tape = drive->driver_data; + + if (!tape->capabilities.lock) + return 0; + + idetape_init_pc(pc); + pc->c[0] = IDETAPE_PREVENT_CMD; + pc->c[4] = prevent; + pc->callback = &idetape_pc_callback; + return 1; +} + +static int __idetape_discard_read_pipeline (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + unsigned long flags; + int cnt; + + if (tape->chrdev_direction != idetape_direction_read) + return 0; + tape->merge_stage_size = 0; + if (tape->merge_stage != NULL) { + __idetape_kfree_stage(tape->merge_stage); + tape->merge_stage = NULL; + } + tape->chrdev_direction = idetape_direction_none; + + if (tape->first_stage == NULL) + return 0; + + spin_lock_irqsave(&tape->spinlock, flags); + tape->next_stage = NULL; + if (idetape_pipeline_active(tape)) + idetape_wait_for_request(drive, tape->active_data_request); + spin_unlock_irqrestore(&tape->spinlock, flags); + + cnt = tape->nr_stages - tape->nr_pending_stages; + while (tape->first_stage != NULL) + idetape_remove_stage_head(drive); + tape->nr_pending_stages = 0; + tape->max_stages = tape->min_pipeline; + return cnt; +} + +/* + * idetape_position_tape positions the tape to the requested block + * using the LOCATE packet command. A READ POSITION command is then + * issued to check where we are positioned. + * + * Like all higher level operations, we queue the commands at the tail + * of the request queue and wait for their completion. + * + */ +static int idetape_position_tape (ide_drive_t *drive, unsigned int block, byte partition, int skip) +{ + idetape_tape_t *tape = drive->driver_data; + int retval; + idetape_pc_t pc; + + if (tape->chrdev_direction == idetape_direction_read) + __idetape_discard_read_pipeline(drive); + idetape_wait_ready(drive, 60 * 5 * HZ); + idetape_create_locate_cmd(drive, &pc, block, partition, skip); + retval = idetape_queue_pc_tail(drive, &pc); + if (retval) + return (retval); + + idetape_create_read_position_cmd(&pc); + return (idetape_queue_pc_tail(drive, &pc)); +} + +static void idetape_discard_read_pipeline (ide_drive_t *drive, int restore_position) +{ + idetape_tape_t *tape = drive->driver_data; + int cnt; + int seek, position; + + cnt = __idetape_discard_read_pipeline(drive); + if (restore_position) { + position = idetape_read_position(drive); +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: address %u, nr_stages %d\n", position, cnt); +#endif + seek = position > cnt ? position - cnt : 0; + if (idetape_position_tape(drive, seek, 0, 0)) { + printk(KERN_INFO "ide-tape: %s: position_tape failed in discard_pipeline()\n", tape->name); + return; + } + } +} + +static void idetape_update_stats (ide_drive_t *drive) +{ + idetape_pc_t pc; + + idetape_create_mode_sense_cmd(&pc, IDETAPE_BUFFER_FILLING_PAGE); + pc.callback = idetape_onstream_buffer_fill_callback; + (void) idetape_queue_pc_tail(drive, &pc); +} + +/* + * idetape_queue_rw_tail generates a read/write request for the block + * device interface and wait for it to be serviced. + */ +static int idetape_queue_rw_tail (ide_drive_t *drive, int cmd, int blocks, struct bio *bio) +{ + idetape_tape_t *tape = drive->driver_data; + struct request rq; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: idetape_queue_rw_tail: cmd=%d\n",cmd); +#endif /* IDETAPE_DEBUG_LOG */ +#if IDETAPE_DEBUG_BUGS + if (idetape_pipeline_active(tape)) { + printk(KERN_ERR "ide-tape: bug: the pipeline is active in idetape_queue_rw_tail\n"); + return (0); + } +#endif /* IDETAPE_DEBUG_BUGS */ + + ide_init_drive_cmd(&rq); + rq.bio = bio; + rq.flags = cmd; + rq.sector = tape->first_frame_position; + rq.nr_sectors = rq.current_nr_sectors = blocks; + if (tape->onstream) + tape->postpone_cnt = 600; + (void) ide_do_drive_cmd(drive, &rq, ide_wait); + + if (cmd != IDETAPE_READ_RQ && cmd != IDETAPE_WRITE_RQ) + return 0; + + if (tape->merge_stage) + idetape_init_merge_stage(tape); + if (rq.errors == IDETAPE_ERROR_GENERAL) + return -EIO; + return (tape->tape_block_size * (blocks-rq.current_nr_sectors)); +} + +/* + * Read back the drive's internal buffer contents, as a part + * of the write error recovery mechanism for old OnStream + * firmware revisions. + */ +static void idetape_onstream_read_back_buffer (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + int frames, i, logical_blk_num; + idetape_stage_t *stage, *first = NULL, *last = NULL; + os_aux_t *aux; + struct request *rq; + unsigned char *p; + unsigned long flags; + + idetape_update_stats(drive); + frames = tape->cur_frames; + logical_blk_num = ntohl(tape->first_stage->aux->logical_blk_num) - frames; + printk(KERN_INFO "ide-tape: %s: reading back %d frames from the drive's internal buffer\n", tape->name, frames); + for (i = 0; i < frames; i++) { + stage = __idetape_kmalloc_stage(tape, 0, 0); + if (!first) + first = stage; + aux = stage->aux; + p = bio_data(stage->bio); + idetape_queue_rw_tail(drive, IDETAPE_READ_BUFFER_RQ, tape->capabilities.ctl, stage->bio); +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: read back logical block %d, data %x %x %x %x\n", tape->name, logical_blk_num, *p++, *p++, *p++, *p++); +#endif + rq = &stage->rq; + ide_init_drive_cmd(rq); + rq->flags = IDETAPE_WRITE_RQ; + rq->sector = tape->first_frame_position; + rq->nr_sectors = rq->current_nr_sectors = tape->capabilities.ctl; + idetape_init_stage(drive, stage, OS_FRAME_TYPE_DATA, logical_blk_num++); + stage->next = NULL; + if (last) + last->next = stage; + last = stage; + } + if (frames) { + spin_lock_irqsave(&tape->spinlock, flags); + last->next = tape->first_stage; + tape->next_stage = tape->first_stage = first; + tape->nr_stages += frames; + tape->nr_pending_stages += frames; + spin_unlock_irqrestore(&tape->spinlock, flags); + } + idetape_update_stats(drive); +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: frames left in buffer: %d\n", tape->name, tape->cur_frames); +#endif +} + +/* + * Error recovery algorithm for the OnStream tape. + */ +static void idetape_onstream_write_error_recovery (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + unsigned int block; + + if (tape->onstream_write_error == OS_WRITE_ERROR) { + printk(KERN_ERR "ide-tape: %s: onstream_write_error_recovery: detected physical bad block at %u, logical %u first frame %u last_frame %u bufblocks %u stages %u skipping %u frames\n", + tape->name, ntohl(tape->sense.information), tape->logical_blk_num, + tape->first_frame_position, tape->last_frame_position, + tape->blocks_in_buffer, tape->nr_stages, + (ntohl(tape->sense.command_specific) >> 16) & 0xff ); + block = ntohl(tape->sense.information) + ((ntohl(tape->sense.command_specific) >> 16) & 0xff); + idetape_update_stats(drive); + printk(KERN_ERR "ide-tape: %s: relocating %d buffered logical blocks to physical block %u\n", tape->name, tape->cur_frames, block); +#if 0 /* isn't once enough ??? MM */ + idetape_update_stats(drive); +#endif + if (tape->firmware_revision_num >= 106) + idetape_position_tape(drive, block, 0, 1); + else { + idetape_onstream_read_back_buffer(drive); + idetape_position_tape(drive, block, 0, 0); + } +#if 0 /* already done in idetape_position_tape MM */ + idetape_read_position(drive); +#endif +#if ONSTREAM_DEBUG + if (tape->debug_level >= 1) + printk(KERN_ERR "ide-tape: %s: positioning complete, cur_frames %d, pos %d, tape pos %d\n", tape->name, tape->cur_frames, tape->first_frame_position, tape->last_frame_position); +#endif + } else if (tape->onstream_write_error == OS_PART_ERROR) { +#if ONSTREAM_DEBUG + if (tape->debug_level >= 1) + printk(KERN_INFO "ide-tape: %s: skipping over config partition\n", tape->name); +#endif + idetape_flush_tape_buffers(drive); + block = idetape_read_position(drive); + if (block != OS_DATA_ENDFRAME1) + printk(KERN_ERR "ide-tape: warning, current position %d, expected %d\n", block, OS_DATA_ENDFRAME1); + idetape_position_tape(drive, 0xbb8, 0, 0); /* 3000 */ + } + tape->onstream_write_error = 0; +} + +/* + * idetape_insert_pipeline_into_queue is used to start servicing the + * pipeline stages, starting from tape->next_stage. + */ +static void idetape_insert_pipeline_into_queue (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + if (tape->next_stage == NULL) + return; + if (!idetape_pipeline_active(tape)) { + if (tape->onstream_write_error) + idetape_onstream_write_error_recovery(drive); + set_bit(IDETAPE_PIPELINE_ACTIVE, &tape->flags); + idetape_active_next_stage(drive); + (void) ide_do_drive_cmd(drive, tape->active_data_request, ide_end); + } +} + +static void idetape_create_inquiry_cmd (idetape_pc_t *pc) +{ + idetape_init_pc(pc); + pc->c[0] = IDETAPE_INQUIRY_CMD; + pc->c[4] = pc->request_transfer = 254; + pc->callback = &idetape_pc_callback; +} + +static void idetape_create_rewind_cmd (ide_drive_t *drive, idetape_pc_t *pc) +{ + idetape_tape_t *tape = drive->driver_data; + + idetape_init_pc (pc); + pc->c[0] = IDETAPE_REWIND_CMD; + if (tape->onstream) + pc->c[1] = 1; + set_bit(PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +static void idetape_create_mode_select_cmd (idetape_pc_t *pc, int length) +{ + idetape_init_pc(pc); + set_bit(PC_WRITING, &pc->flags); + pc->c[0] = IDETAPE_MODE_SELECT_CMD; + pc->c[1] = 0x10; + put_unaligned(htons(length), (unsigned short *) &pc->c[3]); + pc->request_transfer = 255; + pc->callback = &idetape_pc_callback; +} + +static void idetape_create_erase_cmd (idetape_pc_t *pc) +{ + idetape_init_pc(pc); + pc->c[0] = IDETAPE_ERASE_CMD; + pc->c[1] = 1; + set_bit(PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +static void idetape_create_space_cmd (idetape_pc_t *pc,int count,byte cmd) +{ + idetape_init_pc(pc); + pc->c[0] = IDETAPE_SPACE_CMD; + put_unaligned(htonl (count), (unsigned int *) &pc->c[1]); + pc->c[1] = cmd; + set_bit(PC_WAIT_FOR_DSC, &pc->flags); + pc->callback = &idetape_pc_callback; +} + +/* + * Verify that we have the correct tape frame + */ +static int idetape_verify_stage (ide_drive_t *drive, idetape_stage_t *stage, int logical_blk_num, int quiet) +{ + idetape_tape_t *tape = drive->driver_data; + os_aux_t *aux = stage->aux; + os_partition_t *par = &aux->partition; + struct request *rq = &stage->rq; + struct bio *bio; + + if (!tape->onstream) + return 1; + if (tape->raw) { + if (rq->errors) { + bio = stage->bio; + while (bio) { + memset(bio_data(bio), 0, bio->bi_size); + bio = bio->bi_next; + } + strcpy(bio_data(stage->bio), "READ ERROR ON FRAME"); + } + return 1; + } + if (rq->errors == IDETAPE_ERROR_GENERAL) { + printk(KERN_INFO "ide-tape: %s: skipping frame %d, read error\n", tape->name, tape->first_frame_position); + return 0; + } + if (rq->errors == IDETAPE_ERROR_EOD) { + printk(KERN_INFO "ide-tape: %s: skipping frame %d, eod\n", tape->name, tape->first_frame_position); + return 0; + } + if (ntohl(aux->format_id) != 0) { + printk(KERN_INFO "ide-tape: %s: skipping frame %d, format_id %u\n", tape->name, tape->first_frame_position, ntohl(aux->format_id)); + return 0; + } + if (memcmp(aux->application_sig, tape->application_sig, 4) != 0) { + printk(KERN_INFO "ide-tape: %s: skipping frame %d, incorrect application signature\n", tape->name, tape->first_frame_position); + return 0; + } + if (aux->frame_type != OS_FRAME_TYPE_DATA && + aux->frame_type != OS_FRAME_TYPE_EOD && + aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "ide-tape: %s: skipping frame %d, frame type %x\n", tape->name, tape->first_frame_position, aux->frame_type); + return 0; + } + if (par->partition_num != OS_DATA_PARTITION) { + if (!tape->linux_media || tape->linux_media_version != 2) { + printk(KERN_INFO "ide-tape: %s: skipping frame %d, partition num %d\n", tape->name, tape->first_frame_position, par->partition_num); + return 0; + } + } + if (par->par_desc_ver != OS_PARTITION_VERSION) { + printk(KERN_INFO "ide-tape: %s: skipping frame %d, partition version %d\n", tape->name, tape->first_frame_position, par->par_desc_ver); + return 0; + } + if (ntohs(par->wrt_pass_cntr) != tape->wrt_pass_cntr) { + printk(KERN_INFO "ide-tape: %s: skipping frame %d, wrt_pass_cntr %d (expected %d)(logical_blk_num %u)\n", tape->name, tape->first_frame_position, ntohs(par->wrt_pass_cntr), tape->wrt_pass_cntr, ntohl(aux->logical_blk_num)); + return 0; + } + if (aux->frame_seq_num != aux->logical_blk_num) { + printk(KERN_INFO "ide-tape: %s: skipping frame %d, seq != logical\n", tape->name, tape->first_frame_position); + return 0; + } + if (logical_blk_num != -1 && ntohl(aux->logical_blk_num) != logical_blk_num) { + if (!quiet) + printk(KERN_INFO "ide-tape: %s: skipping frame %d, logical_blk_num %u (expected %d)\n", tape->name, tape->first_frame_position, ntohl(aux->logical_blk_num), logical_blk_num); + return 0; + } + if (aux->frame_type == OS_FRAME_TYPE_MARKER) { + rq->errors = IDETAPE_ERROR_FILEMARK; + rq->current_nr_sectors = rq->nr_sectors; + } + return 1; +} + +static void idetape_wait_first_stage (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + unsigned long flags; + + if (tape->first_stage == NULL) + return; + spin_lock_irqsave(&tape->spinlock, flags); + if (tape->active_stage == tape->first_stage) + idetape_wait_for_request(drive, tape->active_data_request); + spin_unlock_irqrestore(&tape->spinlock, flags); +} + +/* + * idetape_add_chrdev_write_request tries to add a character device + * originated write request to our pipeline. In case we don't succeed, + * we revert to non-pipelined operation mode for this request. + * + * 1. Try to allocate a new pipeline stage. + * 2. If we can't, wait for more and more requests to be serviced + * and try again each time. + * 3. If we still can't allocate a stage, fallback to + * non-pipelined operation mode for this request. + */ +static int idetape_add_chrdev_write_request (ide_drive_t *drive, int blocks) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *new_stage; + unsigned long flags; + struct request *rq; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 3) + printk(KERN_INFO "ide-tape: Reached idetape_add_chrdev_write_request\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + /* + * Attempt to allocate a new stage. + * Pay special attention to possible race conditions. + */ + while ((new_stage = idetape_kmalloc_stage(tape)) == NULL) { + spin_lock_irqsave(&tape->spinlock, flags); + if (idetape_pipeline_active(tape)) { + idetape_wait_for_request(drive, tape->active_data_request); + spin_unlock_irqrestore(&tape->spinlock, flags); + } else { + spin_unlock_irqrestore(&tape->spinlock, flags); + idetape_insert_pipeline_into_queue(drive); + if (idetape_pipeline_active(tape)) + continue; + /* + * Linux is short on memory. Fallback to + * non-pipelined operation mode for this request. + */ + return idetape_queue_rw_tail(drive, IDETAPE_WRITE_RQ, blocks, tape->merge_stage->bio); + } + } + rq = &new_stage->rq; + ide_init_drive_cmd(rq); + rq->flags = IDETAPE_WRITE_RQ; + rq->sector = tape->first_frame_position; /* Doesn't actually matter - We always assume sequential access */ + rq->nr_sectors = rq->current_nr_sectors = blocks; + + idetape_switch_buffers(tape, new_stage); + idetape_init_stage(drive, new_stage, OS_FRAME_TYPE_DATA, tape->logical_blk_num); + tape->logical_blk_num++; + idetape_add_stage_tail(drive, new_stage); + tape->pipeline_head++; +#if USE_IOTRACE + IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor); +#endif + calculate_speeds(drive); + + /* + * Estimate whether the tape has stopped writing by checking + * if our write pipeline is currently empty. If we are not + * writing anymore, wait for the pipeline to be full enough + * (90%) before starting to service requests, so that we will + * be able to keep up with the higher speeds of the tape. + * + * For the OnStream drive, we can query the number of pending + * frames in the drive's internal buffer. As long as the tape + * is still writing, it is better to write frames immediately + * rather than gather them in the pipeline. This will give the + * tape's firmware the ability to sense the current incoming + * data rate more accurately, and since the OnStream tape + * supports variable speeds, it can try to adjust itself to the + * incoming data rate. + */ + if (!idetape_pipeline_active(tape)) { + if (tape->nr_stages >= tape->max_stages * 9 / 10 || + tape->nr_stages >= tape->max_stages - tape->uncontrolled_pipeline_head_speed * 3 * 1024 / tape->tape_block_size) { + tape->measure_insert_time = 1; + tape->insert_time = jiffies; + tape->insert_size = 0; + tape->insert_speed = 0; + idetape_insert_pipeline_into_queue(drive); + } else if (tape->onstream) { + idetape_update_stats(drive); + if (tape->cur_frames > 5) + idetape_insert_pipeline_into_queue(drive); + } + } + if (test_and_clear_bit(IDETAPE_PIPELINE_ERROR, &tape->flags)) /* Return a deferred error */ + return -EIO; + return blocks; +} + +/* + * idetape_wait_for_pipeline will wait until all pending pipeline + * requests are serviced. Typically called on device close. + */ +static void idetape_wait_for_pipeline (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + unsigned long flags; + + while (tape->next_stage || idetape_pipeline_active(tape)) { + idetape_insert_pipeline_into_queue(drive); + spin_lock_irqsave(&tape->spinlock, flags); + if (idetape_pipeline_active(tape)) + idetape_wait_for_request(drive, tape->active_data_request); + spin_unlock_irqrestore(&tape->spinlock, flags); + } +} + +static void idetape_empty_write_pipeline (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + int blocks, min; + struct bio *bio; + +#if IDETAPE_DEBUG_BUGS + if (tape->chrdev_direction != idetape_direction_write) { + printk(KERN_ERR "ide-tape: bug: Trying to empty write pipeline, but we are not writing.\n"); + return; + } + if (tape->merge_stage_size > tape->stage_size) { + printk(KERN_ERR "ide-tape: bug: merge_buffer too big\n"); + tape->merge_stage_size = tape->stage_size; + } +#endif /* IDETAPE_DEBUG_BUGS */ + if (tape->merge_stage_size) { + blocks = tape->merge_stage_size / tape->tape_block_size; + if (tape->merge_stage_size % tape->tape_block_size) { + unsigned int i; + + blocks++; + i = tape->tape_block_size - tape->merge_stage_size % tape->tape_block_size; + bio = tape->bio->bi_next; + while (bio) { + atomic_set(&bio->bi_cnt, 0); + bio = bio->bi_next; + } + bio = tape->bio; + while (i) { + if (bio == NULL) { + printk(KERN_INFO "ide-tape: bug, bio NULL\n"); + break; + } + min = min(i, bio->bi_size - atomic_read(&bio->bi_cnt)); + memset(bio_data(bio) + bio->bi_size, 0, min); + atomic_add(min, &bio->bi_cnt); + i -= min; + bio = bio->bi_next; + } + } + (void) idetape_add_chrdev_write_request(drive, blocks); + tape->merge_stage_size = 0; + } + idetape_wait_for_pipeline(drive); + if (tape->merge_stage != NULL) { + __idetape_kfree_stage(tape->merge_stage); + tape->merge_stage = NULL; + } + clear_bit(IDETAPE_PIPELINE_ERROR, &tape->flags); + tape->chrdev_direction = idetape_direction_none; + + /* + * On the next backup, perform the feedback loop again. + * (I don't want to keep sense information between backups, + * as some systems are constantly on, and the system load + * can be totally different on the next backup). + */ + tape->max_stages = tape->min_pipeline; +#if IDETAPE_DEBUG_BUGS + if (tape->first_stage != NULL || + tape->next_stage != NULL || + tape->last_stage != NULL || + tape->nr_stages != 0) { + printk(KERN_ERR "ide-tape: ide-tape pipeline bug, " + "first_stage %p, next_stage %p, " + "last_stage %p, nr_stages %d\n", + tape->first_stage, tape->next_stage, + tape->last_stage, tape->nr_stages); + } +#endif /* IDETAPE_DEBUG_BUGS */ +} + +static void idetape_restart_speed_control (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + tape->restart_speed_control_req = 0; + tape->pipeline_head = 0; + tape->buffer_head = tape->tape_head = tape->cur_frames; + tape->controlled_last_pipeline_head = tape->uncontrolled_last_pipeline_head = 0; + tape->controlled_previous_pipeline_head = tape->uncontrolled_previous_pipeline_head = 0; + tape->pipeline_head_speed = tape->controlled_pipeline_head_speed = 5000; + tape->uncontrolled_pipeline_head_speed = 0; + tape->controlled_pipeline_head_time = tape->uncontrolled_pipeline_head_time = jiffies; + tape->controlled_previous_head_time = tape->uncontrolled_previous_head_time = jiffies; +} + +static int idetape_initiate_read (ide_drive_t *drive, int max_stages) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *new_stage; + struct request rq; + int bytes_read; + int blocks = tape->capabilities.ctl; + + if (tape->chrdev_direction != idetape_direction_read) { /* Initialize read operation */ + if (tape->chrdev_direction == idetape_direction_write) { + idetape_empty_write_pipeline (drive); + idetape_flush_tape_buffers (drive); + } +#if IDETAPE_DEBUG_BUGS + if (tape->merge_stage || tape->merge_stage_size) { + printk (KERN_ERR "ide-tape: merge_stage_size should be 0 now\n"); + tape->merge_stage_size = 0; + } +#endif /* IDETAPE_DEBUG_BUGS */ + if ((tape->merge_stage = __idetape_kmalloc_stage(tape, 0, 0)) == NULL) + return -ENOMEM; + tape->chrdev_direction = idetape_direction_read; + tape->logical_blk_num = 0; + + /* + * Issue a read 0 command to ensure that DSC handshake + * is switched from completion mode to buffer available + * mode. + */ + bytes_read = idetape_queue_rw_tail(drive, IDETAPE_READ_RQ, 0, tape->merge_stage->bio); + if (bytes_read < 0) { + kfree(tape->merge_stage); + tape->merge_stage = NULL; + tape->chrdev_direction = idetape_direction_none; + return bytes_read; + } + } + if (tape->restart_speed_control_req) + idetape_restart_speed_control(drive); + ide_init_drive_cmd(&rq); + rq.flags = IDETAPE_READ_RQ; + rq.sector = tape->first_frame_position; + rq.nr_sectors = rq.current_nr_sectors = blocks; + if (!test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags) && + tape->nr_stages <= max_stages) { + new_stage = idetape_kmalloc_stage(tape); + while (new_stage != NULL) { + new_stage->rq = rq; + idetape_add_stage_tail(drive, new_stage); + if (tape->nr_stages >= max_stages) + break; + new_stage = idetape_kmalloc_stage(tape); + } + } + if (!idetape_pipeline_active(tape)) { + if (tape->nr_pending_stages >= 3 * max_stages / 4) { + tape->measure_insert_time = 1; + tape->insert_time = jiffies; + tape->insert_size = 0; + tape->insert_speed = 0; + idetape_insert_pipeline_into_queue(drive); + } else if (tape->onstream) { + idetape_update_stats(drive); + if (tape->cur_frames < tape->max_frames - 5) + idetape_insert_pipeline_into_queue(drive); + } + } + return 0; +} + +static int idetape_get_logical_blk (ide_drive_t *drive, int logical_blk_num, int max_stages, int quiet) +{ + idetape_tape_t *tape = drive->driver_data; + unsigned long flags; + int cnt = 0, x, position; + + /* + * Search and wait for the next logical tape block + */ + while (1) { + if (cnt++ > 1000) { /* AJN: was 100 */ + printk(KERN_INFO "ide-tape: %s: couldn't find logical block %d, aborting\n", tape->name, logical_blk_num); + return 0; + } + idetape_initiate_read(drive, max_stages); + if (tape->first_stage == NULL) { + if (tape->onstream) { +#if ONSTREAM_DEBUG + if (tape->debug_level >= 1) + printk(KERN_INFO "ide-tape: %s: first_stage == NULL, pipeline error %ld\n", tape->name, (long)test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags)); +#endif + clear_bit(IDETAPE_PIPELINE_ERROR, &tape->flags); + position = idetape_read_position(drive); + printk(KERN_INFO "ide-tape: %s: blank block detected at %d\n", tape->name, position); + if (position >= 3000 && position < 3080) + position += 32; /* Why is this check and number ??? MM */ + if (position >= OS_DATA_ENDFRAME1 && position < 3000) + position = 3000; + else + /* + * compensate for write errors that generally skip 80 frames, + * expect around 20 read errors in a row... + */ + position += 60; + if (position >= OS_DATA_ENDFRAME1 && position < 3000) + position = 3000; + printk(KERN_INFO "ide-tape: %s: positioning tape to block %d\n", tape->name, position); + if (position == 3000) /* seems to be needed to correctly position at block 3000 MM */ + idetape_position_tape(drive, 0, 0, 0); + idetape_position_tape(drive, position, 0, 0); + cnt += 40; + continue; + } else + return 0; + } + idetape_wait_first_stage(drive); + if (idetape_verify_stage(drive, tape->first_stage, logical_blk_num, quiet)) + break; + if (tape->first_stage->rq.errors == IDETAPE_ERROR_EOD) + cnt--; + if (idetape_verify_stage(drive, tape->first_stage, -1, quiet)) { + x = ntohl(tape->first_stage->aux->logical_blk_num); + if (x > logical_blk_num) { + printk(KERN_ERR "ide-tape: %s: couldn't find logical block %d, aborting (block %d found)\n", tape->name, logical_blk_num, x); + return 0; + } + } + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head(drive); + spin_unlock_irqrestore(&tape->spinlock, flags); + } + if (tape->onstream) + tape->logical_blk_num = ntohl(tape->first_stage->aux->logical_blk_num); + return 1; +} + +/* + * idetape_add_chrdev_read_request is called from idetape_chrdev_read + * to service a character device read request and add read-ahead + * requests to our pipeline. + */ +static int idetape_add_chrdev_read_request (ide_drive_t *drive,int blocks) +{ + idetape_tape_t *tape = drive->driver_data; + unsigned long flags; + struct request *rq_ptr; + int bytes_read; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: Reached idetape_add_chrdev_read_request, %d blocks\n", blocks); +#endif /* IDETAPE_DEBUG_LOG */ + + /* + * Wait for the next logical block to be available at the head + * of the pipeline + */ + if (!idetape_get_logical_blk(drive, tape->logical_blk_num, tape->max_stages, 0)) { + if (tape->onstream) { + set_bit(IDETAPE_READ_ERROR, &tape->flags); + return 0; + } + if (test_bit(IDETAPE_PIPELINE_ERROR, &tape->flags)) + return 0; + return idetape_queue_rw_tail(drive, IDETAPE_READ_RQ, blocks, tape->merge_stage->bio); + } + rq_ptr = &tape->first_stage->rq; + bytes_read = tape->tape_block_size * (rq_ptr->nr_sectors - rq_ptr->current_nr_sectors); + rq_ptr->nr_sectors = rq_ptr->current_nr_sectors = 0; + + + if (tape->onstream && !tape->raw && + tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) { +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: EOD reached\n", + tape->name); +#endif + return 0; + } + if (rq_ptr->errors == IDETAPE_ERROR_EOD) + return 0; + if (rq_ptr->errors == IDETAPE_ERROR_FILEMARK) { + idetape_switch_buffers(tape, tape->first_stage); + set_bit(IDETAPE_FILEMARK, &tape->flags); +#if USE_IOTRACE + IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor); +#endif + calculate_speeds(drive); + } else { + idetape_switch_buffers(tape, tape->first_stage); + if (rq_ptr->errors == IDETAPE_ERROR_GENERAL) { +#if ONSTREAM_DEBUG + if (tape->debug_level >= 1) + printk(KERN_INFO "ide-tape: error detected, bytes_read %d\n", bytes_read); +#endif + } + clear_bit(IDETAPE_FILEMARK, &tape->flags); + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head(drive); + spin_unlock_irqrestore(&tape->spinlock, flags); + tape->logical_blk_num++; + tape->pipeline_head++; +#if USE_IOTRACE + IO_trace(IO_IDETAPE_FIFO, tape->pipeline_head, tape->buffer_head, tape->tape_head, tape->minor); +#endif + calculate_speeds(drive); + } +#if IDETAPE_DEBUG_BUGS + if (bytes_read > blocks*tape->tape_block_size) { + printk(KERN_ERR "ide-tape: bug: trying to return more bytes than requested\n"); + bytes_read = blocks*tape->tape_block_size; + } +#endif /* IDETAPE_DEBUG_BUGS */ + return (bytes_read); +} + +static void idetape_pad_zeros (ide_drive_t *drive, int bcount) +{ + idetape_tape_t *tape = drive->driver_data; + struct bio *bio; + int blocks; + + while (bcount) { + unsigned int count; + + bio = tape->merge_stage->bio; + count = min(tape->stage_size, bcount); + bcount -= count; + blocks = count / tape->tape_block_size; + while (count) { + atomic_set(&bio->bi_cnt, min(count, bio->bi_size)); + memset(bio_data(bio), 0, bio->bi_size); + count -= atomic_read(&bio->bi_cnt); + bio = bio->bi_next; + } + idetape_queue_rw_tail(drive, IDETAPE_WRITE_RQ, blocks, tape->merge_stage->bio); + } +} + +static int idetape_pipeline_size (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage; + struct request *rq; + int size = 0; + + idetape_wait_for_pipeline(drive); + stage = tape->first_stage; + while (stage != NULL) { + rq = &stage->rq; + size += tape->tape_block_size * (rq->nr_sectors-rq->current_nr_sectors); + if (rq->errors == IDETAPE_ERROR_FILEMARK) + size += tape->tape_block_size; + stage = stage->next; + } + size += tape->merge_stage_size; + return size; +} + +/* + * Rewinds the tape to the Beginning Of the current Partition (BOP). + * + * We currently support only one partition. + */ +static int idetape_rewind_tape (ide_drive_t *drive) +{ + int retval; + idetape_pc_t pc; + idetape_tape_t *tape = drive->driver_data; +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: Reached idetape_rewind_tape\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + idetape_create_rewind_cmd(drive, &pc); + retval = idetape_queue_pc_tail(drive, &pc); + if (retval) + return retval; + + idetape_create_read_position_cmd(&pc); + retval = idetape_queue_pc_tail(drive, &pc); + if (retval) + return retval; + tape->logical_blk_num = 0; + return 0; +} + +/* + * Our special ide-tape ioctl's. + * + * Currently there aren't any ioctl's. + * mtio.h compatible commands should be issued to the character device + * interface. + */ +static int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_config_t config; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 4) + printk(KERN_INFO "ide-tape: Reached idetape_blkdev_ioctl\n"); +#endif /* IDETAPE_DEBUG_LOG */ + switch (cmd) { + case 0x0340: + if (copy_from_user ((char *) &config, (char *) arg, sizeof (idetape_config_t))) + return -EFAULT; + tape->best_dsc_rw_frequency = config.dsc_rw_frequency; + tape->max_stages = config.nr_stages; + break; + case 0x0350: + config.dsc_rw_frequency = (int) tape->best_dsc_rw_frequency; + config.nr_stages = tape->max_stages; + if (copy_to_user((char *) arg, (char *) &config, sizeof (idetape_config_t))) + return -EFAULT; + break; + default: + return -EIO; + } + return 0; +} + +/* + * The block device interface should not be used for data transfers. + * However, we still allow opening it so that we can issue general + * ide driver configuration ioctl's, such as the interrupt unmask feature. + */ +static int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive) +{ + MOD_INC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_blkdev_open\n"); +#endif + return 0; +} + +static void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive) +{ + MOD_DEC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_blkdev_release\n"); +#endif +} + +/* + * idetape_pre_reset is called before an ATAPI/ATA software reset. + */ +static void idetape_pre_reset (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + if (tape != NULL) + set_bit(IDETAPE_IGNORE_DSC, &tape->flags); +} + +/* + * Character device interface functions + */ +static ide_drive_t *get_drive_ptr (kdev_t i_rdev) +{ + unsigned int i = minor(i_rdev) & ~0xc0; + + if (i >= MAX_HWIFS * MAX_DRIVES) + return NULL; + return (idetape_chrdevs[i].drive); +} + +static int idetape_onstream_space_over_filemarks_backward (ide_drive_t *drive,short mt_op,int mt_count) +{ + idetape_tape_t *tape = drive->driver_data; + int cnt = 0; + int last_mark_addr; + unsigned long flags; + + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_bwd\n", tape->name); + return -EIO; + } + while (cnt != mt_count) { + last_mark_addr = ntohl(tape->first_stage->aux->last_mark_addr); + if (last_mark_addr == -1) + return -EIO; +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: positioning to last mark at %d\n", last_mark_addr); +#endif + idetape_position_tape(drive, last_mark_addr, 0, 0); + cnt++; + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name); + return -EIO; + } + if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "ide-tape: %s: expected to find marker at block %d, not found\n", tape->name, last_mark_addr); + return -EIO; + } + } + if (mt_op == MTBSFM) { + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head(drive); + tape->logical_blk_num++; + spin_unlock_irqrestore(&tape->spinlock, flags); + } + return 0; +} + +/* + * ADRL 1.1 compatible "slow" space filemarks fwd version + * + * Just scans for the filemark sequentially. + */ +static int idetape_onstream_space_over_filemarks_forward_slow (ide_drive_t *drive,short mt_op,int mt_count) +{ + idetape_tape_t *tape = drive->driver_data; + int cnt = 0; + unsigned long flags; + + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd\n", tape->name); + return -EIO; + } + while (1) { + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name); + return -EIO; + } + if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_MARKER) + cnt++; + if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) { +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: space_fwd: EOD reached\n", tape->name); +#endif + return -EIO; + } + if (cnt == mt_count) + break; + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head(drive); + spin_unlock_irqrestore(&tape->spinlock, flags); + } + if (mt_op == MTFSF) { + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head(drive); + tape->logical_blk_num++; + spin_unlock_irqrestore(&tape->spinlock, flags); + } + return 0; +} + + +/* + * Fast linux specific version of OnStream FSF + */ +static int idetape_onstream_space_over_filemarks_forward_fast (ide_drive_t *drive,short mt_op,int mt_count) +{ + idetape_tape_t *tape = drive->driver_data; + int cnt = 0, next_mark_addr; + unsigned long flags; + + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd\n", tape->name); + return -EIO; + } + + /* + * Find nearest (usually previous) marker + */ + while (1) { + if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_MARKER) + break; + if (tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) { +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: space_fwd: EOD reached\n", tape->name); +#endif + return -EIO; + } + if (ntohl(tape->first_stage->aux->filemark_cnt) == 0) { + if (tape->first_mark_addr == -1) { + printk(KERN_INFO "ide-tape: %s: reverting to slow filemark space\n", tape->name); + return idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count); + } + idetape_position_tape(drive, tape->first_mark_addr, 0, 0); + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks_fwd_fast\n", tape->name); + return -EIO; + } + if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "ide-tape: %s: expected to find filemark at %d\n", tape->name, tape->first_mark_addr); + return -EIO; + } + } else { + if (idetape_onstream_space_over_filemarks_backward(drive, MTBSF, 1) < 0) + return -EIO; + mt_count++; + } + } + cnt++; + while (cnt != mt_count) { + next_mark_addr = ntohl(tape->first_stage->aux->next_mark_addr); + if (!next_mark_addr || next_mark_addr > tape->eod_frame_addr) { + printk(KERN_INFO "ide-tape: %s: reverting to slow filemark space\n", tape->name); + return idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count - cnt); +#if ONSTREAM_DEBUG + } else if (tape->debug_level >= 2) { + printk(KERN_INFO "ide-tape: positioning to next mark at %d\n", next_mark_addr); +#endif + } + idetape_position_tape(drive, next_mark_addr, 0, 0); + cnt++; + if (!idetape_get_logical_blk(drive, -1, 10, 0)) { + printk(KERN_INFO "ide-tape: %s: couldn't get logical blk num in space_filemarks\n", tape->name); + return -EIO; + } + if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "ide-tape: %s: expected to find marker at block %d, not found\n", tape->name, next_mark_addr); + return -EIO; + } + } + if (mt_op == MTFSF) { + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head(drive); + tape->logical_blk_num++; + spin_unlock_irqrestore(&tape->spinlock, flags); + } + return 0; +} + +/* + * idetape_space_over_filemarks is now a bit more complicated than just + * passing the command to the tape since we may have crossed some + * filemarks during our pipelined read-ahead mode. + * + * As a minor side effect, the pipeline enables us to support MTFSFM when + * the filemark is in our internal pipeline even if the tape doesn't + * support spacing over filemarks in the reverse direction. + */ +static int idetape_space_over_filemarks (ide_drive_t *drive,short mt_op,int mt_count) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + unsigned long flags; + int retval,count=0; + int speed_control; + + if (tape->onstream) { + if (tape->raw) + return -EIO; + speed_control = tape->speed_control; + tape->speed_control = 0; + if (mt_op == MTFSF || mt_op == MTFSFM) { + if (tape->linux_media) + retval = idetape_onstream_space_over_filemarks_forward_fast(drive, mt_op, mt_count); + else + retval = idetape_onstream_space_over_filemarks_forward_slow(drive, mt_op, mt_count); + } else + retval = idetape_onstream_space_over_filemarks_backward(drive, mt_op, mt_count); + tape->speed_control = speed_control; + tape->restart_speed_control_req = 1; + return retval; + } + + if (tape->chrdev_direction == idetape_direction_read) { + /* + * We have a read-ahead buffer. Scan it for crossed + * filemarks. + */ + tape->merge_stage_size = 0; + clear_bit(IDETAPE_FILEMARK, &tape->flags); + while (tape->first_stage != NULL) { + idetape_wait_first_stage(drive); + if (tape->first_stage->rq.errors == IDETAPE_ERROR_FILEMARK) + count++; + if (count == mt_count) { + switch (mt_op) { + case MTFSF: + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head(drive); + spin_unlock_irqrestore(&tape->spinlock, flags); + case MTFSFM: + return (0); + default: + break; + } + } + spin_lock_irqsave(&tape->spinlock, flags); + idetape_remove_stage_head(drive); + spin_unlock_irqrestore(&tape->spinlock, flags); + } + idetape_discard_read_pipeline(drive, 1); + } + + /* + * The filemark was not found in our internal pipeline. + * Now we can issue the space command. + */ + switch (mt_op) { + case MTFSF: + idetape_create_space_cmd(&pc,mt_count-count,IDETAPE_SPACE_OVER_FILEMARK); + return (idetape_queue_pc_tail(drive, &pc)); + case MTFSFM: + if (!tape->capabilities.sprev) + return (-EIO); + retval = idetape_space_over_filemarks(drive, MTFSF, mt_count-count); + if (retval) return (retval); + return (idetape_space_over_filemarks(drive, MTBSF, 1)); + case MTBSF: + if (!tape->capabilities.sprev) + return (-EIO); + idetape_create_space_cmd(&pc,-(mt_count+count),IDETAPE_SPACE_OVER_FILEMARK); + return (idetape_queue_pc_tail(drive, &pc)); + case MTBSFM: + if (!tape->capabilities.sprev) + return (-EIO); + retval = idetape_space_over_filemarks(drive, MTBSF, mt_count+count); + if (retval) return (retval); + return (idetape_space_over_filemarks(drive, MTFSF, 1)); + default: + printk(KERN_ERR "ide-tape: MTIO operation %d not supported\n",mt_op); + return (-EIO); + } +} + + +/* + * Our character device read / write functions. + * + * The tape is optimized to maximize throughput when it is transferring + * an integral number of the "continuous transfer limit", which is + * a parameter of the specific tape (26 KB on my particular tape). + * (32 kB for Onstream) + * + * As of version 1.3 of the driver, the character device provides an + * abstract continuous view of the media - any mix of block sizes (even 1 + * byte) on the same backup/restore procedure is supported. The driver + * will internally convert the requests to the recommended transfer unit, + * so that an unmatch between the user's block size to the recommended + * size will only result in a (slightly) increased driver overhead, but + * will no longer hit performance. + * This is not applicable to Onstream. + */ +static ssize_t idetape_chrdev_read (struct file *file, char *buf, + size_t count, loff_t *ppos) +{ + struct inode *inode = file->f_dentry->d_inode; + ide_drive_t *drive = get_drive_ptr(inode->i_rdev); + idetape_tape_t *tape = drive->driver_data; + ssize_t bytes_read,temp, actually_read = 0, rc; + + if (ppos != &file->f_pos) { + /* "A request was outside the capabilities of the device." */ + return -ENXIO; + } + if (tape->onstream && (count != tape->tape_block_size)) { + printk(KERN_ERR "ide-tape: %s: use %d bytes as block size (%Zd used)\n", tape->name, tape->tape_block_size, count); + return -EINVAL; + } +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 3) + printk(KERN_INFO "ide-tape: Reached idetape_chrdev_read, count %Zd\n", count); +#endif /* IDETAPE_DEBUG_LOG */ + + if (tape->chrdev_direction != idetape_direction_read) { + if (test_bit(IDETAPE_DETECT_BS, &tape->flags)) + if (count > tape->tape_block_size && (count % tape->tape_block_size) == 0) + tape->user_bs_factor = count / tape->tape_block_size; + } + if ((rc = idetape_initiate_read(drive, tape->max_stages)) < 0) + return rc; + if (count == 0) + return (0); + if (tape->merge_stage_size) { + actually_read = min((unsigned long) (tape->merge_stage_size), (unsigned long) count); + idetape_copy_stage_to_user(tape, buf, tape->merge_stage, actually_read); + buf += actually_read; + tape->merge_stage_size -= actually_read; + count -= actually_read; + } + while (count >= tape->stage_size) { + bytes_read = idetape_add_chrdev_read_request(drive, tape->capabilities.ctl); + if (bytes_read <= 0) + goto finish; + idetape_copy_stage_to_user(tape, buf, tape->merge_stage, bytes_read); + buf += bytes_read; + count -= bytes_read; + actually_read += bytes_read; + } + if (count) { + bytes_read = idetape_add_chrdev_read_request(drive, tape->capabilities.ctl); + if (bytes_read <= 0) + goto finish; + temp = min((unsigned long) count, (unsigned long) bytes_read); + idetape_copy_stage_to_user(tape, buf, tape->merge_stage, temp); + actually_read += temp; + tape->merge_stage_size = bytes_read-temp; + } +finish: + if (!actually_read && test_bit(IDETAPE_FILEMARK, &tape->flags)) { +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: spacing over filemark\n", tape->name); +#endif + idetape_space_over_filemarks(drive, MTFSF, 1); + return 0; + } + if (tape->onstream && !actually_read && + test_and_clear_bit(IDETAPE_READ_ERROR, &tape->flags)) { + printk(KERN_ERR "ide-tape: %s: unrecovered read error on " + "logical block number %d, skipping\n", + tape->name, tape->logical_blk_num); + tape->logical_blk_num++; + return -EIO; + } + return actually_read; +} + +static void idetape_update_last_marker (ide_drive_t *drive, int last_mark_addr, int next_mark_addr) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage; + os_aux_t *aux; + int position; + + if (!tape->onstream || tape->raw) + return; + if (last_mark_addr == -1) + return; + stage = __idetape_kmalloc_stage(tape, 0, 0); + if (stage == NULL) + return; + idetape_flush_tape_buffers(drive); + position = idetape_read_position(drive); +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: current position (2) %d, " + "lblk %d\n", position, tape->logical_blk_num); + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: current position (2) " + "tape block %d\n", tape->last_frame_position); +#endif + idetape_position_tape(drive, last_mark_addr, 0, 0); + if (!idetape_queue_rw_tail(drive, IDETAPE_READ_RQ, 1, stage->bio)) { + printk(KERN_INFO "ide-tape: %s: couldn't read last marker\n", + tape->name); + __idetape_kfree_stage(stage); + idetape_position_tape(drive, position, 0, 0); + return; + } + aux = stage->aux; + if (aux->frame_type != OS_FRAME_TYPE_MARKER) { + printk(KERN_INFO "ide-tape: %s: expected to find marker " + "at addr %d\n", tape->name, last_mark_addr); + __idetape_kfree_stage(stage); + idetape_position_tape(drive, position, 0, 0); + return; + } +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: writing back marker\n"); +#endif + aux->next_mark_addr = htonl(next_mark_addr); + idetape_position_tape(drive, last_mark_addr, 0, 0); + if (!idetape_queue_rw_tail(drive, IDETAPE_WRITE_RQ, 1, stage->bio)) { + printk(KERN_INFO "ide-tape: %s: couldn't write back marker " + "frame at %d\n", tape->name, last_mark_addr); + __idetape_kfree_stage(stage); + idetape_position_tape(drive, position, 0, 0); + return; + } + __idetape_kfree_stage(stage); + idetape_flush_tape_buffers(drive); + idetape_position_tape(drive, position, 0, 0); + return; +} + +static void idetape_write_filler (ide_drive_t *drive, int block, int cnt) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage; + int rc; + + if (!tape->onstream || tape->raw) + return; + stage = __idetape_kmalloc_stage(tape, 1, 1); + if (stage == NULL) + return; + idetape_init_stage(drive, stage, OS_FRAME_TYPE_FILL, 0); + idetape_wait_ready(drive, 60 * 5 * HZ); + rc = idetape_position_tape(drive, block, 0, 0); +#if ONSTREAM_DEBUG + printk(KERN_INFO "write_filler: positioning failed it returned %d\n", rc); +#endif + if (rc != 0) + /* don't write fillers if we cannot position the tape. */ + return; + + strcpy(bio_data(stage->bio), "Filler"); + while (cnt--) { + if (!idetape_queue_rw_tail(drive, IDETAPE_WRITE_RQ, 1, stage->bio)) { + printk(KERN_INFO "ide-tape: %s: write_filler: " + "couldn't write header frame\n", tape->name); + __idetape_kfree_stage(stage); + return; + } + } + __idetape_kfree_stage(stage); +} + +static void __idetape_write_header (ide_drive_t *drive, int block, int cnt) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage; + os_header_t header; + + stage = __idetape_kmalloc_stage(tape, 1, 1); + if (stage == NULL) + return; + idetape_init_stage(drive, stage, OS_FRAME_TYPE_HEADER, tape->logical_blk_num); + idetape_wait_ready(drive, 60 * 5 * HZ); + idetape_position_tape(drive, block, 0, 0); + memset(&header, 0, sizeof(header)); + strcpy(header.ident_str, "ADR_SEQ"); + header.major_rev = 1; + header.minor_rev = OS_ADR_MINREV; + header.par_num = 1; + header.partition.partition_num = OS_DATA_PARTITION; + header.partition.par_desc_ver = OS_PARTITION_VERSION; + header.partition.first_frame_addr = htonl(OS_DATA_STARTFRAME1); + header.partition.last_frame_addr = htonl(tape->capacity); + header.partition.wrt_pass_cntr = htons(tape->wrt_pass_cntr); + header.partition.eod_frame_addr = htonl(tape->eod_frame_addr); + memcpy(bio_data(stage->bio), &header, sizeof(header)); + while (cnt--) { + if (!idetape_queue_rw_tail(drive, IDETAPE_WRITE_RQ, 1, stage->bio)) { + printk(KERN_INFO "ide-tape: %s: couldn't write " + "header frame\n", tape->name); + __idetape_kfree_stage(stage); + return; + } + } + __idetape_kfree_stage(stage); + idetape_flush_tape_buffers(drive); +} + +static void idetape_write_header (ide_drive_t *drive, int locate_eod) +{ + idetape_tape_t *tape = drive->driver_data; + +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: writing tape header\n", + tape->name); +#endif + if (!tape->onstream || tape->raw) + return; + tape->update_frame_cntr++; + __idetape_write_header(drive, 5, 5); + __idetape_write_header(drive, 0xbae, 5); /* 2990 */ + if (locate_eod) { +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: locating back to eod " + "frame addr %d\n", tape->name, + tape->eod_frame_addr); +#endif + idetape_position_tape(drive, tape->eod_frame_addr, 0, 0); + } +} + +static ssize_t idetape_chrdev_write (struct file *file, const char *buf, + size_t count, loff_t *ppos) +{ + struct inode *inode = file->f_dentry->d_inode; + ide_drive_t *drive = get_drive_ptr(inode->i_rdev); + idetape_tape_t *tape = drive->driver_data; + ssize_t retval, actually_written = 0; + int position; + + if (ppos != &file->f_pos) { + /* "A request was outside the capabilities of the device." */ + return -ENXIO; + } + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 3) + printk(KERN_INFO "ide-tape: Reached idetape_chrdev_write, " + "count %Zd\n", count); +#endif /* IDETAPE_DEBUG_LOG */ + + if (tape->onstream) { + if (count != tape->tape_block_size) { + printk(KERN_ERR "ide-tape: %s: chrdev_write: use %d " + "bytes as block size (%Zd used)\n", + tape->name, tape->tape_block_size, count); + return -EINVAL; + } + /* + * Check if we reach the end of the tape. Just assume the + * whole pipeline is filled with write requests! + */ + if (tape->first_frame_position + tape->nr_stages >= tape->capacity - OS_EW) { +#if ONSTREAM_DEBUG + printk(KERN_INFO, "chrdev_write: Write truncated at " + "EOM early warning"); +#endif + if (tape->chrdev_direction == idetape_direction_write) + idetape_write_release(inode); + return -ENOSPC; + } + } + + if (tape->chrdev_direction != idetape_direction_write) { + /* Initialize write operation */ + if (tape->chrdev_direction == idetape_direction_read) + idetape_discard_read_pipeline(drive, 1); +#if IDETAPE_DEBUG_BUGS + if (tape->merge_stage || tape->merge_stage_size) { + printk(KERN_ERR "ide-tape: merge_stage_size " + "should be 0 now\n"); + tape->merge_stage_size = 0; + } +#endif /* IDETAPE_DEBUG_BUGS */ + if ((tape->merge_stage = __idetape_kmalloc_stage(tape, 0, 0)) == NULL) + return -ENOMEM; + tape->chrdev_direction = idetape_direction_write; + idetape_init_merge_stage(tape); + + if (tape->onstream) { + position = idetape_read_position(drive); + if (position <= OS_DATA_STARTFRAME1) { + tape->logical_blk_num = 0; + tape->wrt_pass_cntr++; +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: logical block num 0, setting eod to %d\n", tape->name, OS_DATA_STARTFRAME1); + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: allocating new write pass counter %d\n", tape->name, tape->wrt_pass_cntr); +#endif + tape->filemark_cnt = 0; + tape->eod_frame_addr = OS_DATA_STARTFRAME1; + tape->first_mark_addr = tape->last_mark_addr = -1; + idetape_write_header(drive, 1); + } +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: positioning " + "tape to eod at %d\n", + tape->name, tape->eod_frame_addr); +#endif + position = idetape_read_position(drive); + if (position != tape->eod_frame_addr) + idetape_position_tape(drive, tape->eod_frame_addr, 0, 0); +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: " + "first_frame_position %d\n", + tape->name, tape->first_frame_position); +#endif + } + + /* + * Issue a write 0 command to ensure that DSC handshake + * is switched from completion mode to buffer available + * mode. + */ + retval = idetape_queue_rw_tail(drive, IDETAPE_WRITE_RQ, 0, tape->merge_stage->bio); + if (retval < 0) { + kfree(tape->merge_stage); + tape->merge_stage = NULL; + tape->chrdev_direction = idetape_direction_none; + return retval; + } +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk("ide-tape: first_frame_position %d\n", + tape->first_frame_position); +#endif + } + if (count == 0) + return (0); + if (tape->restart_speed_control_req) + idetape_restart_speed_control(drive); + if (tape->merge_stage_size) { +#if IDETAPE_DEBUG_BUGS + if (tape->merge_stage_size >= tape->stage_size) { + printk(KERN_ERR "ide-tape: bug: merge buffer too big\n"); + tape->merge_stage_size = 0; + } +#endif /* IDETAPE_DEBUG_BUGS */ + actually_written = min((unsigned long) (tape->stage_size - tape->merge_stage_size), (unsigned long) count); + idetape_copy_stage_from_user(tape, tape->merge_stage, buf, actually_written); + buf += actually_written; + tape->merge_stage_size += actually_written; + count -= actually_written; + + if (tape->merge_stage_size == tape->stage_size) { + tape->merge_stage_size = 0; + retval = idetape_add_chrdev_write_request(drive, tape->capabilities.ctl); + if (retval <= 0) + return(retval); + } + } + while (count >= tape->stage_size) { + idetape_copy_stage_from_user(tape, tape->merge_stage, buf, tape->stage_size); + buf += tape->stage_size; + count -= tape->stage_size; + retval = idetape_add_chrdev_write_request(drive, tape->capabilities.ctl); + actually_written += tape->stage_size; + if (retval <= 0) + return(retval); + } + if (count) { + actually_written += count; + idetape_copy_stage_from_user(tape, tape->merge_stage, buf, count); + tape->merge_stage_size += count; + } + return (actually_written); +} + +static int idetape_write_filemark (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + int last_mark_addr; + idetape_pc_t pc; + + if (!tape->onstream) { + idetape_create_write_filemark_cmd(drive, &pc, 1); /* Write a filemark */ + if (idetape_queue_pc_tail(drive, &pc)) { + printk(KERN_ERR "ide-tape: Couldn't write a filemark\n"); + return -EIO; + } + } else if (!tape->raw) { + last_mark_addr = idetape_read_position(drive); + tape->merge_stage = __idetape_kmalloc_stage(tape, 1, 0); + if (tape->merge_stage != NULL) { + idetape_init_stage(drive, tape->merge_stage, OS_FRAME_TYPE_MARKER, tape->logical_blk_num); + idetape_pad_zeros(drive, tape->stage_size); + tape->logical_blk_num++; + __idetape_kfree_stage(tape->merge_stage); + tape->merge_stage = NULL; + } + if (tape->filemark_cnt) + idetape_update_last_marker(drive, tape->last_mark_addr, last_mark_addr); + tape->last_mark_addr = last_mark_addr; + if (tape->filemark_cnt++ == 0) + tape->first_mark_addr = last_mark_addr; + } + return 0; +} + +static void idetape_write_eod (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + if (!tape->onstream || tape->raw) + return; + tape->merge_stage = __idetape_kmalloc_stage(tape, 1, 0); + if (tape->merge_stage != NULL) { + tape->eod_frame_addr = idetape_read_position(drive); + idetape_init_stage(drive, tape->merge_stage, OS_FRAME_TYPE_EOD, tape->logical_blk_num); + idetape_pad_zeros(drive, tape->stage_size); + __idetape_kfree_stage(tape->merge_stage); + tape->merge_stage = NULL; + } + return; +} + +int idetape_seek_logical_blk (ide_drive_t *drive, int logical_blk_num) +{ + idetape_tape_t *tape = drive->driver_data; + int estimated_address = logical_blk_num + 20; + int retries = 0; + int speed_control; + + speed_control = tape->speed_control; + tape->speed_control = 0; + if (logical_blk_num < 0) + logical_blk_num = 0; + if (idetape_get_logical_blk(drive, logical_blk_num, 10, 1)) + goto ok; + while (++retries < 10) { + idetape_discard_read_pipeline(drive, 0); + idetape_position_tape(drive, estimated_address, 0, 0); + if (idetape_get_logical_blk(drive, logical_blk_num, 10, 1)) + goto ok; + if (!idetape_get_logical_blk(drive, -1, 10, 1)) + goto error; + if (tape->logical_blk_num < logical_blk_num) + estimated_address += logical_blk_num - tape->logical_blk_num; + else + break; + } +error: + tape->speed_control = speed_control; + tape->restart_speed_control_req = 1; + printk(KERN_INFO "ide-tape: %s: couldn't seek to logical block %d " + "(at %d), %d retries\n", tape->name, logical_blk_num, + tape->logical_blk_num, retries); + return -EIO; +ok: + tape->speed_control = speed_control; + tape->restart_speed_control_req = 1; + return 0; +} + +/* + * idetape_mtioctop is called from idetape_chrdev_ioctl when + * the general mtio MTIOCTOP ioctl is requested. + * + * We currently support the following mtio.h operations: + * + * MTFSF - Space over mt_count filemarks in the positive direction. + * The tape is positioned after the last spaced filemark. + * + * MTFSFM - Same as MTFSF, but the tape is positioned before the + * last filemark. + * + * MTBSF - Steps background over mt_count filemarks, tape is + * positioned before the last filemark. + * + * MTBSFM - Like MTBSF, only tape is positioned after the last filemark. + * + * Note: + * + * MTBSF and MTBSFM are not supported when the tape doesn't + * supports spacing over filemarks in the reverse direction. + * In this case, MTFSFM is also usually not supported (it is + * supported in the rare case in which we crossed the filemark + * during our read-ahead pipelined operation mode). + * + * MTWEOF - Writes mt_count filemarks. Tape is positioned after + * the last written filemark. + * + * MTREW - Rewinds tape. + * + * MTLOAD - Loads the tape. + * + * MTOFFL - Puts the tape drive "Offline": Rewinds the tape and + * MTUNLOAD prevents further access until the media is replaced. + * + * MTNOP - Flushes tape buffers. + * + * MTRETEN - Retension media. This typically consists of one end + * to end pass on the media. + * + * MTEOM - Moves to the end of recorded data. + * + * MTERASE - Erases tape. + * + * MTSETBLK - Sets the user block size to mt_count bytes. If + * mt_count is 0, we will attempt to autodetect + * the block size. + * + * MTSEEK - Positions the tape in a specific block number, where + * each block is assumed to contain which user_block_size + * bytes. + * + * MTSETPART - Switches to another tape partition. + * + * MTLOCK - Locks the tape door. + * + * MTUNLOCK - Unlocks the tape door. + * + * The following commands are currently not supported: + * + * MTFSS, MTBSS, MTWSM, MTSETDENSITY, + * MTSETDRVBUFFER, MT_ST_BOOLEANS, MT_ST_WRITE_THRESHOLD. + */ +static int idetape_mtioctop (ide_drive_t *drive,short mt_op,int mt_count) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + int i,retval; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 1) + printk(KERN_INFO "ide-tape: Handling MTIOCTOP ioctl: " + "mt_op=%d, mt_count=%d\n", mt_op, mt_count); +#endif /* IDETAPE_DEBUG_LOG */ + /* + * Commands which need our pipelined read-ahead stages. + */ + switch (mt_op) { + case MTFSF: + case MTFSFM: + case MTBSF: + case MTBSFM: + if (!mt_count) + return (0); + return (idetape_space_over_filemarks(drive,mt_op,mt_count)); + default: + break; + } + switch (mt_op) { + case MTWEOF: + idetape_discard_read_pipeline(drive, 1); + for (i = 0; i < mt_count; i++) { + retval = idetape_write_filemark(drive); + if (retval) + return retval; + } + return (0); + case MTREW: + idetape_discard_read_pipeline(drive, 0); + if (idetape_rewind_tape(drive)) + return -EIO; + if (tape->onstream && !tape->raw) + return idetape_position_tape(drive, OS_DATA_STARTFRAME1, 0, 0); + return 0; + case MTLOAD: + idetape_discard_read_pipeline(drive, 0); + idetape_create_load_unload_cmd(drive, &pc, IDETAPE_LU_LOAD_MASK); + return (idetape_queue_pc_tail(drive, &pc)); + case MTUNLOAD: + case MTOFFL: + idetape_discard_read_pipeline(drive, 0); + idetape_create_load_unload_cmd(drive, &pc,!IDETAPE_LU_LOAD_MASK); + return (idetape_queue_pc_tail(drive, &pc)); + case MTNOP: + idetape_discard_read_pipeline(drive, 0); + return (idetape_flush_tape_buffers(drive)); + case MTRETEN: + idetape_discard_read_pipeline(drive, 0); + idetape_create_load_unload_cmd(drive, &pc,IDETAPE_LU_RETENSION_MASK | IDETAPE_LU_LOAD_MASK); + return (idetape_queue_pc_tail(drive, &pc)); + case MTEOM: + if (tape->onstream) { +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: positioning tape to eod at %d\n", tape->name, tape->eod_frame_addr); +#endif + idetape_position_tape(drive, tape->eod_frame_addr, 0, 0); + if (!idetape_get_logical_blk(drive, -1, 10, 0)) + return -EIO; + if (tape->first_stage->aux->frame_type != OS_FRAME_TYPE_EOD) + return -EIO; + return 0; + } + idetape_create_space_cmd(&pc, 0, IDETAPE_SPACE_TO_EOD); + return (idetape_queue_pc_tail(drive, &pc)); + case MTERASE: + if (tape->onstream) { + tape->eod_frame_addr = OS_DATA_STARTFRAME1; + tape->logical_blk_num = 0; + tape->first_mark_addr = tape->last_mark_addr = -1; + idetape_position_tape(drive, tape->eod_frame_addr, 0, 0); + idetape_write_eod(drive); + idetape_flush_tape_buffers(drive); + idetape_write_header(drive, 0); + /* + * write filler frames to the unused frames... + * REMOVE WHEN going to LIN4 application type... + */ + idetape_write_filler(drive, OS_DATA_STARTFRAME1 - 10, 10); + idetape_write_filler(drive, OS_DATA_ENDFRAME1, 10); + idetape_flush_tape_buffers(drive); + (void) idetape_rewind_tape(drive); + return 0; + } + (void) idetape_rewind_tape(drive); + idetape_create_erase_cmd(&pc); + return (idetape_queue_pc_tail(drive, &pc)); + case MTSETBLK: + if (tape->onstream) { + if (mt_count != tape->tape_block_size) { + printk(KERN_INFO "ide-tape: %s: MTSETBLK %d -- only %d bytes block size supported\n", tape->name, mt_count, tape->tape_block_size); + return -EINVAL; + } + return 0; + } + if (mt_count) { + if (mt_count < tape->tape_block_size || mt_count % tape->tape_block_size) + return -EIO; + tape->user_bs_factor = mt_count / tape->tape_block_size; + clear_bit(IDETAPE_DETECT_BS, &tape->flags); + } else + set_bit(IDETAPE_DETECT_BS, &tape->flags); + return 0; + case MTSEEK: + if (!tape->onstream || tape->raw) { + idetape_discard_read_pipeline(drive, 0); + return idetape_position_tape(drive, mt_count * tape->user_bs_factor, tape->partition, 0); + } + return idetape_seek_logical_blk(drive, mt_count); + case MTSETPART: + idetape_discard_read_pipeline(drive, 0); + if (tape->onstream) + return -EIO; + return (idetape_position_tape(drive, 0, mt_count, 0)); + case MTFSR: + case MTBSR: + if (tape->onstream) { + if (!idetape_get_logical_blk(drive, -1, 10, 0)) + return -EIO; + if (mt_op == MTFSR) + return idetape_seek_logical_blk(drive, tape->logical_blk_num + mt_count); + else { + idetape_discard_read_pipeline(drive, 0); + return idetape_seek_logical_blk(drive, tape->logical_blk_num - mt_count); + } + } + case MTLOCK: + if (!idetape_create_prevent_cmd(drive, &pc, 1)) + return 0; + retval = idetape_queue_pc_tail(drive, &pc); + if (retval) return retval; + tape->door_locked = DOOR_EXPLICITLY_LOCKED; + return 0; + case MTUNLOCK: + if (!idetape_create_prevent_cmd(drive, &pc, 0)) + return 0; + retval = idetape_queue_pc_tail(drive, &pc); + if (retval) return retval; + tape->door_locked = DOOR_UNLOCKED; + return 0; + default: + printk(KERN_ERR "ide-tape: MTIO operation %d not " + "supported\n", mt_op); + return (-EIO); + } +} + +/* + * Our character device ioctls. + * + * General mtio.h magnetic io commands are supported here, and not in + * the corresponding block interface. + * + * The following ioctls are supported: + * + * MTIOCTOP - Refer to idetape_mtioctop for detailed description. + * + * MTIOCGET - The mt_dsreg field in the returned mtget structure + * will be set to (user block size in bytes << + * MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK. + * + * The mt_blkno is set to the current user block number. + * The other mtget fields are not supported. + * + * MTIOCPOS - The current tape "block position" is returned. We + * assume that each block contains user_block_size + * bytes. + * + * Our own ide-tape ioctls are supported on both interfaces. + */ +static int idetape_chrdev_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + ide_drive_t *drive = get_drive_ptr(inode->i_rdev); + idetape_tape_t *tape = drive->driver_data; + struct mtop mtop; + struct mtget mtget; + struct mtpos mtpos; + int block_offset = 0, position = tape->first_frame_position; + +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 3) + printk(KERN_INFO "ide-tape: Reached idetape_chrdev_ioctl, " + "cmd=%u\n", cmd); +#endif /* IDETAPE_DEBUG_LOG */ + + tape->restart_speed_control_req = 1; + if (tape->chrdev_direction == idetape_direction_write) { + idetape_empty_write_pipeline(drive); + idetape_flush_tape_buffers(drive); + } + if (cmd == MTIOCGET || cmd == MTIOCPOS) { + block_offset = idetape_pipeline_size(drive) / (tape->tape_block_size * tape->user_bs_factor); + if ((position = idetape_read_position(drive)) < 0) + return -EIO; + } + switch (cmd) { + case MTIOCTOP: + if (copy_from_user((char *) &mtop, (char *) arg, sizeof (struct mtop))) + return -EFAULT; + return (idetape_mtioctop(drive,mtop.mt_op,mtop.mt_count)); + case MTIOCGET: + memset(&mtget, 0, sizeof (struct mtget)); + mtget.mt_type = MT_ISSCSI2; + if (!tape->onstream || tape->raw) + mtget.mt_blkno = position / tape->user_bs_factor - block_offset; + else { + if (!idetape_get_logical_blk(drive, -1, 10, 0)) + mtget.mt_blkno = -1; + else + mtget.mt_blkno = tape->logical_blk_num; + } + mtget.mt_dsreg = ((tape->tape_block_size * tape->user_bs_factor) << MT_ST_BLKSIZE_SHIFT) & MT_ST_BLKSIZE_MASK; + if (tape->onstream) { + mtget.mt_gstat |= GMT_ONLINE(0xffffffff); + if (tape->first_stage && tape->first_stage->aux->frame_type == OS_FRAME_TYPE_EOD) + mtget.mt_gstat |= GMT_EOD(0xffffffff); + if (position <= OS_DATA_STARTFRAME1) + mtget.mt_gstat |= GMT_BOT(0xffffffff); + } + if (copy_to_user((char *) arg,(char *) &mtget, sizeof(struct mtget))) + return -EFAULT; + return 0; + case MTIOCPOS: + if (tape->onstream && !tape->raw) { + if (!idetape_get_logical_blk(drive, -1, 10, 0)) + return -EIO; + mtpos.mt_blkno = tape->logical_blk_num; + } else + mtpos.mt_blkno = position / tape->user_bs_factor - block_offset; + if (copy_to_user((char *) arg,(char *) &mtpos, sizeof(struct mtpos))) + return -EFAULT; + return 0; + default: + if (tape->chrdev_direction == idetape_direction_read) + idetape_discard_read_pipeline(drive, 1); + return (idetape_blkdev_ioctl(drive,inode,file,cmd,arg)); + } +} + +static int __idetape_analyze_headers (ide_drive_t *drive, int block) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_stage_t *stage; + os_header_t *header; + os_aux_t *aux; + + if (!tape->onstream || tape->raw) { + tape->header_ok = tape->linux_media = 1; + return 1; + } + tape->header_ok = tape->linux_media = 0; + tape->update_frame_cntr = 0; + tape->wrt_pass_cntr = 0; + tape->eod_frame_addr = OS_DATA_STARTFRAME1; + tape->first_mark_addr = tape->last_mark_addr = -1; + stage = __idetape_kmalloc_stage(tape, 0, 0); + if (stage == NULL) + return 0; +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: reading header\n", tape->name); +#endif + idetape_position_tape(drive, block, 0, 0); + if (!idetape_queue_rw_tail(drive, IDETAPE_READ_RQ, 1, stage->bio)) { + printk(KERN_INFO "ide-tape: %s: couldn't read header frame\n", + tape->name); + __idetape_kfree_stage(stage); + return 0; + } + header = (os_header_t *) bio_data(stage->bio); + aux = stage->aux; + if (strncmp(header->ident_str, "ADR_SEQ", 7) != 0) { + printk(KERN_INFO "ide-tape: %s: invalid header identification string\n", tape->name); + __idetape_kfree_stage(stage); + return 0; + } + if (header->major_rev != 1 || (header->minor_rev > OS_ADR_MINREV)) + printk(KERN_INFO "ide-tape: warning: revision %d.%d " + "detected (up to 1.%d supported)\n", + header->major_rev, header->minor_rev, OS_ADR_MINREV); + if (header->par_num != 1) + printk(KERN_INFO "ide-tape: warning: %d partitions defined, only one supported\n", header->par_num); + tape->wrt_pass_cntr = ntohs(header->partition.wrt_pass_cntr); + tape->eod_frame_addr = ntohl(header->partition.eod_frame_addr); + tape->filemark_cnt = ntohl(aux->filemark_cnt); + tape->first_mark_addr = ntohl(aux->next_mark_addr); + tape->last_mark_addr = ntohl(aux->last_mark_addr); + tape->update_frame_cntr = ntohl(aux->update_frame_cntr); + memcpy(tape->application_sig, aux->application_sig, 4); + tape->application_sig[4] = 0; + if (memcmp(tape->application_sig, "LIN", 3) == 0) { + tape->linux_media = 1; + tape->linux_media_version = tape->application_sig[3] - '0'; + if (tape->linux_media_version != 3) + printk(KERN_INFO "ide-tape: %s: Linux media version " + "%d detected (current 3)\n", + tape->name, tape->linux_media_version); + } else { + printk(KERN_INFO "ide-tape: %s: non Linux media detected " + "(%s)\n", tape->name, tape->application_sig); + tape->linux_media = 0; + } +#if ONSTREAM_DEBUG + if (tape->debug_level >= 2) + printk(KERN_INFO "ide-tape: %s: detected write pass counter " + "%d, eod frame addr %d\n", tape->name, + tape->wrt_pass_cntr, tape->eod_frame_addr); +#endif + __idetape_kfree_stage(stage); + return 1; +} + +static int idetape_analyze_headers (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + int position, block; + + if (!tape->onstream || tape->raw) { + tape->header_ok = tape->linux_media = 1; + return 1; + } + tape->header_ok = tape->linux_media = 0; + position = idetape_read_position(drive); + for (block = 5; block < 10; block++) + if (__idetape_analyze_headers(drive, block)) + goto ok; + for (block = 0xbae; block < 0xbb3; block++) /* 2990 - 2994 */ + if (__idetape_analyze_headers(drive, block)) + goto ok; + printk(KERN_ERR "ide-tape: %s: failed to find valid ADRL header\n", tape->name); + return 0; +ok: + if (position < OS_DATA_STARTFRAME1) + position = OS_DATA_STARTFRAME1; + idetape_position_tape(drive, position, 0, 0); + tape->header_ok = 1; + return 1; +} + +/* + * Our character device open function. + */ +static int idetape_chrdev_open (struct inode *inode, struct file *filp) +{ + ide_drive_t *drive; + idetape_tape_t *tape; + idetape_pc_t pc; + unsigned int minor = minor(inode->i_rdev); + +#if IDETAPE_DEBUG_LOG + printk(KERN_INFO "ide-tape: Reached idetape_chrdev_open\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + if ((drive = get_drive_ptr(inode->i_rdev)) == NULL) + return -ENXIO; + tape = drive->driver_data; + + if (test_and_set_bit(IDETAPE_BUSY, &tape->flags)) + return -EBUSY; + MOD_INC_USE_COUNT; + if (!tape->onstream) { + idetape_read_position(drive); + if (!test_bit(IDETAPE_ADDRESS_VALID, &tape->flags)) + (void) idetape_rewind_tape(drive); + } else { + if (minor & 64) { + tape->tape_block_size = tape->stage_size = 32768 + 512; + tape->raw = 1; + } else { + tape->tape_block_size = tape->stage_size = 32768; + tape->raw = 0; + } + idetape_onstream_mode_sense_tape_parameter_page(drive, tape->debug_level); + } + if (idetape_wait_ready(drive, 60 * HZ)) { + clear_bit(IDETAPE_BUSY, &tape->flags); + printk(KERN_ERR "ide-tape: %s: drive not ready\n", tape->name); + MOD_DEC_USE_COUNT; + return -EBUSY; + } + idetape_read_position(drive); + MOD_DEC_USE_COUNT; + clear_bit(IDETAPE_PIPELINE_ERROR, &tape->flags); + + if (tape->chrdev_direction == idetape_direction_none) { + MOD_INC_USE_COUNT; + if (idetape_create_prevent_cmd(drive, &pc, 1)) { + if (!idetape_queue_pc_tail(drive, &pc)) { + if (tape->door_locked != DOOR_EXPLICITLY_LOCKED) + tape->door_locked = DOOR_LOCKED; + } + } + idetape_analyze_headers(drive); + } + tape->max_frames = tape->cur_frames = tape->req_buffer_fill = 0; + idetape_restart_speed_control(drive); + tape->restart_speed_control_req = 0; + return 0; +} + +static void idetape_write_release (struct inode *inode) +{ + ide_drive_t *drive = get_drive_ptr(inode->i_rdev); + idetape_tape_t *tape = drive->driver_data; + unsigned int minor = minor(inode->i_rdev); + + idetape_empty_write_pipeline(drive); + tape->merge_stage = __idetape_kmalloc_stage(tape, 1, 0); + if (tape->merge_stage != NULL) { + idetape_pad_zeros(drive, tape->tape_block_size * (tape->user_bs_factor - 1)); + __idetape_kfree_stage(tape->merge_stage); + tape->merge_stage = NULL; + } + idetape_write_filemark(drive); + idetape_write_eod(drive); + idetape_flush_tape_buffers(drive); + idetape_write_header(drive, minor >= 128); + idetape_flush_tape_buffers(drive); + + return; +} + +/* + * Our character device release function. + */ +static int idetape_chrdev_release (struct inode *inode, struct file *filp) +{ + ide_drive_t *drive = get_drive_ptr(inode->i_rdev); + idetape_tape_t *tape; + idetape_pc_t pc; + unsigned int minor = minor(inode->i_rdev); + + lock_kernel(); + tape = drive->driver_data; +#if IDETAPE_DEBUG_LOG + if (tape->debug_level >= 3) + printk(KERN_INFO "ide-tape: Reached idetape_chrdev_release\n"); +#endif /* IDETAPE_DEBUG_LOG */ + + if (tape->chrdev_direction == idetape_direction_write) { + idetape_write_release(inode); + } + if (tape->chrdev_direction == idetape_direction_read) { + if (minor < 128) + idetape_discard_read_pipeline(drive, 1); + else + idetape_wait_for_pipeline(drive); + } + if (tape->cache_stage != NULL) { + __idetape_kfree_stage(tape->cache_stage); + tape->cache_stage = NULL; + } + if (minor < 128) + (void) idetape_rewind_tape(drive); + if (tape->chrdev_direction == idetape_direction_none) { + if (tape->door_locked != DOOR_EXPLICITLY_LOCKED) { + if (idetape_create_prevent_cmd(drive, &pc, 0)) + if (!idetape_queue_pc_tail(drive, &pc)) + tape->door_locked = DOOR_UNLOCKED; + } + MOD_DEC_USE_COUNT; + } + clear_bit(IDETAPE_BUSY, &tape->flags); + unlock_kernel(); + return 0; +} + +/* + * idetape_identify_device is called to check the contents of the + * ATAPI IDENTIFY command results. We return: + * + * 1 If the tape can be supported by us, based on the information + * we have so far. + * + * 0 If this tape driver is not currently supported by us. + */ +static int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id) +{ + struct idetape_id_gcw gcw; +#if IDETAPE_DEBUG_INFO + unsigned short mask,i; +#endif /* IDETAPE_DEBUG_INFO */ + + if (!id) + return 0; + + *((unsigned short *) &gcw) = id->config; + +#if IDETAPE_DEBUG_INFO + printk(KERN_INFO "ide-tape: Dumping ATAPI Identify Device tape parameters\n"); + printk(KERN_INFO "ide-tape: Protocol Type: "); + switch (gcw.protocol) { + case 0: case 1: printk(KERN_INFO "ATA\n");break; + case 2: printk(KERN_INFO "ATAPI\n");break; + case 3: printk(KERN_INFO "Reserved (Unknown to ide-tape)\n");break; + } + printk(KERN_INFO "ide-tape: Device Type: %x - ",gcw.device_type); + switch (gcw.device_type) { + case 0: printk(KERN_INFO "Direct-access Device\n");break; + case 1: printk(KERN_INFO "Streaming Tape Device\n");break; + case 2: case 3: case 4: printk(KERN_INFO "Reserved\n");break; + case 5: printk(KERN_INFO "CD-ROM Device\n");break; + case 6: printk(KERN_INFO "Reserved\n"); + case 7: printk(KERN_INFO "Optical memory Device\n");break; + case 0x1f: printk(KERN_INFO "Unknown or no Device type\n");break; + default: printk(KERN_INFO "Reserved\n"); + } + printk(KERN_INFO "ide-tape: Removable: %s",gcw.removable ? "Yes\n":"No\n"); + printk(KERN_INFO "ide-tape: Command Packet DRQ Type: "); + switch (gcw.drq_type) { + case 0: printk(KERN_INFO "Microprocessor DRQ\n");break; + case 1: printk(KERN_INFO "Interrupt DRQ\n");break; + case 2: printk(KERN_INFO "Accelerated DRQ\n");break; + case 3: printk(KERN_INFO "Reserved\n");break; + } + printk(KERN_INFO "ide-tape: Command Packet Size: "); + switch (gcw.packet_size) { + case 0: printk(KERN_INFO "12 bytes\n");break; + case 1: printk(KERN_INFO "16 bytes\n");break; + default: printk(KERN_INFO "Reserved\n");break; + } + printk(KERN_INFO "ide-tape: Model: %.40s\n",id->model); + printk(KERN_INFO "ide-tape: Firmware Revision: %.8s\n",id->fw_rev); + printk(KERN_INFO "ide-tape: Serial Number: %.20s\n",id->serial_no); + printk(KERN_INFO "ide-tape: Write buffer size: %d bytes\n",id->buf_size*512); + printk(KERN_INFO "ide-tape: DMA: %s",id->capability & 0x01 ? "Yes\n":"No\n"); + printk(KERN_INFO "ide-tape: LBA: %s",id->capability & 0x02 ? "Yes\n":"No\n"); + printk(KERN_INFO "ide-tape: IORDY can be disabled: %s",id->capability & 0x04 ? "Yes\n":"No\n"); + printk(KERN_INFO "ide-tape: IORDY supported: %s",id->capability & 0x08 ? "Yes\n":"Unknown\n"); + printk(KERN_INFO "ide-tape: ATAPI overlap supported: %s",id->capability & 0x20 ? "Yes\n":"No\n"); + printk(KERN_INFO "ide-tape: PIO Cycle Timing Category: %d\n",id->tPIO); + printk(KERN_INFO "ide-tape: DMA Cycle Timing Category: %d\n",id->tDMA); + printk(KERN_INFO "ide-tape: Single Word DMA supported modes: "); + for (i=0,mask=1;i<8;i++,mask=mask << 1) { + if (id->dma_1word & mask) + printk(KERN_INFO "%d ",i); + if (id->dma_1word & (mask << 8)) + printk(KERN_INFO "(active) "); + } + printk(KERN_INFO "\n"); + printk(KERN_INFO "ide-tape: Multi Word DMA supported modes: "); + for (i=0,mask=1;i<8;i++,mask=mask << 1) { + if (id->dma_mword & mask) + printk(KERN_INFO "%d ",i); + if (id->dma_mword & (mask << 8)) + printk(KERN_INFO "(active) "); + } + printk(KERN_INFO "\n"); + if (id->field_valid & 0x0002) { + printk(KERN_INFO "ide-tape: Enhanced PIO Modes: %s\n", + id->eide_pio_modes & 1 ? "Mode 3":"None"); + printk(KERN_INFO "ide-tape: Minimum Multi-word DMA cycle per word: "); + if (id->eide_dma_min == 0) + printk(KERN_INFO "Not supported\n"); + else + printk(KERN_INFO "%d ns\n",id->eide_dma_min); + + printk(KERN_INFO "ide-tape: Manufacturer\'s Recommended Multi-word cycle: "); + if (id->eide_dma_time == 0) + printk(KERN_INFO "Not supported\n"); + else + printk(KERN_INFO "%d ns\n",id->eide_dma_time); + + printk(KERN_INFO "ide-tape: Minimum PIO cycle without IORDY: "); + if (id->eide_pio == 0) + printk(KERN_INFO "Not supported\n"); + else + printk(KERN_INFO "%d ns\n",id->eide_pio); + + printk(KERN_INFO "ide-tape: Minimum PIO cycle with IORDY: "); + if (id->eide_pio_iordy == 0) + printk(KERN_INFO "Not supported\n"); + else + printk(KERN_INFO "%d ns\n",id->eide_pio_iordy); + + } else + printk(KERN_INFO "ide-tape: According to the device, fields 64-70 are not valid.\n"); +#endif /* IDETAPE_DEBUG_INFO */ + + /* Check that we can support this device */ + + if (gcw.protocol !=2 ) + printk(KERN_ERR "ide-tape: Protocol is not ATAPI\n"); + else if (gcw.device_type != 1) + printk(KERN_ERR "ide-tape: Device type is not set to tape\n"); + else if (!gcw.removable) + printk(KERN_ERR "ide-tape: The removable flag is not set\n"); + else if (gcw.packet_size != 0) { + printk(KERN_ERR "ide-tape: Packet size is not 12 bytes long\n"); + if (gcw.packet_size == 1) + printk(KERN_ERR "ide-tape: Sorry, padding to 16 bytes is still not supported\n"); + } else + return 1; + return 0; +} + +/* + * Notify vendor ID to the OnStream tape drive + */ +static void idetape_onstream_set_vendor (ide_drive_t *drive, char *vendor) +{ + idetape_pc_t pc; + idetape_mode_parameter_header_t *header; + + idetape_create_mode_select_cmd(&pc, sizeof(*header) + 8); + pc.buffer[0] = 3 + 8; /* Mode Data Length */ + pc.buffer[1] = 0; /* Medium Type - ignoring */ + pc.buffer[2] = 0; /* Reserved */ + pc.buffer[3] = 0; /* Block Descriptor Length */ + pc.buffer[4 + 0] = 0x36 | (1 << 7); + pc.buffer[4 + 1] = 6; + pc.buffer[4 + 2] = vendor[0]; + pc.buffer[4 + 3] = vendor[1]; + pc.buffer[4 + 4] = vendor[2]; + pc.buffer[4 + 5] = vendor[3]; + pc.buffer[4 + 6] = 0; + pc.buffer[4 + 7] = 0; + if (idetape_queue_pc_tail(drive, &pc)) + printk(KERN_ERR "ide-tape: Couldn't set vendor name to %s\n", vendor); + +} + +/* + * Various unused OnStream commands + */ +#if ONSTREAM_DEBUG +static void idetape_onstream_set_retries (ide_drive_t *drive, int retries) +{ + idetape_pc_t pc; + + idetape_create_mode_select_cmd(&pc, sizeof(idetape_mode_parameter_header_t) + 4); + pc.buffer[0] = 3 + 4; + pc.buffer[1] = 0; /* Medium Type - ignoring */ + pc.buffer[2] = 0; /* Reserved */ + pc.buffer[3] = 0; /* Block Descriptor Length */ + pc.buffer[4 + 0] = 0x2f | (1 << 7); + pc.buffer[4 + 1] = 2; + pc.buffer[4 + 2] = 4; + pc.buffer[4 + 3] = retries; + if (idetape_queue_pc_tail(drive, &pc)) + printk(KERN_ERR "ide-tape: Couldn't set retries to %d\n", retries); +} +#endif + +/* + * Configure 32.5KB block size. + */ +static void idetape_onstream_configure_block_size (ide_drive_t *drive) +{ + idetape_pc_t pc; + idetape_mode_parameter_header_t *header; + idetape_block_size_page_t *bs; + + /* + * Get the current block size from the block size mode page + */ + idetape_create_mode_sense_cmd(&pc, IDETAPE_BLOCK_SIZE_PAGE); + if (idetape_queue_pc_tail(drive, &pc)) + printk(KERN_ERR "ide-tape: can't get tape block size mode page\n"); + header = (idetape_mode_parameter_header_t *) pc.buffer; + bs = (idetape_block_size_page_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t) + header->bdl); + +#if IDETAPE_DEBUG_INFO + printk(KERN_INFO "ide-tape: 32KB play back: %s\n", bs->play32 ? "Yes" : "No"); + printk(KERN_INFO "ide-tape: 32.5KB play back: %s\n", bs->play32_5 ? "Yes" : "No"); + printk(KERN_INFO "ide-tape: 32KB record: %s\n", bs->record32 ? "Yes" : "No"); + printk(KERN_INFO "ide-tape: 32.5KB record: %s\n", bs->record32_5 ? "Yes" : "No"); +#endif /* IDETAPE_DEBUG_INFO */ + + /* + * Configure default auto columns mode, 32.5KB block size + */ + bs->one = 1; + bs->play32 = 0; + bs->play32_5 = 1; + bs->record32 = 0; + bs->record32_5 = 1; + idetape_create_mode_select_cmd(&pc, sizeof(*header) + sizeof(*bs)); + if (idetape_queue_pc_tail(drive, &pc)) + printk(KERN_ERR "ide-tape: Couldn't set tape block size mode page\n"); + +#if ONSTREAM_DEBUG + /* + * In debug mode, we want to see as many errors as possible + * to test the error recovery mechanism. + */ + idetape_onstream_set_retries(drive, 0); +#endif +} + +/* + * Use INQUIRY to get the firmware revision + */ +static void idetape_get_inquiry_results (ide_drive_t *drive) +{ + char *r; + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + idetape_inquiry_result_t *inquiry; + + idetape_create_inquiry_cmd(&pc); + if (idetape_queue_pc_tail(drive, &pc)) { + printk(KERN_ERR "ide-tape: %s: can't get INQUIRY results\n", tape->name); + return; + } + inquiry = (idetape_inquiry_result_t *) pc.buffer; + memcpy(tape->vendor_id, inquiry->vendor_id, 8); + memcpy(tape->product_id, inquiry->product_id, 16); + memcpy(tape->firmware_revision, inquiry->revision_level, 4); + ide_fixstring(tape->vendor_id, 10, 0); + ide_fixstring(tape->product_id, 18, 0); + ide_fixstring(tape->firmware_revision, 6, 0); + r = tape->firmware_revision; + if (*(r + 1) == '.') + tape->firmware_revision_num = (*r - '0') * 100 + (*(r + 2) - '0') * 10 + *(r + 3) - '0'; + else if (tape->onstream) + tape->firmware_revision_num = (*r - '0') * 100 + (*(r + 1) - '0') * 10 + *(r + 2) - '0'; + printk(KERN_INFO "ide-tape: %s <-> %s: %s %s rev %s\n", drive->name, tape->name, tape->vendor_id, tape->product_id, tape->firmware_revision); +} + +/* + * Configure the OnStream ATAPI tape drive for default operation + */ +static void idetape_configure_onstream (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + + if (tape->firmware_revision_num < 105) { + printk(KERN_INFO "ide-tape: %s: Old OnStream firmware revision detected (%s)\n", tape->name, tape->firmware_revision); + printk(KERN_INFO "ide-tape: %s: An upgrade to version 1.05 or above is recommended\n", tape->name); + } + + /* + * Configure 32.5KB (data+aux) block size. + */ + idetape_onstream_configure_block_size(drive); + + /* + * Set vendor name to 'LIN3' for "Linux support version 3". + */ + idetape_onstream_set_vendor(drive, "LIN3"); +} + +/* + * idetape_get_mode_sense_parameters asks the tape about its various + * parameters. This may work for other drives to??? + */ +static void idetape_onstream_mode_sense_tape_parameter_page(ide_drive_t *drive, int debug) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + idetape_mode_parameter_header_t *header; + onstream_tape_paramtr_page_t *prm; + + idetape_create_mode_sense_cmd(&pc, IDETAPE_PARAMTR_PAGE); + if (idetape_queue_pc_tail(drive, &pc)) { + printk(KERN_ERR "ide-tape: Can't get tape parameters page - probably no tape inserted in onstream drive\n"); + return; + } + header = (idetape_mode_parameter_header_t *) pc.buffer; + prm = (onstream_tape_paramtr_page_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t) + header->bdl); + + tape->capacity = ntohs(prm->segtrk) * ntohs(prm->trks); + if (debug) { + printk(KERN_INFO "ide-tape: %s <-> %s: Tape length %dMB (%d frames/track, %d tracks = %d blocks, density: %dKbpi)\n", + drive->name, tape->name, tape->capacity/32, ntohs(prm->segtrk), ntohs(prm->trks), tape->capacity, prm->density); + } + + return; +} + +/* + * idetape_get_mode_sense_results asks the tape about its various + * parameters. In particular, we will adjust our data transfer buffer + * size to the recommended value as returned by the tape. + */ +static void idetape_get_mode_sense_results (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + idetape_mode_parameter_header_t *header; + idetape_capabilities_page_t *capabilities; + + idetape_create_mode_sense_cmd(&pc, IDETAPE_CAPABILITIES_PAGE); + if (idetape_queue_pc_tail(drive, &pc)) { + printk(KERN_ERR "ide-tape: Can't get tape parameters - assuming some default values\n"); + tape->tape_block_size = 512; tape->capabilities.ctl = 52; + tape->capabilities.speed = 450; tape->capabilities.buffer_size = 6 * 52; + return; + } + header = (idetape_mode_parameter_header_t *) pc.buffer; + capabilities = (idetape_capabilities_page_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t) + header->bdl); + + capabilities->max_speed = ntohs (capabilities->max_speed); + capabilities->ctl = ntohs (capabilities->ctl); + capabilities->speed = ntohs (capabilities->speed); + capabilities->buffer_size = ntohs (capabilities->buffer_size); + + if (!capabilities->speed) { + printk(KERN_INFO "ide-tape: %s: overriding capabilities->speed (assuming 650KB/sec)\n", drive->name); + capabilities->speed = 650; + } + if (!capabilities->max_speed) { + printk(KERN_INFO "ide-tape: %s: overriding capabilities->max_speed (assuming 650KB/sec)\n", drive->name); + capabilities->max_speed = 650; + } + + tape->capabilities = *capabilities; /* Save us a copy */ + if (capabilities->blk512) + tape->tape_block_size = 512; + else if (capabilities->blk1024) + tape->tape_block_size = 1024; + else if (tape->onstream && capabilities->blk32768) + tape->tape_block_size = 32768; + +#if IDETAPE_DEBUG_INFO + printk(KERN_INFO "ide-tape: Dumping the results of the MODE SENSE packet command\n"); + printk(KERN_INFO "ide-tape: Mode Parameter Header:\n"); + printk(KERN_INFO "ide-tape: Mode Data Length - %d\n",header->mode_data_length); + printk(KERN_INFO "ide-tape: Medium Type - %d\n",header->medium_type); + printk(KERN_INFO "ide-tape: Device Specific Parameter - %d\n",header->dsp); + printk(KERN_INFO "ide-tape: Block Descriptor Length - %d\n",header->bdl); + + printk(KERN_INFO "ide-tape: Capabilities and Mechanical Status Page:\n"); + printk(KERN_INFO "ide-tape: Page code - %d\n",capabilities->page_code); + printk(KERN_INFO "ide-tape: Page length - %d\n",capabilities->page_length); + printk(KERN_INFO "ide-tape: Read only - %s\n",capabilities->ro ? "Yes":"No"); + printk(KERN_INFO "ide-tape: Supports reverse space - %s\n",capabilities->sprev ? "Yes":"No"); + printk(KERN_INFO "ide-tape: Supports erase initiated formatting - %s\n",capabilities->efmt ? "Yes":"No"); + printk(KERN_INFO "ide-tape: Supports QFA two Partition format - %s\n",capabilities->qfa ? "Yes":"No"); + printk(KERN_INFO "ide-tape: Supports locking the medium - %s\n",capabilities->lock ? "Yes":"No"); + printk(KERN_INFO "ide-tape: The volume is currently locked - %s\n",capabilities->locked ? "Yes":"No"); + printk(KERN_INFO "ide-tape: The device defaults in the prevent state - %s\n",capabilities->prevent ? "Yes":"No"); + printk(KERN_INFO "ide-tape: Supports ejecting the medium - %s\n",capabilities->eject ? "Yes":"No"); + printk(KERN_INFO "ide-tape: Supports error correction - %s\n",capabilities->ecc ? "Yes":"No"); + printk(KERN_INFO "ide-tape: Supports data compression - %s\n",capabilities->cmprs ? "Yes":"No"); + printk(KERN_INFO "ide-tape: Supports 512 bytes block size - %s\n",capabilities->blk512 ? "Yes":"No"); + printk(KERN_INFO "ide-tape: Supports 1024 bytes block size - %s\n",capabilities->blk1024 ? "Yes":"No"); + printk(KERN_INFO "ide-tape: Supports 32768 bytes block size / Restricted byte count for PIO transfers - %s\n",capabilities->blk32768 ? "Yes":"No"); + printk(KERN_INFO "ide-tape: Maximum supported speed in KBps - %d\n",capabilities->max_speed); + printk(KERN_INFO "ide-tape: Continuous transfer limits in blocks - %d\n",capabilities->ctl); + printk(KERN_INFO "ide-tape: Current speed in KBps - %d\n",capabilities->speed); + printk(KERN_INFO "ide-tape: Buffer size - %d\n",capabilities->buffer_size*512); +#endif /* IDETAPE_DEBUG_INFO */ +} + +/* + * ide_get_blocksize_from_block_descriptor does a mode sense page 0 with block descriptor + * and if it succeeds sets the tape block size with the reported value + */ +static void idetape_get_blocksize_from_block_descriptor(ide_drive_t *drive) +{ + + idetape_tape_t *tape = drive->driver_data; + idetape_pc_t pc; + idetape_mode_parameter_header_t *header; + idetape_parameter_block_descriptor_t *block_descrp; + + idetape_create_mode_sense_cmd(&pc, IDETAPE_BLOCK_DESCRIPTOR); + if (idetape_queue_pc_tail(drive, &pc)) { + printk(KERN_ERR "ide-tape: Can't get block descriptor\n"); + if (tape->tape_block_size == 0) { + printk(KERN_WARNING "ide-tape: Cannot deal with zero block size, assume 32k\n"); + tape->tape_block_size = 32768; + } + return; + } + header = (idetape_mode_parameter_header_t *) pc.buffer; + block_descrp = (idetape_parameter_block_descriptor_t *) (pc.buffer + sizeof(idetape_mode_parameter_header_t)); + tape->tape_block_size =( block_descrp->length[0]<<16) + (block_descrp->length[1]<<8) + block_descrp->length[2]; +#if IDETAPE_DEBUG_INFO + printk(KERN_INFO "ide-tape: Adjusted block size - %d\n", tape->tape_block_size); +#endif /* IDETAPE_DEBUG_INFO */ +} +static void idetape_add_settings (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + +/* + * drive setting name read/write ioctl ioctl data type min max mul_factor div_factor data pointer set function + */ + ide_add_setting(drive, "buffer", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 2, &tape->capabilities.buffer_size, NULL); + ide_add_setting(drive, "pipeline_min", SETTING_RW, -1, -1, TYPE_INT, 2, 0xffff, tape->stage_size / 1024, 1, &tape->min_pipeline, NULL); + ide_add_setting(drive, "pipeline", SETTING_RW, -1, -1, TYPE_INT, 2, 0xffff, tape->stage_size / 1024, 1, &tape->max_stages, NULL); + ide_add_setting(drive, "pipeline_max", SETTING_RW, -1, -1, TYPE_INT, 2, 0xffff, tape->stage_size / 1024, 1, &tape->max_pipeline, NULL); + ide_add_setting(drive, "pipeline_used",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->nr_stages, NULL); + ide_add_setting(drive, "pipeline_pending",SETTING_READ,-1, -1, TYPE_INT, 0, 0xffff, tape->stage_size / 1024, 1, &tape->nr_pending_stages, NULL); + ide_add_setting(drive, "speed", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 1, &tape->capabilities.speed, NULL); + ide_add_setting(drive, "stage", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1024, &tape->stage_size, NULL); + ide_add_setting(drive, "tdsc", SETTING_RW, -1, -1, TYPE_INT, IDETAPE_DSC_RW_MIN, IDETAPE_DSC_RW_MAX, 1000, HZ, &tape->best_dsc_rw_frequency, NULL); + ide_add_setting(drive, "dsc_overlap", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->dsc_overlap, NULL); + ide_add_setting(drive, "pipeline_head_speed_c",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->controlled_pipeline_head_speed, NULL); + ide_add_setting(drive, "pipeline_head_speed_u",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->uncontrolled_pipeline_head_speed, NULL); + ide_add_setting(drive, "avg_speed", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->avg_speed, NULL); + ide_add_setting(drive, "debug_level",SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->debug_level, NULL); + if (tape->onstream) { + ide_add_setting(drive, "cur_frames", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 1, &tape->cur_frames, NULL); + ide_add_setting(drive, "max_frames", SETTING_READ, -1, -1, TYPE_SHORT, 0, 0xffff, 1, 1, &tape->max_frames, NULL); + ide_add_setting(drive, "insert_speed", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->insert_speed, NULL); + ide_add_setting(drive, "speed_control",SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->speed_control, NULL); + ide_add_setting(drive, "tape_still_time",SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->tape_still_time, NULL); + ide_add_setting(drive, "max_insert_speed",SETTING_RW, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->max_insert_speed, NULL); + ide_add_setting(drive, "insert_size", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->insert_size, NULL); + ide_add_setting(drive, "capacity", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->capacity, NULL); + ide_add_setting(drive, "first_frame", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->first_frame_position, NULL); + ide_add_setting(drive, "logical_blk", SETTING_READ, -1, -1, TYPE_INT, 0, 0xffff, 1, 1, &tape->logical_blk_num, NULL); + } +} + +/* + * ide_setup is called to: + * + * 1. Initialize our various state variables. + * 2. Ask the tape for its capabilities. + * 3. Allocate a buffer which will be used for data + * transfer. The buffer size is chosen based on + * the recommendation which we received in step (2). + * + * Note that at this point ide.c already assigned us an irq, so that + * we can queue requests here and wait for their completion. + */ +static void idetape_setup (ide_drive_t *drive, idetape_tape_t *tape, int minor) +{ + unsigned long t1, tmid, tn, t; + int speed; + struct idetape_id_gcw gcw; + int stage_size; + struct sysinfo si; + + memset(tape, 0, sizeof (idetape_tape_t)); + spin_lock_init(&tape->spinlock); + drive->driver_data = tape; + drive->ready_stat = 0; /* An ATAPI device ignores DRDY */ + if (strstr(drive->id->model, "OnStream DI-")) + tape->onstream = 1; + drive->dsc_overlap = 1; +#ifdef CONFIG_BLK_DEV_IDEPCI + if (!tape->onstream && HWIF(drive)->pci_dev != NULL) { + /* + * These two ide-pci host adapters appear to need DSC overlap disabled. + * This probably needs further analysis. + */ + if ((HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_ARTOP_ATP850UF) || + (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_TTI_HPT343)) { + printk(KERN_INFO "ide-tape: %s: disabling DSC overlap\n", tape->name); + drive->dsc_overlap = 0; + } + } +#endif /* CONFIG_BLK_DEV_IDEPCI */ + tape->drive = drive; + tape->minor = minor; + tape->name[0] = 'h'; tape->name[1] = 't'; tape->name[2] = '0' + minor; + tape->chrdev_direction = idetape_direction_none; + tape->pc = tape->pc_stack; + tape->max_insert_speed = 10000; + tape->speed_control = 1; + *((unsigned short *) &gcw) = drive->id->config; + if (gcw.drq_type == 1) + set_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags); + + tape->min_pipeline = tape->max_pipeline = tape->max_stages = 10; + + idetape_get_inquiry_results(drive); + idetape_get_mode_sense_results(drive); + idetape_get_blocksize_from_block_descriptor(drive); + if (tape->onstream) { + idetape_onstream_mode_sense_tape_parameter_page(drive, 1); + idetape_configure_onstream(drive); + } + tape->user_bs_factor = 1; + tape->stage_size = tape->capabilities.ctl * tape->tape_block_size; + while (tape->stage_size > 0xffff) { + printk(KERN_NOTICE "ide-tape: decreasing stage size\n"); + tape->capabilities.ctl /= 2; + tape->stage_size = tape->capabilities.ctl * tape->tape_block_size; + } + stage_size = tape->stage_size; + if (tape->onstream) + stage_size = 32768 + 512; + tape->pages_per_stage = stage_size / PAGE_SIZE; + if (stage_size % PAGE_SIZE) { + tape->pages_per_stage++; + tape->excess_bh_size = PAGE_SIZE - stage_size % PAGE_SIZE; + } + + /* + * Select the "best" DSC read/write polling frequency + * and pipeline size. + */ + speed = max(tape->capabilities.speed, tape->capabilities.max_speed); + + tape->max_stages = speed * 1000 * 10 / tape->stage_size; + + /* + * Limit memory use for pipeline to 10% of physical memory + */ + si_meminfo(&si); + if (tape->max_stages * tape->stage_size > si.totalram * si.mem_unit / 10) + tape->max_stages = si.totalram * si.mem_unit / (10 * tape->stage_size); + tape->min_pipeline = tape->max_stages; + tape->max_pipeline = tape->max_stages * 2; + + t1 = (tape->stage_size * HZ) / (speed * 1000); + tmid = (tape->capabilities.buffer_size * 32 * HZ) / (speed * 125); + tn = (IDETAPE_FIFO_THRESHOLD * tape->stage_size * HZ) / (speed * 1000); + + if (tape->max_stages) + t = tn; + else + t = t1; + + /* + * Ensure that the number we got makes sense; limit + * it within IDETAPE_DSC_RW_MIN and IDETAPE_DSC_RW_MAX. + */ + tape->best_dsc_rw_frequency = max((unsigned long) min(t, (unsigned long) IDETAPE_DSC_RW_MAX), (unsigned long) IDETAPE_DSC_RW_MIN); + printk(KERN_INFO "ide-tape: %s <-> %s: %dKBps, %d*%dkB buffer, " + "%dkB pipeline, %lums tDSC%s\n", + drive->name, tape->name, tape->capabilities.speed, + (tape->capabilities.buffer_size * 512) / tape->stage_size, + tape->stage_size / 1024, + tape->max_stages * tape->stage_size / 1024, + tape->best_dsc_rw_frequency * 1000 / HZ, + drive->using_dma ? ", DMA":""); + + idetape_add_settings(drive); +} + +static int idetape_cleanup (ide_drive_t *drive) +{ + idetape_tape_t *tape = drive->driver_data; + int minor = tape->minor; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + if (test_bit (IDETAPE_BUSY, &tape->flags) || drive->usage || + tape->first_stage != NULL || tape->merge_stage_size) { + spin_unlock_irqrestore(&ide_lock, flags); + return 1; + } + idetape_chrdevs[minor].drive = NULL; + spin_unlock_irqrestore(&ide_lock, flags); + DRIVER(drive)->busy = 0; + (void) ide_unregister_subdriver(drive); + drive->driver_data = NULL; + devfs_unregister(tape->de_r); + devfs_unregister(tape->de_n); + kfree (tape); + for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++) + if (idetape_chrdevs[minor].drive != NULL) + return 0; + unregister_chrdev(IDETAPE_MAJOR, "ht"); + idetape_chrdev_present = 0; + return 0; +} + +#ifdef CONFIG_PROC_FS + +static int proc_idetape_read_name + (char *page, char **start, off_t off, int count, int *eof, void *data) +{ + ide_drive_t *drive = (ide_drive_t *) data; + idetape_tape_t *tape = drive->driver_data; + char *out = page; + int len; + + len = sprintf(out, "%s\n", tape->name); + PROC_IDE_READ_RETURN(page, start, off, count, eof, len); +} + +static ide_proc_entry_t idetape_proc[] = { + { "name", S_IFREG|S_IRUGO, proc_idetape_read_name, NULL }, + { NULL, 0, NULL, NULL } +}; + +#else + +#define idetape_proc NULL + +#endif + +int idetape_init (void); +int idetape_reinit(ide_drive_t *drive); + +/* + * IDE subdriver functions, registered with ide.c + */ +static ide_driver_t idetape_driver = { + name: "ide-tape", + version: IDETAPE_VERSION, + media: ide_tape, + busy: 1, +#ifdef CONFIG_IDEDMA_ONLYDISK + supports_dma: 0, +#else + supports_dma: 1, +#endif + supports_dsc_overlap: 1, + cleanup: idetape_cleanup, + standby: NULL, + suspend: NULL, + resume: NULL, + flushcache: NULL, + do_request: idetape_do_request, + end_request: idetape_end_request, + sense: NULL, + error: NULL, + ioctl: idetape_blkdev_ioctl, + open: idetape_blkdev_open, + release: idetape_blkdev_release, + media_change: NULL, + revalidate: NULL, + pre_reset: idetape_pre_reset, + capacity: NULL, + special: NULL, + proc: idetape_proc, + init: idetape_init, + reinit: idetape_reinit, + ata_prebuilder: NULL, + atapi_prebuilder: NULL, +}; + +static ide_module_t idetape_module = { + IDE_DRIVER_MODULE, + idetape_init, + &idetape_driver, + NULL +}; + +/* + * Our character device supporting functions, passed to register_chrdev. + */ +static struct file_operations idetape_fops = { + owner: THIS_MODULE, + read: idetape_chrdev_read, + write: idetape_chrdev_write, + ioctl: idetape_chrdev_ioctl, + open: idetape_chrdev_open, + release: idetape_chrdev_release, +}; + +int idetape_reinit (ide_drive_t *drive) +{ +#if 0 + idetape_tape_t *tape; + int minor, failed = 0, supported = 0; +/* DRIVER(drive)->busy++; */ + MOD_INC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_init\n"); +#endif + if (!idetape_chrdev_present) + for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++ ) + idetape_chrdevs[minor].drive = NULL; + + if ((drive = ide_scan_devices(ide_tape, idetape_driver.name, NULL, failed++)) == NULL) { + ide_register_module(&idetape_module); + MOD_DEC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); +#endif + return 0; + } + if (!idetape_chrdev_present && + register_chrdev(IDETAPE_MAJOR, "ht", &idetape_fops)) { + printk(KERN_ERR "ide-tape: Failed to register character device interface\n"); + MOD_DEC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); +#endif + return -EBUSY; + } + do { + if (!idetape_identify_device(drive, drive->id)) { + printk(KERN_ERR "ide-tape: %s: not supported by this version of ide-tape\n", drive->name); + continue; + } + if (drive->scsi) { + if (strstr(drive->id->model, "OnStream DI-30")) { + printk("ide-tape: ide-scsi emulation is not supported for %s.\n", drive->id->model); + } else { + printk("ide-tape: passing drive %s to ide-scsi emulation.\n", drive->name); + continue; + } + } + tape = (idetape_tape_t *) kmalloc (sizeof (idetape_tape_t), GFP_KERNEL); + if (tape == NULL) { + printk(KERN_ERR "ide-tape: %s: Can't allocate a tape structure\n", drive->name); + continue; + } + if (ide_register_subdriver (drive, &idetape_driver, IDE_SUBDRIVER_VERSION)) { + printk(KERN_ERR "ide-tape: %s: Failed to register the driver with ide.c\n", drive->name); + kfree(tape); + continue; + } + for (minor = 0; idetape_chrdevs[minor].drive != NULL; minor++); + idetape_setup(drive, tape, minor); + idetape_chrdevs[minor].drive = drive; + tape->de_r = + devfs_register (drive->de, "mt", DEVFS_FL_DEFAULT, + HWIF(drive)->major, minor, + S_IFCHR | S_IRUGO | S_IWUGO, + &idetape_fops, NULL); + tape->de_n = + devfs_register (drive->de, "mtn", DEVFS_FL_DEFAULT, + HWIF(drive)->major, minor + 128, + S_IFCHR | S_IRUGO | S_IWUGO, + &idetape_fops, NULL); + devfs_register_tape(tape->de_r); + supported++; + failed--; + } while ((drive = ide_scan_devices(ide_tape, idetape_driver.name, NULL, failed++)) != NULL); + if (!idetape_chrdev_present && !supported) { + devfs_unregister_chrdev(IDETAPE_MAJOR, "ht"); + } else + idetape_chrdev_present = 1; + ide_register_module(&idetape_module); + MOD_DEC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); +#endif + + return 0; +#else + return 1; +#endif +} + +MODULE_DESCRIPTION("ATAPI Streaming TAPE Driver"); +MODULE_LICENSE("GPL"); + +static void __exit idetape_exit (void) +{ + ide_drive_t *drive; + int minor; + + for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++) { + drive = idetape_chrdevs[minor].drive; + if (drive != NULL && idetape_cleanup (drive)) + printk(KERN_ERR "ide-tape: %s: cleanup_module() called while still busy\n", drive->name); + } + ide_unregister_module(&idetape_module); +} + +/* + * idetape_init will register the driver for each tape. + */ +int idetape_init (void) +{ + ide_drive_t *drive; + idetape_tape_t *tape; + int minor, failed = 0, supported = 0; +/* DRIVER(drive)->busy++; */ + MOD_INC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_INC_USE_COUNT in idetape_init\n"); +#endif + if (!idetape_chrdev_present) + for (minor = 0; minor < MAX_HWIFS * MAX_DRIVES; minor++ ) + idetape_chrdevs[minor].drive = NULL; + + if ((drive = ide_scan_devices(ide_tape, idetape_driver.name, NULL, failed++)) == NULL) { + ide_register_module(&idetape_module); + MOD_DEC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); +#endif + return 0; + } + if (!idetape_chrdev_present && + register_chrdev(IDETAPE_MAJOR, "ht", &idetape_fops)) { + printk(KERN_ERR "ide-tape: Failed to register character device interface\n"); + MOD_DEC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); +#endif + return -EBUSY; + } + do { + if (!idetape_identify_device (drive, drive->id)) { + printk(KERN_ERR "ide-tape: %s: not supported by this version of ide-tape\n", drive->name); + continue; + } + if (drive->scsi) { + if (strstr(drive->id->model, "OnStream DI-")) { + printk("ide-tape: ide-scsi emulation is not supported for %s.\n", drive->id->model); + } else { + printk("ide-tape: passing drive %s to ide-scsi emulation.\n", drive->name); + continue; + } + } + tape = (idetape_tape_t *) kmalloc (sizeof (idetape_tape_t), GFP_KERNEL); + if (tape == NULL) { + printk(KERN_ERR "ide-tape: %s: Can't allocate a tape structure\n", drive->name); + continue; + } + if (ide_register_subdriver (drive, &idetape_driver, IDE_SUBDRIVER_VERSION)) { + printk(KERN_ERR "ide-tape: %s: Failed to register the driver with ide.c\n", drive->name); + kfree(tape); + continue; + } + for (minor = 0; idetape_chrdevs[minor].drive != NULL; minor++); + idetape_setup(drive, tape, minor); + idetape_chrdevs[minor].drive = drive; + tape->de_r = + devfs_register (drive->de, "mt", DEVFS_FL_DEFAULT, + HWIF(drive)->major, minor, + S_IFCHR | S_IRUGO | S_IWUGO, + &idetape_fops, NULL); + tape->de_n = + devfs_register (drive->de, "mtn", DEVFS_FL_DEFAULT, + HWIF(drive)->major, minor + 128, + S_IFCHR | S_IRUGO | S_IWUGO, + &idetape_fops, NULL); + devfs_register_tape(tape->de_r); + supported++; + failed--; + } while ((drive = ide_scan_devices(ide_tape, idetape_driver.name, NULL, failed++)) != NULL); + if (!idetape_chrdev_present && !supported) { + unregister_chrdev(IDETAPE_MAJOR, "ht"); + } else + idetape_chrdev_present = 1; + ide_register_module(&idetape_module); + MOD_DEC_USE_COUNT; +#if ONSTREAM_DEBUG + printk(KERN_INFO "ide-tape: MOD_DEC_USE_COUNT in idetape_init\n"); +#endif + return 0; +} + +module_init(idetape_init); +module_exit(idetape_exit); diff -Nru a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-taskfile.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,2784 @@ +/* + * linux/drivers/ide/ide-taskfile.c Version 0.33 April 11, 2002 + * + * Copyright (C) 2000-2002 Michael Cornwell + * Copyright (C) 2000-2002 Andre Hedrick + * Copyright (C) 2001-2002 Klaus Smolin + * IBM Storage Technology Division + * + * The big the bad and the ugly. + * + * Problems to be fixed because of BH interface or the lack therefore. + * + * Fill me in stupid !!! + * + * HOST: + * General refers to the Controller and Driver "pair". + * DATA HANDLER: + * Under the context of Linux it generally refers to an interrupt handler. + * However, it correctly describes the 'HOST' + * DATA BLOCK: + * The amount of data needed to be transfered as predefined in the + * setup of the device. + * STORAGE ATOMIC: + * The 'DATA BLOCK' associated to the 'DATA HANDLER', and can be as + * small as a single sector or as large as the entire command block + * request. + */ + +#include +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DEBUG_TASKFILE 0 /* unset when fixed */ + +#if DEBUG_TASKFILE +#define DTF(x...) printk(x) +#else +#define DTF(x...) +#endif + +#define task_map_rq(rq, flags) ide_map_buffer((rq), (flags)) +#define task_unmap_rq(rq, buf, flags) ide_unmap_buffer((buf), (flags)) + +inline u32 task_read_24 (ide_drive_t *drive) +{ + return (IN_BYTE(IDE_HCYL_REG)<<16) | + (IN_BYTE(IDE_LCYL_REG)<<8) | + IN_BYTE(IDE_SECTOR_REG); +} + +static void ata_bswap_data (void *buffer, int wcount) +{ + u16 *p = buffer; + + while (wcount--) { + *p = *p << 8 | *p >> 8; p++; + *p = *p << 8 | *p >> 8; p++; + } +} + +#if SUPPORT_VLB_SYNC +/* + * Some localbus EIDE interfaces require a special access sequence + * when using 32-bit I/O instructions to transfer data. We call this + * the "vlb_sync" sequence, which consists of three successive reads + * of the sector count register location, with interrupts disabled + * to ensure that the reads all happen together. + */ +static inline void task_vlb_sync (ide_ioreg_t port) +{ + (void) IN_BYTE (port); + (void) IN_BYTE (port); + (void) IN_BYTE (port); +} +#endif /* SUPPORT_VLB_SYNC */ + +/* + * This is used for most PIO data transfers *from* the IDE interface + */ +void ata_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + byte io_32bit; + + /* + * first check if this controller has defined a special function + * for handling polled ide transfers + */ + + if (HWIF(drive)->ideproc) { + HWIF(drive)->ideproc(ideproc_ide_input_data, drive, buffer, wcount); + return; + } + + io_32bit = drive->io_32bit; + + if (io_32bit) { +#if SUPPORT_VLB_SYNC + if (io_32bit & 2) { + unsigned long flags; + local_irq_save(flags); + task_vlb_sync(IDE_NSECTOR_REG); + insl(IDE_DATA_REG, buffer, wcount); + local_irq_restore(flags); + } else +#endif /* SUPPORT_VLB_SYNC */ + insl(IDE_DATA_REG, buffer, wcount); + } else { +#if SUPPORT_SLOW_DATA_PORTS + if (drive->slow) { + unsigned short *ptr = (unsigned short *) buffer; + while (wcount--) { + *ptr++ = inw_p(IDE_DATA_REG); + *ptr++ = inw_p(IDE_DATA_REG); + } + } else +#endif /* SUPPORT_SLOW_DATA_PORTS */ + insw(IDE_DATA_REG, buffer, wcount<<1); + } +} + +/* + * This is used for most PIO data transfers *to* the IDE interface + */ +void ata_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + byte io_32bit; + + if (HWIF(drive)->ideproc) { + HWIF(drive)->ideproc(ideproc_ide_output_data, drive, buffer, wcount); + return; + } + + io_32bit = drive->io_32bit; + + if (io_32bit) { +#if SUPPORT_VLB_SYNC + if (io_32bit & 2) { + unsigned long flags; + local_irq_save(flags); + task_vlb_sync(IDE_NSECTOR_REG); + outsl(IDE_DATA_REG, buffer, wcount); + local_irq_restore(flags); + } else +#endif /* SUPPORT_VLB_SYNC */ + outsl(IDE_DATA_REG, buffer, wcount); + } else { +#if SUPPORT_SLOW_DATA_PORTS + if (drive->slow) { + unsigned short *ptr = (unsigned short *) buffer; + while (wcount--) { + outw_p(*ptr++, IDE_DATA_REG); + outw_p(*ptr++, IDE_DATA_REG); + } + } else +#endif /* SUPPORT_SLOW_DATA_PORTS */ + outsw(IDE_DATA_REG, buffer, wcount<<1); + } +} + +/* + * The following routines are mainly used by the ATAPI drivers. + * + * These routines will round up any request for an odd number of bytes, + * so if an odd bytecount is specified, be sure that there's at least one + * extra byte allocated for the buffer. + */ +void atapi_input_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +{ + if (HWIF(drive)->ideproc) { + HWIF(drive)->ideproc(ideproc_atapi_input_bytes, drive, buffer, bytecount); + return; + } + + ++bytecount; +#if defined(CONFIG_ATARI) || defined(CONFIG_Q40) + if (MACH_IS_ATARI || MACH_IS_Q40) { + /* Atari has a byte-swapped IDE interface */ + insw_swapw(IDE_DATA_REG, buffer, bytecount / 2); + return; + } +#endif /* CONFIG_ATARI */ + ata_input_data (drive, buffer, bytecount / 4); + if ((bytecount & 0x03) >= 2) + insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1); +} + +void atapi_output_bytes (ide_drive_t *drive, void *buffer, unsigned int bytecount) +{ + if (HWIF(drive)->ideproc) { + HWIF(drive)->ideproc(ideproc_atapi_output_bytes, drive, buffer, bytecount); + return; + } + + ++bytecount; +#if defined(CONFIG_ATARI) || defined(CONFIG_Q40) + if (MACH_IS_ATARI || MACH_IS_Q40) { + /* Atari has a byte-swapped IDE interface */ + outsw_swapw(IDE_DATA_REG, buffer, bytecount / 2); + return; + } +#endif /* CONFIG_ATARI */ + ata_output_data (drive, buffer, bytecount / 4); + if ((bytecount & 0x03) >= 2) + outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1); +} + +void taskfile_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + ata_input_data(drive, buffer, wcount); + if (drive->bswap) + ata_bswap_data(buffer, wcount); +} + +void taskfile_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount) +{ + if (drive->bswap) { + ata_bswap_data(buffer, wcount); + ata_output_data(drive, buffer, wcount); + ata_bswap_data(buffer, wcount); + } else { + ata_output_data(drive, buffer, wcount); + } +} + +/* + * Needed for PCI irq sharing + */ +int drive_is_ready (ide_drive_t *drive) +{ + byte stat = 0; + if (drive->waiting_for_dma) + return HWIF(drive)->dmaproc(ide_dma_test_irq, drive); +#if 0 + /* need to guarantee 400ns since last command was issued */ + udelay(1); +#endif + +#ifdef CONFIG_IDEPCI_SHARE_IRQ + /* + * We do a passive status test under shared PCI interrupts on + * cards that truly share the ATA side interrupt, but may also share + * an interrupt with another pci card/device. We make no assumptions + * about possible isa-pnp and pci-pnp issues yet. + */ + if (IDE_CONTROL_REG) + stat = GET_ALTSTAT(); + else +#endif /* CONFIG_IDEPCI_SHARE_IRQ */ + stat = GET_STAT(); /* Note: this may clear a pending IRQ!! */ + + if (stat & BUSY_STAT) + return 0; /* drive busy: definitely not interrupting */ + return 1; /* drive ready: *might* be interrupting */ +} + +/* + * Global for All, and taken from ide-pmac.c + */ +int wait_for_ready (ide_drive_t *drive, int timeout) +{ + byte stat = 0; + + while(--timeout) { + stat = GET_STAT(); + if(!(stat & BUSY_STAT)) { + if (drive->ready_stat == 0) + break; + else if((stat & drive->ready_stat) || (stat & ERR_STAT)) + break; + } + mdelay(1); + } + if((stat & ERR_STAT) || timeout <= 0) { + if (stat & ERR_STAT) { + printk(KERN_ERR "%s: wait_for_ready, error status: %x\n", drive->name, stat); + } + return 1; + } + return 0; +} + +/* + * This routine busy-waits for the drive status to be not "busy". + * It then checks the status for all of the "good" bits and none + * of the "bad" bits, and if all is okay it returns 0. All other + * cases return 1 after invoking ide_error() -- caller should just return. + * + * This routine should get fixed to not hog the cpu during extra long waits.. + * That could be done by busy-waiting for the first jiffy or two, and then + * setting a timer to wake up at half second intervals thereafter, + * until timeout is achieved, before timing out. + */ +int ide_wait_stat (ide_startstop_t *startstop, ide_drive_t *drive, byte good, byte bad, unsigned long timeout) +{ + byte stat; + int i; + unsigned long flags; + + /* bail early if we've exceeded max_failures */ + if (drive->max_failures && (drive->failures > drive->max_failures)) { + *startstop = ide_stopped; + return 1; + } + + udelay(1); /* spec allows drive 400ns to assert "BUSY" */ + if ((stat = GET_STAT()) & BUSY_STAT) { + local_irq_set(flags); + timeout += jiffies; + while ((stat = GET_STAT()) & BUSY_STAT) { + if (time_after(jiffies, timeout)) { + local_irq_restore(flags); + *startstop = DRIVER(drive)->error(drive, "status timeout", stat); + return 1; + } + } + local_irq_restore(flags); + } + /* + * Allow status to settle, then read it again. + * A few rare drives vastly violate the 400ns spec here, + * so we'll wait up to 10usec for a "good" status + * rather than expensively fail things immediately. + * This fix courtesy of Matthew Faupel & Niccolo Rigacci. + */ + for (i = 0; i < 10; i++) { + udelay(1); + if (OK_STAT((stat = GET_STAT()), good, bad)) + return 0; + } + *startstop = DRIVER(drive)->error(drive, "status error", stat); + return 1; +} + +void debug_taskfile (ide_drive_t *drive, ide_task_t *args) +{ +#ifdef CONFIG_IDE_TASK_IOCTL_DEBUG + printk(KERN_INFO "%s: ", drive->name); +// printk("TF.0=x%02x ", args->tfRegister[IDE_DATA_OFFSET]); + printk("TF.1=x%02x ", args->tfRegister[IDE_FEATURE_OFFSET]); + printk("TF.2=x%02x ", args->tfRegister[IDE_NSECTOR_OFFSET]); + printk("TF.3=x%02x ", args->tfRegister[IDE_SECTOR_OFFSET]); + printk("TF.4=x%02x ", args->tfRegister[IDE_LCYL_OFFSET]); + printk("TF.5=x%02x ", args->tfRegister[IDE_HCYL_OFFSET]); + printk("TF.6=x%02x ", args->tfRegister[IDE_SELECT_OFFSET]); + printk("TF.7=x%02x\n", args->tfRegister[IDE_COMMAND_OFFSET]); + printk(KERN_INFO "%s: ", drive->name); +// printk("HTF.0=x%02x ", args->hobRegister[IDE_DATA_OFFSET_HOB]); + printk("HTF.1=x%02x ", args->hobRegister[IDE_FEATURE_OFFSET_HOB]); + printk("HTF.2=x%02x ", args->hobRegister[IDE_NSECTOR_OFFSET_HOB]); + printk("HTF.3=x%02x ", args->hobRegister[IDE_SECTOR_OFFSET_HOB]); + printk("HTF.4=x%02x ", args->hobRegister[IDE_LCYL_OFFSET_HOB]); + printk("HTF.5=x%02x ", args->hobRegister[IDE_HCYL_OFFSET_HOB]); + printk("HTF.6=x%02x ", args->hobRegister[IDE_SELECT_OFFSET_HOB]); + printk("HTF.7=x%02x\n", args->hobRegister[IDE_CONTROL_OFFSET_HOB]); +#endif /* CONFIG_IDE_TASK_IOCTL_DEBUG */ +} + +ide_startstop_t do_rw_taskfile (ide_drive_t *drive, ide_task_t *task) +{ + task_struct_t *taskfile = (task_struct_t *) task->tfRegister; + hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister; + struct hd_driveid *id = drive->id; + byte HIHI = (drive->addressing == 1) ? 0xE0 : 0xEF; + +#ifdef CONFIG_IDE_TASK_IOCTL_DEBUG + void debug_taskfile(drive, task); +#endif /* CONFIG_IDE_TASK_IOCTL_DEBUG */ + + /* ALL Command Block Executions SHALL clear nIEN, unless otherwise */ + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl, IDE_CONTROL_REG); /* clear nIEN */ + SELECT_MASK(HWIF(drive), drive, 0); + + if ((id->command_set_2 & 0x0400) && + (id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + OUT_BYTE(hobfile->feature, IDE_FEATURE_REG); + OUT_BYTE(hobfile->sector_count, IDE_NSECTOR_REG); + OUT_BYTE(hobfile->sector_number, IDE_SECTOR_REG); + OUT_BYTE(hobfile->low_cylinder, IDE_LCYL_REG); + OUT_BYTE(hobfile->high_cylinder, IDE_HCYL_REG); + } + + OUT_BYTE(taskfile->feature, IDE_FEATURE_REG); + OUT_BYTE(taskfile->sector_count, IDE_NSECTOR_REG); + /* refers to number of sectors to transfer */ + OUT_BYTE(taskfile->sector_number, IDE_SECTOR_REG); + /* refers to sector offset or start sector */ + OUT_BYTE(taskfile->low_cylinder, IDE_LCYL_REG); + OUT_BYTE(taskfile->high_cylinder, IDE_HCYL_REG); + + OUT_BYTE((taskfile->device_head & HIHI) | drive->select.all, IDE_SELECT_REG); + if (task->handler != NULL) { + ide_set_handler (drive, task->handler, WAIT_CMD, NULL); + OUT_BYTE(taskfile->command, IDE_COMMAND_REG); + if (task->prehandler != NULL) + return task->prehandler(drive, task->rq); + return ide_started; + } +#if 0 + switch(task->data_phase) { +#ifdef CONFIG_BLK_DEV_IDEDMA + case TASKFILE_OUT_DMAQ: + case TASKFILE_OUT_DMA: + HWIF(drive)->dmaproc(ide_dma_write, drive); + break; + case TASKFILE_IN_DMAQ: + case TASKFILE_IN_DMA: + HWIF(drive)->dmaproc(ide_dma_read, drive); + break; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + default: + if (task->handler == NULL) + return ide_stopped; + ide_set_handler (drive, task->handler, WAIT_WORSTCASE, NULL); + /* Issue the command */ + OUT_BYTE(taskfile->command, IDE_COMMAND_REG); + if (task->prehandler != NULL) + return task->prehandler(drive, HWGROUP(drive)->rq); + } +#else + // if ((rq->cmd == WRITE) && (drive->using_dma)) + /* for dma commands we down set the handler */ + if (drive->using_dma && !(HWIF(drive)->dmaproc(((taskfile->command == WIN_WRITEDMA) || (taskfile->command == WIN_WRITEDMA_EXT)) ? ide_dma_write : ide_dma_read, drive))); +#endif + return ide_started; +} + +#if 0 +/* + * Error reporting, in human readable form (luxurious, but a memory hog). + */ +byte taskfile_dump_status (ide_drive_t *drive, const char *msg, byte stat) +{ + unsigned long flags; + byte err = 0; + + local_irq_set(flags); + printk("%s: %s: status=0x%02x", drive->name, msg, stat); +#if FANCY_STATUS_DUMPS + printk(" { "); + if (stat & BUSY_STAT) + printk("Busy "); + else { + if (stat & READY_STAT) printk("DriveReady "); + if (stat & WRERR_STAT) printk("DeviceFault "); + if (stat & SEEK_STAT) printk("SeekComplete "); + if (stat & DRQ_STAT) printk("DataRequest "); + if (stat & ECC_STAT) printk("CorrectedError "); + if (stat & INDEX_STAT) printk("Index "); + if (stat & ERR_STAT) printk("Error "); + } + printk("}"); +#endif /* FANCY_STATUS_DUMPS */ + printk("\n"); + if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) { + err = GET_ERR(); + printk("%s: %s: error=0x%02x", drive->name, msg, err); +#if FANCY_STATUS_DUMPS + if (drive->media == ide_disk) { + printk(" { "); + if (err & ABRT_ERR) printk("DriveStatusError "); + if (err & ICRC_ERR) printk("%s", (err & ABRT_ERR) ? "BadCRC " : "BadSector "); + if (err & ECC_ERR) printk("UncorrectableError "); + if (err & ID_ERR) printk("SectorIdNotFound "); + if (err & TRK0_ERR) printk("TrackZeroNotFound "); + if (err & MARK_ERR) printk("AddrMarkNotFound "); + printk("}"); + if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR || (err & (ECC_ERR|ID_ERR|MARK_ERR))) { + if ((drive->id->command_set_2 & 0x0400) && + (drive->id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + __u64 sectors = 0; + u32 low = 0, high = 0; + low = task_read_24(drive); + OUT_BYTE(0x80, IDE_CONTROL_REG); + high = task_read_24(drive); + sectors = ((__u64)high << 24) | low; + printk(", LBAsect=%lld", sectors); + } else { + byte cur = IN_BYTE(IDE_SELECT_REG); + if (cur & 0x40) { /* using LBA? */ + printk(", LBAsect=%ld", (unsigned long) + ((cur&0xf)<<24) + |(IN_BYTE(IDE_HCYL_REG)<<16) + |(IN_BYTE(IDE_LCYL_REG)<<8) + | IN_BYTE(IDE_SECTOR_REG)); + } else { + printk(", CHS=%d/%d/%d", + (IN_BYTE(IDE_HCYL_REG)<<8) + + IN_BYTE(IDE_LCYL_REG), + cur & 0xf, + IN_BYTE(IDE_SECTOR_REG)); + } + } + if (HWGROUP(drive)->rq) + printk(", sector=%lu", (__u64) HWGROUP(drive)->rq->sector); + } + } +#endif /* FANCY_STATUS_DUMPS */ + printk("\n"); + } + local_irq_restore(flags); + return err; +} +#endif + +/* + * Clean up after success/failure of an explicit taskfile operation. + */ +void ide_end_taskfile (ide_drive_t *drive, byte stat, byte err) +{ + unsigned long flags; + struct request *rq; + ide_task_t *args; + task_ioreg_t command; + + spin_lock_irqsave(&ide_lock, flags); + rq = HWGROUP(drive)->rq; + spin_unlock_irqrestore(&ide_lock, flags); + args = (ide_task_t *) rq->special; + + command = args->tfRegister[IDE_COMMAND_OFFSET]; + + if (rq->errors == 0) + rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); + + if (args->tf_in_flags.b.data) { + unsigned short data = IN_WORD(IDE_DATA_REG); + args->tfRegister[IDE_DATA_OFFSET] = (data) & 0xFF; + args->hobRegister[IDE_DATA_OFFSET_HOB] = (data >> 8) & 0xFF; + } + args->tfRegister[IDE_ERROR_OFFSET] = err; + args->tfRegister[IDE_NSECTOR_OFFSET] = IN_BYTE(IDE_NSECTOR_REG); + args->tfRegister[IDE_SECTOR_OFFSET] = IN_BYTE(IDE_SECTOR_REG); + args->tfRegister[IDE_LCYL_OFFSET] = IN_BYTE(IDE_LCYL_REG); + args->tfRegister[IDE_HCYL_OFFSET] = IN_BYTE(IDE_HCYL_REG); + args->tfRegister[IDE_SELECT_OFFSET] = IN_BYTE(IDE_SELECT_REG); + args->tfRegister[IDE_STATUS_OFFSET] = stat; + if ((drive->id->command_set_2 & 0x0400) && + (drive->id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + OUT_BYTE(drive->ctl|0x80, IDE_CONTROL_REG_HOB); + args->hobRegister[IDE_FEATURE_OFFSET_HOB] = IN_BYTE(IDE_FEATURE_REG); + args->hobRegister[IDE_NSECTOR_OFFSET_HOB] = IN_BYTE(IDE_NSECTOR_REG); + args->hobRegister[IDE_SECTOR_OFFSET_HOB] = IN_BYTE(IDE_SECTOR_REG); + args->hobRegister[IDE_LCYL_OFFSET_HOB] = IN_BYTE(IDE_LCYL_REG); + args->hobRegister[IDE_HCYL_OFFSET_HOB] = IN_BYTE(IDE_HCYL_REG); + } + +#if 0 +/* taskfile_settings_update(drive, args, command); */ + + if (args->posthandler != NULL) + args->posthandler(drive, args); +#endif + + spin_lock_irqsave(&ide_lock, flags); + blkdev_dequeue_request(rq); + HWGROUP(drive)->rq = NULL; + end_that_request_last(rq); + spin_unlock_irqrestore(&ide_lock, flags); +} + +#if 0 +/* + * try_to_flush_leftover_data() is invoked in response to a drive + * unexpectedly having its DRQ_STAT bit set. As an alternative to + * resetting the drive, this routine tries to clear the condition + * by read a sector's worth of data from the drive. Of course, + * this may not help if the drive is *waiting* for data from *us*. + */ +void task_try_to_flush_leftover_data (ide_drive_t *drive) +{ + int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS; + + if (drive->media != ide_disk) + return; + while (i > 0) { + u32 buffer[16]; + unsigned int wcount = (i > 16) ? 16 : i; + i -= wcount; + taskfile_input_data (drive, buffer, wcount); + } +} + +/* + * taskfile_error() takes action based on the error returned by the drive. + */ +ide_startstop_t taskfile_error (ide_drive_t *drive, const char *msg, byte stat) +{ + struct request *rq; + byte err; + + err = taskfile_dump_status(drive, msg, stat); + if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) + return ide_stopped; + /* retry only "normal" I/O: */ + if (rq->cmd == IDE_DRIVE_TASKFILE) { + rq->errors = 1; + ide_end_taskfile(drive, stat, err); + return ide_stopped; + } + if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */ + rq->errors |= ERROR_RESET; + } else { + if (drive->media == ide_disk && (stat & ERR_STAT)) { + /* err has different meaning on cdrom and tape */ + if (err == ABRT_ERR) { + if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY) + return ide_stopped; /* some newer drives don't support WIN_SPECIFY */ + } else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR)) { + drive->crc_count++; /* UDMA crc error -- just retry the operation */ + } else if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */ + rq->errors = ERROR_MAX; + else if (err & TRK0_ERR) /* help it find track zero */ + rq->errors |= ERROR_RECAL; + } + if ((stat & DRQ_STAT) && rq->cmd != WRITE) + task_try_to_flush_leftover_data(drive); + } + if (GET_STAT() & (BUSY_STAT|DRQ_STAT)) + OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); /* force an abort */ + + if (rq->errors >= ERROR_MAX) { + DRIVER(drive)->end_request(drive, 0); + } else { + if ((rq->errors & ERROR_RESET) == ERROR_RESET) { + ++rq->errors; + return ide_do_reset(drive); + } + if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) + drive->special.b.recalibrate = 1; + ++rq->errors; + } + return ide_stopped; +} +#endif + +/* + * Handler for special commands without a data phase from ide-disk + */ + +/* + * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd. + */ +ide_startstop_t set_multmode_intr (ide_drive_t *drive) +{ + byte stat; + + if (OK_STAT(stat=GET_STAT(),READY_STAT,BAD_STAT)) { + drive->mult_count = drive->mult_req; + } else { + drive->mult_req = drive->mult_count = 0; + drive->special.b.recalibrate = 1; + (void) ide_dump_status(drive, "set_multmode", stat); + } + return ide_stopped; +} + +/* + * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd. + */ +ide_startstop_t set_geometry_intr (ide_drive_t *drive) +{ + int retries = 5; + byte stat; + + while (((stat = GET_STAT()) & BUSY_STAT) && retries--) + udelay(10); + + if (OK_STAT(stat, READY_STAT,BAD_STAT)) + return ide_stopped; + + if (stat & (ERR_STAT|DRQ_STAT)) + return DRIVER(drive)->error(drive, "set_geometry_intr", stat); + + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &set_geometry_intr, WAIT_CMD, NULL); + return ide_started; +} + +/* + * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd. + */ +ide_startstop_t recal_intr (ide_drive_t *drive) +{ + byte stat = GET_STAT(); + + if (!OK_STAT(stat,READY_STAT,BAD_STAT)) + return DRIVER(drive)->error(drive, "recal_intr", stat); + return ide_stopped; +} + +/* + * Handler for commands without a data phase + */ +ide_startstop_t task_no_data_intr (ide_drive_t *drive) +{ + ide_task_t *args = HWGROUP(drive)->rq->special; + byte stat = GET_STAT(); + + local_irq_enable(); + if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { + DTF("%s: command opcode 0x%02x\n", drive->name, + args->tfRegister[IDE_COMMAND_OFFSET]); + return DRIVER(drive)->error(drive, "task_no_data_intr", stat); + /* calls ide_end_drive_cmd */ + } + if (args) + ide_end_drive_cmd (drive, stat, GET_ERR()); + + return ide_stopped; +} + +/* + * Handler for command with PIO data-in phase, READ + */ +/* + * FIXME before 2.4 enable ... + * DATA integrity issue upon error. + */ +ide_startstop_t task_in_intr (ide_drive_t *drive) +{ + byte stat = GET_STAT(); + struct request *rq = HWGROUP(drive)->rq; + char *pBuf = NULL; + unsigned long flags; + + if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) { + if (stat & (ERR_STAT|DRQ_STAT)) { +#if 0 + DTF("%s: attempting to recover last " \ + "sector counter status=0x%02x\n", + drive->name, stat); + /* + * Expect a BUG BOMB if we attempt to rewind the + * offset in the BH aka PAGE in the current BLOCK + * segment. This is different than the HOST segment. + */ +#endif + if (!rq->bio) + rq->current_nr_sectors++; + return DRIVER(drive)->error(drive, "task_in_intr", stat); + } + if (!(stat & BUSY_STAT)) { + DTF("task_in_intr to Soon wait for next interrupt\n"); + if (HWGROUP(drive)->handler == NULL) + ide_set_handler(drive, &task_in_intr, WAIT_CMD, NULL); + return ide_started; + } + } +#if 0 + + /* + * Holding point for a brain dump of a thought :-/ + */ + + if (!OK_STAT(stat,DRIVE_READY,drive->bad_wstat)) { + DTF("%s: READ attempting to recover last " \ + "sector counter status=0x%02x\n", + drive->name, stat); + rq->current_nr_sectors++; + return DRIVER(drive)->error(drive, "task_in_intr", stat); + } + if (!rq->current_nr_sectors) + if (!DRIVER(drive)->end_request(drive, 1)) + return ide_stopped; + + if (--rq->current_nr_sectors <= 0) + if (!DRIVER(drive)->end_request(drive, 1)) + return ide_stopped; +#endif + + pBuf = task_map_rq(rq, &flags); + DTF("Read: %p, rq->current_nr_sectors: %d, stat: %02x\n", + pBuf, (int) rq->current_nr_sectors, stat); + taskfile_input_data(drive, pBuf, SECTOR_WORDS); + task_unmap_rq(rq, pBuf, &flags); + /* + * FIXME :: We really can not legally get a new page/bh + * regardless, if this is the end of our segment. + * BH walking or segment can only be updated after we have a good + * GET_STAT(); return. + */ + if (--rq->current_nr_sectors <= 0) + if (!DRIVER(drive)->end_request(drive, 1)) + return ide_stopped; + /* + * ERM, it is techincally legal to leave/exit here but it makes + * a mess of the code ... + */ + if (HWGROUP(drive)->handler == NULL) + ide_set_handler(drive, &task_in_intr, WAIT_CMD, NULL); + return ide_started; +} + +#undef ALTSTAT_SCREW_UP + +#ifdef ALTSTAT_SCREW_UP +/* + * (ks/hs): Poll Alternate Status Register to ensure + * that drive is not busy. + */ +byte altstat_multi_busy (ide_drive_t *drive, byte stat, const char *msg) +{ + int i; + + DTF("multi%s: ASR = %x\n", msg, stat); + if (stat & BUSY_STAT) { + /* (ks/hs): FIXME: Replace hard-coded 100, error handling? */ + for (i=0; i<100; i++) { + stat = GET_ALTSTAT(); + if ((stat & BUSY_STAT) == 0) + break; + } + } + /* + * (ks/hs): Read Status AFTER Alternate Status Register + */ + return(GET_STAT()); +} + +/* + * (ks/hs): Poll Alternate status register to wait for drive + * to become ready for next transfer + */ +byte altstat_multi_poll (ide_drive_t *drive, byte stat, const char *msg) +{ + + /* (ks/hs): FIXME: Error handling, time-out? */ + while (stat & BUSY_STAT) + stat = GET_ALTSTAT(); + DTF("multi%s: nsect=1, ASR = %x\n", msg, stat); + return(GET_STAT()); /* (ks/hs): Clear pending IRQ */ +} +#endif /* ALTSTAT_SCREW_UP */ + +/* + * Handler for command with Read Multiple + */ +ide_startstop_t task_mulin_intr (ide_drive_t *drive) +{ +#ifdef ALTSTAT_SCREW_UP + byte stat = altstat_multi_busy(drive, GET_ALTSTAT(), "read"); +#else + byte stat = GET_STAT(); +#endif /* ALTSTAT_SCREW_UP */ + struct request *rq = HWGROUP(drive)->rq; + char *pBuf = NULL; + unsigned int msect = drive->mult_count; + unsigned int nsect; + unsigned long flags; + + if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) { + if (stat & (ERR_STAT|DRQ_STAT)) { + if (!rq->bio) { + rq->current_nr_sectors += drive->mult_count; + /* + * NOTE: could rewind beyond beginning :-/ + */ + } else { + printk("%s: MULTI-READ assume all data " \ + "transfered is bad status=0x%02x\n", + drive->name, stat); + } + return DRIVER(drive)->error(drive, "task_mulin_intr", stat); + } + /* no data yet, so wait for another interrupt */ + if (HWGROUP(drive)->handler == NULL) + ide_set_handler(drive, &task_mulin_intr, WAIT_CMD, NULL); + return ide_started; + } + +#ifdef ALTSTAT_SCREW_UP + /* + * Screw the request we do not support bad data-phase setups! + * Either read and learn the ATA standard or crash yourself! + */ + if (!msect) { + /* + * (ks/hs): Drive supports multi-sector transfer, + * drive->mult_count was not set + */ + nsect = 1; + while (rq->current_nr_sectors) { + pBuf = task_map_rq(rq, &flags); + DTF("Multiread: %p, nsect: %d, " \ + "rq->current_nr_sectors: %ld\n", + pBuf, nsect, rq->current_nr_sectors); +// rq->current_nr_sectors -= nsect; + taskfile_input_data(drive, pBuf, nsect * SECTOR_WORDS); + task_unmap_rq(rq, pBuf, &flags); + rq->errors = 0; + rq->current_nr_sectors -= nsect; + stat = altstat_multi_poll(drive, GET_ALTSTAT(), "read"); + } + DRIVER(drive)->end_request(drive, 1); + return ide_stopped; + } +#endif /* ALTSTAT_SCREW_UP */ + + do { + nsect = rq->current_nr_sectors; + if (nsect > msect) + nsect = msect; + pBuf = task_map_rq(rq, &flags); + DTF("Multiread: %p, nsect: %d, msect: %d, " \ + " rq->current_nr_sectors: %d\n", + pBuf, nsect, msect, rq->current_nr_sectors); +// rq->current_nr_sectors -= nsect; +// msect -= nsect; + taskfile_input_data(drive, pBuf, nsect * SECTOR_WORDS); + task_unmap_rq(rq, pBuf, &flags); + rq->errors = 0; + rq->current_nr_sectors -= nsect; + msect -= nsect; + /* + * FIXME :: We really can not legally get a new page/bh + * regardless, if this is the end of our segment. + * BH walking or segment can only be updated after we have a + * good GET_STAT(); return. + */ + if (!rq->current_nr_sectors) { + if (!DRIVER(drive)->end_request(drive, 1)) + return ide_stopped; + } + } while (msect); + if (HWGROUP(drive)->handler == NULL) + ide_set_handler(drive, &task_mulin_intr, WAIT_CMD, NULL); + return ide_started; +} + +/* + * VERIFY ME before 2.4 ... unexpected race is possible based on details + * RMK with 74LS245/373/374 TTL buffer logic because of passthrough. + */ +ide_startstop_t pre_task_out_intr (ide_drive_t *drive, struct request *rq) +{ + char *pBuf = NULL; + unsigned long flags; + ide_startstop_t startstop; + + if (ide_wait_stat(&startstop, drive, DATA_READY, + drive->bad_wstat, WAIT_DRQ)) { + printk(KERN_ERR "%s: no DRQ after issuing %s\n", + drive->name, + drive->addressing ? "WRITE_EXT" : "WRITE"); + return startstop; + } + /* For Write_sectors we need to stuff the first sector */ + pBuf = task_map_rq(rq, &flags); +// rq->current_nr_sectors--; + taskfile_output_data(drive, pBuf, SECTOR_WORDS); + rq->current_nr_sectors--; + /* + * WARNING :: Interrupt could happen instantly :-/ + */ + task_unmap_rq(rq, pBuf, &flags); + return ide_started; +} + +/* + * Handler for command with PIO data-out phase WRITE + * + * WOOHOO this is a CORRECT STATE DIAGRAM NOW, + */ +ide_startstop_t task_out_intr (ide_drive_t *drive) +{ + byte stat = GET_STAT(); + struct request *rq = HWGROUP(drive)->rq; + char *pBuf = NULL; + unsigned long flags; + + if (!OK_STAT(stat,DRIVE_READY,drive->bad_wstat)) { + DTF("%s: WRITE attempting to recover last " \ + "sector counter status=0x%02x\n", + drive->name, stat); + rq->current_nr_sectors++; + return DRIVER(drive)->error(drive, "task_out_intr", stat); + } + /* + * Safe to update request for partial completions. + * We have a good STATUS CHECK!!! + */ + if (!rq->current_nr_sectors) + if (!DRIVER(drive)->end_request(drive, 1)) + return ide_stopped; + if ((rq->current_nr_sectors==1) ^ (stat & DRQ_STAT)) { + rq = HWGROUP(drive)->rq; + pBuf = task_map_rq(rq, &flags); + DTF("write: %p, rq->current_nr_sectors: %d\n", + pBuf, (int) rq->current_nr_sectors); +// rq->current_nr_sectors--; + taskfile_output_data(drive, pBuf, SECTOR_WORDS); + task_unmap_rq(rq, pBuf, &flags); + rq->errors = 0; + rq->current_nr_sectors--; + } + if (HWGROUP(drive)->handler == NULL) + ide_set_handler(drive, &task_out_intr, WAIT_CMD, NULL); + return ide_started; +} + +ide_startstop_t pre_task_mulout_intr (ide_drive_t *drive, struct request *rq) +{ + ide_task_t *args = rq->special; + ide_startstop_t startstop; + +#if 0 + /* + * assign private copy for multi-write + */ + memcpy(&HWGROUP(drive)->wrq, rq, sizeof(struct request)); +#endif + + if (ide_wait_stat(&startstop, drive, DATA_READY, + drive->bad_wstat, WAIT_DRQ)) { + printk(KERN_ERR "%s: no DRQ after issuing %s\n", + drive->name, + drive->addressing ? "MULTWRITE_EXT" : "MULTWRITE"); + return startstop; + } +#if 0 + if (wait_for_ready(drive, 100)) + IDE_DEBUG(__LINE__); //BUG(); +#else + if (!(drive_is_ready(drive))) { + int i; + for (i=0; i<100; i++) { + if (drive_is_ready(drive)) + break; + } + } +#endif + /* + * WARNING :: if the drive as not acked good status we may not + * move the DATA-TRANSFER T-Bar as BSY != 0. + */ + return args->handler(drive); +} + +/* + * FIXME before enabling in 2.4 ... DATA integrity issue upon error. + */ +/* + * Handler for command write multiple + * Called directly from execute_drive_cmd for the first bunch of sectors, + * afterwards only by the ISR + */ +ide_startstop_t task_mulout_intr (ide_drive_t *drive) +{ +#ifdef ALTSTAT_SCREW_UP + byte stat = altstat_multi_busy(drive, GET_ALTSTAT(), "write"); +#else + byte stat = GET_STAT(); +#endif /* ALTSTAT_SCREW_UP */ + + struct request *rq = HWGROUP(drive)->rq; + char *pBuf = NULL; + ide_startstop_t startstop = ide_stopped; + unsigned int msect = drive->mult_count; + unsigned int nsect; + unsigned long flags; + + /* + * (ks/hs): Handle last IRQ on multi-sector transfer, + * occurs after all data was sent in this chunk + */ + if (rq->current_nr_sectors == 0) { + if (stat & (ERR_STAT|DRQ_STAT)) { + if (!rq->bio) { + rq->current_nr_sectors += drive->mult_count; + /* + * NOTE: could rewind beyond beginning :-/ + */ + } else { + printk("%s: MULTI-WRITE assume all data " \ + "transfered is bad status=0x%02x\n", + drive->name, stat); + } + return DRIVER(drive)->error(drive, "task_mulout_intr", stat); + } + if (!rq->bio) + DRIVER(drive)->end_request(drive, 1); + return startstop; + } + /* + * DON'T be lazy code the above and below togather !!! + */ + if (!OK_STAT(stat,DATA_READY,BAD_R_STAT)) { + if (stat & (ERR_STAT|DRQ_STAT)) { + if (!rq->bio) { + rq->current_nr_sectors += drive->mult_count; + /* + * NOTE: could rewind beyond beginning :-/ + */ + } else { + printk("%s: MULTI-WRITE assume all data " \ + "transfered is bad status=0x%02x\n", + drive->name, stat); + } + return DRIVER(drive)->error(drive, "task_mulout_intr", stat); + } + /* no data yet, so wait for another interrupt */ + if (HWGROUP(drive)->handler == NULL) + ide_set_handler(drive, &task_mulout_intr, WAIT_CMD, NULL); + return ide_started; + } + + if (HWGROUP(drive)->handler != NULL) { + unsigned long lflags; + spin_lock_irqsave(&ide_lock, lflags); + HWGROUP(drive)->handler = NULL; + del_timer(&HWGROUP(drive)->timer); + spin_unlock_irqrestore(&ide_lock, lflags); + } + +#ifdef ALTSTAT_SCREW_UP + /* + * Screw the request we do not support bad data-phase setups! + * Either read and learn the ATA standard or crash yourself! + */ + if (!msect) { + nsect = 1; + while (rq->current_nr_sectors) { + pBuf = task_map_rq(rq, &flags); + DTF("Multiwrite: %p, nsect: %d, " \ + "rq->current_nr_sectors: %d\n", + pBuf, nsect, rq->current_nr_sectors); +// rq->current_nr_sectors -= nsect; + taskfile_output_data(drive, pBuf, nsect * SECTOR_WORDS); + task_unmap_rq(pBuf, &flags); + rq->errors = 0; + rq->current_nr_sectors -= nsect; + stat = altstat_multi_poll(drive, GET_ALTSTAT(), "write"); + } + DRIVER(drive)->end_request(drive, 1); + return ide_stopped; + } +#endif /* ALTSTAT_SCREW_UP */ + + do { + nsect = rq->current_nr_sectors; + if (nsect > msect) + nsect = msect; + pBuf = task_map_rq(rq, &flags); + DTF("Multiwrite: %p, nsect: %d, msect: %d, " \ + "rq->current_nr_sectors: %ld\n", + pBuf, nsect, msect, rq->current_nr_sectors); + msect -= nsect; +// rq->current_nr_sectors -= nsect; + taskfile_output_data(drive, pBuf, nsect * SECTOR_WORDS); + task_unmap_rq(rq, pBuf, &flags); + rq->current_nr_sectors -= nsect; + /* + * FIXME :: We really can not legally get a new page/bh + * regardless, if this is the end of our segment. + * BH walking or segment can only be updated after we + * have a good GET_STAT(); return. + */ + if (!rq->current_nr_sectors) { + if (!DRIVER(drive)->end_request(drive, 1)) + if (!rq->bio) + return ide_stopped; + } + } while (msect); + rq->errors = 0; + if (HWGROUP(drive)->handler == NULL) + ide_set_handler(drive, &task_mulout_intr, WAIT_CMD, NULL); + return ide_started; +} + +/* Called by internal to feature out type of command being called */ +ide_pre_handler_t * ide_pre_handler_parser (struct hd_drive_task_hdr *taskfile, struct hd_drive_hob_hdr *hobfile) +{ + switch(taskfile->command) { + /* IDE_DRIVE_TASK_RAW_WRITE */ + case CFA_WRITE_MULTI_WO_ERASE: + case WIN_MULTWRITE: + case WIN_MULTWRITE_EXT: + return &pre_task_mulout_intr; + + /* IDE_DRIVE_TASK_OUT */ + case WIN_WRITE: + case WIN_WRITE_EXT: + case WIN_WRITE_VERIFY: + case WIN_WRITE_BUFFER: + case CFA_WRITE_SECT_WO_ERASE: + case WIN_DOWNLOAD_MICROCODE: + return &pre_task_out_intr; + /* IDE_DRIVE_TASK_OUT */ + case WIN_SMART: + if (taskfile->feature == SMART_WRITE_LOG_SECTOR) + return &pre_task_out_intr; + case WIN_WRITEDMA: + case WIN_WRITEDMA_QUEUED: + case WIN_WRITEDMA_EXT: + case WIN_WRITEDMA_QUEUED_EXT: + /* IDE_DRIVE_TASK_OUT */ + default: + break; + } + return(NULL); +} + +/* Called by internal to feature out type of command being called */ +ide_handler_t * ide_handler_parser (struct hd_drive_task_hdr *taskfile, struct hd_drive_hob_hdr *hobfile) +{ + switch(taskfile->command) { + case WIN_IDENTIFY: + case WIN_PIDENTIFY: + case CFA_TRANSLATE_SECTOR: + case WIN_READ_BUFFER: + case WIN_READ: + case WIN_READ_EXT: + return &task_in_intr; + case WIN_SECURITY_DISABLE: + case WIN_SECURITY_ERASE_UNIT: + case WIN_SECURITY_SET_PASS: + case WIN_SECURITY_UNLOCK: + case WIN_DOWNLOAD_MICROCODE: + case CFA_WRITE_SECT_WO_ERASE: + case WIN_WRITE_BUFFER: + case WIN_WRITE_VERIFY: + case WIN_WRITE: + case WIN_WRITE_EXT: + return &task_out_intr; + case WIN_MULTREAD: + case WIN_MULTREAD_EXT: + return &task_mulin_intr; + case CFA_WRITE_MULTI_WO_ERASE: + case WIN_MULTWRITE: + case WIN_MULTWRITE_EXT: + return &task_mulout_intr; + case WIN_SMART: + switch(taskfile->feature) { + case SMART_READ_VALUES: + case SMART_READ_THRESHOLDS: + case SMART_READ_LOG_SECTOR: + return &task_in_intr; + case SMART_WRITE_LOG_SECTOR: + return &task_out_intr; + default: + return &task_no_data_intr; + } + case CFA_REQ_EXT_ERROR_CODE: + case CFA_ERASE_SECTORS: + case WIN_VERIFY: + case WIN_VERIFY_EXT: + case WIN_SEEK: + return &task_no_data_intr; + case WIN_SPECIFY: + return &set_geometry_intr; + case WIN_RECAL: + // case WIN_RESTORE: + return &recal_intr; + case WIN_NOP: + case WIN_DIAGNOSE: + case WIN_FLUSH_CACHE: + case WIN_FLUSH_CACHE_EXT: + case WIN_STANDBYNOW1: + case WIN_STANDBYNOW2: + case WIN_SLEEPNOW1: + case WIN_SLEEPNOW2: + case WIN_SETIDLE1: + case WIN_CHECKPOWERMODE1: + case WIN_CHECKPOWERMODE2: + case WIN_GETMEDIASTATUS: + case WIN_MEDIAEJECT: + return &task_no_data_intr; + case WIN_SETMULT: + return &set_multmode_intr; + case WIN_READ_NATIVE_MAX: + case WIN_SET_MAX: + case WIN_READ_NATIVE_MAX_EXT: + case WIN_SET_MAX_EXT: + case WIN_SECURITY_ERASE_PREPARE: + case WIN_SECURITY_FREEZE_LOCK: + case WIN_DOORLOCK: + case WIN_DOORUNLOCK: + case WIN_SETFEATURES: + return &task_no_data_intr; + case DISABLE_SEAGATE: + case EXABYTE_ENABLE_NEST: + return &task_no_data_intr; +#ifdef CONFIG_BLK_DEV_IDEDMA + case WIN_READDMA: + case WIN_IDENTIFY_DMA: + case WIN_READDMA_QUEUED: + case WIN_READDMA_EXT: + case WIN_READDMA_QUEUED_EXT: + case WIN_WRITEDMA: + case WIN_WRITEDMA_QUEUED: + case WIN_WRITEDMA_EXT: + case WIN_WRITEDMA_QUEUED_EXT: +#endif + case WIN_FORMAT: + case WIN_INIT: + case WIN_DEVICE_RESET: + case WIN_QUEUED_SERVICE: + case WIN_PACKETCMD: + default: + return(NULL); + } +} + +ide_post_handler_t * ide_post_handler_parser (struct hd_drive_task_hdr *taskfile, struct hd_drive_hob_hdr *hobfile) +{ + switch(taskfile->command) { + case WIN_SPECIFY: /* set_geometry_intr */ + case WIN_RESTORE: /* recal_intr */ + case WIN_SETMULT: /* set_multmode_intr */ + default: + return(NULL); + } +} + +/* Called by ioctl to feature out type of command being called */ +int ide_cmd_type_parser (ide_task_t *args) +{ + struct hd_drive_task_hdr *taskfile = (struct hd_drive_task_hdr *) args->tfRegister; + struct hd_drive_hob_hdr *hobfile = (struct hd_drive_hob_hdr *) args->hobRegister; + + args->prehandler = ide_pre_handler_parser(taskfile, hobfile); + args->handler = ide_handler_parser(taskfile, hobfile); + args->posthandler = ide_post_handler_parser(taskfile, hobfile); + + switch(args->tfRegister[IDE_COMMAND_OFFSET]) { + case WIN_IDENTIFY: + case WIN_PIDENTIFY: + return IDE_DRIVE_TASK_IN; + case CFA_TRANSLATE_SECTOR: + case WIN_READ: + case WIN_READ_EXT: + case WIN_READ_BUFFER: + return IDE_DRIVE_TASK_IN; + case WIN_WRITE: + case WIN_WRITE_EXT: + case WIN_WRITE_VERIFY: + case WIN_WRITE_BUFFER: + case CFA_WRITE_SECT_WO_ERASE: + case WIN_DOWNLOAD_MICROCODE: + return IDE_DRIVE_TASK_RAW_WRITE; + case WIN_MULTREAD: + case WIN_MULTREAD_EXT: + return IDE_DRIVE_TASK_IN; + case CFA_WRITE_MULTI_WO_ERASE: + case WIN_MULTWRITE: + case WIN_MULTWRITE_EXT: + return IDE_DRIVE_TASK_RAW_WRITE; + case WIN_SECURITY_DISABLE: + case WIN_SECURITY_ERASE_UNIT: + case WIN_SECURITY_SET_PASS: + case WIN_SECURITY_UNLOCK: + return IDE_DRIVE_TASK_OUT; + case WIN_SMART: + args->tfRegister[IDE_LCYL_OFFSET] = SMART_LCYL_PASS; + args->tfRegister[IDE_HCYL_OFFSET] = SMART_HCYL_PASS; + switch(args->tfRegister[IDE_FEATURE_OFFSET]) { + case SMART_READ_VALUES: + case SMART_READ_THRESHOLDS: + case SMART_READ_LOG_SECTOR: + return IDE_DRIVE_TASK_IN; + case SMART_WRITE_LOG_SECTOR: + return IDE_DRIVE_TASK_OUT; + default: + return IDE_DRIVE_TASK_NO_DATA; + } +#ifdef CONFIG_BLK_DEV_IDEDMA + case WIN_READDMA: + case WIN_IDENTIFY_DMA: + case WIN_READDMA_QUEUED: + case WIN_READDMA_EXT: + case WIN_READDMA_QUEUED_EXT: + return IDE_DRIVE_TASK_IN; + case WIN_WRITEDMA: + case WIN_WRITEDMA_QUEUED: + case WIN_WRITEDMA_EXT: + case WIN_WRITEDMA_QUEUED_EXT: + return IDE_DRIVE_TASK_RAW_WRITE; +#endif + case WIN_SETFEATURES: + switch(args->tfRegister[IDE_FEATURE_OFFSET]) { + case SETFEATURES_EN_8BIT: + case SETFEATURES_EN_WCACHE: + return IDE_DRIVE_TASK_NO_DATA; + case SETFEATURES_XFER: + return IDE_DRIVE_TASK_SET_XFER; + case SETFEATURES_DIS_DEFECT: + case SETFEATURES_EN_APM: + case SETFEATURES_DIS_MSN: + case SETFEATURES_DIS_RETRY: + case SETFEATURES_EN_AAM: + case SETFEATURES_RW_LONG: + case SETFEATURES_SET_CACHE: + case SETFEATURES_DIS_RLA: + case SETFEATURES_EN_RI: + case SETFEATURES_EN_SI: + case SETFEATURES_DIS_RPOD: + case SETFEATURES_DIS_WCACHE: + case SETFEATURES_EN_DEFECT: + case SETFEATURES_DIS_APM: + case SETFEATURES_EN_ECC: + case SETFEATURES_EN_MSN: + case SETFEATURES_EN_RETRY: + case SETFEATURES_EN_RLA: + case SETFEATURES_PREFETCH: + case SETFEATURES_4B_RW_LONG: + case SETFEATURES_DIS_AAM: + case SETFEATURES_EN_RPOD: + case SETFEATURES_DIS_RI: + case SETFEATURES_DIS_SI: + default: + return IDE_DRIVE_TASK_NO_DATA; + } + case WIN_NOP: + case CFA_REQ_EXT_ERROR_CODE: + case CFA_ERASE_SECTORS: + case WIN_VERIFY: + case WIN_VERIFY_EXT: + case WIN_SEEK: + case WIN_SPECIFY: + case WIN_RESTORE: + case WIN_DIAGNOSE: + case WIN_FLUSH_CACHE: + case WIN_FLUSH_CACHE_EXT: + case WIN_STANDBYNOW1: + case WIN_STANDBYNOW2: + case WIN_SLEEPNOW1: + case WIN_SLEEPNOW2: + case WIN_SETIDLE1: + case DISABLE_SEAGATE: + case WIN_CHECKPOWERMODE1: + case WIN_CHECKPOWERMODE2: + case WIN_GETMEDIASTATUS: + case WIN_MEDIAEJECT: + case WIN_SETMULT: + case WIN_READ_NATIVE_MAX: + case WIN_SET_MAX: + case WIN_READ_NATIVE_MAX_EXT: + case WIN_SET_MAX_EXT: + case WIN_SECURITY_ERASE_PREPARE: + case WIN_SECURITY_FREEZE_LOCK: + case EXABYTE_ENABLE_NEST: + case WIN_DOORLOCK: + case WIN_DOORUNLOCK: + return IDE_DRIVE_TASK_NO_DATA; + case WIN_FORMAT: + case WIN_INIT: + case WIN_DEVICE_RESET: + case WIN_QUEUED_SERVICE: + case WIN_PACKETCMD: + default: + return IDE_DRIVE_TASK_INVALID; + } +} + +/* + * NOTICE: This is additions from IBM to provide a discrete interface, + * for selective taskregister access operations. Nice JOB Klaus!!! + * Glad to be able to work and co-develop this with you and IBM. + */ +ide_startstop_t flagged_taskfile (ide_drive_t *drive, ide_task_t *task) +{ + task_struct_t *taskfile = (task_struct_t *) task->tfRegister; + hob_struct_t *hobfile = (hob_struct_t *) task->hobRegister; + struct hd_driveid *id = drive->id; +#if DEBUG_TASKFILE + byte status; +#endif + + +#ifdef CONFIG_IDE_TASK_IOCTL_DEBUG + void debug_taskfile(drive, task); +#endif /* CONFIG_IDE_TASK_IOCTL_DEBUG */ + + /* + * (ks) Check taskfile in/out flags. + * If set, then execute as it is defined. + * If not set, then define default settings. + * The default values are: + * write and read all taskfile registers (except data) + * write and read the hob registers (sector,nsector,lcyl,hcyl) + */ + if (task->tf_out_flags.all == 0) { + task->tf_out_flags.all = IDE_TASKFILE_STD_OUT_FLAGS; + if ((id->command_set_2 & 0x0400) && + (id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + task->tf_out_flags.all |= (IDE_HOB_STD_OUT_FLAGS << 8); + } + } + + if (task->tf_in_flags.all == 0) { + task->tf_in_flags.all = IDE_TASKFILE_STD_IN_FLAGS; + if ((id->command_set_2 & 0x0400) && + (id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + task->tf_in_flags.all |= (IDE_HOB_STD_IN_FLAGS << 8); + } + } + + /* ALL Command Block Executions SHALL clear nIEN, unless otherwise */ + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl, IDE_CONTROL_REG); /* clear nIEN */ + SELECT_MASK(HWIF(drive), drive, 0); + +#if DEBUG_TASKFILE + status = GET_STAT(); + if (status & 0x80) { + printk("flagged_taskfile -> Bad status. Status = %02x. wait 100 usec ...\n", status); + udelay(100); + status = GET_STAT(); + printk("flagged_taskfile -> Status = %02x\n", status); + } +#endif + + if (task->tf_out_flags.b.data) { + unsigned short data = taskfile->data + (hobfile->data << 8); + OUT_WORD(data, IDE_DATA_REG); + } + + /* (ks) send hob registers first */ + if (task->tf_out_flags.b.nsector_hob) + OUT_BYTE(hobfile->sector_count, IDE_NSECTOR_REG); + if (task->tf_out_flags.b.sector_hob) + OUT_BYTE(hobfile->sector_number, IDE_SECTOR_REG); + if (task->tf_out_flags.b.lcyl_hob) + OUT_BYTE(hobfile->low_cylinder, IDE_LCYL_REG); + if (task->tf_out_flags.b.hcyl_hob) + OUT_BYTE(hobfile->high_cylinder, IDE_HCYL_REG); + + /* (ks) Send now the standard registers */ + if (task->tf_out_flags.b.error_feature) + OUT_BYTE(taskfile->feature, IDE_FEATURE_REG); + /* refers to number of sectors to transfer */ + if (task->tf_out_flags.b.nsector) + OUT_BYTE(taskfile->sector_count, IDE_NSECTOR_REG); + /* refers to sector offset or start sector */ + if (task->tf_out_flags.b.sector) + OUT_BYTE(taskfile->sector_number, IDE_SECTOR_REG); + if (task->tf_out_flags.b.lcyl) + OUT_BYTE(taskfile->low_cylinder, IDE_LCYL_REG); + if (task->tf_out_flags.b.hcyl) + OUT_BYTE(taskfile->high_cylinder, IDE_HCYL_REG); + + /* + * (ks) In the flagged taskfile approch, we will used all specified + * registers and the register value will not be changed. Except the + * select bit (master/slave) in the drive_head register. We must make + * sure that the desired drive is selected. + */ + OUT_BYTE(taskfile->device_head | drive->select.all, IDE_SELECT_REG); + switch(task->data_phase) { + + case TASKFILE_OUT_DMAQ: + case TASKFILE_OUT_DMA: + HWIF(drive)->dmaproc(ide_dma_write, drive); + break; + + case TASKFILE_IN_DMAQ: + case TASKFILE_IN_DMA: + HWIF(drive)->dmaproc(ide_dma_read, drive); + break; + + default: + if (task->handler == NULL) + return ide_stopped; + + ide_set_handler (drive, task->handler, WAIT_WORSTCASE, NULL); + /* Issue the command */ + OUT_BYTE(taskfile->command, IDE_COMMAND_REG); + if (task->prehandler != NULL) + return task->prehandler(drive, HWGROUP(drive)->rq); + } + + return ide_started; +} + +ide_startstop_t flagged_task_no_data_intr (ide_drive_t *drive) +{ + byte stat = GET_STAT(); + + local_irq_enable(); + + if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { + if (stat & ERR_STAT) { + return DRIVER(drive)->error(drive, "flagged_task_no_data_intr", stat); + } + /* + * (ks) Unexpected ATA data phase detected. + * This should not happen. But, it can ! + * I am not sure, which function is best to clean up + * this situation. I choose: ide_error(...) + */ + return DRIVER(drive)->error(drive, "flagged_task_no_data_intr (unexpected phase)", stat); + } + + ide_end_drive_cmd (drive, stat, GET_ERR()); + + return ide_stopped; +} + +/* + * Handler for command with PIO data-in phase + */ +ide_startstop_t flagged_task_in_intr (ide_drive_t *drive) +{ + byte stat = GET_STAT(); + struct request *rq = HWGROUP(drive)->rq; + char *pBuf = NULL; + int retries = 5; + + if (rq->current_nr_sectors == 0) + return DRIVER(drive)->error(drive, "flagged_task_in_intr (no data requested)", stat); + + if (!OK_STAT(stat, DATA_READY, BAD_R_STAT)) { + if (stat & ERR_STAT) { + return DRIVER(drive)->error(drive, "flagged_task_in_intr", stat); + } + /* + * (ks) Unexpected ATA data phase detected. + * This should not happen. But, it can ! + * I am not sure, which function is best to clean up + * this situation. I choose: ide_error(...) + */ + return DRIVER(drive)->error(drive, "flagged_task_in_intr (unexpected data phase)", stat); + } + + pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); + DTF("Read - rq->current_nr_sectors: %d, status: %02x\n", (int) rq->current_nr_sectors, stat); + + taskfile_input_data(drive, pBuf, SECTOR_WORDS); + + if (--rq->current_nr_sectors != 0) { + /* + * (ks) We don't know which command was executed. + * So, we wait the 'WORSTCASE' value. + */ + ide_set_handler(drive, &flagged_task_in_intr, WAIT_WORSTCASE, NULL); + return ide_started; + } + /* + * (ks) Last sector was transfered, wait until drive is ready. + * This can take up to 10 usec. We willl wait max 50 us. + */ + while (((stat = GET_STAT()) & BUSY_STAT) && retries--) + udelay(10); + ide_end_drive_cmd (drive, stat, GET_ERR()); + + return ide_stopped; +} + +ide_startstop_t flagged_task_mulin_intr (ide_drive_t *drive) +{ + byte stat = GET_STAT(); + struct request *rq = HWGROUP(drive)->rq; + char *pBuf = NULL; + int retries = 5; + unsigned int msect, nsect; + + if (rq->current_nr_sectors == 0) + return DRIVER(drive)->error(drive, "flagged_task_mulin_intr (no data requested)", stat); + + msect = drive->mult_count; + if (msect == 0) + return DRIVER(drive)->error(drive, "flagged_task_mulin_intr (multimode not set)", stat); + + if (!OK_STAT(stat, DATA_READY, BAD_R_STAT)) { + if (stat & ERR_STAT) { + return DRIVER(drive)->error(drive, "flagged_task_mulin_intr", stat); + } + /* + * (ks) Unexpected ATA data phase detected. + * This should not happen. But, it can ! + * I am not sure, which function is best to clean up + * this situation. I choose: ide_error(...) + */ + return DRIVER(drive)->error(drive, "flagged_task_mulin_intr (unexpected data phase)", stat); + } + + nsect = (rq->current_nr_sectors > msect) ? msect : rq->current_nr_sectors; + pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); + + DTF("Multiread: %p, nsect: %d , rq->current_nr_sectors: %ld\n", + pBuf, nsect, rq->current_nr_sectors); + + taskfile_input_data(drive, pBuf, nsect * SECTOR_WORDS); + + rq->current_nr_sectors -= nsect; + if (rq->current_nr_sectors != 0) { + /* + * (ks) We don't know which command was executed. + * So, we wait the 'WORSTCASE' value. + */ + ide_set_handler(drive, &flagged_task_mulin_intr, WAIT_WORSTCASE, NULL); + return ide_started; + } + + /* + * (ks) Last sector was transfered, wait until drive is ready. + * This can take up to 10 usec. We willl wait max 50 us. + */ + while (((stat = GET_STAT()) & BUSY_STAT) && retries--) + udelay(10); + ide_end_drive_cmd (drive, stat, GET_ERR()); + + return ide_stopped; +} + +/* + * Pre handler for command with PIO data-out phase + */ +ide_startstop_t flagged_pre_task_out_intr (ide_drive_t *drive, struct request *rq) +{ + byte stat = GET_STAT(); + ide_startstop_t startstop; + + if (!rq->current_nr_sectors) { + return DRIVER(drive)->error(drive, "flagged_pre_task_out_intr (write data not specified)", stat); + } + + if (ide_wait_stat(&startstop, drive, DATA_READY, + BAD_W_STAT, WAIT_DRQ)) { + printk(KERN_ERR "%s: No DRQ bit after issuing write command.\n", drive->name); + return startstop; + } + + taskfile_output_data(drive, rq->buffer, SECTOR_WORDS); + --rq->current_nr_sectors; + + return ide_started; +} + +ide_startstop_t flagged_task_out_intr (ide_drive_t *drive) +{ + byte stat = GET_STAT(); + struct request *rq = HWGROUP(drive)->rq; + char *pBuf = NULL; + + if (!OK_STAT(stat, DRIVE_READY, BAD_W_STAT)) + return DRIVER(drive)->error(drive, "flagged_task_out_intr", stat); + + if (!rq->current_nr_sectors) { + ide_end_drive_cmd (drive, stat, GET_ERR()); + return ide_stopped; + } + + if (!OK_STAT(stat, DATA_READY, BAD_W_STAT)) { + /* + * (ks) Unexpected ATA data phase detected. + * This should not happen. But, it can ! + * I am not sure, which function is best to clean up + * this situation. I choose: ide_error(...) + */ + return DRIVER(drive)->error(drive, "flagged_task_out_intr (unexpected data phase)", stat); + } + + pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); + DTF("Write - rq->current_nr_sectors: %d, status: %02x\n", + (int) rq->current_nr_sectors, stat); + + taskfile_output_data(drive, pBuf, SECTOR_WORDS); + --rq->current_nr_sectors; + + /* + * (ks) We don't know which command was executed. + * So, we wait the 'WORSTCASE' value. + */ + ide_set_handler(drive, &flagged_task_out_intr, WAIT_WORSTCASE, NULL); + + return ide_started; +} + +ide_startstop_t flagged_pre_task_mulout_intr (ide_drive_t *drive, struct request *rq) +{ + byte stat = GET_STAT(); + char *pBuf = NULL; + ide_startstop_t startstop; + unsigned int msect, nsect; + + if (!rq->current_nr_sectors) + return DRIVER(drive)->error(drive, "flagged_pre_task_mulout_intr (write data not specified)", stat); + + msect = drive->mult_count; + if (msect == 0) + return DRIVER(drive)->error(drive, "flagged_pre_task_mulout_intr (multimode not set)", stat); + + if (ide_wait_stat(&startstop, drive, DATA_READY, + BAD_W_STAT, WAIT_DRQ)) { + printk(KERN_ERR "%s: No DRQ bit after issuing write command.\n", drive->name); + return startstop; + } + + nsect = (rq->current_nr_sectors > msect) ? msect : rq->current_nr_sectors; + pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); + DTF("Multiwrite: %p, nsect: %d , rq->current_nr_sectors: %ld\n", + pBuf, nsect, rq->current_nr_sectors); + + taskfile_output_data(drive, pBuf, nsect * SECTOR_WORDS); + + rq->current_nr_sectors -= nsect; + + return ide_started; +} + +ide_startstop_t flagged_task_mulout_intr (ide_drive_t *drive) +{ + byte stat = GET_STAT(); + struct request *rq = HWGROUP(drive)->rq; + char *pBuf = NULL; + unsigned int msect, nsect; + + msect = drive->mult_count; + if (msect == 0) + return DRIVER(drive)->error(drive, "flagged_task_mulout_intr (multimode not set)", stat); + + if (!OK_STAT(stat, DRIVE_READY, BAD_W_STAT)) + return DRIVER(drive)->error(drive, "flagged_task_mulout_intr", stat); + + if (!rq->current_nr_sectors) { + ide_end_drive_cmd (drive, stat, GET_ERR()); + return ide_stopped; + } + + if (!OK_STAT(stat, DATA_READY, BAD_W_STAT)) { + /* + * (ks) Unexpected ATA data phase detected. + * This should not happen. But, it can ! + * I am not sure, which function is best to clean up + * this situation. I choose: ide_error(...) + */ + return DRIVER(drive)->error(drive, "flagged_task_mulout_intr (unexpected data phase)", stat); + } + + nsect = (rq->current_nr_sectors > msect) ? msect : rq->current_nr_sectors; + pBuf = rq->buffer + ((rq->nr_sectors - rq->current_nr_sectors) * SECTOR_SIZE); + DTF("Multiwrite: %p, nsect: %d , rq->current_nr_sectors: %ld\n", + pBuf, nsect, rq->current_nr_sectors); + + taskfile_output_data(drive, pBuf, nsect * SECTOR_WORDS); + rq->current_nr_sectors -= nsect; + + /* + * (ks) We don't know which command was executed. + * So, we wait the 'WORSTCASE' value. + */ + ide_set_handler(drive, &flagged_task_mulout_intr, WAIT_WORSTCASE, NULL); + + return ide_started; +} + +/* + * This function is intended to be used prior to invoking ide_do_drive_cmd(). + */ +void ide_init_drive_taskfile (struct request *rq) +{ + memset(rq, 0, sizeof(*rq)); + rq->flags = REQ_DRIVE_TASKFILE; +} + +int ide_diag_taskfile (ide_drive_t *drive, ide_task_t *args, unsigned long data_size, byte *buf) +{ + struct request rq; + + ide_init_drive_taskfile(&rq); + rq.flags = REQ_DRIVE_TASKFILE; + rq.buffer = buf; + + /* + * (ks) We transfer currently only whole sectors. + * This is suffient for now. But, it would be great, + * if we would find a solution to transfer any size. + * To support special commands like READ LONG. + */ + if (args->command_type != IDE_DRIVE_TASK_NO_DATA) { + if (data_size == 0) + rq.current_nr_sectors = rq.nr_sectors = (args->hobRegister[IDE_NSECTOR_OFFSET_HOB] << 8) | args->tfRegister[IDE_NSECTOR_OFFSET]; + /* rq.hard_cur_sectors */ + else + rq.current_nr_sectors = rq.nr_sectors = data_size / SECTOR_SIZE; + /* rq.hard_cur_sectors */ + } + + if (args->tf_out_flags.all == 0) { + /* + * clean up kernel settings for driver sanity, regardless. + * except for discrete diag services. + */ + args->posthandler = ide_post_handler_parser( + (struct hd_drive_task_hdr *) args->tfRegister, + (struct hd_drive_hob_hdr *) args->hobRegister); + + } + rq.special = args; + return ide_do_drive_cmd(drive, &rq, ide_wait); +} + +int ide_raw_taskfile (ide_drive_t *drive, ide_task_t *args, byte *buf) +{ + return ide_diag_taskfile(drive, args, 0, buf); +} + +#ifdef CONFIG_IDE_TASK_IOCTL_DEBUG +char * ide_ioctl_verbose (unsigned int cmd) +{ + return("unknown"); +} + +char * ide_task_cmd_verbose (byte task) +{ + return("unknown"); +} +#endif /* CONFIG_IDE_TASK_IOCTL_DEBUG */ + +#define MAX_DMA (256*SECTOR_WORDS) + +int ide_taskfile_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + ide_task_request_t *req_task; + ide_task_t args; + byte *outbuf = NULL; + byte *inbuf = NULL; + task_ioreg_t *argsptr = args.tfRegister; + task_ioreg_t *hobsptr = args.hobRegister; + int err = 0; + int tasksize = sizeof(struct ide_task_request_s); + int taskin = 0; + int taskout = 0; + byte io_32bit = drive->io_32bit; + +// printk("IDE Taskfile ...\n"); + + req_task = kmalloc(tasksize, GFP_KERNEL); + if (req_task == NULL) return -ENOMEM; + memset(req_task, 0, tasksize); + if (copy_from_user(req_task, (void *) arg, tasksize)) { + kfree(req_task); + return -EFAULT; + } + + taskout = (int) req_task->out_size; + taskin = (int) req_task->in_size; + + if (taskout) { + int outtotal = tasksize; + outbuf = kmalloc(taskout, GFP_KERNEL); + if (outbuf == NULL) { + err = -ENOMEM; + goto abort; + } + memset(outbuf, 0, taskout); + if (copy_from_user(outbuf, (void *)arg + outtotal, taskout)) { + err = -EFAULT; + goto abort; + } + } + + if (taskin) { + int intotal = tasksize + taskout; + inbuf = kmalloc(taskin, GFP_KERNEL); + if (inbuf == NULL) { + err = -ENOMEM; + goto abort; + } + memset(inbuf, 0, taskin); + if (copy_from_user(inbuf, (void *)arg + intotal , taskin)) { + err = -EFAULT; + goto abort; + } + } + + memset (&args, 0, sizeof (ide_task_t) ); + memcpy(argsptr, req_task->io_ports, HDIO_DRIVE_TASK_HDR_SIZE); + memcpy(hobsptr, req_task->hob_ports, HDIO_DRIVE_HOB_HDR_SIZE); + + args.tf_in_flags = req_task->in_flags; + args.tf_out_flags = req_task->out_flags; + args.data_phase = req_task->data_phase; + args.command_type = req_task->req_cmd; + +#ifdef CONFIG_IDE_TASK_IOCTL_DEBUG + DTF("%s: ide_ioctl_cmd %s: ide_task_cmd %s\n", + drive->name, + ide_ioctl_verbose(cmd), + ide_task_cmd_verbose(args.tfRegister[IDE_COMMAND_OFFSET])); +#endif /* CONFIG_IDE_TASK_IOCTL_DEBUG */ + + drive->io_32bit = 0; + switch(req_task->data_phase) { + case TASKFILE_OUT_DMAQ: + case TASKFILE_OUT_DMA: + err = ide_diag_taskfile(drive, &args, taskout, outbuf); + break; + case TASKFILE_IN_DMAQ: + case TASKFILE_IN_DMA: + err = ide_diag_taskfile(drive, &args, taskin, inbuf); + break; + case TASKFILE_IN_OUT: +#if 0 + args.prehandler = &pre_task_out_intr; + args.handler = &task_out_intr; + args.posthandler = NULL; + err = ide_diag_taskfile(drive, &args, taskout, outbuf); + args.prehandler = NULL; + args.handler = &task_in_intr; + args.posthandler = NULL; + err = ide_diag_taskfile(drive, &args, taskin, inbuf); + break; +#else + err = -EFAULT; + goto abort; +#endif + case TASKFILE_MULTI_OUT: + if (!drive->mult_count) { + /* (hs): give up if multcount is not set */ + printk("%s: %s Multimode Write " \ + "multcount is not set\n", + drive->name, __FUNCTION__); + err = -EPERM; + goto abort; + } + if (args.tf_out_flags.all != 0) { + args.prehandler = &flagged_pre_task_mulout_intr; + args.handler = &flagged_task_mulout_intr; + } else { + args.prehandler = &pre_task_mulout_intr; + args.handler = &task_mulout_intr; + } + err = ide_diag_taskfile(drive, &args, taskout, outbuf); + break; + case TASKFILE_OUT: + if (args.tf_out_flags.all != 0) { + args.prehandler = &flagged_pre_task_out_intr; + args.handler = &flagged_task_out_intr; + } else { + args.prehandler = &pre_task_out_intr; + args.handler = &task_out_intr; + } + err = ide_diag_taskfile(drive, &args, taskout, outbuf); + break; + case TASKFILE_MULTI_IN: + if (!drive->mult_count) { + /* (hs): give up if multcount is not set */ + printk("%s: %s Multimode Read failure " \ + "multcount is not set\n", + drive->name, __FUNCTION__); + err = -EPERM; + goto abort; + } + if (args.tf_out_flags.all != 0) { + args.handler = &flagged_task_mulin_intr; + } else { + args.handler = &task_mulin_intr; + } + err = ide_diag_taskfile(drive, &args, taskin, inbuf); + break; + case TASKFILE_IN: + if (args.tf_out_flags.all != 0) { + args.handler = &flagged_task_in_intr; + } else { + args.handler = &task_in_intr; + } + err = ide_diag_taskfile(drive, &args, taskin, inbuf); + break; + case TASKFILE_NO_DATA: + if (args.tf_out_flags.all != 0) { + args.handler = &flagged_task_no_data_intr; + } else { + args.handler = &task_no_data_intr; + } + err = ide_diag_taskfile(drive, &args, 0, NULL); + break; + default: + err = -EFAULT; + goto abort; + } + + memcpy(req_task->io_ports, &(args.tfRegister), HDIO_DRIVE_TASK_HDR_SIZE); + memcpy(req_task->hob_ports, &(args.hobRegister), HDIO_DRIVE_HOB_HDR_SIZE); + req_task->in_flags = args.tf_in_flags; + req_task->out_flags = args.tf_out_flags; + + if (copy_to_user((void *)arg, req_task, tasksize)) { + err = -EFAULT; + goto abort; + } + if (taskout) { + int outtotal = tasksize; + if (copy_to_user((void *)arg+outtotal, outbuf, taskout)) { + err = -EFAULT; + goto abort; + } + } + if (taskin) { + int intotal = tasksize + taskout; + if (copy_to_user((void *)arg+intotal, inbuf, taskin)) { + err = -EFAULT; + goto abort; + } + } +abort: + kfree(req_task); + if (outbuf != NULL) + kfree(outbuf); + if (inbuf != NULL) + kfree(inbuf); + +// printk("IDE Taskfile ioctl ended. rc = %i\n", err); + + drive->io_32bit = io_32bit; + + return err; +} + +int ide_ata66_check (ide_drive_t *drive, ide_task_t *args); +int set_transfer(ide_drive_t *drive, ide_task_t *args); + +/* + * FIXME : this needs to map into at taskfile. + */ +int ide_cmd_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ +#if 1 + int err = 0; + byte args[4], *argbuf = args; + byte xfer_rate = 0; + int argsize = 4; + ide_task_t tfargs; + + if (NULL == (void *) arg) { + struct request rq; + ide_init_drive_cmd(&rq); + return ide_do_drive_cmd(drive, &rq, ide_wait); + } + + if (copy_from_user(args, (void *)arg, 4)) + return -EFAULT; + + memset(&tfargs, 0, sizeof(ide_task_t)); + tfargs.tfRegister[IDE_FEATURE_OFFSET] = args[2]; + tfargs.tfRegister[IDE_NSECTOR_OFFSET] = args[3]; + tfargs.tfRegister[IDE_SECTOR_OFFSET] = args[1]; + tfargs.tfRegister[IDE_LCYL_OFFSET] = 0x00; + tfargs.tfRegister[IDE_HCYL_OFFSET] = 0x00; + tfargs.tfRegister[IDE_SELECT_OFFSET] = 0x00; + tfargs.tfRegister[IDE_COMMAND_OFFSET] = args[0]; + + if (args[3]) { + argsize = 4 + (SECTOR_WORDS * 4 * args[3]); + argbuf = kmalloc(argsize, GFP_KERNEL); + if (argbuf == NULL) + return -ENOMEM; + memcpy(argbuf, args, 4); + } + if (set_transfer(drive, &tfargs)) { + xfer_rate = args[1]; + if (ide_ata66_check(drive, &tfargs)) + goto abort; + } + + err = ide_wait_cmd(drive, args[0], args[1], args[2], args[3], argbuf); + + if (!err && xfer_rate) { + /* active-retuning-calls future */ + if ((HWIF(drive)->speedproc) != NULL) + HWIF(drive)->speedproc(drive, xfer_rate); + ide_driveid_update(drive); + } +abort: + if (copy_to_user((void *)arg, argbuf, argsize)) + err = -EFAULT; + if (argsize > 4) + kfree(argbuf); + return err; + +#else + + int err = 0; + byte args[4], *argbuf = args; + byte xfer_rate = 0; + int argsize = 0; + ide_task_t tfargs; + + if (NULL == (void *) arg) { + struct request rq; + ide_init_drive_cmd(&rq); + return ide_do_drive_cmd(drive, &rq, ide_wait); + } + + if (copy_from_user(args, (void *)arg, 4)) + return -EFAULT; + + memset(&tfargs, 0, sizeof(ide_task_t)); + tfargs.tfRegister[IDE_FEATURE_OFFSET] = args[2]; + tfargs.tfRegister[IDE_NSECTOR_OFFSET] = args[3]; + tfargs.tfRegister[IDE_SECTOR_OFFSET] = args[1]; + tfargs.tfRegister[IDE_LCYL_OFFSET] = 0x00; + tfargs.tfRegister[IDE_HCYL_OFFSET] = 0x00; + tfargs.tfRegister[IDE_SELECT_OFFSET] = 0x00; + tfargs.tfRegister[IDE_COMMAND_OFFSET] = args[0]; + + if (args[3]) { + argsize = (SECTOR_WORDS * 4 * args[3]); + argbuf = kmalloc(argsize, GFP_KERNEL); + if (argbuf == NULL) + return -ENOMEM; + } + + if (set_transfer(drive, &tfargs)) { + xfer_rate = args[1]; + if (ide_ata66_check(drive, &tfargs)) + goto abort; + } + + tfargs.command_type = ide_cmd_type_parser(&tfargs); + err = ide_raw_taskfile(drive, &tfargs, argbuf); + + if (!err && xfer_rate) { + /* active-retuning-calls future */ + if ((HWIF(drive)->speedproc) != NULL) + HWIF(drive)->speedproc(drive, xfer_rate); + ide_driveid_update(drive); + } +abort: + + args[0] = tfargs.tfRegister[IDE_COMMAND_OFFSET]; + args[1] = tfargs.tfRegister[IDE_FEATURE_OFFSET]; + args[2] = tfargs.tfRegister[IDE_NSECTOR_OFFSET]; + args[3] = 0; + + if (copy_to_user((void *)arg, argbuf, 4)) + err = -EFAULT; + if (argbuf != NULL) { + if (copy_to_user((void *)arg, argbuf + 4, argsize)) + err = -EFAULT; + kfree(argbuf); + } + return err; + +#endif + +} + +/* + * FIXME : this needs to map into at taskfile. + */ +int ide_task_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int err = 0; + byte args[7], *argbuf = args; + int argsize = 7; + + if (copy_from_user(args, (void *)arg, 7)) + return -EFAULT; + err = ide_wait_cmd_task(drive, argbuf); + if (copy_to_user((void *)arg, argbuf, argsize)) + err = -EFAULT; + return err; +} + +EXPORT_SYMBOL(drive_is_ready); +EXPORT_SYMBOL(wait_for_ready); + +EXPORT_SYMBOL(task_read_24); +EXPORT_SYMBOL(ata_input_data); +EXPORT_SYMBOL(ata_output_data); +EXPORT_SYMBOL(atapi_input_bytes); +EXPORT_SYMBOL(atapi_output_bytes); +EXPORT_SYMBOL(taskfile_input_data); +EXPORT_SYMBOL(taskfile_output_data); + +EXPORT_SYMBOL(ide_wait_stat); +EXPORT_SYMBOL(do_rw_taskfile); +EXPORT_SYMBOL(flagged_taskfile); +EXPORT_SYMBOL(ide_end_taskfile); + +EXPORT_SYMBOL(set_multmode_intr); +EXPORT_SYMBOL(set_geometry_intr); +EXPORT_SYMBOL(recal_intr); + +EXPORT_SYMBOL(task_no_data_intr); +EXPORT_SYMBOL(task_in_intr); +EXPORT_SYMBOL(task_mulin_intr); +EXPORT_SYMBOL(pre_task_out_intr); +EXPORT_SYMBOL(task_out_intr); +EXPORT_SYMBOL(pre_task_mulout_intr); +EXPORT_SYMBOL(task_mulout_intr); + +EXPORT_SYMBOL(ide_init_drive_taskfile); +EXPORT_SYMBOL(ide_raw_taskfile); +EXPORT_SYMBOL(ide_pre_handler_parser); +EXPORT_SYMBOL(ide_handler_parser); +EXPORT_SYMBOL(ide_post_handler_parser); +EXPORT_SYMBOL(ide_cmd_type_parser); +EXPORT_SYMBOL(ide_taskfile_ioctl); +EXPORT_SYMBOL(ide_cmd_ioctl); +EXPORT_SYMBOL(ide_task_ioctl); + +/* + * Beginning of Taskfile OPCODE Library and feature sets. + */ + +/* + * All hosts that use the 80c ribbon must use! + * The name is derived from upper byte of word 93 and the 80c ribbon. + */ +byte eighty_ninty_three (ide_drive_t *drive) +{ +#if 0 + if (!HWIF(drive)->udma_four) + return 0; + + if (drive->id->major_rev_num) { + int hssbd = 0; + int i; + /* + * Determime highest Supported SPEC + */ + for (i=1; i<=15; i++) + if (drive->id->major_rev_num & (1<id->hw_config & 0x4000) && +#endif /* CONFIG_IDEDMA_IVB */ + (drive->id->hw_config & 0x6000)) ? 1 : 0); + +#else + + return ((byte) ((HWIF(drive)->udma_four) && +#ifndef CONFIG_IDEDMA_IVB + (drive->id->hw_config & 0x4000) && +#endif /* CONFIG_IDEDMA_IVB */ + (drive->id->hw_config & 0x6000)) ? 1 : 0); +#endif +} + +int ide_ata66_check (ide_drive_t *drive, ide_task_t *args) +{ + if (!HWIF(drive)->udma_four) { + printk("%s: Speed warnings UDMA 3/4/5 is not functional.\n", + HWIF(drive)->name); + return 1; + } + if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_SETFEATURES) && + (args->tfRegister[IDE_SECTOR_OFFSET] > XFER_UDMA_2) && + (args->tfRegister[IDE_FEATURE_OFFSET] == SETFEATURES_XFER)) { +#ifndef CONFIG_IDEDMA_IVB + if ((drive->id->hw_config & 0x6000) == 0) { +#else /* !CONFIG_IDEDMA_IVB */ + if (((drive->id->hw_config & 0x2000) == 0) || + ((drive->id->hw_config & 0x4000) == 0)) { +#endif /* CONFIG_IDEDMA_IVB */ + printk("%s: Speed warnings UDMA 3/4/5 is not functional.\n", drive->name); + return 1; + } + } + return 0; +} + +/* + * Backside of HDIO_DRIVE_CMD call of SETFEATURES_XFER. + * 1 : Safe to update drive->id DMA registers. + * 0 : OOPs not allowed. + */ +int set_transfer (ide_drive_t *drive, ide_task_t *args) +{ + if ((args->tfRegister[IDE_COMMAND_OFFSET] == WIN_SETFEATURES) && + (args->tfRegister[IDE_SECTOR_OFFSET] >= XFER_SW_DMA_0) && + (args->tfRegister[IDE_FEATURE_OFFSET] == SETFEATURES_XFER) && + (drive->id->dma_ultra || + drive->id->dma_mword || + drive->id->dma_1word)) + return 1; + + return 0; +} + +byte ide_auto_reduce_xfer (ide_drive_t *drive) +{ + if (!drive->crc_count) + return drive->current_speed; + drive->crc_count = 0; + + switch(drive->current_speed) { + case XFER_UDMA_7: return XFER_UDMA_6; + case XFER_UDMA_6: return XFER_UDMA_5; + case XFER_UDMA_5: return XFER_UDMA_4; + case XFER_UDMA_4: return XFER_UDMA_3; + case XFER_UDMA_3: return XFER_UDMA_2; + case XFER_UDMA_2: return XFER_UDMA_1; + case XFER_UDMA_1: return XFER_UDMA_0; + /* + * OOPS we do not goto non Ultra DMA modes + * without iCRC's available we force + * the system to PIO and make the user + * invoke the ATA-1 ATA-2 DMA modes. + */ + case XFER_UDMA_0: + default: return XFER_PIO_4; + } +} + +int taskfile_lib_get_identify (ide_drive_t *drive, byte *buf) +{ + ide_task_t args; + memset(&args, 0, sizeof(ide_task_t)); + args.tfRegister[IDE_NSECTOR_OFFSET] = 0x01; + if (drive->media == ide_disk) + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_IDENTIFY; + else + args.tfRegister[IDE_COMMAND_OFFSET] = WIN_PIDENTIFY; + args.command_type = ide_cmd_type_parser(&args); + return ide_raw_taskfile(drive, &args, buf); +} + +/* + * Update the + */ +int ide_driveid_update (ide_drive_t *drive) +{ +#if 0 + struct hd_driveid *id; + + id = kmalloc(SECTOR_WORDS*4, GFP_ATOMIC); + if (!id) + return 0; + + taskfile_lib_get_identify(drive, (char *)&id); + + ide_fix_driveid(id); + if (id) { + drive->id->dma_ultra = id->dma_ultra; + drive->id->dma_mword = id->dma_mword; + drive->id->dma_1word = id->dma_1word; + /* anything more ? */ + kfree(id); + } + return 1; +#else + /* + * Re-read drive->id for possible DMA mode + * change (copied from ide-probe.c) + */ + struct hd_driveid *id; + unsigned long timeout, flags; + + SELECT_MASK(HWIF(drive), drive, 1); + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl,IDE_CONTROL_REG); + ide_delay_50ms(); + OUT_BYTE(WIN_IDENTIFY, IDE_COMMAND_REG); + timeout = jiffies + WAIT_WORSTCASE; + do { + if (time_after(jiffies, timeout)) { + SELECT_MASK(HWIF(drive), drive, 0); + return 0; /* drive timed-out */ + } + ide_delay_50ms(); /* give drive a breather */ + } while (IN_BYTE(IDE_ALTSTATUS_REG) & BUSY_STAT); + ide_delay_50ms(); /* wait for IRQ and DRQ_STAT */ + if (!OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) { + SELECT_MASK(HWIF(drive), drive, 0); + printk("%s: CHECK for good STATUS\n", drive->name); + return 0; + } + local_irq_save(flags); + SELECT_MASK(HWIF(drive), drive, 0); + id = kmalloc(SECTOR_WORDS*4, GFP_ATOMIC); + if (!id) { + local_irq_restore(flags); + return 0; + } + ata_input_data(drive, id, SECTOR_WORDS); + (void) GET_STAT(); /* clear drive IRQ */ + local_irq_enable(); + local_irq_restore(flags); + ide_fix_driveid(id); + if (id) { + drive->id->dma_ultra = id->dma_ultra; + drive->id->dma_mword = id->dma_mword; + drive->id->dma_1word = id->dma_1word; + /* anything more ? */ + kfree(id); + } + + return 1; +#endif +} + + +/* + * Similar to ide_wait_stat(), except it never calls ide_error internally. + * This is a kludge to handle the new ide_config_drive_speed() function, + * and should not otherwise be used anywhere. Eventually, the tuneproc's + * should be updated to return ide_startstop_t, in which case we can get + * rid of this abomination again. :) -ml + * + * It is gone.......... + * + * const char *msg == consider adding for verbose errors. + */ +int ide_config_drive_speed (ide_drive_t *drive, byte speed) +{ + ide_hwif_t *hwif = HWIF(drive); + int i, error = 1; + byte stat; + +#if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI) + hwif->dmaproc(ide_dma_host_off, drive); +#endif /* (CONFIG_BLK_DEV_IDEDMA) && !(CONFIG_DMA_NONPCI) */ + + /* + * Don't use ide_wait_cmd here - it will + * attempt to set_geometry and recalibrate, + * but for some reason these don't work at + * this point (lost interrupt). + */ + /* + * Select the drive, and issue the SETFEATURES command + */ + disable_irq(hwif->irq); /* disable_irq_nosync ?? */ + udelay(1); + SELECT_DRIVE(HWIF(drive), drive); + SELECT_MASK(HWIF(drive), drive, 0); + udelay(1); + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl | 2, IDE_CONTROL_REG); + OUT_BYTE(speed, IDE_NSECTOR_REG); + OUT_BYTE(SETFEATURES_XFER, IDE_FEATURE_REG); + OUT_BYTE(WIN_SETFEATURES, IDE_COMMAND_REG); + if ((IDE_CONTROL_REG) && (drive->quirk_list == 2)) + OUT_BYTE(drive->ctl, IDE_CONTROL_REG); + udelay(1); + /* + * Wait for drive to become non-BUSY + */ + if ((stat = GET_STAT()) & BUSY_STAT) { + unsigned long flags, timeout; + local_irq_set(flags); + timeout = jiffies + WAIT_CMD; + while ((stat = GET_STAT()) & BUSY_STAT) { + if (time_after(jiffies, timeout)) + break; + } + local_irq_restore(flags); + } + + /* + * Allow status to settle, then read it again. + * A few rare drives vastly violate the 400ns spec here, + * so we'll wait up to 10usec for a "good" status + * rather than expensively fail things immediately. + * This fix courtesy of Matthew Faupel & Niccolo Rigacci. + */ + for (i = 0; i < 10; i++) { + udelay(1); + if (OK_STAT((stat = GET_STAT()), DRIVE_READY, BUSY_STAT|DRQ_STAT|ERR_STAT)) { + error = 0; + break; + } + } + + SELECT_MASK(HWIF(drive), drive, 0); + + enable_irq(hwif->irq); + + if (error) { + (void) ide_dump_status(drive, "set_drive_speed_status", stat); + return error; + } + + drive->id->dma_ultra &= ~0xFF00; + drive->id->dma_mword &= ~0x0F00; + drive->id->dma_1word &= ~0x0F00; + +#if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI) + if (speed >= XFER_SW_DMA_0) + hwif->dmaproc(ide_dma_host_on, drive); +#endif /* (CONFIG_BLK_DEV_IDEDMA) && !(CONFIG_DMA_NONPCI) */ + + switch(speed) { + case XFER_UDMA_7: drive->id->dma_ultra |= 0x8080; break; + case XFER_UDMA_6: drive->id->dma_ultra |= 0x4040; break; + case XFER_UDMA_5: drive->id->dma_ultra |= 0x2020; break; + case XFER_UDMA_4: drive->id->dma_ultra |= 0x1010; break; + case XFER_UDMA_3: drive->id->dma_ultra |= 0x0808; break; + case XFER_UDMA_2: drive->id->dma_ultra |= 0x0404; break; + case XFER_UDMA_1: drive->id->dma_ultra |= 0x0202; break; + case XFER_UDMA_0: drive->id->dma_ultra |= 0x0101; break; + case XFER_MW_DMA_2: drive->id->dma_mword |= 0x0404; break; + case XFER_MW_DMA_1: drive->id->dma_mword |= 0x0202; break; + case XFER_MW_DMA_0: drive->id->dma_mword |= 0x0101; break; + case XFER_SW_DMA_2: drive->id->dma_1word |= 0x0404; break; + case XFER_SW_DMA_1: drive->id->dma_1word |= 0x0202; break; + case XFER_SW_DMA_0: drive->id->dma_1word |= 0x0101; break; + default: break; + } + if (!drive->init_speed) + drive->init_speed = speed; + drive->current_speed = speed; + return error; +} + +EXPORT_SYMBOL(eighty_ninty_three); +EXPORT_SYMBOL(ide_auto_reduce_xfer); +EXPORT_SYMBOL(set_transfer); +EXPORT_SYMBOL(taskfile_lib_get_identify); +EXPORT_SYMBOL(ide_driveid_update); +EXPORT_SYMBOL(ide_config_drive_speed); + +#ifdef CONFIG_PKT_TASK_IOCTL + +#if 0 +{ + +{ /* start cdrom */ + + struct cdrom_info *info = drive->driver_data; + + if (info->dma) { + if (info->cmd == READ) { + info->dma = !HWIF(drive)->dmaproc(ide_dma_read, drive); + } else if (info->cmd == WRITE) { + info->dma = !HWIF(drive)->dmaproc(ide_dma_write, drive); + } else { + printk("ide-cd: DMA set, but not allowed\n"); + } + } + + /* Set up the controller registers. */ + OUT_BYTE (info->dma, IDE_FEATURE_REG); + OUT_BYTE (0, IDE_NSECTOR_REG); + OUT_BYTE (0, IDE_SECTOR_REG); + + OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG); + OUT_BYTE (xferlen >> 8 , IDE_HCYL_REG); + if (IDE_CONTROL_REG) + OUT_BYTE (drive->ctl, IDE_CONTROL_REG); + + if (info->dma) + (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); + + if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt) { + ide_set_handler (drive, handler, WAIT_CMD, cdrom_timer_expiry); + OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ + return ide_started; + } else { + OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */ + return (*handler) (drive); + } + +} /* end cdrom */ + +{ /* start floppy */ + + idefloppy_floppy_t *floppy = drive->driver_data; + idefloppy_bcount_reg_t bcount; + int dma_ok = 0; + + floppy->pc=pc; /* Set the current packet command */ + + pc->retries++; + pc->actually_transferred=0; /* We haven't transferred any data yet */ + pc->current_position=pc->buffer; + bcount.all = IDE_MIN(pc->request_transfer, 63 * 1024); + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) { + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); + } + if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) + dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + if (IDE_CONTROL_REG) + OUT_BYTE (drive->ctl,IDE_CONTROL_REG); + OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG); /* Use PIO/DMA */ + OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG); + OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG); + OUT_BYTE (drive->select.all,IDE_SELECT_REG); + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (dma_ok) { /* Begin DMA, if necessary */ + set_bit (PC_DMA_IN_PROGRESS, &pc->flags); + (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +} /* end floppy */ + +{ /* start tape */ + + idetape_tape_t *tape = drive->driver_data; + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (test_and_clear_bit (PC_DMA_ERROR, &pc->flags)) { + printk (KERN_WARNING "ide-tape: DMA disabled, reverting to PIO\n"); + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); + } + if (test_bit (PC_DMA_RECOMMENDED, &pc->flags) && drive->using_dma) + dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + if (IDE_CONTROL_REG) + OUT_BYTE (drive->ctl,IDE_CONTROL_REG); + OUT_BYTE (dma_ok ? 1:0,IDE_FEATURE_REG); /* Use PIO/DMA */ + OUT_BYTE (bcount.b.high,IDE_BCOUNTH_REG); + OUT_BYTE (bcount.b.low,IDE_BCOUNTL_REG); + OUT_BYTE (drive->select.all,IDE_SELECT_REG); +#ifdef CONFIG_BLK_DEV_IDEDMA + if (dma_ok) { /* Begin DMA, if necessary */ + set_bit (PC_DMA_IN_PROGRESS, &pc->flags); + (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + if (test_bit(IDETAPE_DRQ_INTERRUPT, &tape->flags)) { + ide_set_handler(drive, &idetape_transfer_pc, IDETAPE_WAIT_CMD, NULL); + OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); + return ide_started; + } else { + OUT_BYTE(WIN_PACKETCMD, IDE_COMMAND_REG); + return idetape_transfer_pc(drive); + } + +} /* end tape */ + +} +#endif + +int pkt_taskfile_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ +#if 0 + switch(req_task->data_phase) { + case TASKFILE_P_OUT_DMAQ: + case TASKFILE_P_IN_DMAQ: + case TASKFILE_P_OUT_DMA: + case TASKFILE_P_IN_DMA: + case TASKFILE_P_OUT: + case TASKFILE_P_IN: + } +#endif + return -ENOMSG; +} + +EXPORT_SYMBOL(pkt_taskfile_ioctl); + +#endif /* CONFIG_PKT_TASK_IOCTL */ diff -Nru a/drivers/ide/ide-timing.h b/drivers/ide/ide-timing.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide-timing.h Fri Aug 16 14:35:01 2002 @@ -0,0 +1,281 @@ +#ifndef _IDE_TIMING_H +#define _IDE_TIMING_H + +/* + * $Id: ide-timing.h,v 1.6 2001/12/23 22:47:56 vojtech Exp $ + * + * Copyright (c) 1999-2001 Vojtech Pavlik + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include + +#define XFER_PIO_5 0x0d +#define XFER_UDMA_SLOW 0x4f + +struct ide_timing { + short mode; + short setup; /* t1 */ + short act8b; /* t2 for 8-bit io */ + short rec8b; /* t2i for 8-bit io */ + short cyc8b; /* t0 for 8-bit io */ + short active; /* t2 or tD */ + short recover; /* t2i or tK */ + short cycle; /* t0 */ + short udma; /* t2CYCTYP/2 */ +}; + +/* + * PIO 0-5, MWDMA 0-2 and UDMA 0-6 timings (in nanoseconds). + * These were taken from ATA/ATAPI-6 standard, rev 0a, except + * for PIO 5, which is a nonstandard extension and UDMA6, which + * is currently supported only by Maxtor drives. + */ + +static struct ide_timing ide_timing[] = { + + { XFER_UDMA_6, 0, 0, 0, 0, 0, 0, 0, 15 }, + { XFER_UDMA_5, 0, 0, 0, 0, 0, 0, 0, 20 }, + { XFER_UDMA_4, 0, 0, 0, 0, 0, 0, 0, 30 }, + { XFER_UDMA_3, 0, 0, 0, 0, 0, 0, 0, 45 }, + + { XFER_UDMA_2, 0, 0, 0, 0, 0, 0, 0, 60 }, + { XFER_UDMA_1, 0, 0, 0, 0, 0, 0, 0, 80 }, + { XFER_UDMA_0, 0, 0, 0, 0, 0, 0, 0, 120 }, + + { XFER_UDMA_SLOW, 0, 0, 0, 0, 0, 0, 0, 150 }, + + { XFER_MW_DMA_2, 25, 0, 0, 0, 70, 25, 120, 0 }, + { XFER_MW_DMA_1, 45, 0, 0, 0, 80, 50, 150, 0 }, + { XFER_MW_DMA_0, 60, 0, 0, 0, 215, 215, 480, 0 }, + + { XFER_SW_DMA_2, 60, 0, 0, 0, 120, 120, 240, 0 }, + { XFER_SW_DMA_1, 90, 0, 0, 0, 240, 240, 480, 0 }, + { XFER_SW_DMA_0, 120, 0, 0, 0, 480, 480, 960, 0 }, + + { XFER_PIO_5, 20, 50, 30, 100, 50, 30, 100, 0 }, + { XFER_PIO_4, 25, 70, 25, 120, 70, 25, 120, 0 }, + { XFER_PIO_3, 30, 80, 70, 180, 80, 70, 180, 0 }, + + { XFER_PIO_2, 30, 290, 40, 330, 100, 90, 240, 0 }, + { XFER_PIO_1, 50, 290, 93, 383, 125, 100, 383, 0 }, + { XFER_PIO_0, 70, 290, 240, 600, 165, 150, 600, 0 }, + + { XFER_PIO_SLOW, 120, 290, 240, 960, 290, 240, 960, 0 }, + + { -1 } +}; + +#define IDE_TIMING_SETUP 0x01 +#define IDE_TIMING_ACT8B 0x02 +#define IDE_TIMING_REC8B 0x04 +#define IDE_TIMING_CYC8B 0x08 +#define IDE_TIMING_8BIT 0x0e +#define IDE_TIMING_ACTIVE 0x10 +#define IDE_TIMING_RECOVER 0x20 +#define IDE_TIMING_CYCLE 0x40 +#define IDE_TIMING_UDMA 0x80 +#define IDE_TIMING_ALL 0xff + +#define MIN(a,b) ((a)<(b)?(a):(b)) +#define MAX(a,b) ((a)>(b)?(a):(b)) +#define FIT(v,min,max) MAX(MIN(v,max),min) +#define ENOUGH(v,unit) (((v)-1)/(unit)+1) +#define EZ(v,unit) ((v)?ENOUGH(v,unit):0) + +#define XFER_MODE 0xf0 +#define XFER_UDMA_133 0x48 +#define XFER_UDMA_100 0x44 +#define XFER_UDMA_66 0x42 +#define XFER_UDMA 0x40 +#define XFER_MWDMA 0x20 +#define XFER_SWDMA 0x10 +#define XFER_EPIO 0x01 +#define XFER_PIO 0x00 + +static short ide_find_best_mode(ide_drive_t *drive, int map) +{ + struct hd_driveid *id = drive->id; + short best = 0; + + if (!id) + return XFER_PIO_SLOW; + + if ((map & XFER_UDMA) && (id->field_valid & 4)) { /* Want UDMA and UDMA bitmap valid */ + + if ((map & XFER_UDMA_133) == XFER_UDMA_133) + if ((best = (id->dma_ultra & 0x0040) ? XFER_UDMA_6 : 0)) return best; + + if ((map & XFER_UDMA_100) == XFER_UDMA_100) + if ((best = (id->dma_ultra & 0x0020) ? XFER_UDMA_5 : 0)) return best; + + if ((map & XFER_UDMA_66) == XFER_UDMA_66) + if ((best = (id->dma_ultra & 0x0010) ? XFER_UDMA_4 : + (id->dma_ultra & 0x0008) ? XFER_UDMA_3 : 0)) return best; + + if ((best = (id->dma_ultra & 0x0004) ? XFER_UDMA_2 : + (id->dma_ultra & 0x0002) ? XFER_UDMA_1 : + (id->dma_ultra & 0x0001) ? XFER_UDMA_0 : 0)) return best; + } + + if ((map & XFER_MWDMA) && (id->field_valid & 2)) { /* Want MWDMA and drive has EIDE fields */ + + if ((best = (id->dma_mword & 0x0004) ? XFER_MW_DMA_2 : + (id->dma_mword & 0x0002) ? XFER_MW_DMA_1 : + (id->dma_mword & 0x0001) ? XFER_MW_DMA_0 : 0)) return best; + } + + if (map & XFER_SWDMA) { /* Want SWDMA */ + + if (id->field_valid & 2) { /* EIDE SWDMA */ + + if ((best = (id->dma_1word & 0x0004) ? XFER_SW_DMA_2 : + (id->dma_1word & 0x0002) ? XFER_SW_DMA_1 : + (id->dma_1word & 0x0001) ? XFER_SW_DMA_0 : 0)) return best; + } + + if (id->capability & 1) { /* Pre-EIDE style SWDMA */ + + if ((best = (id->tDMA == 2) ? XFER_SW_DMA_2 : + (id->tDMA == 1) ? XFER_SW_DMA_1 : + (id->tDMA == 0) ? XFER_SW_DMA_0 : 0)) return best; + } + } + + + if ((map & XFER_EPIO) && (id->field_valid & 2)) { /* EIDE PIO modes */ + + if ((best = (drive->id->eide_pio_modes & 4) ? XFER_PIO_5 : + (drive->id->eide_pio_modes & 2) ? XFER_PIO_4 : + (drive->id->eide_pio_modes & 1) ? XFER_PIO_3 : 0)) return best; + } + + return (drive->id->tPIO == 2) ? XFER_PIO_2 : + (drive->id->tPIO == 1) ? XFER_PIO_1 : + (drive->id->tPIO == 0) ? XFER_PIO_0 : XFER_PIO_SLOW; +} + +static void ide_timing_quantize(struct ide_timing *t, struct ide_timing *q, int T, int UT) +{ + q->setup = EZ(t->setup * 1000, T); + q->act8b = EZ(t->act8b * 1000, T); + q->rec8b = EZ(t->rec8b * 1000, T); + q->cyc8b = EZ(t->cyc8b * 1000, T); + q->active = EZ(t->active * 1000, T); + q->recover = EZ(t->recover * 1000, T); + q->cycle = EZ(t->cycle * 1000, T); + q->udma = EZ(t->udma * 1000, UT); +} + +static void ide_timing_merge(struct ide_timing *a, struct ide_timing *b, struct ide_timing *m, unsigned int what) +{ + if (what & IDE_TIMING_SETUP ) m->setup = MAX(a->setup, b->setup); + if (what & IDE_TIMING_ACT8B ) m->act8b = MAX(a->act8b, b->act8b); + if (what & IDE_TIMING_REC8B ) m->rec8b = MAX(a->rec8b, b->rec8b); + if (what & IDE_TIMING_CYC8B ) m->cyc8b = MAX(a->cyc8b, b->cyc8b); + if (what & IDE_TIMING_ACTIVE ) m->active = MAX(a->active, b->active); + if (what & IDE_TIMING_RECOVER) m->recover = MAX(a->recover, b->recover); + if (what & IDE_TIMING_CYCLE ) m->cycle = MAX(a->cycle, b->cycle); + if (what & IDE_TIMING_UDMA ) m->udma = MAX(a->udma, b->udma); +} + +static struct ide_timing* ide_timing_find_mode(short speed) +{ + struct ide_timing *t; + + for (t = ide_timing; t->mode != speed; t++) + if (t->mode < 0) + return NULL; + return t; +} + +static int ide_timing_compute(ide_drive_t *drive, short speed, struct ide_timing *t, int T, int UT) +{ + struct hd_driveid *id = drive->id; + struct ide_timing *s, p; + +/* + * Find the mode. + */ + + if (!(s = ide_timing_find_mode(speed))) + return -EINVAL; + +/* + * If the drive is an EIDE drive, it can tell us it needs extended + * PIO/MWDMA cycle timing. + */ + + if (id && id->field_valid & 2) { /* EIDE drive */ + + memset(&p, 0, sizeof(p)); + + switch (speed & XFER_MODE) { + + case XFER_PIO: + if (speed <= XFER_PIO_2) p.cycle = p.cyc8b = id->eide_pio; + else p.cycle = p.cyc8b = id->eide_pio_iordy; + break; + + case XFER_MWDMA: + p.cycle = id->eide_dma_min; + break; + } + + ide_timing_merge(&p, t, t, IDE_TIMING_CYCLE | IDE_TIMING_CYC8B); + } + +/* + * Convert the timing to bus clock counts. + */ + + ide_timing_quantize(s, t, T, UT); + +/* + * Even in DMA/UDMA modes we still use PIO access for IDENTIFY, S.M.A.R.T + * and some other commands. We have to ensure that the DMA cycle timing is + * slower/equal than the fastest PIO timing. + */ + + if ((speed & XFER_MODE) != XFER_PIO) { + ide_timing_compute(drive, ide_find_best_mode(drive, XFER_PIO | XFER_EPIO), &p, T, UT); + ide_timing_merge(&p, t, t, IDE_TIMING_ALL); + } + +/* + * Lenghten active & recovery time so that cycle time is correct. + */ + + if (t->act8b + t->rec8b < t->cyc8b) { + t->act8b += (t->cyc8b - (t->act8b + t->rec8b)) / 2; + t->rec8b = t->cyc8b - t->act8b; + } + + if (t->active + t->recover < t->cycle) { + t->active += (t->cycle - (t->active + t->recover)) / 2; + t->recover = t->cycle - t->active; + } + + return 0; +} + +#endif diff -Nru a/drivers/ide/ide.c b/drivers/ide/ide.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,3837 @@ +/* + * linux/drivers/ide/ide.c Version 6.31 June 9, 2000 + * + * Copyright (C) 1994-1998 Linus Torvalds & authors (see below) + */ + +/* + * Mostly written by Mark Lord + * and Gadi Oxman + * and Andre Hedrick + * + * See linux/MAINTAINERS for address of current maintainer. + * + * This is the multiple IDE interface driver, as evolved from hd.c. + * It supports up to MAX_HWIFS IDE interfaces, on one or more IRQs (usually 14 & 15). + * There can be up to two drives per interface, as per the ATA-2 spec. + * + * Primary: ide0, port 0x1f0; major=3; hda is minor=0; hdb is minor=64 + * Secondary: ide1, port 0x170; major=22; hdc is minor=0; hdd is minor=64 + * Tertiary: ide2, port 0x???; major=33; hde is minor=0; hdf is minor=64 + * Quaternary: ide3, port 0x???; major=34; hdg is minor=0; hdh is minor=64 + * ... + * + * From hd.c: + * | + * | It traverses the request-list, using interrupts to jump between functions. + * | As nearly all functions can be called within interrupts, we may not sleep. + * | Special care is recommended. Have Fun! + * | + * | modified by Drew Eckhardt to check nr of hd's from the CMOS. + * | + * | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug + * | in the early extended-partition checks and added DM partitions. + * | + * | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI). + * | + * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads", + * | and general streamlining by Mark Lord (mlord@pobox.com). + * + * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by: + * + * Mark Lord (mlord@pobox.com) (IDE Perf.Pkg) + * Delman Lee (delman@ieee.org) ("Mr. atdisk2") + * Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom) + * + * This was a rewrite of just about everything from hd.c, though some original + * code is still sprinkled about. Think of it as a major evolution, with + * inspiration from lots of linux users, esp. hamish@zot.apana.org.au + * + * Version 1.0 ALPHA initial code, primary i/f working okay + * Version 1.3 BETA dual i/f on shared irq tested & working! + * Version 1.4 BETA added auto probing for irq(s) + * Version 1.5 BETA added ALPHA (untested) support for IDE cd-roms, + * ... + * Version 5.50 allow values as small as 20 for idebus= + * Version 5.51 force non io_32bit in drive_cmd_intr() + * change delay_10ms() to delay_50ms() to fix problems + * Version 5.52 fix incorrect invalidation of removable devices + * add "hdx=slow" command line option + * Version 5.60 start to modularize the driver; the disk and ATAPI + * drivers can be compiled as loadable modules. + * move IDE probe code to ide-probe.c + * move IDE disk code to ide-disk.c + * add support for generic IDE device subdrivers + * add m68k code from Geert Uytterhoeven + * probe all interfaces by default + * add ioctl to (re)probe an interface + * Version 6.00 use per device request queues + * attempt to optimize shared hwgroup performance + * add ioctl to manually adjust bandwidth algorithms + * add kerneld support for the probe module + * fix bug in ide_error() + * fix bug in the first ide_get_lock() call for Atari + * don't flush leftover data for ATAPI devices + * Version 6.01 clear hwgroup->active while the hwgroup sleeps + * support HDIO_GETGEO for floppies + * Version 6.02 fix ide_ack_intr() call + * check partition table on floppies + * Version 6.03 handle bad status bit sequencing in ide_wait_stat() + * Version 6.10 deleted old entries from this list of updates + * replaced triton.c with ide-dma.c generic PCI DMA + * added support for BIOS-enabled UltraDMA + * rename all "promise" things to "pdc4030" + * fix EZ-DRIVE handling on small disks + * Version 6.11 fix probe error in ide_scan_devices() + * fix ancient "jiffies" polling bugs + * mask all hwgroup interrupts on each irq entry + * Version 6.12 integrate ioctl and proc interfaces + * fix parsing of "idex=" command line parameter + * Version 6.13 add support for ide4/ide5 courtesy rjones@orchestream.com + * Version 6.14 fixed IRQ sharing among PCI devices + * Version 6.15 added SMP awareness to IDE drivers + * Version 6.16 fixed various bugs; even more SMP friendly + * Version 6.17 fix for newest EZ-Drive problem + * Version 6.18 default unpartitioned-disk translation now "BIOS LBA" + * Version 6.19 Re-design for a UNIFORM driver for all platforms, + * model based on suggestions from Russell King and + * Geert Uytterhoeven + * Promise DC4030VL now supported. + * add support for ide6/ide7 + * delay_50ms() changed to ide_delay_50ms() and exported. + * Version 6.20 Added/Fixed Generic ATA-66 support and hwif detection. + * Added hdx=flash to allow for second flash disk + * detection w/o the hang loop. + * Added support for ide8/ide9 + * Added idex=ata66 for the quirky chipsets that are + * ATA-66 compliant, but have yet to determine a method + * of verification of the 80c cable presence. + * Specifically Promise's PDC20262 chipset. + * Version 6.21 Fixing/Fixed SMP spinlock issue with insight from an old + * hat that clarified original low level driver design. + * Version 6.30 Added SMP support; fixed multmode issues. -ml + * Version 6.31 Debug Share INTR's and request queue streaming + * Native ATA-100 support + * Prep for Cascades Project + * + * Some additional driver compile-time options are in ./include/linux/ide.h + * + * To do, in likely order of completion: + * - modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f + * + */ + +#define REVISION "Revision: 6.31" +#define VERSION "Id: ide.c 6.31 2000/06/09" + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#define _IDE_C /* Tell ide.h it's really us */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifndef MODULE +#include +#endif /* MODULE */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "ide_modes.h" + +#ifdef CONFIG_KMOD +#include +#endif /* CONFIG_KMOD */ + +/* default maximum number of failures */ +#define IDE_DEFAULT_MAX_FAILURES 1 + +static const byte ide_hwif_to_major[] = { IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR, IDE4_MAJOR, IDE5_MAJOR, IDE6_MAJOR, IDE7_MAJOR, IDE8_MAJOR, IDE9_MAJOR }; + +static int idebus_parameter; /* holds the "idebus=" parameter */ +static int system_bus_speed; /* holds what we think is VESA/PCI bus speed */ +static int initializing; /* set while initializing built-in drivers */ + +spinlock_t ide_lock __cacheline_aligned_in_smp = SPIN_LOCK_UNLOCKED; + +#ifdef CONFIG_BLK_DEV_IDESCSI_24 +#define CONFIG_BLK_DEV_IDESCSI +extern int idescsi_init(void); +#endif + +#ifdef CONFIG_BLK_DEV_IDEPCI +static int ide_scan_direction; /* THIS was formerly 2.2.x pci=reverse */ +#endif /* CONFIG_BLK_DEV_IDEPCI */ + +#if defined(__mc68000__) || defined(CONFIG_APUS) +/* + * ide_lock is used by the Atari code to obtain access to the IDE interrupt, + * which is shared between several drivers. + */ +static int ide_intr_lock; +#endif /* __mc68000__ || CONFIG_APUS */ + +int noautodma = 0; + +/* + * ide_modules keeps track of the available IDE chipset/probe/driver modules. + */ +ide_module_t *ide_modules; +ide_module_t *ide_probe; + +/* + * This is declared extern in ide.h, for access by other IDE modules: + */ +ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */ + +#if (DISK_RECOVERY_TIME > 0) +/* + * For really screwy hardware (hey, at least it *can* be used with Linux) + * we can enforce a minimum delay time between successive operations. + */ +static unsigned long read_timer (void) +{ + unsigned long t, flags; + int i; + + local_irq_save(flags); + t = jiffies * 11932; + outb_p(0, 0x43); + i = inb_p(0x40); + i |= IN_BYTE(0x40) << 8; + local_irq_restore(flags); + return (t - i); +} +#endif /* DISK_RECOVERY_TIME */ + +static inline void set_recovery_timer (ide_hwif_t *hwif) +{ +#if (DISK_RECOVERY_TIME > 0) + hwif->last_time = read_timer(); +#endif /* DISK_RECOVERY_TIME */ +} + +/* + * Do not even *think* about calling this! + */ +static void init_hwif_data (unsigned int index) +{ + unsigned int unit; + hw_regs_t hw; + ide_hwif_t *hwif = &ide_hwifs[index]; + + /* bulk initialize hwif & drive info with zeros */ + memset(hwif, 0, sizeof(ide_hwif_t)); + memset(&hw, 0, sizeof(hw_regs_t)); + + /* fill in any non-zero initial values */ + hwif->index = index; + ide_init_hwif_ports(&hw, ide_default_io_base(index), 0, &hwif->irq); + memcpy(&hwif->hw, &hw, sizeof(hw)); + memcpy(hwif->io_ports, hw.io_ports, sizeof(hw.io_ports)); + hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET]; +#ifdef CONFIG_BLK_DEV_HD + if (hwif->io_ports[IDE_DATA_OFFSET] == HD_DATA) + hwif->noprobe = 1; /* may be overridden by ide_setup() */ +#endif /* CONFIG_BLK_DEV_HD */ + hwif->major = ide_hwif_to_major[index]; + hwif->name[0] = 'i'; + hwif->name[1] = 'd'; + hwif->name[2] = 'e'; + hwif->name[3] = '0' + index; + hwif->bus_state = BUSSTATE_ON; + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + + drive->media = ide_disk; + drive->select.all = (unit<<4)|0xa0; + drive->hwif = hwif; + drive->ctl = 0x08; + drive->ready_stat = READY_STAT; + drive->bad_wstat = BAD_W_STAT; + drive->special.b.recalibrate = 1; + drive->special.b.set_geometry = 1; + drive->name[0] = 'h'; + drive->name[1] = 'd'; + drive->name[2] = 'a' + (index * MAX_DRIVES) + unit; + drive->max_failures = IDE_DEFAULT_MAX_FAILURES; + init_waitqueue_head(&drive->wqueue); + } +} + +/* + * init_ide_data() sets reasonable default values into all fields + * of all instances of the hwifs and drives, but only on the first call. + * Subsequent calls have no effect (they don't wipe out anything). + * + * This routine is normally called at driver initialization time, + * but may also be called MUCH earlier during kernel "command-line" + * parameter processing. As such, we cannot depend on any other parts + * of the kernel (such as memory allocation) to be functioning yet. + * + * This is too bad, as otherwise we could dynamically allocate the + * ide_drive_t structs as needed, rather than always consuming memory + * for the max possible number (MAX_HWIFS * MAX_DRIVES) of them. + */ +#define MAGIC_COOKIE 0x12345678 +static void __init init_ide_data (void) +{ + unsigned int index; + static unsigned long magic_cookie = MAGIC_COOKIE; + + if (magic_cookie != MAGIC_COOKIE) + return; /* already initialized */ + magic_cookie = 0; + + /* Initialise all interface structures */ + for (index = 0; index < MAX_HWIFS; ++index) + init_hwif_data(index); + + /* Add default hw interfaces */ + ide_init_default_hwifs(); + + idebus_parameter = 0; + system_bus_speed = 0; +} + +/* + * CompactFlash cards and their brethern pretend to be removable hard disks, except: + * (1) they never have a slave unit, and + * (2) they don't have doorlock mechanisms. + * This test catches them, and is invoked elsewhere when setting appropriate config bits. + * + * FIXME: This treatment is probably applicable for *all* PCMCIA (PC CARD) devices, + * so in linux 2.3.x we should change this to just treat all PCMCIA drives this way, + * and get rid of the model-name tests below (too big of an interface change for 2.2.x). + * At that time, we might also consider parameterizing the timeouts and retries, + * since these are MUCH faster than mechanical drives. -M.Lord + */ +int drive_is_flashcard (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + + if (drive->removable && id != NULL) { + if (id->config == 0x848a) return 1; /* CompactFlash */ + if (!strncmp(id->model, "KODAK ATA_FLASH", 15) /* Kodak */ + || !strncmp(id->model, "Hitachi CV", 10) /* Hitachi */ + || !strncmp(id->model, "SunDisk SDCFB", 13) /* SunDisk */ + || !strncmp(id->model, "HAGIWARA HPC", 12) /* Hagiwara */ + || !strncmp(id->model, "LEXAR ATA_FLASH", 15) /* Lexar */ + || !strncmp(id->model, "ATA_FLASH", 9)) /* Simple Tech */ + { + return 1; /* yes, it is a flash memory card */ + } + } + return 0; /* no, it is not a flash memory card */ +} + +/* + * ide_system_bus_speed() returns what we think is the system VESA/PCI + * bus speed (in MHz). This is used for calculating interface PIO timings. + * The default is 40 for known PCI systems, 50 otherwise. + * The "idebus=xx" parameter can be used to override this value. + * The actual value to be used is computed/displayed the first time through. + */ +int ide_system_bus_speed (void) +{ + if (!system_bus_speed) { + if (idebus_parameter) + system_bus_speed = idebus_parameter; /* user supplied value */ +#ifdef CONFIG_PCI + else if (pci_present()) + system_bus_speed = 33; /* safe default value for PCI */ +#endif /* CONFIG_PCI */ + else + system_bus_speed = 50; /* safe default value for VESA and PCI */ + printk("ide: Assuming %dMHz system bus speed for PIO modes%s\n", system_bus_speed, + idebus_parameter ? "" : "; override with idebus=xx"); + } + return system_bus_speed; +} + +/* + * This is our end_request replacement function. + */ +int ide_end_request (ide_drive_t *drive, int uptodate) +{ + struct request *rq; + unsigned long flags; + int ret = 1; + + spin_lock_irqsave(&ide_lock, flags); + rq = HWGROUP(drive)->rq; + + BUG_ON(!(rq->flags & REQ_STARTED)); + + /* + * decide whether to reenable DMA -- 3 is a random magic for now, + * if we DMA timeout more than 3 times, just stay in PIO + */ + if (drive->state == DMA_PIO_RETRY && drive->retry_pio <= 3) { + drive->state = 0; + HWGROUP(drive)->hwif->dmaproc(ide_dma_on, drive); + } + + if (!end_that_request_first(rq, uptodate, rq->hard_cur_sectors)) { + add_blkdev_randomness(major(rq->rq_dev)); + blkdev_dequeue_request(rq); + HWGROUP(drive)->rq = NULL; + end_that_request_last(rq); + ret = 0; + } + spin_unlock_irqrestore(&ide_lock, flags); + return ret; +} + +/* + * This should get invoked any time we exit the driver to + * wait for an interrupt response from a drive. handler() points + * at the appropriate code to handle the next interrupt, and a + * timer is started to prevent us from waiting forever in case + * something goes wrong (see the ide_timer_expiry() handler later on). + */ +void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, + unsigned int timeout, ide_expiry_t *expiry) +{ + unsigned long flags; + ide_hwgroup_t *hwgroup = HWGROUP(drive); + + spin_lock_irqsave(&ide_lock, flags); + if (hwgroup->handler != NULL) { + printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n", + drive->name, hwgroup->handler, handler); + } + hwgroup->handler = handler; + hwgroup->expiry = expiry; + hwgroup->timer.expires = jiffies + timeout; + add_timer(&hwgroup->timer); + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * current_capacity() returns the capacity (in sectors) of a drive + * according to its current geometry/LBA settings. + */ +unsigned long current_capacity (ide_drive_t *drive) +{ + if (!drive->present) + return 0; + if (drive->driver != NULL) + return DRIVER(drive)->capacity(drive); + return 0; +} + +extern struct block_device_operations ide_fops[]; +/* + * ide_geninit() is called exactly *once* for each interface. + */ +void ide_geninit (ide_hwif_t *hwif) +{ + unsigned int unit; + + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + struct gendisk *gd = hwif->gd[unit]; + + if (!drive->present) + continue; + if (drive->media!=ide_disk && drive->media!=ide_floppy + && drive->media != ide_cdrom) + continue; + register_disk(gd,mk_kdev(hwif->major,unit<forced_geom && drive->noprobe) ? 1 : +#endif /* CONFIG_BLK_DEV_ISAPNP */ + 1<name); + } else { + if (time_before(jiffies, hwgroup->poll_timeout)) { + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20, NULL); + return ide_started; /* continue polling */ + } + hwgroup->poll_timeout = 0; /* end of polling */ + printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat); + return do_reset1 (drive, 1); /* do it the old fashioned way */ + } + hwgroup->poll_timeout = 0; /* done polling */ + return ide_stopped; +} + +/* + * reset_pollfunc() gets invoked to poll the interface for completion every 50ms + * during an ide reset operation. If the drives have not yet responded, + * and we have not yet hit our maximum waiting time, then the timer is restarted + * for another 50ms. + */ +static ide_startstop_t reset_pollfunc (ide_drive_t *drive) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + ide_hwif_t *hwif = HWIF(drive); + byte tmp; + + if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) { + if (time_before(jiffies, hwgroup->poll_timeout)) { + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler (drive, &reset_pollfunc, HZ/20, NULL); + return ide_started; /* continue polling */ + } + printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp); + drive->failures++; + } else { + printk("%s: reset: ", hwif->name); + if ((tmp = GET_ERR()) == 1) { + printk("success\n"); + drive->failures = 0; + } else { + drive->failures++; +#if FANCY_STATUS_DUMPS + printk("master: "); + switch (tmp & 0x7f) { + case 1: printk("passed"); + break; + case 2: printk("formatter device error"); + break; + case 3: printk("sector buffer error"); + break; + case 4: printk("ECC circuitry error"); + break; + case 5: printk("controlling MPU error"); + break; + default:printk("error (0x%02x?)", tmp); + } + if (tmp & 0x80) + printk("; slave: failed"); + printk("\n"); +#else + printk("failed\n"); +#endif /* FANCY_STATUS_DUMPS */ + } + } + hwgroup->poll_timeout = 0; /* done polling */ + return ide_stopped; +} + +static void check_dma_crc (ide_drive_t *drive) +{ + if (drive->crc_count) { + (void) HWIF(drive)->dmaproc(ide_dma_off_quietly, drive); + if ((HWIF(drive)->speedproc) != NULL) + HWIF(drive)->speedproc(drive, ide_auto_reduce_xfer(drive)); + if (drive->current_speed >= XFER_SW_DMA_0) + (void) HWIF(drive)->dmaproc(ide_dma_on, drive); + } else { + (void) HWIF(drive)->dmaproc(ide_dma_off, drive); + } +} + +static void pre_reset (ide_drive_t *drive) +{ + if (drive->driver != NULL) + DRIVER(drive)->pre_reset(drive); + + if (!drive->keep_settings) { + if (drive->using_dma) { + check_dma_crc(drive); + } else { + drive->unmask = 0; + drive->io_32bit = 0; + } + return; + } + if (drive->using_dma) + check_dma_crc(drive); +} + +/* + * do_reset1() attempts to recover a confused drive by resetting it. + * Unfortunately, resetting a disk drive actually resets all devices on + * the same interface, so it can really be thought of as resetting the + * interface rather than resetting the drive. + * + * ATAPI devices have their own reset mechanism which allows them to be + * individually reset without clobbering other devices on the same interface. + * + * Unfortunately, the IDE interface does not generate an interrupt to let + * us know when the reset operation has finished, so we must poll for this. + * Equally poor, though, is the fact that this may a very long time to complete, + * (up to 30 seconds worstcase). So, instead of busy-waiting here for it, + * we set a timer to poll at 50ms intervals. + */ +static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi) +{ + unsigned int unit; + unsigned long flags; + ide_hwif_t *hwif = HWIF(drive); + ide_hwgroup_t *hwgroup = HWGROUP(drive); + + local_irq_save(flags); + + /* For an ATAPI device, first try an ATAPI SRST. */ + if (drive->media != ide_disk && !do_not_try_atapi) { + pre_reset(drive); + SELECT_DRIVE(hwif,drive); + udelay (20); + OUT_BYTE (WIN_SRST, IDE_COMMAND_REG); + hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20, NULL); + local_irq_restore(flags); + return ide_started; + } + + /* + * First, reset any device state data we were maintaining + * for any of the drives on this interface. + */ + for (unit = 0; unit < MAX_DRIVES; ++unit) + pre_reset(&hwif->drives[unit]); + +#if OK_TO_RESET_CONTROLLER + if (!IDE_CONTROL_REG) { + local_irq_restore(flags); + return ide_stopped; + } + /* + * Note that we also set nIEN while resetting the device, + * to mask unwanted interrupts from the interface during the reset. + * However, due to the design of PC hardware, this will cause an + * immediate interrupt due to the edge transition it produces. + * This single interrupt gives us a "fast poll" for drives that + * recover from reset very quickly, saving us the first 50ms wait time. + */ + OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG); /* set SRST and nIEN */ + udelay(10); /* more than enough time */ + if (drive->quirk_list == 2) { + OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* clear SRST and nIEN */ + } else { + OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */ + } + udelay(10); /* more than enough time */ + hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler (drive, &reset_pollfunc, HZ/20, NULL); + + /* + * Some weird controller like resetting themselves to a strange + * state when the disks are reset this way. At least, the Winbond + * 553 documentation says that + */ + if (hwif->resetproc != NULL) + hwif->resetproc(drive); + +#endif /* OK_TO_RESET_CONTROLLER */ + + local_irq_restore(flags); + return ide_started; +} + +/* + * ide_do_reset() is the entry point to the drive/interface reset code. + */ +ide_startstop_t ide_do_reset (ide_drive_t *drive) +{ + return do_reset1 (drive, 0); +} + +static inline u32 read_24 (ide_drive_t *drive) +{ + return (IN_BYTE(IDE_HCYL_REG)<<16) | + (IN_BYTE(IDE_LCYL_REG)<<8) | + IN_BYTE(IDE_SECTOR_REG); +} + +/* + * Clean up after success/failure of an explicit drive cmd + */ +void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err) +{ + unsigned long flags; + struct request *rq; + + spin_lock_irqsave(&ide_lock, flags); + rq = HWGROUP(drive)->rq; + spin_unlock_irqrestore(&ide_lock, flags); + + if (rq->flags & REQ_DRIVE_CMD) { + byte *args = (byte *) rq->buffer; + if (rq->errors == 0) + rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); + + if (args) { + args[0] = stat; + args[1] = err; + args[2] = IN_BYTE(IDE_NSECTOR_REG); + } + } else if (rq->flags & REQ_DRIVE_TASK) { + byte *args = (byte *) rq->buffer; + if (rq->errors == 0) + rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); + + if (args) { + args[0] = stat; + args[1] = err; + args[2] = IN_BYTE(IDE_NSECTOR_REG); + args[3] = IN_BYTE(IDE_SECTOR_REG); + args[4] = IN_BYTE(IDE_LCYL_REG); + args[5] = IN_BYTE(IDE_HCYL_REG); + args[6] = IN_BYTE(IDE_SELECT_REG); + } + } else if (rq->flags & REQ_DRIVE_TASKFILE) { + ide_task_t *args = (ide_task_t *) rq->special; + if (rq->errors == 0) + rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT); + + if (args) { + if (args->tf_in_flags.b.data) { + unsigned short data = IN_WORD(IDE_DATA_REG); + args->tfRegister[IDE_DATA_OFFSET] = (data) & 0xFF; + args->hobRegister[IDE_DATA_OFFSET_HOB] = (data >> 8) & 0xFF; + } + args->tfRegister[IDE_ERROR_OFFSET] = err; + args->tfRegister[IDE_NSECTOR_OFFSET] = IN_BYTE(IDE_NSECTOR_REG); + args->tfRegister[IDE_SECTOR_OFFSET] = IN_BYTE(IDE_SECTOR_REG); + args->tfRegister[IDE_LCYL_OFFSET] = IN_BYTE(IDE_LCYL_REG); + args->tfRegister[IDE_HCYL_OFFSET] = IN_BYTE(IDE_HCYL_REG); + args->tfRegister[IDE_SELECT_OFFSET] = IN_BYTE(IDE_SELECT_REG); + args->tfRegister[IDE_STATUS_OFFSET] = stat; + + if ((drive->id->command_set_2 & 0x0400) && + (drive->id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + OUT_BYTE(drive->ctl|0x80, IDE_CONTROL_REG_HOB); + args->hobRegister[IDE_FEATURE_OFFSET_HOB] = IN_BYTE(IDE_FEATURE_REG); + args->hobRegister[IDE_NSECTOR_OFFSET_HOB] = IN_BYTE(IDE_NSECTOR_REG); + args->hobRegister[IDE_SECTOR_OFFSET_HOB] = IN_BYTE(IDE_SECTOR_REG); + args->hobRegister[IDE_LCYL_OFFSET_HOB] = IN_BYTE(IDE_LCYL_REG); + args->hobRegister[IDE_HCYL_OFFSET_HOB] = IN_BYTE(IDE_HCYL_REG); + } + } + } + + spin_lock_irqsave(&ide_lock, flags); + blkdev_dequeue_request(rq); + HWGROUP(drive)->rq = NULL; + end_that_request_last(rq); + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * Error reporting, in human readable form (luxurious, but a memory hog). + */ +byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat) +{ + unsigned long flags; + byte err = 0; + + local_irq_set(flags); + printk("%s: %s: status=0x%02x", drive->name, msg, stat); +#if FANCY_STATUS_DUMPS + printk(" { "); + if (stat & BUSY_STAT) + printk("Busy "); + else { + if (stat & READY_STAT) printk("DriveReady "); + if (stat & WRERR_STAT) printk("DeviceFault "); + if (stat & SEEK_STAT) printk("SeekComplete "); + if (stat & DRQ_STAT) printk("DataRequest "); + if (stat & ECC_STAT) printk("CorrectedError "); + if (stat & INDEX_STAT) printk("Index "); + if (stat & ERR_STAT) printk("Error "); + } + printk("}"); +#endif /* FANCY_STATUS_DUMPS */ + printk("\n"); + if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) { + err = GET_ERR(); + printk("%s: %s: error=0x%02x", drive->name, msg, err); +#if FANCY_STATUS_DUMPS + if (drive->media == ide_disk) { + printk(" { "); + if (err & ABRT_ERR) printk("DriveStatusError "); + if (err & ICRC_ERR) printk("%s", (err & ABRT_ERR) ? "BadCRC " : "BadSector "); + if (err & ECC_ERR) printk("UncorrectableError "); + if (err & ID_ERR) printk("SectorIdNotFound "); + if (err & TRK0_ERR) printk("TrackZeroNotFound "); + if (err & MARK_ERR) printk("AddrMarkNotFound "); + printk("}"); + if ((err & (BBD_ERR | ABRT_ERR)) == BBD_ERR || (err & (ECC_ERR|ID_ERR|MARK_ERR))) { + if ((drive->id->command_set_2 & 0x0400) && + (drive->id->cfs_enable_2 & 0x0400) && + (drive->addressing == 1)) { + __u64 sectors = 0; + u32 low = 0, high = 0; + low = read_24(drive); + OUT_BYTE(drive->ctl|0x80, IDE_CONTROL_REG); + high = read_24(drive); + + sectors = ((__u64)high << 24) | low; + printk(", LBAsect=%llu, high=%d, low=%d", + (unsigned long long) sectors, + high, low); + } else { + byte cur = IN_BYTE(IDE_SELECT_REG); + if (cur & 0x40) { /* using LBA? */ + printk(", LBAsect=%ld", (unsigned long) + ((cur&0xf)<<24) + |(IN_BYTE(IDE_HCYL_REG)<<16) + |(IN_BYTE(IDE_LCYL_REG)<<8) + | IN_BYTE(IDE_SECTOR_REG)); + } else { + printk(", CHS=%d/%d/%d", + (IN_BYTE(IDE_HCYL_REG)<<8) + + IN_BYTE(IDE_LCYL_REG), + cur & 0xf, + IN_BYTE(IDE_SECTOR_REG)); + } + } + if (HWGROUP(drive) && HWGROUP(drive)->rq) + printk(", sector=%ld", HWGROUP(drive)->rq->sector); + } + } +#endif /* FANCY_STATUS_DUMPS */ + printk("\n"); + } + local_irq_restore(flags); + return err; +} + +/* + * try_to_flush_leftover_data() is invoked in response to a drive + * unexpectedly having its DRQ_STAT bit set. As an alternative to + * resetting the drive, this routine tries to clear the condition + * by read a sector's worth of data from the drive. Of course, + * this may not help if the drive is *waiting* for data from *us*. + */ +static void try_to_flush_leftover_data (ide_drive_t *drive) +{ + int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS; + + if (drive->media != ide_disk) + return; + while (i > 0) { + u32 buffer[16]; + unsigned int wcount = (i > 16) ? 16 : i; + i -= wcount; + ata_input_data (drive, buffer, wcount); + } +} + +/* + * FIXME Add an ATAPI error + */ + +/* + * ide_error() takes action based on the error returned by the drive. + */ +ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, byte stat) +{ + struct request *rq; + byte err; + + err = ide_dump_status(drive, msg, stat); + if (drive == NULL || (rq = HWGROUP(drive)->rq) == NULL) + return ide_stopped; + /* retry only "normal" I/O: */ + if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK)) { + rq->errors = 1; + ide_end_drive_cmd(drive, stat, err); + return ide_stopped; + } else if (rq->flags & REQ_DRIVE_TASKFILE) { + rq->errors = 1; + ide_end_drive_cmd(drive, stat, err); +// ide_end_taskfile(drive, stat, err); + return ide_stopped; + } + + if (stat & BUSY_STAT || ((stat & WRERR_STAT) && !drive->nowerr)) { /* other bits are useless when BUSY */ + rq->errors |= ERROR_RESET; + } else { + +/* ide_disk */ + if (drive->media == ide_disk && (stat & ERR_STAT)) { + /* err has different meaning on cdrom and tape */ + if (err == ABRT_ERR) { + if (drive->select.b.lba && IN_BYTE(IDE_COMMAND_REG) == WIN_SPECIFY) + return ide_stopped; /* some newer drives don't support WIN_SPECIFY */ + } else if ((err & (ABRT_ERR | ICRC_ERR)) == (ABRT_ERR | ICRC_ERR)) { + drive->crc_count++; /* UDMA crc error -- just retry the operation */ + } else if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */ + rq->errors = ERROR_MAX; + else if (err & TRK0_ERR) /* help it find track zero */ + rq->errors |= ERROR_RECAL; + } +/* !ide_disk */ + if ((stat & DRQ_STAT) && rq_data_dir(rq) == READ) + try_to_flush_leftover_data(drive); +/* !ide_disk */ + } + if (GET_STAT() & (BUSY_STAT|DRQ_STAT)) + OUT_BYTE(WIN_IDLEIMMEDIATE,IDE_COMMAND_REG); /* force an abort */ + + if (rq->errors >= ERROR_MAX) { + if (drive->driver != NULL) + DRIVER(drive)->end_request(drive, 0); + else + ide_end_request(drive, 0); + } else { + if ((rq->errors & ERROR_RESET) == ERROR_RESET) { + ++rq->errors; + return ide_do_reset(drive); + } + if ((rq->errors & ERROR_RECAL) == ERROR_RECAL) + drive->special.b.recalibrate = 1; + ++rq->errors; + } + return ide_stopped; +} + +/* + * Issue a simple drive command + * The drive must be selected beforehand. + */ +void ide_cmd (ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler) +{ + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler (drive, handler, WAIT_CMD, NULL); + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* clear nIEN */ + SELECT_MASK(HWIF(drive),drive,0); + OUT_BYTE(nsect,IDE_NSECTOR_REG); + OUT_BYTE(cmd,IDE_COMMAND_REG); +} + +/* + * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD. + */ +static ide_startstop_t drive_cmd_intr (ide_drive_t *drive) +{ + struct request *rq = HWGROUP(drive)->rq; + byte *args = (byte *) rq->buffer; + byte stat = GET_STAT(); + int retries = 10; + + local_irq_enable(); + if ((stat & DRQ_STAT) && args && args[3]) { + byte io_32bit = drive->io_32bit; + drive->io_32bit = 0; + ata_input_data(drive, &args[4], args[3] * SECTOR_WORDS); + drive->io_32bit = io_32bit; + while (((stat = GET_STAT()) & BUSY_STAT) && retries--) + udelay(100); + } + + if (!OK_STAT(stat, READY_STAT, BAD_STAT)) + return DRIVER(drive)->error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */ + ide_end_drive_cmd (drive, stat, GET_ERR()); + return ide_stopped; +} + +/* + * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT + * commands to a drive. It used to do much more, but has been scaled back. + */ +static ide_startstop_t do_special (ide_drive_t *drive) +{ + special_t *s = &drive->special; + +#ifdef DEBUG + printk("%s: do_special: 0x%02x\n", drive->name, s->all); +#endif + if (s->b.set_tune) { + s->b.set_tune = 0; + if (HWIF(drive)->tuneproc != NULL) + HWIF(drive)->tuneproc(drive, drive->tune_req); + } else if (drive->driver != NULL) { + return DRIVER(drive)->special(drive); + } else if (s->all) { + printk("%s: bad special flag: 0x%02x\n", drive->name, s->all); + s->all = 0; + } + return ide_stopped; +} + +/* + * execute_drive_cmd() issues a special drive command, + * usually initiated by ioctl() from the external hdparm program. + */ +static ide_startstop_t execute_drive_cmd (ide_drive_t *drive, struct request *rq) +{ + if (rq->flags & REQ_DRIVE_TASKFILE) { + ide_task_t *args = rq->special; + + if (!args) + goto done; + + if (args->tf_out_flags.all != 0) + return flagged_taskfile(drive, args); + return do_rw_taskfile(drive, args); + } else if (rq->flags & REQ_DRIVE_TASK) { + byte *args = rq->buffer; + byte sel; + + if (!args) + goto done; +#ifdef DEBUG + printk("%s: DRIVE_TASK_CMD ", drive->name); + printk("cmd=0x%02x ", args[0]); + printk("fr=0x%02x ", args[1]); + printk("ns=0x%02x ", args[2]); + printk("sc=0x%02x ", args[3]); + printk("lcyl=0x%02x ", args[4]); + printk("hcyl=0x%02x ", args[5]); + printk("sel=0x%02x\n", args[6]); +#endif + OUT_BYTE(args[1], IDE_FEATURE_REG); + OUT_BYTE(args[3], IDE_SECTOR_REG); + OUT_BYTE(args[4], IDE_LCYL_REG); + OUT_BYTE(args[5], IDE_HCYL_REG); + sel = (args[6] & ~0x10); + if (drive->select.b.unit) + sel |= 0x10; + OUT_BYTE(sel, IDE_SELECT_REG); + ide_cmd(drive, args[0], args[2], &drive_cmd_intr); + return ide_started; + } else if (rq->flags & REQ_DRIVE_CMD) { + byte *args = rq->buffer; + + if (!args) + goto done; +#ifdef DEBUG + printk("%s: DRIVE_CMD ", drive->name); + printk("cmd=0x%02x ", args[0]); + printk("sc=0x%02x ", args[1]); + printk("fr=0x%02x ", args[2]); + printk("xx=0x%02x\n", args[3]); +#endif + if (args[0] == WIN_SMART) { + OUT_BYTE(0x4f, IDE_LCYL_REG); + OUT_BYTE(0xc2, IDE_HCYL_REG); + OUT_BYTE(args[2],IDE_FEATURE_REG); + OUT_BYTE(args[1],IDE_SECTOR_REG); + ide_cmd(drive, args[0], args[3], &drive_cmd_intr); + return ide_started; + } + OUT_BYTE(args[2],IDE_FEATURE_REG); + ide_cmd(drive, args[0], args[1], &drive_cmd_intr); + return ide_started; + } + +done: + /* + * NULL is actually a valid way of waiting for + * all current requests to be flushed from the queue. + */ +#ifdef DEBUG + printk("%s: DRIVE_CMD (null)\n", drive->name); +#endif + ide_end_drive_cmd(drive, GET_STAT(), GET_ERR()); + return ide_stopped; +} + +/* + * start_request() initiates handling of a new I/O request + * needed to reverse the perverted changes anonymously made back + * 2.3.99-pre6 + */ +static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq) +{ + ide_startstop_t startstop; + unsigned long block; + unsigned int minor = minor(rq->rq_dev), unit = minor >> PARTN_BITS; + ide_hwif_t *hwif = HWIF(drive); + + BUG_ON(!(rq->flags & REQ_STARTED)); + +#ifdef DEBUG + printk("%s: start_request: current=0x%08lx\n", + hwif->name, (unsigned long) rq); +#endif + + /* bail early if we've exceeded max_failures */ + if (drive->max_failures && (drive->failures > drive->max_failures)) { + goto kill_rq; + } + + /* + * bail early if we've sent a device to sleep, however how to wake + * this needs to be a masked flag. FIXME for proper operations. + */ + if (drive->suspend_reset) { + goto kill_rq; + } + + if (unit >= MAX_DRIVES) { + printk("%s: bad device number: %s\n", + hwif->name, kdevname(rq->rq_dev)); + goto kill_rq; + } +#ifdef DEBUG + if (rq->bh && !buffer_locked(rq->bh)) { + printk("%s: block not locked\n", drive->name); + goto kill_rq; + } +#endif + block = rq->sector; + + if ((rq->flags & REQ_CMD) && + (drive->media == ide_disk || drive->media == ide_floppy)) { + block += drive->sect0; + } + /* Yecch - this will shift the entire interval, + possibly killing some innocent following sector */ + if (block == 0 && drive->remap_0_to_1 == 1) + block = 1; /* redirect MBR access to EZ-Drive partn table */ + +#if (DISK_RECOVERY_TIME > 0) + while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME); +#endif + + SELECT_DRIVE(hwif, drive); + if (ide_wait_stat(&startstop, drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) { + printk("%s: drive not ready for command\n", drive->name); + return startstop; + } + if (!drive->special.all) { + if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK)) + return execute_drive_cmd(drive, rq); + else if (rq->flags & REQ_DRIVE_TASKFILE) + return execute_drive_cmd(drive, rq); + + if (drive->driver != NULL) { + return (DRIVER(drive)->do_request(drive, rq, block)); + } + printk("%s: media type %d not supported\n", drive->name, drive->media); + goto kill_rq; + } + return do_special(drive); +kill_rq: + if (drive->driver != NULL) + DRIVER(drive)->end_request(drive, 0); + else + ide_end_request(drive, 0); + return ide_stopped; +} + +int restart_request (ide_drive_t *drive, struct request *rq) +{ + (void) start_request(drive, rq); + return 0; +} + +/* + * ide_stall_queue() can be used by a drive to give excess bandwidth back + * to the hwgroup by sleeping for timeout jiffies. + */ +void ide_stall_queue (ide_drive_t *drive, unsigned long timeout) +{ + if (timeout > WAIT_WORSTCASE) + timeout = WAIT_WORSTCASE; + drive->sleep = timeout + jiffies; +} + +#define WAKEUP(drive) ((drive)->service_start + 2 * (drive)->service_time) + +/* + * choose_drive() selects the next drive which will be serviced. + */ +static inline ide_drive_t *choose_drive (ide_hwgroup_t *hwgroup) +{ + ide_drive_t *drive, *best; + +repeat: + best = NULL; + drive = hwgroup->drive; + do { + if (!list_empty(&drive->queue.queue_head) && (!drive->sleep || time_after_eq(jiffies, drive->sleep))) { + if (!best + || (drive->sleep && (!best->sleep || 0 < (signed long)(best->sleep - drive->sleep))) + || (!best->sleep && 0 < (signed long)(WAKEUP(best) - WAKEUP(drive)))) + { + if (!blk_queue_plugged(&drive->queue)) + best = drive; + } + } + } while ((drive = drive->next) != hwgroup->drive); + if (best && best->nice1 && !best->sleep && best != hwgroup->drive && best->service_time > WAIT_MIN_SLEEP) { + long t = (signed long)(WAKEUP(best) - jiffies); + if (t >= WAIT_MIN_SLEEP) { + /* + * We *may* have some time to spare, but first let's see if + * someone can potentially benefit from our nice mood today.. + */ + drive = best->next; + do { + if (!drive->sleep + && 0 < (signed long)(WAKEUP(drive) - (jiffies - best->service_time)) + && 0 < (signed long)((jiffies + t) - WAKEUP(drive))) + { + ide_stall_queue(best, IDE_MIN(t, 10 * WAIT_MIN_SLEEP)); + goto repeat; + } + } while ((drive = drive->next) != best); + } + } + return best; +} + +/* + * Issue a new request to a drive from hwgroup + * Caller must have already done spin_lock_irqsave(&ide_lock, ..); + * + * A hwgroup is a serialized group of IDE interfaces. Usually there is + * exactly one hwif (interface) per hwgroup, but buggy controllers (eg. CMD640) + * may have both interfaces in a single hwgroup to "serialize" access. + * Or possibly multiple ISA interfaces can share a common IRQ by being grouped + * together into one hwgroup for serialized access. + * + * Note also that several hwgroups can end up sharing a single IRQ, + * possibly along with many other devices. This is especially common in + * PCI-based systems with off-board IDE controller cards. + * + * The IDE driver uses the single global ide_lock spinlock to protect + * access to the request queues, and to protect the hwgroup->busy flag. + * + * The first thread into the driver for a particular hwgroup sets the + * hwgroup->busy flag to indicate that this hwgroup is now active, + * and then initiates processing of the top request from the request queue. + * + * Other threads attempting entry notice the busy setting, and will simply + * queue their new requests and exit immediately. Note that hwgroup->busy + * remains set even when the driver is merely awaiting the next interrupt. + * Thus, the meaning is "this hwgroup is busy processing a request". + * + * When processing of a request completes, the completing thread or IRQ-handler + * will start the next request from the queue. If no more work remains, + * the driver will clear the hwgroup->busy flag and exit. + * + * The ide_lock (spinlock) is used to protect all access to the + * hwgroup->busy flag, but is otherwise not needed for most processing in + * the driver. This makes the driver much more friendlier to shared IRQs + * than previous designs, while remaining 100% (?) SMP safe and capable. + */ +/* --BenH: made non-static as ide-pmac.c uses it to kick the hwgroup back + * into life on wakeup from machine sleep. + */ +void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq) +{ + ide_drive_t *drive; + ide_hwif_t *hwif; + struct request *rq; + ide_startstop_t startstop; + + ide_get_lock(&ide_intr_lock, ide_intr, hwgroup);/* for atari only: POSSIBLY BROKEN HERE(?) */ + + local_irq_disable(); + /* necessary paranoia: ensure IRQs are masked on local CPU */ + + while (!hwgroup->busy) { + hwgroup->busy = 1; + drive = choose_drive(hwgroup); + if (drive == NULL) { + unsigned long sleep = 0; + hwgroup->rq = NULL; + drive = hwgroup->drive; + do { + if (drive->sleep && (!sleep || 0 < (signed long)(sleep - drive->sleep))) + sleep = drive->sleep; + } while ((drive = drive->next) != hwgroup->drive); + if (sleep) { + /* + * Take a short snooze, and then wake up this hwgroup again. + * This gives other hwgroups on the same a chance to + * play fairly with us, just in case there are big differences + * in relative throughputs.. don't want to hog the cpu too much. + */ + if (time_before(sleep, jiffies + WAIT_MIN_SLEEP)) + sleep = jiffies + WAIT_MIN_SLEEP; +#if 1 + if (timer_pending(&hwgroup->timer)) + printk("ide_set_handler: timer already active\n"); +#endif + hwgroup->sleeping = 1; /* so that ide_timer_expiry knows what to do */ + mod_timer(&hwgroup->timer, sleep); + /* we purposely leave hwgroup->busy==1 while sleeping */ + } else { + /* Ugly, but how can we sleep for the lock otherwise? perhaps from tq_disk? */ + ide_release_lock(&ide_lock); /* for atari only */ + hwgroup->busy = 0; + } + return; /* no more work for this hwgroup (for now) */ + } + hwif = HWIF(drive); + if (hwgroup->hwif->sharing_irq && hwif != hwgroup->hwif && hwif->io_ports[IDE_CONTROL_OFFSET]) { + /* set nIEN for previous hwif */ + SELECT_INTERRUPT(hwif, drive); + } + hwgroup->hwif = hwif; + hwgroup->drive = drive; + drive->sleep = 0; + drive->service_start = jiffies; + + BUG_ON(blk_queue_plugged(&drive->queue)); + + rq = hwgroup->rq = elv_next_request(&drive->queue); + + /* + * Some systems have trouble with IDE IRQs arriving while + * the driver is still setting things up. So, here we disable + * the IRQ used by this interface while the request is being started. + * This may look bad at first, but pretty much the same thing + * happens anyway when any interrupt comes in, IDE or otherwise + * -- the kernel masks the IRQ while it is being handled. + */ + if (masked_irq && hwif->irq != masked_irq) + disable_irq_nosync(hwif->irq); + spin_unlock(&ide_lock); + local_irq_enable(); + /* allow other IRQs while we start this request */ + startstop = start_request(drive, rq); + spin_lock_irq(&ide_lock); + if (masked_irq && hwif->irq != masked_irq) + enable_irq(hwif->irq); + if (startstop == ide_stopped) + hwgroup->busy = 0; + } +} + +/* + * ide_get_queue() returns the queue which corresponds to a given device. + */ +request_queue_t *ide_get_queue (kdev_t dev) +{ + ide_hwif_t *hwif = (ide_hwif_t *)blk_dev[major(dev)].data; + + return &hwif->drives[DEVICE_NR(dev) & 1].queue; +} + +/* + * Passes the stuff to ide_do_request + */ +void do_ide_request(request_queue_t *q) +{ + ide_do_request(q->queuedata, 0); +} + +#ifndef __IDEDMA_TIMEOUT +/* + * un-busy the hwgroup etc, and clear any pending DMA status. we want to + * retry the current request in pio mode instead of risking tossing it + * all away + */ +void ide_dma_timeout_retry(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct request *rq; + + /* + * end current dma transaction + */ + (void) hwif->dmaproc(ide_dma_end, drive); + + /* + * complain a little, later we might remove some of this verbosity + */ + printk("%s: timeout waiting for DMA\n", drive->name); + (void) hwif->dmaproc(ide_dma_timeout, drive); + + /* + * disable dma for now, but remember that we did so because of + * a timeout -- we'll reenable after we finish this next request + * (or rather the first chunk of it) in pio. + */ + drive->retry_pio++; + drive->state = DMA_PIO_RETRY; + (void) hwif->dmaproc(ide_dma_off_quietly, drive); + + /* + * un-busy drive etc (hwgroup->busy is cleared on return) and + * make sure request is sane + */ + rq = HWGROUP(drive)->rq; + HWGROUP(drive)->rq = NULL; + + rq->errors = 0; + rq->sector = rq->bio->bi_sector; + rq->current_nr_sectors = bio_iovec(rq->bio)->bv_len >> 9; + rq->hard_cur_sectors = rq->current_nr_sectors; + if (rq->bio) + rq->buffer = NULL; + + /* + * FIXME or DELETE ME + * + * so what do we do if the device is left in an invalid state + * and will not accept commands. SOFT RESET is the only chance. + */ +} +#endif + +/* + * ide_timer_expiry() is our timeout function for all drive operations. + * But note that it can also be invoked as a result of a "sleep" operation + * triggered by the mod_timer() call in ide_do_request. + */ +void ide_timer_expiry (unsigned long data) +{ + ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data; + ide_handler_t *handler; + ide_expiry_t *expiry; + unsigned long flags; + unsigned long wait; + + spin_lock_irqsave(&ide_lock, flags); + del_timer(&hwgroup->timer); + + if ((handler = hwgroup->handler) == NULL) { + /* + * Either a marginal timeout occurred + * (got the interrupt just as timer expired), + * or we were "sleeping" to give other devices a chance. + * Either way, we don't really want to complain about anything. + */ + if (hwgroup->sleeping) { + hwgroup->sleeping = 0; + hwgroup->busy = 0; + } + } else { + ide_drive_t *drive = hwgroup->drive; + if (!drive) { + printk("ide_timer_expiry: hwgroup->drive was NULL\n"); + hwgroup->handler = NULL; + } else { + ide_hwif_t *hwif; + ide_startstop_t startstop = ide_stopped; + if (!hwgroup->busy) { + hwgroup->busy = 1; /* paranoia */ + printk("%s: ide_timer_expiry: hwgroup->busy was 0 ??\n", drive->name); + } + if ((expiry = hwgroup->expiry) != NULL) { + /* continue */ + if ((wait = expiry(drive)) != 0) { + /* reset timer */ + hwgroup->timer.expires = jiffies + wait; + add_timer(&hwgroup->timer); + spin_unlock_irqrestore(&ide_lock, flags); + return; + } + } + hwgroup->handler = NULL; + /* + * We need to simulate a real interrupt when invoking + * the handler() function, which means we need to globally + * mask the specific IRQ: + */ + spin_unlock(&ide_lock); + hwif = HWIF(drive); +#if DISABLE_IRQ_NOSYNC + disable_irq_nosync(hwif->irq); +#else + disable_irq(hwif->irq); /* disable_irq_nosync ?? */ +#endif /* DISABLE_IRQ_NOSYNC */ + local_irq_disable(); + /* local CPU only, as if we were handling an interrupt */ + if (hwgroup->poll_timeout != 0) { + startstop = handler(drive); + } else if (drive_is_ready(drive)) { + if (drive->waiting_for_dma) + (void) hwgroup->hwif->dmaproc(ide_dma_lostirq, drive); + (void)ide_ack_intr(hwif); + printk("%s: lost interrupt\n", drive->name); + startstop = handler(drive); + } else { + if (drive->waiting_for_dma) { +#ifndef __IDEDMA_TIMEOUT + startstop = ide_stopped; + ide_dma_timeout_retry(drive); +#else /* __IDEDMA_TIMEOUT */ + (void) hwgroup->hwif->dmaproc(ide_dma_end, drive); + printk("%s: timeout waiting for DMA\n", drive->name); + (void) hwgroup->hwif->dmaproc(ide_dma_timeout, drive); +#endif /* __IDEDMA_TIMEOUT */ + } else + startstop = DRIVER(drive)->error(drive, "irq timeout", GET_STAT()); + } + set_recovery_timer(hwif); + drive->service_time = jiffies - drive->service_start; + enable_irq(hwif->irq); + spin_lock_irq(&ide_lock); + if (startstop == ide_stopped) + hwgroup->busy = 0; + } + } + ide_do_request(hwgroup, 0); + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * There's nothing really useful we can do with an unexpected interrupt, + * other than reading the status register (to clear it), and logging it. + * There should be no way that an irq can happen before we're ready for it, + * so we needn't worry much about losing an "important" interrupt here. + * + * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the + * drive enters "idle", "standby", or "sleep" mode, so if the status looks + * "good", we just ignore the interrupt completely. + * + * This routine assumes __cli() is in effect when called. + * + * If an unexpected interrupt happens on irq15 while we are handling irq14 + * and if the two interfaces are "serialized" (CMD640), then it looks like + * we could screw up by interfering with a new request being set up for irq15. + * + * In reality, this is a non-issue. The new command is not sent unless the + * drive is ready to accept one, in which case we know the drive is not + * trying to interrupt us. And ide_set_handler() is always invoked before + * completing the issuance of any new drive command, so we will not be + * accidentally invoked as a result of any valid command completion interrupt. + * + */ +static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup) +{ + byte stat; + ide_hwif_t *hwif = hwgroup->hwif; + + /* + * handle the unexpected interrupt + */ + do { + if (hwif->irq == irq) { + stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); + if (!OK_STAT(stat, READY_STAT, BAD_STAT)) { + /* Try to not flood the console with msgs */ + static unsigned long last_msgtime, count; + ++count; + if (time_after(jiffies, last_msgtime + HZ)) { + last_msgtime = jiffies; + printk("%s%s: unexpected interrupt, status=0x%02x, count=%ld\n", + hwif->name, (hwif->next == hwgroup->hwif) ? "" : "(?)", stat, count); + } + } + } + } while ((hwif = hwif->next) != hwgroup->hwif); +} + +/* + * entry point for all interrupts, caller does __cli() for us + */ +void ide_intr (int irq, void *dev_id, struct pt_regs *regs) +{ + unsigned long flags; + ide_hwgroup_t *hwgroup = (ide_hwgroup_t *)dev_id; + ide_hwif_t *hwif; + ide_drive_t *drive; + ide_handler_t *handler; + ide_startstop_t startstop; + + spin_lock_irqsave(&ide_lock, flags); + hwif = hwgroup->hwif; + + if (!ide_ack_intr(hwif)) { + spin_unlock_irqrestore(&ide_lock, flags); + return; + } + + if ((handler = hwgroup->handler) == NULL || hwgroup->poll_timeout != 0) { + /* + * Not expecting an interrupt from this drive. + * That means this could be: + * (1) an interrupt from another PCI device + * sharing the same PCI INT# as us. + * or (2) a drive just entered sleep or standby mode, + * and is interrupting to let us know. + * or (3) a spurious interrupt of unknown origin. + * + * For PCI, we cannot tell the difference, + * so in that case we just ignore it and hope it goes away. + */ +#ifdef CONFIG_BLK_DEV_IDEPCI + if (IDE_PCI_DEVID_EQ(hwif->pci_devid, IDE_PCI_DEVID_NULL)) +#endif /* CONFIG_BLK_DEV_IDEPCI */ + { + /* + * Probably not a shared PCI interrupt, + * so we can safely try to do something about it: + */ + unexpected_intr(irq, hwgroup); +#ifdef CONFIG_BLK_DEV_IDEPCI + } else { + /* + * Whack the status register, just in case + * we have a leftover pending IRQ. + */ + (void) IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); +#endif /* CONFIG_BLK_DEV_IDEPCI */ + } + spin_unlock_irqrestore(&ide_lock, flags); + return; + } + drive = hwgroup->drive; + if (!drive) { + /* + * This should NEVER happen, and there isn't much + * we could do about it here. + */ + spin_unlock_irqrestore(&ide_lock, flags); + return; + } + if (!drive_is_ready(drive)) { + /* + * This happens regularly when we share a PCI IRQ with + * another device. Unfortunately, it can also happen + * with some buggy drives that trigger the IRQ before + * their status register is up to date. Hopefully we have + * enough advance overhead that the latter isn't a problem. + */ + spin_unlock_irqrestore(&ide_lock, flags); + return; + } + if (!hwgroup->busy) { + hwgroup->busy = 1; /* paranoia */ + printk("%s: ide_intr: hwgroup->busy was 0 ??\n", drive->name); + } + hwgroup->handler = NULL; + del_timer(&hwgroup->timer); + spin_unlock(&ide_lock); + + if (drive->unmask) + local_irq_enable(); + startstop = handler(drive); /* service this interrupt, may set handler for next interrupt */ + spin_lock_irq(&ide_lock); + + /* + * Note that handler() may have set things up for another + * interrupt to occur soon, but it cannot happen until + * we exit from this routine, because it will be the + * same irq as is currently being serviced here, and Linux + * won't allow another of the same (on any CPU) until we return. + */ + set_recovery_timer(HWIF(drive)); + drive->service_time = jiffies - drive->service_start; + if (startstop == ide_stopped) { + if (hwgroup->handler == NULL) { /* paranoia */ + hwgroup->busy = 0; + ide_do_request(hwgroup, hwif->irq); + } else { + printk("%s: ide_intr: huh? expected NULL handler on exit\n", drive->name); + } + } + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * get_info_ptr() returns the (ide_drive_t *) for a given device number. + * It returns NULL if the given device number does not match any present drives. + */ +ide_drive_t *get_info_ptr (kdev_t i_rdev) +{ + int major = major(i_rdev); +#if 0 + int minor = minor(i_rdev) & PARTN_MASK; +#endif + unsigned int h; + + for (h = 0; h < MAX_HWIFS; ++h) { + ide_hwif_t *hwif = &ide_hwifs[h]; + if (hwif->present && major == hwif->major) { + unsigned unit = DEVICE_NR(i_rdev); + if (unit < MAX_DRIVES) { + ide_drive_t *drive = &hwif->drives[unit]; +#if 0 + if ((drive->present) && (drive->part[minor].nr_sects)) +#else + if (drive->present) +#endif + return drive; + } + break; + } + } + return NULL; +} + +/* + * This function is intended to be used prior to invoking ide_do_drive_cmd(). + */ +void ide_init_drive_cmd (struct request *rq) +{ + memset(rq, 0, sizeof(*rq)); + rq->flags = REQ_DRIVE_CMD; +} + +/* + * This function issues a special IDE device request + * onto the request queue. + * + * If action is ide_wait, then the rq is queued at the end of the + * request queue, and the function sleeps until it has been processed. + * This is for use when invoked from an ioctl handler. + * + * If action is ide_preempt, then the rq is queued at the head of + * the request queue, displacing the currently-being-processed + * request and this function returns immediately without waiting + * for the new rq to be completed. This is VERY DANGEROUS, and is + * intended for careful use by the ATAPI tape/cdrom driver code. + * + * If action is ide_next, then the rq is queued immediately after + * the currently-being-processed-request (if any), and the function + * returns without waiting for the new rq to be completed. As above, + * This is VERY DANGEROUS, and is intended for careful use by the + * ATAPI tape/cdrom driver code. + * + * If action is ide_end, then the rq is queued at the end of the + * request queue, and the function returns immediately without waiting + * for the new rq to be completed. This is again intended for careful + * use by the ATAPI tape/cdrom driver code. + */ +int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action) +{ + unsigned long flags; + ide_hwgroup_t *hwgroup = HWGROUP(drive); + unsigned int major = HWIF(drive)->major; + request_queue_t *q = &drive->queue; + struct list_head *queue_head = &q->queue_head; + DECLARE_COMPLETION(wait); + +#ifdef CONFIG_BLK_DEV_PDC4030 + if (HWIF(drive)->chipset == ide_pdc4030 && rq->buffer != NULL) + return -ENOSYS; /* special drive cmds not supported */ +#endif + rq->errors = 0; + rq->rq_status = RQ_ACTIVE; + rq->rq_dev = mk_kdev(major,(drive->select.b.unit)<waiting = &wait; + spin_lock_irqsave(&ide_lock, flags); + if (blk_queue_empty(q) || action == ide_preempt) { + if (action == ide_preempt) + hwgroup->rq = NULL; + } else { + if (action == ide_wait || action == ide_end) { + queue_head = queue_head->prev; + } else + queue_head = queue_head->next; + } + q->elevator.elevator_add_req_fn(q, rq, queue_head); + ide_do_request(hwgroup, 0); + spin_unlock_irqrestore(&ide_lock, flags); + if (action == ide_wait) { + wait_for_completion(&wait); /* wait for it to be serviced */ + return rq->errors ? -EIO : 0; /* return -EIO if errors */ + } + return 0; + +} + +void ide_revalidate_drive (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + int unit = drive - hwif->drives; + struct gendisk *g = hwif->gd[unit]; + int minor = (drive->select.b.unit << g->minor_shift); + + grok_partitions(mk_kdev(g->major, minor), current_capacity(drive)); +} + +/* + * This routine is called to flush all partitions and partition tables + * for a changed disk, and then re-read the new partition table. + * If we are revalidating a disk because of a media change, then we + * enter with usage == 0. If we are using an ioctl, we automatically have + * usage == 1 (we need an open channel to use an ioctl :-), so this + * is our limit. + */ +int ide_revalidate_disk (kdev_t i_rdev) +{ + ide_drive_t *drive; + ide_hwgroup_t *hwgroup; + unsigned long flags; + int res; + + if ((drive = get_info_ptr(i_rdev)) == NULL) + return -ENODEV; + hwgroup = HWGROUP(drive); + spin_lock_irqsave(&ide_lock, flags); + if (drive->busy || (drive->usage > 1)) { + spin_unlock_irqrestore(&ide_lock, flags); + return -EBUSY; + }; + drive->busy = 1; + MOD_INC_USE_COUNT; + spin_unlock_irqrestore(&ide_lock, flags); + + res = wipe_partitions(i_rdev); + + if (!res && DRIVER(drive)->revalidate) + DRIVER(drive)->revalidate(drive); + + drive->busy = 0; + wake_up(&drive->wqueue); + MOD_DEC_USE_COUNT; + return res; +} + +static void revalidate_drives (void) +{ + ide_hwif_t *hwif; + ide_drive_t *drive; + int index, unit; + + for (index = 0; index < MAX_HWIFS; ++index) { + hwif = &ide_hwifs[index]; + for (unit = 0; unit < MAX_DRIVES; ++unit) { + drive = &ide_hwifs[index].drives[unit]; + if (drive->revalidate) { + drive->revalidate = 0; + if (!initializing) + (void) ide_revalidate_disk(mk_kdev(hwif->major, unit<init(); + } + revalidate_drives(); +} + +static void ide_driver_module (void) +{ + int index; + ide_module_t *module = ide_modules; + + for (index = 0; index < MAX_HWIFS; ++index) + if (ide_hwifs[index].present) + goto search; + ide_probe_module(); +search: + while (module) { + (void) module->init(); + module = module->next; + } + revalidate_drives(); +} + +static int ide_open (struct inode * inode, struct file * filp) +{ + ide_drive_t *drive; + + if ((drive = get_info_ptr(inode->i_rdev)) == NULL) + return -ENXIO; + if (drive->driver == NULL) + ide_driver_module(); +#ifdef CONFIG_KMOD + if (drive->driver == NULL) { + if (drive->media == ide_disk) + (void) request_module("ide-disk"); + if (drive->media == ide_cdrom) + (void) request_module("ide-cd"); + if (drive->media == ide_tape) + (void) request_module("ide-tape"); + if (drive->media == ide_floppy) + (void) request_module("ide-floppy"); +#if defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) + if (drive->media == ide_scsi) + (void) request_module("ide-scsi"); +#endif /* defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) */ + } +#endif /* CONFIG_KMOD */ + while (drive->busy) + sleep_on(&drive->wqueue); + drive->usage++; + if (drive->driver != NULL) + return DRIVER(drive)->open(inode, filp, drive); + printk ("%s: driver not present\n", drive->name); + drive->usage--; + return -ENXIO; +} + +/* + * Releasing a block device means we sync() it, so that it can safely + * be forgotten about... + */ +static int ide_release (struct inode * inode, struct file * file) +{ + ide_drive_t *drive; + + if ((drive = get_info_ptr(inode->i_rdev)) != NULL) { + drive->usage--; + if (drive->driver != NULL) + DRIVER(drive)->release(inode, file, drive); + } + return 0; +} + +int ide_replace_subdriver (ide_drive_t *drive, const char *driver) +{ + if (!drive->present || drive->busy || drive->usage) + goto abort; + if (drive->driver != NULL && DRIVER(drive)->cleanup(drive)) + goto abort; + strncpy(drive->driver_req, driver, 9); + ide_driver_module(); + drive->driver_req[0] = 0; + ide_driver_module(); + if (DRIVER(drive) && !strcmp(DRIVER(drive)->name, driver)) + return 0; +abort: + return 1; +} + +#ifdef CONFIG_PROC_FS +ide_proc_entry_t generic_subdriver_entries[] = { + { "capacity", S_IFREG|S_IRUGO, proc_ide_read_capacity, NULL }, + { NULL, 0, NULL, NULL } +}; +#endif + +/* + * Note that we only release the standard ports, + * and do not even try to handle any extra ports + * allocated for weird IDE interface chipsets. + */ +void hwif_unregister (ide_hwif_t *hwif) +{ + if (hwif->straight8) { + ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 8); + goto jump_eight; + } + if (hwif->io_ports[IDE_DATA_OFFSET]) + ide_release_region(hwif->io_ports[IDE_DATA_OFFSET], 1); + if (hwif->io_ports[IDE_ERROR_OFFSET]) + ide_release_region(hwif->io_ports[IDE_ERROR_OFFSET], 1); + if (hwif->io_ports[IDE_NSECTOR_OFFSET]) + ide_release_region(hwif->io_ports[IDE_NSECTOR_OFFSET], 1); + if (hwif->io_ports[IDE_SECTOR_OFFSET]) + ide_release_region(hwif->io_ports[IDE_SECTOR_OFFSET], 1); + if (hwif->io_ports[IDE_LCYL_OFFSET]) + ide_release_region(hwif->io_ports[IDE_LCYL_OFFSET], 1); + if (hwif->io_ports[IDE_HCYL_OFFSET]) + ide_release_region(hwif->io_ports[IDE_HCYL_OFFSET], 1); + if (hwif->io_ports[IDE_SELECT_OFFSET]) + ide_release_region(hwif->io_ports[IDE_SELECT_OFFSET], 1); + if (hwif->io_ports[IDE_STATUS_OFFSET]) + ide_release_region(hwif->io_ports[IDE_STATUS_OFFSET], 1); +jump_eight: + if (hwif->io_ports[IDE_CONTROL_OFFSET]) + ide_release_region(hwif->io_ports[IDE_CONTROL_OFFSET], 1); +#if defined(CONFIG_AMIGA) || defined(CONFIG_MAC) + if (hwif->io_ports[IDE_IRQ_OFFSET]) + ide_release_region(hwif->io_ports[IDE_IRQ_OFFSET], 1); +#endif /* (CONFIG_AMIGA) || (CONFIG_MAC) */ +} + +void ide_unregister (unsigned int index) +{ + struct gendisk *gd; + ide_drive_t *drive, *d; + ide_hwif_t *hwif, *g; + ide_hwgroup_t *hwgroup; + int irq_count = 0, unit, i; + unsigned long flags; + unsigned int p, minor; + ide_hwif_t old_hwif; + + if (index >= MAX_HWIFS) + return; + spin_lock_irqsave(&ide_lock, flags); + hwif = &ide_hwifs[index]; + if (!hwif->present) + goto abort; + for (unit = 0; unit < MAX_DRIVES; ++unit) { + drive = &hwif->drives[unit]; + if (!drive->present) + continue; + if (drive->busy || drive->usage) + goto abort; + if (drive->driver != NULL && DRIVER(drive)->cleanup(drive)) + goto abort; + } + hwif->present = 0; + + /* + * All clear? Then blow away the buffer cache + */ + spin_unlock_irqrestore(&ide_lock, flags); + for (unit = 0; unit < MAX_DRIVES; ++unit) { + drive = &hwif->drives[unit]; + if (!drive->present) + continue; + minor = drive->select.b.unit << PARTN_BITS; + for (p = 0; p < (1<part[p].nr_sects > 0) { + kdev_t devp = mk_kdev(hwif->major, minor+p); + invalidate_device(devp, 0); + } + } +#ifdef CONFIG_PROC_FS + destroy_proc_ide_drives(hwif); +#endif + } + spin_lock_irqsave(&ide_lock, flags); + hwgroup = hwif->hwgroup; + + /* + * free the irq if we were the only hwif using it + */ + g = hwgroup->hwif; + do { + if (g->irq == hwif->irq) + ++irq_count; + g = g->next; + } while (g != hwgroup->hwif); + if (irq_count == 1) + ide_free_irq(hwif->irq, hwgroup); + + /* + * Note that we only release the standard ports, + * and do not even try to handle any extra ports + * allocated for weird IDE interface chipsets. + */ + hwif_unregister(hwif); + + /* + * Remove us from the hwgroup, and free + * the hwgroup if we were the only member + */ + d = hwgroup->drive; + for (i = 0; i < MAX_DRIVES; ++i) { + drive = &hwif->drives[i]; + if (drive->de) { + devfs_unregister (drive->de); + drive->de = NULL; + } + if (!drive->present) + continue; + while (hwgroup->drive->next != drive) + hwgroup->drive = hwgroup->drive->next; + hwgroup->drive->next = drive->next; + if (hwgroup->drive == drive) + hwgroup->drive = NULL; + if (drive->id != NULL) { + kfree(drive->id); + drive->id = NULL; + } + drive->present = 0; + blk_cleanup_queue(&drive->queue); + } + if (d->present) + hwgroup->drive = d; + while (hwgroup->hwif->next != hwif) + hwgroup->hwif = hwgroup->hwif->next; + hwgroup->hwif->next = hwif->next; + if (hwgroup->hwif == hwif) + kfree(hwgroup); + else + hwgroup->hwif = HWIF(hwgroup->drive); + +#if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI) + if (hwif->dma_base) { + (void) ide_release_dma(hwif); + hwif->dma_base = 0; + } +#endif /* (CONFIG_BLK_DEV_IDEDMA) && !(CONFIG_DMA_NONPCI) */ + + /* + * Remove us from the kernel's knowledge + */ + unregister_blkdev(hwif->major, hwif->name); + blk_dev[hwif->major].data = NULL; + blk_dev[hwif->major].queue = NULL; + blk_clear(hwif->major); + gd = hwif->gd[0]; + if (gd) { + int i; + for (i = 0; i < MAX_DRIVES; i++) + del_gendisk(gd + i); + kfree(gd->part); + if (gd->de_arr) + kfree (gd->de_arr); + if (gd->flags) + kfree (gd->flags); + kfree(gd); + for (i = 0; i < MAX_DRIVES; i++) + hwif->gd[i] = NULL; + } + old_hwif = *hwif; + init_hwif_data (index); /* restore hwif data to pristine status */ + hwif->hwgroup = old_hwif.hwgroup; + hwif->tuneproc = old_hwif.tuneproc; + hwif->speedproc = old_hwif.speedproc; + hwif->selectproc = old_hwif.selectproc; + hwif->resetproc = old_hwif.resetproc; + hwif->intrproc = old_hwif.intrproc; + hwif->maskproc = old_hwif.maskproc; + hwif->quirkproc = old_hwif.quirkproc; + hwif->rwproc = old_hwif.rwproc; + hwif->ideproc = old_hwif.ideproc; + hwif->dmaproc = old_hwif.dmaproc; + hwif->busproc = old_hwif.busproc; + hwif->bus_state = old_hwif.bus_state; + hwif->dma_base = old_hwif.dma_base; + hwif->dma_extra = old_hwif.dma_extra; + hwif->config_data = old_hwif.config_data; + hwif->select_data = old_hwif.select_data; + hwif->proc = old_hwif.proc; +#ifndef CONFIG_BLK_DEV_IDECS + hwif->irq = old_hwif.irq; +#endif /* CONFIG_BLK_DEV_IDECS */ + hwif->major = old_hwif.major; + hwif->chipset = old_hwif.chipset; + hwif->autodma = old_hwif.autodma; + hwif->udma_four = old_hwif.udma_four; +#ifdef CONFIG_BLK_DEV_IDEPCI + hwif->pci_dev = old_hwif.pci_dev; + hwif->pci_devid = old_hwif.pci_devid; +#endif /* CONFIG_BLK_DEV_IDEPCI */ + hwif->straight8 = old_hwif.straight8; + hwif->hwif_data = old_hwif.hwif_data; +abort: + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * Setup hw_regs_t structure described by parameters. You + * may set up the hw structure yourself OR use this routine to + * do it for you. + */ +void ide_setup_ports ( hw_regs_t *hw, + ide_ioreg_t base, int *offsets, + ide_ioreg_t ctrl, ide_ioreg_t intr, + ide_ack_intr_t *ack_intr, int irq) +{ + int i; + + for (i = 0; i < IDE_NR_PORTS; i++) { + if (offsets[i] == -1) { + switch(i) { + case IDE_CONTROL_OFFSET: + hw->io_ports[i] = ctrl; + break; +#if defined(CONFIG_AMIGA) || defined(CONFIG_MAC) + case IDE_IRQ_OFFSET: + hw->io_ports[i] = intr; + break; +#endif /* (CONFIG_AMIGA) || (CONFIG_MAC) */ + default: + hw->io_ports[i] = 0; + break; + } + } else { + hw->io_ports[i] = base + offsets[i]; + } + } + hw->irq = irq; + hw->dma = NO_DMA; + hw->ack_intr = ack_intr; +} + +/* + * Register an IDE interface, specifing exactly the registers etc + * Set init=1 iff calling before probes have taken place. + */ +int ide_register_hw (hw_regs_t *hw, ide_hwif_t **hwifp) +{ + int index, retry = 1; + ide_hwif_t *hwif; + + do { + for (index = 0; index < MAX_HWIFS; ++index) { + hwif = &ide_hwifs[index]; + if (hwif->hw.io_ports[IDE_DATA_OFFSET] == hw->io_ports[IDE_DATA_OFFSET]) + goto found; + } + for (index = 0; index < MAX_HWIFS; ++index) { + hwif = &ide_hwifs[index]; + if ((!hwif->present && !hwif->mate && !initializing) || + (!hwif->hw.io_ports[IDE_DATA_OFFSET] && initializing)) + goto found; + } + for (index = 0; index < MAX_HWIFS; index++) + ide_unregister(index); + } while (retry--); + return -1; +found: + if (hwif->present) + ide_unregister(index); + if (hwif->present) + return -1; + memcpy(&hwif->hw, hw, sizeof(*hw)); + memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->hw.io_ports)); + hwif->irq = hw->irq; + hwif->noprobe = 0; + hwif->chipset = hw->chipset; + + if (!initializing) { + ide_probe_module(); +#ifdef CONFIG_PROC_FS + create_proc_ide_interfaces(); +#endif + ide_driver_module(); + } + + if (hwifp) + *hwifp = hwif; + + return (initializing || hwif->present) ? index : -1; +} + +/* + * Compatability function with existing drivers. If you want + * something different, use the function above. + */ +int ide_register (int arg1, int arg2, int irq) +{ + hw_regs_t hw; + ide_init_hwif_ports(&hw, (ide_ioreg_t) arg1, (ide_ioreg_t) arg2, NULL); + hw.irq = irq; + return ide_register_hw(&hw, NULL); +} + +void ide_add_setting (ide_drive_t *drive, const char *name, int rw, int read_ioctl, int write_ioctl, int data_type, int min, int max, int mul_factor, int div_factor, void *data, ide_procset_t *set) +{ + ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting = NULL; + + while ((*p) && strcmp((*p)->name, name) < 0) + p = &((*p)->next); + if ((setting = kmalloc(sizeof(*setting), GFP_KERNEL)) == NULL) + goto abort; + memset(setting, 0, sizeof(*setting)); + if ((setting->name = kmalloc(strlen(name) + 1, GFP_KERNEL)) == NULL) + goto abort; + strcpy(setting->name, name); setting->rw = rw; + setting->read_ioctl = read_ioctl; setting->write_ioctl = write_ioctl; + setting->data_type = data_type; setting->min = min; + setting->max = max; setting->mul_factor = mul_factor; + setting->div_factor = div_factor; setting->data = data; + setting->set = set; setting->next = *p; + if (drive->driver) + setting->auto_remove = 1; + *p = setting; + return; +abort: + if (setting) + kfree(setting); +} + +void ide_remove_setting (ide_drive_t *drive, char *name) +{ + ide_settings_t **p = (ide_settings_t **) &drive->settings, *setting; + + while ((*p) && strcmp((*p)->name, name)) + p = &((*p)->next); + if ((setting = (*p)) == NULL) + return; + (*p) = setting->next; + kfree(setting->name); + kfree(setting); +} + +static ide_settings_t *ide_find_setting_by_ioctl (ide_drive_t *drive, int cmd) +{ + ide_settings_t *setting = drive->settings; + + while (setting) { + if (setting->read_ioctl == cmd || setting->write_ioctl == cmd) + break; + setting = setting->next; + } + return setting; +} + +ide_settings_t *ide_find_setting_by_name (ide_drive_t *drive, char *name) +{ + ide_settings_t *setting = drive->settings; + + while (setting) { + if (strcmp(setting->name, name) == 0) + break; + setting = setting->next; + } + return setting; +} + +static void auto_remove_settings (ide_drive_t *drive) +{ + ide_settings_t *setting; +repeat: + setting = drive->settings; + while (setting) { + if (setting->auto_remove) { + ide_remove_setting(drive, setting->name); + goto repeat; + } + setting = setting->next; + } +} + +int ide_read_setting (ide_drive_t *drive, ide_settings_t *setting) +{ + int val = -EINVAL; + unsigned long flags; + + if ((setting->rw & SETTING_READ)) { + spin_lock_irqsave(&ide_lock, flags); + switch(setting->data_type) { + case TYPE_BYTE: + val = *((u8 *) setting->data); + break; + case TYPE_SHORT: + val = *((u16 *) setting->data); + break; + case TYPE_INT: + case TYPE_INTA: + val = *((u32 *) setting->data); + break; + } + spin_unlock_irqrestore(&ide_lock, flags); + } + return val; +} + +int ide_spin_wait_hwgroup (ide_drive_t *drive) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + unsigned long timeout = jiffies + (3 * HZ); + + spin_lock_irq(&ide_lock); + + while (hwgroup->busy) { + unsigned long lflags; + spin_unlock_irq(&ide_lock); + local_irq_set(lflags); + if (time_after(jiffies, timeout)) { + local_irq_restore(lflags); + printk("%s: channel busy\n", drive->name); + return -EBUSY; + } + local_irq_restore(lflags); + spin_lock_irq(&ide_lock); + } + return 0; +} + +/* + * FIXME: This should be changed to enqueue a special request + * to the driver to change settings, and then wait on a sema for completion. + * The current scheme of polling is kludgey, though safe enough. + */ +int ide_write_setting (ide_drive_t *drive, ide_settings_t *setting, int val) +{ + int i; + u32 *p; + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (!(setting->rw & SETTING_WRITE)) + return -EPERM; + if (val < setting->min || val > setting->max) + return -EINVAL; + if (setting->set) + return setting->set(drive, val); + if (ide_spin_wait_hwgroup(drive)) + return -EBUSY; + switch (setting->data_type) { + case TYPE_BYTE: + *((u8 *) setting->data) = val; + break; + case TYPE_SHORT: + *((u16 *) setting->data) = val; + break; + case TYPE_INT: + *((u32 *) setting->data) = val; + break; + case TYPE_INTA: + p = (u32 *) setting->data; + for (i = 0; i < 1 << PARTN_BITS; i++, p++) + *p = val; + break; + } + spin_unlock_irq(&ide_lock); + return 0; +} + +static int set_io_32bit(ide_drive_t *drive, int arg) +{ + drive->io_32bit = arg; +#ifdef CONFIG_BLK_DEV_DTC2278 + if (HWIF(drive)->chipset == ide_dtc2278) + HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg; +#endif /* CONFIG_BLK_DEV_DTC2278 */ + return 0; +} + +static int set_using_dma (ide_drive_t *drive, int arg) +{ + if (!drive->driver || !DRIVER(drive)->supports_dma) + return -EPERM; + if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc) + return -EPERM; + if (HWIF(drive)->dmaproc(arg ? ide_dma_on : ide_dma_off, drive)) + return -EIO; + return 0; +} + +static int set_pio_mode (ide_drive_t *drive, int arg) +{ + struct request rq; + + if (!HWIF(drive)->tuneproc) + return -ENOSYS; + if (drive->special.b.set_tune) + return -EBUSY; + ide_init_drive_cmd(&rq); + drive->tune_req = (byte) arg; + drive->special.b.set_tune = 1; + (void) ide_do_drive_cmd (drive, &rq, ide_wait); + return 0; +} + +void ide_add_generic_settings (ide_drive_t *drive) +{ +/* + * drive setting name read/write access read ioctl write ioctl data type min max mul_factor div_factor data pointer set function + */ + ide_add_setting(drive, "io_32bit", drive->no_io_32bit ? SETTING_READ : SETTING_RW, HDIO_GET_32BIT, HDIO_SET_32BIT, TYPE_BYTE, 0, 1 + (SUPPORT_VLB_SYNC << 1), 1, 1, &drive->io_32bit, set_io_32bit); + ide_add_setting(drive, "keepsettings", SETTING_RW, HDIO_GET_KEEPSETTINGS, HDIO_SET_KEEPSETTINGS, TYPE_BYTE, 0, 1, 1, 1, &drive->keep_settings, NULL); + ide_add_setting(drive, "nice1", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->nice1, NULL); + ide_add_setting(drive, "pio_mode", SETTING_WRITE, -1, HDIO_SET_PIO_MODE, TYPE_BYTE, 0, 255, 1, 1, NULL, set_pio_mode); + ide_add_setting(drive, "slow", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->slow, NULL); + ide_add_setting(drive, "unmaskirq", drive->no_unmask ? SETTING_READ : SETTING_RW, HDIO_GET_UNMASKINTR, HDIO_SET_UNMASKINTR, TYPE_BYTE, 0, 1, 1, 1, &drive->unmask, NULL); + ide_add_setting(drive, "using_dma", SETTING_RW, HDIO_GET_DMA, HDIO_SET_DMA, TYPE_BYTE, 0, 1, 1, 1, &drive->using_dma, set_using_dma); + ide_add_setting(drive, "ide_scsi", SETTING_RW, -1, -1, TYPE_BYTE, 0, 1, 1, 1, &drive->scsi, NULL); + ide_add_setting(drive, "init_speed", SETTING_RW, -1, -1, TYPE_BYTE, 0, 70, 1, 1, &drive->init_speed, NULL); + ide_add_setting(drive, "current_speed", SETTING_RW, -1, -1, TYPE_BYTE, 0, 70, 1, 1, &drive->current_speed, NULL); + ide_add_setting(drive, "number", SETTING_RW, -1, -1, TYPE_BYTE, 0, 3, 1, 1, &drive->dn, NULL); +} + +int ide_wait_cmd (ide_drive_t *drive, int cmd, int nsect, int feature, int sectors, byte *buf) +{ + struct request rq; + byte buffer[4]; + + if (!buf) + buf = buffer; + memset(buf, 0, 4 + SECTOR_WORDS * 4 * sectors); + ide_init_drive_cmd(&rq); + rq.buffer = buf; + *buf++ = cmd; + *buf++ = nsect; + *buf++ = feature; + *buf++ = sectors; + return ide_do_drive_cmd(drive, &rq, ide_wait); +} + +int ide_wait_cmd_task (ide_drive_t *drive, byte *buf) +{ + struct request rq; + + ide_init_drive_cmd(&rq); + rq.flags = REQ_DRIVE_TASK; + rq.buffer = buf; + return ide_do_drive_cmd(drive, &rq, ide_wait); +} + +/* + * Delay for *at least* 50ms. As we don't know how much time is left + * until the next tick occurs, we wait an extra tick to be safe. + * This is used only during the probing/polling for drives at boot time. + * + * However, its usefullness may be needed in other places, thus we export it now. + * The future may change this to a millisecond setable delay. + */ +void ide_delay_50ms (void) +{ +#ifndef CONFIG_BLK_DEV_IDECS + mdelay(50); +#else + __set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(HZ/20); +#endif /* CONFIG_BLK_DEV_IDECS */ +} + +int system_bus_clock (void) +{ + return((int) ((!system_bus_speed) ? ide_system_bus_speed() : system_bus_speed )); +} + +int ide_reinit_drive (ide_drive_t *drive) +{ + switch (drive->media) { +#ifdef CONFIG_BLK_DEV_IDECD + case ide_cdrom: + { + extern int ide_cdrom_reinit(ide_drive_t *drive); + if (ide_cdrom_reinit(drive)) + return 1; + break; + } +#endif /* CONFIG_BLK_DEV_IDECD */ +#ifdef CONFIG_BLK_DEV_IDEDISK + case ide_disk: + { + extern int idedisk_reinit(ide_drive_t *drive); + if (idedisk_reinit(drive)) + return 1; + break; + } +#endif /* CONFIG_BLK_DEV_IDEDISK */ +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + case ide_floppy: + { + extern int idefloppy_reinit(ide_drive_t *drive); + if (idefloppy_reinit(drive)) + return 1; + break; + } +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ +#ifdef CONFIG_BLK_DEV_IDETAPE + case ide_tape: + { + extern int idetape_reinit(ide_drive_t *drive); + if (idetape_reinit(drive)) + return 1; + break; + } +#endif /* CONFIG_BLK_DEV_IDETAPE */ +#ifdef CONFIG_BLK_DEV_IDESCSI +/* + * { + * extern int idescsi_reinit(ide_drive_t *drive); + * if (idescsi_reinit(drive)) + * return 1; + * break; + * } + */ +#endif /* CONFIG_BLK_DEV_IDESCSI */ + default: + return 1; + } + return 0; +} + +static int ide_ioctl (struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int err = 0, major, minor; + ide_drive_t *drive; + struct request rq; + kdev_t dev; + ide_settings_t *setting; + + major = major(dev); minor = minor(dev); + if ((drive = get_info_ptr(inode->i_rdev)) == NULL) + return -ENODEV; + + if ((setting = ide_find_setting_by_ioctl(drive, cmd)) != NULL) { + if (cmd == setting->read_ioctl) { + err = ide_read_setting(drive, setting); + return err >= 0 ? put_user(err, (long *) arg) : err; + } else { + if ((minor(inode->i_rdev) & PARTN_MASK)) + return -EINVAL; + return ide_write_setting(drive, setting, arg); + } + } + + ide_init_drive_cmd (&rq); + switch (cmd) { + case HDIO_GETGEO: + { + struct hd_geometry *loc = (struct hd_geometry *) arg; + unsigned short bios_cyl = drive->bios_cyl; /* truncate */ + if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL; + if (put_user(drive->bios_head, (byte *) &loc->heads)) return -EFAULT; + if (put_user(drive->bios_sect, (byte *) &loc->sectors)) return -EFAULT; + if (put_user(bios_cyl, (unsigned short *) &loc->cylinders)) return -EFAULT; + if (put_user((unsigned)drive->part[minor(inode->i_rdev)&PARTN_MASK].start_sect, + (unsigned long *) &loc->start)) return -EFAULT; + return 0; + } + + case HDIO_GETGEO_BIG_RAW: + { + struct hd_big_geometry *loc = (struct hd_big_geometry *) arg; + if (!loc || (drive->media != ide_disk && drive->media != ide_floppy)) return -EINVAL; + if (put_user(drive->head, (byte *) &loc->heads)) return -EFAULT; + if (put_user(drive->sect, (byte *) &loc->sectors)) return -EFAULT; + if (put_user(drive->cyl, (unsigned int *) &loc->cylinders)) return -EFAULT; + if (put_user((unsigned)drive->part[minor(inode->i_rdev)&PARTN_MASK].start_sect, + (unsigned long *) &loc->start)) return -EFAULT; + return 0; + } + + case BLKRRPART: /* Re-read partition tables */ + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + return ide_revalidate_disk(inode->i_rdev); + + case HDIO_OBSOLETE_IDENTITY: + case HDIO_GET_IDENTITY: + if (minor(inode->i_rdev) & PARTN_MASK) + return -EINVAL; + if (drive->id == NULL) + return -ENOMSG; + if (copy_to_user((char *)arg, (char *)drive->id, (cmd == HDIO_GET_IDENTITY) ? sizeof(*drive->id) : 142)) + return -EFAULT; + return 0; + + case HDIO_GET_NICE: + return put_user(drive->dsc_overlap << IDE_NICE_DSC_OVERLAP | + drive->atapi_overlap << IDE_NICE_ATAPI_OVERLAP | + drive->nice0 << IDE_NICE_0 | + drive->nice1 << IDE_NICE_1 | + drive->nice2 << IDE_NICE_2, + (long *) arg); + +#ifdef CONFIG_IDE_TASK_IOCTL + case HDIO_DRIVE_TASKFILE: + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + switch(drive->media) { + case ide_disk: + return ide_taskfile_ioctl(drive, inode, file, cmd, arg); +#ifdef CONFIG_PKT_TASK_IOCTL + case ide_cdrom: + case ide_tape: + case ide_floppy: + return pkt_taskfile_ioctl(drive, inode, file, cmd, arg); +#endif /* CONFIG_PKT_TASK_IOCTL */ + default: + return -ENOMSG; + } +#endif /* CONFIG_IDE_TASK_IOCTL */ + + case HDIO_DRIVE_CMD: + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + return ide_cmd_ioctl(drive, inode, file, cmd, arg); + + case HDIO_DRIVE_TASK: + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + return ide_task_ioctl(drive, inode, file, cmd, arg); + + case HDIO_SCAN_HWIF: + { + int args[3]; + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if (copy_from_user(args, (void *)arg, 3 * sizeof(int))) + return -EFAULT; + if (ide_register(args[0], args[1], args[2]) == -1) + return -EIO; + return 0; + } + case HDIO_UNREGISTER_HWIF: + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + /* (arg > MAX_HWIFS) checked in function */ + ide_unregister(arg); + return 0; + case HDIO_SET_NICE: + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if (drive->driver == NULL) + return -EPERM; + if (arg != (arg & ((1 << IDE_NICE_DSC_OVERLAP) | (1 << IDE_NICE_1)))) + return -EPERM; + drive->dsc_overlap = (arg >> IDE_NICE_DSC_OVERLAP) & 1; + if (drive->dsc_overlap && !DRIVER(drive)->supports_dsc_overlap) { + drive->dsc_overlap = 0; + return -EPERM; + } + drive->nice1 = (arg >> IDE_NICE_1) & 1; + return 0; + case HDIO_DRIVE_RESET: + { + unsigned long flags; + if (!capable(CAP_SYS_ADMIN)) return -EACCES; +#if 1 + spin_lock_irqsave(&ide_lock, flags); + if ( HWGROUP(drive)->handler != NULL) { + printk("%s: ide_set_handler: handler not null; %p\n", drive->name, HWGROUP(drive)->handler); + (void) HWGROUP(drive)->handler(drive); +// HWGROUP(drive)->handler = NULL; + HWGROUP(drive)->expiry = NULL; + del_timer(&HWGROUP(drive)->timer); + } + spin_unlock_irqrestore(&ide_lock, flags); + +#endif + (void) ide_do_reset(drive); + if (drive->suspend_reset) { +/* + * APM WAKE UP todo !! + * int nogoodpower = 1; + * while(nogoodpower) { + * check_power1() or check_power2() + * nogoodpower = 0; + * } + * HWIF(drive)->multiproc(drive); + */ + return ide_revalidate_disk(inode->i_rdev); + } + return 0; + } + case BLKGETSIZE: + case BLKGETSIZE64: + case BLKROSET: + case BLKROGET: + case BLKFLSBUF: + case BLKSSZGET: + case BLKPG: + case BLKELVGET: + case BLKELVSET: + case BLKBSZGET: + case BLKBSZSET: + return blk_ioctl(inode->i_bdev, cmd, arg); + + case CDROMEJECT: + case CDROMCLOSETRAY: + return block_ioctl(inode->i_bdev, cmd, arg); + + case HDIO_GET_BUSSTATE: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (put_user(HWIF(drive)->bus_state, (long *)arg)) + return -EFAULT; + return 0; + + case HDIO_SET_BUSSTATE: + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + if (HWIF(drive)->busproc) + HWIF(drive)->busproc(drive, (int)arg); + return 0; + + default: + if (drive->driver != NULL) + return DRIVER(drive)->ioctl(drive, inode, file, cmd, arg); + return -EPERM; + } +} + +static int ide_check_media_change (kdev_t i_rdev) +{ + ide_drive_t *drive; + + if ((drive = get_info_ptr(i_rdev)) == NULL) + return -ENODEV; + if (drive->driver != NULL) + return DRIVER(drive)->media_change(drive); + return 0; +} + +void ide_fixstring (byte *s, const int bytecount, const int byteswap) +{ + byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */ + + if (byteswap) { + /* convert from big-endian to host byte order */ + for (p = end ; p != s;) { + unsigned short *pp = (unsigned short *) (p -= 2); + *pp = ntohs(*pp); + } + } + + /* strip leading blanks */ + while (s != end && *s == ' ') + ++s; + + /* compress internal blanks and strip trailing blanks */ + while (s != end && *s) { + if (*s++ != ' ' || (s != end && *s && *s != ' ')) + *p++ = *(s-1); + } + + /* wipe out trailing garbage */ + while (p != end) + *p++ = '\0'; +} + +/* + * stridx() returns the offset of c within s, + * or -1 if c is '\0' or not found within s. + */ +static int __init stridx (const char *s, char c) +{ + char *i = strchr(s, c); + return (i && c) ? i - s : -1; +} + +/* + * match_parm() does parsing for ide_setup(): + * + * 1. the first char of s must be '='. + * 2. if the remainder matches one of the supplied keywords, + * the index (1 based) of the keyword is negated and returned. + * 3. if the remainder is a series of no more than max_vals numbers + * separated by commas, the numbers are saved in vals[] and a + * count of how many were saved is returned. Base10 is assumed, + * and base16 is allowed when prefixed with "0x". + * 4. otherwise, zero is returned. + */ +static int __init match_parm (char *s, const char *keywords[], int vals[], int max_vals) +{ + static const char *decimal = "0123456789"; + static const char *hex = "0123456789abcdef"; + int i, n; + + if (*s++ == '=') { + /* + * Try matching against the supplied keywords, + * and return -(index+1) if we match one + */ + if (keywords != NULL) { + for (i = 0; *keywords != NULL; ++i) { + if (!strcmp(s, *keywords++)) + return -(i+1); + } + } + /* + * Look for a series of no more than "max_vals" + * numeric values separated by commas, in base10, + * or base16 when prefixed with "0x". + * Return a count of how many were found. + */ + for (n = 0; (i = stridx(decimal, *s)) >= 0;) { + vals[n] = i; + while ((i = stridx(decimal, *++s)) >= 0) + vals[n] = (vals[n] * 10) + i; + if (*s == 'x' && !vals[n]) { + while ((i = stridx(hex, *++s)) >= 0) + vals[n] = (vals[n] * 0x10) + i; + } + if (++n == max_vals) + break; + if (*s == ',' || *s == ';') + ++s; + } + if (!*s) + return n; + } + return 0; /* zero = nothing matched */ +} + +/* + * ide_setup() gets called VERY EARLY during initialization, + * to handle kernel "command line" strings beginning with "hdx=" + * or "ide". Here is the complete set currently supported: + * + * "hdx=" is recognized for all "x" from "a" to "h", such as "hdc". + * "idex=" is recognized for all "x" from "0" to "3", such as "ide1". + * + * "hdx=noprobe" : drive may be present, but do not probe for it + * "hdx=none" : drive is NOT present, ignore cmos and do not probe + * "hdx=nowerr" : ignore the WRERR_STAT bit on this drive + * "hdx=cdrom" : drive is present, and is a cdrom drive + * "hdx=cyl,head,sect" : disk drive is present, with specified geometry + * "hdx=noremap" : do not remap 0->1 even though EZD was detected + * "hdx=autotune" : driver will attempt to tune interface speed + * to the fastest PIO mode supported, + * if possible for this drive only. + * Not fully supported by all chipset types, + * and quite likely to cause trouble with + * older/odd IDE drives. + * + * "hdx=slow" : insert a huge pause after each access to the data + * port. Should be used only as a last resort. + * + * "hdx=swapdata" : when the drive is a disk, byte swap all data + * "hdx=bswap" : same as above.......... + * "hdxlun=xx" : set the drive last logical unit. + * "hdx=flash" : allows for more than one ata_flash disk to be + * registered. In most cases, only one device + * will be present. + * "hdx=scsi" : the return of the ide-scsi flag, this is useful for + * allowwing ide-floppy, ide-tape, and ide-cdrom|writers + * to use ide-scsi emulation on a device specific option. + * "idebus=xx" : inform IDE driver of VESA/PCI bus speed in MHz, + * where "xx" is between 20 and 66 inclusive, + * used when tuning chipset PIO modes. + * For PCI bus, 25 is correct for a P75 system, + * 30 is correct for P90,P120,P180 systems, + * and 33 is used for P100,P133,P166 systems. + * If in doubt, use idebus=33 for PCI. + * As for VLB, it is safest to not specify it. + * + * "idex=noprobe" : do not attempt to access/use this interface + * "idex=base" : probe for an interface at the addr specified, + * where "base" is usually 0x1f0 or 0x170 + * and "ctl" is assumed to be "base"+0x206 + * "idex=base,ctl" : specify both base and ctl + * "idex=base,ctl,irq" : specify base, ctl, and irq number + * "idex=autotune" : driver will attempt to tune interface speed + * to the fastest PIO mode supported, + * for all drives on this interface. + * Not fully supported by all chipset types, + * and quite likely to cause trouble with + * older/odd IDE drives. + * "idex=noautotune" : driver will NOT attempt to tune interface speed + * This is the default for most chipsets, + * except the cmd640. + * "idex=serialize" : do not overlap operations on idex and ide(x^1) + * "idex=four" : four drives on idex and ide(x^1) share same ports + * "idex=reset" : reset interface before first use + * "idex=dma" : enable DMA by default on both drives if possible + * "idex=ata66" : informs the interface that it has an 80c cable + * for chipsets that are ATA-66 capable, but + * the ablity to bit test for detection is + * currently unknown. + * "ide=reverse" : Formerly called to pci sub-system, but now local. + * + * The following are valid ONLY on ide0, (except dc4030) + * and the defaults for the base,ctl ports must not be altered. + * + * "ide0=dtc2278" : probe/support DTC2278 interface + * "ide0=ht6560b" : probe/support HT6560B interface + * "ide0=cmd640_vlb" : *REQUIRED* for VLB cards with the CMD640 chip + * (not for PCI -- automatically detected) + * "ide0=qd65xx" : probe/support qd65xx interface + * "ide0=ali14xx" : probe/support ali14xx chipsets (ALI M1439, M1443, M1445) + * "ide0=umc8672" : probe/support umc8672 chipsets + * "idex=dc4030" : probe/support Promise DC4030VL interface + * "ide=doubler" : probe/support IDE doublers on Amiga + */ +int __init ide_setup (char *s) +{ + int i, vals[3]; + ide_hwif_t *hwif; + ide_drive_t *drive; + unsigned int hw, unit; + const char max_drive = 'a' + ((MAX_HWIFS * MAX_DRIVES) - 1); + const char max_hwif = '0' + (MAX_HWIFS - 1); + + + if (strncmp(s,"hd",2) == 0 && s[2] == '=') /* hd= is for hd.c */ + return 0; /* driver and not us */ + + if (strncmp(s,"ide",3) && + strncmp(s,"idebus",6) && + strncmp(s,"hd",2)) /* hdx= & hdxlun= */ + return 0; + + printk("ide_setup: %s", s); + init_ide_data (); + +#ifdef CONFIG_BLK_DEV_IDEDOUBLER + if (!strcmp(s, "ide=doubler")) { + extern int ide_doubler; + + printk(" : Enabled support for IDE doublers\n"); + ide_doubler = 1; + return 1; + } +#endif /* CONFIG_BLK_DEV_IDEDOUBLER */ + + if (!strcmp(s, "ide=nodma")) { + printk("IDE: Prevented DMA\n"); + noautodma = 1; + return 1; + } + +#ifdef CONFIG_BLK_DEV_IDEPCI + if (!strcmp(s, "ide=reverse")) { + ide_scan_direction = 1; + printk(" : Enabled support for IDE inverse scan order.\n"); + return 1; + } +#endif /* CONFIG_BLK_DEV_IDEPCI */ + + /* + * Look for drive options: "hdx=" + */ + if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) { + const char *hd_words[] = {"none", "noprobe", "nowerr", "cdrom", + "serialize", "autotune", "noautotune", + "slow", "swapdata", "bswap", "flash", + "remap", "noremap", "scsi", NULL}; + unit = s[2] - 'a'; + hw = unit / MAX_DRIVES; + unit = unit % MAX_DRIVES; + hwif = &ide_hwifs[hw]; + drive = &hwif->drives[unit]; + if (strncmp(s + 4, "ide-", 4) == 0) { + strncpy(drive->driver_req, s + 4, 9); + goto done; + } + /* + * Look for last lun option: "hdxlun=" + */ + if (s[3] == 'l' && s[4] == 'u' && s[5] == 'n') { + if (match_parm(&s[6], NULL, vals, 1) != 1) + goto bad_option; + if (vals[0] >= 0 && vals[0] <= 7) { + drive->last_lun = vals[0]; + drive->forced_lun = 1; + } else + printk(" -- BAD LAST LUN! Expected value from 0 to 7"); + goto done; + } + switch (match_parm(&s[3], hd_words, vals, 3)) { + case -1: /* "none" */ + drive->nobios = 1; /* drop into "noprobe" */ + case -2: /* "noprobe" */ + drive->noprobe = 1; + goto done; + case -3: /* "nowerr" */ + drive->bad_wstat = BAD_R_STAT; + hwif->noprobe = 0; + goto done; + case -4: /* "cdrom" */ + drive->present = 1; + drive->media = ide_cdrom; + hwif->noprobe = 0; + goto done; + case -5: /* "serialize" */ + printk(" -- USE \"ide%d=serialize\" INSTEAD", hw); + goto do_serialize; + case -6: /* "autotune" */ + drive->autotune = 1; + goto done; + case -7: /* "noautotune" */ + drive->autotune = 2; + goto done; + case -8: /* "slow" */ + drive->slow = 1; + goto done; + case -9: /* "swapdata" or "bswap" */ + case -10: + drive->bswap = 1; + goto done; + case -11: /* "flash" */ + drive->ata_flash = 1; + goto done; + case -12: /* "remap" */ + drive->remap_0_to_1 = 1; + goto done; + case -13: /* "noremap" */ + drive->remap_0_to_1 = 2; + goto done; + case -14: /* "scsi" */ +#if defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) + drive->scsi = 1; + goto done; +#else + drive->scsi = 0; + goto bad_option; +#endif /* defined(CONFIG_BLK_DEV_IDESCSI) && defined(CONFIG_SCSI) */ + case 3: /* cyl,head,sect */ + drive->media = ide_disk; + drive->cyl = drive->bios_cyl = vals[0]; + drive->head = drive->bios_head = vals[1]; + drive->sect = drive->bios_sect = vals[2]; + drive->present = 1; + drive->forced_geom = 1; + hwif->noprobe = 0; + goto done; + default: + goto bad_option; + } + } + + if (s[0] != 'i' || s[1] != 'd' || s[2] != 'e') + goto bad_option; + /* + * Look for bus speed option: "idebus=" + */ + if (s[3] == 'b' && s[4] == 'u' && s[5] == 's') { + if (match_parm(&s[6], NULL, vals, 1) != 1) + goto bad_option; + if (vals[0] >= 20 && vals[0] <= 66) { + idebus_parameter = vals[0]; + } else + printk(" -- BAD BUS SPEED! Expected value from 20 to 66"); + goto done; + } + /* + * Look for interface options: "idex=" + */ + if (s[3] >= '0' && s[3] <= max_hwif) { + /* + * Be VERY CAREFUL changing this: note hardcoded indexes below + * -8,-9,-10 : are reserved for future idex calls to ease the hardcoding. + */ + const char *ide_words[] = { + "noprobe", "serialize", "autotune", "noautotune", "reset", "dma", "ata66", + "minus8", "minus9", "minus10", + "four", "qd65xx", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", "dc4030", NULL }; + hw = s[3] - '0'; + hwif = &ide_hwifs[hw]; + i = match_parm(&s[4], ide_words, vals, 3); + + /* + * Cryptic check to ensure chipset not already set for hwif: + */ + if (i > 0 || i <= -11) { /* is parameter a chipset name? */ + if (hwif->chipset != ide_unknown) + goto bad_option; /* chipset already specified */ + if (i <= -11 && i != -18 && hw != 0) + goto bad_hwif; /* chipset drivers are for "ide0=" only */ + if (i <= -11 && i != -18 && ide_hwifs[hw+1].chipset != ide_unknown) + goto bad_option; /* chipset for 2nd port already specified */ + printk("\n"); + } + + switch (i) { +#ifdef CONFIG_BLK_DEV_PDC4030 + case -18: /* "dc4030" */ + { + extern void init_pdc4030(void); + init_pdc4030(); + goto done; + } +#endif /* CONFIG_BLK_DEV_PDC4030 */ +#ifdef CONFIG_BLK_DEV_ALI14XX + case -17: /* "ali14xx" */ + { + extern void init_ali14xx (void); + init_ali14xx(); + goto done; + } +#endif /* CONFIG_BLK_DEV_ALI14XX */ +#ifdef CONFIG_BLK_DEV_UMC8672 + case -16: /* "umc8672" */ + { + extern void init_umc8672 (void); + init_umc8672(); + goto done; + } +#endif /* CONFIG_BLK_DEV_UMC8672 */ +#ifdef CONFIG_BLK_DEV_DTC2278 + case -15: /* "dtc2278" */ + { + extern void init_dtc2278 (void); + init_dtc2278(); + goto done; + } +#endif /* CONFIG_BLK_DEV_DTC2278 */ +#ifdef CONFIG_BLK_DEV_CMD640 + case -14: /* "cmd640_vlb" */ + { + extern int cmd640_vlb; /* flag for cmd640.c */ + cmd640_vlb = 1; + goto done; + } +#endif /* CONFIG_BLK_DEV_CMD640 */ +#ifdef CONFIG_BLK_DEV_HT6560B + case -13: /* "ht6560b" */ + { + extern void init_ht6560b (void); + init_ht6560b(); + goto done; + } +#endif /* CONFIG_BLK_DEV_HT6560B */ +#if CONFIG_BLK_DEV_QD65XX + case -12: /* "qd65xx" */ + { + extern void init_qd65xx (void); + init_qd65xx(); + goto done; + } +#endif /* CONFIG_BLK_DEV_QD65XX */ +#ifdef CONFIG_BLK_DEV_4DRIVES + case -11: /* "four" drives on one set of ports */ + { + ide_hwif_t *mate = &ide_hwifs[hw^1]; + mate->drives[0].select.all ^= 0x20; + mate->drives[1].select.all ^= 0x20; + hwif->chipset = mate->chipset = ide_4drives; + mate->irq = hwif->irq; + memcpy(mate->io_ports, hwif->io_ports, sizeof(hwif->io_ports)); + goto do_serialize; + } +#endif /* CONFIG_BLK_DEV_4DRIVES */ + case -10: /* minus10 */ + case -9: /* minus9 */ + case -8: /* minus8 */ + goto bad_option; + case -7: /* ata66 */ +#ifdef CONFIG_BLK_DEV_IDEPCI + hwif->udma_four = 1; + goto done; +#else /* !CONFIG_BLK_DEV_IDEPCI */ + hwif->udma_four = 0; + goto bad_hwif; +#endif /* CONFIG_BLK_DEV_IDEPCI */ + case -6: /* dma */ + hwif->autodma = 1; + goto done; + case -5: /* "reset" */ + hwif->reset = 1; + goto done; + case -4: /* "noautotune" */ + hwif->drives[0].autotune = 2; + hwif->drives[1].autotune = 2; + goto done; + case -3: /* "autotune" */ + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + goto done; + case -2: /* "serialize" */ + do_serialize: + hwif->mate = &ide_hwifs[hw^1]; + hwif->mate->mate = hwif; + hwif->serialized = hwif->mate->serialized = 1; + goto done; + + case -1: /* "noprobe" */ + hwif->noprobe = 1; + goto done; + + case 1: /* base */ + vals[1] = vals[0] + 0x206; /* default ctl */ + case 2: /* base,ctl */ + vals[2] = 0; /* default irq = probe for it */ + case 3: /* base,ctl,irq */ + hwif->hw.irq = vals[2]; + ide_init_hwif_ports(&hwif->hw, (ide_ioreg_t) vals[0], (ide_ioreg_t) vals[1], &hwif->irq); + memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports)); + hwif->irq = vals[2]; + hwif->noprobe = 0; + hwif->chipset = ide_generic; + goto done; + + case 0: goto bad_option; + default: + printk(" -- SUPPORT NOT CONFIGURED IN THIS KERNEL\n"); + return 1; + } + } +bad_option: + printk(" -- BAD OPTION\n"); + return 1; +bad_hwif: + printk("-- NOT SUPPORTED ON ide%d", hw); +done: + printk("\n"); + return 1; +} + +/* + * probe_for_hwifs() finds/initializes "known" IDE interfaces + */ +static void __init probe_for_hwifs (void) +{ +#ifdef CONFIG_PCI + if (pci_present()) + { +#ifdef CONFIG_BLK_DEV_IDEPCI + ide_scan_pcibus(ide_scan_direction); +#endif /* CONFIG_BLK_DEV_IDEPCI */ + } +#endif /* CONFIG_PCI */ + +#ifdef CONFIG_ETRAX_IDE + { + extern void init_e100_ide(void); + init_e100_ide(); + } +#endif /* CONFIG_ETRAX_IDE */ +#ifdef CONFIG_BLK_DEV_CMD640 + { + extern void ide_probe_for_cmd640x(void); + ide_probe_for_cmd640x(); + } +#endif /* CONFIG_BLK_DEV_CMD640 */ +#ifdef CONFIG_BLK_DEV_PDC4030 + { + extern int ide_probe_for_pdc4030(void); + (void) ide_probe_for_pdc4030(); + } +#endif /* CONFIG_BLK_DEV_PDC4030 */ +#ifdef CONFIG_BLK_DEV_IDE_PMAC + { + extern void pmac_ide_probe(void); + pmac_ide_probe(); + } +#endif /* CONFIG_BLK_DEV_IDE_PMAC */ +#ifdef CONFIG_BLK_DEV_IDE_SWARM + { + extern void swarm_ide_probe(void); + swarm_ide_probe(); + } +#endif /* CONFIG_BLK_DEV_IDE_SWARM */ +#ifdef CONFIG_BLK_DEV_IDE_ICSIDE + { + extern void icside_init(void); + icside_init(); + } +#endif /* CONFIG_BLK_DEV_IDE_ICSIDE */ +#ifdef CONFIG_BLK_DEV_IDE_RAPIDE + { + extern void rapide_init(void); + rapide_init(); + } +#endif /* CONFIG_BLK_DEV_IDE_RAPIDE */ +#ifdef CONFIG_BLK_DEV_GAYLE + { + extern void gayle_init(void); + gayle_init(); + } +#endif /* CONFIG_BLK_DEV_GAYLE */ +#ifdef CONFIG_BLK_DEV_FALCON_IDE + { + extern void falconide_init(void); + falconide_init(); + } +#endif /* CONFIG_BLK_DEV_FALCON_IDE */ +#ifdef CONFIG_BLK_DEV_MAC_IDE + { + extern void macide_init(void); + macide_init(); + } +#endif /* CONFIG_BLK_DEV_MAC_IDE */ +#ifdef CONFIG_BLK_DEV_Q40IDE + { + extern void q40ide_init(void); + q40ide_init(); + } +#endif /* CONFIG_BLK_DEV_Q40IDE */ +#ifdef CONFIG_BLK_DEV_BUDDHA + { + extern void buddha_init(void); + buddha_init(); + } +#endif /* CONFIG_BLK_DEV_BUDDHA */ +#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) + { + extern void pnpide_init(int enable); + pnpide_init(1); + } +#endif /* CONFIG_BLK_DEV_ISAPNP */ +} + +void __init ide_init_builtin_drivers (void) +{ + /* + * Probe for special PCI and other "known" interface chipsets + */ + probe_for_hwifs (); + +#ifdef CONFIG_BLK_DEV_IDE +#if defined(__mc68000__) || defined(CONFIG_APUS) + if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) { + ide_get_lock(&ide_intr_lock, NULL, NULL);/* for atari only */ + disable_irq(ide_hwifs[0].irq); /* disable_irq_nosync ?? */ +// disable_irq_nosync(ide_hwifs[0].irq); + } +#endif /* __mc68000__ || CONFIG_APUS */ + + (void) ideprobe_init(); + +#if defined(__mc68000__) || defined(CONFIG_APUS) + if (ide_hwifs[0].io_ports[IDE_DATA_OFFSET]) { + enable_irq(ide_hwifs[0].irq); + ide_release_lock(&ide_lock); /* for atari only */ + } +#endif /* __mc68000__ || CONFIG_APUS */ +#endif /* CONFIG_BLK_DEV_IDE */ + +#ifdef CONFIG_PROC_FS + proc_ide_create(); +#endif + + /* + * Attempt to match drivers for the available drives + */ +#ifdef CONFIG_BLK_DEV_IDEDISK + (void) idedisk_init(); +#endif /* CONFIG_BLK_DEV_IDEDISK */ +#ifdef CONFIG_BLK_DEV_IDECD + (void) ide_cdrom_init(); +#endif /* CONFIG_BLK_DEV_IDECD */ +#ifdef CONFIG_BLK_DEV_IDETAPE + (void) idetape_init(); +#endif /* CONFIG_BLK_DEV_IDETAPE */ +#ifdef CONFIG_BLK_DEV_IDEFLOPPY + (void) idefloppy_init(); +#endif /* CONFIG_BLK_DEV_IDEFLOPPY */ +#ifdef CONFIG_BLK_DEV_IDESCSI + #ifdef CONFIG_SCSI + (void) idescsi_init(); + #else + #warning ide scsi-emulation selected but no SCSI-subsystem in kernel + #endif +#endif /* CONFIG_BLK_DEV_IDESCSI */ +} + +static int default_cleanup (ide_drive_t *drive) +{ + return ide_unregister_subdriver(drive); +} + +static int default_standby (ide_drive_t *drive) +{ + return 0; +} + +static int default_suspend (ide_drive_t *drive) +{ + return 0; +} + +static int default_resume (ide_drive_t *drive) +{ + return 0; +} + +static int default_flushcache (ide_drive_t *drive) +{ + return 0; +} + +static ide_startstop_t default_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + ide_end_request(drive, 0); + return ide_stopped; +} + +static int default_end_request (ide_drive_t *drive, int uptodate) +{ + return ide_end_request(drive, uptodate); +} + +static byte default_sense (ide_drive_t *drive, const char *msg, byte stat) +{ + return ide_dump_status(drive, msg, stat); +} + +static ide_startstop_t default_error (ide_drive_t *drive, const char *msg, byte stat) +{ + return ide_error(drive, msg, stat); +} + +static int default_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + return -EIO; +} + +static int default_open (struct inode *inode, struct file *filp, ide_drive_t *drive) +{ + drive->usage--; + return -EIO; +} + +static void default_release (struct inode *inode, struct file *filp, ide_drive_t *drive) +{ +} + +static int default_check_media_change (ide_drive_t *drive) +{ + return 1; +} + +static void default_pre_reset (ide_drive_t *drive) +{ +} + +static unsigned long default_capacity (ide_drive_t *drive) +{ + return 0x7fffffff; +} + +static ide_startstop_t default_special (ide_drive_t *drive) +{ + special_t *s = &drive->special; + + s->all = 0; + drive->mult_req = 0; + return ide_stopped; +} + +static int default_init (void) +{ + return 0; +} + +static int default_reinit (ide_drive_t *drive) +{ + printk(KERN_ERR "%s: does not support hotswap of device class !\n", drive->name); + + return 0; +} + +static void setup_driver_defaults (ide_drive_t *drive) +{ + ide_driver_t *d = drive->driver; + + if (d->cleanup == NULL) d->cleanup = default_cleanup; + if (d->standby == NULL) d->standby = default_standby; + if (d->suspend == NULL) d->suspend = default_suspend; + if (d->resume == NULL) d->resume = default_resume; + if (d->flushcache == NULL) d->flushcache = default_flushcache; + if (d->do_request == NULL) d->do_request = default_do_request; + if (d->end_request == NULL) d->end_request = default_end_request; + if (d->sense == NULL) d->sense = default_sense; + if (d->error == NULL) d->error = default_error; + if (d->ioctl == NULL) d->ioctl = default_ioctl; + if (d->open == NULL) d->open = default_open; + if (d->release == NULL) d->release = default_release; + if (d->media_change == NULL) d->media_change = default_check_media_change; + if (d->pre_reset == NULL) d->pre_reset = default_pre_reset; + if (d->capacity == NULL) d->capacity = default_capacity; + if (d->special == NULL) d->special = default_special; + if (d->init == NULL) d->init = default_init; + if (d->reinit == NULL) d->reinit = default_reinit; +} + +ide_drive_t *ide_scan_devices (byte media, const char *name, ide_driver_t *driver, int n) +{ + unsigned int unit, index, i; + + for (index = 0, i = 0; index < MAX_HWIFS; ++index) { + ide_hwif_t *hwif = &ide_hwifs[index]; + if (!hwif->present) + continue; + for (unit = 0; unit < MAX_DRIVES; ++unit) { + ide_drive_t *drive = &hwif->drives[unit]; + char *req = drive->driver_req; + if (*req && !strstr(name, req)) + continue; + if (drive->present && drive->media == media && drive->driver == driver && ++i > n) + return drive; + } + } + return NULL; +} + +int ide_register_subdriver (ide_drive_t *drive, ide_driver_t *driver, int version) +{ + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + if (version != IDE_SUBDRIVER_VERSION || !drive->present || + drive->driver != NULL || drive->busy || drive->usage) { + spin_unlock_irqrestore(&ide_lock, flags); + return 1; + } + drive->driver = driver; + setup_driver_defaults(drive); + spin_unlock_irqrestore(&ide_lock, flags); + if (drive->autotune != 2) { + if (driver->supports_dma && HWIF(drive)->dmaproc != NULL) { + /* + * Force DMAing for the beginning of the check. + * Some chipsets appear to do interesting things, + * if not checked and cleared. + * PARANOIA!!! + */ + (void) (HWIF(drive)->dmaproc(ide_dma_off_quietly, drive)); + (void) (HWIF(drive)->dmaproc(ide_dma_check, drive)); + } + drive->dsc_overlap = (drive->next != drive && driver->supports_dsc_overlap); + drive->nice1 = 1; + } + drive->revalidate = 1; + drive->suspend_reset = 0; +#ifdef CONFIG_PROC_FS + ide_add_proc_entries(drive->proc, generic_subdriver_entries, drive); + ide_add_proc_entries(drive->proc, driver->proc, drive); +#endif + return 0; +} + +int ide_unregister_subdriver (ide_drive_t *drive) +{ + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + if (drive->usage || drive->busy || + drive->driver == NULL || DRIVER(drive)->busy) { + spin_unlock_irqrestore(&ide_lock, flags); + return 1; + } +#if defined(CONFIG_BLK_DEV_ISAPNP) && defined(CONFIG_ISAPNP) && defined(MODULE) + pnpide_init(0); +#endif /* CONFIG_BLK_DEV_ISAPNP */ +#ifdef CONFIG_PROC_FS + ide_remove_proc_entries(drive->proc, DRIVER(drive)->proc); + ide_remove_proc_entries(drive->proc, generic_subdriver_entries); +#endif + auto_remove_settings(drive); + drive->driver = NULL; + spin_unlock_irqrestore(&ide_lock, flags); + return 0; +} + +int ide_register_module (ide_module_t *module) +{ + ide_module_t *p = ide_modules; + + while (p) { + if (p == module) + return 1; + p = p->next; + } + module->next = ide_modules; + ide_modules = module; + revalidate_drives(); + return 0; +} + +void ide_unregister_module (ide_module_t *module) +{ + ide_module_t **p; + + for (p = &ide_modules; (*p) && (*p) != module; p = &((*p)->next)); + if (*p) + *p = (*p)->next; +} + +struct block_device_operations ide_fops[] = {{ + owner: THIS_MODULE, + open: ide_open, + release: ide_release, + ioctl: ide_ioctl, + check_media_change: ide_check_media_change, + revalidate: ide_revalidate_disk +}}; + +EXPORT_SYMBOL(ide_hwifs); +EXPORT_SYMBOL(ide_register_module); +EXPORT_SYMBOL(ide_unregister_module); +EXPORT_SYMBOL(ide_spin_wait_hwgroup); + +/* + * Probe module + */ +devfs_handle_t ide_devfs_handle; + +EXPORT_SYMBOL(ide_lock); +EXPORT_SYMBOL(ide_probe); +EXPORT_SYMBOL(drive_is_flashcard); +EXPORT_SYMBOL(ide_timer_expiry); +EXPORT_SYMBOL(ide_intr); +EXPORT_SYMBOL(ide_fops); +EXPORT_SYMBOL(ide_get_queue); +EXPORT_SYMBOL(ide_add_generic_settings); +EXPORT_SYMBOL(ide_devfs_handle); +EXPORT_SYMBOL(do_ide_request); +/* + * Driver module + */ +EXPORT_SYMBOL(ide_scan_devices); +EXPORT_SYMBOL(ide_register_subdriver); +EXPORT_SYMBOL(ide_unregister_subdriver); +EXPORT_SYMBOL(ide_replace_subdriver); +EXPORT_SYMBOL(ide_set_handler); +EXPORT_SYMBOL(ide_dump_status); +EXPORT_SYMBOL(ide_error); +EXPORT_SYMBOL(ide_fixstring); +EXPORT_SYMBOL(ide_do_reset); +EXPORT_SYMBOL(restart_request); +EXPORT_SYMBOL(ide_init_drive_cmd); +EXPORT_SYMBOL(ide_do_drive_cmd); +EXPORT_SYMBOL(ide_end_drive_cmd); +EXPORT_SYMBOL(ide_end_request); +EXPORT_SYMBOL(ide_revalidate_drive); +EXPORT_SYMBOL(ide_revalidate_disk); +EXPORT_SYMBOL(ide_cmd); +EXPORT_SYMBOL(ide_wait_cmd); +EXPORT_SYMBOL(ide_wait_cmd_task); +EXPORT_SYMBOL(ide_delay_50ms); +EXPORT_SYMBOL(ide_stall_queue); +#ifdef CONFIG_PROC_FS +EXPORT_SYMBOL(ide_add_proc_entries); +EXPORT_SYMBOL(ide_remove_proc_entries); +EXPORT_SYMBOL(proc_ide_read_geometry); +EXPORT_SYMBOL(create_proc_ide_interfaces); +EXPORT_SYMBOL(recreate_proc_ide_device); +EXPORT_SYMBOL(destroy_proc_ide_device); +#endif +EXPORT_SYMBOL(ide_add_setting); +EXPORT_SYMBOL(ide_remove_setting); + +EXPORT_SYMBOL(ide_register_hw); +EXPORT_SYMBOL(ide_register); +EXPORT_SYMBOL(ide_unregister); +EXPORT_SYMBOL(ide_setup_ports); +EXPORT_SYMBOL(hwif_unregister); +EXPORT_SYMBOL(get_info_ptr); +EXPORT_SYMBOL(current_capacity); + +EXPORT_SYMBOL(system_bus_clock); + +EXPORT_SYMBOL(ide_reinit_drive); + +static int ide_notify_reboot (struct notifier_block *this, unsigned long event, void *x) +{ + ide_hwif_t *hwif; + ide_drive_t *drive; + int i, unit; + + switch (event) { + case SYS_HALT: + case SYS_POWER_OFF: + case SYS_RESTART: + break; + default: + return NOTIFY_DONE; + } + + printk("flushing ide devices: "); + + for (i = 0; i < MAX_HWIFS; i++) { + hwif = &ide_hwifs[i]; + if (!hwif->present) + continue; + for (unit = 0; unit < MAX_DRIVES; ++unit) { + drive = &hwif->drives[unit]; + if (!drive->present) + continue; + + /* set the drive to standby */ + printk("%s ", drive->name); + if (event != SYS_RESTART) + if (drive->driver != NULL && DRIVER(drive)->standby(drive)) + continue; + + if (drive->driver != NULL && DRIVER(drive)->cleanup(drive)) + continue; + } + } + printk("\n"); + return NOTIFY_DONE; +} + +static struct notifier_block ide_notifier = { + ide_notify_reboot, + NULL, + 5 +}; + +/* + * This is gets invoked once during initialization, to set *everything* up + */ +int __init ide_init (void) +{ + static char banner_printed; + int i; + + if (!banner_printed) { + printk(KERN_INFO "Uniform Multi-Platform E-IDE driver " REVISION "\n"); + ide_devfs_handle = devfs_mk_dir (NULL, "ide", NULL); + system_bus_speed = ide_system_bus_speed(); + banner_printed = 1; + } + + init_ide_data (); + + initializing = 1; + ide_init_builtin_drivers(); + initializing = 0; + + for (i = 0; i < MAX_HWIFS; ++i) { + ide_hwif_t *hwif = &ide_hwifs[i]; + if (hwif->present) + ide_geninit(hwif); + } + + register_reboot_notifier(&ide_notifier); + return 0; +} + +module_init(ide_init); + +#ifdef MODULE +char *options = NULL; +MODULE_PARM(options,"s"); +MODULE_LICENSE("GPL"); + +static void __init parse_options (char *line) +{ + char *next = line; + + if (line == NULL || !*line) + return; + while ((line = next) != NULL) { + if ((next = strchr(line,' ')) != NULL) + *next++ = 0; + if (!ide_setup(line)) + printk ("Unknown option '%s'\n", line); + } +} + +int init_module (void) +{ + parse_options(options); + return ide_init(); +} + +void cleanup_module (void) +{ + int index; + + unregister_reboot_notifier(&ide_notifier); + for (index = 0; index < MAX_HWIFS; ++index) { + ide_unregister(index); +#if defined(CONFIG_BLK_DEV_IDEDMA) && !defined(CONFIG_DMA_NONPCI) + if (ide_hwifs[index].dma_base) + (void) ide_release_dma(&ide_hwifs[index]); +#endif /* (CONFIG_BLK_DEV_IDEDMA) && !(CONFIG_DMA_NONPCI) */ + } + +#ifdef CONFIG_PROC_FS + proc_ide_destroy(); +#endif + devfs_unregister (ide_devfs_handle); +} + +#else /* !MODULE */ + +__setup("", ide_setup); + +#endif /* MODULE */ diff -Nru a/drivers/ide/ide_modes.h b/drivers/ide/ide_modes.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ide_modes.h Fri Aug 16 14:35:01 2002 @@ -0,0 +1,236 @@ +/* + * linux/drivers/ide/ide_modes.h + * + * Copyright (C) 1996 Linus Torvalds, Igor Abramov, and Mark Lord + */ + +#ifndef _IDE_MODES_H +#define _IDE_MODES_H + +#include + +/* + * Shared data/functions for determining best PIO mode for an IDE drive. + * Most of this stuff originally lived in cmd640.c, and changes to the + * ide_pio_blacklist[] table should be made with EXTREME CAUTION to avoid + * breaking the fragile cmd640.c support. + */ + +#ifdef CONFIG_BLK_DEV_IDE_MODES + +/* + * Standard (generic) timings for PIO modes, from ATA2 specification. + * These timings are for access to the IDE data port register *only*. + * Some drives may specify a mode, while also specifying a different + * value for cycle_time (from drive identification data). + */ +typedef struct ide_pio_timings_s { + int setup_time; /* Address setup (ns) minimum */ + int active_time; /* Active pulse (ns) minimum */ + int cycle_time; /* Cycle time (ns) minimum = (setup + active + recovery) */ +} ide_pio_timings_t; + +typedef struct ide_pio_data_s { + byte pio_mode; + byte use_iordy; + byte overridden; + byte blacklisted; + unsigned int cycle_time; +} ide_pio_data_t; + +#ifndef _IDE_C + +int ide_scan_pio_blacklist (char *model); +byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d); +extern const ide_pio_timings_t ide_pio_timings[6]; + +#else /* _IDE_C */ + +const ide_pio_timings_t ide_pio_timings[6] = { + { 70, 165, 600 }, /* PIO Mode 0 */ + { 50, 125, 383 }, /* PIO Mode 1 */ + { 30, 100, 240 }, /* PIO Mode 2 */ + { 30, 80, 180 }, /* PIO Mode 3 with IORDY */ + { 25, 70, 120 }, /* PIO Mode 4 with IORDY */ + { 20, 50, 100 } /* PIO Mode 5 with IORDY (nonstandard) */ +}; + +/* + * Black list. Some drives incorrectly report their maximal PIO mode, + * at least in respect to CMD640. Here we keep info on some known drives. + */ +static struct ide_pio_info { + const char *name; + int pio; +} ide_pio_blacklist [] = { +/* { "Conner Peripherals 1275MB - CFS1275A", 4 }, */ + { "Conner Peripherals 540MB - CFS540A", 3 }, + + { "WDC AC2700", 3 }, + { "WDC AC2540", 3 }, + { "WDC AC2420", 3 }, + { "WDC AC2340", 3 }, + { "WDC AC2250", 0 }, + { "WDC AC2200", 0 }, + { "WDC AC21200", 4 }, + { "WDC AC2120", 0 }, + { "WDC AC2850", 3 }, + { "WDC AC1270", 3 }, + { "WDC AC1170", 1 }, + { "WDC AC1210", 1 }, + { "WDC AC280", 0 }, +/* { "WDC AC21000", 4 }, */ + { "WDC AC31000", 3 }, + { "WDC AC31200", 3 }, +/* { "WDC AC31600", 4 }, */ + + { "Maxtor 7131 AT", 1 }, + { "Maxtor 7171 AT", 1 }, + { "Maxtor 7213 AT", 1 }, + { "Maxtor 7245 AT", 1 }, + { "Maxtor 7345 AT", 1 }, + { "Maxtor 7546 AT", 3 }, + { "Maxtor 7540 AV", 3 }, + + { "SAMSUNG SHD-3121A", 1 }, + { "SAMSUNG SHD-3122A", 1 }, + { "SAMSUNG SHD-3172A", 1 }, + +/* { "ST51080A", 4 }, + * { "ST51270A", 4 }, + * { "ST31220A", 4 }, + * { "ST31640A", 4 }, + * { "ST32140A", 4 }, + * { "ST3780A", 4 }, + */ + { "ST5660A", 3 }, + { "ST3660A", 3 }, + { "ST3630A", 3 }, + { "ST3655A", 3 }, + { "ST3391A", 3 }, + { "ST3390A", 1 }, + { "ST3600A", 1 }, + { "ST3290A", 0 }, + { "ST3144A", 0 }, + { "ST3491A", 1 }, /* reports 3, should be 1 or 2 (depending on */ + /* drive) according to Seagates FIND-ATA program */ + + { "QUANTUM ELS127A", 0 }, + { "QUANTUM ELS170A", 0 }, + { "QUANTUM LPS240A", 0 }, + { "QUANTUM LPS210A", 3 }, + { "QUANTUM LPS270A", 3 }, + { "QUANTUM LPS365A", 3 }, + { "QUANTUM LPS540A", 3 }, + { "QUANTUM LIGHTNING 540A", 3 }, + { "QUANTUM LIGHTNING 730A", 3 }, + + { "QUANTUM FIREBALL_540", 3 }, /* Older Quantum Fireballs don't work */ + { "QUANTUM FIREBALL_640", 3 }, + { "QUANTUM FIREBALL_1080", 3 }, + { "QUANTUM FIREBALL_1280", 3 }, + { NULL, 0 } +}; + +/* + * This routine searches the ide_pio_blacklist for an entry + * matching the start/whole of the supplied model name. + * + * Returns -1 if no match found. + * Otherwise returns the recommended PIO mode from ide_pio_blacklist[]. + */ +int ide_scan_pio_blacklist (char *model) +{ + struct ide_pio_info *p; + + for (p = ide_pio_blacklist; p->name != NULL; p++) { + if (strncmp(p->name, model, strlen(p->name)) == 0) + return p->pio; + } + return -1; +} + +/* + * This routine returns the recommended PIO settings for a given drive, + * based on the drive->id information and the ide_pio_blacklist[]. + * This is used by most chipset support modules when "auto-tuning". + */ + +/* + * Drive PIO mode auto selection + */ +byte ide_get_best_pio_mode (ide_drive_t *drive, byte mode_wanted, byte max_mode, ide_pio_data_t *d) +{ + int pio_mode; + int cycle_time = 0; + int use_iordy = 0; + struct hd_driveid* id = drive->id; + int overridden = 0; + int blacklisted = 0; + + if (mode_wanted != 255) { + pio_mode = mode_wanted; + } else if (!drive->id) { + pio_mode = 0; + } else if ((pio_mode = ide_scan_pio_blacklist(id->model)) != -1) { + overridden = 1; + blacklisted = 1; + use_iordy = (pio_mode > 2); + } else { + pio_mode = id->tPIO; + if (pio_mode > 2) { /* 2 is maximum allowed tPIO value */ + pio_mode = 2; + overridden = 1; + } + if (id->field_valid & 2) { /* drive implements ATA2? */ + if (id->capability & 8) { /* drive supports use_iordy? */ + use_iordy = 1; + cycle_time = id->eide_pio_iordy; + if (id->eide_pio_modes & 7) { + overridden = 0; + if (id->eide_pio_modes & 4) + pio_mode = 5; + else if (id->eide_pio_modes & 2) + pio_mode = 4; + else + pio_mode = 3; + } + } else { + cycle_time = id->eide_pio; + } + } + +#if 0 + if (drive->id->major_rev_num & 0x0004) printk("ATA-2 "); +#endif + + /* + * Conservative "downgrade" for all pre-ATA2 drives + */ + if (pio_mode && pio_mode < 4) { + pio_mode--; + overridden = 1; +#if 0 + use_iordy = (pio_mode > 2); +#endif + if (cycle_time && cycle_time < ide_pio_timings[pio_mode].cycle_time) + cycle_time = 0; /* use standard timing */ + } + } + if (pio_mode > max_mode) { + pio_mode = max_mode; + cycle_time = 0; + } + if (d) { + d->pio_mode = pio_mode; + d->cycle_time = cycle_time ? cycle_time : ide_pio_timings[pio_mode].cycle_time; + d->use_iordy = use_iordy; + d->overridden = overridden; + d->blacklisted = blacklisted; + } + return pio_mode; +} + +#endif /* _IDE_C */ +#endif /* CONFIG_BLK_DEV_IDE_MODES */ +#endif /* _IDE_MODES_H */ diff -Nru a/drivers/ide/it8172.c b/drivers/ide/it8172.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/it8172.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,389 @@ +/* + * + * BRIEF MODULE DESCRIPTION + * IT8172 IDE controller support + * + * Copyright 2000 MontaVista Software Inc. + * Author: MontaVista Software, Inc. + * stevel@mvista.com or source@mvista.com + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ide_modes.h" + +/* + * Prototypes + */ +static byte it8172_ratemask (ide_drive_t *drive); +static byte it8172_ratefilter (ide_drive_t *drive, byte speed); +static void it8172_tune_drive (ide_drive_t *drive, byte pio); +static byte it8172_dma_2_pio (byte xfer_rate); +static int it8172_tune_chipset (ide_drive_t *drive, byte xferspeed); +#ifdef CONFIG_BLK_DEV_IDEDMA +static int it8172_config_chipset_for_dma (ide_drive_t *drive); +static int it8172_dmaproc(ide_dma_action_t func, ide_drive_t *drive); +#endif +unsigned int __init pci_init_it8172 (struct pci_dev *dev, const char *name); +void __init ide_init_it8172 (ide_hwif_t *hwif); + +static byte it8172_ratemask (ide_drive_t *drive) +{ + byte mode = 0x00; +#if 1 + mode |= 0x01; +#endif + return (mode &= ~0xF8); +} + +static byte it8172_ratefilter (ide_drive_t *drive, byte speed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + byte mode = it8172_ratemask(drive); + + switch(mode) { + case 0x04: // while (speed > XFER_UDMA_6) speed--; break; + case 0x03: // while (speed > XFER_UDMA_5) speed--; break; + case 0x02: while (speed > XFER_UDMA_4) speed--; break; + case 0x01: while (speed > XFER_UDMA_2) speed--; break; + case 0x00: + default: while (speed > XFER_MW_DMA_2) speed--; break; + break; + } +#else + while (speed > XFER_PIO_4) speed--; +#endif /* CONFIG_BLK_DEV_IDEDMA */ +// printk("%s: mode == %02x speed == %02x\n", drive->name, mode, speed); + return speed; +} + +static void it8172_tune_drive (ide_drive_t *drive, byte pio) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + int is_slave = (hwif->drives[1] == drive); + unsigned long flags; + u16 drive_enables; + u32 drive_timing; + + pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + spin_lock_irqsave(&ide_lock, flags); + pci_read_config_word(dev, 0x40, &drive_enables); + pci_read_config_dword(dev, 0x44, &drive_timing); + + /* + * FIX! The DIOR/DIOW pulse width and recovery times in port 0x44 + * are being left at the default values of 8 PCI clocks (242 nsec + * for a 33 MHz clock). These can be safely shortened at higher + * PIO modes. The DIOR/DIOW pulse width and recovery times only + * apply to PIO modes, not to the DMA modes. + */ + + /* + * Enable port 0x44. The IT8172G spec is confused; it calls + * this register the "Slave IDE Timing Register", but in fact, + * it controls timing for both master and slave drives. + */ + drive_enables |= 0x4000; + + if (is_slave) { + drive_enables &= 0xc006; + if (pio > 1) + /* enable prefetch and IORDY sample-point */ + drive_enables |= 0x0060; + } else { + drive_enables &= 0xc060; + if (pio > 1) + /* enable prefetch and IORDY sample-point */ + drive_enables |= 0x0006; + } + + pci_write_config_word(dev, 0x40, drive_enables); + spin_unlock_irqrestore(&ide_lock, flags) +} + +static byte it8172_dma_2_pio (byte xfer_rate) +{ + switch(xfer_rate) { + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + case XFER_MW_DMA_2: + case XFER_PIO_4: + return 4; + case XFER_MW_DMA_1: + case XFER_PIO_3: + return 3; + case XFER_SW_DMA_2: + case XFER_PIO_2: + return 2; + case XFER_MW_DMA_0: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + case XFER_PIO_1: + case XFER_PIO_0: + case XFER_PIO_SLOW: + default: + return 0; + } +} + +static int it8172_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte speed = it8172_ratefilter(drive, xferspeed); + int a_speed = 3 << (drive->dn * 4); + int u_flag = 1 << drive->dn; + int u_speed = 0; + byte reg48, reg4a; + + pci_read_config_byte(dev, 0x48, ®48); + pci_read_config_byte(dev, 0x4a, ®4a); + + /* + * Setting the DMA cycle time to 2 or 3 PCI clocks (60 and 91 nsec + * at 33 MHz PCI clock) seems to cause BadCRC errors during DMA + * transfers on some drives, even though both numbers meet the minimum + * ATAPI-4 spec of 73 and 54 nsec for UDMA 1 and 2 respectively. + * So the faster times are just commented out here. The good news is + * that the slower cycle time has very little affect on transfer + * performance. + */ + + switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA + case XFER_UDMA_4: + case XFER_UDMA_2: //u_speed = 2 << (drive->dn * 4); break; + case XFER_UDMA_5: + case XFER_UDMA_3: + case XFER_UDMA_1: //u_speed = 1 << (drive->dn * 4); break; + case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break; + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + case XFER_SW_DMA_2: break; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_0: break; + default: return -1; + } + + if (speed >= XFER_UDMA_0) { + pci_write_config_byte(dev, 0x48, reg48 | u_flag); + reg4a &= ~a_speed; + pci_write_config_byte(dev, 0x4a, reg4a | u_speed); + } else { + pci_write_config_byte(dev, 0x48, reg48 & ~u_flag); + pci_write_config_byte(dev, 0x4a, reg4a & ~a_speed); + } + + it8172_tune_drive(drive, it8172_dma_2_pio(speed)); + return (ide_config_drive_speed(drive, speed)); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static int it8172_config_chipset_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + byte mode = it8172_ratemask(drive); + byte speed, tspeed, dma = 1; + + switch(mode) { + case 0x01: + if (id->dma_ultra & 0x0040) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0020) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0010) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0008) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0004) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0002) + { speed = XFER_UDMA_1; break; } + if (id->dma_ultra & 0x0001) + { speed = XFER_UDMA_0; break; } + case 0x00: + if (id->dma_mword & 0x0004) + { speed = XFER_MW_DMA_2; break; } + if (id->dma_mword & 0x0002) + { speed = XFER_MW_DMA_1; break; } + if (id->dma_1word & 0x0004) + { speed = XFER_SW_DMA_2; break; } + default: + tspeed = ide_get_best_pio_mode(drive, 255, 4, NULL); + speed = it8172_dma_2_pio(XFER_PIO_0 + tspeed); + dma = 0; + break; + } + + (void) it8172_tune_chipset(drive, speed); + +// return ((int)(dma) ? ide_dma_on : ide_dma_off_quietly); + return ((int)((id->dma_ultra >> 11) & 7) ? ide_dma_on : + ((id->dma_ultra >> 8) & 7) ? ide_dma_on : + ((id->dma_mword >> 8) & 7) ? ide_dma_on : + ((id->dma_1word >> 8) & 7) ? ide_dma_on : + ide_dma_off_quietly); +} + +static int config_drive_xfer_rate (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_dma_action_t dma_func = ide_dma_on; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && HWIF(drive)->autodma) { + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + goto fast_ata_pio; + } + dma_func = ide_dma_off_quietly; + if (id->field_valid & 4) { + if (id->dma_ultra & 0x007F) { + /* Force if Capable UltraDMA */ + dma_func = it8172_config_chipset_for_dma(drive); + if ((id->field_valid & 2) && + (dma_func != ide_dma_on)) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & 0x0007) || + (id->dma_1word & 0x007)) { + /* Force if Capable regular DMA modes */ + dma_func = it8172_config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } + } else if (ide_dmaproc(ide_dma_good_drive, drive)) { + if (id->eide_dma_time > 150) { + goto no_dma_set; + } + /* Consult the list of known "good" drives */ + dma_func = it8172_config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + dma_func = ide_dma_off_quietly; +no_dma_set: + it8172_tune_drive(drive, 5); + } + return HWIF(drive)->dmaproc(dma_func, drive); +} + +static int it8172_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + return config_drive_xfer_rate(drive); + default : + break; + } + /* Other cases are done by generic IDE-DMA code. */ + return ide_dmaproc(func, drive); +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + +unsigned int __init pci_init_it8172 (struct pci_dev *dev, const char *name) +{ + unsigned char progif; + + /* + * Place both IDE interfaces into PCI "native" mode + */ + pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); + pci_write_config_byte(dev, PCI_CLASS_PROG, progif | 0x05); + + return IT8172_IDE_IRQ; +} + + +void __init ide_init_it8172 (ide_hwif_t *hwif) +{ + struct pci_dev* dev = hwif->pci_dev; + unsigned long cmdBase, ctrlBase; + + hwif->autodma = 0; + hwif->tuneproc = &it8172_tune_drive; + hwif->speedproc = &it8172_tune_chipset; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + + cmdBase = dev->resource[0].start; + ctrlBase = dev->resource[1].start; + + ide_init_hwif_ports(&hwif->hw, cmdBase, ctrlBase | 2, NULL); + memcpy(hwif->io_ports, hwif->hw.io_ports, sizeof(hwif->io_ports)); + hwif->noprobe = 0; + + if (!hwif->dma_base) + return; + +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->dmaproc = &it8172_dmaproc; +# ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +# endif /* CONFIG_IDEDMA_AUTO */ +#endif /* !CONFIG_BLK_DEV_IDEDMA */ +} + +extern void ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d); + +void __init fixup_device_it8172 (struct pci_dev *dev, ide_pci_device_t *d) +{ + if ((!(PCI_FUNC(dev->devfn) & 1) || + (!((dev->class >> 8) == PCI_CLASS_STORAGE_IDE)))) + return; /* IT8172 is more than only a IDE controller */ + + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); +} + diff -Nru a/drivers/ide/macide.c b/drivers/ide/macide.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/macide.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,147 @@ +/* + * linux/drivers/ide/macide.c -- Macintosh IDE Driver + * + * Copyright (C) 1998 by Michael Schmitz + * + * This driver was written based on information obtained from the MacOS IDE + * driver binary by Mikael Forselius + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define IDE_BASE 0x50F1A000 /* Base address of IDE controller */ + +/* + * Generic IDE registers as offsets from the base + * These match MkLinux so they should be correct. + */ + +#define IDE_DATA 0x00 +#define IDE_ERROR 0x04 /* see err-bits */ +#define IDE_NSECTOR 0x08 /* nr of sectors to read/write */ +#define IDE_SECTOR 0x0c /* starting sector */ +#define IDE_LCYL 0x10 /* starting cylinder */ +#define IDE_HCYL 0x14 /* high byte of starting cyl */ +#define IDE_SELECT 0x18 /* 101dhhhh , d=drive, hhhh=head */ +#define IDE_STATUS 0x1c /* see status-bits */ +#define IDE_CONTROL 0x38 /* control/altstatus */ + +/* + * Mac-specific registers + */ + +/* + * this register is odd; it doesn't seem to do much and it's + * not word-aligned like virtually every other hardware register + * on the Mac... + */ + +#define IDE_IFR 0x101 /* (0x101) IDE interrupt flags on Quadra: + * + * Bit 0+1: some interrupt flags + * Bit 2+3: some interrupt enable + * Bit 4: ?? + * Bit 5: IDE interrupt flag (any hwif) + * Bit 6: maybe IDE interrupt enable (any hwif) ?? + * Bit 7: Any interrupt condition + */ + +volatile unsigned char *ide_ifr = (unsigned char *) (IDE_BASE + IDE_IFR); + +static int macide_offsets[IDE_NR_PORTS] = { + IDE_DATA, IDE_ERROR, IDE_NSECTOR, IDE_SECTOR, IDE_LCYL, + IDE_HCYL, IDE_SELECT, IDE_STATUS, IDE_CONTROL +}; + +int macide_ack_intr(ide_hwif_t* hwif) +{ + if (*ide_ifr & 0x20) { + *ide_ifr &= ~0x20; + return 1; + } + return 0; +} + +#ifdef CONFIG_BLK_DEV_MAC_MEDIABAY +static void macide_mediabay_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + int state = baboon->mb_status & 0x04; + + printk("macide: media bay %s detected\n", state? "removal":"insertion"); +} +#endif + +/* + * Probe for a Macintosh IDE interface + */ + +void macide_init(void) +{ + hw_regs_t hw; + int index = -1; + + switch (macintosh_config->ide_type) { + case MAC_IDE_QUADRA: + ide_setup_ports(&hw, (ide_ioreg_t)IDE_BASE, macide_offsets, + 0, 0, macide_ack_intr, IRQ_NUBUS_F); + index = ide_register_hw(&hw, NULL); + break; + case MAC_IDE_PB: + ide_setup_ports(&hw, (ide_ioreg_t)IDE_BASE, macide_offsets, + 0, 0, macide_ack_intr, IRQ_NUBUS_C); + index = ide_register_hw(&hw, NULL); + break; + case MAC_IDE_BABOON: + ide_setup_ports(&hw, (ide_ioreg_t)BABOON_BASE, macide_offsets, + 0, 0, NULL, IRQ_BABOON_1); + index = ide_register_hw(&hw, NULL); + if (index == -1) break; + if (macintosh_config->ident == MAC_MODEL_PB190) { + + /* Fix breakage in ide-disk.c: drive capacity */ + /* is not initialized for drives without a */ + /* hardware ID, and we cna't get that without */ + /* probing the drive which freezes a 190. */ + + ide_drive_t *drive = &ide_hwifs[index].drives[0]; + drive->capacity = drive->cyl*drive->head*drive->sect; + +#ifdef CONFIG_BLK_DEV_MAC_MEDIABAY + request_irq(IRQ_BABOON_2, macide_mediabay_interrupt, + IRQ_FLG_FAST, "mediabay", + macide_mediabay_interrupt); +#endif + } + break; + + default: + return; + } + + if (index != -1) { + if (macintosh_config->ide_type == MAC_IDE_QUADRA) + printk("ide%d: Macintosh Quadra IDE interface\n", index); + else if (macintosh_config->ide_type == MAC_IDE_PB) + printk("ide%d: Macintosh Powerbook IDE interface\n", index); + else if (macintosh_config->ide_type == MAC_IDE_BABOON) + printk("ide%d: Macintosh Powerbook Baboon IDE interface\n", index); + else + printk("ide%d: Unknown Macintosh IDE interface\n", index); + } +} diff -Nru a/drivers/ide/ns87415.c b/drivers/ide/ns87415.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/ns87415.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,204 @@ +/* + * linux/drivers/ide/ns87415.c Version 1.01 Mar. 18, 2000 + * + * Copyright (C) 1997-1998 Mark Lord + * Copyright (C) 1998 Eddie C. Dost + * Copyright (C) 1999-2000 Andre Hedrick + * + * Inspired by an earlier effort from David S. Miller + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static unsigned int ns87415_count = 0, ns87415_control[MAX_HWIFS] = { 0 }; + +/* + * This routine either enables/disables (according to drive->present) + * the IRQ associated with the port (HWIF(drive)), + * and selects either PIO or DMA handshaking for the next I/O operation. + */ +static void ns87415_prepare_drive (ide_drive_t *drive, unsigned int use_dma) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int bit, other, new, *old = (unsigned int *) hwif->select_data; + struct pci_dev *dev = hwif->pci_dev; + unsigned long flags; + + local_irq_save(flags); + new = *old; + + /* Adjust IRQ enable bit */ + bit = 1 << (8 + hwif->channel); + new = drive->present ? (new & ~bit) : (new | bit); + + /* Select PIO or DMA, DMA may only be selected for one drive/channel. */ + bit = 1 << (20 + drive->select.b.unit + (hwif->channel << 1)); + other = 1 << (20 + (1 - drive->select.b.unit) + (hwif->channel << 1)); + new = use_dma ? ((new & ~other) | bit) : (new & ~bit); + + if (new != *old) { + unsigned char stat; + + /* + * Don't change DMA engine settings while Write Buffers + * are busy. + */ + (void) pci_read_config_byte(dev, 0x43, &stat); + while (stat & 0x03) { + udelay(1); + (void) pci_read_config_byte(dev, 0x43, &stat); + } + + *old = new; + (void) pci_write_config_dword(dev, 0x40, new); + + /* + * And let things settle... + */ + udelay(10); + } + + local_irq_restore(flags); +} + +static void ns87415_selectproc (ide_drive_t *drive) +{ + ns87415_prepare_drive (drive, drive->using_dma); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static int ns87415_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + byte dma_stat; + + switch (func) { + case ide_dma_end: /* returns 1 on error, 0 otherwise */ + drive->waiting_for_dma = 0; + dma_stat = IN_BYTE(hwif->dma_base+2); + /* stop DMA */ + OUT_BYTE(IN_BYTE(hwif->dma_base)&~1, hwif->dma_base); + /* from ERRATA: clear the INTR & ERROR bits */ + OUT_BYTE(IN_BYTE(hwif->dma_base)|6, hwif->dma_base); + /* and free any DMA resources */ + ide_destroy_dmatable(drive); + /* verify good DMA status */ + return (dma_stat & 7) != 4; + case ide_dma_write: + case ide_dma_read: + /* select DMA xfer */ + ns87415_prepare_drive(drive, 1); + /* use standard DMA stuff */ + if (!ide_dmaproc(func, drive)) + return 0; + /* DMA failed: select PIO xfer */ + ns87415_prepare_drive(drive, 0); + return 1; + case ide_dma_check: + if (drive->media != ide_disk) + return ide_dmaproc(ide_dma_off_quietly, drive); + /* Fallthrough... */ + default: + return ide_dmaproc(func, drive); /* use standard DMA stuff */ + } +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +void __init ide_init_ns87415 (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + unsigned int ctrl, using_inta; + byte progif; +#ifdef __sparc_v9__ + int timeout; + byte stat; +#endif + + hwif->autodma = 0; + hwif->selectproc = &ns87415_selectproc; + + /* Set a good latency timer and cache line size value. */ + (void) pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64); +#ifdef __sparc_v9__ + (void) pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 0x10); +#endif + + /* + * We cannot probe for IRQ: both ports share common IRQ on INTA. + * Also, leave IRQ masked during drive probing, to prevent infinite + * interrupts from a potentially floating INTA.. + * + * IRQs get unmasked in selectproc when drive is first used. + */ + (void) pci_read_config_dword(dev, 0x40, &ctrl); + (void) pci_read_config_byte(dev, 0x09, &progif); + /* is irq in "native" mode? */ + using_inta = progif & (1 << (hwif->channel << 1)); + if (!using_inta) + using_inta = ctrl & (1 << (4 + hwif->channel)); + if (hwif->mate) { + hwif->select_data = hwif->mate->select_data; + } else { + hwif->select_data = (unsigned long) + &ns87415_control[ns87415_count++]; + ctrl |= (1 << 8) | (1 << 9); /* mask both IRQs */ + if (using_inta) + ctrl &= ~(1 << 6); /* unmask INTA */ + *((unsigned int *)hwif->select_data) = ctrl; + (void) pci_write_config_dword(dev, 0x40, ctrl); + + /* + * Set prefetch size to 512 bytes for both ports, + * but don't turn on/off prefetching here. + */ + pci_write_config_byte(dev, 0x55, 0xee); + +#ifdef __sparc_v9__ + /* + * XXX: Reset the device, if we don't it will not respond + * to SELECT_DRIVE() properly during first probe_hwif(). + */ + timeout = 10000; + OUT_BYTE(12, hwif->io_ports[IDE_CONTROL_OFFSET]); + udelay(10); + OUT_BYTE(8, hwif->io_ports[IDE_CONTROL_OFFSET]); + do { + udelay(50); + stat = IN_BYTE(hwif->io_ports[IDE_STATUS_OFFSET]); + if (stat == 0xff) + break; + } while ((stat & BUSY_STAT) && --timeout); +#endif + } + + if (!using_inta) + hwif->irq = hwif->channel ? 15 : 14; /* legacy mode */ + else if (!hwif->irq && hwif->mate && hwif->mate->irq) + hwif->irq = hwif->mate->irq; /* share IRQ with mate */ + + if (!hwif->dma_base) + return; + +#ifdef CONFIG_BLK_DEV_IDEDMA + OUT_BYTE(0x60, hwif->dma_base + 2); + hwif->dmaproc = &ns87415_dmaproc; +#ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +#endif /* CONFIG_IDEDMA_AUTO */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ +} diff -Nru a/drivers/ide/opti621.c b/drivers/ide/opti621.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/opti621.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,339 @@ +/* + * linux/drivers/ide/opti621.c Version 0.6 Jan 02, 1999 + * + * Copyright (C) 1996-1998 Linus Torvalds & authors (see below) + */ + +/* + * Authors: + * Jaromir Koutek , + * Jan Harkes , + * Mark Lord + * Some parts of code are from ali14xx.c and from rz1000.c. + * + * OPTi is trademark of OPTi, Octek is trademark of Octek. + * + * I used docs from OPTi databook, from ftp.opti.com, file 9123-0002.ps + * and disassembled/traced setupvic.exe (DOS program). + * It increases kernel code about 2 kB. + * I don't have this card no more, but I hope I can get some in case + * of needed development. + * My card is Octek PIDE 1.01 (on card) or OPTiViC (program). + * It has a place for a secondary connector in circuit, but nothing + * is there. Also BIOS says no address for + * secondary controller (see bellow in ide_init_opti621). + * I've only tested this on my system, which only has one disk. + * It's Western Digital WDAC2850, with PIO mode 3. The PCI bus + * is at 20 MHz (I have DX2/80, I tried PCI at 40, but I got random + * lockups). I tried the OCTEK double speed CD-ROM and + * it does not work! But I can't boot DOS also, so it's probably + * hardware fault. I have connected Conner 80MB, the Seagate 850MB (no + * problems) and Seagate 1GB (as slave, WD as master). My experiences + * with the third, 1GB drive: I got 3MB/s (hdparm), but sometimes + * it slows to about 100kB/s! I don't know why and I have + * not this drive now, so I can't try it again. + * I write this driver because I lost the paper ("manual") with + * settings of jumpers on the card and I have to boot Linux with + * Loadlin except LILO, cause I have to run the setupvic.exe program + * already or I get disk errors (my test: rpm -Vf + * /usr/X11R6/bin/XF86_SVGA - or any big file). + * Some numbers from hdparm -t /dev/hda: + * Timing buffer-cache reads: 32 MB in 3.02 seconds =10.60 MB/sec + * Timing buffered disk reads: 16 MB in 5.52 seconds = 2.90 MB/sec + * I have 4 Megs/s before, but I don't know why (maybe changes + * in hdparm test). + * After release of 0.1, I got some successful reports, so it might work. + * + * The main problem with OPTi is that some timings for master + * and slave must be the same. For example, if you have master + * PIO 3 and slave PIO 0, driver have to set some timings of + * master for PIO 0. Second problem is that opti621_tune_drive + * got only one drive to set, but have to set both drives. + * This is solved in compute_pios. If you don't set + * the second drive, compute_pios use ide_get_best_pio_mode + * for autoselect mode (you can change it to PIO 0, if you want). + * If you then set the second drive to another PIO, the old value + * (automatically selected) will be overrided by yours. + * There is a 25/33MHz switch in configuration + * register, but driver is written for use at any frequency which get + * (use idebus=xx to select PCI bus speed). + * Use ide0=autotune for automatical tune of the PIO modes. + * If you get strange results, do not use this and set PIO manually + * by hdparm. + * + * Version 0.1, Nov 8, 1996 + * by Jaromir Koutek, for 2.1.8. + * Initial version of driver. + * + * Version 0.2 + * Number 0.2 skipped. + * + * Version 0.3, Nov 29, 1997 + * by Mark Lord (probably), for 2.1.68 + * Updates for use with new IDE block driver. + * + * Version 0.4, Dec 14, 1997 + * by Jan Harkes + * Fixed some errors and cleaned the code. + * + * Version 0.5, Jan 2, 1998 + * by Jaromir Koutek + * Updates for use with (again) new IDE block driver. + * Update of documentation. + * + * Version 0.6, Jan 2, 1999 + * by Jaromir Koutek + * Reversed to version 0.3 of the driver, because + * 0.5 doesn't work. + */ + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ +#define OPTI621_DEBUG /* define for debug messages */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ide_modes.h" + +#define OPTI621_MAX_PIO 3 +/* In fact, I do not have any PIO 4 drive + * (address: 25 ns, data: 70 ns, recovery: 35 ns), + * but OPTi 82C621 is programmable and it can do (minimal values): + * on 40MHz PCI bus (pulse 25 ns): + * address: 25 ns, data: 25 ns, recovery: 50 ns; + * on 20MHz PCI bus (pulse 50 ns): + * address: 50 ns, data: 50 ns, recovery: 100 ns. + */ + +/* #define READ_PREFETCH 0 */ +/* Uncommnent for disable read prefetch. + * There is some readprefetch capatibility in hdparm, + * but when I type hdparm -P 1 /dev/hda, I got errors + * and till reset drive is inaccessible. + * This (hw) read prefetch is safe on my drive. + */ + +#ifndef READ_PREFETCH +#define READ_PREFETCH 0x40 /* read prefetch is enabled */ +#endif /* else read prefetch is disabled */ + +#define READ_REG 0 /* index of Read cycle timing register */ +#define WRITE_REG 1 /* index of Write cycle timing register */ +#define CNTRL_REG 3 /* index of Control register */ +#define STRAP_REG 5 /* index of Strap register */ +#define MISC_REG 6 /* index of Miscellaneous register */ + +int reg_base; + +#define PIO_NOT_EXIST 254 +#define PIO_DONT_KNOW 255 + +/* there are stored pio numbers from other calls of opti621_tune_drive */ + +static void compute_pios(ide_drive_t *drive, byte pio) +/* Store values into drive->drive_data + * second_contr - 0 for primary controller, 1 for secondary + * slave_drive - 0 -> pio is for master, 1 -> pio is for slave + * pio - PIO mode for selected drive (for other we don't know) + */ +{ + int d; + ide_hwif_t *hwif = HWIF(drive); + + drive->drive_data = ide_get_best_pio_mode(drive, pio, OPTI621_MAX_PIO, NULL); + for (d = 0; d < 2; ++d) { + drive = &hwif->drives[d]; + if (drive->present) { + if (drive->drive_data == PIO_DONT_KNOW) + drive->drive_data = ide_get_best_pio_mode(drive, 255, OPTI621_MAX_PIO, NULL); +#ifdef OPTI621_DEBUG + printk("%s: Selected PIO mode %d\n", drive->name, drive->drive_data); +#endif + } else { + drive->drive_data = PIO_NOT_EXIST; + } + } +} + +int cmpt_clk(int time, int bus_speed) +/* Returns (rounded up) time in clocks for time in ns, + * with bus_speed in MHz. + * Example: bus_speed = 40 MHz, time = 80 ns + * 1000/40 = 25 ns (clk value), + * 80/25 = 3.2, rounded up to 4 (I hope ;-)). + * Use idebus=xx to select right frequency. + */ +{ + return ((time*bus_speed+999)/1000); +} + +static void write_reg(byte value, int reg) +/* Write value to register reg, base of register + * is at reg_base (0x1f0 primary, 0x170 secondary, + * if not changed by PCI configuration). + * This is from setupvic.exe program. + */ +{ + IN_WORD(reg_base+1); + IN_WORD(reg_base+1); + OUT_BYTE(3, reg_base+2); + OUT_BYTE(value, reg_base+reg); + OUT_BYTE(0x83, reg_base+2); +} + +static byte read_reg(int reg) +/* Read value from register reg, base of register + * is at reg_base (0x1f0 primary, 0x170 secondary, + * if not changed by PCI configuration). + * This is from setupvic.exe program. + */ +{ + byte ret; + IN_WORD(reg_base+1); + IN_WORD(reg_base+1); + OUT_BYTE(3, reg_base+2); + ret=IN_BYTE(reg_base+reg); + OUT_BYTE(0x83, reg_base+2); + return ret; +} + +typedef struct pio_clocks_s { + int address_time; /* Address setup (clocks) */ + int data_time; /* Active/data pulse (clocks) */ + int recovery_time; /* Recovery time (clocks) */ +} pio_clocks_t; + +static void compute_clocks(int pio, pio_clocks_t *clks) +{ + if (pio != PIO_NOT_EXIST) { + int adr_setup, data_pls; + int bus_speed = system_bus_clock(); + + adr_setup = ide_pio_timings[pio].setup_time; + data_pls = ide_pio_timings[pio].active_time; + clks->address_time = cmpt_clk(adr_setup, bus_speed); + clks->data_time = cmpt_clk(data_pls, bus_speed); + clks->recovery_time = cmpt_clk(ide_pio_timings[pio].cycle_time + - adr_setup-data_pls, bus_speed); + if (clks->address_time<1) clks->address_time = 1; + if (clks->address_time>4) clks->address_time = 4; + if (clks->data_time<1) clks->data_time = 1; + if (clks->data_time>16) clks->data_time = 16; + if (clks->recovery_time<2) clks->recovery_time = 2; + if (clks->recovery_time>17) clks->recovery_time = 17; + } else { + clks->address_time = 1; + clks->data_time = 1; + clks->recovery_time = 2; + /* minimal values */ + } + +} + +/* Main tune procedure, called from tuneproc. */ +static void opti621_tune_drive (ide_drive_t *drive, byte pio) +{ + /* primary and secondary drives share some registers, + * so we have to program both drives + */ + unsigned long flags; + byte pio1, pio2; + pio_clocks_t first, second; + int ax, drdy; + byte cycle1, cycle2, misc; + ide_hwif_t *hwif = HWIF(drive); + + /* sets drive->drive_data for both drives */ + compute_pios(drive, pio); + pio1 = hwif->drives[0].drive_data; + pio2 = hwif->drives[1].drive_data; + + compute_clocks(pio1, &first); + compute_clocks(pio2, &second); + + /* ax = max(a1,a2) */ + ax = (first.address_time < second.address_time) ? second.address_time : first.address_time; + + drdy = 2; /* DRDY is default 2 (by OPTi Databook) */ + + cycle1 = ((first.data_time-1)<<4) | (first.recovery_time-2); + cycle2 = ((second.data_time-1)<<4) | (second.recovery_time-2); + misc = READ_PREFETCH | ((ax-1)<<4) | ((drdy-2)<<1); + +#ifdef OPTI621_DEBUG + printk("%s: master: address: %d, data: %d, recovery: %d, drdy: %d [clk]\n", + hwif->name, ax, first.data_time, first.recovery_time, drdy); + printk("%s: slave: address: %d, data: %d, recovery: %d, drdy: %d [clk]\n", + hwif->name, ax, second.data_time, second.recovery_time, drdy); +#endif + + spin_lock_irqsave(&ide_lock, flags); + + reg_base = hwif->io_ports[IDE_DATA_OFFSET]; + OUT_BYTE(0xc0, reg_base+CNTRL_REG); /* allow Register-B */ + OUT_BYTE(0xff, reg_base+5); /* hmm, setupvic.exe does this ;-) */ + IN_BYTE(reg_base+CNTRL_REG); /* if reads 0xff, adapter not exist? */ + read_reg(CNTRL_REG); /* if reads 0xc0, no interface exist? */ + read_reg(STRAP_REG); /* read version, probably 0 */ + + /* program primary drive */ + write_reg(0, MISC_REG); /* select Index-0 for Register-A */ + write_reg(cycle1, READ_REG); /* set read cycle timings */ + write_reg(cycle1, WRITE_REG); /* set write cycle timings */ + + /* program secondary drive */ + write_reg(1, MISC_REG); /* select Index-1 for Register-B */ + write_reg(cycle2, READ_REG); /* set read cycle timings */ + write_reg(cycle2, WRITE_REG); /* set write cycle timings */ + + write_reg(0x85, CNTRL_REG); /* use Register-A for drive 0 */ + /* use Register-B for drive 1 */ + + write_reg(misc, MISC_REG); /* set address setup, DRDY timings, */ + /* and read prefetch for both drives */ + + spin_unlock_irqrestore(&ide_lock, flags); +} + +/* + * ide_init_opti621() is called once for each hwif found at boot. + */ +void __init ide_init_opti621 (ide_hwif_t *hwif) +{ + hwif->autodma = 0; + hwif->drives[0].drive_data = PIO_DONT_KNOW; + hwif->drives[1].drive_data = PIO_DONT_KNOW; + hwif->tuneproc = &opti621_tune_drive; + + /* safety call for Anton A */ + hwif->dma_base = 0; +} + +extern void ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d); + +void __init fixup_device_opti621 (struct pci_dev *dev, ide_pci_device_t *d) +{ +#if 0 + if (IDE_PCI_DEVID_EQ(d->devid, DEVID_OPTI621V) && + !(PCI_FUNC(dev->devfn) & 1)) +#else + if ((dev->device == PCI_DEVICE_ID_OPTI_82C558) && + (!(PCI_FUNC(dev->devfn) & 1))) +#endif + return; /* OPTI621 is more than only a IDE controller */ + + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); +} + diff -Nru a/drivers/ide/pdc202xx.c b/drivers/ide/pdc202xx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/pdc202xx.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,1430 @@ +/* + * linux/drivers/ide/pdc202xx.c Version 0.35 Mar. 30, 2002 + * + * Copyright (C) 1998-2002 Andre Hedrick + * + * Promise Ultra33 cards with BIOS v1.20 through 1.28 will need this + * compiled into the kernel if you have more than one card installed. + * Note that BIOS v1.29 is reported to fix the problem. Since this is + * safe chipset tuning, including this support is harmless + * + * Promise Ultra66 cards with BIOS v1.11 this + * compiled into the kernel if you have more than one card installed. + * + * Promise Ultra100 cards. + * + * The latest chipset code will support the following :: + * Three Ultra33 controllers and 12 drives. + * 8 are UDMA supported and 4 are limited to DMA mode 2 multi-word. + * The 8/4 ratio is a BIOS code limit by promise. + * + * UNLESS you enable "CONFIG_PDC202XX_BURST" + * + */ + +/* + * Portions Copyright (C) 1999 Promise Technology, Inc. + * Author: Frank Tiernan (frankt@promise.com) + * Released under terms of General Public License + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ide_modes.h" + +#define PDC202XX_DEBUG_DRIVE_INFO 0 +#define PDC202XX_DECODE_REGISTER_INFO 0 + +#define DISPLAY_PDC202XX_TIMINGS + +#ifndef SPLIT_BYTE +#define SPLIT_BYTE(B,H,L) ((H)=(B>>4), (L)=(B-((B>>4)<<4))) +#endif + +#if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +static int pdc202xx_get_info(char *, char **, off_t, int); +extern int (*pdc202xx_display_info)(char *, char **, off_t, int); /* ide-proc.c */ + +byte pdc202xx_proc = 0; + +#define PDC202_MAX_DEVS 5 + +static struct pci_dev *pdc202_devs[PDC202_MAX_DEVS]; +static int n_pdc202_devs; + +const char *pdc_quirk_drives[] = { + "QUANTUM FIREBALLlct08 08", + "QUANTUM FIREBALLP KA6.4", + "QUANTUM FIREBALLP KA9.1", + "QUANTUM FIREBALLP LM20.4", + "QUANTUM FIREBALLP KX20.5", + "QUANTUM FIREBALLP KX27.3", + "QUANTUM FIREBALLP LM20.5", + NULL +}; + +char *pdc202xx_pio_verbose (u32 drive_pci) +{ + if ((drive_pci & 0x000ff000) == 0x000ff000) return("NOTSET"); + if ((drive_pci & 0x00000401) == 0x00000401) return("PIO 4"); + if ((drive_pci & 0x00000602) == 0x00000602) return("PIO 3"); + if ((drive_pci & 0x00000803) == 0x00000803) return("PIO 2"); + if ((drive_pci & 0x00000C05) == 0x00000C05) return("PIO 1"); + if ((drive_pci & 0x00001309) == 0x00001309) return("PIO 0"); + return("PIO ?"); +} + +char *pdc202xx_dma_verbose (u32 drive_pci) +{ + if ((drive_pci & 0x00036000) == 0x00036000) return("MWDMA 2"); + if ((drive_pci & 0x00046000) == 0x00046000) return("MWDMA 1"); + if ((drive_pci & 0x00056000) == 0x00056000) return("MWDMA 0"); + if ((drive_pci & 0x00056000) == 0x00056000) return("SWDMA 2"); + if ((drive_pci & 0x00068000) == 0x00068000) return("SWDMA 1"); + if ((drive_pci & 0x000BC000) == 0x000BC000) return("SWDMA 0"); + return("PIO---"); +} + +char *pdc202xx_ultra_verbose (u32 drive_pci, u16 slow_cable) +{ + if ((drive_pci & 0x000ff000) == 0x000ff000) + return("NOTSET"); + if ((drive_pci & 0x00012000) == 0x00012000) + return((slow_cable) ? "UDMA 2" : "UDMA 4"); + if ((drive_pci & 0x00024000) == 0x00024000) + return((slow_cable) ? "UDMA 1" : "UDMA 3"); + if ((drive_pci & 0x00036000) == 0x00036000) + return("UDMA 0"); + return(pdc202xx_dma_verbose(drive_pci)); +} + +static char * pdc202xx_info (char *buf, struct pci_dev *dev) +{ + char *p = buf; + + u32 bibma = pci_resource_start(dev, 4); + u32 reg60h = 0, reg64h = 0, reg68h = 0, reg6ch = 0; + u16 reg50h = 0, pmask = (1<<10), smask = (1<<11); + u8 hi = 0, lo = 0; + + /* + * at that point bibma+0x2 et bibma+0xa are byte registers + * to investigate: + */ + u8 c0 = inb_p((unsigned short)bibma + 0x02); + u8 c1 = inb_p((unsigned short)bibma + 0x0a); + + u8 sc11 = inb_p((unsigned short)bibma + 0x11); + u8 sc1a = inb_p((unsigned short)bibma + 0x1a); + u8 sc1b = inb_p((unsigned short)bibma + 0x1b); + u8 sc1c = inb_p((unsigned short)bibma + 0x1c); + u8 sc1d = inb_p((unsigned short)bibma + 0x1d); + u8 sc1e = inb_p((unsigned short)bibma + 0x1e); + u8 sc1f = inb_p((unsigned short)bibma + 0x1f); + + pci_read_config_word(dev, 0x50, ®50h); + pci_read_config_dword(dev, 0x60, ®60h); + pci_read_config_dword(dev, 0x64, ®64h); + pci_read_config_dword(dev, 0x68, ®68h); + pci_read_config_dword(dev, 0x6c, ®6ch); + + p += sprintf(p, "\n "); + switch(dev->device) { + case PCI_DEVICE_ID_PROMISE_20267: + p += sprintf(p, "PDC20267"); break; + case PCI_DEVICE_ID_PROMISE_20265: + p += sprintf(p, "PDC20265"); break; + case PCI_DEVICE_ID_PROMISE_20263: + p += sprintf(p, "PDC20263"); break; + case PCI_DEVICE_ID_PROMISE_20262: + p += sprintf(p, "PDC20262"); break; + case PCI_DEVICE_ID_PROMISE_20246: + p += sprintf(p, "PDC20246"); + reg50h |= 0x0c00; + break; + default: + p += sprintf(p, "PDC202XX"); break; + } + p += sprintf(p, " Chipset.\n"); + + p += sprintf(p, "------------------------------- General Status " + "---------------------------------\n"); + p += sprintf(p, "Burst Mode : %sabled\n", + (sc1f & 0x01) ? "en" : "dis"); + p += sprintf(p, "Host Mode : %s\n", + (sc1f & 0x08) ? "Tri-Stated" : "Normal"); + p += sprintf(p, "Bus Clocking : %s\n", + ((sc1f & 0xC0) == 0xC0) ? "100 External" : + ((sc1f & 0x80) == 0x80) ? "66 External" : + ((sc1f & 0x40) == 0x40) ? "33 External" : "33 PCI Internal"); + p += sprintf(p, "IO pad select : %s mA\n", + ((sc1c & 0x03) == 0x03) ? "10" : + ((sc1c & 0x02) == 0x02) ? "8" : + ((sc1c & 0x01) == 0x01) ? "6" : + ((sc1c & 0x00) == 0x00) ? "4" : "??"); + SPLIT_BYTE(sc1e, hi, lo); + p += sprintf(p, "Status Polling Period : %d\n", hi); + p += sprintf(p, "Interrupt Check Status Polling Delay : %d\n", lo); + p += sprintf(p, "--------------- Primary Channel " + "---------------- Secondary Channel " + "-------------\n"); + p += sprintf(p, " %s %s\n", + (c0&0x80)?"disabled":"enabled ", + (c1&0x80)?"disabled":"enabled "); + p += sprintf(p, "66 Clocking %s %s\n", + (sc11&0x02)?"enabled ":"disabled", + (sc11&0x08)?"enabled ":"disabled"); + p += sprintf(p, " Mode %s Mode %s\n", + (sc1a & 0x01) ? "MASTER" : "PCI ", + (sc1b & 0x01) ? "MASTER" : "PCI "); + p += sprintf(p, " %s %s\n", + (sc1d & 0x08) ? "Error " : + ((sc1d & 0x05) == 0x05) ? "Not My INTR " : + (sc1d & 0x04) ? "Interrupting" : + (sc1d & 0x02) ? "FIFO Full " : + (sc1d & 0x01) ? "FIFO Empty " : "????????????", + (sc1d & 0x80) ? "Error " : + ((sc1d & 0x50) == 0x50) ? "Not My INTR " : + (sc1d & 0x40) ? "Interrupting" : + (sc1d & 0x20) ? "FIFO Full " : + (sc1d & 0x10) ? "FIFO Empty " : "????????????"); + p += sprintf(p, "--------------- drive0 --------- drive1 " + "-------- drive0 ---------- drive1 ------\n"); + p += sprintf(p, "DMA enabled: %s %s " + " %s %s\n", + (c0&0x20)?"yes":"no ", (c0&0x40)?"yes":"no ", + (c1&0x20)?"yes":"no ", (c1&0x40)?"yes":"no "); + p += sprintf(p, "DMA Mode: %s %s " + " %s %s\n", + pdc202xx_ultra_verbose(reg60h, (reg50h & pmask)), + pdc202xx_ultra_verbose(reg64h, (reg50h & pmask)), + pdc202xx_ultra_verbose(reg68h, (reg50h & smask)), + pdc202xx_ultra_verbose(reg6ch, (reg50h & smask))); + p += sprintf(p, "PIO Mode: %s %s " + " %s %s\n", + pdc202xx_pio_verbose(reg60h), + pdc202xx_pio_verbose(reg64h), + pdc202xx_pio_verbose(reg68h), + pdc202xx_pio_verbose(reg6ch)); +#if 0 + p += sprintf(p, "--------------- Can ATAPI DMA ---------------\n"); +#endif + return (char *)p; +} + +static char * pdc202xx_info_new (char *buf, struct pci_dev *dev) +{ + char *p = buf; +// u32 bibma = pci_resource_start(dev, 4); + +// u32 reg60h = 0, reg64h = 0, reg68h = 0, reg6ch = 0; +// u16 reg50h = 0, word88 = 0; +// int udmasel[4]={0,0,0,0}, piosel[4]={0,0,0,0}, i=0, hd=0; + + p += sprintf(p, "\n "); + switch(dev->device) { + case PCI_DEVICE_ID_PROMISE_20277: + p += sprintf(p, "PDC20277"); + case PCI_DEVICE_ID_PROMISE_20276: + p += sprintf(p, "PDC20276"); break; + case PCI_DEVICE_ID_PROMISE_20275: + p += sprintf(p, "PDC20275"); break; + case PCI_DEVICE_ID_PROMISE_20271: + p += sprintf(p, "PDC20271"); break; + case PCI_DEVICE_ID_PROMISE_20269: + p += sprintf(p, "PDC20269 TX2"); break; + case PCI_DEVICE_ID_PROMISE_20268: + p += sprintf(p, "PDC20268 TX2"); break; + case PCI_DEVICE_ID_PROMISE_20268R: + p += sprintf(p, "PDC20270 TX2/4"); break; + default: + p += sprintf(p, "PDC202XX"); break; + break; + } + p += sprintf(p, " Chipset.\n"); + return (char *)p; +} + +static int pdc202xx_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + int i; + + for (i = 0; i < n_pdc202_devs; i++) { + struct pci_dev *dev = pdc202_devs[i]; + + switch(dev->device) { + case PCI_DEVICE_ID_PROMISE_20277: + case PCI_DEVICE_ID_PROMISE_20276: + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20271: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20268: + case PCI_DEVICE_ID_PROMISE_20268R: + p = pdc202xx_info_new(buffer, dev); + break; + default: + p = pdc202xx_info(buffer, dev); + break; + } + } + return p-buffer; /* => must be less than 4k! */ +} +#endif /* defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS) */ + +/* A Register */ +#define SYNC_ERRDY_EN 0xC0 + +#define SYNC_IN 0x80 /* control bit, different for master vs. slave drives */ +#define ERRDY_EN 0x40 /* control bit, different for master vs. slave drives */ +#define IORDY_EN 0x20 /* PIO: IOREADY */ +#define PREFETCH_EN 0x10 /* PIO: PREFETCH */ + +#define PA3 0x08 /* PIO"A" timing */ +#define PA2 0x04 /* PIO"A" timing */ +#define PA1 0x02 /* PIO"A" timing */ +#define PA0 0x01 /* PIO"A" timing */ + +/* B Register */ + +#define MB2 0x80 /* DMA"B" timing */ +#define MB1 0x40 /* DMA"B" timing */ +#define MB0 0x20 /* DMA"B" timing */ + +#define PB4 0x10 /* PIO_FORCE 1:0 */ + +#define PB3 0x08 /* PIO"B" timing */ /* PIO flow Control mode */ +#define PB2 0x04 /* PIO"B" timing */ /* PIO 4 */ +#define PB1 0x02 /* PIO"B" timing */ /* PIO 3 half */ +#define PB0 0x01 /* PIO"B" timing */ /* PIO 3 other half */ + +/* C Register */ +#define IORDYp_NO_SPEED 0x4F +#define SPEED_DIS 0x0F + +#define DMARQp 0x80 +#define IORDYp 0x40 +#define DMAR_EN 0x20 +#define DMAW_EN 0x10 + +#define MC3 0x08 /* DMA"C" timing */ +#define MC2 0x04 /* DMA"C" timing */ +#define MC1 0x02 /* DMA"C" timing */ +#define MC0 0x01 /* DMA"C" timing */ + +#if PDC202XX_DECODE_REGISTER_INFO + +#define REG_A 0x01 +#define REG_B 0x02 +#define REG_C 0x04 +#define REG_D 0x08 + +static void decode_registers (byte registers, byte value) +{ + byte bit = 0, bit1 = 0, bit2 = 0; + + switch(registers) { + case REG_A: + bit2 = 0; + printk("A Register "); + if (value & 0x80) printk("SYNC_IN "); + if (value & 0x40) printk("ERRDY_EN "); + if (value & 0x20) printk("IORDY_EN "); + if (value & 0x10) printk("PREFETCH_EN "); + if (value & 0x08) { printk("PA3 ");bit2 |= 0x08; } + if (value & 0x04) { printk("PA2 ");bit2 |= 0x04; } + if (value & 0x02) { printk("PA1 ");bit2 |= 0x02; } + if (value & 0x01) { printk("PA0 ");bit2 |= 0x01; } + printk("PIO(A) = %d ", bit2); + break; + case REG_B: + bit1 = 0;bit2 = 0; + printk("B Register "); + if (value & 0x80) { printk("MB2 ");bit1 |= 0x80; } + if (value & 0x40) { printk("MB1 ");bit1 |= 0x40; } + if (value & 0x20) { printk("MB0 ");bit1 |= 0x20; } + printk("DMA(B) = %d ", bit1 >> 5); + if (value & 0x10) printk("PIO_FORCED/PB4 "); + if (value & 0x08) { printk("PB3 ");bit2 |= 0x08; } + if (value & 0x04) { printk("PB2 ");bit2 |= 0x04; } + if (value & 0x02) { printk("PB1 ");bit2 |= 0x02; } + if (value & 0x01) { printk("PB0 ");bit2 |= 0x01; } + printk("PIO(B) = %d ", bit2); + break; + case REG_C: + bit2 = 0; + printk("C Register "); + if (value & 0x80) printk("DMARQp "); + if (value & 0x40) printk("IORDYp "); + if (value & 0x20) printk("DMAR_EN "); + if (value & 0x10) printk("DMAW_EN "); + + if (value & 0x08) { printk("MC3 ");bit2 |= 0x08; } + if (value & 0x04) { printk("MC2 ");bit2 |= 0x04; } + if (value & 0x02) { printk("MC1 ");bit2 |= 0x02; } + if (value & 0x01) { printk("MC0 ");bit2 |= 0x01; } + printk("DMA(C) = %d ", bit2); + break; + case REG_D: + printk("D Register "); + break; + default: + return; + } + printk("\n %s ", (registers & REG_D) ? "DP" : + (registers & REG_C) ? "CP" : + (registers & REG_B) ? "BP" : + (registers & REG_A) ? "AP" : "ERROR"); + for (bit=128;bit>0;bit/=2) + printk("%s", (value & bit) ? "1" : "0"); + printk("\n"); +} + +#endif /* PDC202XX_DECODE_REGISTER_INFO */ + +#if 0 +static byte pdc202xx_ratemask (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + byte mode = 0x00; + + switch(dev->device) { + case PCI_DEVICE_ID_PROMISE_20277: + case PCI_DEVICE_ID_PROMISE_20276: + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20271: + case PCI_DEVICE_ID_PROMISE_20269: + { mode |= 0x04; break; } + case PCI_DEVICE_ID_PROMISE_20268R: + case PCI_DEVICE_ID_PROMISE_20268: + { mode |= 0x03; break; } + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20265: + { mode |= 0x03; break; } + case PCI_DEVICE_ID_PROMISE_20263: + case PCI_DEVICE_ID_PROMISE_20262: + { mode |= 0x02; break; } + case PCI_DEVICE_ID_PROMISE_20246: + { mode |= 0x01; break; } + default: + return (mode &= ~0xF8); + } + + if (!eighty_ninty_three(drive)) { + mode &= ~0xFE; + mode |= 0x01; + } + return (mode &= ~0xF8); +} + +static byte pdc202xx_ratefilter (ide_drive_t *drive, byte speed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + byte mode = pdc202xx_ratemask(drive); + + switch(mode) { + case 0x04: while (speed > XFER_UDMA_6) speed--; break; + case 0x03: while (speed > XFER_UDMA_5) speed--; break; + case 0x02: while (speed > XFER_UDMA_4) speed--; break; + case 0x01: while (speed > XFER_UDMA_2) speed--; break; + case 0x00: + default: while (speed > XFER_MW_DMA_2) speed--; break; + break; + } +#else + while (speed > XFER_PIO_4) speed--; +#endif /* CONFIG_BLK_DEV_IDEDMA */ +// printk("%s: mode == %02x speed == %02x\n", drive->name, mode, speed); + return speed; +} + +#else +static byte pdc202xx_ratefilter (ide_drive_t *drive, byte speed) +{ + return speed; +} +#endif + +static int check_in_drive_lists (ide_drive_t *drive, const char **list) +{ + struct hd_driveid *id = drive->id; + + if (pdc_quirk_drives == list) { + while (*list) { + if (strstr(id->model, *list++)) { + return 2; + } + } + } else { + while (*list) { + if (!strcmp(*list++,id->model)) { + return 1; + } + } + } + return 0; +} + +static int pdc202xx_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte speed = pdc202xx_ratefilter(drive, xferspeed); + + unsigned int drive_conf; + byte drive_pci, AP, BP, CP, DP; + byte TA = 0, TB = 0, TC = 0; + + switch (drive->dn) { + case 0: drive_pci = 0x60; break; + case 1: drive_pci = 0x64; break; + case 2: drive_pci = 0x68; break; + case 3: drive_pci = 0x6c; break; + default: return -1; + } + + if ((drive->media != ide_disk) && (speed < XFER_SW_DMA_0)) + return -1; + + pci_read_config_dword(dev, drive_pci, &drive_conf); + pci_read_config_byte(dev, (drive_pci), &AP); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + pci_read_config_byte(dev, (drive_pci)|0x02, &CP); + pci_read_config_byte(dev, (drive_pci)|0x03, &DP); + + if (speed < XFER_SW_DMA_0) { + if ((AP & 0x0F) || (BP & 0x07)) { + /* clear PIO modes of lower 8421 bits of A Register */ + pci_write_config_byte(dev, (drive_pci), AP &~0x0F); + pci_read_config_byte(dev, (drive_pci), &AP); + + /* clear PIO modes of lower 421 bits of B Register */ + pci_write_config_byte(dev, (drive_pci)|0x01, BP &~0x07); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + + pci_read_config_byte(dev, (drive_pci), &AP); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + } +#ifdef CONFIG_BLK_DEV_IDEDMA + } else { + if ((BP & 0xF0) && (CP & 0x0F)) { + /* clear DMA modes of upper 842 bits of B Register */ + /* clear PIO forced mode upper 1 bit of B Register */ + pci_write_config_byte(dev, (drive_pci)|0x01, BP &~0xF0); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + + /* clear DMA modes of lower 8421 bits of C Register */ + pci_write_config_byte(dev, (drive_pci)|0x02, CP &~0x0F); + pci_read_config_byte(dev, (drive_pci)|0x02, &CP); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + } + + pci_read_config_byte(dev, (drive_pci), &AP); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + pci_read_config_byte(dev, (drive_pci)|0x02, &CP); + + switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA + /* case XFER_UDMA_6: */ + case XFER_UDMA_5: + case XFER_UDMA_4: TB = 0x20; TC = 0x01; break; + case XFER_UDMA_3: TB = 0x40; TC = 0x02; break; + case XFER_UDMA_2: TB = 0x20; TC = 0x01; break; + case XFER_UDMA_1: TB = 0x40; TC = 0x02; break; + case XFER_UDMA_0: TB = 0x60; TC = 0x03; break; + case XFER_MW_DMA_2: TB = 0x60; TC = 0x03; break; + case XFER_MW_DMA_1: TB = 0x60; TC = 0x04; break; + case XFER_MW_DMA_0: TB = 0x60; TC = 0x05; break; + case XFER_SW_DMA_2: TB = 0x60; TC = 0x05; break; + case XFER_SW_DMA_1: TB = 0x80; TC = 0x06; break; + case XFER_SW_DMA_0: TB = 0xC0; TC = 0x0B; break; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: TA = 0x01; TB = 0x04; break; + case XFER_PIO_3: TA = 0x02; TB = 0x06; break; + case XFER_PIO_2: TA = 0x03; TB = 0x08; break; + case XFER_PIO_1: TA = 0x05; TB = 0x0C; break; + case XFER_PIO_0: + default: TA = 0x09; TB = 0x13; break; + } + + if (speed < XFER_SW_DMA_0) { + pci_write_config_byte(dev, (drive_pci), AP|TA); + pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB); +#ifdef CONFIG_BLK_DEV_IDEDMA + } else { + pci_write_config_byte(dev, (drive_pci)|0x01, BP|TB); + pci_write_config_byte(dev, (drive_pci)|0x02, CP|TC); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + } + +#if PDC202XX_DECODE_REGISTER_INFO + pci_read_config_byte(dev, (drive_pci), &AP); + pci_read_config_byte(dev, (drive_pci)|0x01, &BP); + pci_read_config_byte(dev, (drive_pci)|0x02, &CP); + pci_read_config_byte(dev, (drive_pci)|0x03, &DP); + + decode_registers(REG_A, AP); + decode_registers(REG_B, BP); + decode_registers(REG_C, CP); + decode_registers(REG_D, DP); +#endif /* PDC202XX_DECODE_REGISTER_INFO */ +#if PDC202XX_DEBUG_DRIVE_INFO + printk("%s: %s drive%d 0x%08x ", + drive->name, ide_xfer_verbose(speed), + drive->dn, drive_conf); + pci_read_config_dword(dev, drive_pci, &drive_conf); + printk("0x%08x\n", drive_conf); +#endif /* PDC202XX_DEBUG_DRIVE_INFO */ + + return (ide_config_drive_speed(drive, speed)); +} + +static int pdc202xx_new_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); +#ifdef CONFIG_BLK_DEV_IDEDMA + unsigned long indexreg = (hwif->dma_base + 1); + unsigned long datareg = (hwif->dma_base + 3); +#else + struct pci_dev *dev = hwif->pci_dev; + unsigned long high_16 = pci_resource_start(dev, 4); + unsigned long indexreg = high_16 + (hwif->channel ? 0x09 : 0x01); + unsigned long datareg = (indexreg + 2); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + byte thold = 0x10; + byte adj = (drive->dn%2) ? 0x08 : 0x00; + byte speed = pdc202xx_ratefilter(drive, xferspeed); + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (speed == XFER_UDMA_2) { + OUT_BYTE((thold + adj), indexreg); + OUT_BYTE((IN_BYTE(datareg) & 0x7f), datareg); + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + switch (speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA + case XFER_UDMA_7: + speed = XFER_UDMA_6; + case XFER_UDMA_6: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x1a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x01, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xcb, datareg); + break; + case XFER_UDMA_5: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x1a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x02, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xcb, datareg); + break; + case XFER_UDMA_4: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x1a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x03, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xcd, datareg); + break; + case XFER_UDMA_3: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x1a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x05, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xcd, datareg); + break; + case XFER_UDMA_2: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x2a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x07, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xcd, datareg); + break; + case XFER_UDMA_1: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x3a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x0a, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xd0, datareg); + break; + case XFER_UDMA_0: + OUT_BYTE((0x10 + adj), indexreg); + OUT_BYTE(0x4a, datareg); + OUT_BYTE((0x11 + adj), indexreg); + OUT_BYTE(0x0f, datareg); + OUT_BYTE((0x12 + adj), indexreg); + OUT_BYTE(0xd5, datareg); + break; + case XFER_MW_DMA_2: + OUT_BYTE((0x0e + adj), indexreg); + OUT_BYTE(0x69, datareg); + OUT_BYTE((0x0f + adj), indexreg); + OUT_BYTE(0x25, datareg); + break; + case XFER_MW_DMA_1: + OUT_BYTE((0x0e + adj), indexreg); + OUT_BYTE(0x6b, datareg); + OUT_BYTE((0x0f+ adj), indexreg); + OUT_BYTE(0x27, datareg); + break; + case XFER_MW_DMA_0: + OUT_BYTE((0x0e + adj), indexreg); + OUT_BYTE(0xdf, datareg); + OUT_BYTE((0x0f + adj), indexreg); + OUT_BYTE(0x5f, datareg); + break; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: + OUT_BYTE((0x0c + adj), indexreg); + OUT_BYTE(0x23, datareg); + OUT_BYTE((0x0d + adj), indexreg); + OUT_BYTE(0x09, datareg); + OUT_BYTE((0x13 + adj), indexreg); + OUT_BYTE(0x25, datareg); + break; + case XFER_PIO_3: + OUT_BYTE((0x0c + adj), indexreg); + OUT_BYTE(0x27, datareg); + OUT_BYTE((0x0d + adj), indexreg); + OUT_BYTE(0x0d, datareg); + OUT_BYTE((0x13 + adj), indexreg); + OUT_BYTE(0x35, datareg); + break; + case XFER_PIO_2: + OUT_BYTE((0x0c + adj), indexreg); + OUT_BYTE(0x23, datareg); + OUT_BYTE((0x0d + adj), indexreg); + OUT_BYTE(0x26, datareg); + OUT_BYTE((0x13 + adj), indexreg); + OUT_BYTE(0x64, datareg); + break; + case XFER_PIO_1: + OUT_BYTE((0x0c + adj), indexreg); + OUT_BYTE(0x46, datareg); + OUT_BYTE((0x0d + adj), indexreg); + OUT_BYTE(0x29, datareg); + OUT_BYTE((0x13 + adj), indexreg); + OUT_BYTE(0xa4, datareg); + break; + case XFER_PIO_0: + OUT_BYTE((0x0c + adj), indexreg); + OUT_BYTE(0xfb, datareg); + OUT_BYTE((0x0d + adj), indexreg); + OUT_BYTE(0x2b, datareg); + OUT_BYTE((0x13 + adj), indexreg); + OUT_BYTE(0xac, datareg); + break; + default: + break; + } + + return (ide_config_drive_speed(drive, speed)); +} + +/* 0 1 2 3 4 5 6 7 8 + * 960, 480, 390, 300, 240, 180, 120, 90, 60 + * 180, 150, 120, 90, 60 + * DMA_Speed + * 180, 120, 90, 90, 90, 60, 30 + * 11, 5, 4, 3, 2, 1, 0 + */ +static int config_chipset_for_pio (ide_drive_t *drive, byte pio) +{ + byte speed = 0x00; + + pio = (pio == 5) ? 4 : pio; + speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, pio, NULL); + + return ((int) pdc202xx_tune_chipset(drive, speed)); +} + +static void pdc202xx_tune_drive (ide_drive_t *drive, byte pio) +{ + (void) config_chipset_for_pio(drive, pio); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static int config_chipset_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; +// byte mode = pdc202xx_ratemask(drive); + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + unsigned long high_16 = pci_resource_start(dev, 4); + unsigned long dma_base = hwif->dma_base; + unsigned long indexreg = dma_base + 1; + unsigned long datareg = dma_base + 3; + byte iordy = 0x13; + byte adj = (drive->dn%2) ? 0x08 : 0x00; + byte cable = 0; + byte jumpbit = 0; + unsigned int drive_conf; + byte drive_pci = 0; + byte test1, test2, speed = -1; + byte AP; + unsigned short EP; + byte CLKSPD = 0; + byte udma_33 = 1; + byte udma_66 = (eighty_ninty_three(drive)) ? 1 : 0; + byte udma_100 = 0; + byte udma_133 = 0; + byte mask = hwif->channel ? 0x08 : 0x02; + unsigned short c_mask = hwif->channel ? (1<<11) : (1<<10); + + byte ultra_66 = ((id->dma_ultra & 0x0010) || + (id->dma_ultra & 0x0008)) ? 1 : 0; + + switch(dev->device) { + case PCI_DEVICE_ID_PROMISE_20277: + case PCI_DEVICE_ID_PROMISE_20276: + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20271: + case PCI_DEVICE_ID_PROMISE_20269: + udma_133 = (udma_66) ? 1 : 0; + udma_100 = (udma_66) ? 1 : 0; + OUT_BYTE(0x0b, (hwif->dma_base + 1)); + cable = ((IN_BYTE((hwif->dma_base + 3)) & 0x04)); + jumpbit = 1; + break; + case PCI_DEVICE_ID_PROMISE_20268R: + udma_100 = 1; + udma_66 = 1; + OUT_BYTE(0x0b, (hwif->dma_base + 1)); + cable = ((IN_BYTE((hwif->dma_base + 3)) & 0x04)); + jumpbit = 1; + break; + case PCI_DEVICE_ID_PROMISE_20268: + udma_100 = (udma_66) ? 1 : 0; + OUT_BYTE(0x0b, (hwif->dma_base + 1)); + cable = ((IN_BYTE((hwif->dma_base + 3)) & 0x04)); + jumpbit = 1; + break; + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20265: + udma_100 = (udma_66) ? 1 : 0; + pci_read_config_word(dev, 0x50, &EP); + cable = (EP & c_mask); + jumpbit = 0; + break; + case PCI_DEVICE_ID_PROMISE_20263: + case PCI_DEVICE_ID_PROMISE_20262: + pci_read_config_word(dev, 0x50, &EP); + cable = (EP & c_mask); + jumpbit = 0; + break; + default: + udma_100 = 0; udma_133 = 0; cable = 1; jumpbit = 0; + break; + } + + if (!jumpbit) + CLKSPD = IN_BYTE(high_16 + 0x11); + /* + * Set the control register to use the 66Mhz system + * clock for UDMA 3/4 mode operation. If one drive on + * a channel is U66 capable but the other isn't we + * fall back to U33 mode. The BIOS INT 13 hooks turn + * the clock on then off for each read/write issued. I don't + * do that here because it would require modifying the + * kernel, seperating the fop routines from the kernel or + * somehow hooking the fops calls. It may also be possible to + * leave the 66Mhz clock on and readjust the timing + * parameters. + */ + + if ((ultra_66) && (cable)) { +#ifdef DEBUG + printk("ULTRA 66/100/133: %s channel of Ultra 66/100/133 " + "requires an 80-pin cable for Ultra66 operation.\n", + hwif->channel ? "Secondary" : "Primary"); + printk(" Switching to Ultra33 mode.\n"); +#endif /* DEBUG */ + /* Primary : zero out second bit */ + /* Secondary : zero out fourth bit */ + if (!jumpbit) + OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11)); + printk("Warning: %s channel requires an 80-pin cable for operation.\n", hwif->channel ? "Secondary":"Primary"); + printk("%s reduced to Ultra33 mode.\n", drive->name); + udma_66 = 0; + } else { + if (ultra_66) { + /* + * check to make sure drive on same channel + * is u66 capable + */ + if (hwif->drives[!(drive->dn%2)].id) { + if (hwif->drives[!(drive->dn%2)].id->dma_ultra & 0x0078) { + if (!jumpbit) + OUT_BYTE(CLKSPD | mask, (high_16 + 0x11)); + } else { + if (!jumpbit) + OUT_BYTE(CLKSPD & ~mask, (high_16 + 0x11)); + } + } else { /* udma4 drive by itself */ + if (!jumpbit) + OUT_BYTE(CLKSPD | mask, (high_16 + 0x11)); + } + } + } + + if (jumpbit) { + if (drive->media != ide_disk) return ide_dma_off_quietly; + if (id->capability & 4) { /* IORDY_EN & PREFETCH_EN */ + OUT_BYTE((iordy + adj), indexreg); + OUT_BYTE((IN_BYTE(datareg)|0x03), datareg); + } + goto jumpbit_is_set; + } + + switch(drive->dn) { + case 0: drive_pci = 0x60; + pci_read_config_dword(dev, drive_pci, &drive_conf); + if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) + goto chipset_is_set; + pci_read_config_byte(dev, (drive_pci), &test1); + if (!(test1 & SYNC_ERRDY_EN)) + pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN); + break; + case 1: drive_pci = 0x64; + pci_read_config_dword(dev, drive_pci, &drive_conf); + if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) + goto chipset_is_set; + pci_read_config_byte(dev, 0x60, &test1); + pci_read_config_byte(dev, (drive_pci), &test2); + if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN)) + pci_write_config_byte(dev, (drive_pci), test2|SYNC_ERRDY_EN); + break; + case 2: drive_pci = 0x68; + pci_read_config_dword(dev, drive_pci, &drive_conf); + if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) + goto chipset_is_set; + pci_read_config_byte(dev, (drive_pci), &test1); + if (!(test1 & SYNC_ERRDY_EN)) + pci_write_config_byte(dev, (drive_pci), test1|SYNC_ERRDY_EN); + break; + case 3: drive_pci = 0x6c; + pci_read_config_dword(dev, drive_pci, &drive_conf); + if ((drive_conf != 0x004ff304) && (drive_conf != 0x004ff3c4)) + goto chipset_is_set; + pci_read_config_byte(dev, 0x68, &test1); + pci_read_config_byte(dev, (drive_pci), &test2); + if ((test1 & SYNC_ERRDY_EN) && !(test2 & SYNC_ERRDY_EN)) + pci_write_config_byte(dev, (drive_pci), test2|SYNC_ERRDY_EN); + break; + default: + return ide_dma_off; + } + +chipset_is_set: + + if (drive->media != ide_disk) { + hwif->tuneproc(drive, 5); + return ide_dma_off_quietly; + } + + pci_read_config_byte(dev, (drive_pci), &AP); + if (id->capability & 4) /* IORDY_EN */ + pci_write_config_byte(dev, (drive_pci), AP|IORDY_EN); + pci_read_config_byte(dev, (drive_pci), &AP); + if (drive->media == ide_disk) /* PREFETCH_EN */ + pci_write_config_byte(dev, (drive_pci), AP|PREFETCH_EN); + +jumpbit_is_set: + + if ((id->dma_ultra & 0x0040)&&(udma_133)) speed = XFER_UDMA_6; + else if ((id->dma_ultra & 0x0020)&&(udma_100)) speed = XFER_UDMA_5; + else if ((id->dma_ultra & 0x0010)&&(udma_66)) speed = XFER_UDMA_4; + else if ((id->dma_ultra & 0x0008)&&(udma_66)) speed = XFER_UDMA_3; + else if ((id->dma_ultra & 0x0004)&&(udma_33)) speed = XFER_UDMA_2; + else if ((id->dma_ultra & 0x0002)&&(udma_33)) speed = XFER_UDMA_1; + else if ((id->dma_ultra & 0x0001)&&(udma_33)) speed = XFER_UDMA_0; + else if (id->dma_mword & 0x0004) speed = XFER_MW_DMA_2; + else if (id->dma_mword & 0x0002) speed = XFER_MW_DMA_1; + else if (id->dma_mword & 0x0001) speed = XFER_MW_DMA_0; + else if ((id->dma_1word & 0x0004)&&(!jumpbit)) speed = XFER_SW_DMA_2; + else if ((id->dma_1word & 0x0002)&&(!jumpbit)) speed = XFER_SW_DMA_1; + else if ((id->dma_1word & 0x0001)&&(!jumpbit)) speed = XFER_SW_DMA_0; + else { + /* restore original pci-config space */ + if (!jumpbit) + pci_write_config_dword(dev, drive_pci, drive_conf); + hwif->tuneproc(drive, 5); + return ide_dma_off_quietly; + } + + (void) hwif->speedproc(drive, speed); + + return ((int) ((id->dma_ultra >> 14) & 3) ? ide_dma_on : + ((id->dma_ultra >> 11) & 7) ? ide_dma_on : + ((id->dma_ultra >> 8) & 7) ? ide_dma_on : + ((id->dma_mword >> 8) & 7) ? ide_dma_on : + ((id->dma_1word >> 8) & 7) ? ide_dma_on : + ide_dma_off_quietly); +} + +static int config_drive_xfer_rate (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + ide_dma_action_t dma_func = ide_dma_off_quietly; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && hwif->autodma) { + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + goto fast_ata_pio; + } + dma_func = ide_dma_off_quietly; + if (id->field_valid & 4) { + if (id->dma_ultra & 0x007F) { + /* Force if Capable UltraDMA */ + dma_func = config_chipset_for_dma(drive); + if ((id->field_valid & 2) && + (dma_func != ide_dma_on)) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & 0x0007) || + (id->dma_1word & 0x0007)) { + /* Force if Capable regular DMA modes */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } + } else if (ide_dmaproc(ide_dma_good_drive, drive)) { + if (id->eide_dma_time > 150) { + goto no_dma_set; + } + /* Consult the list of known "good" drives */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + dma_func = ide_dma_off_quietly; +no_dma_set: + hwif->tuneproc(drive, 5); + } + + return hwif->dmaproc(dma_func, drive); +} + +int pdc202xx_quirkproc (ide_drive_t *drive) +{ + return ((int) check_in_drive_lists(drive, pdc_quirk_drives)); +} + +/* + * pdc202xx_dmaproc() initiates/aborts (U)DMA read/write operations on a drive. + */ +int pdc202xx_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + byte dma_stat = 0; + byte sc1d = 0; + byte newchip = 0; + byte clock = 0; + byte hardware48hack = 0; + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + unsigned long high_16 = pci_resource_start(dev, 4); + unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20); + unsigned long dma_base = hwif->dma_base; + + switch (dev->device) { + case PCI_DEVICE_ID_PROMISE_20277: + case PCI_DEVICE_ID_PROMISE_20276: + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20271: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20268R: + case PCI_DEVICE_ID_PROMISE_20268: + newchip = 1; + break; + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20265: + case PCI_DEVICE_ID_PROMISE_20263: + case PCI_DEVICE_ID_PROMISE_20262: + hardware48hack = 1; + clock = IN_BYTE(high_16 + 0x11); + default: + break; + } + + switch (func) { + case ide_dma_check: + return config_drive_xfer_rate(drive); + case ide_dma_begin: + /* Note that this is done *after* the cmd has + * been issued to the drive, as per the BM-IDE spec. + * The Promise Ultra33 doesn't work correctly when + * we do this part before issuing the drive cmd. + */ + if ((drive->addressing == 1) && (hardware48hack)) { + struct request *rq = HWGROUP(drive)->rq; + unsigned long word_count = 0; + + OUT_BYTE(clock|(hwif->channel ? 0x08 : 0x02), high_16 + 0x11); + word_count = (rq->nr_sectors << 8); + word_count = (rq->cmd == READ) ? word_count | 0x05000000 : word_count | 0x06000000; + outl(word_count, atapi_reg); + } + break; + case ide_dma_end: + if ((drive->addressing == 1) && (hardware48hack)) { + outl(0, atapi_reg); /* zero out extra */ + clock = IN_BYTE(high_16 + 0x11); + OUT_BYTE(clock & ~(hwif->channel ? 0x08:0x02), high_16 + 0x11); + } + break; + case ide_dma_test_irq: /* returns 1 if dma irq issued, 0 otherwise */ + dma_stat = IN_BYTE(dma_base+2); + if (newchip) + return (dma_stat & 4) == 4; + + sc1d = IN_BYTE(high_16 + 0x001d); + if (hwif->channel) { + if ((sc1d & 0x50) == 0x50) goto somebody_else; + else if ((sc1d & 0x40) == 0x40) + return (dma_stat & 4) == 4; + } else { + if ((sc1d & 0x05) == 0x05) goto somebody_else; + else if ((sc1d & 0x04) == 0x04) + return (dma_stat & 4) == 4; + } +somebody_else: + return (dma_stat & 4) == 4; /* return 1 if INTR asserted */ + case ide_dma_lostirq: + case ide_dma_timeout: + if (hwif->resetproc != NULL) + hwif->resetproc(drive); + default: + break; + } + return ide_dmaproc(func, drive); /* use standard DMA stuff */ +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +void pdc202xx_new_reset (ide_drive_t *drive) +{ + /* + * Deleted this because it is redundant from the caller. + */ + printk("PDC202XX: %s channel reset.\n", + HWIF(drive)->channel ? "Secondary" : "Primary"); +} + +void pdc202xx_reset_pci (struct pci_dev *dev) +{ + unsigned long high_16 = pci_resource_start(dev, 4); + byte udma_speed_flag = IN_BYTE(high_16 + 0x001f); + + OUT_BYTE(udma_speed_flag | 0x10, high_16 + 0x001f); + mdelay(100); + OUT_BYTE(udma_speed_flag & ~0x10, high_16 + 0x001f); + mdelay(2000); /* 2 seconds ?! */ +} + +void pdc202xx_reset_host (ide_hwif_t *hwif) +{ + pdc202xx_reset_pci(hwif->pci_dev); + printk("PDC202XX: %s channel reset.\n", + hwif->channel ? "Secondary" : "Primary"); +} + +void pdc202xx_reset (ide_drive_t *drive) +{ + pdc202xx_reset_host(HWIF(drive)); +} + +/* + * Since SUN Cobalt is attempting to do this operation, I should disclose + * this has been a long time ago Thu Jul 27 16:40:57 2000 was the patch date + * HOTSWAP ATA Infrastructure. + */ +static int pdc202xx_tristate (ide_drive_t * drive, int state) +{ +#if 0 + ide_hwif_t *hwif = HWIF(drive); + unsigned long high_16 = pci_resource_start(hwif->pci_dev, 4); + byte sc1f = IN_BYTE(high_16 + 0x001f); + + if (!hwif) + return -EINVAL; + +// hwif->bus_state = state; + + if (state) { + OUT_BYTE(sc1f | 0x08, high_16 + 0x001f); + } else { + OUT_BYTE(sc1f & ~0x08, high_16 + 0x001f); + } +#endif + return 0; +} + +unsigned int __init pci_init_pdc202xx (struct pci_dev *dev, const char *name) +{ + unsigned long high_16 = pci_resource_start(dev, 4); + byte udma_speed_flag = IN_BYTE(high_16 + 0x001f); + byte primary_mode = IN_BYTE(high_16 + 0x001a); + byte secondary_mode = IN_BYTE(high_16 + 0x001b); + byte newchip = 0; + + if (dev->resource[PCI_ROM_RESOURCE].start) { + pci_write_config_dword(dev, PCI_ROM_ADDRESS, + dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE); + printk("%s: ROM enabled at 0x%08lx\n", + name, dev->resource[PCI_ROM_RESOURCE].start); + } + + switch (dev->device) { + case PCI_DEVICE_ID_PROMISE_20277: + case PCI_DEVICE_ID_PROMISE_20276: + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20271: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20268R: + case PCI_DEVICE_ID_PROMISE_20268: + newchip = 1; + break; + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20265: + pdc202xx_reset_pci(dev); + break; + case PCI_DEVICE_ID_PROMISE_20263: + case PCI_DEVICE_ID_PROMISE_20262: + /* + * software reset - this is required because the bios + * will set UDMA timing on if the hdd supports it. The + * user may want to turn udma off. A bug in the pdc20262 + * is that it cannot handle a downgrade in timing from + * UDMA to DMA. Disk accesses after issuing a set + * feature command will result in errors. A software + * reset leaves the timing registers intact, + * but resets the drives. + */ + pdc202xx_reset_pci(dev); + default: + if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) { + byte irq = 0, irq2 = 0; + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); + pci_read_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, &irq2); /* 0xbc */ + if (irq != irq2) { + pci_write_config_byte(dev, (PCI_INTERRUPT_LINE)|0x80, irq); /* 0xbc */ + printk("%s: pci-config space interrupt mirror fixed.\n", name); + } + } + break; + } + + if (newchip) + goto fttk_tx_series; + + printk("%s: (U)DMA Burst Bit %sABLED " \ + "Primary %s Mode " \ + "Secondary %s Mode.\n", + name, + (udma_speed_flag & 1) ? "EN" : "DIS", + (primary_mode & 1) ? "MASTER" : "PCI", + (secondary_mode & 1) ? "MASTER" : "PCI" ); + +#ifdef CONFIG_PDC202XX_BURST + if (!(udma_speed_flag & 1)) { + printk("%s: FORCING BURST BIT 0x%02x -> 0x%02x ", name, udma_speed_flag, (udma_speed_flag|1)); + OUT_BYTE(udma_speed_flag|1, high_16 + 0x001f); + printk("%sCTIVE\n", (IN_BYTE(high_16 + 0x001f) & 1) ? "A" : "INA"); + } +#endif /* CONFIG_PDC202XX_BURST */ + +#ifdef CONFIG_PDC202XX_MASTER + if (!(primary_mode & 1)) { + printk("%s: FORCING PRIMARY MODE BIT 0x%02x -> 0x%02x ", + name, primary_mode, (primary_mode|1)); + OUT_BYTE(primary_mode|1, high_16 + 0x001a); + printk("%s\n", (IN_BYTE(high_16 + 0x001a) & 1) ? "MASTER" : "PCI"); + } + + if (!(secondary_mode & 1)) { + printk("%s: FORCING SECONDARY MODE BIT 0x%02x -> 0x%02x ", + name, secondary_mode, (secondary_mode|1)); + OUT_BYTE(secondary_mode|1, high_16 + 0x001b); + printk("%s\n", (IN_BYTE(high_16 + 0x001b) & 1) ? "MASTER" : "PCI"); + } +#endif /* CONFIG_PDC202XX_MASTER */ + +fttk_tx_series: + + pdc202_devs[n_pdc202_devs++] = dev; + +#if defined(DISPLAY_PDC202XX_TIMINGS) && defined(CONFIG_PROC_FS) + if (!pdc202xx_proc) { + pdc202xx_proc = 1; + pdc202xx_display_info = &pdc202xx_get_info; + } +#endif /* DISPLAY_PDC202XX_TIMINGS && CONFIG_PROC_FS */ + + return dev->irq; +} + +unsigned int __init ata66_pdc202xx (ide_hwif_t *hwif) +{ + unsigned short mask = (hwif->channel) ? (1<<11) : (1<<10); + unsigned short CIS; + + switch(hwif->pci_dev->device) { + case PCI_DEVICE_ID_PROMISE_20277: + case PCI_DEVICE_ID_PROMISE_20276: + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20271: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20268: + case PCI_DEVICE_ID_PROMISE_20268R: + OUT_BYTE(0x0b, (hwif->dma_base + 1)); + return (!(IN_BYTE((hwif->dma_base + 3)) & 0x04)); + case PCI_DEVICE_ID_PROMISE_20267: + hwif->addressing = (hwif->channel) ? 0 : 1; + default: + pci_read_config_word(hwif->pci_dev, 0x50, &CIS); + return (!(CIS & mask)); + } +} + +void __init ide_init_pdc202xx (ide_hwif_t *hwif) +{ + hwif->tuneproc = &pdc202xx_tune_drive; + hwif->quirkproc = &pdc202xx_quirkproc; + + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + hwif->autodma = 0; + +#undef CONFIG_PDC202XX_32_UNMASK +#ifdef CONFIG_PDC202XX_32_UNMASK + hwif->drives[0].io_32bit = 1; + hwif->drives[1].io_32bit = 1; + hwif->drives[0].unmask = 1; + hwif->drives[1].unmask = 1; +#endif /* CONFIG_PDC202XX_32_UNMASK */ + + switch(hwif->pci_dev->device) { + case PCI_DEVICE_ID_PROMISE_20277: + case PCI_DEVICE_ID_PROMISE_20276: + case PCI_DEVICE_ID_PROMISE_20275: + case PCI_DEVICE_ID_PROMISE_20271: + case PCI_DEVICE_ID_PROMISE_20269: + case PCI_DEVICE_ID_PROMISE_20268: + case PCI_DEVICE_ID_PROMISE_20268R: + hwif->speedproc = &pdc202xx_new_tune_chipset; + hwif->resetproc = &pdc202xx_new_reset; + break; + case PCI_DEVICE_ID_PROMISE_20267: + case PCI_DEVICE_ID_PROMISE_20265: + case PCI_DEVICE_ID_PROMISE_20263: + case PCI_DEVICE_ID_PROMISE_20262: + hwif->busproc = &pdc202xx_tristate; + hwif->resetproc = &pdc202xx_reset; + case PCI_DEVICE_ID_PROMISE_20246: + hwif->speedproc = &pdc202xx_tune_chipset; + default: + break; + } + + if (!hwif->dma_base) + return; + +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->dmaproc = &pdc202xx_dmaproc; +# ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +# endif /* CONFIG_IDEDMA_AUTO */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ +} + +extern void ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d); + +void __init fixup_device_pdc20265 (struct pci_dev *dev, ide_pci_device_t *d) +{ + if ((dev->bus->self) && + (dev->bus->self->vendor == PCI_VENDOR_ID_INTEL) && + (dev->bus->self->device == PCI_DEVICE_ID_INTEL_I960)) { + printk(KERN_INFO "ide: Skipping Promise PDC20265 " + "attached to I2O RAID controller.\n"); + return; + } + + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); +} + +void __init fixup_device_pdc20270 (struct pci_dev *dev, ide_pci_device_t *d) +{ + struct pci_dev *dev2 = NULL, *findev; + ide_pci_device_t *d2; + + if ((dev->bus->self && + dev->bus->self->vendor == PCI_VENDOR_ID_DEC) && + (dev->bus->self->device == PCI_DEVICE_ID_DEC_21150)) { + if (PCI_SLOT(dev->devfn) & 2) { + return; + } + d->extra = 0; + pci_for_each_dev(findev) { + if ((findev->vendor == dev->vendor) && + (findev->device == dev->device) && + (PCI_SLOT(findev->devfn) & 2)) { + byte irq = 0, irq2 = 0; + dev2 = findev; + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, &irq); + pci_read_config_byte(dev2, PCI_INTERRUPT_LINE, &irq2); + if (irq != irq2) { + dev2->irq = dev->irq; + pci_write_config_byte(dev2, PCI_INTERRUPT_LINE, irq); + } + } + } + } + + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); + if (!dev2) + return; + d2 = d; + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d2->name, dev2->bus->number, dev2->devfn); + ide_setup_pci_device(dev2, d2); +} diff -Nru a/drivers/ide/pdc4030.c b/drivers/ide/pdc4030.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/pdc4030.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,716 @@ +/* -*- linux-c -*- + * linux/drivers/ide/pdc4030.c Version 0.90 May 27, 1999 + * + * Copyright (C) 1995-1999 Linus Torvalds & authors (see below) + */ + +/* + * Principal Author/Maintainer: peterd@pnd-pc.demon.co.uk + * + * This file provides support for the second port and cache of Promise + * IDE interfaces, e.g. DC4030VL, DC4030VL-1 and DC4030VL-2. + * + * Thanks are due to Mark Lord for advice and patiently answering stupid + * questions, and all those mugs^H^H^H^Hbrave souls who've tested this, + * especially Andre Hedrick. + * + * Version 0.01 Initial version, #include'd in ide.c rather than + * compiled separately. + * Reads use Promise commands, writes as before. Drives + * on second channel are read-only. + * Version 0.02 Writes working on second channel, reads on both + * channels. Writes fail under high load. Suspect + * transfers of >127 sectors don't work. + * Version 0.03 Brought into line with ide.c version 5.27. + * Other minor changes. + * Version 0.04 Updated for ide.c version 5.30 + * Changed initialization strategy + * Version 0.05 Kernel integration. -ml + * Version 0.06 Ooops. Add hwgroup to direct call of ide_intr() -ml + * Version 0.07 Added support for DC4030 variants + * Secondary interface autodetection + * Version 0.08 Renamed to pdc4030.c + * Version 0.09 Obsolete - never released - did manual write request + * splitting before max_sectors[major][minor] available. + * Version 0.10 Updated for 2.1 series of kernels + * Version 0.11 Updated for 2.3 series of kernels + * Autodetection code added. + * + * Version 0.90 Transition to BETA code. No lost/unexpected interrupts + */ + +/* + * Once you've compiled it in, you'll have to also enable the interface + * setup routine from the kernel command line, as in + * + * 'linux ide0=dc4030' or 'linux ide1=dc4030' + * + * It should now work as a second controller also ('ide1=dc4030') but only + * if you DON'T have BIOS V4.44, which has a bug. If you have this version + * and EPROM programming facilities, you need to fix 4 bytes: + * 2496: 81 81 + * 2497: 3E 3E + * 2498: 22 98 * + * 2499: 06 05 * + * 249A: F0 F0 + * 249B: 01 01 + * ... + * 24A7: 81 81 + * 24A8: 3E 3E + * 24A9: 22 98 * + * 24AA: 06 05 * + * 24AB: 70 70 + * 24AC: 01 01 + * + * As of January 1999, Promise Technology Inc. have finally supplied me with + * some technical information which has shed a glimmer of light on some of the + * problems I was having, especially with writes. + * + * There are still problems with the robustness and efficiency of this driver + * because I still don't understand what the card is doing with interrupts. + */ + +#define DEBUG_READ +#define DEBUG_WRITE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "pdc4030.h" + +/* + * promise_selectproc() is invoked by ide.c + * in preparation for access to the specified drive. + */ +static void promise_selectproc (ide_drive_t *drive) +{ + unsigned int number; + + number = (HWIF(drive)->channel << 1) + drive->select.b.unit; + OUT_BYTE(number,IDE_FEATURE_REG); +} + +/* + * pdc4030_cmd handles the set of vendor specific commands that are initiated + * by command F0. They all have the same success/failure notification - + * 'P' (=0x50) on success, 'p' (=0x70) on failure. + */ +int pdc4030_cmd(ide_drive_t *drive, byte cmd) +{ + unsigned long timeout, timer; + byte status_val; + + promise_selectproc(drive); /* redundant? */ + OUT_BYTE(0xF3,IDE_SECTOR_REG); + OUT_BYTE(cmd,IDE_SELECT_REG); + OUT_BYTE(PROMISE_EXTENDED_COMMAND,IDE_COMMAND_REG); + timeout = HZ * 10; + timeout += jiffies; + do { + if(time_after(jiffies, timeout)) { + return 2; /* device timed out */ + } + /* This is out of delay_10ms() */ + /* Delays at least 10ms to give interface a chance */ + timer = jiffies + (HZ + 99)/100 + 1; + while (time_after(timer, jiffies)); + status_val = IN_BYTE(IDE_SECTOR_REG); + } while (status_val != 0x50 && status_val != 0x70); + + if(status_val == 0x50) + return 0; /* device returned success */ + else + return 1; /* device returned failure */ +} + +/* + * pdc4030_identify sends a vendor-specific IDENTIFY command to the drive + */ +int pdc4030_identify(ide_drive_t *drive) +{ + return pdc4030_cmd(drive, PROMISE_IDENTIFY); +} + +int enable_promise_support = 0; + +void __init init_pdc4030 (void) +{ + enable_promise_support = 1; +} + +/* + * setup_pdc4030() + * Completes the setup of a Promise DC4030 controller card, once found. + */ +int __init setup_pdc4030 (ide_hwif_t *hwif) +{ + ide_drive_t *drive; + ide_hwif_t *hwif2; + struct dc_ident ident; + int i; + ide_startstop_t startstop; + + if (!hwif) return 0; + + drive = &hwif->drives[0]; + hwif2 = &ide_hwifs[hwif->index+1]; + if (hwif->chipset == ide_pdc4030) /* we've already been found ! */ + return 1; + + if (IN_BYTE(IDE_NSECTOR_REG) == 0xFF || + IN_BYTE(IDE_SECTOR_REG) == 0xFF) { + return 0; + } + if (IDE_CONTROL_REG) + OUT_BYTE(0x08,IDE_CONTROL_REG); + if (pdc4030_cmd(drive,PROMISE_GET_CONFIG)) { + return 0; + } + if (ide_wait_stat(&startstop, drive,DATA_READY,BAD_W_STAT,WAIT_DRQ)) { + printk(KERN_INFO + "%s: Failed Promise read config!\n",hwif->name); + return 0; + } + ata_input_data(drive, &ident, SECTOR_WORDS); + if (ident.id[1] != 'P' || ident.id[0] != 'T') { + return 0; + } + printk(KERN_INFO "%s: Promise caching controller, ",hwif->name); + switch(ident.type) { + case 0x43: printk("DC4030VL-2, "); break; + case 0x41: printk("DC4030VL-1, "); break; + case 0x40: printk("DC4030VL, "); break; + default: + printk("unknown - type 0x%02x - please report!\n" + ,ident.type); + printk("Please e-mail the following data to " + "promise@pnd-pc.demon.co.uk along with\n" + "a description of your card and drives:\n"); + for (i=0; i < 0x90; i++) { + printk("%02x ", ((unsigned char *)&ident)[i]); + if ((i & 0x0f) == 0x0f) printk("\n"); + } + return 0; + } + printk("%dKB cache, ",(int)ident.cache_mem); + switch(ident.irq) { + case 0x00: hwif->irq = 14; break; + case 0x01: hwif->irq = 12; break; + default: hwif->irq = 15; break; + } + printk("on IRQ %d\n",hwif->irq); + + /* + * Once found and identified, we set up the next hwif in the array + * (hwif2 = ide_hwifs[hwif->index+1]) with the same io ports, irq + * and other settings as the main hwif. This gives us two "mated" + * hwifs pointing to the Promise card. + * + * We also have to shift the default values for the remaining + * interfaces "up by one" to make room for the second interface on the + * same set of values. + */ + + hwif->chipset = hwif2->chipset = ide_pdc4030; + hwif->mate = hwif2; + hwif2->mate = hwif; + hwif2->channel = 1; + hwif->selectproc = hwif2->selectproc = &promise_selectproc; + hwif->serialized = hwif2->serialized = 1; + +/* Shift the remaining interfaces down by one */ + for (i=MAX_HWIFS-1 ; i > hwif->index+1 ; i--) { + ide_hwif_t *h = &ide_hwifs[i]; + +#ifdef DEBUG + printk(KERN_DEBUG "Shifting i/f %d values to i/f %d\n",i-1,i); +#endif /* DEBUG */ + ide_init_hwif_ports(&h->hw, (h-1)->io_ports[IDE_DATA_OFFSET], 0, NULL); + memcpy(h->io_ports, h->hw.io_ports, sizeof(h->io_ports)); + h->noprobe = (h-1)->noprobe; + } + ide_init_hwif_ports(&hwif2->hw, hwif->io_ports[IDE_DATA_OFFSET], 0, NULL); + memcpy(hwif2->io_ports, hwif->hw.io_ports, sizeof(hwif2->io_ports)); + hwif2->irq = hwif->irq; + hwif2->hw.irq = hwif->hw.irq = hwif->irq; + for (i=0; i<2 ; i++) { + hwif->drives[i].io_32bit = 3; + hwif2->drives[i].io_32bit = 3; + hwif->drives[i].keep_settings = 1; + hwif2->drives[i].keep_settings = 1; + if (!ident.current_tm[i].cyl) + hwif->drives[i].noprobe = 1; + if (!ident.current_tm[i+2].cyl) + hwif2->drives[i].noprobe = 1; + } + return 1; +} + +/* + * detect_pdc4030() + * Tests for the presence of a DC4030 Promise card on this interface + * Returns: 1 if found, 0 if not found + */ +int __init detect_pdc4030(ide_hwif_t *hwif) +{ + ide_drive_t *drive = &hwif->drives[0]; + + if (IDE_DATA_REG == 0) { /* Skip test for non-existent interface */ + return 0; + } + OUT_BYTE(0xF3, IDE_SECTOR_REG); + OUT_BYTE(0x14, IDE_SELECT_REG); + OUT_BYTE(PROMISE_EXTENDED_COMMAND, IDE_COMMAND_REG); + + ide_delay_50ms(); + + if (IN_BYTE(IDE_ERROR_REG) == 'P' && + IN_BYTE(IDE_NSECTOR_REG) == 'T' && + IN_BYTE(IDE_SECTOR_REG) == 'I') { + return 1; + } else { + return 0; + } +} + +void __init ide_probe_for_pdc4030(void) +{ + unsigned int index; + ide_hwif_t *hwif; + + if (enable_promise_support == 0) + return; + for (index = 0; index < MAX_HWIFS; index++) { + hwif = &ide_hwifs[index]; + if (hwif->chipset == ide_unknown && detect_pdc4030(hwif)) { + setup_pdc4030(hwif); + } + } +} + +/* + * promise_read_intr() is the handler for disk read/multread interrupts + */ +static ide_startstop_t promise_read_intr (ide_drive_t *drive) +{ + byte stat; + int total_remaining; + unsigned int sectors_left, sectors_avail, nsect; + struct request *rq; +#ifdef CONFIG_IDE_TASKFILE_IO + unsigned long flags; + char *to; +#endif /* CONFIG_IDE_TASKFILE_IO */ + + if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) + return DRIVER(drive)->error(drive, "promise_read_intr", stat); + +read_again: + do { + sectors_left = IN_BYTE(IDE_NSECTOR_REG); + IN_BYTE(IDE_SECTOR_REG); + } while (IN_BYTE(IDE_NSECTOR_REG) != sectors_left); + rq = HWGROUP(drive)->rq; + sectors_avail = rq->nr_sectors - sectors_left; + if (!sectors_avail) + goto read_again; + +read_next: + rq = HWGROUP(drive)->rq; + nsect = rq->current_nr_sectors; + if (nsect > sectors_avail) + nsect = sectors_avail; + sectors_avail -= nsect; +#ifdef CONFIG_IDE_TASKFILE_IO + to = ide_map_buffer(rq, &flags); + ata_input_data(drive, to, nsect * SECTOR_WORDS); +#else /* !CONFIG_IDE_TASKFILE_IO */ + ata_input_data(drive, rq->buffer, nsect * SECTOR_WORDS); +#endif /* CONFIG_IDE_TASKFILE_IO */ + +#ifdef DEBUG_READ + printk(KERN_DEBUG "%s: promise_read: sectors(%ld-%ld), " + "buf=0x%08lx, rem=%ld\n", drive->name, rq->sector, + rq->sector+nsect-1, +#ifdef CONFIG_IDE_TASKFILE_IO + (unsigned long) to, +#else /* !CONFIG_IDE_TASKFILE_IO */ + (unsigned long) rq->buffer, +#endif /* CONFIG_IDE_TASKFILE_IO */ + rq->nr_sectors-nsect); +#endif /* DEBUG_READ */ + +#ifdef CONFIG_IDE_TASKFILE_IO + ide_unmap_buffer(to, &flags); +#else /* !CONFIG_IDE_TASKFILE_IO */ + rq->buffer += nsect<<9; +#endif /* CONFIG_IDE_TASKFILE_IO */ + rq->sector += nsect; + rq->errors = 0; + rq->nr_sectors -= nsect; + total_remaining = rq->nr_sectors; + if ((rq->current_nr_sectors -= nsect) <= 0) { + DRIVER(drive)->end_request(drive, 1); + } +/* + * Now the data has been read in, do the following: + * + * if there are still sectors left in the request, + * if we know there are still sectors available from the interface, + * go back and read the next bit of the request. + * else if DRQ is asserted, there are more sectors available, so + * go back and find out how many, then read them in. + * else if BUSY is asserted, we are going to get an interrupt, so + * set the handler for the interrupt and just return + */ + if (total_remaining > 0) { + if (sectors_avail) + goto read_next; + stat = GET_STAT(); + if (stat & DRQ_STAT) + goto read_again; + if (stat & BUSY_STAT) { + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler (drive, &promise_read_intr, WAIT_CMD, NULL); +#ifdef DEBUG_READ + printk(KERN_DEBUG "%s: promise_read: waiting for" + "interrupt\n", drive->name); +#endif /* DEBUG_READ */ + return ide_started; + } + printk(KERN_ERR "%s: Eeek! promise_read_intr: sectors left " + "!DRQ !BUSY\n", drive->name); + return DRIVER(drive)->error(drive, "promise read intr", stat); + } + return ide_stopped; +} + +/* + * promise_complete_pollfunc() + * This is the polling function for waiting (nicely!) until drive stops + * being busy. It is invoked at the end of a write, after the previous poll + * has finished. + * + * Once not busy, the end request is called. + */ +static ide_startstop_t promise_complete_pollfunc(ide_drive_t *drive) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + struct request *rq = hwgroup->rq; + int i; + + if (GET_STAT() & BUSY_STAT) { + if (time_before(jiffies, hwgroup->poll_timeout)) { + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &promise_complete_pollfunc, HZ/100, NULL); + return ide_started; /* continue polling... */ + } + hwgroup->poll_timeout = 0; + printk(KERN_ERR "%s: completion timeout - still busy!\n", + drive->name); + return DRIVER(drive)->error(drive, "busy timeout", GET_STAT()); + } + + hwgroup->poll_timeout = 0; +#ifdef DEBUG_WRITE + printk(KERN_DEBUG "%s: Write complete - end_request\n", drive->name); +#endif /* DEBUG_WRITE */ + for (i = rq->nr_sectors; i > 0; ) { + i -= rq->current_nr_sectors; + DRIVER(drive)->end_request(drive, 1); + } + return ide_stopped; +} + +/* + * promise_multwrite() transfers a block of up to mcount sectors of data + * to a drive as part of a disk multiple-sector write operation. + * + * Returns 0 on success. + * + * Note that we may be called from two contexts - the do_rw_disk context + * and IRQ context. The IRQ can happen any time after we've output the + * full "mcount" number of sectors, so we must make sure we update the + * state _before_ we output the final part of the data! + */ +int promise_multwrite (ide_drive_t *drive, unsigned int mcount) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + struct request *rq = &hwgroup->wrq; + + do { + char *buffer; + int nsect = rq->current_nr_sectors; +#ifdef CONFIG_IDE_TASKFILE_IO + unsigned long flags; +#endif /* CONFIG_IDE_TASKFILE_IO */ + + if (nsect > mcount) + nsect = mcount; + mcount -= nsect; +#ifdef CONFIG_IDE_TASKFILE_IO + buffer = ide_map_buffer(rq, &flags); + rq->sector += nsect; +#else /* !CONFIG_IDE_TASKFILE_IO */ + buffer = rq->buffer; + + rq->sector += nsect; + rq->buffer += nsect << 9; +#endif /* CONFIG_IDE_TASKFILE_IO */ + rq->nr_sectors -= nsect; + rq->current_nr_sectors -= nsect; + + /* Do we move to the next bh after this? */ + if (!rq->current_nr_sectors) { + struct bio *bio = rq->bio; + + /* + * only move to next bio, when we have processed + * all bvecs in this one. + */ + if (++bio->bi_idx >= bio->bi_vcnt) { + bio->bi_idx = 0; + bio = bio->bi_next; + } + + /* end early early we ran out of requests */ + if (!bio) { + mcount = 0; + } else { + rq->bio = bio; + rq->current_nr_sectors = bio_iovec(bio)->bv_len >> 9; + rq->hard_cur_sectors = rq->current_nr_sectors; + } + } + + /* + * Ok, we're all setup for the interrupt + * re-entering us on the last transfer. + */ + taskfile_output_data(drive, buffer, nsect<<7); +#ifdef CONFIG_IDE_TASKFILE_IO + ide_unmap_buffer(buffer, &flags); +#endif /* CONFIG_IDE_TASKFILE_IO */ + } while (mcount); + + return 0; +} + +/* + * promise_write_pollfunc() is the handler for disk write completion polling. + */ +static ide_startstop_t promise_write_pollfunc (ide_drive_t *drive) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + + if (IN_BYTE(IDE_NSECTOR_REG) != 0) { + if (time_before(jiffies, hwgroup->poll_timeout)) { + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler (drive, &promise_write_pollfunc, HZ/100, NULL); + return ide_started; /* continue polling... */ + } + hwgroup->poll_timeout = 0; + printk(KERN_ERR "%s: write timed-out!\n",drive->name); + return DRIVER(drive)->error(drive, "write timeout", GET_STAT()); + } + + /* + * Now write out last 4 sectors and poll for not BUSY + */ + promise_multwrite(drive, 4); + hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &promise_complete_pollfunc, HZ/100, NULL); +#ifdef DEBUG_WRITE + printk(KERN_DEBUG "%s: Done last 4 sectors - status = %02x\n", + drive->name, GET_STAT()); +#endif /* DEBUG_WRITE */ + return ide_started; +} + +/* + * promise_write() transfers a block of one or more sectors of data to a + * drive as part of a disk write operation. All but 4 sectors are transferred + * in the first attempt, then the interface is polled (nicely!) for completion + * before the final 4 sectors are transferred. There is no interrupt generated + * on writes (at least on the DC4030VL-2), we just have to poll for NOT BUSY. + */ +static ide_startstop_t promise_write (ide_drive_t *drive) +{ + ide_hwgroup_t *hwgroup = HWGROUP(drive); + struct request *rq = &hwgroup->wrq; + +#ifdef DEBUG_WRITE + printk(KERN_DEBUG "%s: promise_write: sectors(%ld-%ld), " + "buffer=%p\n", drive->name, rq->sector, + rq->sector + rq->nr_sectors - 1, rq->buffer); +#endif /* DEBUG_WRITE */ + + /* + * If there are more than 4 sectors to transfer, do n-4 then go into + * the polling strategy as defined above. + */ + if (rq->nr_sectors > 4) { + if (promise_multwrite(drive, rq->nr_sectors - 4)) + return ide_stopped; + hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler (drive, &promise_write_pollfunc, HZ/100, NULL); + return ide_started; + } else { + /* + * There are 4 or fewer sectors to transfer, do them all in one go + * and wait for NOT BUSY. + */ + if (promise_multwrite(drive, rq->nr_sectors)) + return ide_stopped; + hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE; + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &promise_complete_pollfunc, HZ/100, NULL); +#ifdef DEBUG_WRITE + printk(KERN_DEBUG "%s: promise_write: <= 4 sectors, " + "status = %02x\n", drive->name, GET_STAT()); +#endif /* DEBUG_WRITE */ + return ide_started; + } +} + +/* + * do_pdc4030_io() is called from do_rw_disk, having had the block number + * already set up. It issues a READ or WRITE command to the Promise + * controller, assuming LBA has been used to set up the block number. + */ +#ifndef CONFIG_IDE_TASKFILE_IO +ide_startstop_t do_pdc4030_io (ide_drive_t *drive, struct request *rq) +{ +#else /* CONFIG_IDE_TASKFILE_IO */ +ide_startstop_t do_pdc4030_io (ide_drive_t *drive, ide_task_t *task) +{ + struct request *rq = HWGROUP(drive)->rq; + task_struct_t *taskfile = (task_struct_t *) task->tfRegister; +#endif /* CONFIG_IDE_TASKFILE_IO */ + ide_startstop_t startstop; + unsigned long timeout; + byte stat; + + BUG_ON(!(rq->flags & REQ_CMD)); + +#ifdef CONFIG_IDE_TASKFILE_IO + if (IDE_CONTROL_REG) + OUT_BYTE(drive->ctl, IDE_CONTROL_REG); /* clear nIEN */ + SELECT_MASK(HWIF(drive), drive, 0); + + OUT_BYTE(taskfile->feature, IDE_FEATURE_REG); + OUT_BYTE(taskfile->sector_count, IDE_NSECTOR_REG); + /* refers to number of sectors to transfer */ + OUT_BYTE(taskfile->sector_number, IDE_SECTOR_REG); + /* refers to sector offset or start sector */ + OUT_BYTE(taskfile->low_cylinder, IDE_LCYL_REG); + OUT_BYTE(taskfile->high_cylinder, IDE_HCYL_REG); + OUT_BYTE(taskfile->device_head, IDE_SELECT_REG); + OUT_BYTE(taskfile->command, IDE_COMMAND_REG); +#endif /* CONFIG_IDE_TASKFILE_IO */ + + if (rq_data_dir(rq) == READ) { +#ifndef CONFIG_IDE_TASKFILE_IO + OUT_BYTE(PROMISE_READ, IDE_COMMAND_REG); +#endif /* CONFIG_IDE_TASKFILE_IO */ +/* + * The card's behaviour is odd at this point. If the data is + * available, DRQ will be true, and no interrupt will be + * generated by the card. If this is the case, we need to call the + * "interrupt" handler (promise_read_intr) directly. Otherwise, if + * an interrupt is going to occur, bit0 of the SELECT register will + * be high, so we can set the handler the just return and be interrupted. + * If neither of these is the case, we wait for up to 50ms (badly I'm + * afraid!) until one of them is. + */ + timeout = jiffies + HZ/20; /* 50ms wait */ + do { + stat=GET_STAT(); + if (stat & DRQ_STAT) { + udelay(1); + return promise_read_intr(drive); + } + if (IN_BYTE(IDE_SELECT_REG) & 0x01) { +#ifdef DEBUG_READ + printk(KERN_DEBUG "%s: read: waiting for " + "interrupt\n", drive->name); +#endif /* DEBUG_READ */ + ide_set_handler(drive, &promise_read_intr, WAIT_CMD, NULL); + return ide_started; + } + udelay(1); + } while (time_before(jiffies, timeout)); + + printk(KERN_ERR "%s: reading: No DRQ and not " + "waiting - Odd!\n", drive->name); + return ide_stopped; + } else if (rq_data_dir(rq) == WRITE) { +#ifndef CONFIG_IDE_TASKFILE_IO + OUT_BYTE(PROMISE_WRITE, IDE_COMMAND_REG); +#endif /* CONFIG_IDE_TASKFILE_IO */ + if (ide_wait_stat(&startstop, drive, DATA_READY, + drive->bad_wstat, WAIT_DRQ)) { + printk(KERN_ERR "%s: no DRQ after issuing " + "PROMISE_WRITE\n", drive->name); + return startstop; + } + if (!drive->unmask) + local_irq_disable(); + HWGROUP(drive)->wrq = *rq; /* scratchpad */ + return promise_write(drive); + } else { + blk_dump_rq_flags(rq, "do_pdc4030_io - bad command\n"); + DRIVER(drive)->end_request(drive, 0); + return ide_stopped; + } +} + +#ifdef CONFIG_IDE_TASKFILE_IO + +ide_startstop_t promise_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block) +{ + struct hd_drive_task_hdr taskfile; + ide_task_t args; + + memset(&taskfile, 0, sizeof(struct hd_drive_task_hdr)); + + taskfile.sector_count = rq->nr_sectors; + taskfile.sector_number = block; + taskfile.low_cylinder = (block>>=8); + taskfile.high_cylinder = (block>>=8); + taskfile.device_head = ((block>>8)&0x0f)|drive->select.all; + taskfile.command = (rq->cmd==READ)?PROMISE_READ:PROMISE_WRITE; + + memcpy(args.tfRegister, &taskfile, sizeof(struct hd_drive_task_hdr)); + memcpy(args.hobRegister, NULL, sizeof(struct hd_drive_hob_hdr)); + args.command_type = ide_cmd_type_parser(&args); + args.prehandler = NULL; + args.handler = NULL; + args.posthandler = NULL; + args.rq = (struct request *) rq; + rq->special = NULL; + rq->special = (ide_task_t *)&args; + + return do_pdc4030_io(drive, &args); +} +#endif /* CONFIG_IDE_TASKFILE_IO */ + diff -Nru a/drivers/ide/pdc4030.h b/drivers/ide/pdc4030.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/pdc4030.h Fri Aug 16 14:35:01 2002 @@ -0,0 +1,44 @@ +/* + * linux/drivers/ide/pdc4030.h + * + * Copyright (C) 1995-1998 Linus Torvalds & authors + */ + +/* + * Principal author: Peter Denison + */ + +#ifndef IDE_PROMISE_H +#define IDE_PROMISE_H + +#define PROMISE_EXTENDED_COMMAND 0xF0 +#define PROMISE_READ 0xF2 +#define PROMISE_WRITE 0xF3 +/* Extended commands - main command code = 0xf0 */ +#define PROMISE_GET_CONFIG 0x10 +#define PROMISE_IDENTIFY 0x20 + +struct translation_mode { + u16 cyl; + u8 head; + u8 sect; +}; + +struct dc_ident { + u8 type; + u8 unknown1; + u8 hw_revision; + u8 firmware_major; + u8 firmware_minor; + u8 bios_address; + u8 irq; + u8 unknown2; + u16 cache_mem; + u16 unknown3; + u8 id[2]; + u16 info; + struct translation_mode current_tm[4]; + u8 pad[SECTOR_WORDS*4 - 32]; +}; + +#endif /* IDE_PROMISE_H */ diff -Nru a/drivers/ide/pdcadma.c b/drivers/ide/pdcadma.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/pdcadma.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,106 @@ +/* + * linux/drivers/ide/pdcadma.c Version 0.01 June 21, 2001 + * + * Copyright (C) 1999-2000 Andre Hedrick + * May be copied or modified under the terms of the GNU General Public License + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "ide_modes.h" + +#undef DISPLAY_PDCADMA_TIMINGS + +#if defined(DISPLAY_PDCADMA_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +static int pdcadma_get_info(char *, char **, off_t, int); +extern int (*pdcadma_display_info)(char *, char **, off_t, int); /* ide-proc.c */ +static struct pci_dev *bmide_dev; + +static int pdcadma_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + u32 bibma = pci_resource_start(bmide_dev, 4); + + p += sprintf(p, "\n PDC ADMA %04X Chipset.\n", bmide_dev->device); + p += sprintf(p, "UDMA\n"); + p += sprintf(p, "PIO\n"); + + return p-buffer; /* => must be less than 4k! */ +} +#endif /* defined(DISPLAY_PDCADMA_TIMINGS) && defined(CONFIG_PROC_FS) */ + +byte pdcadma_proc = 0; + +#ifdef CONFIG_BLK_DEV_IDEDMA +/* + * pdcadma_dmaproc() initiates/aborts (U)DMA read/write operations on a drive. + */ + +int pdcadma_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + func = ide_dma_off_quietly; + default: + break; + } + return ide_dmaproc(func, drive); /* use standard DMA stuff */ +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +unsigned int __init pci_init_pdcadma (struct pci_dev *dev, const char *name) +{ +#if defined(DISPLAY_PDCADMA_TIMINGS) && defined(CONFIG_PROC_FS) + if (!pdcadma_proc) { + pdcadma_proc = 1; + bmide_dev = dev; + pdcadma_display_info = &pdcadma_get_info; + } +#endif /* DISPLAY_PDCADMA_TIMINGS && CONFIG_PROC_FS */ + return 0; +} + +unsigned int __init ata66_pdcadma (ide_hwif_t *hwif) +{ + return 1; +} + +void __init ide_init_pdcadma (ide_hwif_t *hwif) +{ + hwif->autodma = 0; + hwif->dma_base = 0; + +// hwif->tuneproc = &pdcadma_tune_drive; +// hwif->speedproc = &pdcadma_tune_chipset; + +// if (hwif->dma_base) { +// hwif->dmaproc = &pdcadma_dmaproc; +// hwif->autodma = 1; +// } +} + +void __init ide_dmacapable_pdcadma (ide_hwif_t *hwif, unsigned long dmabase) +{ +// ide_setup_dma(hwif, dmabase, 8); +} + diff -Nru a/drivers/ide/piix.c b/drivers/ide/piix.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/piix.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,617 @@ +/* + * linux/drivers/ide/piix.c Version 0.32 June 9, 2000 + * + * Copyright (C) 1998-1999 Andrzej Krzysztofowicz, Author and Maintainer + * Copyright (C) 1998-2000 Andre Hedrick + * May be copied or modified under the terms of the GNU General Public License + * + * PIO mode setting function for Intel chipsets. + * For use instead of BIOS settings. + * + * 40-41 + * 42-43 + * + * 41 + * 43 + * + * | PIO 0 | c0 | 80 | 0 | piix_tune_drive(drive, 0); + * | PIO 2 | SW2 | d0 | 90 | 4 | piix_tune_drive(drive, 2); + * | PIO 3 | MW1 | e1 | a1 | 9 | piix_tune_drive(drive, 3); + * | PIO 4 | MW2 | e3 | a3 | b | piix_tune_drive(drive, 4); + * + * sitre = word40 & 0x4000; primary + * sitre = word42 & 0x4000; secondary + * + * 44 8421|8421 hdd|hdb + * + * 48 8421 hdd|hdc|hdb|hda udma enabled + * + * 0001 hda + * 0010 hdb + * 0100 hdc + * 1000 hdd + * + * 4a 84|21 hdb|hda + * 4b 84|21 hdd|hdc + * + * ata-33/82371AB + * ata-33/82371EB + * ata-33/82801AB ata-66/82801AA + * 00|00 udma 0 00|00 reserved + * 01|01 udma 1 01|01 udma 3 + * 10|10 udma 2 10|10 udma 4 + * 11|11 reserved 11|11 reserved + * + * 54 8421|8421 ata66 drive|ata66 enable + * + * pci_read_config_word(HWIF(drive)->pci_dev, 0x40, ®40); + * pci_read_config_word(HWIF(drive)->pci_dev, 0x42, ®42); + * pci_read_config_word(HWIF(drive)->pci_dev, 0x44, ®44); + * pci_read_config_word(HWIF(drive)->pci_dev, 0x48, ®48); + * pci_read_config_word(HWIF(drive)->pci_dev, 0x4a, ®4a); + * pci_read_config_word(HWIF(drive)->pci_dev, 0x54, ®54); + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ide_modes.h" + +#define PIIX_DEBUG_DRIVE_INFO 0 + +#define DISPLAY_PIIX_TIMINGS + +#if defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +static int piix_get_info(char *, char **, off_t, int); +extern int (*piix_display_info)(char *, char **, off_t, int); /* ide-proc.c */ +static struct pci_dev *bmide_dev; + +static int piix_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + u32 bibma = pci_resource_start(bmide_dev, 4); + u16 reg40 = 0, psitre = 0, reg42 = 0, ssitre = 0; + u8 c0 = 0, c1 = 0; + u8 reg44 = 0, reg48 = 0, reg4a = 0, reg4b = 0, reg54 = 0, reg55 = 0; + + p += sprintf(p, "\n "); + switch(bmide_dev->device) { + case PCI_DEVICE_ID_INTEL_82801BA_8: + case PCI_DEVICE_ID_INTEL_82801BA_9: + case PCI_DEVICE_ID_INTEL_82801CA_10: + case PCI_DEVICE_ID_INTEL_82801CA_11: + case PCI_DEVICE_ID_INTEL_82801E_11: + case PCI_DEVICE_ID_INTEL_82801DB_11: + p += sprintf(p, "Intel PIIX4 Ultra 100 Chipset.\n"); + break; + case PCI_DEVICE_ID_INTEL_82372FB_1: + case PCI_DEVICE_ID_INTEL_82801AA_1: + p += sprintf(p, "Intel PIIX4 Ultra 66 Chipset.\n"); + break; + case PCI_DEVICE_ID_INTEL_82451NX: + case PCI_DEVICE_ID_INTEL_82801AB_1: + case PCI_DEVICE_ID_INTEL_82443MX_1: + case PCI_DEVICE_ID_INTEL_82371AB: + p += sprintf(p, "Intel PIIX4 Ultra 33 Chipset.\n"); + break; + case PCI_DEVICE_ID_INTEL_82371SB_1: + p += sprintf(p, "Intel PIIX3 Chipset.\n"); + break; + case PCI_DEVICE_ID_INTEL_82371MX: + p += sprintf(p, "Intel MPIIX Chipset.\n"); + return p-buffer; /* => must be less than 4k! */ + case PCI_DEVICE_ID_INTEL_82371FB_1: + case PCI_DEVICE_ID_INTEL_82371FB_0: + default: + p += sprintf(p, "Intel PIIX Chipset.\n"); + break; + } + + pci_read_config_word(bmide_dev, 0x40, ®40); + pci_read_config_word(bmide_dev, 0x42, ®42); + pci_read_config_byte(bmide_dev, 0x44, ®44); + pci_read_config_byte(bmide_dev, 0x48, ®48); + pci_read_config_byte(bmide_dev, 0x4a, ®4a); + pci_read_config_byte(bmide_dev, 0x4b, ®4b); + pci_read_config_byte(bmide_dev, 0x54, ®54); + pci_read_config_byte(bmide_dev, 0x55, ®55); + + psitre = (reg40 & 0x4000) ? 1 : 0; + ssitre = (reg42 & 0x4000) ? 1 : 0; + + /* + * at that point bibma+0x2 et bibma+0xa are byte registers + * to investigate: + */ + c0 = inb_p((unsigned short)bibma + 0x02); + c1 = inb_p((unsigned short)bibma + 0x0a); + + p += sprintf(p, "--------------- Primary Channel " + "---------------- Secondary Channel " + "-------------\n"); + p += sprintf(p, " %sabled " + " %sabled\n", + (c0&0x80) ? "dis" : " en", + (c1&0x80) ? "dis" : " en"); + p += sprintf(p, "--------------- drive0 --------- drive1 " + "-------- drive0 ---------- drive1 ------\n"); + p += sprintf(p, "DMA enabled: %s %s " + " %s %s\n", + (c0&0x20) ? "yes" : "no ", + (c0&0x40) ? "yes" : "no ", + (c1&0x20) ? "yes" : "no ", + (c1&0x40) ? "yes" : "no " ); + p += sprintf(p, "UDMA enabled: %s %s " + " %s %s\n", + (reg48&0x01) ? "yes" : "no ", + (reg48&0x02) ? "yes" : "no ", + (reg48&0x04) ? "yes" : "no ", + (reg48&0x08) ? "yes" : "no " ); + p += sprintf(p, "UDMA enabled: %s %s " + " %s %s\n", + ((reg54&0x11) && (reg55&0x10) && (reg4a&0x01)) ? "5" : + ((reg54&0x11) && (reg4a&0x02)) ? "4" : + ((reg54&0x11) && (reg4a&0x01)) ? "3" : + (reg4a&0x02) ? "2" : + (reg4a&0x01) ? "1" : + (reg4a&0x00) ? "0" : "X", + ((reg54&0x22) && (reg55&0x20) && (reg4a&0x10)) ? "5" : + ((reg54&0x22) && (reg4a&0x20)) ? "4" : + ((reg54&0x22) && (reg4a&0x10)) ? "3" : + (reg4a&0x20) ? "2" : + (reg4a&0x10) ? "1" : + (reg4a&0x00) ? "0" : "X", + ((reg54&0x44) && (reg55&0x40) && (reg4b&0x03)) ? "5" : + ((reg54&0x44) && (reg4b&0x02)) ? "4" : + ((reg54&0x44) && (reg4b&0x01)) ? "3" : + (reg4b&0x02) ? "2" : + (reg4b&0x01) ? "1" : + (reg4b&0x00) ? "0" : "X", + ((reg54&0x88) && (reg55&0x80) && (reg4b&0x30)) ? "5" : + ((reg54&0x88) && (reg4b&0x20)) ? "4" : + ((reg54&0x88) && (reg4b&0x10)) ? "3" : + (reg4b&0x20) ? "2" : + (reg4b&0x10) ? "1" : + (reg4b&0x00) ? "0" : "X"); + + p += sprintf(p, "UDMA\n"); + p += sprintf(p, "DMA\n"); + p += sprintf(p, "PIO\n"); + +/* + * FIXME.... Add configuration junk data....blah blah...... + */ + + return p-buffer; /* => must be less than 4k! */ +} +#endif /* defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS) */ + +/* + * Used to set Fifo configuration via kernel command line: + */ + +byte piix_proc = 0; + + +static byte piix_ratemask (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + byte mode = 0x00; + + switch(dev->device) { + case PCI_DEVICE_ID_INTEL_82801BA_8: + case PCI_DEVICE_ID_INTEL_82801BA_9: + case PCI_DEVICE_ID_INTEL_82801CA_10: + case PCI_DEVICE_ID_INTEL_82801CA_11: + case PCI_DEVICE_ID_INTEL_82801E_11: + case PCI_DEVICE_ID_INTEL_82801DB_11: + mode |= 0x03; + break; + case PCI_DEVICE_ID_INTEL_82801AA_1: + case PCI_DEVICE_ID_INTEL_82372FB_1: + mode |= 0x02; + break; + case PCI_DEVICE_ID_INTEL_82371AB: + case PCI_DEVICE_ID_INTEL_82443MX_1: + case PCI_DEVICE_ID_INTEL_82451NX: + case PCI_DEVICE_ID_INTEL_82801AB_1: + mode |= 0x01; + case PCI_DEVICE_ID_INTEL_82371SB_1: + case PCI_DEVICE_ID_INTEL_82371FB_1: + case PCI_DEVICE_ID_INTEL_82371FB_0: + case PCI_DEVICE_ID_INTEL_82371MX: + default: + return (mode &= ~0xF8); + } + if (!eighty_ninty_three(drive)) { + mode &= ~0xFE; + mode |= 0x01; + } + return (mode &= ~0xF8); +} + +static byte piix_ratefilter (ide_drive_t *drive, byte speed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + byte mode = piix_ratemask(drive); + + switch(mode) { + case 0x04: while (speed > XFER_UDMA_6) speed--; break; + case 0x03: while (speed > XFER_UDMA_5) speed--; break; + case 0x02: while (speed > XFER_UDMA_4) speed--; break; + case 0x01: while (speed > XFER_UDMA_2) speed--; break; + case 0x00: + default: while (speed > XFER_MW_DMA_2) speed--; break; + break; + } +#else + while (speed > XFER_PIO_4) speed--; +#endif /* CONFIG_BLK_DEV_IDEDMA */ +// printk("%s: mode == %02x speed == %02x\n", drive->name, mode, speed); + return speed; +} + +static byte piix_dma_2_pio (byte xfer_rate) { + switch(xfer_rate) { + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + case XFER_MW_DMA_2: + case XFER_PIO_4: + return 4; + case XFER_MW_DMA_1: + case XFER_PIO_3: + return 3; + case XFER_SW_DMA_2: + case XFER_PIO_2: + return 2; + case XFER_MW_DMA_0: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + case XFER_PIO_1: + case XFER_PIO_0: + case XFER_PIO_SLOW: + default: + return 0; + } +} + +/* + * Based on settings done by AMI BIOS + * (might be useful if drive is not registered in CMOS for any reason). + */ +static void piix_tune_drive (ide_drive_t *drive, byte pio) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + int is_slave = (&hwif->drives[1] == drive); + int master_port = hwif->channel ? 0x42 : 0x40; + int slave_port = 0x44; + unsigned long flags; + u16 master_data; + byte slave_data; + /* ISP RTC */ + byte timings[][2] = { { 0, 0 }, + { 0, 0 }, + { 1, 0 }, + { 2, 1 }, + { 2, 3 }, }; + + pio = ide_get_best_pio_mode(drive, pio, 5, NULL); + spin_lock_irqsave(&ide_lock, flags); + pci_read_config_word(dev, master_port, &master_data); + if (is_slave) { + master_data = master_data | 0x4000; + if (pio > 1) + /* enable PPE, IE and TIME */ + master_data = master_data | 0x0070; + pci_read_config_byte(dev, slave_port, &slave_data); + slave_data = slave_data & (hwif->channel ? 0x0f : 0xf0); + slave_data = slave_data | (((timings[pio][0] << 2) | timings[pio][1]) << (hwif->channel ? 4 : 0)); + } else { + master_data = master_data & 0xccf8; + if (pio > 1) + /* enable PPE, IE and TIME */ + master_data = master_data | 0x0007; + master_data = master_data | (timings[pio][0] << 12) | (timings[pio][1] << 8); + } + pci_write_config_word(dev, master_port, master_data); + if (is_slave) + pci_write_config_byte(dev, slave_port, slave_data); + spin_unlock_irqrestore(&ide_lock, flags); +} + +static int piix_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte maslave = hwif->channel ? 0x42 : 0x40; + byte speed = piix_ratefilter(drive, xferspeed); + int a_speed = 3 << (drive->dn * 4); + int u_flag = 1 << drive->dn; + int v_flag = 0x01 << drive->dn; + int w_flag = 0x10 << drive->dn; + int u_speed = 0; + int sitre; + short reg4042, reg44, reg48, reg4a, reg54; + byte reg55; + + pci_read_config_word(dev, maslave, ®4042); + sitre = (reg4042 & 0x4000) ? 1 : 0; + pci_read_config_word(dev, 0x44, ®44); + pci_read_config_word(dev, 0x48, ®48); + pci_read_config_word(dev, 0x4a, ®4a); + pci_read_config_word(dev, 0x54, ®54); + pci_read_config_byte(dev, 0x55, ®55); + + switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA + case XFER_UDMA_4: + case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break; + case XFER_UDMA_5: + case XFER_UDMA_3: + case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break; + case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break; + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_SW_DMA_2: break; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_0: break; + default: return -1; + } + + if (speed >= XFER_UDMA_0) { + if (!(reg48 & u_flag)) + pci_write_config_word(dev, 0x48, reg48|u_flag); + if (speed == XFER_UDMA_5) { + pci_write_config_byte(dev, 0x55, (byte) reg55|w_flag); + } else { + pci_write_config_byte(dev, 0x55, (byte) reg55 & ~w_flag); + } + if (!(reg4a & u_speed)) { + pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); + pci_write_config_word(dev, 0x4a, reg4a|u_speed); + } + if (speed > XFER_UDMA_2) { + if (!(reg54 & v_flag)) { + pci_write_config_word(dev, 0x54, reg54|v_flag); + } + } else { + pci_write_config_word(dev, 0x54, reg54 & ~v_flag); + } + } else { + if (reg48 & u_flag) + pci_write_config_word(dev, 0x48, reg48 & ~u_flag); + if (reg4a & a_speed) + pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); + if (reg54 & v_flag) + pci_write_config_word(dev, 0x54, reg54 & ~v_flag); + if (reg55 & w_flag) + pci_write_config_byte(dev, 0x55, (byte) reg55 & ~w_flag); + } + + piix_tune_drive(drive, piix_dma_2_pio(speed)); + return (ide_config_drive_speed(drive, speed)); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static int piix_config_drive_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + byte mode = piix_ratemask(drive); + byte speed, tspeed, dma = 1; + + switch(mode) { + case 0x03: + if (id->dma_ultra & 0x0040) + { speed = XFER_UDMA_5; break; } + if (id->dma_ultra & 0x0020) + { speed = XFER_UDMA_5; break; } + case 0x02: + if (id->dma_ultra & 0x0010) + { speed = XFER_UDMA_4; break; } + if (id->dma_ultra & 0x0008) + { speed = XFER_UDMA_3; break; } + case 0x01: + if (id->dma_ultra & 0x0004) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0002) + { speed = XFER_UDMA_1; break; } + if (id->dma_ultra & 0x0001) + { speed = XFER_UDMA_0; break; } + case 0x00: + if (id->dma_mword & 0x0004) + { speed = XFER_MW_DMA_2; break; } + if (id->dma_mword & 0x0002) + { speed = XFER_MW_DMA_1; break; } + if (id->dma_1word & 0x0004) + { speed = XFER_SW_DMA_2; break; } + default: + tspeed = ide_get_best_pio_mode(drive, 255, 5, NULL); + speed = piix_dma_2_pio(XFER_PIO_0 + tspeed); + dma = 0; + break; + } + + (void) piix_tune_chipset(drive, speed); + +// return ((int) (dma) ? ide_dma_on : ide_dma_off_quietly); + return ((int) ((id->dma_ultra >> 11) & 7) ? ide_dma_on : + ((id->dma_ultra >> 8) & 7) ? ide_dma_on : + ((id->dma_mword >> 8) & 7) ? ide_dma_on : + ((id->dma_1word >> 8) & 7) ? ide_dma_on : + ide_dma_off_quietly); +} + +static int config_drive_xfer_rate (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_dma_action_t dma_func = ide_dma_on; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && HWIF(drive)->autodma) { + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + goto fast_ata_pio; + } + dma_func = ide_dma_off_quietly; + if (id->field_valid & 4) { + if (id->dma_ultra & 0x007F) { + /* Force if Capable UltraDMA */ + dma_func = piix_config_drive_for_dma(drive); + if ((id->field_valid & 2) && + (dma_func != ide_dma_on)) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & 0x0007) || + (id->dma_1word & 0x007)) { + /* Force if Capable regular DMA modes */ + dma_func = piix_config_drive_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } + } else if (ide_dmaproc(ide_dma_good_drive, drive)) { + if (id->eide_dma_time > 150) { + goto no_dma_set; + } + /* Consult the list of known "good" drives */ + dma_func = piix_config_drive_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + dma_func = ide_dma_off_quietly; +no_dma_set: + piix_tune_drive(drive, 255); + } + return HWIF(drive)->dmaproc(dma_func, drive); +} + +static int piix_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + return config_drive_xfer_rate(drive); + default : + break; + } + /* Other cases are done by generic IDE-DMA code. */ + return ide_dmaproc(func, drive); +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +unsigned int __init pci_init_piix (struct pci_dev *dev, const char *name) +{ + switch(dev->device) { + case PCI_DEVICE_ID_INTEL_82801AA_1: + case PCI_DEVICE_ID_INTEL_82801AB_1: + case PCI_DEVICE_ID_INTEL_82801BA_8: + case PCI_DEVICE_ID_INTEL_82801BA_9: + case PCI_DEVICE_ID_INTEL_82801CA_10: + case PCI_DEVICE_ID_INTEL_82801CA_11: + case PCI_DEVICE_ID_INTEL_82801E_11: + case PCI_DEVICE_ID_INTEL_82801DB_11: + { + unsigned int extra = 0; + pci_read_config_dword(dev, 0x54, &extra); + pci_write_config_dword(dev, 0x54, extra|0x400); + } + default: + break; + } + +#if defined(DISPLAY_PIIX_TIMINGS) && defined(CONFIG_PROC_FS) + if (!piix_proc) { + piix_proc = 1; + bmide_dev = dev; + piix_display_info = &piix_get_info; + } +#endif /* DISPLAY_PIIX_TIMINGS && CONFIG_PROC_FS */ + return 0; +} + +/* + * Sheesh, someone at Intel needs to go read the ATA-4/5 T13 standards. + * It does not specify device detection, but channel!!! + * You determine later if bit 13 of word93 is set... + */ +unsigned int __init ata66_piix (ide_hwif_t *hwif) +{ + byte reg54h = 0, reg55h = 0, ata66 = 0; + byte mask = hwif->channel ? 0xc0 : 0x30; + + pci_read_config_byte(hwif->pci_dev, 0x54, ®54h); + pci_read_config_byte(hwif->pci_dev, 0x55, ®55h); + + ata66 = (reg54h & mask) ? 1 : 0; + + return ata66; +} + +void __init ide_init_piix (ide_hwif_t *hwif) +{ +#ifndef CONFIG_IA64 + if (!hwif->irq) + hwif->irq = hwif->channel ? 15 : 14; +#endif /* CONFIG_IA64 */ + + if (hwif->pci_dev->device == PCI_DEVICE_ID_INTEL_82371MX) { + /* This is a painful system best to let it self tune for now */ + return; + } + + hwif->autodma = 0; + hwif->tuneproc = &piix_tune_drive; + hwif->speedproc = &piix_tune_chipset; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + + if (!hwif->dma_base) + return; + +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->dmaproc = &piix_dmaproc; +#ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +#endif /* CONFIG_IDEDMA_AUTO */ +#endif /* !CONFIG_BLK_DEV_IDEDMA */ +} + +extern void ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d); + +void __init fixup_device_piix (struct pci_dev *dev, ide_pci_device_t *d) +{ + if (dev->resource[0].start != 0x01F1) + ide_register_xp_fix(dev); + + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); +} diff -Nru a/drivers/ide/q40ide.c b/drivers/ide/q40ide.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/q40ide.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,91 @@ +/* + * linux/drivers/ide/q40ide.c -- Q40 I/O port IDE Driver + * + * (c) Richard Zidlicky + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file COPYING in the main directory of this archive for + * more details. + * + * + */ + +#include +#include +#include +#include +#include + +#include + + /* + * Bases of the IDE interfaces + */ + +#define Q40IDE_NUM_HWIFS 2 + +#define PCIDE_BASE1 0x1f0 +#define PCIDE_BASE2 0x170 +#define PCIDE_BASE3 0x1e8 +#define PCIDE_BASE4 0x168 +#define PCIDE_BASE5 0x1e0 +#define PCIDE_BASE6 0x160 + +static const q40ide_ioreg_t pcide_bases[Q40IDE_NUM_HWIFS] = { + PCIDE_BASE1, PCIDE_BASE2, /* PCIDE_BASE3, PCIDE_BASE4 , PCIDE_BASE5, + PCIDE_BASE6 */ +}; + + + /* + * Offsets from one of the above bases + */ + + +/* HD_DATA was redefined in asm-m68k/ide.h */ +#undef HD_DATA +#define HD_DATA 0x1f0 + + +#define PCIDE_REG(x) ((q40ide_ioreg_t)(HD_##x-PCIDE_BASE1)) + +static const int pcide_offsets[IDE_NR_PORTS] = { + PCIDE_REG(DATA), PCIDE_REG(ERROR), PCIDE_REG(NSECTOR), PCIDE_REG(SECTOR), + PCIDE_REG(LCYL), PCIDE_REG(HCYL), PCIDE_REG(CURRENT), PCIDE_REG(STATUS), + PCIDE_REG(CMD) +}; + +static int q40ide_default_irq(q40ide_ioreg_t base) +{ + switch (base) { + case 0x1f0: return 14; + case 0x170: return 15; + case 0x1e8: return 11; + default: + return 0; + } +} + + + + /* + * Probe for Q40 IDE interfaces + */ + +void q40ide_init(void) +{ + int i; + + if (!MACH_IS_Q40) + return ; + + for (i = 0; i < Q40IDE_NUM_HWIFS; i++) { + hw_regs_t hw; + + ide_setup_ports(&hw,(ide_ioreg_t) pcide_bases[i], (int *)pcide_offsets, + pcide_bases[i]+0x206, + 0, NULL, q40ide_default_irq(pcide_bases[i])); + ide_register_hw(&hw, NULL); + } +} + diff -Nru a/drivers/ide/qd65xx.c b/drivers/ide/qd65xx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/qd65xx.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,453 @@ +/* + * linux/drivers/ide/qd65xx.c Version 0.07 Sep 30, 2001 + * + * Copyright (C) 1996-2001 Linus Torvalds & author (see below) + */ + +/* + * Version 0.03 Cleaned auto-tune, added probe + * Version 0.04 Added second channel tuning + * Version 0.05 Enhanced tuning ; added qd6500 support + * Version 0.06 Added dos driver's list + * Version 0.07 Second channel bug fix + * + * QDI QD6500/QD6580 EIDE controller fast support + * + * Please set local bus speed using kernel parameter idebus + * for example, "idebus=33" stands for 33Mhz VLbus + * To activate controller support, use "ide0=qd65xx" + * To enable tuning, use "ide0=autotune" + * To enable second channel tuning (qd6580 only), use "ide1=autotune" + */ + +/* + * Rewritten from the work of Colten Edwards by + * Samuel Thibault + */ + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ide_modes.h" +#include "qd65xx.h" + +/* + * I/O ports are 0x30-0x31 (and 0x32-0x33 for qd6580) + * or 0xb0-0xb1 (and 0xb2-0xb3 for qd6580) + * -- qd6500 is a single IDE interface + * -- qd6580 is a dual IDE interface + * + * More research on qd6580 being done by willmore@cig.mot.com (David) + * More Information given by Petr Soucek (petr@ryston.cz) + * http://www.ryston.cz/petr/vlb + */ + +/* + * base: Timer1 + * + * + * base+0x01: Config (R/O) + * + * bit 0: ide baseport: 1 = 0x1f0 ; 0 = 0x170 (only useful for qd6500) + * bit 1: qd65xx baseport: 1 = 0xb0 ; 0 = 0x30 + * bit 2: ID3: bus speed: 1 = <=33MHz ; 0 = >33MHz + * bit 3: qd6500: 1 = disabled, 0 = enabled + * qd6580: 1 + * upper nibble: + * qd6500: 1100 + * qd6580: either 1010 or 0101 + * + * + * base+0x02: Timer2 (qd6580 only) + * + * + * base+0x03: Control (qd6580 only) + * + * bits 0-3 must always be set 1 + * bit 4 must be set 1, but is set 0 by dos driver while measuring vlb clock + * bit 0 : 1 = Only primary port enabled : channel 0 for hda, channel 1 for hdb + * 0 = Primary and Secondary ports enabled : channel 0 for hda & hdb + * channel 1 for hdc & hdd + * bit 1 : 1 = only disks on primary port + * 0 = disks & ATAPI devices on primary port + * bit 2-4 : always 0 + * bit 5 : status, but of what ? + * bit 6 : always set 1 by dos driver + * bit 7 : set 1 for non-ATAPI devices on primary port + * (maybe read-ahead and post-write buffer ?) + */ + +static int timings[4]={-1,-1,-1,-1}; /* stores current timing for each timer */ + +static void qd_write_reg (byte content, byte reg) +{ + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + OUT_BYTE(content,reg); + spin_unlock_irqrestore(&ide_lock, flags); +} + +byte __init qd_read_reg (byte reg) +{ + unsigned long flags; + byte read; + + spin_lock_irqsave(&ide_lock, flags); + read = IN_BYTE(reg); + spin_unlock_irqrestore(&ide_lock, flags); + return read; +} + +/* + * qd_select: + * + * This routine is invoked from ide.c to prepare for access to a given drive. + */ + +static void qd_select (ide_drive_t *drive) +{ + byte index = (( (QD_TIMREG(drive)) & 0x80 ) >> 7) | + (QD_TIMREG(drive) & 0x02); + + if (timings[index] != QD_TIMING(drive)) + qd_write_reg(timings[index] = QD_TIMING(drive), QD_TIMREG(drive)); +} + +/* + * qd6500_compute_timing + * + * computes the timing value where + * lower nibble represents active time, in count of VLB clocks + * upper nibble represents recovery time, in count of VLB clocks + */ + +static byte qd6500_compute_timing (ide_hwif_t *hwif, int active_time, int recovery_time) +{ + byte active_cycle,recovery_cycle; + + if (ide_system_bus_speed()<=33) { + active_cycle = 9 - IDE_IN(active_time * ide_system_bus_speed() / 1000 + 1, 2, 9); + recovery_cycle = 15 - IDE_IN(recovery_time * ide_system_bus_speed() / 1000 + 1, 0, 15); + } else { + active_cycle = 8 - IDE_IN(active_time * ide_system_bus_speed() / 1000 + 1, 1, 8); + recovery_cycle = 18 - IDE_IN(recovery_time * ide_system_bus_speed() / 1000 + 1, 3, 18); + } + + return((recovery_cycle<<4) | 0x08 | active_cycle); +} + +/* + * qd6580_compute_timing + * + * idem for qd6580 + */ + +static byte qd6580_compute_timing (int active_time, int recovery_time) +{ + byte active_cycle = 17-IDE_IN(active_time * ide_system_bus_speed() / 1000 + 1, 2, 17); + byte recovery_cycle = 15-IDE_IN(recovery_time * ide_system_bus_speed() / 1000 + 1, 2, 15); + + return((recovery_cycle<<4) | active_cycle); +} + +/* + * qd_find_disk_type + * + * tries to find timing from dos driver's table + */ + +static int qd_find_disk_type (ide_drive_t *drive, + int *active_time, int *recovery_time) +{ + struct qd65xx_timing_s *p; + char model[40]; + + if (!*drive->id->model) return 0; + + strncpy(model,drive->id->model,40); + ide_fixstring(model,40,1); /* byte-swap */ + + for (p = qd65xx_timing ; p->offset != -1 ; p++) { + if (!strncmp(p->model, model+p->offset,4)) { + printk(KERN_DEBUG "%s: listed !\n",drive->name); + *active_time = p->active; + *recovery_time = p->recovery; + return 1; + } + } + return 0; +} + +/* + * qd_timing_ok: + * + * check whether timings don't conflict + */ + +static int qd_timing_ok (ide_drive_t drives[]) +{ + return (IDE_IMPLY(drives[0].present && drives[1].present, + IDE_IMPLY(QD_TIMREG(drives) == QD_TIMREG(drives+1), + QD_TIMING(drives) == QD_TIMING(drives+1)))); + /* if same timing register, must be same timing */ +} + +/* + * qd_set_timing: + * + * records the timing, and enables selectproc as needed + */ + +static void qd_set_timing (ide_drive_t *drive, byte timing) +{ + ide_hwif_t *hwif = HWIF(drive); + + drive->drive_data &= 0xff00; + drive->drive_data |= timing; + if (qd_timing_ok(hwif->drives)) { + qd_select(drive); /* selects once */ + hwif->selectproc = NULL; + } else + hwif->selectproc = &qd_select; + + printk(KERN_DEBUG "%s: %#x\n",drive->name,timing); +} + +/* + * qd6500_tune_drive + */ + +static void qd6500_tune_drive (ide_drive_t *drive, byte pio) +{ + int active_time = 175; + int recovery_time = 415; /* worst case values from the dos driver */ + + if (drive->id && !qd_find_disk_type(drive,&active_time,&recovery_time) + && drive->id->tPIO && (drive->id->field_valid & 0x02) + && drive->id->eide_pio >= 240) { + + printk(KERN_INFO "%s: PIO mode%d\n", drive->name, + drive->id->tPIO); + active_time = 110; + recovery_time = drive->id->eide_pio - 120; + } + + qd_set_timing(drive,qd6500_compute_timing(HWIF(drive),active_time,recovery_time)); +} + +/* + * qd6580_tune_drive + */ + +static void qd6580_tune_drive (ide_drive_t *drive, byte pio) +{ + ide_pio_data_t d; + int base = HWIF(drive)->select_data; + int active_time = 175; + int recovery_time = 415; /* worst case values from the dos driver */ + + if (drive->id && !qd_find_disk_type(drive,&active_time,&recovery_time)) { + pio = ide_get_best_pio_mode(drive, pio, 255, &d); + pio = IDE_MIN(pio,4); + + switch (pio) { + case 0: break; + case 3: + if (d.cycle_time >= 110) { + active_time = 86; + recovery_time = d.cycle_time-102; + } else + printk(KERN_WARNING "%s: Strange recovery time !\n",drive->name); + break; + case 4: + if (d.cycle_time >= 69) { + active_time = 70; + recovery_time = d.cycle_time-61; + } else + printk(KERN_WARNING "%s: Strange recovery time !\n",drive->name); + break; + default: + if (d.cycle_time >= 180) { + active_time = 110; + recovery_time = d.cycle_time - 120; + } else { + active_time = ide_pio_timings[pio].active_time; + recovery_time = d.cycle_time + -active_time; + } + } + printk(KERN_INFO "%s: PIO mode%d\n",drive->name,pio); + } + + if (!HWIF(drive)->channel && drive->media != ide_disk) { + qd_write_reg(0x5f,QD_CONTROL_PORT); + printk(KERN_WARNING "%s: ATAPI: disabled read-ahead FIFO and post-write buffer on %s.\n",drive->name,HWIF(drive)->name); + } + + qd_set_timing(drive,qd6580_compute_timing(active_time,recovery_time)); +} + +/* + * qd_testreg + * + * tests if the given port is a register + */ + +static int __init qd_testreg(int port) +{ + byte savereg; + byte readreg; + unsigned long flags; + + spin_lock_irqsave(&ide_lock, flags); + savereg = inb_p(port); + outb_p(QD_TESTVAL,port); /* safe value */ + readreg = inb_p(port); + OUT_BYTE(savereg,port); + spin_unlock_irqrestore(&ide_lock, flags); + + if (savereg == QD_TESTVAL) { + printk(KERN_ERR "Outch ! the probe for qd65xx isn't reliable !\n"); + printk(KERN_ERR "Please contact maintainers to tell about your hardware\n"); + printk(KERN_ERR "Assuming qd65xx is not present.\n"); + return 1; + } + + return (readreg != QD_TESTVAL); +} + +/* + * probe: + * + * looks at the specified baseport, and if qd found, registers & initialises it + * return 1 if another qd may be probed + */ + +int __init probe (int base) +{ + byte config; + byte index; + + config = qd_read_reg(QD_CONFIG_PORT); + + if (! ((config & QD_CONFIG_BASEPORT) >> 1 == (base == 0xb0)) ) return 1; + + index = ! (config & QD_CONFIG_IDE_BASEPORT); + + if ((config & 0xf0) == QD_CONFIG_QD6500) { + ide_hwif_t *hwif = &ide_hwifs[index]; + + if (qd_testreg(base)) return 1; /* bad register */ + + /* qd6500 found */ + + printk(KERN_NOTICE "%s: qd6500 at %#x\n", + ide_hwifs[index].name, base); + + printk(KERN_DEBUG "qd6500: config=%#x, ID3=%u\n", + config, QD_ID3); + + if (config & QD_CONFIG_DISABLED) { + printk(KERN_WARNING "qd6500 is disabled !\n"); + return 1; + } + + hwif->chipset = ide_qd65xx; + hwif->select_data = base; + hwif->config_data = config; + hwif->drives[0].drive_data = + hwif->drives[1].drive_data = QD6500_DEF_DATA; + hwif->drives[0].io_32bit = + hwif->drives[1].io_32bit = 1; + hwif->tuneproc = &qd6500_tune_drive; + return 1; + } + + if (((config & 0xf0) == QD_CONFIG_QD6580_A) || ((config & 0xf0) == QD_CONFIG_QD6580_B)) { + + byte control; + + if (qd_testreg(base) || qd_testreg(base+0x02)) return 1; + /* bad registers */ + + /* qd6580 found */ + + control = qd_read_reg(QD_CONTROL_PORT); + + printk(KERN_NOTICE "qd6580 at %#x\n", base); + printk(KERN_DEBUG "qd6580: config=%#x, control=%#x, ID3=%u\n", + config, control, QD_ID3); + + if (control & QD_CONTR_SEC_DISABLED) { + ide_hwif_t *hwif = &ide_hwifs[index]; + + /* secondary disabled */ + printk(KERN_INFO "%s: qd6580: single IDE board\n", + ide_hwifs[index].name); + + hwif->chipset = ide_qd65xx; + hwif->select_data = base; + hwif->config_data = config | (control <<8); + hwif->drives[0].drive_data = + hwif->drives[1].drive_data = QD6580_DEF_DATA; + hwif->drives[0].io_32bit = + hwif->drives[1].io_32bit = 1; + hwif->tuneproc = &qd6580_tune_drive; + + qd_write_reg(QD_DEF_CONTR,QD_CONTROL_PORT); + + return 1; + } else { + int i,j; + /* secondary enabled */ + printk(KERN_INFO "%s&%s: qd6580: dual IDE board\n", + ide_hwifs[0].name,ide_hwifs[1].name); + + for (i=0;i<2;i++) { + + ide_hwifs[i].chipset = ide_qd65xx; + ide_hwifs[i].mate = &ide_hwifs[i^1]; + ide_hwifs[i].channel = i; + + ide_hwifs[i].select_data = base; + ide_hwifs[i].config_data = config | (control <<8); + ide_hwifs[i].tuneproc = &qd6580_tune_drive; + + for (j=0;j<2;j++) { + ide_hwifs[i].drives[j].drive_data = + i?QD6580_DEF_DATA2:QD6580_DEF_DATA; + ide_hwifs[i].drives[j].io_32bit = 1; + } + } + + qd_write_reg(QD_DEF_CONTR,QD_CONTROL_PORT); + + return 0; /* no other qd65xx possible */ + } + } + /* no qd65xx found */ + return 1; +} + +/* + * init_qd65xx: + * + * called at the very beginning of initialization ; should just probe and link + */ + +void __init init_qd65xx (void) +{ + if (probe(0x30)) probe(0xb0); +} diff -Nru a/drivers/ide/qd65xx.h b/drivers/ide/qd65xx.h --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/qd65xx.h Fri Aug 16 14:35:01 2002 @@ -0,0 +1,140 @@ +/* + * linux/drivers/ide/qd65xx.h + * + * Copyright (c) 2000 Linus Torvalds & authors + */ + +/* + * Authors: Petr Soucek + * Samuel Thibault + */ + +/* truncates a in [b,c] */ +#define IDE_IN(a,b,c) ( ((a)<(b)) ? (b) : ( (a)>(c) ? (c) : (a)) ) + +#define IDE_IMPLY(a,b) ((!(a)) || (b)) + +#define QD_TIM1_PORT (base) +#define QD_CONFIG_PORT (base+0x01) +#define QD_TIM2_PORT (base+0x02) +#define QD_CONTROL_PORT (base+0x03) + +#define QD_CONFIG_IDE_BASEPORT 0x01 +#define QD_CONFIG_BASEPORT 0x02 +#define QD_CONFIG_ID3 0x04 +#define QD_CONFIG_DISABLED 0x08 +#define QD_CONFIG_QD6500 0xc0 +#define QD_CONFIG_QD6580_A 0xa0 +#define QD_CONFIG_QD6580_B 0x50 + +#define QD_CONTR_SEC_DISABLED 0x01 + +#define QD_ID3 ((config & QD_CONFIG_ID3)!=0) + +#define QD_CONFIG(hwif) ((hwif)->config_data & 0x00ff) +#define QD_CONTROL(hwif) (((hwif)->config_data & 0xff00) >> 8) + +#define QD_TIMING(drive) (byte)(((drive)->drive_data) & 0x00ff) +#define QD_TIMREG(drive) (byte)((((drive)->drive_data) & 0xff00) >> 8) + +#define QD6500_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0c : 0x08)) +#define QD6580_DEF_DATA ((QD_TIM1_PORT<<8) | (QD_ID3 ? 0x0a : 0x00)) +#define QD6580_DEF_DATA2 ((QD_TIM2_PORT<<8) | (QD_ID3 ? 0x0a : 0x00)) +#define QD_DEF_CONTR (0x40 | ((control & 0x02) ? 0x9f : 0x1f)) + +#define QD_TESTVAL 0x19 /* safe value */ + +/* Drive specific timing taken from DOS driver v3.7 */ + +struct qd65xx_timing_s { + char offset; /* ofset from the beginning of Model Number" */ + char model[4]; /* 4 chars from Model number, no conversion */ + short active; /* active time */ + short recovery; /* recovery time */ +} qd65xx_timing [] = { + { 30, "2040", 110, 225 }, /* Conner CP30204 */ + { 30, "2045", 135, 225 }, /* Conner CP30254 */ + { 30, "1040", 155, 325 }, /* Conner CP30104 */ + { 30, "1047", 135, 265 }, /* Conner CP30174 */ + { 30, "5344", 135, 225 }, /* Conner CP3544 */ + { 30, "01 4", 175, 405 }, /* Conner CP-3104 */ + { 27, "C030", 175, 375 }, /* Conner CP3000 */ + { 8, "PL42", 110, 295 }, /* Quantum LP240 */ + { 8, "PL21", 110, 315 }, /* Quantum LP120 */ + { 8, "PL25", 175, 385 }, /* Quantum LP52 */ + { 4, "PA24", 110, 285 }, /* WD Piranha SP4200 */ + { 6, "2200", 110, 260 }, /* WD Caviar AC2200 */ + { 6, "3204", 110, 235 }, /* WD Caviar AC2340 */ + { 6, "1202", 110, 265 }, /* WD Caviar AC2120 */ + { 0, "DS3-", 135, 315 }, /* Teac SD340 */ + { 8, "KM32", 175, 355 }, /* Toshiba MK234 */ + { 2, "53A1", 175, 355 }, /* Seagate ST351A */ + { 2, "4108", 175, 295 }, /* Seagate ST1480A */ + { 2, "1344", 175, 335 }, /* Seagate ST3144A */ + { 6, "7 12", 110, 225 }, /* Maxtor 7213A */ + { 30, "02F4", 145, 295 }, /* Conner 3204F */ + { 2, "1302", 175, 335 }, /* Seagate ST3120A */ + { 2, "2334", 145, 265 }, /* Seagate ST3243A */ + { 2, "2338", 145, 275 }, /* Seagate ST3283A */ + { 2, "3309", 145, 275 }, /* Seagate ST3390A */ + { 2, "5305", 145, 275 }, /* Seagate ST3550A */ + { 2, "4100", 175, 295 }, /* Seagate ST1400A */ + { 2, "4110", 175, 295 }, /* Seagate ST1401A */ + { 2, "6300", 135, 265 }, /* Seagate ST3600A */ + { 2, "5300", 135, 265 }, /* Seagate ST3500A */ + { 6, "7 31", 135, 225 }, /* Maxtor 7131 AT */ + { 6, "7 43", 115, 265 }, /* Maxtor 7345 AT */ + { 6, "7 42", 110, 255 }, /* Maxtor 7245 AT */ + { 6, "3 04", 135, 265 }, /* Maxtor 340 AT */ + { 6, "61 0", 135, 285 }, /* WD AC160 */ + { 6, "1107", 135, 235 }, /* WD AC1170 */ + { 6, "2101", 110, 220 }, /* WD AC1210 */ + { 6, "4202", 135, 245 }, /* WD AC2420 */ + { 6, "41 0", 175, 355 }, /* WD Caviar 140 */ + { 6, "82 0", 175, 355 }, /* WD Caviar 280 */ + { 8, "PL01", 175, 375 }, /* Quantum LP105 */ + { 8, "PL25", 110, 295 }, /* Quantum LP525 */ + { 10, "4S 2", 175, 385 }, /* Quantum ELS42 */ + { 10, "8S 5", 175, 385 }, /* Quantum ELS85 */ + { 10, "1S72", 175, 385 }, /* Quantum ELS127 */ + { 10, "1S07", 175, 385 }, /* Quantum ELS170 */ + { 8, "ZE42", 135, 295 }, /* Quantum EZ240 */ + { 8, "ZE21", 175, 385 }, /* Quantum EZ127 */ + { 8, "ZE58", 175, 385 }, /* Quantum EZ85 */ + { 8, "ZE24", 175, 385 }, /* Quantum EZ42 */ + { 27, "C036", 155, 325 }, /* Conner CP30064 */ + { 27, "C038", 155, 325 }, /* Conner CP30084 */ + { 6, "2205", 110, 255 }, /* WDC AC2250 */ + { 2, " CHA", 140, 415 }, /* WDC AH series; WDC AH260, WDC */ + { 2, " CLA", 140, 415 }, /* WDC AL series: WDC AL2120, 2170, */ + { 4, "UC41", 140, 415 }, /* WDC CU140 */ + { 6, "1207", 130, 275 }, /* WDC AC2170 */ + { 6, "2107", 130, 275 }, /* WDC AC1270 */ + { 6, "5204", 130, 275 }, /* WDC AC2540 */ + { 30, "3004", 110, 235 }, /* Conner CP30340 */ + { 30, "0345", 135, 255 }, /* Conner CP30544 */ + { 12, "12A3", 175, 320 }, /* MAXTOR LXT-213A */ + { 12, "43A0", 145, 240 }, /* MAXTOR LXT-340A */ + { 6, "7 21", 180, 290 }, /* Maxtor 7120 AT */ + { 6, "7 71", 135, 240 }, /* Maxtor 7170 AT */ + { 12, "45\0000", 110, 205 }, /* MAXTOR MXT-540 */ + { 8, "PL11", 180, 290 }, /* QUANTUM LP110A */ + { 8, "OG21", 150, 275 }, /* QUANTUM GO120 */ + { 12, "42A5", 175, 320 }, /* MAXTOR LXT-245A */ + { 2, "2309", 175, 295 }, /* ST3290A */ + { 2, "3358", 180, 310 }, /* ST3385A */ + { 2, "6355", 180, 310 }, /* ST3655A */ + { 2, "1900", 175, 270 }, /* ST9100A */ + { 2, "1954", 175, 270 }, /* ST9145A */ + { 2, "1909", 175, 270 }, /* ST9190AG */ + { 2, "2953", 175, 270 }, /* ST9235A */ + { 2, "1359", 175, 270 }, /* ST3195A */ + { 24, "3R11", 175, 290 }, /* ALPS ELECTRIC Co.,LTD, DR311C */ + { 0, "2M26", 175, 215 }, /* M262XT-0Ah */ + { 4, "2253", 175, 300 }, /* HP C2235A */ + { 4, "-32A", 145, 245 }, /* H3133-A2 */ + { 30, "0326", 150, 270 }, /* Samsung Electronics 120MB */ + { 30, "3044", 110, 195 }, /* Conner CFA340A */ + { 30, "43A0", 110, 195 }, /* Conner CFA340A */ + { -1, " ", 175, 415 } /* unknown disk name */ +}; diff -Nru a/drivers/ide/rapide.c b/drivers/ide/rapide.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/rapide.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,94 @@ +/* + * linux/drivers/ide/rapide.c + * + * Copyright (c) 1996-1998 Russell King. + * + * Changelog: + * 08-06-1996 RMK Created + * 13-04-1998 RMK Added manufacturer and product IDs + */ + +#include +#include +#include +#include +#include +#include + +#include + +static card_ids __init rapide_cids[] = { + { MANU_YELLOWSTONE, PROD_YELLOWSTONE_RAPIDE32 }, + { 0xffff, 0xffff } +}; + +static struct expansion_card *ec[MAX_ECARDS]; +static int result[MAX_ECARDS]; + +static inline int rapide_register(struct expansion_card *ec) +{ + unsigned long port = ecard_address (ec, ECARD_MEMC, 0); + hw_regs_t hw; + + int i; + + memset(&hw, 0, sizeof(hw)); + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) { + hw.io_ports[i] = (ide_ioreg_t)port; + port += 1 << 4; + } + hw.io_ports[IDE_CONTROL_OFFSET] = port + 0x206; + hw.irq = ec->irq; + + return ide_register_hw(&hw, NULL); +} + +int __init rapide_init(void) +{ + int i; + + for (i = 0; i < MAX_ECARDS; i++) + ec[i] = NULL; + + ecard_startfind(); + + for (i = 0; ; i++) { + if ((ec[i] = ecard_find(0, rapide_cids)) == NULL) + break; + + ecard_claim(ec[i]); + result[i] = rapide_register(ec[i]); + } + for (i = 0; i < MAX_ECARDS; i++) + if (ec[i] && result[i] < 0) { + ecard_release(ec[i]); + ec[i] = NULL; + } + return 0; +} + +#ifdef MODULE +MODULE_LICENSE("GPL"); + +int init_module (void) +{ + return rapide_init(); +} + +void cleanup_module (void) +{ + int i; + + for (i = 0; i < MAX_ECARDS; i++) + if (ec[i]) { + unsigned long port; + port = ecard_address(ec[i], ECARD_MEMC, 0); + + ide_unregister_port(port, ec[i]->irq, 16); + ecard_release(ec[i]); + ec[i] = NULL; + } +} +#endif + diff -Nru a/drivers/ide/rz1000.c b/drivers/ide/rz1000.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/rz1000.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,100 @@ +/* + * linux/drivers/ide/rz1000.c Version 0.05 December 8, 1997 + * + * Copyright (C) 1995-1998 Linus Torvalds & author (see below) + */ + +/* + * Principal Author: mlord@pobox.com (Mark Lord) + * + * See linux/MAINTAINERS for address of current maintainer. + * + * This file provides support for disabling the buggy read-ahead + * mode of the RZ1000 IDE chipset, commonly used on Intel motherboards. + * + * Dunno if this fixes both ports, or only the primary port (?). + */ + +#undef REALLY_SLOW_IO /* most systems can safely undef this */ + +#include /* for CONFIG_BLK_DEV_IDEPCI */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef CONFIG_BLK_DEV_IDEPCI + +void __init ide_init_rz1000 (ide_hwif_t *hwif) /* called from ide-pci.c */ +{ + unsigned short reg; + struct pci_dev *dev = hwif->pci_dev; + + hwif->chipset = ide_rz1000; + if (!pci_read_config_word (dev, 0x40, ®) && + !pci_write_config_word(dev, 0x40, reg & 0xdfff)) { + printk("%s: disabled chipset read-ahead " + "(buggy RZ1000/RZ1001)\n", hwif->name); + } else { + hwif->serialized = 1; + hwif->drives[0].no_unmask = 1; + hwif->drives[1].no_unmask = 1; + printk("%s: serialized, disabled unmasking " + "(buggy RZ1000/RZ1001)\n", hwif->name); + } +} + +#else + +static void __init init_rz1000 (struct pci_dev *dev, const char *name) +{ + unsigned short reg, h; + + if (!pci_read_config_word (dev, PCI_COMMAND, ®) && + !(reg & PCI_COMMAND_IO)) { + printk("%s: buggy IDE controller disabled (BIOS)\n", name); + return; + } + if (!pci_read_config_word (dev, 0x40, ®) && + !pci_write_config_word(dev, 0x40, reg & 0xdfff)) { + printk("IDE: disabled chipset read-ahead (buggy %s)\n", name); + } else { + for (h = 0; h < MAX_HWIFS; ++h) { + ide_hwif_t *hwif = &ide_hwifs[h]; + if ((hwif->io_ports[IDE_DATA_OFFSET] == 0x1f0 || + hwif->io_ports[IDE_DATA_OFFSET] == 0x170) && + (hwif->chipset == ide_unknown || + hwif->chipset == ide_generic)) { + hwif->chipset = ide_rz1000; + hwif->serialized = 1; + hwif->drives[0].no_unmask = 1; + hwif->drives[1].no_unmask = 1; + if (hwif->io_ports[IDE_DATA_OFFSET] == 0x170) + hwif->channel = 1; + printk("%s: serialized, disabled unmasking " + "(buggy %s)\n", hwif->name, name); + } + } + } +} + +void __init ide_probe_for_rz100x (void) /* called from ide.c */ +{ + struct pci_dev *dev = NULL; + + while ((dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, dev))!=NULL) + init_rz1000 (dev, "RZ1000"); + while ((dev = pci_find_device(PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1001, dev))!=NULL) + init_rz1000 (dev, "RZ1001"); +} + +#endif /* CONFIG_BLK_DEV_IDEPCI */ diff -Nru a/drivers/ide/serverworks.c b/drivers/ide/serverworks.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/serverworks.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,789 @@ +/* + * linux/drivers/ide/serverworks.c Version 0.6 05 April 2002 + * + * Copyright (C) 1998-2000 Michel Aubry + * Copyright (C) 1998-2000 Andrzej Krzysztofowicz + * Copyright (C) 1998-2000 Andre Hedrick + * Portions copyright (c) 2001 Sun Microsystems + * + * + * RCC/ServerWorks IDE driver for Linux + * + * OSB4: `Open South Bridge' IDE Interface (fn 1) + * supports UDMA mode 2 (33 MB/s) + * + * CSB5: `Champion South Bridge' IDE Interface (fn 1) + * all revisions support UDMA mode 4 (66 MB/s) + * revision A2.0 and up support UDMA mode 5 (100 MB/s) + * + * *** The CSB5 does not provide ANY register *** + * *** to detect 80-conductor cable presence. *** + * + * CSB6: `Champion South Bridge' IDE Interface (optional: third channel) + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ide_modes.h" + +#define DISPLAY_SVWKS_TIMINGS 1 +#undef SVWKS_DEBUG_DRIVE_INFO + +#if defined(DISPLAY_SVWKS_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +#define SVWKS_MAX_DEVS 2 +static struct pci_dev *svwks_devs[SVWKS_MAX_DEVS]; +static int n_svwks_devs; + +static byte svwks_revision = 0; + +static int svwks_get_info(char *, char **, off_t, int); +extern int (*svwks_display_info)(char *, char **, off_t, int); /* ide-proc.c */ + +static int svwks_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + int i; + + p += sprintf(p, "\n " + "ServerWorks OSB4/CSB5/CSB6\n"); + + for (i = 0; i < n_svwks_devs; i++) { + struct pci_dev *dev = svwks_devs[i]; + u32 bibma = pci_resource_start(dev, 4); + u32 reg40, reg44; + u16 reg48, reg56; + u8 reg54, c0=0, c1=0; + + pci_read_config_dword(dev, 0x40, ®40); + pci_read_config_dword(dev, 0x44, ®44); + pci_read_config_word(dev, 0x48, ®48); + pci_read_config_byte(dev, 0x54, ®54); + pci_read_config_word(dev, 0x56, ®56); + + /* + * at that point bibma+0x2 et bibma+0xa are byte registers + * to investigate: + */ + c0 = inb_p((unsigned short)bibma + 0x02); + c1 = inb_p((unsigned short)bibma + 0x0a); + + switch(dev->device) { + case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE: + p += sprintf(p, "\n " + "ServerWorks CSB6 Chipset (rev %02x)\n", + svwks_revision); + break; + case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: + p += sprintf(p, "\n " + "ServerWorks CSB5 Chipset (rev %02x)\n", + svwks_revision); + break; + case PCI_DEVICE_ID_SERVERWORKS_OSB4IDE: + p += sprintf(p, "\n " + "ServerWorks OSB4 Chipset (rev %02x)\n", + svwks_revision); + break; + default: + p += sprintf(p, "\n " + "ServerWorks %04x Chipset (rev %02x)\n", + dev->device, svwks_revision); + break; + } + + p += sprintf(p, "------------------------------- " + "General Status " + "---------------------------------\n"); + p += sprintf(p, "--------------- Primary Channel " + "---------------- Secondary Channel " + "-------------\n"); + p += sprintf(p, " %sabled %sabled\n", + (c0&0x80) ? "dis" : " en", + (c1&0x80) ? "dis" : " en"); + p += sprintf(p, "--------------- drive0 --------- drive1 " + "-------- drive0 ---------- drive1 ------\n"); + p += sprintf(p, "DMA enabled: %s %s" + " %s %s\n", + (c0&0x20) ? "yes" : "no ", + (c0&0x40) ? "yes" : "no ", + (c1&0x20) ? "yes" : "no ", + (c1&0x40) ? "yes" : "no " ); + p += sprintf(p, "UDMA enabled: %s %s" + " %s %s\n", + (reg54 & 0x01) ? "yes" : "no ", + (reg54 & 0x02) ? "yes" : "no ", + (reg54 & 0x04) ? "yes" : "no ", + (reg54 & 0x08) ? "yes" : "no " ); + p += sprintf(p, "UDMA enabled: %s %s" + " %s %s\n", + ((reg56&0x0005)==0x0005)?"5": + ((reg56&0x0004)==0x0004)?"4": + ((reg56&0x0003)==0x0003)?"3": + ((reg56&0x0002)==0x0002)?"2": + ((reg56&0x0001)==0x0001)?"1": + ((reg56&0x000F))?"?":"0", + ((reg56&0x0050)==0x0050)?"5": + ((reg56&0x0040)==0x0040)?"4": + ((reg56&0x0030)==0x0030)?"3": + ((reg56&0x0020)==0x0020)?"2": + ((reg56&0x0010)==0x0010)?"1": + ((reg56&0x00F0))?"?":"0", + ((reg56&0x0500)==0x0500)?"5": + ((reg56&0x0400)==0x0400)?"4": + ((reg56&0x0300)==0x0300)?"3": + ((reg56&0x0200)==0x0200)?"2": + ((reg56&0x0100)==0x0100)?"1": + ((reg56&0x0F00))?"?":"0", + ((reg56&0x5000)==0x5000)?"5": + ((reg56&0x4000)==0x4000)?"4": + ((reg56&0x3000)==0x3000)?"3": + ((reg56&0x2000)==0x2000)?"2": + ((reg56&0x1000)==0x1000)?"1": + ((reg56&0xF000))?"?":"0"); + p += sprintf(p, "DMA enabled: %s %s" + " %s %s\n", + ((reg44&0x00002000)==0x00002000)?"2": + ((reg44&0x00002100)==0x00002100)?"1": + ((reg44&0x00007700)==0x00007700)?"0": + ((reg44&0x0000FF00)==0x0000FF00)?"X":"?", + ((reg44&0x00000020)==0x00000020)?"2": + ((reg44&0x00000021)==0x00000021)?"1": + ((reg44&0x00000077)==0x00000077)?"0": + ((reg44&0x000000FF)==0x000000FF)?"X":"?", + ((reg44&0x20000000)==0x20000000)?"2": + ((reg44&0x21000000)==0x21000000)?"1": + ((reg44&0x77000000)==0x77000000)?"0": + ((reg44&0xFF000000)==0xFF000000)?"X":"?", + ((reg44&0x00200000)==0x00200000)?"2": + ((reg44&0x00210000)==0x00210000)?"1": + ((reg44&0x00770000)==0x00770000)?"0": + ((reg44&0x00FF0000)==0x00FF0000)?"X":"?"); + + p += sprintf(p, "PIO enabled: %s %s" + " %s %s\n", + ((reg40&0x00002000)==0x00002000)?"4": + ((reg40&0x00002200)==0x00002200)?"3": + ((reg40&0x00003400)==0x00003400)?"2": + ((reg40&0x00004700)==0x00004700)?"1": + ((reg40&0x00005D00)==0x00005D00)?"0":"?", + ((reg40&0x00000020)==0x00000020)?"4": + ((reg40&0x00000022)==0x00000022)?"3": + ((reg40&0x00000034)==0x00000034)?"2": + ((reg40&0x00000047)==0x00000047)?"1": + ((reg40&0x0000005D)==0x0000005D)?"0":"?", + ((reg40&0x20000000)==0x20000000)?"4": + ((reg40&0x22000000)==0x22000000)?"3": + ((reg40&0x34000000)==0x34000000)?"2": + ((reg40&0x47000000)==0x47000000)?"1": + ((reg40&0x5D000000)==0x5D000000)?"0":"?", + ((reg40&0x00200000)==0x00200000)?"4": + ((reg40&0x00220000)==0x00220000)?"3": + ((reg40&0x00340000)==0x00340000)?"2": + ((reg40&0x00470000)==0x00470000)?"1": + ((reg40&0x005D0000)==0x005D0000)?"0":"?"); + + } + p += sprintf(p, "\n"); + + return p-buffer; /* => must be less than 4k! */ +} +#endif /* defined(DISPLAY_SVWKS_TIMINGS) && defined(CONFIG_PROC_FS) */ + +#define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */ + +#define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */ + +byte svwks_proc = 0; + +static struct pci_dev *isa_dev; + +static byte svwks_ratemask (ide_drive_t *drive) +{ + struct pci_dev *dev = HWIF(drive)->pci_dev; + byte mode = 0; + + if (dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) { + u32 reg = 0; + mode &= ~0x01; + if (isa_dev) + pci_read_config_dword(isa_dev, 0x64, ®); + if ((reg & 0x00004000) == 0x00004000) + mode |= 0x01; + } else if (svwks_revision < SVWKS_CSB5_REVISION_NEW) { + mode |= 0x01; + } else if (svwks_revision >= SVWKS_CSB5_REVISION_NEW) { + u8 btr =0; + pci_read_config_byte(dev, 0x5A, &btr); + mode |= btr; + if (!eighty_ninty_three(drive)) + mode &= ~0x02; + } + if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) && + (!(PCI_FUNC(dev->devfn) & 1))) + mode = 0x02; + mode &= ~0xFC; + return (mode); +} + +static byte svwks_ratefilter (ide_drive_t *drive, byte speed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + byte mode = svwks_ratemask(drive); + + switch(mode) { + case 0x04: while (speed > XFER_UDMA_6) speed--; break; + case 0x03: while (speed > XFER_UDMA_5) speed--; break; + case 0x02: while (speed > XFER_UDMA_4) speed--; break; + case 0x01: while (speed > XFER_UDMA_2) speed--; break; + case 0x00: + default: while (speed > XFER_MW_DMA_2) speed--; break; + break; + } +#else + while (speed > XFER_PIO_4) speed--; +#endif /* CONFIG_BLK_DEV_IDEDMA */ +// printk("%s: mode == %02x speed == %02x\n", drive->name, mode, speed); + return speed; +} + +static byte svwks_csb_check (struct pci_dev *dev) +{ + switch (dev->device) { + case PCI_DEVICE_ID_SERVERWORKS_CSB5IDE: + case PCI_DEVICE_ID_SERVERWORKS_CSB6IDE: + return 1; + default: + break; + } + return 0; +} +static int svwks_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + byte udma_modes[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 }; + byte dma_modes[] = { 0x77, 0x21, 0x20 }; + byte pio_modes[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 }; + + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte unit = (drive->select.b.unit & 0x01); + byte csb5 = svwks_csb_check(dev); + + byte drive_pci = 0x00; + byte drive_pci2 = 0x00; + byte drive_pci3 = hwif->channel ? 0x57 : 0x56; + + byte ultra_enable = 0x00; + byte ultra_timing = 0x00; + byte dma_timing = 0x00; + byte pio_timing = 0x00; + unsigned short csb5_pio = 0x00; + + byte pio = ide_get_best_pio_mode(drive, 255, 5, NULL); + byte speed = svwks_ratefilter(drive, xferspeed); + + switch (drive->dn) { + case 0: drive_pci = 0x41; drive_pci2 = 0x45; break; + case 1: drive_pci = 0x40; drive_pci2 = 0x44; break; + case 2: drive_pci = 0x43; drive_pci2 = 0x47; break; + case 3: drive_pci = 0x42; drive_pci2 = 0x46; break; + default: + return -1; + } + + pci_read_config_byte(dev, drive_pci, &pio_timing); + pci_read_config_byte(dev, drive_pci2, &dma_timing); + pci_read_config_byte(dev, drive_pci3, &ultra_timing); + pci_read_config_word(dev, 0x4A, &csb5_pio); + pci_read_config_byte(dev, 0x54, &ultra_enable); + + pio_timing &= ~0xFF; + dma_timing &= ~0xFF; + ultra_timing &= ~(0x0F << (4*unit)); + ultra_enable &= ~(0x01 << drive->dn); + csb5_pio &= ~(0x0F << (4*drive->dn)); + + switch(speed) { + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_1: + case XFER_PIO_0: + pio_timing |= pio_modes[speed - XFER_PIO_0]; + csb5_pio |= ((speed - XFER_PIO_0) << (4*drive->dn)); + break; + +#ifdef CONFIG_BLK_DEV_IDEDMA + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + pio_timing |= pio_modes[pio]; + csb5_pio |= (pio << (4*drive->dn)); + dma_timing |= dma_modes[speed - XFER_MW_DMA_0]; + break; + + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + pio_timing |= pio_modes[pio]; + csb5_pio |= (pio << (4*drive->dn)); + dma_timing |= dma_modes[2]; + ultra_timing |= ((udma_modes[speed - XFER_UDMA_0]) << (4*unit)); + ultra_enable |= (0x01 << drive->dn); +#endif + default: + break; + } + + pci_write_config_byte(dev, drive_pci, pio_timing); + if (csb5) + pci_write_config_word(dev, 0x4A, csb5_pio); + +#ifdef CONFIG_BLK_DEV_IDEDMA + pci_write_config_byte(dev, drive_pci2, dma_timing); + pci_write_config_byte(dev, drive_pci3, ultra_timing); + pci_write_config_byte(dev, 0x54, ultra_enable); +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + return (ide_config_drive_speed(drive, speed)); +} + +static void config_chipset_for_pio (ide_drive_t *drive) +{ + unsigned short eide_pio_timing[6] = {960, 480, 240, 180, 120, 90}; + unsigned short xfer_pio = drive->id->eide_pio_modes; + byte timing, speed, pio; + + pio = ide_get_best_pio_mode(drive, 255, 5, NULL); + + if (xfer_pio> 4) + xfer_pio = 0; + + if (drive->id->eide_pio_iordy > 0) + for (xfer_pio = 5; + xfer_pio>0 && + drive->id->eide_pio_iordy>eide_pio_timing[xfer_pio]; + xfer_pio--); + else + xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 : + (drive->id->eide_pio_modes & 2) ? 0x04 : + (drive->id->eide_pio_modes & 1) ? 0x03 : + (drive->id->tPIO & 2) ? 0x02 : + (drive->id->tPIO & 1) ? 0x01 : xfer_pio; + + timing = (xfer_pio >= pio) ? xfer_pio : pio; + + switch(timing) { + case 4: speed = XFER_PIO_4;break; + case 3: speed = XFER_PIO_3;break; + case 2: speed = XFER_PIO_2;break; + case 1: speed = XFER_PIO_1;break; + default: + speed = (!drive->id->tPIO) ? XFER_PIO_0 : XFER_PIO_SLOW; + break; + } + (void) svwks_tune_chipset(drive, speed); + drive->current_speed = speed; +} + +static void svwks_tune_drive (ide_drive_t *drive, byte pio) +{ + byte speed; + switch(pio) { + case 4: speed = XFER_PIO_4;break; + case 3: speed = XFER_PIO_3;break; + case 2: speed = XFER_PIO_2;break; + case 1: speed = XFER_PIO_1;break; + default: speed = XFER_PIO_0;break; + } + (void) svwks_tune_chipset(drive, speed); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static int config_chipset_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + byte mode = svwks_ratemask(drive); + byte speed, dma = 1; + + if (HWIF(drive)->pci_dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) + mode = 0; + + switch(mode) { + case 0x04: + if (id->dma_ultra & 0x0040) + { speed = XFER_UDMA_6; break; } + case 0x03: + if (id->dma_ultra & 0x0020) + { speed = XFER_UDMA_5; break; } + case 0x02: + if (id->dma_ultra & 0x0010) + { speed = XFER_UDMA_4; break; } + if (id->dma_ultra & 0x0008) + { speed = XFER_UDMA_3; break; } + case 0x01: + if (id->dma_ultra & 0x0004) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0002) + { speed = XFER_UDMA_1; break; } + if (id->dma_ultra & 0x0001) + { speed = XFER_UDMA_0; break; } + if (id->dma_mword & 0x0004) + { speed = XFER_MW_DMA_2; break; } + if (id->dma_mword & 0x0002) + { speed = XFER_MW_DMA_1; break; } + if (id->dma_mword & 0x0001) + { speed = XFER_MW_DMA_0; break; } +#if 0 + if (id->dma_1word & 0x0004) + { speed = XFER_SW_DMA_2; break; } + if (id->dma_1word & 0x0002) + { speed = XFER_SW_DMA_1; break; } + if (id->dma_1word & 0x0001) + { speed = XFER_SW_DMA_0; break; } +#endif + default: + speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL); + dma = 0; + break; + } + + (void) svwks_tune_chipset(drive, speed); + +// return ((int) (dma) ? ide_dma_on : ide_dma_off_quietly); + return ((int) ((id->dma_ultra >> 11) & 7) ? ide_dma_on : + ((id->dma_ultra >> 8) & 7) ? ide_dma_on : + ((id->dma_mword >> 8) & 7) ? ide_dma_on : + ((id->dma_1word >> 8) & 7) ? ide_dma_on : + ide_dma_off_quietly); +} + +static int config_drive_xfer_rate (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_dma_action_t dma_func = ide_dma_on; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && HWIF(drive)->autodma) { + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + goto fast_ata_pio; + } + dma_func = ide_dma_off_quietly; + if (id->field_valid & 4) { + if (id->dma_ultra & 0x003F) { + /* Force if Capable UltraDMA */ + dma_func = config_chipset_for_dma(drive); + if ((id->field_valid & 2) && + (dma_func != ide_dma_on)) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & 0x0007) || + (id->dma_1word & 0x007)) { + /* Force if Capable regular DMA modes */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } + } else if (ide_dmaproc(ide_dma_good_drive, drive)) { + if (id->eide_dma_time > 150) { + goto no_dma_set; + } + /* Consult the list of known "good" drives */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + dma_func = ide_dma_off_quietly; +no_dma_set: + config_chipset_for_pio(drive); + // HWIF(drive)->tuneproc(drive, 5); + } + return HWIF(drive)->dmaproc(dma_func, drive); +} + +static int svwks_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + return config_drive_xfer_rate(drive); + case ide_dma_end: + { + ide_hwif_t *hwif = HWIF(drive); + unsigned long dma_base = hwif->dma_base; + + if(IN_BYTE(dma_base+0x02)&1) + { +#if 0 + int i; + printk(KERN_ERR "Curious - OSB4 thinks the DMA is still running.\n"); + for(i=0;i<10;i++) + { + if(!(IN_BYTE(dma_base+0x02)&1)) + { + printk(KERN_ERR "OSB4 now finished.\n"); + break; + } + udelay(5); + } +#endif + printk(KERN_CRIT "Serverworks OSB4 in impossible state.\n"); + printk(KERN_CRIT "Disable UDMA or if you are using Seagate then try switching disk types\n"); + printk(KERN_CRIT "on this controller. Please report this event to osb4-bug@ide.cabal.tm\n"); +#if 0 + /* Panic might sys_sync -> death by corrupt disk */ + panic("OSB4: continuing might cause disk corruption.\n"); +#else + printk(KERN_CRIT "OSB4: continuing might cause disk corruption.\n"); + while(1) + cpu_relax(); +#endif + } + /* and drop through */ + } + default: + break; + } + /* Other cases are done by generic IDE-DMA code. */ + return ide_dmaproc(func, drive); +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +unsigned int __init pci_init_svwks (struct pci_dev *dev, const char *name) +{ + unsigned int reg; + byte btr; + + /* save revision id to determine DMA capability */ + pci_read_config_byte(dev, PCI_REVISION_ID, &svwks_revision); + + /* force Master Latency Timer value to 64 PCICLKs */ + pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x40); + + /* OSB4 : South Bridge and IDE */ + if (dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) { + isa_dev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL); + if (isa_dev) { + pci_read_config_dword(isa_dev, 0x64, ®); + reg &= ~0x00002000; /* disable 600ns interrupt mask */ + reg |= 0x00004000; /* enable UDMA/33 support */ + pci_write_config_dword(isa_dev, 0x64, reg); + } + } + + /* setup CSB5/CSB6 : South Bridge and IDE option RAID */ + else if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) || + (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE)) { + /* Third Channel Test */ + if (!(PCI_FUNC(dev->devfn) & 1)) { +#if 1 + struct pci_dev * findev = NULL; + unsigned int reg4c = 0; + findev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS, + PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL); + if (findev) { + pci_read_config_dword(findev, 0x4C, ®4c); + reg4c &= ~0x000007FF; + reg4c |= 0x00000040; + reg4c |= 0x00000020; + pci_write_config_dword(findev, 0x4C, reg4c); + } +#endif + outb_p(0x06, 0x0c00); + dev->irq = inb_p(0x0c01); +#if 1 + /* WE need to figure out how to get the correct one */ + printk("%s: interrupt %d\n", name, dev->irq); + if (dev->irq != 0x0B) + dev->irq = 0x0B; +#endif + } else { + /* + * This is a device pin issue on CSB6. + * Since there will be a future raid mode, + * early versions of the chipset require the + * interrupt pin to be set, and it is a compatablity + * mode issue. + */ + dev->irq = 0; + } + pci_write_config_dword(dev, 0x40, 0x99999999); + pci_write_config_dword(dev, 0x44, 0xFFFFFFFF); + /* setup the UDMA Control register + * + * 1. clear bit 6 to enable DMA + * 2. enable DMA modes with bits 0-1 + * 00 : legacy + * 01 : udma2 + * 10 : udma2/udma4 + * 11 : udma2/udma4/udma5 + */ + pci_read_config_byte(dev, 0x5A, &btr); + btr &= ~0x40; + if (!(PCI_FUNC(dev->devfn) & 1)) + btr |= 0x2; + else + btr |= (svwks_revision >= SVWKS_CSB5_REVISION_NEW) ? 0x3 : 0x2; + pci_write_config_byte(dev, 0x5A, btr); + } + + svwks_devs[n_svwks_devs++] = dev; + +#if defined(DISPLAY_SVWKS_TIMINGS) && defined(CONFIG_PROC_FS) + if (!svwks_proc) { + svwks_proc = 1; + svwks_display_info = &svwks_get_info; + } +#endif /* DISPLAY_SVWKS_TIMINGS && CONFIG_PROC_FS */ + + return (dev->irq) ? dev->irq : 0; +} + +static unsigned int __init ata66_svwks_svwks (ide_hwif_t *hwif) +{ +// struct pci_dev *dev = hwif->pci_dev; +// return 0; + return 1; +} + +/* On Dell PowerEdge servers with a CSB5/CSB6, the top two bits + * of the subsystem device ID indicate presence of an 80-pin cable. + * Bit 15 clear = secondary IDE channel does not have 80-pin cable. + * Bit 15 set = secondary IDE channel has 80-pin cable. + * Bit 14 clear = primary IDE channel does not have 80-pin cable. + * Bit 14 set = primary IDE channel has 80-pin cable. + */ +static unsigned int __init ata66_svwks_dell (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL && + dev->vendor == PCI_VENDOR_ID_SERVERWORKS && + (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE || + dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE)) + return ((1 << (hwif->channel + 14)) & + dev->subsystem_device) ? 1 : 0; + return 0; +} + +/* Sun Cobalt Alpine hardware avoids the 80-pin cable + * detect issue by attaching the drives directly to the board. + * This check follows the Dell precedent (how scary is that?!) + * + * WARNING: this only works on Alpine hardware! + */ +static unsigned int __init ata66_svwks_cobalt (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + if (dev->subsystem_vendor == PCI_VENDOR_ID_SUN && + dev->vendor == PCI_VENDOR_ID_SERVERWORKS && + dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB5IDE) + return ((1 << (hwif->channel + 14)) & + dev->subsystem_device) ? 1 : 0; + return 0; +} + +unsigned int __init ata66_svwks (ide_hwif_t *hwif) +{ + struct pci_dev *dev = hwif->pci_dev; + + /* Server Works */ + if (dev->subsystem_vendor == PCI_VENDOR_ID_SERVERWORKS) + return ata66_svwks_svwks (hwif); + + /* Dell PowerEdge */ + if (dev->subsystem_vendor == PCI_VENDOR_ID_DELL) + return ata66_svwks_dell (hwif); + + /* Cobalt Alpine */ + if (dev->subsystem_vendor == PCI_VENDOR_ID_SUN) + return ata66_svwks_cobalt (hwif); + + return 0; +} + +void __init ide_init_svwks (ide_hwif_t *hwif) +{ + if (!hwif->irq) + hwif->irq = hwif->channel ? 15 : 14; + + hwif->tuneproc = &svwks_tune_drive; + hwif->speedproc = &svwks_tune_chipset; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + hwif->autodma = 0; + + if (!hwif->dma_base) + return; + +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->dmaproc = &svwks_dmaproc; +# ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +# endif /* CONFIG_IDEDMA_AUTO */ +#endif /* !CONFIG_BLK_DEV_IDEDMA */ +} + +/* + * We allow the BM-DMA driver to only work on enabled interfaces. + */ +void __init ide_dmacapable_svwks (ide_hwif_t *hwif, unsigned long dmabase) +{ + struct pci_dev *dev = hwif->pci_dev; + if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) && + (!(PCI_FUNC(dev->devfn) & 1)) && (hwif->channel)) + return; +#if 0 + if (svwks_revision == (SVWKS_CSB5_REVISION_NEW + 1)) { + if (hwif->mate && hwif->mate->dma_base) { + dmabase = hwif->mate->dma_base - (hwif->channel ? 0 : 8); + } else { + dmabase = pci_resource_start(dev, 4); + if (!dmabase) { + printk("%s: dma_base is invalid (0x%04lx)\n", + hwif->name, dmabase); + dmabase = 0; + } + } + } +#endif + ide_setup_dma(hwif, dmabase, 8); +} + +extern void ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d); + +void __init fixup_device_csb6 (struct pci_dev *dev, ide_pci_device_t *d) +{ + if (!(PCI_FUNC(dev->devfn) & 1)) { + d->bootable = NEVER_BOARD; + } + + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); +} + diff -Nru a/drivers/ide/sis5513.c b/drivers/ide/sis5513.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/sis5513.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,927 @@ +/* + * linux/drivers/ide/sis5513.c Version 0.13 March 6, 2002 + * + * Copyright (C) 1999-2000 Andre Hedrick + * Copyright (C) 2002 Lionel Bouton , Maintainer + * May be copied or modified under the terms of the GNU General Public License + * + * + * Thanks : + * + * SiS Taiwan : for direct support and hardware. + * Daniela Engert : for initial ATA100 advices and numerous others. + * John Fremlin, Manfred Spraul : + * for checking code correctness, providing patches. + * + * + * Original tests and design on the SiS620/5513 chipset. + * ATA100 tests and design on the SiS735/5513 chipset. + * ATA16/33 design from specs + */ + +/* + * TODO: + * - Get ridden of SisHostChipInfo[] completness dependancy. + * - Get ATA-133 datasheets, implement ATA-133 init code. + * - Study drivers/ide/ide-timing.h. + * - Are there pre-ATA_16 SiS5513 chips ? -> tune init code for them + * or remove ATA_00 define + * - More checks in the config registers (force values instead of + * relying on the BIOS setting them correctly). + * - Further optimisations ? + * . for example ATA66+ regs 0x48 & 0x4A + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include "ide_modes.h" + +/* When DEBUG is defined it outputs initial PCI config register + values and changes made to them by the driver */ +// #define DEBUG +/* When BROKEN_LEVEL is defined it limits the DMA mode + at boot time to its value */ +// #define BROKEN_LEVEL XFER_SW_DMA_0 +#define DISPLAY_SIS_TIMINGS + +/* Miscellaneaous flags */ +#define SIS5513_LATENCY 0x01 + +/* registers layout and init values are chipset family dependant */ +/* 1/ define families */ +#define ATA_00 0x00 +#define ATA_16 0x01 +#define ATA_33 0x02 +#define ATA_66 0x03 +#define ATA_100a 0x04 // SiS730 is ATA100 with ATA66 layout +#define ATA_100 0x05 +#define ATA_133 0x06 +/* 2/ variable holding the controller chipset family value */ +static unsigned char chipset_family; + + +/* + * Debug code: following IDE config registers' changes + */ +#ifdef DEBUG +/* Copy of IDE Config registers 0x00 -> 0x57 + Fewer might be used depending on the actual chipset */ +static unsigned char ide_regs_copy[0x58]; + +static byte sis5513_max_config_register(void) { + switch(chipset_family) { + case ATA_00: + case ATA_16: return 0x4f; + case ATA_33: return 0x52; + case ATA_66: + case ATA_100a: + case ATA_100: + case ATA_133: + default: return 0x57; + } +} + +/* Read config registers, print differences from previous read */ +static void sis5513_load_verify_registers(struct pci_dev* dev, char* info) { + int i; + byte reg_val; + byte changed=0; + byte max = sis5513_max_config_register(); + + printk("SIS5513: %s, changed registers:\n", info); + for(i=0; i<=max; i++) { + pci_read_config_byte(dev, i, ®_val); + if (reg_val != ide_regs_copy[i]) { + printk("%0#x: %0#x -> %0#x\n", + i, ide_regs_copy[i], reg_val); + ide_regs_copy[i]=reg_val; + changed=1; + } + } + + if (!changed) { + printk("none\n"); + } +} + +/* Load config registers, no printing */ +static void sis5513_load_registers(struct pci_dev* dev) { + int i; + byte max = sis5513_max_config_register(); + + for(i=0; i<=max; i++) { + pci_read_config_byte(dev, i, &(ide_regs_copy[i])); + } +} + +/* Print a register */ +static void sis5513_print_register(int reg) { + printk(" %0#x:%0#x", reg, ide_regs_copy[reg]); +} + +/* Print valuable registers */ +static void sis5513_print_registers(struct pci_dev* dev, char* marker) { + int i; + byte max = sis5513_max_config_register(); + + sis5513_load_registers(dev); + printk("SIS5513 %s\n", marker); + printk("SIS5513 dump:"); + for(i=0x00; i<0x40; i++) { + if ((i % 0x10)==0) printk("\n "); + sis5513_print_register(i); + } + for(; i<49; i++) { + sis5513_print_register(i); + } + printk("\n "); + + for(; i<=max; i++) { + sis5513_print_register(i); + } + printk("\n"); +} +#endif + + +/* + * Devices supported + */ +static const struct { + const char *name; + unsigned short host_id; + unsigned char chipset_family; + unsigned char flags; +} SiSHostChipInfo[] = { + { "SiS750", PCI_DEVICE_ID_SI_750, ATA_100, SIS5513_LATENCY }, + { "SiS745", PCI_DEVICE_ID_SI_745, ATA_100, SIS5513_LATENCY }, + { "SiS740", PCI_DEVICE_ID_SI_740, ATA_100, SIS5513_LATENCY }, + { "SiS735", PCI_DEVICE_ID_SI_735, ATA_100, SIS5513_LATENCY }, + { "SiS730", PCI_DEVICE_ID_SI_730, ATA_100a, SIS5513_LATENCY }, + { "SiS650", PCI_DEVICE_ID_SI_650, ATA_100, SIS5513_LATENCY }, + { "SiS645", PCI_DEVICE_ID_SI_645, ATA_100, SIS5513_LATENCY }, + { "SiS635", PCI_DEVICE_ID_SI_635, ATA_100, SIS5513_LATENCY }, + { "SiS640", PCI_DEVICE_ID_SI_640, ATA_66, SIS5513_LATENCY }, + { "SiS630", PCI_DEVICE_ID_SI_630, ATA_66, SIS5513_LATENCY }, + { "SiS620", PCI_DEVICE_ID_SI_620, ATA_66, SIS5513_LATENCY }, + { "SiS540", PCI_DEVICE_ID_SI_540, ATA_66, 0}, + { "SiS530", PCI_DEVICE_ID_SI_530, ATA_66, 0}, + { "SiS5600", PCI_DEVICE_ID_SI_5600, ATA_33, 0}, + { "SiS5598", PCI_DEVICE_ID_SI_5598, ATA_33, 0}, + { "SiS5597", PCI_DEVICE_ID_SI_5597, ATA_33, 0}, + { "SiS5591", PCI_DEVICE_ID_SI_5591, ATA_33, 0}, + { "SiS5513", PCI_DEVICE_ID_SI_5513, ATA_16, 0}, + { "SiS5511", PCI_DEVICE_ID_SI_5511, ATA_16, 0}, +}; + +/* Cycle time bits and values vary accross chip dma capabilities + These three arrays hold the register layout and the values to set. + Indexed by chipset_family and (dma_mode - XFER_UDMA_0) */ +static byte cycle_time_offset[] = {0,0,5,4,4,0,0}; +static byte cycle_time_range[] = {0,0,2,3,3,4,4}; +static byte cycle_time_value[][XFER_UDMA_5 - XFER_UDMA_0 + 1] = { + {0,0,0,0,0,0}, /* no udma */ + {0,0,0,0,0,0}, /* no udma */ + {3,2,1,0,0,0}, + {7,5,3,2,1,0}, + {7,5,3,2,1,0}, + {11,7,5,4,2,1}, + {0,0,0,0,0,0} /* not yet known, ask SiS */ +}; + +static struct pci_dev *host_dev = NULL; + + +/* + * Printing configuration + */ +#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +static int sis_get_info(char *, char **, off_t, int); +extern int (*sis_display_info)(char *, char **, off_t, int); /* ide-proc.c */ +static struct pci_dev *bmide_dev; + +static char* cable_type[] = { + "80 pins", + "40 pins" +}; + +static char* recovery_time[] ={ + "12 PCICLK", "1 PCICLK", + "2 PCICLK", "3 PCICLK", + "4 PCICLK", "5 PCICLCK", + "6 PCICLK", "7 PCICLCK", + "8 PCICLK", "9 PCICLCK", + "10 PCICLK", "11 PCICLK", + "13 PCICLK", "14 PCICLK", + "15 PCICLK", "15 PCICLK" +}; + +static char* active_time[] = { + "8 PCICLK", "1 PCICLCK", + "2 PCICLK", "3 PCICLK", + "4 PCICLK", "5 PCICLK", + "6 PCICLK", "12 PCICLK" +}; + +static char* cycle_time[] = { + "Reserved", "2 CLK", + "3 CLK", "4 CLK", + "5 CLK", "6 CLK", + "7 CLK", "8 CLK", + "9 CLK", "10 CLK", + "11 CLK", "12 CLK", + "Reserved", "Reserved", + "Reserved", "Reserved" +}; + +/* Generic add master or slave info function */ +static char* get_drives_info (char *buffer, byte pos) +{ + byte reg00, reg01, reg10, reg11; /* timing registers */ + char* p = buffer; + +/* Postwrite/Prefetch */ + pci_read_config_byte(bmide_dev, 0x4b, ®00); + p += sprintf(p, "Drive %d: Postwrite %s \t \t Postwrite %s\n", + pos, (reg00 & (0x10 << pos)) ? "Enabled" : "Disabled", + (reg00 & (0x40 << pos)) ? "Enabled" : "Disabled"); + p += sprintf(p, " Prefetch %s \t \t Prefetch %s\n", + (reg00 & (0x01 << pos)) ? "Enabled" : "Disabled", + (reg00 & (0x04 << pos)) ? "Enabled" : "Disabled"); + + pci_read_config_byte(bmide_dev, 0x40+2*pos, ®00); + pci_read_config_byte(bmide_dev, 0x41+2*pos, ®01); + pci_read_config_byte(bmide_dev, 0x44+2*pos, ®10); + pci_read_config_byte(bmide_dev, 0x45+2*pos, ®11); + +/* UDMA */ + if (chipset_family >= ATA_33) { + p += sprintf(p, " UDMA %s \t \t \t UDMA %s\n", + (reg01 & 0x80) ? "Enabled" : "Disabled", + (reg11 & 0x80) ? "Enabled" : "Disabled"); + + p += sprintf(p, " UDMA Cycle Time "); + switch(chipset_family) { + case ATA_33: p += sprintf(p, cycle_time[(reg01 & 0x60) >> 5]); break; + case ATA_66: + case ATA_100a: p += sprintf(p, cycle_time[(reg01 & 0x70) >> 4]); break; + case ATA_100: p += sprintf(p, cycle_time[reg01 & 0x0F]); break; + case ATA_133: + default: p += sprintf(p, "133+ ?"); break; + } + p += sprintf(p, " \t UDMA Cycle Time "); + switch(chipset_family) { + case ATA_33: p += sprintf(p, cycle_time[(reg11 & 0x60) >> 5]); break; + case ATA_66: + case ATA_100a: p += sprintf(p, cycle_time[(reg11 & 0x70) >> 4]); break; + case ATA_100: p += sprintf(p, cycle_time[reg11 & 0x0F]); break; + case ATA_133: + default: p += sprintf(p, "133+ ?"); break; + } + p += sprintf(p, "\n"); + } + +/* Data Active */ + p += sprintf(p, " Data Active Time "); + switch(chipset_family) { + case ATA_00: + case ATA_16: /* confirmed */ + case ATA_33: + case ATA_66: + case ATA_100a: p += sprintf(p, active_time[reg01 & 0x07]); break; + case ATA_100: p += sprintf(p, active_time[(reg00 & 0x70) >> 4]); break; + case ATA_133: + default: p += sprintf(p, "133+ ?"); break; + } + p += sprintf(p, " \t Data Active Time "); + switch(chipset_family) { + case ATA_00: + case ATA_16: + case ATA_33: + case ATA_66: + case ATA_100a: p += sprintf(p, active_time[reg11 & 0x07]); break; + case ATA_100: p += sprintf(p, active_time[(reg10 & 0x70) >> 4]); break; + case ATA_133: + default: p += sprintf(p, "133+ ?"); break; + } + p += sprintf(p, "\n"); + +/* Data Recovery */ + /* warning: may need (reg&0x07) for pre ATA66 chips */ + p += sprintf(p, " Data Recovery Time %s \t Data Recovery Time %s\n", + recovery_time[reg00 & 0x0f], recovery_time[reg10 & 0x0f]); + + return p; +} + +static char* get_masters_info(char* buffer) +{ + return get_drives_info(buffer, 0); +} + +static char* get_slaves_info(char* buffer) +{ + return get_drives_info(buffer, 1); +} + +/* Main get_info, called on /proc/ide/sis reads */ +static int sis_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + byte reg; + u16 reg2, reg3; + + p += sprintf(p, "\nSiS 5513 "); + switch(chipset_family) { + case ATA_00: p += sprintf(p, "Unknown???"); break; + case ATA_16: p += sprintf(p, "DMA 16"); break; + case ATA_33: p += sprintf(p, "Ultra 33"); break; + case ATA_66: p += sprintf(p, "Ultra 66"); break; + case ATA_100a: + case ATA_100: p += sprintf(p, "Ultra 100"); break; + case ATA_133: + default: p+= sprintf(p, "Ultra 133+"); break; + } + p += sprintf(p, " chipset\n"); + p += sprintf(p, "--------------- Primary Channel " + "---------------- Secondary Channel " + "-------------\n"); + +/* Status */ + pci_read_config_byte(bmide_dev, 0x4a, ®); + p += sprintf(p, "Channel Status: "); + if (chipset_family < ATA_66) { + p += sprintf(p, "%s \t \t \t \t %s\n", + (reg & 0x04) ? "On" : "Off", + (reg & 0x02) ? "On" : "Off"); + } else { + p += sprintf(p, "%s \t \t \t \t %s \n", + (reg & 0x02) ? "On" : "Off", + (reg & 0x04) ? "On" : "Off"); + } + +/* Operation Mode */ + pci_read_config_byte(bmide_dev, 0x09, ®); + p += sprintf(p, "Operation Mode: %s \t \t \t %s \n", + (reg & 0x01) ? "Native" : "Compatible", + (reg & 0x04) ? "Native" : "Compatible"); + +/* 80-pin cable ? */ + if (chipset_family > ATA_33) { + pci_read_config_byte(bmide_dev, 0x48, ®); + p += sprintf(p, "Cable Type: %s \t \t \t %s\n", + (reg & 0x10) ? cable_type[1] : cable_type[0], + (reg & 0x20) ? cable_type[1] : cable_type[0]); + } + +/* Prefetch Count */ + pci_read_config_word(bmide_dev, 0x4c, ®2); + pci_read_config_word(bmide_dev, 0x4e, ®3); + p += sprintf(p, "Prefetch Count: %d \t \t \t \t %d\n", + reg2, reg3); + + p = get_masters_info(p); + p = get_slaves_info(p); + + return p-buffer; +} +#endif /* defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) */ + + +byte sis_proc = 0; + +static byte sis5513_ratemask (ide_drive_t *drive) +{ +// struct pci_dev *dev = HWIF(drive)->pci_dev; + byte mode = 0x00; + + switch(chipset_family) { + case ATA_133: // { mode |= 0x04; break; } + case ATA_100: + case ATA_100a: { mode |= 0x03; break; } + case ATA_66: { mode |= 0x02; break; } + case ATA_33: { mode |= 0x01; break; } + case ATA_16: + case ATA_00: + default: + return (mode &= ~0xF8); + } + if (!eighty_ninty_three(drive)) { + mode &= ~0xFE; + mode |= 0x01; + } + return (mode &= ~0xF8); +} + +static byte sis5513_ratefilter (ide_drive_t *drive, byte speed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + byte mode = sis5513_ratemask(drive); + + switch(mode) { + case 0x04: while (speed > XFER_UDMA_6) speed--; break; + case 0x03: while (speed > XFER_UDMA_5) speed--; break; + case 0x02: while (speed > XFER_UDMA_4) speed--; break; + case 0x01: while (speed > XFER_UDMA_2) speed--; break; + case 0x00: + default: while (speed > XFER_MW_DMA_2) speed--; break; + break; + } +#else + while (speed > XFER_PIO_4) speed--; +#endif /* CONFIG_BLK_DEV_IDEDMA */ +// printk("%s: mode == %02x speed == %02x\n", drive->name, mode, speed); + return speed; +} + +/* + * Configuration functions + */ +/* Enables per-drive prefetch and postwrite */ +static void config_drive_art_rwp (ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + + byte reg4bh = 0; + byte rw_prefetch = (0x11 << drive->dn); + +#ifdef DEBUG + printk("SIS5513: config_drive_art_rwp, drive %d\n", drive->dn); + sis5513_load_verify_registers(dev, "config_drive_art_rwp start"); +#endif + + if (drive->media != ide_disk) + return; + pci_read_config_byte(dev, 0x4b, ®4bh); + + if ((reg4bh & rw_prefetch) != rw_prefetch) + pci_write_config_byte(dev, 0x4b, reg4bh|rw_prefetch); +#ifdef DEBUG + sis5513_load_verify_registers(dev, "config_drive_art_rwp end"); +#endif +} + + +/* Set per-drive active and recovery time */ +static void config_art_rwp_pio (ide_drive_t *drive, byte pio) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + + byte timing, drive_pci, test1, test2; + + unsigned short eide_pio_timing[6] = {600, 390, 240, 180, 120, 90}; + unsigned short xfer_pio = drive->id->eide_pio_modes; + +#ifdef DEBUG + sis5513_load_verify_registers(dev, "config_drive_art_rwp_pio start"); +#endif + + config_drive_art_rwp(drive); + pio = ide_get_best_pio_mode(drive, 255, pio, NULL); + + if (xfer_pio> 4) + xfer_pio = 0; + + if (drive->id->eide_pio_iordy > 0) { + for (xfer_pio = 5; + (xfer_pio > 0) && + (drive->id->eide_pio_iordy > eide_pio_timing[xfer_pio]); + xfer_pio--); + } else { + xfer_pio = (drive->id->eide_pio_modes & 4) ? 0x05 : + (drive->id->eide_pio_modes & 2) ? 0x04 : + (drive->id->eide_pio_modes & 1) ? 0x03 : xfer_pio; + } + + timing = (xfer_pio >= pio) ? xfer_pio : pio; + +#ifdef DEBUG + printk("SIS5513: config_drive_art_rwp_pio, drive %d, pio %d, timing %d\n", + drive->dn, pio, timing); +#endif + + switch(drive->dn) { + case 0: drive_pci = 0x40; break; + case 1: drive_pci = 0x42; break; + case 2: drive_pci = 0x44; break; + case 3: drive_pci = 0x46; break; + default: return; + } + + /* register layout changed with newer ATA100 chips */ + if (chipset_family < ATA_100) { + pci_read_config_byte(dev, drive_pci, &test1); + pci_read_config_byte(dev, drive_pci+1, &test2); + + /* Clear active and recovery timings */ + test1 &= ~0x0F; + test2 &= ~0x07; + + switch(timing) { + case 4: test1 |= 0x01; test2 |= 0x03; break; + case 3: test1 |= 0x03; test2 |= 0x03; break; + case 2: test1 |= 0x04; test2 |= 0x04; break; + case 1: test1 |= 0x07; test2 |= 0x06; break; + default: break; + } + pci_write_config_byte(dev, drive_pci, test1); + pci_write_config_byte(dev, drive_pci+1, test2); + } else { + switch(timing) { /* active recovery + v v */ + case 4: test1 = 0x30|0x01; break; + case 3: test1 = 0x30|0x03; break; + case 2: test1 = 0x40|0x04; break; + case 1: test1 = 0x60|0x07; break; + default: break; + } + pci_write_config_byte(dev, drive_pci, test1); + } + +#ifdef DEBUG + sis5513_load_verify_registers(dev, "config_drive_art_rwp_pio start"); +#endif +} + +static int config_chipset_for_pio (ide_drive_t *drive, byte pio) +{ + byte speed; + + switch(pio) { + case 4: speed = XFER_PIO_4; break; + case 3: speed = XFER_PIO_3; break; + case 2: speed = XFER_PIO_2; break; + case 1: speed = XFER_PIO_1; break; + default: speed = XFER_PIO_0; break; + } + + config_art_rwp_pio(drive, pio); + return (ide_config_drive_speed(drive, speed)); +} + +static int sis5513_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte speed = sis5513_ratefilter(drive, xferspeed); + byte drive_pci, reg; + +#ifdef DEBUG + sis5513_load_verify_registers(dev, "sis5513_tune_chipset start"); + printk("SIS5513: sis5513_tune_chipset, drive %d, speed %d\n", + drive->dn, speed); +#endif +#if 1 + switch(drive->dn) { + case 0: drive_pci = 0x40; break; + case 1: drive_pci = 0x42; break; + case 2: drive_pci = 0x44; break; + case 3: drive_pci = 0x46; break; + default: return ide_dma_off; + } +#else +// drive_pci = (0x40 + ((drive->dn) *2)); +// drive_pci = 0x40; +// drive_pci |= ((drive->dn) << 1); +#endif + +#ifdef BROKEN_LEVEL +#ifdef DEBUG + printk("SIS5513: BROKEN_LEVEL activated, speed=%d -> speed=%d\n", speed, BROKEN_LEVEL); +#endif + if (speed > BROKEN_LEVEL) speed = BROKEN_LEVEL; +#endif + + pci_read_config_byte(dev, drive_pci+1, ®); + /* Disable UDMA bit for non UDMA modes on UDMA chips */ + if ((speed < XFER_UDMA_0) && (chipset_family > ATA_16)) { + reg &= 0x7F; + pci_write_config_byte(dev, drive_pci+1, reg); + } + + /* Config chip for mode */ + switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA + case XFER_UDMA_5: + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + /* Force the UDMA bit on if we want to use UDMA */ + reg |= 0x80; + /* clean reg cycle time bits */ + reg &= ~((0xFF >> (8 - cycle_time_range[chipset_family])) + << cycle_time_offset[chipset_family]); + /* set reg cycle time bits */ + reg |= cycle_time_value[chipset_family-ATA_00][speed-XFER_UDMA_0] + << cycle_time_offset[chipset_family]; + pci_write_config_byte(dev, drive_pci+1, reg); + break; + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_MW_DMA_0: + case XFER_SW_DMA_2: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + break; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: return((int) config_chipset_for_pio(drive, 4)); + case XFER_PIO_3: return((int) config_chipset_for_pio(drive, 3)); + case XFER_PIO_2: return((int) config_chipset_for_pio(drive, 2)); + case XFER_PIO_1: return((int) config_chipset_for_pio(drive, 1)); + case XFER_PIO_0: + default: return((int) config_chipset_for_pio(drive, 0)); + } +#ifdef DEBUG + sis5513_load_verify_registers(dev, "sis5513_tune_chipset end"); +#endif + return ((int) ide_config_drive_speed(drive, speed)); +} + +static void sis5513_tune_drive (ide_drive_t *drive, byte pio) +{ + (void) config_chipset_for_pio(drive, pio); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +/* + * ((id->hw_config & 0x4000|0x2000) && (HWIF(drive)->udma_four)) + */ +static int config_chipset_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + byte mode = sis5513_ratemask(drive); + byte speed = 0; + +#ifdef DEBUG + printk("SIS5513: config_chipset_for_dma, drive %d ultra %d\n", + drive->dn, mode); +#endif + + switch(mode) { + case 0x04: + if (id->dma_ultra & 0x0040) + { speed = XFER_UDMA_6; break; } + case 0x03: + if (id->dma_ultra & 0x0020) + { speed = XFER_UDMA_5; break; } + case 0x02: + if (id->dma_ultra & 0x0010) + { speed = XFER_UDMA_4; break; } + if (id->dma_ultra & 0x0008) + { speed = XFER_UDMA_3; break; } + case 0x01: + if (id->dma_ultra & 0x0004) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0002) + { speed = XFER_UDMA_1; break; } + if (id->dma_ultra & 0x0001) + { speed = XFER_UDMA_0; break; } + case 0x00: + if (id->dma_mword & 0x0004) + { speed = XFER_MW_DMA_2; break; } + if (id->dma_mword & 0x0002) + { speed = XFER_MW_DMA_1; break; } + if (id->dma_mword & 0x0001) + { speed = XFER_MW_DMA_0; break; } + if (id->dma_1word & 0x0004) + { speed = XFER_SW_DMA_2; break; } + if (id->dma_1word & 0x0002) + { speed = XFER_SW_DMA_1; break; } + if (id->dma_1word & 0x0001) + { speed = XFER_SW_DMA_0; break; } + default: + return ((int) ide_dma_off_quietly); + } + sis5513_tune_chipset(drive, speed); +// return ((int) ide_dma_on); + return ((int) ((id->dma_ultra >> 14) & 3) ? ide_dma_on : + ((id->dma_ultra >> 11) & 7) ? ide_dma_on : + ((id->dma_ultra >> 8) & 7) ? ide_dma_on : + ((id->dma_mword >> 8) & 7) ? ide_dma_on : + ((id->dma_1word >> 8) & 7) ? ide_dma_on : + ide_dma_off_quietly); +} + +static int config_drive_xfer_rate (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_dma_action_t dma_func = ide_dma_off_quietly; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && HWIF(drive)->autodma) { + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + goto fast_ata_pio; + } + dma_func = ide_dma_off_quietly; + if (id->field_valid & 4) { + if (id->dma_ultra & 0x003F) { + /* Force if Capable UltraDMA */ + dma_func = config_chipset_for_dma(drive); + if ((id->field_valid & 2) && + (dma_func != ide_dma_on)) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & 0x0007) || + (id->dma_1word & 0x0007)) { + /* Force if Capable regular DMA modes */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } + } else if ((ide_dmaproc(ide_dma_good_drive, drive)) && + (id->eide_dma_time > 150)) { + /* Consult the list of known "good" drives */ + dma_func = config_chipset_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + dma_func = ide_dma_off_quietly; +no_dma_set: + sis5513_tune_drive(drive, 5); + } + + return HWIF(drive)->dmaproc(dma_func, drive); +} + +/* initiates/aborts (U)DMA read/write operations on a drive. */ +int sis5513_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + config_drive_art_rwp(drive); + config_art_rwp_pio(drive, 5); + return config_drive_xfer_rate(drive); + default: + break; + } + return ide_dmaproc(func, drive); /* use standard DMA stuff */ +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +/* Chip detection and general config */ +unsigned int __init pci_init_sis5513 (struct pci_dev *dev, const char *name) +{ + struct pci_dev *host; + int i = 0; + + /* Find the chip */ + for (i = 0; i < ARRAY_SIZE(SiSHostChipInfo) && !host_dev; i++) { + host = pci_find_device (PCI_VENDOR_ID_SI, + SiSHostChipInfo[i].host_id, + NULL); + if (!host) + continue; + + host_dev = host; + chipset_family = SiSHostChipInfo[i].chipset_family; + printk(SiSHostChipInfo[i].name); + printk("\n"); + +#ifdef DEBUG + sis5513_print_registers(dev, "pci_init_sis5513 start"); +#endif + + if (SiSHostChipInfo[i].flags & SIS5513_LATENCY) { + byte latency = (chipset_family == ATA_100)? 0x80 : 0x10; /* Lacking specs */ + pci_write_config_byte(dev, PCI_LATENCY_TIMER, latency); + } + } + + /* Make general config ops here + 1/ tell IDE channels to operate in Compabitility mode only + 2/ tell old chips to allow per drive IDE timings */ + if (host_dev) { + byte reg; + switch(chipset_family) { + case ATA_133: + case ATA_100: + /* Set compatibility bit */ + pci_read_config_byte(dev, 0x49, ®); + if (!(reg & 0x01)) { + pci_write_config_byte(dev, 0x49, reg|0x01); + } + break; + case ATA_100a: + case ATA_66: + /* On ATA_66 chips the bit was elsewhere */ + pci_read_config_byte(dev, 0x52, ®); + if (!(reg & 0x04)) { + pci_write_config_byte(dev, 0x52, reg|0x04); + } + break; + case ATA_33: + /* On ATA_33 we didn't have a single bit to set */ + pci_read_config_byte(dev, 0x09, ®); + if ((reg & 0x0f) != 0x00) { + pci_write_config_byte(dev, 0x09, reg&0xf0); + } + case ATA_16: + /* force per drive recovery and active timings + needed on ATA_33 and below chips */ + pci_read_config_byte(dev, 0x52, ®); + if (!(reg & 0x08)) { + pci_write_config_byte(dev, 0x52, reg|0x08); + } + break; + case ATA_00: + default: break; + } + +#if defined(DISPLAY_SIS_TIMINGS) && defined(CONFIG_PROC_FS) + if (!sis_proc) { + sis_proc = 1; + bmide_dev = dev; + sis_display_info = &sis_get_info; + } +#endif + } +#ifdef DEBUG + sis5513_load_verify_registers(dev, "pci_init_sis5513 end"); +#endif + return 0; +} + +unsigned int __init ata66_sis5513 (ide_hwif_t *hwif) +{ + byte reg48h = 0, ata66 = 0; + byte mask = hwif->channel ? 0x20 : 0x10; + pci_read_config_byte(hwif->pci_dev, 0x48, ®48h); + + if (chipset_family >= ATA_66) { + ata66 = (reg48h & mask) ? 0 : 1; + } + return ata66; +} + +void __init ide_init_sis5513 (ide_hwif_t *hwif) +{ + + hwif->irq = hwif->channel ? 15 : 14; + + hwif->tuneproc = &sis5513_tune_drive; + hwif->speedproc = &sis5513_tune_chipset; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + hwif->autodma = 0; + + if (!(hwif->dma_base)) + return; + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (host_dev) { + if (chipset_family > ATA_16) + hwif->dmaproc = &sis5513_dmaproc; + else + hwif->autodma = 0; + } +# ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +# endif /* CONFIG_IDEDMA_AUTO */ +#endif /* CONFIG_BLK_DEV_IDEDMA */ + return; +} + +extern void ide_setup_pci_device (struct pci_dev *dev, ide_pci_device_t *d); + +void __init fixup_device_sis5513 (struct pci_dev *dev, ide_pci_device_t *d) +{ + if (dev->resource[0].start != 0x01F1) + ide_register_xp_fix(dev); + + printk("%s: IDE controller on PCI bus %02x dev %02x\n", + d->name, dev->bus->number, dev->devfn); + ide_setup_pci_device(dev, d); +} diff -Nru a/drivers/ide/sl82c105.c b/drivers/ide/sl82c105.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/sl82c105.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,272 @@ +/* + * linux/drivers/ide/sl82c105.c + * + * SL82C105/Winbond 553 IDE driver + * + * Maintainer unknown. + * + * Drive tuning added from Rebel.com's kernel sources + * -- Russell King (15/11/98) linux@arm.linux.org.uk + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ide_modes.h" + +/* + * Convert a PIO mode and cycle time to the required on/off + * times for the interface. This has protection against run-away + * timings. + */ +static unsigned int get_timing_sl82c105(ide_pio_data_t *p) +{ + unsigned int cmd_on; + unsigned int cmd_off; + + cmd_on = (ide_pio_timings[p->pio_mode].active_time + 29) / 30; + cmd_off = (p->cycle_time - 30 * cmd_on + 29) / 30; + + if (cmd_on > 32) + cmd_on = 32; + if (cmd_on == 0) + cmd_on = 1; + + if (cmd_off > 32) + cmd_off = 32; + if (cmd_off == 0) + cmd_off = 1; + + return (cmd_on - 1) << 8 | (cmd_off - 1) | (p->use_iordy ? 0x40 : 0x00); +} + +/* + * Configure the drive and chipset for PIO + */ +static void config_for_pio(ide_drive_t *drive, int pio, int report) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + ide_pio_data_t p; + unsigned short drv_ctrl = 0x909; + unsigned int xfer_mode, reg; + + reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0); + + pio = ide_get_best_pio_mode(drive, pio, 5, &p); + + switch (pio) { + default: + case 0: xfer_mode = XFER_PIO_0; break; + case 1: xfer_mode = XFER_PIO_1; break; + case 2: xfer_mode = XFER_PIO_2; break; + case 3: xfer_mode = XFER_PIO_3; break; + case 4: xfer_mode = XFER_PIO_4; break; + } + + if (ide_config_drive_speed(drive, xfer_mode) == 0) + drv_ctrl = get_timing_sl82c105(&p); + + if (drive->using_dma == 0) { + /* + * If we are actually using MW DMA, then we can not + * reprogram the interface drive control register. + */ + pci_write_config_word(dev, reg, drv_ctrl); + pci_read_config_word(dev, reg, &drv_ctrl); + + if (report) { + printk("%s: selected %s (%dns) (%04X)\n", drive->name, + ide_xfer_verbose(xfer_mode), p.cycle_time, drv_ctrl); + } + } +} + +/* + * Configure the drive and the chipset for DMA + */ +static int config_for_dma(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + unsigned short drv_ctrl = 0x909; + unsigned int reg; + + reg = (hwif->channel ? 0x4c : 0x44) + (drive->select.b.unit ? 4 : 0); + + if (ide_config_drive_speed(drive, XFER_MW_DMA_2) == 0) + drv_ctrl = 0x0240; + + pci_write_config_word(dev, reg, drv_ctrl); + + return 0; +} + +/* + * Check to see if the drive and + * chipset is capable of DMA mode + */ +static int sl82c105_check_drive(ide_drive_t *drive) +{ + ide_dma_action_t dma_func = ide_dma_off_quietly; + + do { + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + + if (!hwif->autodma) + break; + + if (!id || !(id->capability & 1)) + break; + + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + break; + } + + if (id->field_valid & 2) { + if (id->dma_mword & 7 || id->dma_1word & 7) + dma_func = ide_dma_on; + break; + } + + if (ide_dmaproc(ide_dma_good_drive, drive)) { + dma_func = ide_dma_on; + break; + } + } while (0); + + return HWIF(drive)->dmaproc(dma_func, drive); +} + +/* + * Our own dmaproc, only to intercept ide_dma_check + */ +static int sl82c105_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + return sl82c105_check_drive(drive); + case ide_dma_on: + if (config_for_dma(drive)) + func = ide_dma_off; + /* fall through */ + case ide_dma_off_quietly: + case ide_dma_off: + config_for_pio(drive, 4, 0); + break; + default: + break; + } + return ide_dmaproc(func, drive); +} + +/* + * We only deal with PIO mode here - DMA mode 'using_dma' is not + * initialised at the point that this function is called. + */ +static void tune_sl82c105(ide_drive_t *drive, byte pio) +{ + config_for_pio(drive, pio, 1); + + /* + * We support 32-bit I/O on this interface, and it + * doesn't have problems with interrupts. + */ + drive->io_32bit = 1; + drive->unmask = 1; +} + +/* + * Return the revision of the Winbond bridge + * which this function is part of. + */ +static unsigned int sl82c105_bridge_revision(struct pci_dev *dev) +{ + struct pci_dev *bridge; + unsigned char rev; + + bridge = pci_find_device(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_83C553, NULL); + + /* + * If we are part of a Winbond 553 + */ + if (!bridge || bridge->class >> 8 != PCI_CLASS_BRIDGE_ISA) + return -1; + + if (bridge->bus != dev->bus || + PCI_SLOT(bridge->devfn) != PCI_SLOT(dev->devfn)) + return -1; + + /* + * We need to find function 0's revision, not function 1 + */ + pci_read_config_byte(bridge, PCI_REVISION_ID, &rev); + + return rev; +} + +/* + * Enable the PCI device + */ +unsigned int __init pci_init_sl82c105(struct pci_dev *dev, const char *msg) +{ + unsigned char ctrl_stat; + + /* + * Enable the ports + */ + pci_read_config_byte(dev, 0x40, &ctrl_stat); + pci_write_config_byte(dev, 0x40, ctrl_stat | 0x33); + + return dev->irq; +} + +void __init dma_init_sl82c105(ide_hwif_t *hwif, unsigned long dma_base) +{ + unsigned int rev; + byte dma_state; + + dma_state = IN_BYTE(dma_base + 2); + rev = sl82c105_bridge_revision(hwif->pci_dev); + if (rev <= 5) { + hwif->autodma = 0; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + printk(" %s: Winbond 553 bridge revision %d, BM-DMA disabled\n", + hwif->name, rev); + dma_state &= ~0x60; + } else { + dma_state |= 0x60; + hwif->autodma = 1; + } + OUT_BYTE(dma_state, dma_base + 2); + + hwif->dmaproc = NULL; + ide_setup_dma(hwif, dma_base, 8); + if (hwif->dmaproc) + hwif->dmaproc = sl82c105_dmaproc; +} + +/* + * Initialise the chip + */ +void __init ide_init_sl82c105(ide_hwif_t *hwif) +{ + hwif->tuneproc = tune_sl82c105; +} + diff -Nru a/drivers/ide/slc90e66.c b/drivers/ide/slc90e66.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/slc90e66.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,470 @@ +/* + * linux/drivers/ide/slc90e66.c Version 0.10 October 4, 2000 + * + * Copyright (C) 2000 Andre Hedrick + * May be copied or modified under the terms of the GNU General Public License + * + * 00:07.1 IDE interface: EFAR Microsystems: + * Unknown device 9130 (prog-if 8a [Master SecP PriP]) + * Control: I/O+ Mem- BusMaster+ SpecCycle- MemWINV- + * VGASnoop- ParErr- Stepping- SERR- FastB2B- + * Status: Cap- 66Mhz- UDF- FastB2B- ParErr- DEVSEL=medium + * >TAbort- SERR- +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ide_modes.h" + +#define SLC90E66_DEBUG_DRIVE_INFO 0 + +#define DISPLAY_SLC90E66_TIMINGS + +#if defined(DISPLAY_SLC90E66_TIMINGS) && defined(CONFIG_PROC_FS) +#include +#include + +static int slc90e66_get_info(char *, char **, off_t, int); +extern int (*slc90e66_display_info)(char *, char **, off_t, int); /* ide-proc.c */ +static struct pci_dev *bmide_dev; + +static int slc90e66_get_info (char *buffer, char **addr, off_t offset, int count) +{ + char *p = buffer; + u32 bibma = pci_resource_start(bmide_dev, 4); + u16 reg40 = 0, psitre = 0, reg42 = 0, ssitre = 0; + u8 c0 = 0, c1 = 0; + u8 reg44 = 0, reg47 = 0, reg48 = 0, reg4a = 0, reg4b = 0; + + pci_read_config_word(bmide_dev, 0x40, ®40); + pci_read_config_word(bmide_dev, 0x42, ®42); + pci_read_config_byte(bmide_dev, 0x44, ®44); + pci_read_config_byte(bmide_dev, 0x47, ®47); + pci_read_config_byte(bmide_dev, 0x48, ®48); + pci_read_config_byte(bmide_dev, 0x4a, ®4a); + pci_read_config_byte(bmide_dev, 0x4b, ®4b); + + psitre = (reg40 & 0x4000) ? 1 : 0; + ssitre = (reg42 & 0x4000) ? 1 : 0; + + /* + * at that point bibma+0x2 et bibma+0xa are byte registers + * to investigate: + */ +#ifdef __mips__ /* only for mips? */ + c0 = inb_p(bibma + 0x02); + c1 = inb_p(bibma + 0x0a); +#else + c0 = inb_p((unsigned short)bibma + 0x02); + c1 = inb_p((unsigned short)bibma + 0x0a); +#endif + + p += sprintf(p, " SLC90E66 Chipset.\n"); + p += sprintf(p, "--------------- Primary Channel " + "---------------- Secondary Channel " + "-------------\n"); + p += sprintf(p, " %sabled " + " %sabled\n", + (c0&0x80) ? "dis" : " en", + (c1&0x80) ? "dis" : " en"); + p += sprintf(p, "--------------- drive0 --------- drive1 " + "-------- drive0 ---------- drive1 ------\n"); + p += sprintf(p, "DMA enabled: %s %s " + " %s %s\n", + (c0&0x20) ? "yes" : "no ", + (c0&0x40) ? "yes" : "no ", + (c1&0x20) ? "yes" : "no ", + (c1&0x40) ? "yes" : "no " ); + p += sprintf(p, "UDMA enabled: %s %s " + " %s %s\n", + (reg48&0x01) ? "yes" : "no ", + (reg48&0x02) ? "yes" : "no ", + (reg48&0x04) ? "yes" : "no ", + (reg48&0x08) ? "yes" : "no " ); + p += sprintf(p, "UDMA enabled: %s %s " + " %s %s\n", + ((reg4a&0x04)==0x04) ? "4" : + ((reg4a&0x03)==0x03) ? "3" : + (reg4a&0x02) ? "2" : + (reg4a&0x01) ? "1" : + (reg4a&0x00) ? "0" : "X", + ((reg4a&0x40)==0x40) ? "4" : + ((reg4a&0x30)==0x30) ? "3" : + (reg4a&0x20) ? "2" : + (reg4a&0x10) ? "1" : + (reg4a&0x00) ? "0" : "X", + ((reg4b&0x04)==0x04) ? "4" : + ((reg4b&0x03)==0x03) ? "3" : + (reg4b&0x02) ? "2" : + (reg4b&0x01) ? "1" : + (reg4b&0x00) ? "0" : "X", + ((reg4b&0x40)==0x40) ? "4" : + ((reg4b&0x30)==0x30) ? "3" : + (reg4b&0x20) ? "2" : + (reg4b&0x10) ? "1" : + (reg4b&0x00) ? "0" : "X"); + + p += sprintf(p, "UDMA\n"); + p += sprintf(p, "DMA\n"); + p += sprintf(p, "PIO\n"); + +/* + * FIXME.... Add configuration junk data....blah blah...... + */ + + return p-buffer; /* => must be less than 4k! */ +} +#endif /* defined(DISPLAY_SLC90E66_TIMINGS) && defined(CONFIG_PROC_FS) */ + +/* + * Used to set Fifo configuration via kernel command line: + */ + +byte slc90e66_proc = 0; + +static byte slc90e66_ratemask (ide_drive_t *drive) +{ + byte mode = 0x00; + + mode |= 0x02; + + if (!eighty_ninty_three(drive)) { + mode &= ~0xFE; + mode |= 0x01; + } + return (mode &= ~0xF8); +} + +static byte slc90e66_ratefilter (ide_drive_t *drive, byte speed) +{ +#ifdef CONFIG_BLK_DEV_IDEDMA + byte mode = slc90e66_ratemask(drive); + + switch(mode) { + case 0x04: // while (speed > XFER_UDMA_6) speed--; break; + case 0x03: // while (speed > XFER_UDMA_5) speed--; break; + case 0x02: while (speed > XFER_UDMA_4) speed--; break; + case 0x01: while (speed > XFER_UDMA_2) speed--; break; + case 0x00: + default: while (speed > XFER_MW_DMA_2) speed--; break; + break; + } +#else + while (speed > XFER_PIO_4) speed--; +#endif /* CONFIG_BLK_DEV_IDEDMA */ +// printk("%s: mode == %02x speed == %02x\n", drive->name, mode, speed); + return speed; +} + +static byte slc90e66_dma_2_pio (byte xfer_rate) { + switch(xfer_rate) { + case XFER_UDMA_4: + case XFER_UDMA_3: + case XFER_UDMA_2: + case XFER_UDMA_1: + case XFER_UDMA_0: + case XFER_MW_DMA_2: + case XFER_PIO_4: + return 4; + case XFER_MW_DMA_1: + case XFER_PIO_3: + return 3; + case XFER_SW_DMA_2: + case XFER_PIO_2: + return 2; + case XFER_MW_DMA_0: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + case XFER_PIO_1: + case XFER_PIO_0: + case XFER_PIO_SLOW: + default: + return 0; + } +} + +/* + * Based on settings done by AMI BIOS + * (might be useful if drive is not registered in CMOS for any reason). + */ +static void slc90e66_tune_drive (ide_drive_t *drive, byte pio) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + int is_slave = (&hwif->drives[1] == drive); + int master_port = hwif->channel ? 0x42 : 0x40; + int slave_port = 0x44; + unsigned long flags; + u16 master_data; + byte slave_data; + /* ISP RTC */ + byte timings[][2] = { { 0, 0 }, + { 0, 0 }, + { 1, 0 }, + { 2, 1 }, + { 2, 3 }, }; + + pio = ide_get_best_pio_mode(drive, pio, 5, NULL); + spin_lock_irqsave(&ide_lock, flags); + pci_read_config_word(dev, master_port, &master_data); + if (is_slave) { + master_data = master_data | 0x4000; + if (pio > 1) + /* enable PPE, IE and TIME */ + master_data = master_data | 0x0070; + pci_read_config_byte(dev, slave_port, &slave_data); + slave_data = slave_data & (hwif->channel ? 0x0f : 0xf0); + slave_data = slave_data | (((timings[pio][0] << 2) | timings[pio][1]) << (hwif->channel ? 4 : 0)); + } else { + master_data = master_data & 0xccf8; + if (pio > 1) + /* enable PPE, IE and TIME */ + master_data = master_data | 0x0007; + master_data = master_data | (timings[pio][0] << 12) | (timings[pio][1] << 8); + } + pci_write_config_word(dev, master_port, master_data); + if (is_slave) + pci_write_config_byte(dev, slave_port, slave_data); + spin_unlock_irqrestore(&ide_lock, flags); +} + +static int slc90e66_tune_chipset (ide_drive_t *drive, byte xferspeed) +{ + ide_hwif_t *hwif = HWIF(drive); + struct pci_dev *dev = hwif->pci_dev; + byte maslave = hwif->channel ? 0x42 : 0x40; + byte speed = slc90e66_ratefilter(drive, xferspeed); + int a_speed = 7 << (drive->dn * 4); + int u_flag = 1 << drive->dn; + int u_speed = 0; + int sitre; + short reg4042, reg44, reg48, reg4a; + + pci_read_config_word(dev, maslave, ®4042); + sitre = (reg4042 & 0x4000) ? 1 : 0; + pci_read_config_word(dev, 0x44, ®44); + pci_read_config_word(dev, 0x48, ®48); + pci_read_config_word(dev, 0x4a, ®4a); + + switch(speed) { +#ifdef CONFIG_BLK_DEV_IDEDMA + case XFER_UDMA_4: u_speed = 4 << (drive->dn * 4); break; + case XFER_UDMA_3: u_speed = 3 << (drive->dn * 4); break; + case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break; + case XFER_UDMA_1: u_speed = 1 << (drive->dn * 4); break; + case XFER_UDMA_0: u_speed = 0 << (drive->dn * 4); break; + case XFER_MW_DMA_2: + case XFER_MW_DMA_1: + case XFER_SW_DMA_2: break; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + case XFER_PIO_4: + case XFER_PIO_3: + case XFER_PIO_2: + case XFER_PIO_0: break; + default: return -1; + } + + if (speed >= XFER_UDMA_0) { + if (!(reg48 & u_flag)) + pci_write_config_word(dev, 0x48, reg48|u_flag); + if ((reg4a & u_speed) != u_speed) { + pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); + pci_read_config_word(dev, 0x4a, ®4a); + pci_write_config_word(dev, 0x4a, reg4a|u_speed); + } + } else { + if (reg48 & u_flag) + pci_write_config_word(dev, 0x48, reg48 & ~u_flag); + if (reg4a & a_speed) + pci_write_config_word(dev, 0x4a, reg4a & ~a_speed); + } + + slc90e66_tune_drive(drive, slc90e66_dma_2_pio(speed)); + return (ide_config_drive_speed(drive, speed)); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static int slc90e66_config_drive_for_dma (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + byte mode = slc90e66_ratemask(drive); + byte speed, tspeed, dma = 1; + + switch(mode) { + case 0x02: + if (id->dma_ultra & 0x0010) + { speed = XFER_UDMA_4; break; } + if (id->dma_ultra & 0x0008) + { speed = XFER_UDMA_3; break; } + case 0x01: + if (id->dma_ultra & 0x0004) + { speed = XFER_UDMA_2; break; } + if (id->dma_ultra & 0x0002) + { speed = XFER_UDMA_1; break; } + if (id->dma_ultra & 0x0001) + { speed = XFER_UDMA_0; break; } + case 0x00: + if (id->dma_mword & 0x0004) + { speed = XFER_MW_DMA_2; break; } + if (id->dma_mword & 0x0002) + { speed = XFER_MW_DMA_1; break; } + if (id->dma_1word & 0x0004) + { speed = XFER_SW_DMA_2; break; } + default: + tspeed = ide_get_best_pio_mode(drive, 255, 5, NULL); + speed = slc90e66_dma_2_pio(XFER_PIO_0 + tspeed); + dma = 0; + break; + } + + (void) slc90e66_tune_chipset(drive, speed); +// return ((int) (dma) ? ide_dma_on : ide_dma_off_quietly); + return ((int) ((id->dma_ultra >> 11) & 7) ? ide_dma_on : + ((id->dma_ultra >> 8) & 7) ? ide_dma_on : + ((id->dma_mword >> 8) & 7) ? ide_dma_on : + ((id->dma_1word >> 8) & 7) ? ide_dma_on : + ide_dma_off_quietly); +} + +static int config_drive_xfer_rate (ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_dma_action_t dma_func = ide_dma_on; + + drive->init_speed = 0; + + if (id && (id->capability & 1) && HWIF(drive)->autodma) { + /* Consult the list of known "bad" drives */ + if (ide_dmaproc(ide_dma_bad_drive, drive)) { + dma_func = ide_dma_off; + goto fast_ata_pio; + } + dma_func = ide_dma_off_quietly; + if (id->field_valid & 4) { + if (id->dma_ultra & 0x007F) { + /* Force if Capable UltraDMA */ + dma_func = slc90e66_config_drive_for_dma(drive); + if ((id->field_valid & 2) && + (dma_func != ide_dma_on)) + goto try_dma_modes; + } + } else if (id->field_valid & 2) { +try_dma_modes: + if ((id->dma_mword & 0x0007) || + (id->dma_1word & 0x007)) { + /* Force if Capable regular DMA modes */ + dma_func = slc90e66_config_drive_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } + } else if (ide_dmaproc(ide_dma_good_drive, drive)) { + if (id->eide_dma_time > 150) { + goto no_dma_set; + } + /* Consult the list of known "good" drives */ + dma_func = slc90e66_config_drive_for_dma(drive); + if (dma_func != ide_dma_on) + goto no_dma_set; + } else { + goto fast_ata_pio; + } + } else if ((id->capability & 8) || (id->field_valid & 2)) { +fast_ata_pio: + dma_func = ide_dma_off_quietly; +no_dma_set: + slc90e66_tune_drive(drive, 5); + } + return HWIF(drive)->dmaproc(dma_func, drive); +} + +static int slc90e66_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + switch (func) { + case ide_dma_check: + return config_drive_xfer_rate(drive); + default : + break; + } + /* Other cases are done by generic IDE-DMA code. */ + return ide_dmaproc(func, drive); +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +unsigned int __init pci_init_slc90e66 (struct pci_dev *dev, const char *name) +{ +#if defined(DISPLAY_SLC90E66_TIMINGS) && defined(CONFIG_PROC_FS) + if (!slc90e66_proc) { + slc90e66_proc = 1; + bmide_dev = dev; + slc90e66_display_info = &slc90e66_get_info; + } +#endif /* DISPLAY_SLC90E66_TIMINGS && CONFIG_PROC_FS */ + return 0; +} + +unsigned int __init ata66_slc90e66 (ide_hwif_t *hwif) +{ + byte reg47 = 0, ata66 = 0; + byte mask = hwif->channel ? 0x01 : 0x02; /* bit0:Primary */ + + pci_read_config_byte(hwif->pci_dev, 0x47, ®47); + ata66 = (reg47 & mask) ? 0 : 1; /* bit[0(1)]: 0:80, 1:40 */ + return ata66; +} + +void __init ide_init_slc90e66 (ide_hwif_t *hwif) +{ + if (!hwif->irq) + hwif->irq = hwif->channel ? 15 : 14; + + hwif->autodma = 0; + hwif->speedproc = &slc90e66_tune_chipset; + hwif->tuneproc = &slc90e66_tune_drive; + hwif->drives[0].autotune = 1; + hwif->drives[1].autotune = 1; + + if (!hwif->dma_base) + return; + +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->dmaproc = &slc90e66_dmaproc; +#ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +#endif /* CONFIG_IDEDMA_AUTO */ +#endif /* !CONFIG_BLK_DEV_IDEDMA */ +} diff -Nru a/drivers/ide/trm290.c b/drivers/ide/trm290.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/trm290.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,315 @@ +/* + * linux/drivers/ide/trm290.c Version 1.02 Mar. 18, 2000 + * + * Copyright (c) 1997-1998 Mark Lord + * May be copied or modified under the terms of the GNU General Public License + */ + +/* + * This module provides support for the bus-master IDE DMA function + * of the Tekram TRM290 chip, used on a variety of PCI IDE add-on boards, + * including a "Precision Instruments" board. The TRM290 pre-dates + * the sff-8038 standard (ide-dma.c) by a few months, and differs + * significantly enough to warrant separate routines for some functions, + * while re-using others from ide-dma.c. + * + * EXPERIMENTAL! It works for me (a sample of one). + * + * Works reliably for me in DMA mode (READs only), + * DMA WRITEs are disabled by default (see #define below); + * + * DMA is not enabled automatically for this chipset, + * but can be turned on manually (with "hdparm -d1") at run time. + * + * I need volunteers with "spare" drives for further testing + * and development, and maybe to help figure out the peculiarities. + * Even knowing the registers (below), some things behave strangely. + */ + +#define TRM290_NO_DMA_WRITES /* DMA writes seem unreliable sometimes */ + +/* + * TRM-290 PCI-IDE2 Bus Master Chip + * ================================ + * The configuration registers are addressed in normal I/O port space + * and are used as follows: + * + * trm290_base depends on jumper settings, and is probed for by ide-dma.c + * + * trm290_base+2 when WRITTEN: chiptest register (byte, write-only) + * bit7 must always be written as "1" + * bits6-2 undefined + * bit1 1=legacy_compatible_mode, 0=native_pci_mode + * bit0 1=test_mode, 0=normal(default) + * + * trm290_base+2 when READ: status register (byte, read-only) + * bits7-2 undefined + * bit1 channel0 busmaster interrupt status 0=none, 1=asserted + * bit0 channel0 interrupt status 0=none, 1=asserted + * + * trm290_base+3 Interrupt mask register + * bits7-5 undefined + * bit4 legacy_header: 1=present, 0=absent + * bit3 channel1 busmaster interrupt status 0=none, 1=asserted (read only) + * bit2 channel1 interrupt status 0=none, 1=asserted (read only) + * bit1 channel1 interrupt mask: 1=masked, 0=unmasked(default) + * bit0 channel0 interrupt mask: 1=masked, 0=unmasked(default) + * + * trm290_base+1 "CPR" Config Pointer Register (byte) + * bit7 1=autoincrement CPR bits 2-0 after each access of CDR + * bit6 1=min. 1 wait-state posted write cycle (default), 0=0 wait-state + * bit5 0=enabled master burst access (default), 1=disable (write only) + * bit4 PCI DEVSEL# timing select: 1=medium(default), 0=fast + * bit3 0=primary IDE channel, 1=secondary IDE channel + * bits2-0 register index for accesses through CDR port + * + * trm290_base+0 "CDR" Config Data Register (word) + * two sets of seven config registers, + * selected by CPR bit 3 (channel) and CPR bits 2-0 (index 0 to 6), + * each index defined below: + * + * Index-0 Base address register for command block (word) + * defaults: 0x1f0 for primary, 0x170 for secondary + * + * Index-1 general config register (byte) + * bit7 1=DMA enable, 0=DMA disable + * bit6 1=activate IDE_RESET, 0=no action (default) + * bit5 1=enable IORDY, 0=disable IORDY (default) + * bit4 0=16-bit data port(default), 1=8-bit (XT) data port + * bit3 interrupt polarity: 1=active_low, 0=active_high(default) + * bit2 power-saving-mode(?): 1=enable, 0=disable(default) (write only) + * bit1 bus_master_mode(?): 1=enable, 0=disable(default) + * bit0 enable_io_ports: 1=enable(default), 0=disable + * + * Index-2 read-ahead counter preload bits 0-7 (byte, write only) + * bits7-0 bits7-0 of readahead count + * + * Index-3 read-ahead config register (byte, write only) + * bit7 1=enable_readahead, 0=disable_readahead(default) + * bit6 1=clear_FIFO, 0=no_action + * bit5 undefined + * bit4 mode4 timing control: 1=enable, 0=disable(default) + * bit3 undefined + * bit2 undefined + * bits1-0 bits9-8 of read-ahead count + * + * Index-4 base address register for control block (word) + * defaults: 0x3f6 for primary, 0x376 for secondary + * + * Index-5 data port timings (shared by both drives) (byte) + * standard PCI "clk" (clock) counts, default value = 0xf5 + * + * bits7-6 setup time: 00=1clk, 01=2clk, 10=3clk, 11=4clk + * bits5-3 hold time: 000=1clk, 001=2clk, 010=3clk, + * 011=4clk, 100=5clk, 101=6clk, + * 110=8clk, 111=12clk + * bits2-0 active time: 000=2clk, 001=3clk, 010=4clk, + * 011=5clk, 100=6clk, 101=8clk, + * 110=12clk, 111=16clk + * + * Index-6 command/control port timings (shared by both drives) (byte) + * same layout as Index-5, default value = 0xde + * + * Suggested CDR programming for PIO mode0 (600ns): + * 0x01f0,0x21,0xff,0x80,0x03f6,0xf5,0xde ; primary + * 0x0170,0x21,0xff,0x80,0x0376,0xf5,0xde ; secondary + * + * Suggested CDR programming for PIO mode3 (180ns): + * 0x01f0,0x21,0xff,0x80,0x03f6,0x09,0xde ; primary + * 0x0170,0x21,0xff,0x80,0x0376,0x09,0xde ; secondary + * + * Suggested CDR programming for PIO mode4 (120ns): + * 0x01f0,0x21,0xff,0x80,0x03f6,0x00,0xde ; primary + * 0x0170,0x21,0xff,0x80,0x0376,0x00,0xde ; secondary + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static void trm290_prepare_drive (ide_drive_t *drive, unsigned int use_dma) +{ + ide_hwif_t *hwif = HWIF(drive); + unsigned int reg; + unsigned long flags; + + /* select PIO or DMA */ + reg = use_dma ? (0x21 | 0x82) : (0x21 & ~0x82); + + local_irq_save(flags); + + if (reg != hwif->select_data) { + hwif->select_data = reg; + /* set PIO/DMA */ + OUT_BYTE(0x51|(hwif->channel<<3), hwif->config_data+1); + OUT_WORD(reg & 0xff, hwif->config_data); + } + + /* enable IRQ if not probing */ + if (drive->present) { + reg = IN_WORD(hwif->config_data+3) & 0x13; + reg &= ~(1 << hwif->channel); + OUT_WORD(reg, hwif->config_data+3); + } + + local_irq_restore(flags); +} + +static void trm290_selectproc (ide_drive_t *drive) +{ + trm290_prepare_drive(drive, drive->using_dma); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA +static int trm290_dmaproc (ide_dma_action_t func, ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); +// ide_task_t *args = HWGROUP(drive)->rq->special; + unsigned int count, reading = 2, writing = 0; + + switch (func) { + case ide_dma_write: + reading = 0; + writing = 1; +#ifdef TRM290_NO_DMA_WRITES + break; /* always use PIO for writes */ +#endif + case ide_dma_read: + if (!(count = ide_build_dmatable(drive, func))) + /* try PIO instead of DMA */ + break; + /* select DMA xfer */ + trm290_prepare_drive(drive, 1); + outl(hwif->dmatable_dma|reading|writing, hwif->dma_base); + drive->waiting_for_dma = 1; + /* start DMA */ + OUT_WORD((count * 2) - 1, hwif->dma_base+2); + if (drive->media != ide_disk) + return 0; + if (HWGROUP(drive)->handler != NULL) /* paranoia check */ + BUG(); + ide_set_handler(drive, &ide_dma_intr, WAIT_CMD, NULL); +/* + * FIX ME to use only ACB ide_task_t args Struct + */ +#if 0 + { + ide_task_t *args = HWGROUP(drive)->rq->special; + OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } +#else + if (HWGROUP(drive)->rq->flags == REQ_DRIVE_TASKFILE) { + ide_task_t *args = HWGROUP(drive)->rq->special; + OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG); + } else if (drive->addressing == 1) + OUT_BYTE(reading ? WIN_READDMA_EXT : WIN_WRITEDMA_EXT, IDE_COMMAND_REG); + else + OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG); +#endif + return HWIF(drive)->dmaproc(ide_dma_begin, drive); + case ide_dma_begin: + return 0; + case ide_dma_end: + drive->waiting_for_dma = 0; + /* purge DMA mappings */ + ide_destroy_dmatable(drive); + return (IN_WORD(hwif->dma_base+2) != 0x00ff); + case ide_dma_test_irq: + return (IN_WORD(hwif->dma_base+2) == 0x00ff); + default: + return ide_dmaproc(func, drive); + } + trm290_prepare_drive(drive, 0); /* select PIO xfer */ + return 1; +} +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +/* + * Invoked from ide-dma.c at boot time. + */ +void __init ide_init_trm290 (ide_hwif_t *hwif) +{ + unsigned int cfgbase = 0; + unsigned long flags; + byte reg; + struct pci_dev *dev = hwif->pci_dev; + + hwif->chipset = ide_trm290; + cfgbase = pci_resource_start(dev, 4); + if ((dev->class & 5) && cfgbase) { + hwif->config_data = cfgbase; + printk("TRM290: chip config base at 0x%04lx\n", + hwif->config_data); + } else { + hwif->config_data = 0x3df0; + printk("TRM290: using default config base at 0x%04lx\n", + hwif->config_data); + } + + local_irq_save(flags); + /* put config reg into first byte of hwif->select_data */ + OUT_BYTE(0x51|(hwif->channel<<3), hwif->config_data+1); + /* select PIO as default */ + hwif->select_data = 0x21; + OUT_BYTE(hwif->select_data, hwif->config_data); + /* get IRQ info */ + reg = IN_BYTE(hwif->config_data+3); + /* mask IRQs for both ports */ + reg = (reg & 0x10) | 0x03; + OUT_BYTE(reg, hwif->config_data+3); + local_irq_restore(flags); + + if ((reg & 0x10)) + hwif->irq = hwif->channel ? 15 : 14; /* legacy mode */ + else if (!hwif->irq && hwif->mate && hwif->mate->irq) + hwif->irq = hwif->mate->irq; /* sharing IRQ with mate */ + ide_setup_dma(hwif, (hwif->config_data + 4) ^ (hwif->channel ? 0x0080 : 0x0000), 3); + +#ifdef CONFIG_BLK_DEV_IDEDMA + hwif->dmaproc = &trm290_dmaproc; +#endif /* CONFIG_BLK_DEV_IDEDMA */ + + hwif->selectproc = &trm290_selectproc; + hwif->autodma = 0; /* play it safe for now */ +#if 1 + { + /* + * My trm290-based card doesn't seem to work with all possible values + * for the control basereg, so this kludge ensures that we use only + * values that are known to work. Ugh. -ml + */ + unsigned short old, compat = hwif->channel ? 0x374 : 0x3f4; + static unsigned short next_offset = 0; + + OUT_BYTE(0x54|(hwif->channel<<3), hwif->config_data+1); + old = IN_WORD(hwif->config_data) & ~1; + if (old != compat && IN_BYTE(old+2) == 0xff) { + compat += (next_offset += 0x400); /* leave lower 10 bits untouched */ +#if 1 + if (ide_check_region(compat + 2, 1)) + printk("Aieee %s: ide_check_region failure at 0x%04x\n", hwif->name, (compat + 2)); + /* + * The region check is not needed; however......... + * Since this is the checked in ide-probe.c, + * this is only an assignment. + */ +#endif + hwif->io_ports[IDE_CONTROL_OFFSET] = compat + 2; + OUT_WORD(compat|1, hwif->config_data); + printk("%s: control basereg workaround: old=0x%04x, new=0x%04x\n", hwif->name, old, IN_WORD(hwif->config_data) & ~1); + } + } +#endif +} diff -Nru a/drivers/ide/umc8672.c b/drivers/ide/umc8672.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/umc8672.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,156 @@ +/* + * linux/drivers/ide/umc8672.c Version 0.05 Jul 31, 1996 + * + * Copyright (C) 1995-1996 Linus Torvalds & author (see below) + */ + +/* + * Principal Author/Maintainer: PODIEN@hml2.atlas.de (Wolfram Podien) + * + * This file provides support for the advanced features + * of the UMC 8672 IDE interface. + * + * Version 0.01 Initial version, hacked out of ide.c, + * and #include'd rather than compiled separately. + * This will get cleaned up in a subsequent release. + * + * Version 0.02 now configs/compiles separate from ide.c -ml + * Version 0.03 enhanced auto-tune, fix display bug + * Version 0.05 replace sti() with restore_flags() -ml + * add detection of possible race condition -ml + */ + +/* + * VLB Controller Support from + * Wolfram Podien + * Rohoefe 3 + * D28832 Achim + * Germany + * + * To enable UMC8672 support there must a lilo line like + * append="ide0=umc8672"... + * To set the speed according to the abilities of the hardware there must be a + * line like + * #define UMC_DRIVE0 11 + * in the beginning of the driver, which sets the speed of drive 0 to 11 (there + * are some lines present). 0 - 11 are allowed speed values. These values are + * the results from the DOS speed test program supplied from UMC. 11 is the + * highest speed (about PIO mode 3) + */ +#define REALLY_SLOW_IO /* some systems can safely undef this */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ide_modes.h" + +/* + * Default speeds. These can be changed with "auto-tune" and/or hdparm. + */ +#define UMC_DRIVE0 1 /* DOS measured drive speeds */ +#define UMC_DRIVE1 1 /* 0 to 11 allowed */ +#define UMC_DRIVE2 1 /* 11 = Fastest Speed */ +#define UMC_DRIVE3 1 /* In case of crash reduce speed */ + +static byte current_speeds[4] = {UMC_DRIVE0, UMC_DRIVE1, UMC_DRIVE2, UMC_DRIVE3}; +static const byte pio_to_umc [5] = {0,3,7,10,11}; /* rough guesses */ + +/* 0 1 2 3 4 5 6 7 8 9 10 11 */ +static const byte speedtab [3][12] = { + {0xf, 0xb, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }, + {0x3, 0x2, 0x2, 0x2, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1 }, + {0xff,0xcb,0xc0,0x58,0x36,0x33,0x23,0x22,0x21,0x11,0x10,0x0}}; + +static void out_umc (char port,char wert) +{ + outb_p(port,0x108); + outb_p(wert,0x109); +} + +static inline byte in_umc (char port) +{ + outb_p(port,0x108); + return inb_p(0x109); +} + +static void umc_set_speeds (byte speeds[]) +{ + int i, tmp; + + outb_p(0x5A,0x108); /* enable umc */ + + out_umc (0xd7,(speedtab[0][speeds[2]] | (speedtab[0][speeds[3]]<<4))); + out_umc (0xd6,(speedtab[0][speeds[0]] | (speedtab[0][speeds[1]]<<4))); + tmp = 0; + for (i = 3; i >= 0; i--) { + tmp = (tmp << 2) | speedtab[1][speeds[i]]; + } + out_umc (0xdc,tmp); + for (i = 0;i < 4; i++) { + out_umc (0xd0+i,speedtab[2][speeds[i]]); + out_umc (0xd8+i,speedtab[2][speeds[i]]); + } + outb_p(0xa5,0x108); /* disable umc */ + + printk ("umc8672: drive speeds [0 to 11]: %d %d %d %d\n", + speeds[0], speeds[1], speeds[2], speeds[3]); +} + +static void tune_umc (ide_drive_t *drive, byte pio) +{ + unsigned long flags; + ide_hwgroup_t *hwgroup = ide_hwifs[HWIF(drive)->index^1].hwgroup; + + pio = ide_get_best_pio_mode(drive, pio, 4, NULL); + printk("%s: setting umc8672 to PIO mode%d (speed %d)\n", + drive->name, pio, pio_to_umc[pio]); + spin_lock_irqsave(&ide_lock, flags); + if (hwgroup && hwgroup->handler != NULL) { + printk("umc8672: other interface is busy: exiting tune_umc()\n"); + } else { + current_speeds[drive->name[2] - 'a'] = pio_to_umc[pio]; + umc_set_speeds (current_speeds); + } + spin_unlock_irqrestore(&ide_lock, flags); +} + +void __init init_umc8672 (void) /* called from ide.c */ +{ + unsigned long flags; + + local_irq_save(flags); + if (check_region(0x108, 2)) { + local_irq_restore(flags); + printk("\numc8672: PORTS 0x108-0x109 ALREADY IN USE\n"); + return; + } + outb_p(0x5A,0x108); /* enable umc */ + if (in_umc (0xd5) != 0xa0) { + local_irq_restore(flags); + printk ("umc8672: not found\n"); + return; + } + outb_p(0xa5,0x108); /* disable umc */ + + umc_set_speeds (current_speeds); + local_irq_restore(flags); + + request_region(0x108, 2, "umc8672"); + ide_hwifs[0].chipset = ide_umc8672; + ide_hwifs[1].chipset = ide_umc8672; + ide_hwifs[0].tuneproc = &tune_umc; + ide_hwifs[1].tuneproc = &tune_umc; + ide_hwifs[0].mate = &ide_hwifs[1]; + ide_hwifs[1].mate = &ide_hwifs[0]; + ide_hwifs[1].channel = 1; +} diff -Nru a/drivers/ide/via82cxxx.c b/drivers/ide/via82cxxx.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/ide/via82cxxx.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,565 @@ +/* + * $Id: via82cxxx.c,v 3.34 2002/02/12 11:26:11 vojtech Exp $ + * + * Copyright (c) 2000-2001 Vojtech Pavlik + * + * Based on the work of: + * Michel Aubry + * Jeff Garzik + * Andre Hedrick + */ + +/* + * VIA IDE driver for Linux. Supports + * + * vt82c576, vt82c586, vt82c586a, vt82c586b, vt82c596a, vt82c596b, + * vt82c686, vt82c686a, vt82c686b, vt8231, vt8233, vt8233c, vt8233a + * + * southbridges, which can be found in + * + * VIA Apollo Master, VP, VP2, VP2/97, VP3, VPX, VPX/97, MVP3, MVP4, P6, Pro, + * ProII, ProPlus, Pro133, Pro133+, Pro133A, Pro133A Dual, Pro133T, Pro133Z, + * PLE133, PLE133T, Pro266, Pro266T, ProP4X266, PM601, PM133, PN133, PL133T, + * PX266, PM266, KX133, KT133, KT133A, KT133E, KLE133, KT266, KX266, KM133, + * KM133A, KL133, KN133, KM266 + * PC-Chips VXPro, VXPro+, VXTwo, TXPro-III, TXPro-AGP, AGPPro, ViaGra, BXToo, + * BXTel, BXpert + * AMD 640, 640 AGP, 750 IronGate, 760, 760MP + * ETEQ 6618, 6628, 6629, 6638 + * Micron Samurai + * + * chipsets. Supports + * + * PIO 0-5, MWDMA 0-2, SWDMA 0-2 and UDMA 0-6 + * + * (this includes UDMA33, 66, 100 and 133) modes. UDMA66 and higher modes are + * autoenabled only in case the BIOS has detected a 80 wire cable. To ignore + * the BIOS data and assume the cable is present, use 'ide0=ata66' or + * 'ide1=ata66' on the kernel command line. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ide-timing.h" + +#define VIA_IDE_ENABLE 0x40 +#define VIA_IDE_CONFIG 0x41 +#define VIA_FIFO_CONFIG 0x43 +#define VIA_MISC_1 0x44 +#define VIA_MISC_2 0x45 +#define VIA_MISC_3 0x46 +#define VIA_DRIVE_TIMING 0x48 +#define VIA_8BIT_TIMING 0x4e +#define VIA_ADDRESS_SETUP 0x4c +#define VIA_UDMA_TIMING 0x50 + +#define VIA_UDMA 0x007 +#define VIA_UDMA_NONE 0x000 +#define VIA_UDMA_33 0x001 +#define VIA_UDMA_66 0x002 +#define VIA_UDMA_100 0x003 +#define VIA_UDMA_133 0x004 +#define VIA_BAD_PREQ 0x010 /* Crashes if PREQ# till DDACK# set */ +#define VIA_BAD_CLK66 0x020 /* 66 MHz clock doesn't work correctly */ +#define VIA_SET_FIFO 0x040 /* Needs to have FIFO split set */ +#define VIA_NO_UNMASK 0x080 /* Doesn't work with IRQ unmasking on */ +#define VIA_BAD_ID 0x100 /* Has wrong vendor ID (0x1107) */ + +/* + * VIA SouthBridge chips. + */ + +static struct via_isa_bridge { + char *name; + unsigned short id; + unsigned char rev_min; + unsigned char rev_max; + unsigned short flags; +} via_isa_bridges[] = { +#ifdef FUTURE_BRIDGES + { "vt8237", PCI_DEVICE_ID_VIA_8237, 0x00, 0x2f, VIA_UDMA_133 }, + { "vt8235", PCI_DEVICE_ID_VIA_8235, 0x00, 0x2f, VIA_UDMA_133 }, +#endif + { "vt8233a", PCI_DEVICE_ID_VIA_8233A, 0x00, 0x2f, VIA_UDMA_133 }, + { "vt8233c", PCI_DEVICE_ID_VIA_8233C_0, 0x00, 0x2f, VIA_UDMA_100 }, + { "vt8233", PCI_DEVICE_ID_VIA_8233_0, 0x00, 0x2f, VIA_UDMA_100 }, + { "vt8231", PCI_DEVICE_ID_VIA_8231, 0x00, 0x2f, VIA_UDMA_100 }, + { "vt82c686b", PCI_DEVICE_ID_VIA_82C686, 0x40, 0x4f, VIA_UDMA_100 }, + { "vt82c686a", PCI_DEVICE_ID_VIA_82C686, 0x10, 0x2f, VIA_UDMA_66 }, + { "vt82c686", PCI_DEVICE_ID_VIA_82C686, 0x00, 0x0f, VIA_UDMA_33 | VIA_BAD_CLK66 }, + { "vt82c596b", PCI_DEVICE_ID_VIA_82C596, 0x10, 0x2f, VIA_UDMA_66 }, + { "vt82c596a", PCI_DEVICE_ID_VIA_82C596, 0x00, 0x0f, VIA_UDMA_33 | VIA_BAD_CLK66 }, + { "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x47, 0x4f, VIA_UDMA_33 | VIA_SET_FIFO }, + { "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x40, 0x46, VIA_UDMA_33 | VIA_SET_FIFO | VIA_BAD_PREQ }, + { "vt82c586b", PCI_DEVICE_ID_VIA_82C586_0, 0x30, 0x3f, VIA_UDMA_33 | VIA_SET_FIFO }, + { "vt82c586a", PCI_DEVICE_ID_VIA_82C586_0, 0x20, 0x2f, VIA_UDMA_33 | VIA_SET_FIFO }, + { "vt82c586", PCI_DEVICE_ID_VIA_82C586_0, 0x00, 0x0f, VIA_UDMA_NONE | VIA_SET_FIFO }, + { "vt82c576", PCI_DEVICE_ID_VIA_82C576, 0x00, 0x2f, VIA_UDMA_NONE | VIA_SET_FIFO | VIA_NO_UNMASK }, + { "vt82c576", PCI_DEVICE_ID_VIA_82C576, 0x00, 0x2f, VIA_UDMA_NONE | VIA_SET_FIFO | VIA_NO_UNMASK | VIA_BAD_ID }, + { NULL } +}; + +static struct via_isa_bridge *via_config; +static unsigned char via_enabled; +static unsigned int via_80w; +static unsigned int via_clock; +static char *via_dma[] = { "MWDMA16", "UDMA33", "UDMA66", "UDMA100", "UDMA133" }; + +/* + * VIA /proc entry. + */ + +#ifdef CONFIG_PROC_FS + +#include +#include + +int via_proc, via_base; +static struct pci_dev *bmide_dev, *isa_dev; +extern int (*via_display_info)(char *, char **, off_t, int); /* ide-proc.c */ + +static char *via_control3[] = { "No limit", "64", "128", "192" }; + +#define via_print(format, arg...) p += sprintf(p, format "\n" , ## arg) +#define via_print_drive(name, format, arg...)\ + p += sprintf(p, name); for (i = 0; i < 4; i++) p += sprintf(p, format, ## arg); p += sprintf(p, "\n"); + +static int via_get_info(char *buffer, char **addr, off_t offset, int count) +{ + int speed[4], cycle[4], setup[4], active[4], recover[4], den[4], + uen[4], udma[4], umul[4], active8b[4], recover8b[4]; + struct pci_dev *dev = bmide_dev; + unsigned int v, u, i; + unsigned short c, w; + unsigned char t, x; + char *p = buffer; + + via_print("----------VIA BusMastering IDE Configuration----------------"); + + via_print("Driver Version: 3.34"); + via_print("South Bridge: VIA %s", via_config->name); + + pci_read_config_byte(isa_dev, PCI_REVISION_ID, &t); + pci_read_config_byte(dev, PCI_REVISION_ID, &x); + via_print("Revision: ISA %#x IDE %#x", t, x); + via_print("Highest DMA rate: %s", via_dma[via_config->flags & VIA_UDMA]); + + via_print("BM-DMA base: %#x", via_base); + via_print("PCI clock: %d.%dMHz", via_clock / 1000, via_clock / 100 % 10); + + pci_read_config_byte(dev, VIA_MISC_1, &t); + via_print("Master Read Cycle IRDY: %dws", (t & 64) >> 6); + via_print("Master Write Cycle IRDY: %dws", (t & 32) >> 5); + via_print("BM IDE Status Register Read Retry: %s", (t & 8) ? "yes" : "no"); + + pci_read_config_byte(dev, VIA_MISC_3, &t); + via_print("Max DRDY Pulse Width: %s%s", via_control3[(t & 0x03)], (t & 0x03) ? " PCI clocks" : ""); + + via_print("-----------------------Primary IDE-------Secondary IDE------"); + via_print("Read DMA FIFO flush: %10s%20s", (t & 0x80) ? "yes" : "no", (t & 0x40) ? "yes" : "no"); + via_print("End Sector FIFO flush: %10s%20s", (t & 0x20) ? "yes" : "no", (t & 0x10) ? "yes" : "no"); + + pci_read_config_byte(dev, VIA_IDE_CONFIG, &t); + via_print("Prefetch Buffer: %10s%20s", (t & 0x80) ? "yes" : "no", (t & 0x20) ? "yes" : "no"); + via_print("Post Write Buffer: %10s%20s", (t & 0x40) ? "yes" : "no", (t & 0x10) ? "yes" : "no"); + + pci_read_config_byte(dev, VIA_IDE_ENABLE, &t); + via_print("Enabled: %10s%20s", (t & 0x02) ? "yes" : "no", (t & 0x01) ? "yes" : "no"); + + c = IN_BYTE(via_base + 0x02) | (IN_BYTE(via_base + 0x0a) << 8); + via_print("Simplex only: %10s%20s", (c & 0x80) ? "yes" : "no", (c & 0x8000) ? "yes" : "no"); + + via_print("Cable Type: %10s%20s", (via_80w & 1) ? "80w" : "40w", (via_80w & 2) ? "80w" : "40w"); + + via_print("-------------------drive0----drive1----drive2----drive3-----"); + + pci_read_config_byte(dev, VIA_ADDRESS_SETUP, &t); + pci_read_config_dword(dev, VIA_DRIVE_TIMING, &v); + pci_read_config_word(dev, VIA_8BIT_TIMING, &w); + + if (via_config->flags & VIA_UDMA) + pci_read_config_dword(dev, VIA_UDMA_TIMING, &u); + else u = 0; + + for (i = 0; i < 4; i++) { + + setup[i] = ((t >> ((3 - i) << 1)) & 0x3) + 1; + recover8b[i] = ((w >> ((1 - (i >> 1)) << 3)) & 0xf) + 1; + active8b[i] = ((w >> (((1 - (i >> 1)) << 3) + 4)) & 0xf) + 1; + active[i] = ((v >> (((3 - i) << 3) + 4)) & 0xf) + 1; + recover[i] = ((v >> ((3 - i) << 3)) & 0xf) + 1; + udma[i] = ((u >> ((3 - i) << 3)) & 0x7) + 2; + umul[i] = ((u >> (((3 - i) & 2) << 3)) & 0x8) ? 1 : 2; + uen[i] = ((u >> ((3 - i) << 3)) & 0x20); + den[i] = (c & ((i & 1) ? 0x40 : 0x20) << ((i & 2) << 2)); + + speed[i] = 2 * via_clock / (active[i] + recover[i]); + cycle[i] = 1000000 * (active[i] + recover[i]) / via_clock; + + if (!uen[i] || !den[i]) + continue; + + switch (via_config->flags & VIA_UDMA) { + + case VIA_UDMA_33: + speed[i] = 2 * via_clock / udma[i]; + cycle[i] = 1000000 * udma[i] / via_clock; + break; + + case VIA_UDMA_66: + speed[i] = 4 * via_clock / (udma[i] * umul[i]); + cycle[i] = 500000 * (udma[i] * umul[i]) / via_clock; + break; + + case VIA_UDMA_100: + speed[i] = 6 * via_clock / udma[i]; + cycle[i] = 333333 * udma[i] / via_clock; + break; + + case VIA_UDMA_133: + speed[i] = 8 * via_clock / udma[i]; + cycle[i] = 250000 * udma[i] / via_clock; + break; + } + } + + via_print_drive("Transfer Mode: ", "%10s", den[i] ? (uen[i] ? "UDMA" : "DMA") : "PIO"); + + via_print_drive("Address Setup: ", "%8dns", 1000000 * setup[i] / via_clock); + via_print_drive("Cmd Active: ", "%8dns", 1000000 * active8b[i] / via_clock); + via_print_drive("Cmd Recovery: ", "%8dns", 1000000 * recover8b[i] / via_clock); + via_print_drive("Data Active: ", "%8dns", 1000000 * active[i] / via_clock); + via_print_drive("Data Recovery: ", "%8dns", 1000000 * recover[i] / via_clock); + via_print_drive("Cycle Time: ", "%8dns", cycle[i]); + via_print_drive("Transfer Rate: ", "%4d.%dMB/s", speed[i] / 1000, speed[i] / 100 % 10); + + return p - buffer; /* hoping it is less than 4K... */ +} + +#endif + +/* + * via_set_speed() writes timing values to the chipset registers + */ + +static void via_set_speed(struct pci_dev *dev, unsigned char dn, struct ide_timing *timing) +{ + unsigned char t; + + pci_read_config_byte(dev, VIA_ADDRESS_SETUP, &t); + t = (t & ~(3 << ((3 - dn) << 1))) | ((FIT(timing->setup, 1, 4) - 1) << ((3 - dn) << 1)); + pci_write_config_byte(dev, VIA_ADDRESS_SETUP, t); + + pci_write_config_byte(dev, VIA_8BIT_TIMING + (1 - (dn >> 1)), + ((FIT(timing->act8b, 1, 16) - 1) << 4) | (FIT(timing->rec8b, 1, 16) - 1)); + + pci_write_config_byte(dev, VIA_DRIVE_TIMING + (3 - dn), + ((FIT(timing->active, 1, 16) - 1) << 4) | (FIT(timing->recover, 1, 16) - 1)); + + switch (via_config->flags & VIA_UDMA) { + case VIA_UDMA_33: t = timing->udma ? (0xe0 | (FIT(timing->udma, 2, 5) - 2)) : 0x03; break; + case VIA_UDMA_66: t = timing->udma ? (0xe8 | (FIT(timing->udma, 2, 9) - 2)) : 0x0f; break; + case VIA_UDMA_100: t = timing->udma ? (0xe0 | (FIT(timing->udma, 2, 9) - 2)) : 0x07; break; + case VIA_UDMA_133: t = timing->udma ? (0xe0 | (FIT(timing->udma, 2, 9) - 2)) : 0x07; break; + default: return; + } + + pci_write_config_byte(dev, VIA_UDMA_TIMING + (3 - dn), t); +} + +/* + * via_set_drive() computes timing values configures the drive and + * the chipset to a desired transfer mode. It also can be called + * by upper layers. + */ + +static int via_set_drive(ide_drive_t *drive, unsigned char speed) +{ + ide_drive_t *peer = HWIF(drive)->drives + (~drive->dn & 1); + struct ide_timing t, p; + unsigned int T, UT; + + if (speed != XFER_PIO_SLOW && speed != drive->current_speed) + if (ide_config_drive_speed(drive, speed)) + printk(KERN_WARNING "ide%d: Drive %d didn't accept speed setting. Oh, well.\n", + drive->dn >> 1, drive->dn & 1); + + T = 1000000000 / via_clock; + + switch (via_config->flags & VIA_UDMA) { + case VIA_UDMA_33: UT = T; break; + case VIA_UDMA_66: UT = T/2; break; + case VIA_UDMA_100: UT = T/3; break; + case VIA_UDMA_133: UT = T/4; break; + default: UT = T; + } + + ide_timing_compute(drive, speed, &t, T, UT); + + if (peer->present) { + ide_timing_compute(peer, peer->current_speed, &p, T, UT); + ide_timing_merge(&p, &t, &t, IDE_TIMING_8BIT); + } + + via_set_speed(HWIF(drive)->pci_dev, drive->dn, &t); + + if (!drive->init_speed) + drive->init_speed = speed; + drive->current_speed = speed; + + return 0; +} + +/* + * via82cxxx_tune_drive() is a callback from upper layers for + * PIO-only tuning. + */ + +static void via82cxxx_tune_drive(ide_drive_t *drive, unsigned char pio) +{ + if (!((via_enabled >> HWIF(drive)->channel) & 1)) + return; + + if (pio == 255) { + via_set_drive(drive, ide_find_best_mode(drive, XFER_PIO | XFER_EPIO)); + return; + } + + via_set_drive(drive, XFER_PIO_0 + MIN(pio, 5)); +} + +#ifdef CONFIG_BLK_DEV_IDEDMA + +/* + * via82cxxx_dmaproc() is a callback from upper layers that can do + * a lot, but we use it for DMA/PIO tuning only, delegating everything + * else to the default ide_dmaproc(). + */ + +int via82cxxx_dmaproc(ide_dma_action_t func, ide_drive_t *drive) +{ + + if (func == ide_dma_check) { + + short w80 = HWIF(drive)->udma_four; + + short speed = ide_find_best_mode(drive, + XFER_PIO | XFER_EPIO | XFER_SWDMA | XFER_MWDMA | + (via_config->flags & VIA_UDMA ? XFER_UDMA : 0) | + (w80 && (via_config->flags & VIA_UDMA) >= VIA_UDMA_66 ? XFER_UDMA_66 : 0) | + (w80 && (via_config->flags & VIA_UDMA) >= VIA_UDMA_100 ? XFER_UDMA_100 : 0) | + (w80 && (via_config->flags & VIA_UDMA) >= VIA_UDMA_133 ? XFER_UDMA_133 : 0)); + + via_set_drive(drive, speed); + + func = (HWIF(drive)->autodma && (speed & XFER_MODE) != XFER_PIO) + ? ide_dma_on : ide_dma_off_quietly; + } + + return ide_dmaproc(func, drive); +} + +#endif /* CONFIG_BLK_DEV_IDEDMA */ + +/* + * The initialization callback. Here we determine the IDE chip type + * and initialize its drive independent registers. + */ + +unsigned int __init pci_init_via82cxxx(struct pci_dev *dev, const char *name) +{ + struct pci_dev *isa = NULL; + unsigned char t, v; + unsigned int u; + int i; + +/* + * Find the ISA bridge to see how good the IDE is. + */ + + for (via_config = via_isa_bridges; via_config->id; via_config++) + if ((isa = pci_find_device(PCI_VENDOR_ID_VIA + + !!(via_config->flags & VIA_BAD_ID), via_config->id, NULL))) { + + pci_read_config_byte(isa, PCI_REVISION_ID, &t); + if (t >= via_config->rev_min && t <= via_config->rev_max) + break; + } + + if (!via_config->id) { + printk(KERN_WARNING "VP_IDE: Unknown VIA SouthBridge, contact Vojtech Pavlik \n"); + return -ENODEV; + } + +/* + * Check 80-wire cable presence and setup Clk66. + */ + + switch (via_config->flags & VIA_UDMA) { + + case VIA_UDMA_66: + pci_read_config_dword(dev, VIA_UDMA_TIMING, &u); /* Enable Clk66 */ + pci_write_config_dword(dev, VIA_UDMA_TIMING, u | 0x80008); + for (i = 24; i >= 0; i -= 8) + if (((u >> (i & 16)) & 8) && ((u >> i) & 0x20) && (((u >> i) & 7) < 2)) + via_80w |= (1 << (1 - (i >> 4))); /* 2x PCI clock and UDMA w/ < 3T/cycle */ + break; + + case VIA_UDMA_100: + pci_read_config_dword(dev, VIA_UDMA_TIMING, &u); + for (i = 24; i >= 0; i -= 8) + if (((u >> i) & 0x10) || (((u >> i) & 0x20) && (((u >> i) & 7) < 4))) + via_80w |= (1 << (1 - (i >> 4))); /* BIOS 80-wire bit or UDMA w/ < 60ns/cycle */ + break; + + case VIA_UDMA_133: + pci_read_config_dword(dev, VIA_UDMA_TIMING, &u); + for (i = 24; i >= 0; i -= 8) + if (((u >> i) & 0x10) || (((u >> i) & 0x20) && (((u >> i) & 7) < 8))) + via_80w |= (1 << (1 - (i >> 4))); /* BIOS 80-wire bit or UDMA w/ < 60ns/cycle */ + break; + + } + + if (via_config->flags & VIA_BAD_CLK66) { /* Disable Clk66 */ + pci_read_config_dword(dev, VIA_UDMA_TIMING, &u); /* Would cause trouble on 596a and 686 */ + pci_write_config_dword(dev, VIA_UDMA_TIMING, u & ~0x80008); + } + +/* + * Check whether interfaces are enabled. + */ + + pci_read_config_byte(dev, VIA_IDE_ENABLE, &v); + via_enabled = ((v & 1) ? 2 : 0) | ((v & 2) ? 1 : 0); + +/* + * Set up FIFO sizes and thresholds. + */ + + pci_read_config_byte(dev, VIA_FIFO_CONFIG, &t); + + if (via_config->flags & VIA_BAD_PREQ) /* Disable PREQ# till DDACK# */ + t &= 0x7f; /* Would crash on 586b rev 41 */ + + if (via_config->flags & VIA_SET_FIFO) { /* Fix FIFO split between channels */ + t &= (t & 0x9f); + switch (via_enabled) { + case 1: t |= 0x00; break; /* 16 on primary */ + case 2: t |= 0x60; break; /* 16 on secondary */ + case 3: t |= 0x20; break; /* 8 pri 8 sec */ + } + } + + pci_write_config_byte(dev, VIA_FIFO_CONFIG, t); + +/* + * Determine system bus clock. + */ + + via_clock = system_bus_clock() * 1000; + + switch (via_clock) { + case 33000: via_clock = 33333; break; + case 37000: via_clock = 37500; break; + case 41000: via_clock = 41666; break; + } + + if (via_clock < 20000 || via_clock > 50000) { + printk(KERN_WARNING "VP_IDE: User given PCI clock speed impossible (%d), using 33 MHz instead.\n", via_clock); + printk(KERN_WARNING "VP_IDE: Use ide0=ata66 if you want to assume 80-wire cable.\n"); + via_clock = 33333; + } + +/* + * Print the boot message. + */ + + pci_read_config_byte(isa, PCI_REVISION_ID, &t); + printk(KERN_INFO "VP_IDE: VIA %s (rev %02x) IDE %s controller on pci%s\n", + via_config->name, t, via_dma[via_config->flags & VIA_UDMA], dev->slot_name); + +/* + * Setup /proc/ide/via entry. + */ + +#ifdef CONFIG_PROC_FS + if (!via_proc) { + via_base = pci_resource_start(dev, 4); + bmide_dev = dev; + isa_dev = isa; + via_display_info = &via_get_info; + via_proc = 1; + } +#endif + + return 0; +} + +unsigned int __init ata66_via82cxxx(ide_hwif_t *hwif) +{ + return ((via_enabled & via_80w) >> hwif->channel) & 1; +} + +void __init ide_init_via82cxxx(ide_hwif_t *hwif) +{ + int i; + + hwif->tuneproc = &via82cxxx_tune_drive; + hwif->speedproc = &via_set_drive; + hwif->autodma = 0; + + for (i = 0; i < 2; i++) { + hwif->drives[i].io_32bit = 1; + hwif->drives[i].unmask = (via_config->flags & VIA_NO_UNMASK) ? 0 : 1; + hwif->drives[i].autotune = 1; + hwif->drives[i].dn = hwif->channel * 2 + i; + } + +#ifdef CONFIG_BLK_DEV_IDEDMA + if (hwif->dma_base) { + hwif->dmaproc = &via82cxxx_dmaproc; +#ifdef CONFIG_IDEDMA_AUTO + if (!noautodma) + hwif->autodma = 1; +#endif + } +#endif /* CONFIG_BLK_DEV_IDEDMA */ +} + +/* + * We allow the BM-DMA driver to only work on enabled interfaces. + */ + +void __init ide_dmacapable_via82cxxx(ide_hwif_t *hwif, unsigned long dmabase) +{ + if ((via_enabled >> hwif->channel) & 1) + ide_setup_dma(hwif, dmabase, 8); +} diff -Nru a/drivers/isdn/Config.in b/drivers/isdn/Config.in --- a/drivers/isdn/Config.in Fri Aug 16 14:34:58 2002 +++ b/drivers/isdn/Config.in Fri Aug 16 14:34:58 2002 @@ -22,8 +22,9 @@ tristate 'CAPI2.0 support' CONFIG_ISDN_CAPI if [ "$CONFIG_ISDN_CAPI" != "n" ]; then source drivers/isdn/capi/Config.in - source drivers/isdn/hardware/Config.in fi + + source drivers/isdn/hardware/Config.in fi fi endmenu diff -Nru a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c --- a/drivers/isdn/capi/capi.c Fri Aug 16 14:35:01 2002 +++ b/drivers/isdn/capi/capi.c Fri Aug 16 14:35:01 2002 @@ -957,14 +957,14 @@ static struct file_operations capi_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: capi_read, - write: capi_write, - poll: capi_poll, - ioctl: capi_ioctl, - open: capi_open, - release: capi_release, + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = capi_read, + .write = capi_write, + .poll = capi_poll, + .ioctl = capi_ioctl, + .open = capi_open, + .release = capi_release, }; #ifdef CONFIG_ISDN_CAPI_MIDDLEWARE diff -Nru a/drivers/isdn/capi/capifs.c b/drivers/isdn/capi/capifs.c --- a/drivers/isdn/capi/capifs.c Fri Aug 16 14:34:55 2002 +++ b/drivers/isdn/capi/capifs.c Fri Aug 16 14:34:55 2002 @@ -72,16 +72,16 @@ static struct inode *capifs_new_inode(struct super_block *sb); static struct file_operations capifs_root_operations = { - read: generic_read_dir, - readdir: capifs_root_readdir, + .read = generic_read_dir, + .readdir = capifs_root_readdir, }; struct inode_operations capifs_root_inode_operations = { - lookup: capifs_root_lookup, + .lookup = capifs_root_lookup, }; static struct dentry_operations capifs_dentry_operations = { - d_revalidate: capifs_revalidate, + .d_revalidate = capifs_revalidate, }; /* @@ -222,8 +222,8 @@ } static struct super_operations capifs_sops = { - put_super: capifs_put_super, - statfs: simple_statfs, + .put_super = capifs_put_super, + .statfs = simple_statfs, }; static int capifs_parse_options(char *options, struct capifs_sb_info *sbi) @@ -371,10 +371,10 @@ } static struct file_system_type capifs_fs_type = { - owner: THIS_MODULE, - name: "capifs", - get_sb: capifs_get_sb, - kill_sb: kill_anon_super, + .owner = THIS_MODULE, + .name = "capifs", + .get_sb = capifs_get_sb, + .kill_sb = kill_anon_super, }; void capifs_new_ncci(char type, unsigned int num, kdev_t device) diff -Nru a/drivers/isdn/capi/capilib.c b/drivers/isdn/capi/capilib.c --- a/drivers/isdn/capi/capilib.c Fri Aug 16 14:35:01 2002 +++ b/drivers/isdn/capi/capilib.c Fri Aug 16 14:35:01 2002 @@ -4,7 +4,7 @@ #include #define DBG(format, arg...) do { \ -printk(KERN_DEBUG __FUNCTION__ ": " format "\n" , ## arg); \ +printk(KERN_DEBUG "%s: " format "\n" , __FUNCTION__ , ## arg); \ } while (0) struct capilib_msgidqueue { diff -Nru a/drivers/isdn/capi/kcapi.h b/drivers/isdn/capi/kcapi.h --- a/drivers/isdn/capi/kcapi.h Fri Aug 16 14:34:55 2002 +++ b/drivers/isdn/capi/kcapi.h Fri Aug 16 14:34:55 2002 @@ -16,7 +16,7 @@ #include #define DBG(format, arg...) do { \ -printk(KERN_INFO __FUNCTION__ ": " format "\n" , ## arg); \ +printk(KERN_DEBUG "%s: " format "\n" , __FUNCTION__ , ## arg); \ } while (0) diff -Nru a/drivers/isdn/capi/kcapi_proc.c b/drivers/isdn/capi/kcapi_proc.c --- a/drivers/isdn/capi/kcapi_proc.c Fri Aug 16 14:34:58 2002 +++ b/drivers/isdn/capi/kcapi_proc.c Fri Aug 16 14:34:58 2002 @@ -90,17 +90,17 @@ } struct seq_operations seq_controller_ops = { - start: controller_start, - next: controller_next, - stop: controller_stop, - show: controller_show, + .start = controller_start, + .next = controller_next, + .stop = controller_stop, + .show = controller_show, }; struct seq_operations seq_contrstats_ops = { - start: controller_start, - next: controller_next, - stop: controller_stop, - show: contrstats_show, + .start = controller_start, + .next = controller_next, + .stop = controller_stop, + .show = contrstats_show, }; static int seq_controller_open(struct inode *inode, struct file *file) @@ -114,17 +114,17 @@ } static struct file_operations proc_controller_ops = { - open: seq_controller_open, - read: seq_read, - llseek: seq_lseek, - release: seq_release, + .open = seq_controller_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, }; static struct file_operations proc_contrstats_ops = { - open: seq_contrstats_open, - read: seq_read, - llseek: seq_lseek, - release: seq_release, + .open = seq_contrstats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, }; // /proc/capi/applications: @@ -193,17 +193,17 @@ } struct seq_operations seq_applications_ops = { - start: applications_start, - next: applications_next, - stop: applications_stop, - show: applications_show, + .start = applications_start, + .next = applications_next, + .stop = applications_stop, + .show = applications_show, }; struct seq_operations seq_applstats_ops = { - start: applications_start, - next: applications_next, - stop: applications_stop, - show: applstats_show, + .start = applications_start, + .next = applications_next, + .stop = applications_stop, + .show = applstats_show, }; static int @@ -219,17 +219,17 @@ } static struct file_operations proc_applications_ops = { - open: seq_applications_open, - read: seq_read, - llseek: seq_lseek, - release: seq_release, + .open = seq_applications_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, }; static struct file_operations proc_applstats_ops = { - open: seq_applstats_open, - read: seq_read, - llseek: seq_lseek, - release: seq_release, + .open = seq_applstats_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, }; static void diff -Nru a/drivers/isdn/divert/divert_procfs.c b/drivers/isdn/divert/divert_procfs.c --- a/drivers/isdn/divert/divert_procfs.c Fri Aug 16 14:35:00 2002 +++ b/drivers/isdn/divert/divert_procfs.c Fri Aug 16 14:35:00 2002 @@ -261,14 +261,14 @@ #ifdef CONFIG_PROC_FS static struct file_operations isdn_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: isdn_divert_read, - write: isdn_divert_write, - poll: isdn_divert_poll, - ioctl: isdn_divert_ioctl, - open: isdn_divert_open, - release: isdn_divert_close, + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = isdn_divert_read, + .write = isdn_divert_write, + .poll = isdn_divert_poll, + .ioctl = isdn_divert_ioctl, + .open = isdn_divert_open, + .release = isdn_divert_close, }; /****************************/ diff -Nru a/drivers/isdn/hardware/Config.in b/drivers/isdn/hardware/Config.in --- a/drivers/isdn/hardware/Config.in Fri Aug 16 14:34:57 2002 +++ b/drivers/isdn/hardware/Config.in Fri Aug 16 14:34:57 2002 @@ -2,4 +2,26 @@ # ISDN hardware drivers # -source drivers/isdn/hardware/avm/Config.in +if [ "$CONFIG_ISDN_CAPI" != "n" ]; then + comment 'CAPI hardware drivers' + source drivers/isdn/hardware/avm/Config.in +fi + +if [ "$CONFIG_ISDN" != "n" ]; then + comment 'ISDN4Linux hardware drivers' + + source drivers/isdn/hisax/Config.in + + mainmenu_option next_comment + comment 'Active cards' + + source drivers/isdn/icn/Config.in + source drivers/isdn/pcbit/Config.in + source drivers/isdn/sc/Config.in + source drivers/isdn/act2000/Config.in + source drivers/isdn/eicon/Config.in + source drivers/isdn/tpam/Config.in + source drivers/isdn/hysdn/Config.in + endmenu +fi + diff -Nru a/drivers/isdn/hardware/avm/Config.in b/drivers/isdn/hardware/avm/Config.in --- a/drivers/isdn/hardware/avm/Config.in Fri Aug 16 14:34:52 2002 +++ b/drivers/isdn/hardware/avm/Config.in Fri Aug 16 14:34:52 2002 @@ -3,7 +3,7 @@ # mainmenu_option next_comment -comment 'Drivers for active AVM cards' +comment 'Active AVM cards' bool 'Support AVM cards' CONFIG_CAPI_AVM diff -Nru a/drivers/isdn/hardware/avm/b1pci.c b/drivers/isdn/hardware/avm/b1pci.c --- a/drivers/isdn/hardware/avm/b1pci.c Fri Aug 16 14:34:52 2002 +++ b/drivers/isdn/hardware/avm/b1pci.c Fri Aug 16 14:34:52 2002 @@ -352,10 +352,10 @@ } static struct pci_driver b1pci_pci_driver = { - name: "b1pci", - id_table: b1pci_pci_tbl, - probe: b1pci_pci_probe, - remove: __devexit_p(b1pci_pci_remove), + .name = "b1pci", + .id_table = b1pci_pci_tbl, + .probe = b1pci_pci_probe, + .remove = __devexit_p(b1pci_pci_remove), }; static int __init b1pci_init(void) diff -Nru a/drivers/isdn/hardware/avm/c4.c b/drivers/isdn/hardware/avm/c4.c --- a/drivers/isdn/hardware/avm/c4.c Fri Aug 16 14:34:51 2002 +++ b/drivers/isdn/hardware/avm/c4.c Fri Aug 16 14:34:51 2002 @@ -1232,10 +1232,10 @@ } static struct pci_driver c4_pci_driver = { - name: "c4", - id_table: c4_pci_tbl, - probe: c4_probe, - remove: c4_remove, + .name = "c4", + .id_table = c4_pci_tbl, + .probe = c4_probe, + .remove = c4_remove, }; static int __init c4_init(void) diff -Nru a/drivers/isdn/hardware/avm/t1pci.c b/drivers/isdn/hardware/avm/t1pci.c --- a/drivers/isdn/hardware/avm/t1pci.c Fri Aug 16 14:34:53 2002 +++ b/drivers/isdn/hardware/avm/t1pci.c Fri Aug 16 14:34:53 2002 @@ -215,10 +215,10 @@ } static struct pci_driver t1pci_pci_driver = { - name: "t1pci", - id_table: t1pci_pci_tbl, - probe: t1pci_probe, - remove: t1pci_remove, + .name = "t1pci", + .id_table = t1pci_pci_tbl, + .probe = t1pci_probe, + .remove = t1pci_remove, }; static int __init t1pci_init(void) diff -Nru a/drivers/isdn/hisax/Config.in b/drivers/isdn/hisax/Config.in --- a/drivers/isdn/hisax/Config.in Fri Aug 16 14:34:53 2002 +++ b/drivers/isdn/hisax/Config.in Fri Aug 16 14:34:53 2002 @@ -1,5 +1,5 @@ mainmenu_option next_comment -comment 'Passive ISDN cards' +comment 'Passive cards' dep_tristate 'HiSax SiemensChipSet driver support' CONFIG_ISDN_DRV_HISAX $CONFIG_ISDN if [ "$CONFIG_ISDN_DRV_HISAX" != "n" ]; then comment ' D-channel protocol features' diff -Nru a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c --- a/drivers/isdn/hisax/config.c Fri Aug 16 14:35:00 2002 +++ b/drivers/isdn/hisax/config.c Fri Aug 16 14:35:00 2002 @@ -1919,6 +1919,8 @@ break; case PH_DEACTIVATE | INDICATION: L1L2(st, pr, NULL); + clear_bit(BC_FLG_BUSY, &bcs->Flag); + skb_queue_purge(&bcs->squeue); bcs->hw.b_if = NULL; break; case PH_DATA | INDICATION: @@ -2006,6 +2008,9 @@ else set_bit(FLG_L1_PULL_REQ, &st->l1.Flags); break; + case PH_DEACTIVATE | REQUEST: + test_and_clear_bit(BC_FLG_BUSY, &bcs->Flag); + skb_queue_purge(&bcs->squeue); default: B_L2L1(b_if, pr, arg); break; diff -Nru a/drivers/isdn/hisax/hisax_debug.h b/drivers/isdn/hisax/hisax_debug.h --- a/drivers/isdn/hisax/hisax_debug.h Fri Aug 16 14:34:56 2002 +++ b/drivers/isdn/hisax/hisax_debug.h Fri Aug 16 14:34:56 2002 @@ -28,7 +28,7 @@ #define DBG(level, format, arg...) do { \ if (level & __debug_variable) \ -printk(KERN_DEBUG __FUNCTION__ ": " format "\n" , ## arg); \ +printk(KERN_DEBUG "%s: " format "\n" , __FUNCTION__ , ## arg); \ } while (0) #define DBG_PACKET(level,data,count) \ diff -Nru a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c --- a/drivers/isdn/hisax/hisax_fcpcipnp.c Fri Aug 16 14:34:54 2002 +++ b/drivers/isdn/hisax/hisax_fcpcipnp.c Fri Aug 16 14:34:54 2002 @@ -944,17 +944,17 @@ } static struct pci_driver fcpci_driver = { - name: "fcpci", - probe: fcpci_probe, - remove: __devexit_p(fcpci_remove), - id_table: fcpci_ids, + .name = "fcpci", + .probe = fcpci_probe, + .remove = __devexit_p(fcpci_remove), + .id_table = fcpci_ids, }; static struct isapnp_driver fcpnp_driver = { - name: "fcpnp", - probe: fcpnp_probe, - remove: __devexit_p(fcpnp_remove), - id_table: fcpnp_ids, + .name = "fcpnp", + .probe = fcpnp_probe, + .remove = __devexit_p(fcpnp_remove), + .id_table = fcpnp_ids, }; static int __init hisax_fcpcipnp_init(void) diff -Nru a/drivers/isdn/hisax/hisax_hfcpci.c b/drivers/isdn/hisax/hisax_hfcpci.c --- a/drivers/isdn/hisax/hisax_hfcpci.c Fri Aug 16 14:34:56 2002 +++ b/drivers/isdn/hisax/hisax_hfcpci.c Fri Aug 16 14:34:56 2002 @@ -40,12 +40,12 @@ MODULE_DESCRIPTION("HFC PCI ISDN driver"); #define ID(ven, dev, name) \ - { vendor: PCI_VENDOR_ID_##ven, \ - device: PCI_DEVICE_ID_##dev, \ - subvendor: PCI_ANY_ID, \ - subdevice: PCI_ANY_ID, \ - class: 0, \ - class_mask: 0, \ + { .vendor = PCI_VENDOR_ID_##ven, \ + .device = PCI_DEVICE_ID_##dev, \ + .subvendor = PCI_ANY_ID, \ + .subdevice = PCI_ANY_ID, \ + .class = 0, \ + .class_mask = 0, \ driver_data: (unsigned long) name } static struct pci_device_id hfcpci_ids[] __devinitdata = { @@ -1604,10 +1604,10 @@ } static struct pci_driver hfcpci_driver = { - name: "hfcpci", - probe: hfcpci_probe, - remove: hfcpci_remove, - id_table: hfcpci_ids, + .name = "hfcpci", + .probe = hfcpci_probe, + .remove = hfcpci_remove, + .id_table = hfcpci_ids, }; static int __init hisax_hfcpci_init(void) diff -Nru a/drivers/isdn/hisax/ipacx.c b/drivers/isdn/hisax/ipacx.c --- a/drivers/isdn/hisax/ipacx.c Fri Aug 16 14:34:58 2002 +++ b/drivers/isdn/hisax/ipacx.c Fri Aug 16 14:34:58 2002 @@ -66,9 +66,6 @@ if (cs->debug &L1_DEB_ISAC) debugl1(cs, "ph_command (%#x) in (%#x)", command, cs->dc.isac.ph_state); -//################################### -// printk(KERN_INFO "ph_command (%#x)\n", command); -//################################### cs->writeisac(cs, IPACX_CIX0, (command << 4) | 0x0E); } @@ -82,9 +79,6 @@ event = cs->readisac(cs, IPACX_CIR0) >> 4; if (cs->debug &L1_DEB_ISAC) debugl1(cs, "cic_int(event=%#x)", event); -//######################################### -// printk(KERN_INFO "cic_int(%x)\n", event); -//######################################### cs->dc.isac.ph_state = event; dch_sched_event(cs, D_L1STATECHANGE); } @@ -417,9 +411,6 @@ int count; istad = cs->readisac(cs, IPACX_ISTAD); -//############################################## -// printk(KERN_WARNING "dch_int(istad=%02x)\n", istad); -//############################################## if (istad &0x80) { // RME rstad = cs->readisac(cs, IPACX_RSTAD); @@ -714,9 +705,6 @@ bcs = cs->bcs + hscx; istab = cs->BC_Read_Reg(cs, hscx, IPACX_ISTAB); -//############################################## -// printk(KERN_WARNING "bch_int(istab=%02x)\n", istab); -//############################################## if (!test_bit(BC_FLG_INIT, &bcs->Flag)) return; if (istab &0x80) { // RME @@ -963,15 +951,11 @@ u_char ista; while ((ista = cs->readisac(cs, IPACX_ISTA))) { -//################################################# -// printk(KERN_WARNING "interrupt_ipacx(ista=%02x)\n", ista); -//################################################# - if (ista &0x80) bch_int(cs, 0); // B channel interrupts - if (ista &0x40) bch_int(cs, 1); - - if (ista &0x01) dch_int(cs); // D channel - if (ista &0x10) cic_int(cs); // Layer 1 state - } + if (ista &0x80) bch_int(cs, 0); // B channel interrupts + if (ista &0x40) bch_int(cs, 1); + if (ista &0x01) dch_int(cs); // D channel + if (ista &0x10) cic_int(cs); // Layer 1 state + } } //---------------------------------------------------------- @@ -1003,9 +987,6 @@ init_ipacx(struct IsdnCardState *cs, int part) { if (part &1) { // initialise chip -//################################################## -// printk(KERN_INFO "init_ipacx(%x)\n", part); -//################################################## clear_pending_ints(cs); bch_init(cs, 0); bch_init(cs, 1); diff -Nru a/drivers/isdn/hisax/st5481.h b/drivers/isdn/hisax/st5481.h --- a/drivers/isdn/hisax/st5481.h Fri Aug 16 14:34:59 2002 +++ b/drivers/isdn/hisax/st5481.h Fri Aug 16 14:34:59 2002 @@ -219,13 +219,13 @@ #define L1_EVENT_COUNT (EV_TIMER3 + 1) #define ERR(format, arg...) \ -printk(KERN_ERR __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg) +printk(KERN_ERR __FILE__ ": %s: " format "\n" , __FUNCTION__ , ## arg) #define WARN(format, arg...) \ -printk(KERN_WARNING __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg) +printk(KERN_WARNING __FILE__ ": %s: " format "\n" , __FUNCTION__ , ## arg) #define INFO(format, arg...) \ -printk(KERN_INFO __FILE__ ": " __FUNCTION__ ": " format "\n" , ## arg) +printk(KERN_INFO __FILE__ ": %s: " format "\n" , __FUNCTION__ , ## arg) #include "st5481_hdlc.h" #include "fsm.h" diff -Nru a/drivers/isdn/hisax/st5481_init.c b/drivers/isdn/hisax/st5481_init.c --- a/drivers/isdn/hisax/st5481_init.c Fri Aug 16 14:35:00 2002 +++ b/drivers/isdn/hisax/st5481_init.c Fri Aug 16 14:35:00 2002 @@ -176,10 +176,10 @@ MODULE_DEVICE_TABLE (usb, st5481_ids); static struct usb_driver st5481_usb_driver = { - name: "st5481_usb", - probe: probe_st5481, - disconnect: __devexit_p(disconnect_st5481), - id_table: st5481_ids, + .name = "st5481_usb", + .probe = probe_st5481, + .disconnect = __devexit_p(disconnect_st5481), + .id_table = st5481_ids, }; static int __init st5481_usb_init(void) diff -Nru a/drivers/isdn/hysdn/hysdn_procconf.c b/drivers/isdn/hysdn/hysdn_procconf.c --- a/drivers/isdn/hysdn/hysdn_procconf.c Fri Aug 16 14:34:54 2002 +++ b/drivers/isdn/hysdn/hysdn_procconf.c Fri Aug 16 14:34:54 2002 @@ -377,11 +377,11 @@ /******************************************************/ static struct file_operations conf_fops = { - llseek: no_llseek, - read: hysdn_conf_read, - write: hysdn_conf_write, - open: hysdn_conf_open, - release: hysdn_conf_close, + .llseek = no_llseek, + .read = hysdn_conf_read, + .write = hysdn_conf_write, + .open = hysdn_conf_open, + .release = hysdn_conf_close, }; /*****************************/ diff -Nru a/drivers/isdn/hysdn/hysdn_proclog.c b/drivers/isdn/hysdn/hysdn_proclog.c --- a/drivers/isdn/hysdn/hysdn_proclog.c Fri Aug 16 14:34:54 2002 +++ b/drivers/isdn/hysdn/hysdn_proclog.c Fri Aug 16 14:34:54 2002 @@ -389,12 +389,12 @@ /**************************************************/ static struct file_operations log_fops = { - llseek: no_llseek, - read: hysdn_log_read, - write: hysdn_log_write, - poll: hysdn_log_poll, - open: hysdn_log_open, - release: hysdn_log_close, + .llseek = no_llseek, + .read = hysdn_log_read, + .write = hysdn_log_write, + .poll = hysdn_log_poll, + .open = hysdn_log_open, + .release = hysdn_log_close, }; diff -Nru a/drivers/isdn/i4l/Config.in b/drivers/isdn/i4l/Config.in --- a/drivers/isdn/i4l/Config.in Fri Aug 16 14:34:54 2002 +++ b/drivers/isdn/i4l/Config.in Fri Aug 16 14:34:54 2002 @@ -24,22 +24,3 @@ dep_tristate 'Support isdn diversion services' CONFIG_ISDN_DIVERSION $CONFIG_ISDN endmenu -comment 'low-level hardware drivers' - -source drivers/isdn/hisax/Config.in - -### Active ISDN cards - -mainmenu_option next_comment -comment 'Active ISDN cards' - -source drivers/isdn/icn/Config.in -source drivers/isdn/pcbit/Config.in -source drivers/isdn/sc/Config.in -source drivers/isdn/act2000/Config.in -source drivers/isdn/eicon/Config.in -source drivers/isdn/tpam/Config.in -source drivers/isdn/hysdn/Config.in - -endmenu - diff -Nru a/drivers/isdn/i4l/isdn_audio.c b/drivers/isdn/i4l/isdn_audio.c --- a/drivers/isdn/i4l/isdn_audio.c Fri Aug 16 14:34:50 2002 +++ b/drivers/isdn/i4l/isdn_audio.c Fri Aug 16 14:34:50 2002 @@ -1,9 +1,9 @@ -/* $Id: isdn_audio.c,v 1.21.6.2 2001/09/23 22:24:31 kai Exp $ +/* $Id: isdn_audio.c,v 1.21.6.3 2002/08/13 09:45:33 keil Exp $ * * Linux ISDN subsystem, audio conversion and compression (linklevel). * * Copyright 1994-1999 by Fritz Elfert (fritz@isdn4linux.de) - * DTMF code (c) 1996 by Christian Mock (cm@kukuruz.ping.at) + * DTMF code (c) 1996 by Christian Mock (cm@tahina.priv.at) * Silence detection (c) 1998 by Armin Schindler (mac@gismo.telekom.de) * * This software may be used and distributed according to the terms @@ -15,7 +15,7 @@ #include "isdn_audio.h" #include "isdn_common.h" -char *isdn_audio_revision = "$Revision: 1.21.6.2 $"; +char *isdn_audio_revision = "$Revision: 1.21.6.3 $"; /* * Misc. lookup-tables. diff -Nru a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c --- a/drivers/isdn/i4l/isdn_common.c Fri Aug 16 14:34:56 2002 +++ b/drivers/isdn/i4l/isdn_common.c Fri Aug 16 14:34:56 2002 @@ -1090,14 +1090,14 @@ static struct file_operations isdn_status_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: isdn_status_read, - write: isdn_status_write, - poll: isdn_status_poll, - ioctl: isdn_status_ioctl, - open: isdn_status_open, - release: isdn_status_release, + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = isdn_status_read, + .write = isdn_status_write, + .poll = isdn_status_poll, + .ioctl = isdn_status_ioctl, + .open = isdn_status_open, + .release = isdn_status_release, }; /* @@ -1609,14 +1609,14 @@ static struct file_operations isdn_ctrl_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: isdn_ctrl_read, - write: isdn_ctrl_write, - poll: isdn_ctrl_poll, - ioctl: isdn_ctrl_ioctl, - open: isdn_ctrl_open, - release: isdn_ctrl_release, + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = isdn_ctrl_read, + .write = isdn_ctrl_write, + .poll = isdn_ctrl_poll, + .ioctl = isdn_ctrl_ioctl, + .open = isdn_ctrl_open, + .release = isdn_ctrl_release, }; /* @@ -1661,8 +1661,8 @@ static struct file_operations isdn_fops = { - owner: THIS_MODULE, - open: isdn_open, + .owner = THIS_MODULE, + .open = isdn_open, }; char * diff -Nru a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c --- a/drivers/isdn/i4l/isdn_net.c Fri Aug 16 14:34:57 2002 +++ b/drivers/isdn/i4l/isdn_net.c Fri Aug 16 14:34:57 2002 @@ -48,6 +48,12 @@ ST_WAIT_BEFORE_CB, }; +enum { + ST_CHARGE_NULL, + ST_CHARGE_GOT_CINF, /* got a first charge info */ + ST_CHARGE_HAVE_CINT, /* got a second chare info and thus the timing */ +}; + /* keep clear of ISDN_CMD_* and ISDN_STAT_* */ enum { EV_NET_DIAL = 0x200, @@ -349,12 +355,7 @@ * outgoing packet), if counter exceeds configured limit either do a * hangup immediately or - if configured - wait until just before the next * charge-info. - * - * cps-calculation (needed for dynamic channel-bundling): - * Since this function is called every second, simply reset the - * byte-counter of the interface after copying it to the cps-variable. */ -unsigned long last_jiffies = -HZ; void isdn_net_autohup() @@ -366,59 +367,54 @@ list_for_each(l, &isdn_net_devs) { isdn_net_dev *p = list_entry(l, isdn_net_dev, global_list); isdn_net_local *l = &p->local; - if (jiffies == last_jiffies) - l->cps = l->transcount; - else - l->cps = (l->transcount * HZ) / (jiffies - last_jiffies); - l->transcount = 0; - if (dev->net_verbose > 3) - printk(KERN_DEBUG "%s: %d bogocps\n", l->name, l->cps); - if ((l->flags & ISDN_NET_CONNECTED) && (l->dialstate == ST_ACTIVE)) { + + if (!(l->flags & ISDN_NET_CONNECTED) || l->dialstate != ST_ACTIVE) + continue; + + if(dev->global_flags & ISDN_GLOBAL_STOPPED || + ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_OFF) { + isdn_net_hangup(&p->dev); + continue; + } + dbg_net_dial("%s: huptimer %d, onhtime %d, chargetime %ld, chargeint %d\n", + l->name, l->huptimer, l->onhtime, l->chargetime, l->chargeint); + + if (!(l->onhtime)) + continue; + + if (l->huptimer++ <= l->onhtime) { anymore = 1; - l->huptimer++; - printk("huptimer %d, onhtime %d, chargetime %d, chargeint %d\n", l->huptimer, l->onhtime, l->chargetime, l->chargeint); - /* - * if there is some dialmode where timeout-hangup - * should _not_ be done, check for that here - */ - if ((l->onhtime) && - (l->huptimer > l->onhtime)) - { - if (l->hupflags & ISDN_MANCHARGE && - l->hupflags & ISDN_CHARGEHUP) { - while (time_after(jiffies, l->chargetime + l->chargeint)) - l->chargetime += l->chargeint; - if (time_after(jiffies, l->chargetime + l->chargeint - 2 * HZ)) - if (l->outgoing || l->hupflags & ISDN_INHUP) { - HERE; - isdn_net_hangup(&p->dev); - } - } else if (l->outgoing) { - if (l->hupflags & ISDN_CHARGEHUP) { - if (l->hupflags & ISDN_WAITCHARGE) { - printk(KERN_DEBUG "isdn_net: Hupflags of %s are %X\n", - l->name, l->hupflags); - isdn_net_hangup(&p->dev); - } else if (time_after(jiffies, l->chargetime + l->chargeint)) { - printk(KERN_DEBUG - "isdn_net: %s: chtime = %lu, chint = %d\n", - l->name, l->chargetime, l->chargeint); - isdn_net_hangup(&p->dev); - } - } - } else if (l->hupflags & ISDN_INHUP) { - HERE; + continue; + } + if (l->hupflags & ISDN_MANCHARGE && l->hupflags & ISDN_CHARGEHUP) { + while (time_after(jiffies, l->chargetime + l->chargeint)) + l->chargetime += l->chargeint; + + if (time_after(jiffies, l->chargetime + l->chargeint - 2 * HZ)) { + if (l->outgoing || l->hupflags & ISDN_INHUP) { isdn_net_hangup(&p->dev); + continue; } } - - if(dev->global_flags & ISDN_GLOBAL_STOPPED || (ISDN_NET_DIALMODE(*l) == ISDN_NET_DM_OFF)) { - isdn_net_hangup(&p->dev); - break; + } else if (l->outgoing) { + if (l->hupflags & ISDN_CHARGEHUP) { + if (l->charge_state != ST_CHARGE_HAVE_CINT) { + dbg_net_dial("%s: did not get CINT\n", l->name); + isdn_net_hangup(&p->dev); + continue; + } else if (time_after(jiffies, l->chargetime + l->chargeint)) { + dbg_net_dial("%s: chtime = %lu, chint = %d\n", + l->name, l->chargetime, l->chargeint); + isdn_net_hangup(&p->dev); + continue; + } } + } else if (l->hupflags & ISDN_INHUP) { + isdn_net_hangup(&p->dev); + continue; } + anymore = 1; } - last_jiffies = jiffies; isdn_timer_ctrl(ISDN_TIMER_NETHANGUP, anymore); } @@ -453,6 +449,10 @@ lp->dialstarted = 0; lp->dialwait_timer = 0; + lp->transcount = 0; + lp->cps = 0; + lp->last_jiffies = jiffies; + #ifdef CONFIG_ISDN_PPP if (lp->p_encap == ISDN_NET_ENCAP_SYNCPPP) isdn_ppp_wakeup_daemon(lp); @@ -499,7 +499,7 @@ isdn_BUG(); return; } - printk("%s: %s %#x\n", __FUNCTION__, lp->name, lp->dial_event); + printk("%s: %s %#x\n", __FUNCTION__ , lp->name, lp->dial_event); isdn_net_handle_event(lp, lp->dial_event, NULL); } @@ -608,13 +608,11 @@ } lp->huptimer = 0; lp->outgoing = 1; - if (lp->chargeint) { - lp->hupflags |= ISDN_HAVECHARGE; - lp->hupflags &= ~ISDN_WAITCHARGE; - } else { - lp->hupflags |= ISDN_WAITCHARGE; - lp->hupflags &= ~ISDN_HAVECHARGE; - } + if (lp->chargeint) + lp->charge_state = ST_CHARGE_HAVE_CINT; + else + lp->charge_state = ST_CHARGE_NULL; + if (lp->cbdelay && (lp->flags & ISDN_NET_CBOUT)) { lp->dial_timer.expires = jiffies + lp->cbdelay; lp->dial_event = EV_NET_TIMER_CB; @@ -701,15 +699,19 @@ * usage by isdn_net_autohup() */ lp->charge++; - if (lp->hupflags & ISDN_HAVECHARGE) { - lp->hupflags &= ~ISDN_WAITCHARGE; - lp->chargeint = jiffies - lp->chargetime - (2 * HZ); + switch (lp->charge_state) { + case ST_CHARGE_NULL: + lp->charge_state = ST_CHARGE_GOT_CINF; + break; + case ST_CHARGE_GOT_CINF: + lp->charge_state = ST_CHARGE_HAVE_CINT; + /* fall through */ + case ST_CHARGE_HAVE_CINT: + lp->chargeint = jiffies - lp->chargetime - 2 * HZ; + break; } - if (lp->hupflags & ISDN_WAITCHARGE) - lp->hupflags |= ISDN_HAVECHARGE; lp->chargetime = jiffies; - printk(KERN_DEBUG "isdn_net: Got CINF chargetime of %s now %lu\n", - lp->name, lp->chargetime); + dbg_net_dial("%s: got CINF\n", lp->name); return 1; } break; @@ -1054,7 +1056,7 @@ int retv = 0; if (((isdn_net_local *) (ndev->priv))->master) { - printk("isdn BUG at %s:%d!\n", __FILE__, __LINE__); + isdn_BUG(); dev_kfree_skb(skb); return 0; } @@ -1083,6 +1085,14 @@ * should move to userspace and get based on an overall cps * calculation */ + if (jiffies != lp->last_jiffies) { + lp->cps = lp->transcount * HZ / (jiffies - lp->last_jiffies); + lp->last_jiffies = jiffies; + lp->transcount = 0; + } + if (dev->net_verbose > 3) + printk(KERN_DEBUG "%s: %d bogocps\n", lp->name, lp->cps); + if (lp->cps > lp->triggercps) { if (lp->slave) { if (!lp->sqfull) { @@ -2381,8 +2391,7 @@ lp->outgoing = 0; lp->huptimer = 0; - lp->hupflags |= ISDN_WAITCHARGE; - lp->hupflags &= ~ISDN_HAVECHARGE; + lp->charge_state = ST_CHARGE_NULL; /* Got incoming Call, setup L2 and L3 protocols, * then wait for D-Channel-connect */ @@ -2837,8 +2846,9 @@ else lp->hupflags &= ~ISDN_INHUP; if (cfg->chargeint > 10) { - lp->hupflags |= ISDN_HAVECHARGE | ISDN_MANCHARGE; lp->chargeint = cfg->chargeint * HZ; + lp->charge_state = ST_CHARGE_HAVE_CINT; + lp->hupflags |= ISDN_MANCHARGE; } if (cfg->p_encap != lp->p_encap) { if (cfg->p_encap == ISDN_NET_ENCAP_RAWIP) { diff -Nru a/drivers/isdn/i4l/isdn_net.h b/drivers/isdn/i4l/isdn_net.h --- a/drivers/isdn/i4l/isdn_net.h Fri Aug 16 14:35:00 2002 +++ b/drivers/isdn/i4l/isdn_net.h Fri Aug 16 14:35:00 2002 @@ -12,8 +12,6 @@ */ /* Definitions for hupflags: */ -#define ISDN_WAITCHARGE 1 /* did not get a charge info yet */ -#define ISDN_HAVECHARGE 2 /* We know a charge info */ #define ISDN_CHARGEHUP 4 /* We want to use the charge mechanism */ #define ISDN_INHUP 8 /* Even if incoming, close after huptimeout */ #define ISDN_MANCHARGE 16 /* Charge Interval manually set */ diff -Nru a/drivers/isdn/i4l/isdn_ppp.c b/drivers/isdn/i4l/isdn_ppp.c --- a/drivers/isdn/i4l/isdn_ppp.c Fri Aug 16 14:34:59 2002 +++ b/drivers/isdn/i4l/isdn_ppp.c Fri Aug 16 14:34:59 2002 @@ -852,14 +852,14 @@ struct file_operations isdn_ppp_fops = { - owner: THIS_MODULE, - llseek: no_llseek, - read: isdn_ppp_read, - write: isdn_ppp_write, - poll: isdn_ppp_poll, - ioctl: isdn_ppp_ioctl, - open: isdn_ppp_open, - release: isdn_ppp_release, + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = isdn_ppp_read, + .write = isdn_ppp_write, + .poll = isdn_ppp_poll, + .ioctl = isdn_ppp_ioctl, + .open = isdn_ppp_open, + .release = isdn_ppp_release, }; /* diff -Nru a/drivers/isdn/tpam/tpam_main.c b/drivers/isdn/tpam/tpam_main.c --- a/drivers/isdn/tpam/tpam_main.c Fri Aug 16 14:34:54 2002 +++ b/drivers/isdn/tpam/tpam_main.c Fri Aug 16 14:34:54 2002 @@ -251,10 +251,10 @@ MODULE_DEVICE_TABLE(pci, tpam_pci_tbl); static struct pci_driver tpam_driver = { - name: "tpam", - id_table: tpam_pci_tbl, - probe: tpam_probe, - remove: __devexit_p(tpam_remove), + .name = "tpam", + .id_table = tpam_pci_tbl, + .probe = tpam_probe, + .remove = __devexit_p(tpam_remove), }; static int __init tpam_init(void) { diff -Nru a/drivers/md/md.c b/drivers/md/md.c --- a/drivers/md/md.c Fri Aug 16 14:34:55 2002 +++ b/drivers/md/md.c Fri Aug 16 14:34:55 2002 @@ -116,16 +116,7 @@ static struct block_device_operations md_fops; static devfs_handle_t devfs_handle; -static struct gendisk md_gendisk= -{ - .major = MD_MAJOR, - .major_name = "md", - .minor_shift = 0, - .part = md_hd_struct, - .nr_real = MAX_MD_DEVS, - .next = NULL, - .fops = &md_fops, -}; +static struct gendisk *disks[MAX_MD_DEVS]; /* * Enables to iterate over all existing md arrays @@ -1353,7 +1344,7 @@ int chunk_size; struct list_head *tmp; mdk_rdev_t *rdev; - + struct gendisk *disk; if (list_empty(&mddev->disks)) { MD_BUG(); @@ -1458,6 +1449,24 @@ md_blocksizes[mdidx(mddev)] = bdev_hardsect_size(rdev->bdev); #endif } + + disk = kmalloc(sizeof(struct gendisk), GFP_KERNEL); + if (!disk) + return -ENOMEM; + memset(disk, 0, sizeof(struct gendisk)); + disk->major_name = kmalloc(6, GFP_KERNEL); + if (!disk->major_name) { + kfree(disk); + return -ENOMEM; + } + disk->major = MD_MAJOR; + disk->first_minor = mdidx(mddev); + disk->minor_shift = 0; + sprintf(disk->major_name, "md%d", mdidx(mddev)); + disk->part = md_hd_struct + mdidx(mddev); + disk->nr_real = 1; + disk->fops = &md_fops; + mddev->pers = pers[pnum]; blk_queue_make_request(&mddev->queue, mddev->pers->make_request); @@ -1467,6 +1476,8 @@ if (err) { printk(KERN_ERR "md: pers->run() failed ...\n"); mddev->pers = NULL; + kfree(disk->major_name); + kfree(disk); return -EINVAL; } @@ -1477,15 +1488,11 @@ if (mddev->pers->sync_request) mddev->state &= ~(1 << MD_SB_CLEAN); md_update_sb(mddev); - md_recover_arrays(); - /* - * md_size has units of 1K blocks, which are - * twice as large as sectors. - */ - md_hd_struct[mdidx(mddev)].start_sect = 0; - register_disk(&md_gendisk, mk_kdev(MAJOR_NR,mdidx(mddev)), + add_gendisk(disk); + register_disk(disk, mk_kdev(disk->major,disk->first_minor), 1, &md_fops, md_size[mdidx(mddev)]<<1); + disks[mdidx(mddev)] = disk; return (0); } @@ -1538,6 +1545,7 @@ { int err = 0; kdev_t dev = mddev_to_kdev(mddev); + struct gendisk *disk; if (atomic_read(&mddev->active)>1) { printk(STILL_IN_USE, mdidx(mddev)); @@ -1590,6 +1598,14 @@ if (ro) set_device_ro(dev, 1); } + disk = disks[mdidx(mddev)]; + disks[mdidx(mddev)] = NULL; + + if (disk) { + del_gendisk(disk); + kfree(disk->major_name); + kfree(disk); + } /* * Free resources if final stop @@ -3223,8 +3239,6 @@ blk_queue_make_request(BLK_DEFAULT_QUEUE(MAJOR_NR), md_fail_request); blk_dev[MAJOR_NR].queue = md_queue_proc; - add_gendisk(&md_gendisk); - md_recovery_thread = md_register_thread(md_do_recovery, NULL, name); if (!md_recovery_thread) printk(KERN_ALERT @@ -3572,7 +3586,6 @@ remove_proc_entry("mdstat", NULL); #endif - del_gendisk(&md_gendisk); blk_dev[MAJOR_NR].queue = NULL; blk_clear(MAJOR_NR); diff -Nru a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c --- a/drivers/message/i2o/i2o_block.c Fri Aug 16 14:34:59 2002 +++ b/drivers/message/i2o/i2o_block.c Fri Aug 16 14:34:59 2002 @@ -59,6 +59,7 @@ #include #include #include +#include #include #include @@ -173,6 +174,7 @@ struct i2ob_request request_queue[MAX_I2OB_DEPTH]; struct i2ob_request *i2ob_qhead; request_queue_t req_queue; + spinlock_t lock; }; static struct i2ob_iop_queue *i2ob_queues[MAX_I2O_CONTROLLERS]; static struct i2ob_request *i2ob_backlog[MAX_I2O_CONTROLLERS]; @@ -185,7 +187,8 @@ static struct i2ob_device i2ob_dev[MAX_I2OB<<4]; static int i2ob_dev_count = 0; static struct hd_struct i2ob[MAX_I2OB<<4]; -static struct gendisk i2ob_gendisk; /* Declared later */ +static struct gendisk i2o_disk[MAX_I2OB]; +static char i2o_names[MAX_I2OB * 8]; /* * Mutex and spin lock for event handling synchronization @@ -211,7 +214,6 @@ static int i2ob_init_iop(unsigned int); static request_queue_t* i2ob_get_queue(kdev_t); static int i2ob_query_device(struct i2ob_device *, int, int, void*, int); -static int do_i2ob_revalidate(kdev_t, int); static int i2ob_evt(void *); static int evt_pid = 0; @@ -477,10 +479,10 @@ ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; ireq->req->errors++; - spin_lock_irqsave(&I2O_LOCK(c->unit), flags); + spin_lock_irqsave(I2O_LOCK(c->unit), flags); i2ob_unhook_request(ireq, c->unit); i2ob_end_request(ireq->req); - spin_unlock_irqrestore(&I2O_LOCK(c->unit), flags); + spin_unlock_irqrestore(I2O_LOCK(c->unit), flags); /* Now flush the message by making it a NOP */ m[0]&=0x00FFFFFF; @@ -501,12 +503,12 @@ if(msg->function == I2O_CMD_BLOCK_CFLUSH) { - spin_lock_irqsave(&I2O_LOCK(c->unit), flags); + spin_lock_irqsave(I2O_LOCK(c->unit), flags); dev->constipated=0; DEBUG(("unconstipated\n")); if(i2ob_backlog_request(c, dev)==0) i2ob_request(dev->req_queue); - spin_unlock_irqrestore(&I2O_LOCK(c->unit), flags); + spin_unlock_irqrestore(I2O_LOCK(c->unit), flags); return; } @@ -522,10 +524,10 @@ ireq=&i2ob_queues[c->unit]->request_queue[m[3]]; ireq->req->errors++; printk(KERN_WARNING "I2O Block: Data transfer to deleted device!\n"); - spin_lock_irqsave(&I2O_LOCK(c->unit), flags); + spin_lock_irqsave(I2O_LOCK(c->unit), flags); i2ob_unhook_request(ireq, c->unit); i2ob_end_request(ireq->req); - spin_unlock_irqrestore(&I2O_LOCK(c->unit), flags); + spin_unlock_irqrestore(I2O_LOCK(c->unit), flags); return; } @@ -571,7 +573,7 @@ */ - spin_lock_irqsave(&I2O_LOCK(c->unit), flags); + spin_lock_irqsave(I2O_LOCK(c->unit), flags); if(err==4) { /* @@ -616,7 +618,7 @@ */ i2ob_request(dev->req_queue); - spin_unlock_irqrestore(&I2O_LOCK(c->unit), flags); + spin_unlock_irqrestore(I2O_LOCK(c->unit), flags); /* * and out @@ -624,7 +626,7 @@ return; } - spin_unlock_irqrestore(&I2O_LOCK(c->unit), flags); + spin_unlock_irqrestore(I2O_LOCK(c->unit), flags); printk(KERN_ERR "\n/dev/%s error: %s", dev->i2odev->dev_name, bsa_errors[m[4]&0XFFFF]); if(m[4]&0x00FF0000) @@ -640,7 +642,7 @@ * may be running polled controllers from a BH... */ - spin_lock_irqsave(&I2O_LOCK(c->unit), flags); + spin_lock_irqsave(I2O_LOCK(c->unit), flags); i2ob_unhook_request(ireq, c->unit); i2ob_end_request(ireq->req); atomic_dec(&i2ob_queues[c->unit]->queue_depth); @@ -652,7 +654,7 @@ if(i2ob_backlog_request(c, dev)==0) i2ob_request(dev->req_queue); - spin_unlock_irqrestore(&I2O_LOCK(c->unit), flags); + spin_unlock_irqrestore(I2O_LOCK(c->unit), flags); } /* @@ -715,8 +717,14 @@ */ case I2O_EVT_IND_BSA_VOLUME_LOAD: { + struct gendisk *p = &i2o_disk[unit>>4]; i2ob_install_device(i2ob_dev[unit].i2odev->controller, i2ob_dev[unit].i2odev, unit); + add_gendisk(p); + register_disk(p, + mk_kdev(p->major, p->first_minor), + 1<minor_shift, p->fops, + i2ob[unit].nr_sects); break; } @@ -728,12 +736,11 @@ */ case I2O_EVT_IND_BSA_VOLUME_UNLOAD: { + struct gendisk *p = &i2o_disk[unit>>4]; + wipe_partitions(mk_kdev(MAJOR_NR, unit)); + del_gendisk(p); for(i = unit; i <= unit+15; i++) - { blk_queue_max_sectors(i2ob_dev[i].req_queue, 0); - i2ob[i].nr_sects = 0; - i2ob_gendisk.part[i].nr_sects = 0; - } i2ob_media_change_flag[unit] = 1; break; } @@ -758,16 +765,12 @@ { u64 size; - if(do_i2ob_revalidate(mk_kdev(MAJOR_NR, unit),0) != -EBUSY) - continue; - if(i2ob_query_device(&i2ob_dev[unit], 0x0004, 0, &size, 8) !=0 ) i2ob_query_device(&i2ob_dev[unit], 0x0000, 4, &size, 8); - spin_lock_irqsave(&I2O_LOCK(unit), flags); - i2ob_gendisk.part[unit].nr_sects = size>>9; - i2ob[unit].nr_sects = (int)(size>>9); - spin_unlock_irqrestore(&I2O_LOCK(unit), flags); + spin_lock_irqsave(I2O_LOCK(unit), flags); + i2ob[unit].nr_sects = size>>9; + spin_unlock_irqrestore(I2O_LOCK(unit), flags); break; } @@ -827,7 +830,7 @@ * We cannot touch the request queue or the timer * flag without holding the queue_lock */ - spin_lock_irqsave(&req_queue->queue_lock,flags); + spin_lock_irqsave(req_queue->queue_lock,flags); /* * Clear the timer started flag so that @@ -843,7 +846,7 @@ /* * Free the lock. */ - spin_unlock_irqrestore(&req_queue->queue_lock,flags); + spin_unlock_irqrestore(req_queue->queue_lock,flags); } static int i2ob_backlog_request(struct i2o_controller *c, struct i2ob_device *dev) @@ -1028,37 +1031,6 @@ *hds = (unsigned char) heads; } - -/* - * Rescan the partition tables - */ - -static int do_i2ob_revalidate(kdev_t dev, int maxu) -{ - int minor=minor(dev); - int i; - - minor&=0xF0; - - i2ob_dev[minor].refcnt++; - if(i2ob_dev[minor].refcnt>maxu+1) - { - i2ob_dev[minor].refcnt--; - return -EBUSY; - } - - wipe_partitions(mk_kdev(MAJOR_NR, minor), 1); - - /* - * Do a physical check and then reconfigure - */ - - i2ob_install_device(i2ob_dev[minor].controller, i2ob_dev[minor].i2odev, - minor); - i2ob_dev[minor].refcnt--; - return 0; -} - /* * Issue device specific ioctl calls. */ @@ -1066,33 +1038,19 @@ static int i2ob_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { + struct hd_geometry g; + int u = minor(inode->i_rdev) & 0xF0; /* Anyone capable of this syscall can do *real bad* things */ if (!capable(CAP_SYS_ADMIN)) return -EPERM; - if (!inode || kdev_none(inode->i_rdev)) - return -EINVAL; - switch (cmd) { - case HDIO_GETGEO: - { - struct hd_geometry g; - int u = minor(inode->i_rdev) & 0xF0; - i2o_block_biosparam(i2ob[u].nr_sects, + if (cmd != HDIO_GETGEO) + return -EINVAL; + i2o_block_biosparam(i2ob[u].nr_sects, &g.cylinders, &g.heads, &g.sectors); - g.start = get_start_sect(inode->i_bdev); - return copy_to_user((void *)arg, &g, sizeof(g)) - ? -EFAULT : 0; - } - - case BLKRRPART: - if(!capable(CAP_SYS_ADMIN)) - return -EACCES; - return do_i2ob_revalidate(inode->i_rdev,1); - - default: - return -EINVAL; - } + g.start = get_start_sect(inode->i_bdev); + return copy_to_user((void *)arg, &g, sizeof(g)) ? -EFAULT : 0; } /* @@ -1265,8 +1223,7 @@ i2ob_query_device(dev, 0x0000, 5, &flags, 4); i2ob_query_device(dev, 0x0000, 6, &status, 4); - i2ob_gendisk.part[unit].nr_sects = size>>9; - i2ob[unit].nr_sects = (int)(size>>9); + i2ob[unit].nr_sects = size>>9; /* Set limit based on inbound frame size */ limit = (d->controller->status_block->inbound_frame_size - 8)/2; @@ -1303,7 +1260,7 @@ } - sprintf(d->dev_name, "%s%c", i2ob_gendisk.major_name, 'a' + (unit>>4)); + strcpy(d->dev_name, i2o_disk[unit>>4].major_name); printk(KERN_INFO "%s: Max segments %d, queue depth %d, byte limit %d.\n", d->dev_name, i2ob_dev[unit].max_segments, i2ob_dev[unit].depth, limit); @@ -1363,8 +1320,6 @@ */ dev->req_queue = &i2ob_queues[c->unit]->req_queue; - grok_partitions(mk_kdev(MAJOR_NR, unit), (long)(size>>9)); - /* * Register for the events we're interested in and that the * device actually supports. @@ -1372,6 +1327,7 @@ i2o_event_register(c, d->lct_data.tid, i2ob_context, unit, (I2OB_EVENT_MASK & d->lct_data.event_capabilities)); + i2ob[unit].nr_sects = size>>9; return 0; } @@ -1398,13 +1354,14 @@ &i2ob_queues[unit]->request_queue[i+1]; i2ob_queues[unit]->request_queue[i].num = i; } + i2ob_queues[unit]->lock = SPIN_LOCK_UNLOCKED; /* Queue is MAX_I2OB + 1... */ i2ob_queues[unit]->request_queue[i].next = NULL; i2ob_queues[unit]->i2ob_qhead = &i2ob_queues[unit]->request_queue[0]; atomic_set(&i2ob_queues[unit]->queue_depth, 0); - blk_init_queue(&i2ob_queues[unit]->req_queue, i2ob_request); + blk_init_queue(&i2ob_queues[unit]->req_queue, i2ob_request, &i2ob_queues[unit]->lock); i2ob_queues[unit]->req_queue.queuedata = &i2ob_queues[unit]; return 0; @@ -1510,6 +1467,12 @@ printk(KERN_WARNING "Could not install I2O block device\n"); else { + struct gendisk *p = &i2o_disk[scan_unit>>4]; + add_gendisk(p); + register_disk(p, + mk_kdev(p->major, p->first_minor), + 1<minor_shift, p->fops, + i2ob[scan_unit].nr_sects); scan_unit+=16; i2ob_dev_count++; @@ -1597,6 +1560,12 @@ printk(KERN_ERR "i2o_block: Could not install new device\n"); else { + struct gendisk *p = &i2o_disk[unit>>4]; + add_gendisk(p); + register_disk(p, + mk_kdev(p->major, p->first_minor), + 1<minor_shift, p->fops, + i2ob[unit].nr_sects); i2ob_dev_count++; i2o_device_notify_on(d, &i2o_block_handler); } @@ -1617,7 +1586,7 @@ int i = 0; unsigned long flags; - spin_lock_irqsave(&I2O_LOCK(c->unit), flags); + spin_lock_irqsave(I2O_LOCK(c->unit), flags); /* * Need to do this...we somtimes get two events from the IRTOS @@ -1639,7 +1608,7 @@ if(unit >= MAX_I2OB<<4) { printk(KERN_ERR "i2ob_del_device called, but not in dev table!\n"); - spin_unlock_irqrestore(&I2O_LOCK(c->unit), flags); + spin_unlock_irqrestore(I2O_LOCK(c->unit), flags); return; } @@ -1647,15 +1616,15 @@ * This will force errors when i2ob_get_queue() is called * by the kenrel. */ + wipe_partitions(mk_kdev(MAJOR_NR, unit)); + del_gendisk(&i2o_disk[unit>>4]); i2ob_dev[unit].req_queue = NULL; for(i = unit; i <= unit+15; i++) { i2ob_dev[i].i2odev = NULL; blk_queue_max_sectors(i2ob_dev[i].req_queue, 0); - i2ob[i].nr_sects = 0; - i2ob_gendisk.part[i].nr_sects = 0; } - spin_unlock_irqrestore(&I2O_LOCK(c->unit), flags); + spin_unlock_irqrestore(I2O_LOCK(c->unit), flags); /* * Decrease usage count for module @@ -1694,7 +1663,9 @@ static int i2ob_revalidate(kdev_t dev) { - return do_i2ob_revalidate(dev, 0); + int minor = minor(dev) & ~15; + return i2ob_install_device(i2ob_dev[minor].controller, i2ob_dev[minor].i2odev, + minor); } /* @@ -1751,17 +1722,6 @@ revalidate: i2ob_revalidate, }; -static struct gendisk i2ob_gendisk = -{ - major: MAJOR_NR, - major_name: "i2o/hd", - minor_shift: 4, - part: i2ob, - nr_real: MAX_I2OB, - fops: &i2ob_fops, -}; - - /* * And here should be modules and kernel interface * (Just smiley confuses emacs :-) @@ -1797,8 +1757,6 @@ blk_dev[MAJOR_NR].queue = i2ob_get_queue; - blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), i2ob_request); - for (i = 0; i < MAX_I2OB << 4; i++) { i2ob_dev[i].refcnt = 0; i2ob_dev[i].flags = 0; @@ -1809,6 +1767,18 @@ i2ob_dev[i].tail = NULL; i2ob_dev[i].depth = MAX_I2OB_DEPTH; } + + for (i = 0; i < MAX_I2OB; i++) { + struct gendisk *disk = i2o_disk + i; + disk->major = MAJOR_NR; + disk->first_minor = i<<4; + disk->minor_shift = 4; + disk->part = i2ob + (i<<4); + disk->fops = &i2ob_fops; + disk->nr_real = 1; + disk->major_name = i2o_names + i*8; + sprintf(disk->major_name, "i2o/hd%c", 'a' + i); + } /* * Set up the queue @@ -1834,7 +1804,6 @@ if(i2o_install_handler(&i2o_block_handler)<0) { unregister_blkdev(MAJOR_NR, "i2o_block"); - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); printk(KERN_ERR "i2o_block: unable to register OSM.\n"); return -EINVAL; } @@ -1856,15 +1825,8 @@ /* * Finally see what is actually plugged in to our controllers */ - for (i = 0; i < MAX_I2OB; i++) - register_disk(&i2ob_gendisk, mk_kdev(MAJOR_NR,i<<4), 1<<4, - &i2ob_fops, 0); - i2ob_probe(); - /* - * Adding i2ob_gendisk into the gendisk list. - */ - add_gendisk(&i2ob_gendisk); + i2ob_probe(); return 0; } @@ -1927,12 +1889,5 @@ */ if (unregister_blkdev(MAJOR_NR, "i2o_block") != 0) printk("i2o_block: cleanup_module failed\n"); - - /* - * free request queue - */ - blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); - - del_gendisk(&i2ob_gendisk); } #endif diff -Nru a/drivers/net/appletalk/Config.in b/drivers/net/appletalk/Config.in --- a/drivers/net/appletalk/Config.in Fri Aug 16 14:34:51 2002 +++ b/drivers/net/appletalk/Config.in Fri Aug 16 14:34:51 2002 @@ -3,7 +3,6 @@ # mainmenu_option next_comment -comment 'Appletalk devices' dep_mbool 'Appletalk interfaces support' CONFIG_DEV_APPLETALK $CONFIG_ATALK if [ "$CONFIG_DEV_APPLETALK" = "y" ]; then tristate ' Apple/Farallon LocalTalk PC support' CONFIG_LTPC @@ -18,4 +17,3 @@ bool ' Appletalk-IP to IP Decapsulation support' CONFIG_IPDDP_DECAP fi fi -endmenu diff -Nru a/drivers/net/appletalk/cops.c b/drivers/net/appletalk/cops.c --- a/drivers/net/appletalk/cops.c Fri Aug 16 14:34:58 2002 +++ b/drivers/net/appletalk/cops.c Fri Aug 16 14:34:58 2002 @@ -181,7 +181,7 @@ int board; /* Holds what board type is. */ int nodeid; /* Set to 1 once have nodeid. */ unsigned char node_acquire; /* Node ID when acquired. */ - struct at_addr node_addr; /* Full node address */ + struct atalk_addr node_addr; /* Full node address */ }; /* Index to functions, as function prototypes. */ @@ -955,8 +955,8 @@ static int cops_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct cops_local *lp = (struct cops_local *)dev->priv; - struct sockaddr_at *sa=(struct sockaddr_at *)&ifr->ifr_addr; - struct at_addr *aa=(struct at_addr *)&lp->node_addr; + struct sockaddr_at *sa = (struct sockaddr_at *)&ifr->ifr_addr; + struct atalk_addr *aa = (struct atalk_addr *)&lp->node_addr; switch(cmd) { diff -Nru a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c --- a/drivers/net/appletalk/ipddp.c Fri Aug 16 14:34:53 2002 +++ b/drivers/net/appletalk/ipddp.c Fri Aug 16 14:34:53 2002 @@ -116,7 +116,7 @@ u32 paddr = ((struct rtable*)skb->dst)->rt_gateway; struct ddpehdr *ddp; struct ipddp_route *rt; - struct at_addr *our_addr; + struct atalk_addr *our_addr; /* * Find appropriate route to use, based only on IP number. diff -Nru a/drivers/net/appletalk/ipddp.h b/drivers/net/appletalk/ipddp.h --- a/drivers/net/appletalk/ipddp.h Fri Aug 16 14:35:01 2002 +++ b/drivers/net/appletalk/ipddp.h Fri Aug 16 14:35:01 2002 @@ -15,7 +15,7 @@ { struct net_device *dev; /* Carrier device */ __u32 ip; /* IP address */ - struct at_addr at; /* Gateway appletalk address */ + struct atalk_addr at; /* Gateway appletalk address */ int flags; struct ipddp_route *next; }; diff -Nru a/drivers/net/appletalk/ltpc.c b/drivers/net/appletalk/ltpc.c --- a/drivers/net/appletalk/ltpc.c Fri Aug 16 14:34:55 2002 +++ b/drivers/net/appletalk/ltpc.c Fri Aug 16 14:34:55 2002 @@ -262,7 +262,7 @@ struct ltpc_private { struct net_device_stats stats; - struct at_addr my_addr; + struct atalk_addr my_addr; }; /* transmit queue element struct */ @@ -826,7 +826,7 @@ { struct sockaddr_at *sa = (struct sockaddr_at *) &ifr->ifr_addr; /* we'll keep the localtalk node address in dev->pa_addr */ - struct at_addr *aa = &((struct ltpc_private *)dev->priv)->my_addr; + struct atalk_addr *aa = &((struct ltpc_private *)dev->priv)->my_addr; struct lt_init c; int ltflags; diff -Nru a/drivers/net/bonding.c b/drivers/net/bonding.c --- a/drivers/net/bonding.c Fri Aug 16 14:34:56 2002 +++ b/drivers/net/bonding.c Fri Aug 16 14:34:56 2002 @@ -399,7 +399,7 @@ { static int (* ioctl)(struct net_device *, struct ifreq *, int); struct ifreq ifr; - struct mii_ioctl_data mii; + struct mii_ioctl_data *mii; struct ethtool_value etool; if ((ioctl = dev->do_ioctl) != NULL) { /* ioctl to access MII */ @@ -425,21 +425,24 @@ } } - ifr.ifr_data = (char*)&mii; - /* try MIIPHY first then, if that doesn't work, try MIIREG */ - if (ioctl(dev, &ifr, SIOCGMIIPHY) == 0) { - /* now, mii.phy_id contains info about link status : - - mii.phy_id & 0x04 means link up - - mii.phy_id & 0x20 means end of auto-negociation - */ - return mii.phy_id; + /* + * We cannot assume that SIOCGMIIPHY will also read a + * register; not all network drivers support that. + */ + + /* Yes, the mii is overlaid on the ifreq.ifr_ifru */ + mii = (struct mii_ioctl_data *)&ifr.ifr_data; + if (ioctl(dev, &ifr, SIOCGMIIPHY) != 0) { + return MII_LINK_READY; /* can't tell */ } - mii.reg_num = 1; /* the MII register we want to read */ + mii->reg_num = 1; if (ioctl(dev, &ifr, SIOCGMIIREG) == 0) { - /* mii.val_out contians the same link info as phy_id */ - /* above */ - return mii.val_out; + /* + * mii->val_out contains MII reg 1, BMSR + * 0x0004 means link established + */ + return mii->val_out; } } @@ -641,12 +644,6 @@ */ write_lock_irqsave(&bond->lock, flags); - /* - * Lock the master device so that noone trys to transmit - * while we're changing things - */ - spin_lock_bh(&master->xmit_lock); - /* set promiscuity flag to slaves */ if ( (master->flags & IFF_PROMISC) && !(bond->flags & IFF_PROMISC) ) bond_set_promiscuity(bond, 1); @@ -680,7 +677,6 @@ bond_mc_list_destroy (bond); bond_mc_list_copy (master->mc_list, bond, GFP_KERNEL); - spin_unlock_bh(&master->xmit_lock); write_unlock_irqrestore(&bond->lock, flags); } diff -Nru a/drivers/pci/pci.ids b/drivers/pci/pci.ids --- a/drivers/pci/pci.ids Fri Aug 16 14:34:58 2002 +++ b/drivers/pci/pci.ids Fri Aug 16 14:34:58 2002 @@ -7,7 +7,7 @@ # so if you have anything to contribute, please visit the home page or # send a diff -u against the most recent pci.ids to pci-ids@ucw.cz. # -# $Id: pci.ids,v 1.24 2001/10/28 21:55:26 mares Exp $ +# $Id: pci.ids,v 1.46 2002/08/14 17:38:51 mares Exp $ # # Vendors, devices and subsystems. Please keep sorted. @@ -26,6 +26,10 @@ 0675 Dynalink 1700 IS64PH ISDN Adapter 1702 IS64PH ISDN Adapter +# Wrong ID used in subsystem ID of VIA USB controllers. +0925 VIA Technologies, Inc. (Wrong ID) +09c1 Arris + 0704 CM 200E Cable Modem 0a89 BREA Technologies Inc 0e11 Compaq Computer Corporation 0001 PCI to EISA Bridge @@ -47,7 +51,7 @@ a0f7 PCI Hotplug Controller 8086 002a PCI Hotplug Controller A 8086 002b PCI Hotplug Controller B - a0f8 USB Open Host Controller + a0f8 ZFMicro Chipset USB a0fc Fibre Channel Host Controller ae10 Smart-2/P RAID Controller 0e11 4030 Smart-2/P Array Controller @@ -88,11 +92,12 @@ b13c NC3162 Fast Ethernet NIC b144 NC3123 Fast Ethernet NIC b163 NC3134 Fast Ethernet NIC - b164 NC3135 Fast Ethernet Upgrade Module + b164 NC3165 Fast Ethernet Upgrade Module b178 Smart Array 5i/532 b1a4 NC7131 Gigabit Server Adapter f130 NetFlex-3/P ThunderLAN 1.0 f150 NetFlex-3/P ThunderLAN 2.3 +0e55 HaSoTec GmbH 1000 LSI Logic / Symbios Logic (formerly NCR) 0001 53c810 1000 1000 8100S @@ -104,15 +109,22 @@ 000a 53c1510 000b 53c896 000c 53c895 + 1de1 3907 DC-390U2W 000d 53c885 000f 53c875 0e11 7004 Embedded Ultra Wide SCSI Controller 1092 8760 FirePort 40 Dual SCSI Controller 1de1 3904 DC390F Ultra Wide SCSI Controller + 0010 53c895 + 0e11 4040 Integrated Array Controller + 0e11 4048 Integrated Array Controller 0012 53c895a + 0013 53c875a 0020 53c1010 Ultra3 SCSI Adapter + 1de1 1020 DC-390U3W 0021 53c1010 66MHz Ultra3 SCSI Adapter 0030 53c1030 + 1028 1010 LSI U320 SCSI Controller 0040 53c1035 008f 53c875J 1092 8000 FirePort 40 SCSI Controller @@ -122,12 +134,20 @@ 0623 FC929 LAN 0624 FC919 0625 FC919 LAN - 0701 83C885 + 0626 FC929X + 0627 FC929X LAN + 0628 FC919X + 0629 FC919X LAN + 0701 83C885 NT50 DigitalScape Fast Ethernet 0702 Yellowfin G-NIC gigabit ethernet 1318 0000 PEI100X 0901 61C102 1000 63C815 -1001 Initio + 1960 PowerEdge Expandable RAID Controller 4 + 1028 0518 PowerEdge Expandable RAID Controller 4/DC + 1028 0520 PowerEdge Expandable RAID Controller 4/SC + 1028 0531 PowerEdge Expandable RAID Controller 4/QC +1001 Kolter Electronic 0010 PCI 1616 Measurement card with 32 digital I/O lines 0011 OPTO-PCI Opto-Isolated digital I/O board 0012 PCI-AD/DA Analogue I/O board @@ -139,28 +159,65 @@ 9100 INI-9100/9100W SCSI Host 1002 ATI Technologies Inc 4158 68800AX [Mach32] + 4242 Radeon 8500 DV + 1002 02aa Radeon 8500 AIW DV Edition 4354 215CT [Mach64 CT] 4358 210888CX [Mach64 CX] 4554 210888ET [Mach64 ET] 4654 Mach64 VT 4742 3D Rage Pro AGP 1X/2X + 1002 0040 Rage Pro Turbo AGP 2X + 1002 0044 Rage Pro Turbo AGP 2X + 1002 0061 Rage Pro AIW AGP 2X + 1002 0062 Rage Pro AIW AGP 2X + 1002 0063 Rage Pro AIW AGP 2X + 1002 0080 Rage Pro Turbo AGP 2X + 1002 0084 Rage Pro Turbo AGP 2X + 1002 4742 Rage Pro Turbo AGP 2X + 1002 8001 Rage Pro Turbo AGP 2X + 1028 0082 Rage Pro Turbo AGP 2X 1028 4082 Optiplex GX1 Onboard Display Adapter - 8086 4152 Rage 3D Pro AGP + 1028 8082 Rage Pro Turbo AGP 2X + 1028 c082 Rage Pro Turbo AGP 2X + 8086 4152 Xpert 98D AGP 2X + 8086 464a Rage Pro Turbo AGP 2X 4744 3D Rage Pro AGP 1X + 1002 4744 Rage Pro Turbo AGP 4747 3D Rage Pro 4749 3D Rage Pro + 1002 0061 Rage Pro AIW + 1002 0062 Rage Pro AIW 474c Rage XC - 474d Rage XL AGP + 474d Rage XL AGP 2X + 1002 0004 Xpert 98 RXL AGP 2X + 1002 0008 Xpert 98 RXL AGP 2X + 1002 0080 Rage XL AGP 2X + 1002 0084 Xpert 98 AGP 2X + 1002 474d Rage XL AGP + 1033 806a Rage XL AGP 474e Rage XC AGP + 1002 474e Rage XC AGP 474f Rage XL + 1002 0008 Rage XL + 1002 474f Rage XL 4750 3D Rage Pro 215GP + 1002 0040 Rage Pro Turbo + 1002 0044 Rage Pro Turbo + 1002 0080 Rage Pro Turbo + 1002 0084 Rage Pro Turbo + 1002 4750 Rage Pro Turbo 4751 3D Rage Pro 215GQ 4752 Rage XL + 1002 0008 Rage XL + 1002 4752 Rage XL 4753 Rage XC + 1002 4753 Rage XC 4754 3D Rage I/II 215GT [Mach64 GT] 4755 3D Rage II+ 215GTB [Mach64 GTB] 4756 3D Rage IIC 215IIC [Mach64 GT IIC] + 1002 4756 Rage IIC 4757 3D Rage IIC AGP + 1002 4757 Rage IIC AGP 1028 0089 Rage 3D IIC 1028 4082 Rage 3D IIC 1028 8082 Rage 3D IIC @@ -169,78 +226,166 @@ 4759 3D Rage IIC 475a 3D Rage IIC AGP 1002 0087 Rage 3D IIC + 1002 475a Rage IIC AGP 4c42 3D Rage LT Pro AGP-133 0e11 b0e8 Rage 3D LT Pro 0e11 b10e 3D Rage LT Pro (Compaq Armada 1750) + 1002 0040 Rage LT Pro AGP 2X + 1002 0044 Rage LT Pro AGP 2X + 1002 4c42 Rage LT Pro AGP 2X + 1002 8001 Rage LT Pro AGP 2X 1028 0085 Rage 3D LT Pro 4c44 3D Rage LT Pro AGP-66 4c45 Rage Mobility M3 AGP 4c46 Rage Mobility M3 AGP 2x 4c47 3D Rage LT-G 215LG 4c49 3D Rage LT Pro + 1002 0004 Rage LT Pro + 1002 0040 Rage LT Pro + 1002 0044 Rage LT Pro + 1002 4c49 Rage LT Pro 4c4d Rage Mobility P/M AGP 2x + 1002 0084 Xpert 98 AGP 2X (Mobility) 4c4e Rage Mobility L AGP 2x 4c50 3D Rage LT Pro + 1002 4c50 Rage LT Pro 4c51 3D Rage LT Pro 4c52 Rage Mobility P/M 4c53 Rage Mobility L 4c54 264LT [Mach64 LT] - 4c57 Radeon Mobility M6 LW + 4c57 Radeon Mobility M7 LW + 1028 00e6 Radeon Mobility M7 LW (Dell Inspiron 8100) + 4c58 Radeon Mobility M7 LX [Radeon Mobility FireGL 7800] 4c59 Radeon Mobility M6 LY + 1014 0235 ThinkPad A30p (2653-64G) + 1014 0239 ThinkPad X22/X23/X24 + 104d 80e7 VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP 4c5a Radeon Mobility M6 LZ 4d46 Rage Mobility M4 AGP 4d4c Rage Mobility M4 AGP - 5041 Rage 128 PA - 5042 Rage 128 PB - 5043 Rage 128 PC - 5044 Rage 128 PD - 5045 Rage 128 PE - 5046 Rage 128 PF + 5041 Rage 128 PA/PRO + 5042 Rage 128 PB/PRO AGP 2x + 5043 Rage 128 PC/PRO AGP 4x + 5044 Rage 128 PD/PRO TMDS + 1002 0028 Rage 128 AIW + 1002 0029 Rage 128 AIW + 5045 Rage 128 PE/PRO AGP 2x TMDS + 5046 Rage 128 PF/PRO AGP 4x TMDS + 1002 0004 Rage Fury Pro + 1002 0008 Rage Fury Pro/Xpert 2000 Pro + 1002 0014 Rage Fury Pro + 1002 0018 Rage Fury Pro/Xpert 2000 Pro + 1002 0028 Rage 128 Pro AIW AGP + 1002 002a Rage 128 Pro AIW AGP + 1002 0048 Rage Fury Pro 1002 2000 Rage Fury MAXX AGP 4x (TMDS) (VGA device) 1002 2001 Rage Fury MAXX AGP 4x (TMDS) (Extra device?!) - 5047 Rage 128 PG - 5048 Rage 128 PH - 5049 Rage 128 PI - 504a Rage 128 PJ - 504b Rage 128 PK - 504c Rage 128 PL - 504d Rage 128 PM - 504e Rage 128 PN - 504f Rage 128 PO - 5050 Rage 128 PP - 5051 Rage 128 PQ - 5052 Rage 128 PR - 5053 Rage 128 PS - 5054 Rage 128 PT - 5055 Rage 128 PU - 5056 Rage 128 PV - 5057 Rage 128 PW - 5058 Rage 128 PX + 5047 Rage 128 PG/PRO + 5048 Rage 128 PH/PRO AGP 2x + 5049 Rage 128 PI/PRO AGP 4x + 504a Rage 128 PJ/PRO TMDS + 504b Rage 128 PK/PRO AGP 2x TMDS + 504c Rage 128 PL/PRO AGP 4x TMDS + 504d Rage 128 PM/PRO + 504e Rage 128 PN/PRO AGP 2x + 504f Rage 128 PO/PRO AGP 4x + 5050 Rage 128 PP/PRO TMDS + 1002 0008 Xpert 128 + 5051 Rage 128 PQ/PRO AGP 2x TMDS + 5052 Rage 128 PR/PRO AGP 4x TMDS + 5053 Rage 128 PS/PRO + 5054 Rage 128 PT/PRO AGP 2x + 5055 Rage 128 PU/PRO AGP 4x + 5056 Rage 128 PV/PRO TMDS + 5057 Rage 128 PW/PRO AGP 2x TMDS + 5058 Rage 128 PX/PRO AGP 4x TMDS 5144 Radeon QD + 1002 0008 Radeon 7000/Radeon VE + 1002 0009 Radeon 7000/Radeon + 1002 000a Radeon 7000/Radeon + 1002 001a Radeon 7000/Radeon + 1002 0029 Radeon AIW + 1002 0038 Radeon 7000/Radeon + 1002 0039 Radeon 7000/Radeon + 1002 008a Radeon 7000/Radeon + 1002 00ba Radeon 7000/Radeon + 1002 0139 Radeon 7000/Radeon + 1002 028a Radeon 7000/Radeon + 1002 02aa Radeon AIW + 1002 053a Radeon 7000/Radeon 5145 Radeon QE 5146 Radeon QF 5147 Radeon QG - 514c Radeon 8500 QL + 5148 Radeon R200 QH [Radeon 8500] + 1002 0152 FireGL 8800 + 1002 0172 FireGL 8700 + 5149 Radeon R200 QI + 514a Radeon R200 QJ + 514b Radeon R200 QK + 514c Radeon R200 QL [Radeon 8500 LE] + 1002 003a Radeon R200 QL [Radeon 8500 LE] + 1002 013a Radeon 8500 + 5157 Radeon 7500 QW + 1002 013a Radeon 7500 + 174b 7161 Radeon RV200 QW [Radeon 7500 LE] + 5158 Radeon 7500 QX 5159 Radeon VE QY + 1002 000a Radeon 7000/Radeon VE + 1002 0038 Radeon 7000/Radeon VE + 1002 003a Radeon 7000/Radeon VE + 1002 00ba Radeon 7000/Radeon VE + 1002 013a Radeon 7000/Radeon VE + 174b 7112 Radeon 7000 64M TVO 515a Radeon VE QZ - 5245 Rage 128 RE - 5246 Rage 128 RF + 5168 Radeon R200 Qh + 5169 Radeon R200 Qi + 516a Radeon R200 Qj + 516b Radeon R200 Qk + 5245 Rage 128 RE/SG + 1002 0008 Xpert 128 + 1002 0028 Rage 128 AIW + 1002 0029 Rage 128 AIW + 1002 0068 Rage 128 AIW + 5246 Rage 128 RF/SG AGP + 1002 0004 Magnum/Xpert 128/Xpert 99 + 1002 0008 Magnum/Xpert128/X99/Xpert2000 + 1002 0028 Rage 128 AIW AGP + 1002 0044 Rage Fury/Xpert 128/Xpert 2000 + 1002 0068 Rage 128 AIW AGP + 1002 0448 Rage Fury 5247 Rage 128 RG - 524b Rage 128 RK - 524c Rage 128 RL - 5345 Rage 128 SE - 5346 Rage 128 SF - 5347 Rage 128 SG - 5348 Rage 128 4x - 534b Rage 128 SK - 534c Rage 128 SL - 534d Rage 128 SM - 534e Rage 128 SN + 524b Rage 128 RK/VR + 524c Rage 128 RL/VR AGP + 1002 0008 Xpert 99/Xpert 2000 + 1002 0088 Xpert 99 + 5345 Rage 128 SE/4x + 5346 Rage 128 SF/4x AGP 2x + 5347 Rage 128 SG/4x AGP 4x + 5348 Rage 128 SH + 534b Rage 128 SK/4x + 534c Rage 128 SL/4x AGP 2x + 534d Rage 128 SM/4x AGP 4x + 1002 0008 Xpert 99/Xpert 2000 + 1002 0018 Xpert 2000 + 534e Rage 128 4x 5354 Mach 64 VT 1002 5654 Mach 64 reference - 5446 Rage 128 Pro TF - 544c Rage 128 Pro TL - 5452 Rage 128 Pro TR + 5446 Rage 128 Pro Ultra TF + 1002 0004 Rage Fury Pro + 1002 0008 Rage Fury Pro/Xpert 2000 Pro + 1002 0018 Rage Fury Pro/Xpert 2000 Pro + 1002 0028 Rage 128 AIW Pro AGP + 1002 0029 Rage 128 AIW + 1002 002a Rage 128 AIW Pro AGP + 1002 002b Rage 128 AIW + 1002 0048 Xpert 2000 Pro + 544c Rage 128 Pro Ultra TL + 5452 Rage 128 Pro Ultra TR + 1002 001c Rage 128 Pro 4XL + 103c 1279 Rage 128 Pro 4XL + 5453 Rage 128 Pro Ultra TS + 5454 Rage 128 Pro Ultra TT + 5455 Rage 128 Pro Ultra TU 5654 264VT [Mach64 VT] 1002 5654 Mach64VT Reference 5655 264VT3 [Mach64 VT3] @@ -256,7 +401,7 @@ 000c 82C541 [Lynx] 000d 82C543 [Lynx] 0101 82C532 - 0102 82C534 + 0102 82C534 [Eagle] 0103 82C538 0104 82C535 0105 82C147 @@ -291,11 +436,17 @@ 0001 DP83810 0002 87415/87560 IDE 000e 87560 Legacy I/O - 000f OHCI Compliant FireWire Controller - 0011 National PCI System I/O + 000f FireWire Controller + 0011 NS87560 National PCI System I/O 0012 USB Controller 0020 DP83815 (MacPhyter) Ethernet Controller 0022 DP83820 10/100/1000 Ethernet Controller + 0500 SCx200 Bridge + 0501 SCx200 SMI + 0502 SCx200 IDE + 0503 SCx200 Audio + 0504 SCx200 Video + 0505 SCx200 XBus d001 87410 IDE 100c Tseng Labs Inc 3202 ET4000/W32p rev A @@ -306,9 +457,10 @@ 4702 ET6300 100d AST Research Inc 100e Weitek - 9000 P9000 - 9001 P9000 - 9100 P9100 + 9000 P9000 Viper + 9001 P9000 Viper + 9002 P9000 Viper + 9100 P9100 Viper Pro/SE 1010 Video Logic, Ltd. 1011 Digital Equipment Corporation 0001 DECchip 21050 @@ -317,6 +469,7 @@ 0007 NVRAM [Zephyr NVRAM] 0008 KZPSA [KZPSA] 0009 DECchip 21140 [FasterNet] + 1025 0310 21140 Fast Ethernet 10b8 2001 SMC9332BDT EtherPower 10/100 10b8 2002 SMC9332BVT EtherPower T4 10/100 10b8 2003 SMC9334BDT EtherPower 10/100 (1-port) @@ -329,6 +482,7 @@ 1186 1112 DFE-570TX Fast Ethernet 1186 1140 DFE-660 Cardbus Ethernet 10/100 1186 1142 DFE-660 Cardbus Ethernet 10/100 + 11f6 0503 Freedomline Fast Ethernet 1282 9100 AEF-380TXD Fast Ethernet 1385 1100 FA310TX Fast Ethernet 2646 0001 KNE100TX Fast Ethernet @@ -339,11 +493,16 @@ 1186 0100 DE-530+ 0016 DGLPB [OPPO] 0019 DECchip 21142/43 - 1011 500b DE500 Fast Ethernet + 1011 500a DE500A Fast Ethernet + 1011 500b DE500B Fast Ethernet 1014 0001 10/100 EtherJet Cardbus 1025 0315 ALN315 Fast Ethernet + 1033 800c PC-9821-CS01 + 1033 800d PC-9821NR-B06 108d 0016 Rapidfire 2327 10/100 Ethernet + 108d 0017 GoCard 2250 Ethernet 10/100 Cardbus 10b8 2005 SMC8032DT Extreme Ethernet 10/100 + 10b8 8034 SMC8034 Extreme Ethernet 10/100 10ef 8169 Cardbus Fast Ethernet 1109 2a00 ANA-6911A/TX Fast Ethernet 1109 2b00 ANA-6911A/TXC Fast Ethernet @@ -356,25 +515,38 @@ 1186 1100 DFE-500TX Fast Ethernet 1186 1101 DFE-500TX Fast Ethernet 1186 1102 DFE-500TX Fast Ethernet + 1259 2800 AT-2800Tx Fast Ethernet 1266 0004 Eagle Fast EtherMAX 12af 0019 NetFlyer Cardbus Fast Ethernet 1374 0001 Cardbus Ethernet Card 10/100 + 1374 0002 Cardbus Ethernet Card 10/100 + 1374 0007 Cardbus Ethernet Card 10/100 + 1374 0008 Cardbus Ethernet Card 10/100 1395 0001 10/100 Ethernet CardBus PC Card + 13d1 ab01 EtherFast 10/100 Cardbus (PCMPC200) 8086 0001 EtherExpress PRO/100 Mobile CardBus 32 + 001a Farallon PN9000SX 0021 DECchip 21052 0022 DECchip 21150 0023 DECchip 21150 0024 DECchip 21152 0025 DECchip 21153 0026 DECchip 21154 + 0034 56k Modem Cardbus + 1374 0003 56k Modem Cardbus 0045 DECchip 21553 0046 DECchip 21554 + 0e11 4050 Integrated Smart Array + 0e11 4051 Integrated Smart Array + 0e11 4058 Integrated Smart Array 103c 10c2 Hewlett-Packard NetRAID-4M + 12d9 000a VoIP PCI Gateway 9005 0365 Adaptec 5400S 9005 1364 Dell PowerEdge RAID Controller 2 9005 1365 Dell PowerEdge RAID Controller 2 + e4bf 1000 CC8-1-BLUES 1065 StrongARM DC21285 - 1069 0020 DAC960P + 1069 0020 DAC960P / DAC1164P 1012 Micronics Computers Inc 1013 Cirrus Logic 0038 GD 7548 @@ -392,10 +564,13 @@ 00d0 GD 5462 00d2 GD 5462 [Laguna I] 00d4 GD 5464 [Laguna] + 00d5 GD 5464 BD [Laguna] 00d6 GD 5465 [Laguna] + 13ce 8031 Barco Metheus 2 Megapixel, Dual Head + 13cf 8031 Barco Metheus 2 Megapixel, Dual Head 00e8 GD 5436U 1100 CL 6729 - 1110 PD 6832 + 1110 PD 6832 PCMCIA/CardBus Ctrlr 1112 PD 6834 PCMCIA/CardBus Ctrlr 1113 PD 6833 PCMCIA/CardBus Ctrlr 1200 GD 7542 [Nordic] @@ -406,6 +581,8 @@ 1014 1010 CS4610 SoundFusion Audio Accelerator 6003 CS 4614/22/24 [CrystalClear SoundFusion Audio Accelerator] 1013 4280 Crystal SoundFusion PCI Audio Accelerator + 1681 0050 Hercules Game Theater XP + 6004 CS 4614/22/24 [CrystalClear SoundFusion Audio Accelerator] 6005 Crystal CS4281 PCI Audio 1013 4281 Crystal CS4281 PCI Audio 10cf 10a8 Crystal CS4281 PCI Audio @@ -425,6 +602,7 @@ 0017 CPU to PCI Bridge 0018 TR Auto LANstreamer 001b GXT-150P + 001c Carrera 001d 82G2675 0020 MCA 0022 IBM27-82351 @@ -465,11 +643,14 @@ 00b7 256-bit Graphics Rasterizer [Fire GL1] 1902 00b8 Fire GL1 00be ATM 622MBPS Controller (1410be00) + 00fc CPC710 Dual Bridge and Memory Controller (PCI-64) + 0105 CPC710 Dual Bridge and Memory Controller (PCI-32) 0142 Yotta Video Compositor Input 1014 0143 Yotta Input Controller (ytin) 0144 Yotta Video Compositor Output 1014 0145 Yotta Output Controller (ytout) 0156 405GP PLB to PCI Bridge + 01a7 PCI-X to PCI-X Bridge 01bd Netfinity ServeRAID controller 01be ServeRAID-4M 01bf ServeRAID-4L @@ -498,20 +679,28 @@ c24a 90C 101e American Megatrends Inc. 1960 MegaRAID + 101e 0471 MegaRAID 471 Enterprise 1600 RAID Controller + 101e 0475 MegaRAID 475 Express 500 RAID Controller + 101e 0493 MegaRAID 493 Elite 1600 RAID Controller 1028 0471 PowerEdge RAID Controller 3/QC + 1028 0475 PowerEdge RAID Controller 3/SC 1028 0493 PowerEdge RAID Controller 3/DC - 9010 MegaRAID + 1028 0511 PowerEdge Cost Effective RAID Controller ATA100/4Ch + 9010 MegaRAID 428 Ultra RAID Controller 9030 EIDE Controller 9031 EIDE Controller 9032 EIDE & SCSI Controller 9033 SCSI Controller 9040 Multimedia card - 9060 MegaRAID + 9060 MegaRAID 434 Ultra GT RAID Controller + 9063 MegaRAC + 101e 0767 Dell Remote Assistant Card 2 101f PictureTel 1020 Hitachi Computer Products 1021 OKI Electric Industry Co. Ltd. 1022 Advanced Micro Devices [AMD] 2000 79c970 [PCnet LANCE] + 1014 2000 NetFinity 10/100 Fast Ethernet 103c 104c Ethernet with LAN remote power Adapter 103c 1064 Ethernet with LAN remote power Adapter 103c 1065 Ethernet with LAN remote power Adapter @@ -520,11 +709,15 @@ 103c 10ea Ethernet with LAN remote power Adapter 1113 1220 EN1220 10/100 Fast Ethernet 1259 2450 AT-2450 10/100 Fast Ethernet + 1259 2454 AT-2450v4 10Mb Ethernet Adapter 1259 2700 AT-2700TX 10/100 Fast Ethernet 1259 2701 AT-2700FX 100Mb Ethernet 2001 79c978 [HomePNA] + 1092 0a78 Multimedia Home Network Adapter + 1668 0299 ActionLink Home Network Adapter 2020 53c974 [PCscsi] 2040 79c974 + 3000 ELanSC520 Microcontroller 7006 AMD-751 [Irongate] System Controller 7007 AMD-751 [Irongate] AGP Bridge 700c AMD-760 MP [IGD4-2P] System Controller @@ -539,13 +732,16 @@ 7409 AMD-756 [Viper] IDE 740b AMD-756 [Viper] ACPI 740c AMD-756 [Viper] USB - 7410 AMD-765 [Viper] ISA - 7411 AMD-765 [Viper] IDE - 7413 AMD-765 [Viper] ACPI - 7414 AMD-765 [Viper] USB + 7410 AMD-766 [ViperPlus] ISA + 7411 AMD-766 [ViperPlus] IDE + 7413 AMD-766 [ViperPlus] ACPI + 7414 AMD-766 [ViperPlus] USB 7440 AMD-768 [Opus] ISA + 1043 8044 A7M-D Mainboard 7441 AMD-768 [Opus] IDE 7443 AMD-768 [Opus] ACPI + 1043 8044 A7M-D Mainboard + 7445 AMD-768 [Opus] Audio 7448 AMD-768 [Opus] PCI 7449 AMD-768 [Opus] USB 7454 AMD-8151 System Controller @@ -558,6 +754,7 @@ 746a AMD-8111 SMBus 2.0 746b AMD-8111 ACPI 746d AMD-8111 AC97 Audio + 756b AMD-8111 ACPI 1023 Trident Microsystems 0194 82C194 2000 4DWave DX @@ -570,6 +767,7 @@ 8520 CyberBlade i1 0e11 b16e CyberBlade i1 AGP 1023 8520 CyberBlade i1 AGP + 8820 CyberBlade XPAi1 9320 TGUI 9320 9350 GUI Accelerator 9360 Flat panel GUI Accelerator @@ -587,13 +785,14 @@ 9470 TGUI 9470 9520 Cyber 9520 9525 Cyber 9525 + 10cf 1094 Lifebook C6155 9540 Cyber 9540 - 9660 TGUI 9660/968x/968x + 9660 TGUI 9660/938x/968x 9680 TGUI 9680 9682 TGUI 9682 9683 TGUI 9683 9685 ProVIDIA 9685 - 9750 3DIm`age 975 + 9750 3DImage 9750 1014 9750 3DImage 9750 1023 9750 3DImage 9750 9753 TGUI 9753 @@ -633,6 +832,7 @@ 1621 M1621 Northbridge [Aladdin-Pro II] 1631 M1631 Northbridge+3D Graphics [Aladdin TNT2] 1641 M1641 Northbridge [Aladdin-Pro IV] + 1647 M1647 [MaGiK1] PCI North Bridge 3141 M3141 3143 M3143 3145 M3145 @@ -642,35 +842,48 @@ 3307 M3307 MPEG-I Video Controller 3309 M3309 MPEG-II Video w/ Software Audio Decoder 3321 M3321 MPEG-II Audio/Video Decoder - 5212 ALI M4803 + 5212 M4803 5215 ALI PCI EIDE Controller 5217 M5217H 5219 M5219 5225 M5225 5229 M5229 5235 M5235 - 5237 ALI M5237 PCI USB Host Controller + 5237 M5237 PCI USB Host Controller 5240 EIDE Controller 5241 PCMCIA Bridge 5242 General Purpose Controller 5243 PCI to PCI Bridge Controller 5244 Floppy Disk Controller - 5247 ALI M1541 PCI to PCI Bridge - 5251 M5251 P1394 OHCI Controller - 5427 ALI PCI to AGP Bridge - 5451 ALI M5451 PCI AC-Link Controller Audio Device - 5453 ALI M5453 PCI AC-Link Controller Modem Device - 7101 ALI M7101 PCI PMU Power Management Controller - 10b9 7101 ALI M7101 PCI PMU Power Management Controller + 5247 M1541 PCI to PCI Bridge + 5251 M5251 P1394 Controller + 5427 PCI to AGP Bridge + 5451 M5451 PCI AC-Link Controller Audio Device + 5453 M5453 PCI AC-Link Controller Modem Device + 7101 M7101 PCI PMU Power Management Controller + 10b9 7101 M7101 PCI PMU Power Management Controller 1028 Dell Computer Corporation 0001 PowerEdge Expandable RAID Controller 2/Si - 0002 PowerEdge Expandable RAID Controller 3/Di + 1028 0001 PowerEdge Expandable RAID Controller 2/Si + 0002 PowerEdge Expandable RAID Controller 3 + 1028 0002 PowerEdge Expandable RAID Controller 3/Di + 1028 00d1 PowerEdge Expandable RAID Controller 3/Di + 1028 00d9 PowerEdge Expandable RAID Controller 3/Di 0003 PowerEdge Expandable RAID Controller 3/Si + 1028 0003 PowerEdge Expandable RAID Controller 3/Si 0004 PowerEdge Expandable RAID Controller 3/Si + 1028 00d0 PowerEdge Expandable RAID Controller 3/Si 0005 PowerEdge Expandable RAID Controller 3/Di 0006 PowerEdge Expandable RAID Controller 3/Di + 0007 Remote Assistant Card 3 0008 PowerEdge Expandable RAID Controller 3/Di - 000a PowerEdge Expandable RAID Controller 3/Di + 000a PowerEdge Expandable RAID Controller 3 + 1027 0121 PowerEdge Expandable RAID Controller 3/Di + 1028 0106 PowerEdge Expandable RAID Controller 3/Di + 1028 011b PowerEdge Expandable RAID Controller 3/Di + 000c Embedded Systems Management Device 4 + 000e PowerEdge Expandable RAID Controller + 000f PowerEdge Expandable RAID Controller 4/Di 1029 Siemens Nixdorf IS 102a LSI Logic 0000 HYDRA @@ -711,9 +924,23 @@ 102b ca6c Millennium G250 AGP 102b dbbc Millennium G200 AGP 102b dbc2 Millennium G200 MMS (Dual G200) + 102b dbc3 G200 Multi-Monitor 102b dbc8 Millennium G200 MMS (Dual G200) + 102b dbd2 G200 Multi-Monitor + 102b dbd3 G200 Multi-Monitor + 102b dbd4 G200 Multi-Monitor + 102b dbd5 G200 Multi-Monitor + 102b dbd8 G200 Multi-Monitor + 102b dbd9 G200 Multi-Monitor 102b dbe2 Millennium G200 MMS (Quad G200) + 102b dbe3 G200 Multi-Monitor 102b dbe8 Millennium G200 MMS (Quad G200) + 102b dbf2 G200 Multi-Monitor + 102b dbf3 G200 Multi-Monitor + 102b dbf4 G200 Multi-Monitor + 102b dbf5 G200 Multi-Monitor + 102b dbf8 G200 Multi-Monitor + 102b dbf9 G200 Multi-Monitor 102b f806 Mystique G200 Video AGP 102b ff00 MGA-G200 AGP 102b ff02 Mystique G200 AGP @@ -721,16 +948,18 @@ 102b ff04 Marvel G200 AGP 110a 0032 MGA-G200 AGP 0525 MGA G400 AGP - 0e11 b16f Matrox MGA-G400 AGP + 0e11 b16f MGA-G400 AGP 102b 0328 Millennium G400 16Mb SDRAM 102b 0338 Millennium G400 16Mb SDRAM 102b 0378 Millennium G400 32Mb SDRAM 102b 0541 Millennium G450 Dual Head 102b 0542 Millennium G450 Dual Head LX - 102b 0641 Millennium G450 32Mb SDRAM + 102b 0543 Millennium G450 Single Head LX + 102b 0641 Millennium G450 32Mb SDRAM Dual Head 102b 0642 Millennium G450 32Mb SDRAM Dual Head LX + 102b 0643 Millennium G450 32Mb SDRAM Single Head LX 102b 07c0 Millennium G450 Dual Head LE - 102b 07c1 Millennium G450 SDR Dual Head + 102b 07c1 Millennium G450 SDR Dual Head LE 102b 0d41 Millennium G450 Dual Head PCI 102b 0d42 Millennium G450 Dual Head LX PCI 102b 0e00 Marvel G450 eTV @@ -746,11 +975,26 @@ 102b 2159 Millennium G400 Dual Head 16Mb 102b 2179 Millennium G400 MAX/Dual Head 32Mb 102b 217d Millennium G400 Dual Head Max + 102b 23c0 Millennium G450 + 102b 23c1 Millennium G450 + 102b 23c2 Millennium G450 DVI + 102b 23c3 Millennium G450 DVI 102b 2f58 Millennium G400 102b 2f78 Millennium G400 102b 3693 Marvel G400 AGP - 1705 0001 Millennium G450 32MB SGRAM - b16f 0e11 Matrox MGA-G400 AGP + 102b 5dd0 4Sight II + 102b 5f50 4Sight II + 102b 5f51 4Sight II + 102b 5f52 4Sight II + 102b 9010 Millennium G400 Dual Head + 1458 0400 GA-G400 + 1705 0001 Digital First Millennium G450 32MB SGRAM + 1705 0002 Digital First Millennium G450 16MB SGRAM + 1705 0003 Digital First Millennium G450 32MB + 1705 0004 Digital First Millennium G450 16MB + b16f 0e11 MGA-G400 AGP + 0527 MGA Parhelia AGP + 102b 0840 Parhelia 128Mb 0d10 MGA Ultima/Impression 1000 MGA G100 [Productiva] 102b ff01 Productiva G100 @@ -765,12 +1009,15 @@ 110a 001e MGA-G100 AGP 2007 MGA Mistral 2527 MGA G550 AGP + 102b 0f83 Millennium G550 102b 0f84 Millennium G550 Dual Head DDR 32Mb + 102b 1e41 Millennium G550 4536 VIA Framegrabber 6573 Shark 10/100 Multiport SwitchNIC 102c Chips and Technologies 00b8 F64310 00c0 F69000 HiQVideo + 102c 00c0 F69000 HiQVideo 00d0 F65545 00d8 F65545 00dc F65548 @@ -781,12 +1028,14 @@ 00f0 F68554 00f4 F68554 HiQVision 00f5 F68555 + 0c30 F69030 102d Wyse Technology Inc. 50dc 3328 Audio 102e Olivetti Advanced Technology 102f Toshiba America 0009 r4x00 0020 ATM Meteor 155 + 102f 00f8 ATM Meteor 155 1030 TMC Research 1031 Miro Computer Products AG 5601 DC20 ASIC @@ -809,6 +1058,8 @@ 0029 PowerVR PCX1 002a PowerVR 3D 0035 USB + 1179 0001 USB + 12ee 7000 Root Hub 003e NAPCCARD Cardbus Controller 0046 PowerVR PCX2 [midas] 005a Vrc5074 [Nile 4] @@ -826,7 +1077,10 @@ 1033 8014 RCV56ACF 56k Voice Modem 009b Vrc5476 00a6 VRC5477 AC97 + 00cd IEEE 1394 [OrangeLink] Host Controller + 12ee 8011 Root hub 00e0 USB 2.0 + 12ee 7001 Root hub 1034 Framatome Connectors USA Inc. 1035 Comp. & Comm. Research Lab 1036 Future Domain Corp. @@ -840,10 +1094,12 @@ 0008 85C503/5513 0009 ACPI 0018 SiS85C503/5513 (LPC Bridge) - 0200 5597/5598 VGA + 0200 5597/5598/6326 VGA 1039 0000 SiS5597 SVGA (Shared RAM) 0204 82C204 0205 SG86C205 + 0300 300/200 + 107d 2720 Leadtek WinFast VR300 0406 85C501/2 0496 85C496 0530 530 Host @@ -852,10 +1108,19 @@ 0601 85C601 0620 620 Host 0630 630 Host + 0633 633 Host + 0635 635 Host + 0645 645 Host + 0646 645DX Host + 0650 650 Host 0730 730 Host + 0733 733 Host 0735 735 Host + 0740 740 Host + 0745 745 Host 0900 SiS900 10/100 Ethernet 1039 0900 SiS900 10/100 Ethernet Adapter + 0961 SiS961 [MuTIOL Media IO] 3602 83C602 5107 5107 5300 SiS540 PCI Display Adapter @@ -877,7 +1142,7 @@ 6300 SiS630 GUI Accelerator+3D 6306 6306 3D-AGP 1039 6306 SiS530,620 GUI Accelerator+3D - 6326 86C326 + 6326 86C326 5598/6326 1039 6326 SiS6326 GUI Accelerator 1092 0a50 SpeedStar A50 1092 0a70 SpeedStar A70 @@ -885,8 +1150,9 @@ 1092 4920 SpeedStar A70 1569 6326 SiS6326 GUI Accelerator 7001 7001 - 7007 OHCI Compliant FireWire Controller + 7007 FireWire Controller 7012 SiS7012 PCI Audio Accelerator + 7013 56k Winmodem (Smart Link HAMR5600 compatible) 7016 SiS7016 10/100 Ethernet Adapter 1039 7016 SiS7016 10/100 Ethernet Adapter 7018 SiS PCI Audio Accelerator @@ -916,15 +1182,36 @@ 103b Tatung Co. of America 103c Hewlett-Packard Company 1005 A4977A Visualize EG - 1030 J2585A - 1031 J2585B + 1006 Visualize FX6 + 1008 Visualize FX4 + 100a Visualize FX2 + 1028 Tach TL Fibre Channel Host Adapter + 1029 Tach XL2 Fibre Channel Host Adapter + 107e 000f Interphase 5560 Fibre Channel Adapter + 9004 9210 1Gb/2Gb Family Fibre Channel Controller + 9004 9211 1Gb/2Gb Family Fibre Channel Controller + 102a Tach TS Fibre Channel Host Adapter + 107e 000e Interphase 5540/5541 Fibre Channel Adapter + 9004 9110 1Gb/2Gb Family Fibre Channel Controller + 9004 9111 1Gb/2Gb Family Fibre Channel Controller + 1030 J2585A DeskDirect 10/100VG NIC + 1031 J2585B HP 10/100VG PCI LAN Adapter 103c 1040 J2973A DeskDirect 10BaseT NIC 103c 1041 J2585B DeskDirect 10/100VG NIC 103c 1042 J2970A DeskDirect 10BaseT/2 NIC 1040 J2973A DeskDirect 10BaseT NIC 1041 J2585B DeskDirect 10/100 NIC 1042 J2970A DeskDirect 10BaseT/2 NIC + 1048 Diva Serial [GSP] Multiport UART + 103c 1049 Tosca Console + 103c 104a Tosca Secondary + 103c 104b Maestro SP2 + 103c 1223 Halfdome Console + 103c 1226 Keystone SP2 + 103c 1227 Powerbar SP2 + 103c 1282 Everest SP2 1064 79C970 PCnet Ethernet Controller + 108b Visualize FXe 10c1 NetServer Smart IRQ Router 10ed TopTools Remote Control 1200 82557B 10/100 NIC @@ -935,8 +1222,9 @@ 1229 zx1 System Bus Adapter 122a zx1 I/O Controller 122e zx1 Local Bus Adapter - 2910 E2910A - 2925 E2925A + 1290 Auxiliary Diva Serial Port + 2910 E2910A PCIBus Exerciser + 2925 E2925A 32 Bit, 33 MHzPCI Exerciser & Analyzer 103e Solliday Engineering 103f Synopsys/Logic Modeling Group 1040 Accelgraphics Inc. @@ -948,11 +1236,49 @@ 3010 Samurai_1 3020 Samurai_IDE 1043 Asustek Computer, Inc. + 0675 ISDNLink P-IN100-ST-D + 4057 V8200 GeForce 3 1044 Distributed Processing Technology 1012 Domino RAID Engine a400 SmartCache/Raid I-IV Controller a500 PCI Bridge a501 SmartRAID V Controller + 1044 c001 PM1554U2 Ultra2 Single Channel + 1044 c002 PM1654U2 Ultra2 Single Channel + 1044 c003 PM1564U3 Ultra3 Single Channel + 1044 c004 PM1564U3 Ultra3 Dual Channel + 1044 c005 PM1554U2 Ultra2 Single Channel (NON ACPI) + 1044 c00a PM2554U2 Ultra2 Single Channel + 1044 c00b PM2654U2 Ultra2 Single Channel + 1044 c00c PM2664U3 Ultra3 Single Channel + 1044 c00d PM2664U3 Ultra3 Dual Channel + 1044 c00e PM2554U2 Ultra2 Single Channel (NON ACPI) + 1044 c00f PM2654U2 Ultra2 Single Channel (NON ACPI) + 1044 c014 PM3754U2 Ultra2 Single Channel (NON ACPI) + 1044 c015 PM3755U2B Ultra2 Single Channel (NON ACPI) + 1044 c016 PM3755F Fibre Channel (NON ACPI) + 1044 c01e PM3757U2 Ultra2 Single Channel + 1044 c01f PM3757U2 Ultra2 Dual Channel + 1044 c020 PM3767U3 Ultra3 Dual Channel + 1044 c021 PM3767U3 Ultra3 Quad Channel + 1044 c028 PM2865U3 Ultra3 Single Channel + 1044 c029 PM2865U3 Ultra3 Dual Channel + 1044 c02a PM2865F Fibre Channel + 1044 c03c 2000S Ultra3 Single Channel + 1044 c03d 2000S Ultra3 Dual Channel + 1044 c03e 2000F Fibre Channel + 1044 c046 3000S Ultra3 Single Channel + 1044 c047 3000S Ultra3 Dual Channel + 1044 c048 3000F Fibre Channel + 1044 c050 5000S Ultra3 Single Channel + 1044 c051 5000S Ultra3 Dual Channel + 1044 c052 5000F Fibre Channel + 1044 c05a 2400A UDMA Four Channel + 1044 c05b 2400A UDMA Four Channel DAC + 1044 c064 3010S Ultra3 Dual Channel + 1044 c065 3010S Ultra3 Four Channel + 1044 c066 3010S Fibre Channel + a511 SmartRAID V Controller 1045 OPTi Inc. a0f8 82C750 [Vendetta] USB Controller c101 92C264 @@ -963,8 +1289,8 @@ c567 82C750 [Vendetta], device 0 c568 82C750 [Vendetta], device 1 c569 82C579 [Viper XPress+ Chipset] - c621 82C621 - c700 82C700 + c621 82C621 [Viper-M/N+] + c700 82C700 [FireStar] c701 82C701 [FireStar Plus] c814 82C814 [Firebridge 1] c822 82C822 @@ -975,16 +1301,20 @@ c895 82C895 c935 EV1935 ECTIVA MachOne PCI Audio d568 82C825 [Firebridge 2] + d721 IDE [FireStar] 1046 IPC Corporation, Ltd. 1047 Genoa Systems Corp 1048 Elsa AG + 0d22 Quadro4 900XGL [ELSA GLoria4 900XGL] 1000 QuickStep 1000 3000 QuickStep 3000 1049 Fountain Technologies, Inc. 104a SGS Thomson Microelectronics 0008 STG 2000X 0009 STG 1764X + 0981 DEC-Tulip compatible 10/100 Ethernet 1746 STG 1764X + 2774 DEC-Tulip compatible 10/100 Ethernet 3520 MPEG-II decoder card 104b BusLogic 0140 BT-946C (old) [multimaster 01] @@ -996,6 +1326,13 @@ 1000 Eagle i/f AS 3d04 TVP4010 [Permedia] 3d07 TVP4020 [Permedia 2] + 1011 4d10 Comet + 1040 000f AccelStar II + 1040 0011 AccelStar II + 1048 0a31 WINNER 2000 + 1048 0a32 GLoria Synergy + 1048 0a35 GLoria Synergy + 107d 2633 WinFast 3D L2300 1092 0127 FIRE GL 1000 PRO 1092 0136 FIRE GL 1000 PRO 1092 0141 FIRE GL 1000 PRO @@ -1008,17 +1345,31 @@ 1092 0156 FIRE GL 1000 PRO 1092 0157 FIRE GL 1000 PRO 1097 3d01 Jeronimo Pro + 1102 100f Graphics Blaster Extreme 3d3d 0100 Reference Permedia 2 3D 8000 PCILynx/PCILynx2 IEEE 1394 Link Layer Controller e4bf 1010 CF1-1-SNARE e4bf 1020 CF1-2-SNARE - 8009 OHCI Compliant FireWire Controller - 8019 TSB12LV23 OHCI Compliant IEEE-1394 Controller + 8009 FireWire Controller + 104d 8032 8032 OHCI i.LINK (IEEE 1394) Controller + 8017 PCI4410 FireWire Controller + 8019 TSB12LV23 IEEE-1394 Controller 11bd 000a Studio DV500-1394 11bd 000e Studio DV e4bf 1010 CF2-1-CYMBAL + 8020 TSB12LV26 IEEE-1394 Controller (Link) + 8021 TSB43AA22 IEEE-1394 Controller (PHY/Link Integrated) + 104d 80df Vaio PCG-FX403 + 104d 80e7 VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP + 8022 TSB43AB22 IEEE-1394a-2000 Controller (PHY/Link) + 8023 TSB43AB22/A IEEE-1394a-2000 Controller (PHY/Link) + 8024 TSB43AB23 IEEE-1394a-2000 Controller (PHY/Link) + 8026 TSB43AB21 IEEE-1394a-2000 Controller (PHY/Link) + 8027 PCI4451 IEEE-1394 Controller + 1028 00e6 PCI4451 IEEE-1394 Controller (Dell Inspiron 8100) a001 TDC1570 a100 TDC1561 + a102 TNETA1575 HyperSAR Plus w/PCI Host i/f & UTOPIA i/f ac10 PCI1050 ac11 PCI1053 ac12 PCI1130 @@ -1035,14 +1386,24 @@ ac1e PCI1211 ac1f PCI1251B ac20 TI 2030 + ac21 PCI2031 + ac22 PCI2032 PCI Docking Bridge + ac23 PCI2250 PCI-to-PCI Bridge + ac28 PCI2050 PCI-to-PCI Bridge ac30 PCI1260 PC card Cardbus Controller ac40 PCI4450 PC card Cardbus Controller ac41 PCI4410 PC card Cardbus Controller ac42 PCI4451 PC card Cardbus Controller + 1028 00e6 PCI4451 PC card CardBus Controller (Dell Inspiron 8100) ac50 PCI1410 PC card Cardbus Controller ac51 PCI1420 + 1014 023b ThinkPad T23 (2647-4MG) + 10cf 1095 Lifebook C6155 + e4bf 1000 CP2-2-HIPHOP ac52 PCI1451 PC card Cardbus Controller ac53 PCI1421 PC card Cardbus Controller + ac55 PCI1250 PC card Cardbus Controller + ac60 PCI2040 PCI to DSP Bridge Controller fe00 FireWire Host Controller fe03 12C01A FireWire Host Controller 104d Sony Corporation @@ -1073,19 +1434,21 @@ 1053 Young Micro Systems 1054 Hitachi, Ltd 1055 Efar Microsystems - 9130 slc90e66 [Victory66] IDE - 9460 slc90e66 [Victory66] ISA - 9462 slc90e66 [Victory66] USB - 9463 slc90e66 [Victory66] ACPI + 9130 SLC90E66 [Victory66] IDE + 9460 SLC90E66 [Victory66] ISA + 9462 SLC90E66 [Victory66] USB + 9463 SLC90E66 [Victory66] ACPI 1056 ICL # Motorola made a mistake and used 1507 instead of 1057 in some chips. Please look at the 1507 entry as well when updating this. 1057 Motorola 0001 MPC105 [Eagle] 0002 MPC106 [Grackle] + 0003 MPC8240 [Kahlua] 0100 MC145575 [HFC-PCI] 0431 KTI829c 100VG 1801 Audio I/O Controller (MIDI) ecc0 0030 Layla + 18c0 MPC8265A/MPC8266 4801 Raven 4802 Falcon 4803 Hawk @@ -1109,19 +1472,34 @@ 14c8 0302 SM56 PCI Fax Modem 1668 0300 SM56 PCI Speakerphone Modem 1668 0302 SM56 PCI Fax Modem + 6400 MPC190 Security Processor (S1 family, encryption) 1058 Electronics & Telecommunications RSH 1059 Teknor Industrial Computers Inc 105a Promise Technology, Inc. 0d30 20265 + 105a 4d33 Ultra100 + 0d38 20263 + 105a 4d39 Fasttrak66 1275 20275 4d30 20267 + 105a 4d33 Ultra100 + 105a 4d39 Fasttrak100 4d33 20246 + 105a 4d33 20246 IDE Controller 4d38 20262 + 105a 4d30 Ultra Device on SuperTrak + 105a 4d33 Ultra66 + 105a 4d39 Fasttrak66 4d68 20268 - 6268 20268R + 105a 4d68 Ultra100TX2 4d69 20269 - 5275 20276 + 5275 PDC20276 IDE + 105a 0275 SuperTrak SX6000 IDE 5300 DC5300 + 6268 20268R + 6269 PDC20271 + 105a 6269 Fasttrack tx2 + 7275 PDC20277 105b Foxconn International, Inc. 105c Wipro Infotech Limited 105d Number 9 Computer Company @@ -1139,7 +1517,24 @@ 105d 0009 Imagine 128 series 2e 4Mb DRAM 105d 000a Imagine 128 series 2 8Mb VRAM 105d 000b Imagine 128 series 2 8Mb H-VRAM + 11a4 000a Barco Metheus 5 Megapixel + 13cc 0000 Barco Metheus 5 Megapixel + 13cc 0004 Barco Metheus 5 Megapixel + 13cc 0005 Barco Metheus 5 Megapixel + 13cc 0006 Barco Metheus 5 Megapixel + 13cc 0008 Barco Metheus 5 Megapixel + 13cc 0009 Barco Metheus 5 Megapixel + 13cc 000a Barco Metheus 5 Megapixel + 13cc 000c Barco Metheus 5 Megapixel 493d Imagine 128 T2R [Ticket to Ride] + 11a4 000a Barco Metheus 5 Megapixel, Dual Head + 11a4 000b Barco Metheus 5 Megapixel, Dual Head + 13cc 0002 Barco Metheus 4 Megapixel, Dual Head + 13cc 0003 Barco Metheus 5 Megapixel, Dual Head + 13cc 0007 Barco Metheus 5 Megapixel, Dual Head + 13cc 0008 Barco Metheus 5 Megapixel, Dual Head + 13cc 0009 Barco Metheus 5 Megapixel, Dual Head + 13cc 000a Barco Metheus 5 Megapixel, Dual Head 5348 Revolution 4 105e Vtech Computers Ltd 105f Infotronic America Inc @@ -1175,10 +1570,12 @@ 1065 Texas Microsystems 1066 PicoPower Technology 0000 PT80C826 - 0001 PT86C52x [Vesuvius] - 0002 PT80C524 [Nile] + 0001 PT86C521 [Vesuvius v1] Host Bridge + 0002 PT86C523 [Vesuvius v3] PCI-ISA Bridge Master + 0003 PT86C524 [Nile] PCI-to-PCI Bridge + 0004 PT86C525 [Nile-II] PCI-to-PCI Bridge 0005 National PC87550 System Controller - 8002 PT80C524 [Nile] + 8002 PT86C523 [Vesuvius v3] PCI-ISA Bridge Slave 1067 Mitsubishi Electric 1002 VG500 [VolumePro Volume Rendering Accelerator] 1068 Diversified Technology @@ -1186,7 +1583,9 @@ 0001 DAC960P 0002 DAC960PD 0010 DAC960PX - ba55 eXtremeRAID support Device + 0050 AcceleRAID 352/170/160 support Device + ba55 eXtremeRAID 1100 support Device + ba56 eXtremeRAID 2000/3000 support Device 106a Aten Research Inc 106b Apple Computer Inc. 0001 Bandit PowerPC host bridge @@ -1257,19 +1656,21 @@ 1075 Advanced Integrations Research 1076 Chaintech Computer Co. Ltd 1077 QLogic Corp. - 1016 QLA10160 - 1020 ISP1020 - 1022 ISP1022 - 1080 QLA1080 - 1216 QLA12160 + 1016 ISP10160 Single Channel Ultra3 SCSI Processor + 1020 ISP1020 Fast-wide SCSI + 1022 ISP1022 Fast-wide SCSI + 1080 ISP1080 SCSI Host Adapter + 1216 ISP12160 Dual Channel Ultra3 SCSI Processor 101e 8471 QLA12160 on AMI MegaRAID 101e 8493 QLA12160 on AMI MegaRAID - 1240 QLA1240 - 1280 QLA1280 - 2020 ISP2020A - 2100 QLA2100 + 1240 ISP1240 SCSI Host Adapter + 1280 ISP1280 + 2020 ISP2020A Fast!SCSI Basic Adapter + 2100 QLA2100 64-bit Fibre Channel Adapter + 1077 0001 QLA2100 64-bit Fibre Channel Adapter 2200 QLA2200 - 2300 QLA2300 + 2300 QLA2300 64-bit FC-AL Adapter + 2312 QLA2312 Fibre Channel Adapter 1078 Cyrix Corporation 0000 5510 [Grappa] 0001 PCI Master @@ -1279,6 +1680,10 @@ 0102 5530 IDE [Kahlua] 0103 5530 Audio [Kahlua] 0104 5530 Video [Kahlua] + 0400 ZFMicro PCI Bridge + 0401 ZFMicro Chipset SMI + 0402 ZFMicro Chipset IDE + 0403 ZFMicro Expansion Bus 1079 I-Bus 107a NetWorth 107b Gateway 2000 @@ -1286,9 +1691,22 @@ 107d LeadTek Research Inc. 0000 P86C850 107e Interphase Corporation - 0001 ATM Interface Card + 0001 5515 ATM Adapter [Flipper] 0002 100 VG AnyLan Controller - 0008 155 Mbit ATM Controller + 0004 5526 Fibre Channel Host Adapter + 0005 x526 Fibre Channel Host Adapter + 0008 5525/5575 ATM Adapter (155 Mbit) [Atlantic] + 9003 5535-4P-BRI-ST + 9007 5535-4P-BRI-U + 9008 5535-1P-SR + 900c 5535-1P-SR-ST + 900e 5535-1P-SR-U + 9011 5535-1P-PRI + 9013 5535-2P-PRI + 9023 5536-4P-BRI-ST + 9027 5536-4P-BRI-U + 9031 5536-1P-PRI + 9033 5536-2P-PRI 107f Data Technology Corporation 0802 SL82C105 1080 Contaq Microsystems @@ -1306,9 +1724,10 @@ 1087 Cache Computer 1088 Microcomputer Systems (M) Son 1089 Data General Corporation -108a Bit3 Computer Corp. +108a SBS Technologies (formerly Bit3 Computer Corp.) 0001 VME Bridge Model 617 0010 VME Bridge Model 618 + 0040 dataBLIZZARD 3000 VME Bridge Model 2706 108c Oakleigh Systems Inc. 108d Olicom @@ -1361,6 +1780,7 @@ 00a8 Speedstar 64 0550 Viper V550 08d4 Supra 2260 Modem + 094c SupraExpress 56i Pro 1092 Viper V330 6120 Maximum DVD 8810 Stealth SE @@ -1404,8 +1824,12 @@ 0647 PCI0647 0648 PCI0648 0649 PCI0649 + 0e11 005d Integrated Ultra ATA-100 Dual Channel Controller + 0e11 007e Integrated Ultra ATA-100 IDE RAID Controller + 101e 0649 AMI MegaRAID IDE 100 Controller 0650 PBC0650A 0670 USB0670 + 1095 0670 USB0670 0673 USB0673 0680 PCI0680 1096 Alacron @@ -1419,17 +1843,23 @@ 109c Megachips Corporation 109d Zida Technologies Ltd. 109e Brooktree Corporation - 0350 Bt848 TV with DMA push + 0350 Bt848 Video Capture 0351 Bt849A Video capture + 0369 Bt878 Video Capture + 1002 0001 TV-Wonder + 1002 0003 TV-Wonder/VE 036c Bt879(??) Video Capture 13e9 0070 Win/TV (Video Section) - 036e Bt878 + 036e Bt878 Video Capture 0070 13eb WinTV/GO + 0070 ff01 Viewcast Osprey 200 + 11bd 001c PCTV Sat (DBC receiver) 127a 0001 Bt878 Mediastream Controller NTSC 127a 0002 Bt878 Mediastream Controller PAL BG 127a 0003 Bt878a Mediastream Controller PAL BG 127a 0048 Bt878/832 Mediastream Controller 144f 3000 MagicTView CPH060 - Video + 1461 0004 AVerTV WDM Video Capture 14f1 0001 Bt878 Mediastream Controller NTSC 14f1 0002 Bt878 Mediastream Controller PAL BG 14f1 0003 Bt878a Mediastream Controller PAL BG @@ -1437,7 +1867,7 @@ 1851 1850 FlyVideo'98 - Video 1851 1851 FlyVideo II 1852 1852 FlyVideo'98 - Video (with FM Tuner) - 036f Bt879 + 036f Bt879 Video Capture 127a 0044 Bt879 Video Capture NTSC 127a 0122 Bt879 Video Capture PAL I 127a 0144 Bt879 Video Capture NTSC @@ -1471,19 +1901,24 @@ 1851 1850 FlyVideo'98 1851 1851 FlyVideo'98 EZ - video 1852 1852 FlyVideo'98 (with FM Tuner) - 0878 Bt878 + 0878 Bt878 Audio Capture 0070 13eb WinTV/GO + 0070 ff01 Viewcast Osprey 200 + 1002 0001 TV-Wonder + 1002 0003 TV-Wonder/VE + 11bd 001c PCTV Sat (DBC receiver) 127a 0001 Bt878 Video Capture (Audio Section) 127a 0002 Bt878 Video Capture (Audio Section) 127a 0003 Bt878 Video Capture (Audio Section) 127a 0048 Bt878 Video Capture (Audio Section) 13e9 0070 Win/TV (Audio Section) 144f 3000 MagicTView CPH060 - Audio + 1461 0004 AVerTV WDM Audio Capture 14f1 0001 Bt878 Video Capture (Audio Section) 14f1 0002 Bt878 Video Capture (Audio Section) 14f1 0003 Bt878 Video Capture (Audio Section) 14f1 0048 Bt878 Video Capture (Audio Section) - 0879 Bt879 Video Capture (Audio Section) + 0879 Bt879 Audio Capture 127a 0044 Bt879 Video Capture (Audio Section) 127a 0122 Bt879 Video Capture (Audio Section) 127a 0144 Bt879 Video Capture (Audio Section) @@ -1510,7 +1945,7 @@ 14f1 1522 Bt879 Video Capture (Audio Section) 14f1 1622 Bt879 Video Capture (Audio Section) 14f1 1722 Bt879 Video Capture (Audio Section) - 0880 Bt880 Video Capture (Audio Section) + 0880 Bt880 Audio Capture 2115 BtV 2115 Mediastream controller 2125 BtV 2125 Mediastream controller 2164 BtV 2164 @@ -1580,14 +2015,28 @@ 0001 i960 PCI bus interface 1076 VScom 800 8 port serial adaptor 1077 VScom 400 4 port serial adaptor - 9030 PCI <-> IOBus Bridge (Hot Swap) - 15ed 1002 Macrolink MCCS 8-port Serial (Hot Swap) - 15ed 1003 Macrolink MCCS 16-port Serial (Hot Swap) + 1078 VScom 210 2 port serial and 1 port parallel adaptor + 1103 VScom 200 2 port serial adaptor + 1146 VScom 010 1 port parallel adaptor + 1147 VScom 020 2 port parallel adaptor + 2724 Thales PCSM Security Card + 9030 PCI <-> IOBus Bridge Hot Swap + 15ed 1002 MCCS 8-port Serial Hot Swap + 15ed 1003 MCCS 16-port Serial Hot Swap 9036 9036 9050 PCI <-> IOBus Bridge 10b5 2273 SH-ARC SoHard ARCnet card + 1522 0001 RockForce 4 Port V.90 Data/Fax/Voice Modem + 1522 0002 RockForce 2 Port V.90 Data/Fax/Voice Modem + 1522 0003 RockForce 6 Port V.90 Data/Fax/Voice Modem + 1522 0004 RockForce 8 Port V.90 Data/Fax/Voice Modem + 1522 0010 RockForce2000 4 Port V.90 Data/Fax/Voice Modem + 1522 0020 RockForce2000 2 Port V.90 Data/Fax/Voice Modem 15ed 1000 Macrolink MCCS 8-port Serial 15ed 1001 Macrolink MCCS 16-port Serial + 15ed 1002 Macrolink MCCS 8-port Serial Hot Swap + 15ed 1003 Macrolink MCCS 16-port Serial Hot Swap + d531 c002 PCIntelliCAN 2xSJA1000 CAN bus d84d 4006 EX-4006 1P d84d 4008 EX-4008 1P EPP/ECP d84d 4014 EX-4014 2P @@ -1605,12 +2054,15 @@ d84d 4065 EX-4065 8S(16C550) RS-232 d84d 4068 EX-4068 8S(16C650) RS-232 d84d 4078 EX-4078 2S(16C552) RS-232+1P + 9054 PCI <-> IOBus Bridge + 10b5 2455 Wessex Techology PHIL-PCI 9060 9060 906d 9060SD 125c 0640 Aries 16000P 906e 9060ES 9080 9080 10b5 9080 9080 [real subsystem ID not set] + 129d 0002 Aculab PCI Prosidy card a001 GTEK Jetport II 2 port serial adaptor c001 GTEK Cyclone 16/32 port serial adaptor 10b6 Madge Networks @@ -1632,14 +2084,20 @@ 000a Smart 100/16/4 PCI Ringnode 10b6 000a Smart 100/16/4 PCI Ringnode 000b 16/4 CardBus Adapter Mk2 + 10b6 0008 16/4 CardBus Adapter Mk2 10b6 000b 16/4 Cardbus Adapter Mk2 - 1000 Collage 25 ATM Adapter + 000c RapidFire 3140V2 16/4 TR Adapter + 10b6 000c RapidFire 3140V2 16/4 TR Adapter + 1000 Collage 25/155 ATM Client Adapter 1001 Collage 155 ATM Server Adapter 10b7 3Com Corporation - 0001 3c985 1000BaseSX - 3390 Token Link Velocity + 0001 3c985 1000BaseSX (SX/TX) + 1006 MINI PCI type 3B Data Fax Modem + 1007 Mini PCI 56k Winmodem + 10b7 615c Mini PCI 56K Modem + 3390 3c339 TokenLink Velocity 3590 3c359 TokenLink Velocity XL - 10b7 3590 TokenLink Velocity XL Adapter + 10b7 3590 TokenLink Velocity XL Adapter (3C359/359B) 4500 3c450 Cyclone/unknown 5055 3c555 Laptop Hurricane 5057 3c575 [Megahertz] 10/100 LAN CardBus @@ -1647,6 +2105,7 @@ 5157 3c575 [Megahertz] 10/100 LAN CardBus 10b7 5b57 3C575 Megahertz 10/100 LAN Cardbus PC Card 5257 3CCFE575CT Cyclone CardBus + 10b7 5c57 FE575C-3Com 10/100 LAN CardBus-Fast Ethernet 5900 3c590 10BaseT [Vortex] 5920 3c592 EISA 10mbps Demon/Vortex 5950 3c595 100BaseTX [Vortex] @@ -1657,10 +2116,20 @@ 10b7 5b57 3C575 Megahertz 10/100 LAN Cardbus PC Card 6055 3c556 Hurricane CardBus 6056 3c556B Hurricane CardBus + 10b7 6556 10/100 Mini PCI Ethernet Adapter 6560 3CCFE656 Cyclone CardBus + 10b7 656a 3CCFEM656 10/100 LAN+56K Modem CardBus + 6561 3CCFEM656 10/100 LAN+56K Modem CardBus + 10b7 656b 3CCFEM656 10/100 LAN+56K Modem CardBus 6562 3CCFEM656 [id 6562] Cyclone CardBus + 10b7 656b 3CCFEM656B 10/100 LAN+56K Modem CardBus + 6563 3CCFEM656B 10/100 LAN+56K Modem CardBus + 10b7 656b 3CCFEM656 10/100 LAN+56K Modem CardBus 6564 3CCFEM656 [id 6564] Cyclone CardBus 7646 3cSOHO100-TX Hurricane + 7940 3c803 FDDILink UTP Controller + 7980 3c804 FDDILink SAS Controller + 7990 3c805 FDDILink DAS Controller 8811 Token ring 9000 3c900 10BaseT [Boomerang] 9001 3c900 Combo [Boomerang] @@ -1671,7 +2140,7 @@ 9006 3c900B-TPC [Etherlink XL TPC] 900a 3c900B-FL [Etherlink XL FL] 9050 3c905 100BaseTX [Boomerang] - 9051 3c905 100BaseT4 + 9051 3c905 100BaseT4 [Boomerang] 9055 3c905B 100BaseTX [Cyclone] 1028 0080 3C905B Fast Etherlink XL 10/100 1028 0081 3C905B Fast Etherlink XL 10/100 @@ -1694,19 +2163,31 @@ 1028 0098 3C905B Fast Etherlink XL 10/100 1028 0099 3C905B Fast Etherlink XL 10/100 10b7 9055 3C905B Fast Etherlink XL 10/100 - 9056 3c905B-T4 + 9056 3c905B-T4 [Fast EtherLink XL 10/100] 9058 3c905B-Combo [Deluxe Etherlink XL 10/100] 905a 3c905B-FX [Fast Etherlink XL FX 10/100] - 9200 3c905C-TX [Fast Etherlink] + 9200 3c905C-TX/TX-M [Tornado] + 1028 0095 Integrated 3C905C-TX Fast Etherlink for PC Management NIC 10b7 1000 3C905C-TX Fast Etherlink for PC Management NIC + 10b7 7000 10/100 Mini PCI Ethernet Adapter 9800 3c980-TX [Fast Etherlink XL Server Adapter] 10b7 9800 3c980-TX Fast Etherlink XL Server Adapter 9805 3c980-TX 10/100baseTX NIC [Python-T] + 10b7 1201 3c982-TXM 10/100baseTX Dual Port A [Hydra] + 10b7 1202 3c982-TXM 10/100baseTX Dual Port B [Hydra] 10b7 9805 3c980 10/100baseTX NIC [Python-T] + 9900 3C990-TX Typhoon + 9902 3CR990-TX-95 56-bit Typhoon Client + 9903 3CR990-TX-97 168-bit Typhoon Client + 9904 3C990B-TX-M/3C990BSVR [Typhoon2] + 9905 3CR990-FX-95/97/95 [Typhon Fiber] + 9908 3CR990SVR95 56-bit Typhoon Server + 9909 3CR990SVR97 Typhoon Server + 990b 3C990SVR [Typhoon Server] 10b8 Standard Microsystems Corp [SMC] 0005 83C170QF - 1055 e000 LANEPIC - 1055 e002 LANEPIC + 1055 e000 LANEPIC 10/100 [EVB171Q-PCI] + 1055 e002 LANEPIC 10/100 [EVB171G-PCI] 10b8 a011 EtherPower II 10/100 10b8 a014 EtherPower II 10/100 10b8 a015 EtherPower II 10/100 @@ -1748,7 +2229,13 @@ 1543 M1543 1621 M1621 1631 ALI M1631 PCI North Bridge Aladdin Pro III + 1632 M1632M Northbridge+Trident 1641 ALI M1641 PCI North Bridge Aladdin Pro IV + 1644 M1644/M1644T Northbridge+Trident + 1646 M1646 Northbridge+Trident + 1647 M1647 Northbridge [MAGiK 1 / MobileMAGiK 1] + 1651 M1651/M1651T Northbridge [Aladdin-Pro 5/5M,Aladdin-Pro 5T/5TM] + 1671 M1671 Northbridge [Aladdin-P4] 3141 M3141 3143 M3143 3145 M3145 @@ -1763,11 +2250,21 @@ 5219 M5219 5225 M5225 5229 M5229 IDE + 1043 8053 A7A266 Motherboard IDE 5235 M5225 - 5237 M5237 USB - 5243 M5243 - 5247 M5247 - 5451 M5451 PCI South Bridge Audio + 5237 USB 1.1 Controller + 5239 USB 2.0 Controller + 5243 M1541 PCI to AGP Controller + 5247 PCI to AGP Controller + 5251 M5251 P1394 OHCI 1.0 Controller + 5253 M5253 P1394 OHCI 1.1 Controller + 5261 M5261 Ethernet Controller + 5451 M5451 PCI AC-Link Controller Audio Device + 5453 M5453 PCI AC-Link Controller Modem Device + 5455 M5455 PCI AC-Link Controller Audio Device + 5457 M5457 AC-Link Modem Interface Controller + 5471 M5471 Memory Stick Controller + 5473 M5473 SD-MMC Controller 7101 M7101 PMU 10b9 7101 ALI M7101 Power Management Controller 10ba Mitsubishi Electric Corp. @@ -1829,6 +2326,7 @@ 110a 8005 MagicMedia 256AV Audio Device 14c0 0004 MagicMedia 256AV Audio Device 8006 NM2360 [MagicMedia 256ZX Audio] + 8016 NM2360 [MagicMedia 256ZX Audio] 10c9 Dataexpert Corporation 10ca Fujitsu Microelectr., Inc. 10cb Omron Corporation @@ -1855,6 +2353,7 @@ 10d9 Macronix, Inc. [MXIC] 0512 MX98713 0531 MX987x5 + 1186 1200 DFE-540TX ProFAST 10/100 Adapter 8625 MX86250 8888 MX86200 10da Compaq IPG-Austin @@ -1869,12 +2368,13 @@ 10dc ATT2C15-3 FPGA 10dd Evans & Sutherland 10de nVidia Corporation - 0008 EDGE 3D [NV1] - 0009 EDGE 3D [NV1] - 0010 Mutara V08 [NV2] - 0020 Riva TnT [NV04] + 0008 NV1 [EDGE 3D] + 0009 NV1 [EDGE 3D] + 0010 NV2 [Mutara V08] + 0020 NV4 [Riva TnT] 1043 0200 V3400 TNT 1048 0c18 Erazor II SGRAM + 1048 0c1b Erazor II 1092 0550 Viper V550 1092 0552 Viper V550 1092 4804 Viper V550 @@ -1887,10 +2387,12 @@ 1092 4904 Viper V550 1092 4914 Viper V550 1092 8225 Viper V550 + 10b4 273d Velocity 4400 + 10b4 2740 Velocity 4400 10de 0020 Riva TNT 1102 1015 Graphics Blaster CT6710 1102 1016 Graphics Blaster RIVA TNT - 0028 Riva TnT2 [NV5] + 0028 NV5 [Riva TnT2] 1043 0200 AGP-V3800 SGRAM 1043 0201 AGP-V3800 SDRAM 1043 0205 PCI-V3800 @@ -1898,13 +2400,15 @@ 1092 4804 Viper V770 1092 4a00 Viper V770 1092 4a02 Viper V770 Ultra + 1092 5a00 RIVA TNT2/TNT2 Pro 1092 6a02 Viper V770 Ultra 1092 7a02 Viper V770 Ultra 10de 0005 RIVA TNT2 Pro + 10de 000f Compaq NVIDIA TNT2 Pro 1102 1020 3D Blaster RIVA TNT2 1102 1026 3D Blaster RIVA TNT2 Digital 14af 5810 Maxi Gamer Xentor - 0029 Riva TnT2 Ultra [NV5] + 0029 NV5 [Riva TnT2 Ultra] 1043 0200 AGP-V3800 Deluxe 1043 0201 AGP-V3800 Ultra SDRAM 1043 0205 PCI-V3800 Ultra @@ -1912,55 +2416,97 @@ 1102 1029 3D Blaster RIVA TNT2 Ultra 1102 102f 3D Blaster RIVA TNT2 Ultra 14af 5820 Maxi Gamer Xentor 32 - 002a Riva TnT2 [NV5] - 002b Riva TnT2 [NV5] - 002c Vanta [NV6] + 002a NV5 [Riva TnT2] + 002b NV5 [Riva TnT2] + 002c NV6 [Vanta] 1043 0200 AGP-V3800 Combat SDRAM 1043 0201 AGP-V3800 Combat 1092 6820 Viper V730 1102 1031 CT6938 VANTA 8MB 1102 1034 CT6894 VANTA 16MB 14af 5008 Maxi Gamer Phoenix 2 - 002d Vanta [NV6] + 002d RIVA TNT2 Model 64 1043 0200 AGP-V3800M 1043 0201 AGP-V3800M 1102 1023 CT6892 RIVA TNT2 Value 1102 1024 CT6932 RIVA TNT2 Value 32Mb - 1102 102c CT6931 RIVA TNT2 Value (Jumper) + 1102 102c CT6931 RIVA TNT2 Value [Jumper] 1462 8808 MSI-8808 - 002e Vanta [NV6] - 002f Vanta [NV6] - 00a0 Riva TNT2 + 1554 1041 PixelView RIVA TNT2 M64 32MB + 002e NV6 [Vanta] + 002f NV6 [Vanta] + 00a0 NV5 [Riva TNT2] 14af 5810 Maxi Gamer Xentor - 0100 GeForce 256 + 0100 NV10 [GeForce 256 SDR] 1043 0200 AGP-V6600 SGRAM 1043 0201 AGP-V6600 SDRAM 1043 4008 AGP-V6600 SGRAM 1043 4009 AGP-V6600 SDRAM 1102 102d CT6941 GeForce 256 - 0101 GeForce 256 DDR + 14af 5022 3D Prophet SE + 0101 NV10 [GeForce 256 DDR] 1043 0202 AGP-V6800 DDR 1043 400a AGP-V6800 DDR SGRAM 1043 400b AGP-V6800 DDR SDRAM 1102 102e CT6971 GeForce 256 DDR 14af 5021 3D Prophet DDR-DVI - 0103 Quadro (GeForce 256 GL) - 0110 NV11 (GeForce2 MX) - 0111 NV11 (GeForce2 MX DDR) - 0112 GeForce2 Go - 0113 NV11 (GeForce2 MXR) - 0150 NV15 (GeForce2 Pro) + 0103 NV10 [Quadro] + 0110 NV11 [GeForce2 MX] + 1043 4015 AGP-V7100 Pro + 1043 4031 V7100 Pro with TV output + 14af 7103 3D Prophet II MX Dual-Display + 0111 NV11 [GeForce2 MX DDR] + 0112 NV11 [GeForce2 Go] + 0113 NV11 [GeForce2 MXR] + 0150 NV15 [GeForce2 GTS] + 1043 4016 V7700 AGP Video Card 107d 2840 WinFast GeForce2 GTS with TV output - 0151 NV15 DDR (GeForce2 GTS) - 0152 NV15 Bladerunner (GeForce2 Ultra) - 0153 NV15 GL (Quadro2 Pro) + 1462 8831 Creative GeForce2 Pro + 0151 NV15 [GeForce2 Ti] + 0152 NV15 [GeForce2 Ultra, Bladerunner] + 1048 0c56 GLADIAC Ultra + 0153 NV15 [Quadro2 Pro] + 0170 NV17 [GeForce4 MX460] + 0171 NV17 [GeForce4 MX440] + 0172 NV17 [GeForce4 MX420] + 0173 NV1x + 0174 NV17 [GeForce4 440 Go] + 0175 NV17 [GeForce4 420 Go] + 0176 NV17 [GeForce4 420 Go 32M] + 0178 Quadro4 500XGL + 0179 NV17 [GeForce4 440 Go 64M] + 017a Quadro4 200/400NVS + 017b Quadro4 550XGL + 017c Quadro4 550 GoGL + 01a0 NV15 [GeForce2 - nForce GPU] + 01a4 nForce CPU bridge + 01ab nForce 420 Memory Controller (DDR) + 01ac nForce 220/420 Memory Controller + 01ad nForce 220/420 Memory Controller + 01b1 nForce Audio + 01b2 nForce ISA Bridge + 01b4 nForce PCI System Management + 01b7 nForce AGP to PCI Bridge + 01b8 nForce PCI-to-PCI bridge 01bc nForce IDE - 0200 NV20 (GeForce3) - 0203 Quadro DCC + 0200 NV20 [GeForce3] + 1043 402f AGP-V8200 DDR + 0201 NV20 [GeForce3 Ti200] + 0202 NV20 [GeForce3 Ti500] + 1043 405b V8200 T5 + 0203 NV20 [Quadro DCC] + 0250 NV25 [GeForce4 Ti4600] + 0251 NV25 [GeForce4 Ti4400] + 0253 NV25 [GeForce4 Ti4200] + 0258 Quadro4 900XGL + 0259 Quadro4 750XGL + 025b Quadro4 700XGL 10df Emulex Corporation 10df Light Pulse Fibre Channel Adapter 1ae5 LP6000 Fibre Channel Host Adapter f700 LP7000 Fibre Channel Host Adapter + f800 LP8000 Fibre Channel Host Adapter + f900 LP9000 Fibre Channel Host Adapter 10e0 Integrated Micro Solutions Inc. 5026 IMS5026/27/28 5027 IMS5027 @@ -1988,14 +2534,15 @@ 8043 LANai4.x [Myrinet LANai interface chip] 8062 S5933_PARASTATION 807d S5933 [Matchmaker] - 8088 Kingsberg Spacetec Format Synchronizer - 8089 Kingsberg Spacetec Serial Output Board + 8088 Kongsberg Spacetec Format Synchronizer + 8089 Kongsberg Spacetec Serial Output Board 809c S5933_HEPC3 80d7 PCI-9112 80d9 PCI-9118 80da PCI-9812 811a PCI-IEEE1355-DS-DE Interface 8170 S5933 [Matchmaker] (Chipset Development Tool) + 82db AJA HDNTV HD SDI Framestore 10e9 Alps Electric Co., Ltd. 10ea Intergraphics Systems 1680 IGA-1680 @@ -2010,8 +2557,8 @@ 8111 Twist3 Frame Grabber 10ec Realtek Semiconductor Co., Ltd. 8029 RTL-8029(AS) - 10b8 2011 EZ-Card - 10ec 8029 RT8029(AS) + 10b8 2011 EZ-Card (SMC1208) + 10ec 8029 RTL-8029(AS) 1113 1208 EN1208 1186 0300 DE-528 1259 2400 AT-2400 @@ -2019,14 +2566,16 @@ 10ec 8129 RT8129 Fast Ethernet Adapter 8138 RT8139 (B/C) Cardbus Fast Ethernet Adapter 10ec 8138 RT8139 (B/C) Fast Ethernet Adapter - 8139 RTL-8139 + 8139 RTL-8139/8139C/8139C+ 1025 8920 ALN-325 1025 8921 ALN-325 10bd 0320 EP-320X-R 10ec 8139 RT8139 1186 1300 DFE-538TX 1186 1320 SN5200 + 1186 8139 DRN-32TX 1259 2500 AT-2500TX + 1259 2503 AT-2500TX/ACPI 1429 d010 ND010 1432 9130 EN-9130TX 1436 8139 RT8139 @@ -2047,6 +2596,8 @@ 3fc1 RME Digi96/8 3fc2 RME Digi96/8 Pro 3fc3 RME Digi96/8 Pad + 3fc4 RME Digi9652 (Hammerfall) + 3fc5 RME Hammerfall DSP 10ef Racore Computer Products, Inc. 8154 M815x Token Ring Adapter 10f0 Peritek Corporation @@ -2064,6 +2615,8 @@ 000c TARGA 1000 10fb Thesys Gesellschaft für Mikroelektronik mbH 10fc I-O Data Device, Inc. +# What's in the cardbus end of a Sony ACR-A01 card, comes with newer Vaio CD-RW drives + 0003 Cardbus IDE Controller 10fd Soyo Computer, Inc 10fe Fast Multimedia AG 10ff NCube @@ -2086,26 +2639,45 @@ 1102 8025 SBLive! Mainboard Implementation 1102 8026 CT4830 SBLive! Value 1102 8027 CT4832 SBLive! Value + 1102 8028 CT4760 SBLive! OEM version 1102 8031 CT4831 SBLive! Value 1102 8040 CT4760 SBLive! 1102 8051 CT4850 SBLive! Value - 7002 SB Live! + 1102 8061 SBLive! Player 5.1 + 0004 SB Audigy + 1102 0051 SB0090 Audigy Player + 4001 SB Audigy FireWire Port + 7002 SB Live! MIDI/Game Port 1102 0020 Gameport Joystick + 7003 SB Audigy MIDI/Game port + 1102 0040 SB Audigy MIDI/Gameport 8938 ES1371 1103 Triones Technologies, Inc. 0003 HPT343 - 0004 HPT366 / HPT370 +# Revisions: 01=HPT366, 03=HPT370, 04=HPT370A, 05=HPT372 + 0004 HPT366/368/370/370A/372 + 1103 0001 HPT370A 1103 0005 HPT370 UDMA100 + 0005 HPT372A + 0006 HPT302 + 0007 HPT371 + 0008 HPT374 1104 RasterOps Corp. 1105 Sigma Designs, Inc. + 1105 REALmagic Xcard MPEG 1/2/3/4 DVD Decoder 8300 REALmagic Hollywood Plus DVD Decoder + 8400 EM840x REALmagic DVD/MPEG-2 Audio/Video Decoder 1106 VIA Technologies, Inc. + 0130 VT6305 1394.A Controller 0305 VT8363/8365 [KT133/KM133] + 1043 8033 A7V Mainboard + 1043 8042 A7V133/A7V133-C Mainboard + 147b a401 KT7/KT7-RAID/KT7A/KT7A-RAID Mainboard 0391 VT8371 [KX133] 0501 VT8501 [Apollo MVP4] 0505 VT82C505 0561 VT82C561 - 0571 Bus Master IDE + 0571 VT82C586B PIPC Bus Master IDE 0576 VT82C576 3V [Apollo Master] 0585 VT82C585VP [Apollo VP1/VPX] 0586 VT82C586/A/B PCI-to-ISA [Apollo VP] @@ -2120,6 +2692,8 @@ 0605 VT8605 [ProSavage PM133] 0680 VT82C680 [Apollo P6] 0686 VT82C686 [Apollo Super South] + 1043 8033 A7V Mainboard + 1043 8042 A7V133/A7V133-C Mainboard 1106 0000 VT82C686/A PCI to ISA Bridge 1106 0686 VT82C686/A PCI to ISA Bridge 0691 VT82C693A/694x [Apollo PRO133x] @@ -2131,30 +2705,52 @@ 1106 VT82C570MV 1571 VT82C416MV 1595 VT82C595/97 [Apollo VP2/97] - 3038 UHCI USB + 3038 USB + 0925 1234 USB Controller 1234 0925 MVP3 USB Controller 3040 VT82C586B ACPI - 3043 VT86C100A [Rhine 10/100] + 3043 VT86C100A [Rhine] 10bd 0000 VT86C100A Fast Ethernet Adapter 1106 0100 VT86C100A Fast Ethernet Adapter - 1186 1400 DFE-530TX - 3044 OHCI Compliant IEEE 1394 Host Controller + 1186 1400 DFE-530TX rev A + 3044 IEEE 1394 Host Controller 3050 VT82C596 Power Management 3051 VT82C596 Power Management 3057 VT82C686 [Apollo Super ACPI] - 3058 AC97 Audio Controller + 1043 8033 A7V Mainboard + 1043 8042 A7V133/A7V133-C Mainboard + 3058 VT82C686 AC97 Audio Controller + 0e11 b194 Soundmax integrated digital audio + 1106 4511 Onboard Audio on EP7KXA + 1458 7600 Onboard Audio 1462 3091 MS-6309 Onboard Audio - 3059 AC97 Audio Controller - 3065 Ethernet Controller + 15dd 7609 Onboard Audio + 3059 VT8233 AC97 Audio Controller + 3065 VT6102 [Rhine-II] + 1186 1400 DFE-530TX rev A + 1186 1401 DFE-530TX rev B 3068 AC97 Modem Controller 3074 VT8233 PCI to ISA Bridge 3091 VT8633 [Apollo Pro266] 3099 VT8367 [KT266] + 1043 8064 A7V266-E + 1043 807f A7V333 + 3101 VT8653 Host Bridge + 3102 VT8662 Host Bridge + 3103 VT8615 Host Bridge + 3104 USB 2.0 3109 VT8233C PCI to ISA Bridge + 3112 VT8361 [KLE133] Host Bridge + 3128 VT8753 [P4X266 AGP] + 3133 VT3133 Host Bridge + 3147 VT8233A ISA Bridge + 3148 P4M266 Host Bridge + 3156 P/KN266 Host Bridge + 3177 VT8233A ISA Bridge 5030 VT82C596 ACPI [Apollo PRO] 6100 VT85C100A [Rhine II] 8231 VT8231 [PCI-to-ISA Bridge] - 8235 VT8235 Power Management + 8235 VT8235 ACPI 8305 VT8363/8365 [KT133/KM133 AGP] 8391 VT8371 [KX133 AGP] 8501 VT8501 [Apollo MVP4 AGP] @@ -2164,8 +2760,13 @@ 8601 VT8601 [Apollo ProMedia AGP] 8605 VT8605 [PM133 AGP] 8691 VT82C691 [Apollo Pro] + 8693 VT82C693 [Apollo Pro Plus] PCI Bridge b091 VT8633 [Apollo Pro266 AGP] - b099 VT8367 [KT266 AGP] + b099 VT8367 [KT333 AGP] + b101 VT8653 AGP Bridge + b102 VT8362 AGP Bridge + b103 VT8615 AGP Bridge + b112 VT8361 [KLE133] AGP Bridge 1107 Stratus Computers 0576 VIA VT82C570MV [Apollo] (Wrong vendor ID!) 1108 Proteon, Inc. @@ -2187,6 +2788,7 @@ 6120 SZB6120 110b Chromatic Research Inc. 0001 Mpact Media Processor + 0004 Mpact 2 110c Mini-Max Technology, Inc. 110d Znyx Advanced Systems 110e CPU Technology @@ -2210,6 +2812,7 @@ 5105 10Mbps Network card 9211 EN-1207D Fast Ethernet Adapter 1113 9211 EN-1207D Fast Ethernet Adapter + 9511 Fast Ethernet Adapter 1114 Atmel Corporation 1115 3D Labs 1116 Data Translation @@ -2314,6 +2917,7 @@ 0001 Powerbis Bridge 111d Integrated Device Tech 0001 IDT77211 ATM Adapter + 0003 IDT77252 ATM network controller 111e Eldec 111f Precision Digital Images 4a47 Precision MX Video engine interface @@ -2329,9 +2933,10 @@ 0200 ForeRunner PCA-200 ATM 0210 PCA-200PC 0250 ATM - 0300 PCA-200E + 0300 ForeRunner PCA-200EPC ATM 0310 ATM 0400 ForeRunnerHE ATM Adapter + 1127 0400 ForeRunnerHE ATM 1129 Firmworks 112a Hermes Electronics Company, Ltd. 112b Linotype - Hell AG @@ -2343,6 +2948,9 @@ 0001 MVC IM-PCI Video frame grabber/processor 1130 Computervision 1131 Philips Semiconductors + 7130 SAA7130 Video Broadcast Decoder +# PCI audio and video broadcast decoder (http://www.semiconductors.philips.com/pip/saa7134hl) + 7134 SAA7134 7145 SAA7145 7146 SAA7146 114b 2003 DVRaptor Video Edit/Capture Card @@ -2356,8 +2964,11 @@ 7912 EiconCard S91 7941 EiconCard S94 7942 EiconCard S94 + 7943 EiconCard S94 + 7944 EiconCard S94 b921 EiconCard P92 b922 EiconCard P92 + b923 EiconCard P92 e001 DIVA 20PRO 1133 e001 DIVA Pro 2.0 S/T e002 DIVA 20 @@ -2366,10 +2977,15 @@ 1133 e003 DIVA Pro 2.0 U e004 DIVA 20_U 1133 e004 DIVA 2.0 U + e005 DIVA LOW + 1133 e005 DIVA 2.01 S/T e010 DIVA Server BRI-2M 1133 e010 DIVA Server BRI-2M + e012 DIVA Server BRI-8M + 1133 e012 DIVA Server BRI-8M e014 DIVA Server PRI-30M 1133 e014 DIVA Server PRI-30M + e018 DIVA Server BRI-2M/-2F 1134 Mercury Computer Systems 0001 Raceway Bridge 1135 Fuji Xerox Co Ltd @@ -2394,8 +3010,8 @@ 113f Equinox Systems, Inc. 0808 SST-64P Adapter 1010 SST-128P Adapter - 80c0 SST-16P Adapter - 80c4 SST-16P Adapter + 80c0 SST-16P DB Adapter + 80c4 SST-16P RJ Adapter 80c8 SST-16P Adapter 8888 SST-4P Adapter 9090 SST-8P Adapter @@ -2440,8 +3056,10 @@ 1148 9844 SK-9844 (1000Base-SX dual link) 1148 9861 SK-9861 (1000Base-SX VF45 single link) 1148 9862 SK-9862 (1000Base-SX VF45 dual link) + 4400 Gigabit Ethernet 1149 Win System Corporation 114a VMIC + 5579 VMIPCI-5579 (Reflective Memory Card) 7587 VMIVME-7587 114b Canopus Co., Ltd 114c Annabooks @@ -2467,12 +3085,24 @@ 001a DataFirePRIme E1 (1-port) 001b AccelePort C/X (IBM) 001d DataFire RAS T1/E1/PRI + 114f 0050 DataFire RAS E1 Adapter + 114f 0051 DataFire RAS Dual E1 Adapter + 114f 0052 DataFire RAS T1 Adapter + 114f 0053 DataFire RAS Dual T1 Adapter 0023 AccelePort RAS 0024 DataFire RAS B4 ST/U + 114f 0030 DataFire RAS BRI U Adapter + 114f 0031 DataFire RAS BRI S/T Adapter 0026 AccelePort 4r 920 0027 AccelePort Xr 920 0034 AccelePort 2r 920 0035 DataFire DSP T1/E1/PRI cPCI + 0040 AccelePort Xp + 0042 AccelePort 2p PCI + 0070 Datafire Micro V IOM2 (Europe) + 0071 Datafire Micro V (Europe) + 0072 Datafire Micro V IOM2 (North America) + 0073 Datafire Micro V (North America) 6001 Avanstar 1150 Thinking Machines Corp 1151 JAE Electronics Inc. @@ -2495,8 +3125,11 @@ 0003 Cardbus Ethernet 10/100 1014 0181 10/100 EtherJet Cardbus Adapter 1014 1181 10/100 EtherJet Cardbus Adapter + 1014 8181 10/100 EtherJet Cardbus Adapter + 1014 9181 10/100 EtherJet Cardbus Adapter 115d 0181 Cardbus Ethernet 10/100 115d 1181 Cardbus Ethernet 10/100 + 1179 0181 Cardbus Ethernet 10/100 8086 8181 EtherExpress PRO/100 Mobile CardBus 32 Adapter 8086 9181 EtherExpress PRO/100 Mobile CardBus 32 Adapter 0005 Cardbus Ethernet 10/100 @@ -2512,12 +3145,15 @@ 000b Cardbus Ethernet 10/100 1014 0183 10/100 EtherJet Cardbus Adapter 115d 0183 Cardbus Ethernet 10/100 + 000c Mini-PCI V.90 56k Modem 000f Cardbus Ethernet 10/100 1014 0183 10/100 EtherJet Cardbus Adapter 115d 0183 Cardbus Ethernet 10/100 0101 Cardbus 56k modem 115d 1081 Cardbus 56k Modem 0103 Cardbus Ethernet + 56k Modem + 1014 9181 Cardbus 56k Modem + 1115 1181 Cardbus Ethernet 100 + 56k Modem 115d 1181 CBEM56G-100 Ethernet + 56k Modem 8086 9181 PRO/100 LAN + Modem56 CardBus 115e Peer Protocols Inc @@ -2533,16 +3169,21 @@ 1165 Imagraph Corporation 0001 Motion TPEG Recorder/Player with audio 1166 ServerWorks + 0005 CNB20-LE Host Bridge 0007 CNB20-LE Host Bridge 0008 CNB20HE Host Bridge 0009 CNB20LE Host Bridge 0010 CIOB30 0011 CMIC-HE + 0013 CNB20-HE Host Bridge + 0014 CNB20-HE Host Bridge + 0015 CMIC-GC Host Bridge + 0016 CMIC-GC Host Bridge 0200 OSB4 South Bridge 0201 CSB5 South Bridge 0211 OSB4 IDE Controller 0212 CSB5 IDE Controller - 0220 OSB4/CSB5 OHCI USB Controller + 0220 OSB4/CSB5 USB Controller 1167 Mutoh Industries Inc 1168 Thine Electronics Inc 1169 Centre for Development of Advanced Computing @@ -2567,6 +3208,7 @@ 1178 Alfa, Inc. afa1 Fast Ethernet Adapter 1179 Toshiba America Info Systems + 0103 EX-IDE Type-B 0404 DVD Decoder card 0406 Tecra Video Capture device 0407 DVD Decoder card (Version 2) @@ -2578,6 +3220,8 @@ 0618 CPU to PCI and PCI to ISA bridge # Claimed to be Lucent DSP1645 [Mars], but that's apparently incorrect. Does anyone know the correct ID? 0701 FIR Port + 0804 TC6371AF SmartMedia Controller + 0805 SD TypA Controller 0d01 FIR Port Type-DO 1179 0001 FIR Port Type-DO 117a A-Trend Technology @@ -2591,16 +3235,29 @@ 0466 RL5c466 0475 RL5c475 0476 RL5c476 II + 104d 80df Vaio PCG-FX403 + 104d 80e7 VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP 0477 RL5c477 0478 RL5c478 + 1014 0184 ThinkPad A30p (2653-64G) + 0522 R5C522 IEEE 1394 Controller + 1014 01cf ThinkPad A30p (2653-64G) + 0551 R5C551 IEEE 1394 Controller + 0552 R5C552 IEEE 1394 Controller 1181 Telmatics International 1183 Fujikura Ltd 1184 Forks Inc 1185 Dataworld International Ltd 1186 D-Link System Inc 0100 DC21041 - 1002 Sundance Ethernet + 1002 DL10050 Sundance Ethernet + 1186 1002 DFE-550TX + 1186 1012 DFE-580TX 1300 RTL8139 Ethernet + 1186 1300 DFE-538TX 10/100 Ethernet Adapter + 1186 1301 DFE-530TX+ 10/100 Ethernet Adapter + 1340 DFE-690TXD CardBus PC Card + 1561 DRP-32TXD Cardbus PC Card 4000 DL2K Ethernet 1187 Advanced Technology Laboratories, Inc. 1188 Shima Seiki Manufacturing Ltd. @@ -2690,6 +3347,7 @@ 11ad ffff LNE100TX 1385 f004 FA310TX c115 LNE100TX [Linksys EtherFast 10/100] + 11ad c001 LNE100TX [ver 2.0] 11ae Aztech System Ltd 11af Avid Technology Inc. 11b0 V3 Semiconductor Inc. @@ -2720,6 +3378,7 @@ 0440 56k WinModem 0001 0440 LT WinModem 56k Data+Fax+Voice+Dsvd 1033 8015 LT WinModem 56k Data+Fax+Voice+Dsvd + 1033 8047 LT WinModem 56k Data+Fax+Voice+Dsvd 1033 804f LT WinModem 56k Data+Fax+Voice+Dsvd 10cf 102c LB LT Modem V.90 56k 10cf 104a BIBLO LT Modem 56k @@ -2727,15 +3386,18 @@ 1179 0001 Internal V.90 Modem 11c1 0440 LT WinModem 56k Data+Fax+Voice+Dsvd 122d 4101 MDP7800-U Modem + 122d 4102 MDP7800SP-U Modem 13e0 0040 LT WinModem 56k Data+Fax+Voice+Dsvd 13e0 0440 LT WinModem 56k Data+Fax+Voice+Dsvd 13e0 0441 LT WinModem 56k Data+Fax+Voice+Dsvd + 13e0 0450 LT WinModem 56k Data+Fax+Voice+Dsvd 13e0 f100 LT WinModem 56k Data+Fax+Voice+Dsvd 13e0 f101 LT WinModem 56k Data+Fax+Voice+Dsvd 144d 2101 LT56PV Modem 149f 0440 LT WinModem 56k Data+Fax+Voice+Dsvd 0441 56k WinModem 1033 804d LT WinModem 56k Data+Fax + 1033 8065 LT WinModem 56k Data+Fax 1092 0440 Supra 56i 1179 0001 Internal V.90 Modem 11c1 0440 LT WinModem 56k Data+Fax @@ -2745,11 +3407,16 @@ 13e0 0100 LT WinModem 56k Data+Fax 13e0 0410 LT WinModem 56k Data+Fax 13e0 0420 TelePath Internet 56k WinModem + 13e0 0440 LT WinModem 56k Data+Fax 13e0 0443 LT WinModem 56k Data+Fax + 13e0 f102 LT WinModem 56k Data+Fax 1416 9804 CommWave 56k Modem 141d 0440 LT WinModem 56k Data+Fax 144f 0441 Lucent 56k V.90 DF Modem + 144f 0449 Lucent 56k V.90 DF Modem + 144f 110d Lucent Win Modem 1468 0441 Presario 56k V.90 DF Modem + 1668 0440 Lucent Win Modem 0442 56k WinModem 0001 0440 LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd 11c1 0440 LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd @@ -2758,6 +3425,7 @@ 13e0 0442 LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd 13fc 2471 LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd 144d 2104 LT56PT Modem + 144f 1104 LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd 149f 0440 LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd 1668 0440 LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd 0443 LT WinModem @@ -2766,14 +3434,23 @@ 0446 LT WinModem 0447 LT WinModem 0448 WinModem 56k + 1014 0131 Lucent Win Modem + 1033 8066 LT WinModem 56k Data+Fax+Voice+Dsvd + 13e0 0030 56k Voice Modem 13e0 0040 LT WinModem 56k Data+Fax+Voice+Dsvd +# Actiontech eth+modem card as used by Dell &c. + 1668 2400 LT WinModem 56k (MiniPCI Ethernet+Modem) 0449 WinModem 56k 0e11 b14d 56k V.90 Modem 13e0 0020 LT WinModem 56k Data+Fax 13e0 0041 TelePath Internet 56k WinModem + 1436 0440 Lucent Win Modem 144f 0449 Lucent 56k V.90 DFi Modem + 1468 0410 IBM ThinkPad T23 (2647-4MG) + 1468 0440 Lucent Win Modem 1468 0449 Presario 56k V.90 DFi Modem 044a F-1156IV WinModem (V90, 56KFlex) + 10cf 1072 LB Global LT Modem 13e0 0012 LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd 13e0 0042 LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd 144f 1005 LT WinModem 56k Data+Fax+Voice+VoiceView+Dsvd @@ -2781,6 +3458,7 @@ 044c LT WinModem 044d LT WinModem 044e LT WinModem + 044f V90 WildWire Modem 0450 LT WinModem 0451 LT WinModem 0452 LT WinModem @@ -2792,7 +3470,13 @@ 0458 LT WinModem 0459 LT WinModem 045a LT WinModem + 0461 V90 WildWire Modem + 0462 V90 WildWire Modem 0480 Venus Modem (V90, 56KFlex) + 5801 USB + 5802 USS-312 USB Controller + 5811 FW323 + dead 0800 FireWire Host Bus Adapter 11c2 Sand Microelectronics 11c3 NEC Corp 11c4 Document Technologies, Inc @@ -2823,6 +3507,7 @@ 11d2 Intercom Inc. 11d3 Trancell Systems Inc 11d4 Analog Devices + 1805 SM56 PCI modem 1889 AD1889 sound chip 11d5 Ikon Corporation 0115 10115 @@ -2878,6 +3563,7 @@ 0113 FreedomLine 100 1401 ReadyLink 2000 2011 RL100-ATX 10/100 + 11f6 2011 RL100-ATX 2201 ReadyLink 100TX (Winbond W89C840) 11f6 2011 ReadyLink 100TX 9881 RL100TX @@ -2896,11 +3582,13 @@ 0004 RocketPort 32 Intf 0005 RocketPort Octacable 0006 RocketPort 8J + 0007 RocketPort 4-port 0008 RocketPort 8-port 0009 RocketPort 16-port 000a RocketPort Plus Quadcable 000b RocketPort Plus Octacable 000c RocketPort 8-port Modem + 8015 RocketPort 4-port UART 16954 11ff Scion Corporation 1200 CSS Corporation 1201 Vista Controls Corp @@ -2917,20 +3605,21 @@ 120c Technical Corp. 120d Compression Labs, Inc. 120e Cyclades Corporation - 0100 Cyclom_Y below first megabyte - 0101 Cyclom_Y above first megabyte - 0102 Cyclom_4Y below first megabyte - 0103 Cyclom_4Y above first megabyte - 0104 Cyclom_8Y below first megabyte - 0105 Cyclom_8Y above first megabyte - 0200 Cyclom_Z below first megabyte - 0201 Cyclom_Z above first megabyte + 0100 Cyclom-Y below first megabyte + 0101 Cyclom-Y above first megabyte + 0102 Cyclom-4Y below first megabyte + 0103 Cyclom-4Y above first megabyte + 0104 Cyclom-8Y below first megabyte + 0105 Cyclom-8Y above first megabyte + 0200 Cyclades-Z below first megabyte + 0201 Cyclades-Z above first megabyte 0300 PC300/RSV or /X21 (2 ports) - 0301 PC300/RSV or /X21 (1 ports) + 0301 PC300/RSV or /X21 (1 port) 0310 PC300/TE (2 ports) 0311 PC300/TE (1 port) 0320 PC300/TE-M (2 ports) 0321 PC300/TE-M (1 port) + 0400 PC400 120f Essential Communications 0001 Roadrunner serial HIPPI 1210 Hyperparallel Technologies @@ -2941,12 +3630,15 @@ 1215 Interware Co., Ltd 1216 Purup Prepress A/S 1217 O2 Micro, Inc. - 6729 6729 - 673a 6730 - 6832 6832 - 6836 6836 + 6729 OZ6729 + 673a OZ6730 + 6832 OZ6832/6833 Cardbus Controller + 6836 OZ6836/6860 Cardbus Controller 6872 OZ6812 Cardbus Controller + 6925 OZ6922 Cardbus Controller 6933 OZ6933 Cardbus Controller + 1025 1016 Travelmate 612 TX + 6972 OZ6912 Cardbus Controller 1218 Hybricon Corp. 1219 First Virtual Corporation 121a 3Dfx Interactive, Inc. @@ -2961,7 +3653,7 @@ 1092 8030 Monster Fusion 1092 8035 Monster Fusion AGP 10b0 0001 Dragon 4000 - 1102 1017 CT6760 3D Blaster Banshee + 1102 1018 3D Blaster Banshee VE 121a 0001 Voodoo Banshee AGP 121a 0003 Voodoo Banshee AGP SGRAM 121a 0004 Voodoo Banshee @@ -2991,6 +3683,7 @@ 121a 0062 Voodoo3 3500 TV (SECAM) 0009 Voodoo 4 / Voodoo 5 121a 0009 Voodoo5 AGP 5500/6000 + 0057 Voodoo 3/3000 [Avenger] 121b Advanced Telecommunications Modules 121c Nippon Texaco., Ltd 121d Lippert Automationstechnik GmbH @@ -3059,12 +3752,19 @@ 1240 Marathon Technologies Corp. 1241 DSC Communications 1242 Jaycor Networks, Inc. + 1242 JNI Corporation (former Jaycor Networks, Inc.) 4643 FCI-1063 Fibre Channel Adapter + 6562 FCX2-6562 Dual Channel PCI-X Fibre Channel Adapter + 656a FCX-6562 PCI-X Fibre Channel Adapter 1243 Delphax 1244 AVM Audiovisuelles MKTG & Computer System GmbH 0700 B1 ISDN + 0800 C4 ISDN 0a00 A1 ISDN [Fritz] 1244 0a00 FRITZ!Card ISDN Controller + 0e00 Fritz!PCI v2.0 ISDN + 1100 C2 ISDN + 1200 T1 ISDN 1245 A.P.D., S.A. 1246 Dipix Technologies, Inc. 1247 Xylon Research, Inc. @@ -3072,11 +3772,14 @@ 1249 Samsung Electronics Co., Ltd. 124a AEG Electrocom GmbH 124b SBS/Greenspring Modular I/O + 0040 cPCI-200 Four Slot IndustryPack carrier + 124b 9080 PCI9080 Bridge 124c Solitron Technologies, Inc. 124d Stallion Technologies, Inc. 0000 EasyConnection 8/32 0002 EasyConnection 8/64 0003 EasyIO + 0004 EasyConnection/RA 124e Cylink 124f Infotrend Technology, Inc. 0041 IFT-2000 Series RAID Controller @@ -3102,6 +3805,7 @@ 125b Asix Electronics Corporation 1400 ALFA GFC2204 125c Aurora Technologies, Inc. + 0640 Aries 16000P 125d ESS Technology 0000 ES336H Fax Modem (Early Model) 1948 Solo? @@ -3123,13 +3827,27 @@ 1989 ESS Modem 125d 1989 ESS Modem 1998 ES1983S Maestro-3i PCI Audio Accelerator + 1028 00e6 ES1983S Maestro-3i (Dell Inspiron 8100) 1999 ES1983S Maestro-3i PCI Modem Accelerator + 199a ES1983S Maestro-3i PCI Audio Accelerator + 199b ES1983S Maestro-3i PCI Modem Accelerator 2808 ES336H Fax Modem (Later Model) 2838 ES2838/2839 SuperLink Modem 2898 ES2898 Modem + 125d 0424 ES56-PI Data Fax Modem + 125d 0425 ES56T-PI Data Fax Modem + 125d 0426 ES56V-PI Data Fax Modem + 125d 0427 VW-PI Data Fax Modem + 125d 0428 ES56ST-PI Data Fax Modem + 125d 0429 ES56SV-PI Data Fax Modem + 147a c001 ES56-PI Data Fax Modem + 14fe 0428 ES56-PI Data Fax Modem + 14fe 0429 ES56-PI Data Fax Modem 125e Specialvideo Engineering SRL 125f Concurrent Technologies, Inc. 1260 Harris Semiconductor + 3873 Prism 2.5 Wavelan chipset + 1186 3501 DWL-520 Wireless PCI Adapter 8130 HMP8130 NTSC/PAL Video Decoder 8131 HMP8131 NTSC/PAL Video Decoder 1261 Matsushita-Kotobuki Electronics Industries, Ltd. @@ -3166,6 +3884,7 @@ 0002 DirecPC 1274 Ensoniq 1371 ES1371 [AudioPCI-97] + 0e11 0024 AudioPCI on Motherboard Compaq Deskpro 0e11 b1a7 ES1371, ES1373 AudioPCI 1033 80ac ES1371, ES1373 AudioPCI 1042 1854 Tazer @@ -3222,7 +3941,9 @@ 4942 4c4c Creative Sound Blaster AudioPCI128 5880 5880 AudioPCI 1274 2000 Creative Sound Blaster AudioPCI128 + 1274 2003 Creative SoundBlaster AudioPCI 128 1274 5880 Creative Sound Blaster AudioPCI128 + 1458 a000 5880 AudioPCI On Motherboard 6OXET 1462 6880 5880 AudioPCI On Motherboard MS-6188 1.00 270f 2001 5880 AudioPCI On Motherboard 6CTR 270f 2200 5880 AudioPCI On Motherboard 6WTX @@ -3231,6 +3952,7 @@ 1276 Switched Network Technologies, Inc. 1277 Comstream 1278 Transtech Parallel Systems Ltd. + 0701 TPE3/TM3 PowerPC Node 1279 Transmeta Corporation 0295 Northbridge 0395 LongRun Northbridge @@ -3238,93 +3960,103 @@ 0397 BIOS scratchpad 127a Rockwell International 1002 HCF 56k Data/Fax Modem - 122d 4002 HPG / MDP3858-U # Aztech - 122d 4005 MDP3858-E # Aztech - 122d 4007 MDP3858-A/-NZ # Aztech - 122d 4012 MDP3858-SA # Aztech - 122d 4017 MDP3858-W # Aztech - 122d 4018 MDP3858-W # Aztech + 1092 094c SupraExpress 56i PRO [Diamond SUP2380] + 122d 4002 HPG / MDP3858-U + 122d 4005 MDP3858-E + 122d 4007 MDP3858-A/-NZ + 122d 4012 MDP3858-SA + 122d 4017 MDP3858-W + 122d 4018 MDP3858-W + 127a 1002 Rockwell 56K D/F HCF Modem 1003 HCF 56k Data/Fax Modem - 0e11 b0bc 229-DF Zephyr # Compaq - 0e11 b114 229-DF Cheetah # Compaq - 1033 802b 229-DF # NEC - 13df 1003 PCI56RX Modem # E-Tech Inc - 13e0 0117 IBM # GVC - 13e0 0147 IBM # GVC - 13e0 0197 IBM # GVC - 13e0 01c7 IBM # GVC - 13e0 01f7 IBM # GVC - 1436 1003 IBM # CIS - 1436 1103 IBM # CIS + 0e11 b0bc 229-DF Zephyr + 0e11 b114 229-DF Cheetah + 1033 802b 229-DF + 13df 1003 PCI56RX Modem + 13e0 0117 IBM + 13e0 0147 IBM F-1156IV+/R3 Spain V.90 Modem + 13e0 0197 IBM + 13e0 01c7 IBM F-1156IV+/R3 WW V.90 Modem + 13e0 01f7 IBM + 1436 1003 IBM + 1436 1103 IBM 5614PM3G V.90 Modem 1436 1602 Compaq 229-DF Ducati 1004 HCF 56k Data/Fax/Voice Modem + 1048 1500 MicroLink 56k Modem 10cf 1059 Fujitsu 229-DFRT 1005 HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem - 1033 8029 229-DFSV # NEC - 1033 8054 Modem # NEC + 1033 8029 229-DFSV + 1033 8054 Modem 10cf 103c Fujitsu 10cf 1055 Fujitsu 229-DFSV 10cf 1056 Fujitsu 229-DFSV - 122d 4003 MDP3858SP-U # Aztech - 122d 4006 Packard Bell MDP3858V-E # Aztech - 122d 4008 MDP3858SP-A/SP-NZ # Aztech - 122d 4009 MDP3858SP-E # Aztech - 122d 4010 MDP3858V-U # Aztech - 122d 4011 MDP3858SP-SA # Aztech - 122d 4013 MDP3858V-A/V-NZ # Aztech - 122d 4015 MDP3858SP-W # Aztech - 122d 4016 MDP3858V-W # Aztech - 122d 4019 MDP3858V-SA # Aztech - 13df 1005 PCI56RVP Modem # E-Tech Inc - 13e0 0187 IBM # GVC - 13e0 01a7 IBM # GVC - 13e0 01b7 IBM # GVC - 13e0 01d7 IBM # GVC - 1436 1005 IBM # CIS - 1436 1105 IBM # CIS + 122d 4003 MDP3858SP-U + 122d 4006 Packard Bell MDP3858V-E + 122d 4008 MDP3858SP-A/SP-NZ + 122d 4009 MDP3858SP-E + 122d 4010 MDP3858V-U + 122d 4011 MDP3858SP-SA + 122d 4013 MDP3858V-A/V-NZ + 122d 4015 MDP3858SP-W + 122d 4016 MDP3858V-W + 122d 4019 MDP3858V-SA + 13df 1005 PCI56RVP Modem + 13e0 0187 IBM + 13e0 01a7 IBM + 13e0 01b7 IBM DF-1156IV+/R3 Spain V.90 Modem + 13e0 01d7 IBM DF-1156IV+/R3 WW V.90 Modem + 1436 1005 IBM + 1436 1105 IBM + 1437 1105 IBM 5614PS3G V.90 Modem + 1022 HCF 56k Modem + 1436 1303 M3-5614PM3G V.90 Modem 1023 HCF 56k Data/Fax Modem - 122d 4020 Packard Bell MDP3858-WE # Aztech - 122d 4023 MDP3858-UE # Aztech - 13e0 0247 IBM # GVC - 13e0 0297 IBM # GVC - 13e0 02c7 IBM # GVC - 1436 1203 IBM # CIS - 1436 1303 IBM # CIS + 122d 4020 Packard Bell MDP3858-WE + 122d 4023 MDP3858-UE + 13e0 0247 IBM F-1156IV+/R6 Spain V.90 Modem + 13e0 0297 IBM + 13e0 02c7 IBM F-1156IV+/R6 WW V.90 Modem + 1436 1203 IBM + 1436 1303 IBM 1024 HCF 56k Data/Fax/Voice Modem 1025 HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem 10cf 106a Fujitsu 235-DFSV - 122d 4021 Packard Bell MDP3858V-WE # Aztech - 122d 4022 MDP3858SP-WE # Aztech - 122d 4024 MDP3858V-UE # Aztech - 122d 4025 MDP3858SP-UE # Aztech + 122d 4021 Packard Bell MDP3858V-WE + 122d 4022 MDP3858SP-WE + 122d 4024 MDP3858V-UE + 122d 4025 MDP3858SP-UE 1026 HCF 56k PCI Speakerphone Modem + 1032 HCF 56k Modem + 1033 HCF 56k Modem + 1034 HCF 56k Modem 1035 HCF 56k PCI Speakerphone Modem + 1036 HCF 56k Modem 1085 HCF 56k Volcano PCI Modem 2005 HCF 56k Data/Fax Modem - 104d 8044 229-DFSV # Sony - 104d 8045 229-DFSV # Sony - 104d 8055 PBE/Aztech 235W-DFSV # Sony - 104d 8056 235-DFSV # Sony - 104d 805a Modem # Sony - 104d 805f Modem # Sony - 104d 8074 Modem # Sony + 104d 8044 229-DFSV + 104d 8045 229-DFSV + 104d 8055 PBE/Aztech 235W-DFSV + 104d 8056 235-DFSV + 104d 805a Modem + 104d 805f Modem + 104d 8074 Modem 2013 HSF 56k Data/Fax Modem - 1179 0001 Modem # Toshiba - 1179 ff00 Modem # Toshiba + 1179 0001 Modem + 1179 ff00 Modem 2014 HSF 56k Data/Fax/Voice Modem 10cf 1057 Fujitsu Citicorp III - 122d 4050 MSP3880-U # Aztech - 122d 4055 MSP3880-W # Aztech + 122d 4050 MSP3880-U + 122d 4055 MSP3880-W 2015 HSF 56k Data/Fax/Voice/Spkp (w/Handset) Modem 10cf 1063 Fujitsu 10cf 1064 Fujitsu 1468 2015 Fujitsu 2016 HSF 56k Data/Fax/Voice/Spkp Modem - 122d 4051 MSP3880V-W # Aztech - 122d 4052 MSP3880SP-W # Aztech - 122d 4054 MSP3880V-U # Aztech - 122d 4056 MSP3880SP-U # Aztech - 122d 4057 MSP3880SP-A # Aztech + 122d 4051 MSP3880V-W + 122d 4052 MSP3880SP-W + 122d 4054 MSP3880V-U + 122d 4056 MSP3880SP-U + 122d 4057 MSP3880SP-A 4311 Riptide HSF 56k PCI Modem 127a 4311 Ring Modular? Riptide HSF RT HP Dom 13e0 0210 HP-GVC @@ -3334,10 +4066,11 @@ 1235 4321 Hewlett Packard DF 1235 4324 Hewlett Packard DF 13e0 0210 Hewlett Packard DF - 144d 2321 Riptide # Samsung + 144d 2321 Riptide 4322 Riptide PCI Game Controller 1235 4322 Riptide PCI Game Controller 8234 RapidFire 616X ATM155 Adapter + 108d 0022 RapidFire 616X ATM155 Adapter 108d 0027 RapidFire 616X ATM155 Adapter 127b Pixera Corporation 127c Crosspoint Solutions, Inc. @@ -3389,6 +4122,7 @@ 1298 Spellcaster Telecommunications Inc. 1299 Knowledge Technology Lab. 129a VMetro, inc. + 0615 PBT-615 PCI-X Bus Analyzer 129b Image Access 129c Jaycor 129d Compcore Multimedia, Inc. @@ -3410,9 +4144,11 @@ 12ac Measurex Corporation 12ad Multidata GmbH 12ae Alteon Networks Inc. - 0001 AceNIC Gigabit Ethernet (Fibre) - 1410 0104 Gigabit Ethernet-SX PCI Adapter (14100401) + 0001 AceNIC Gigabit Ethernet + 12ae 0001 Gigabit Ethernet-SX (Universal) + 1410 0104 Gigabit Ethernet-SX PCI Adapter 0002 AceNIC Gigabit Ethernet (Copper) + 12ae 0002 Gigabit Ethernet-T (3C986-T) 12af TDK USA Corp 12b0 Jorge Scientific Corp 12b1 GammaLink @@ -3459,6 +4195,8 @@ 5598 PCI NE2K Ethernet 12c4 Connect Tech Inc 12c5 Picture Elements Incorporated + 007e Imaging/Scanning Subsystem Engine + 007f Imaging/Scanning Subsystem Engine 0081 PCIVST [Grayscale Thresholding Engine] 0085 Video Simulator/Sender 0086 THR2 Multi-scale Thresholder @@ -3478,19 +4216,24 @@ 0008 NV1 0009 DAC64 0018 Riva128 + 1048 0c10 VICTORY Erazor 107b 8030 STB Velocity 128 1092 0350 Viper V330 1092 1092 Viper V330 10b4 1b1b STB Velocity 128 - 10b4 1b20 STB Velocity 128 + 10b4 1b1d STB Velocity 128 + 10b4 1b1e STB Velocity 128, PAL TV-Out + 10b4 1b20 STB Velocity 128 Sapphire 10b4 1b21 STB Velocity 128 10b4 1b22 STB Velocity 128 AGP, NTSC TV-Out 10b4 1b23 STB Velocity 128 AGP, PAL TV-Out 10b4 1b27 STB Velocity 128 DVD + 10b4 1b88 MVP Pro 128 10b4 222a STB Velocity 128 AGP 10b4 2230 STB Velocity 128 + 10b4 2232 STB Velocity 128 10b4 2235 STB Velocity 128 AGP - 2a15 54a3 3DVision-SAGP + 2a15 54a3 3DVision-SAGP / 3DexPlorer 3000 0019 Riva128ZX 0020 TNT 0028 TNT2 @@ -3498,7 +4241,7 @@ 002c VTNT2 00a0 ITNT2 12d3 Vingmed Sound A/S -12d4 DGM&S +12d4 Ulticom (Formerly DGM&S) 12d5 Equator Technologies 12d6 Analogic Corp 12d7 Biotronic SRL @@ -3531,6 +4274,7 @@ 1092 2100 Sonic Impact A3D 1092 2110 Sonic Impact A3D 1092 2200 Sonic Impact A3D + 122d 1002 AU8820 Vortex Digital Audio Processor 12eb 0001 AU8820 Vortex Digital Audio Processor 5053 3355 Montego 0002 Vortex 2 @@ -3751,6 +4495,7 @@ 135a Brain Boxes 135b Giganet Inc 135c Quatech Inc + 00f0 MPAC-100 Syncronous Serial Card (Zilog 85230) 135d ABB Network Partner AB 135e Sealevel Systems Inc 7101 Single Port RS-232/422/485/530 @@ -3797,6 +4542,7 @@ 1383 Controlnet Inc 1384 Reality Simulation Systems Inc 1385 Netgear + 4100 802.11b Wireless Adapter (MA301) 620a GA620 622a GA622 630a GA630 @@ -3822,6 +4568,8 @@ 2180 Intellio C218 Turbo PCI 3200 Intellio C320 Turbo PCI 1394 Level One Communications + 0001 LXT1001 Gigabit Ethernet + 1394 0001 NetCelerator Adapter 1395 Ambicom Inc 1396 Cipher Systems Inc 1397 Cologne Chip Designs GmbH @@ -3831,6 +4579,9 @@ 1398 Clarion co. Ltd 1399 Rios systems Co Ltd 139a Alacritech Inc + 0001 Quad Port 10/100 Server Accelerator + 0003 Single Port 10/100 Server Accelerator + 0005 Single Port Gigabit Server Accelerator 139b Mediasonic Multimedia Systems Ltd 139c Quantum 3d Inc 139d EPL limited @@ -3839,12 +4590,17 @@ 13a0 Crystal Group Inc 13a1 Kawasaki Heavy Industries Ltd 13a2 Ositech Communications Inc -13a3 Hi-Fn +13a3 Hifn Inc. + 0005 7751 Security Processor + 0006 6500 Public Key Processor + 0007 7811 Security Processor + 0012 7951 Security Processor 13a4 Rascom Inc 13a5 Audio Digital Imaging Inc 13a6 Videonics Inc 13a7 Teles AG 13a8 Exar Corp. + 0158 XR17C158 Octal UART 13a9 Siemens Medical Systems, Ultrasound Group 13aa Broadband Networks Inc 13ab Arcom Control Systems Ltd @@ -3873,6 +4629,7 @@ 13c1 3ware Inc 1000 3ware ATA-RAID 1001 3ware 7000-series ATA-RAID + 1002 3ware ATA-RAID 13c2 Technotrend Systemtechnik GmbH 13c3 Janz Computer AG 13c4 Phase Metrics @@ -3889,6 +4646,7 @@ 13cf Studio Audio & Video Ltd 13d0 Techsan Electronics Co Ltd 13d1 Abocom Systems Inc + ab06 RTL8139 [FE2000VX] CardBus Fast Ethernet Attached Port Adapter 13d2 Shark Multimedia Inc 13d3 IMC Networks 13d4 Graphics Microsystems Inc @@ -3922,11 +4680,12 @@ 13ee Hayes Microcomputer Products Inc 13ef Coppercom Inc 13f0 Sundance Technology Inc - 0201 Sundance Ethernet + 0201 ST201 Sundance Ethernet 13f1 Oce' - Technologies B.V. 13f2 Ford Microelectronics Inc 13f3 Mcdata Corporation -13f4 Troika Design Inc +13f4 Troika Networks, Inc. + 1401 Zentai Fibre Channel Adapter 13f5 Kansai Electric Co. Ltd 13f6 C-Media Electronics Inc 0100 CM8338A @@ -3934,6 +4693,8 @@ 0101 CM8338B 13f6 0101 CMI8338-031 PCI Audio Device 0111 CM8738 + 1043 8077 CMI8738 6-channel audio controller + 1043 80e2 CMI8738 6ch-MX 13f6 0111 CMI8738/C3DX PCI Audio Device 0211 CM8738 13f7 Wildfire Communications @@ -3944,6 +4705,7 @@ 13fc Computer Peripherals International 13fd Micro Science Inc 13fe Advantech Co. Ltd + 1756 PCI-1756 13ff Silicon Spice Inc 1400 Artx Inc 1401 9432 TX @@ -3963,11 +4725,13 @@ 0500 Lava Single Serial 0600 Lava Port 650 8000 Lava Parallel + 8001 Dual parallel port controller A 8002 Lava Dual Parallel port A 8003 Lava Dual Parallel port B 8800 BOCA Research IOPPAR 1408 Aloka Co. Ltd 1409 Timedia Technology Co Ltd + 7168 PCI2S550 (Dual 16550 UART) 140a DSP Research Inc 140b Ramix Inc 140c Elmic Systems Inc @@ -3981,13 +4745,16 @@ 1413 Addonics 1414 Microsoft Corporation 1415 Oxford Semiconductor Ltd - 9501 16PCI954 Function 0 - 15ed 2000 Macrolink MCCR Serial p0-3 of 8 - 15ed 2001 Macrolink MCCR Serial p0-3 of 16 - 9511 16PCI954 Function 1 - 15ed 2000 Macrolink MCCR Serial p4-7 of 8 - 15ed 2001 Macrolink MCCR Serial p4-15 of 16 - 9521 16PCI952 PCI/dual 16950 UART + 8403 VScom 011H-EP1 1 port parallel adaptor + 9501 OX16PCI954 (Quad 16950 UART) function 0 + 15ed 2000 MCCR Serial p0-3 of 8 + 15ed 2001 MCCR Serial p0-3 of 16 + 950a EXSYS EX-41092 Dual 16950 Serial adapter + 950b OXCB950 Cardbus 16950 UART + 9511 OX16PCI954 (Quad 16950 UART) function 1 + 15ed 2000 MCCR Serial p4-7 of 8 + 15ed 2001 MCCR Serial p4-15 of 16 + 9521 OX16PCI952 (Dual 16950 UART) 1416 Multiwave Innovation pte Ltd 1417 Convergenet Technologies Inc 1418 Kyushu electronics systems Inc @@ -4164,6 +4931,10 @@ 14b8 Techsoft Technology Co Ltd 14b9 AIRONET Wireless Communications 0001 PC4800 + 0340 PC4800 + 0350 PC4800 + 4500 PC4500 + 4800 PC4800 14ba INTERNIX Inc. 14bb SEMTECH Corporation 14bc Globespan Semiconductor Inc. @@ -4189,6 +4960,22 @@ 14d0 Ericsson Axe R & D 14d1 Computer Hi-Tech Co Ltd 14d2 Titan Electronics Inc + 8001 VScom 010L 1 port parallel adaptor + 8002 VScom 020L 2 port parallel adaptor + 8010 VScom 100L 1 port serial adaptor + 8011 VScom 110L 1 port serial and 1 port parallel adaptor + 8020 VScom 200L 1 port serial adaptor + 8021 VScom 210L 2 port serial and 1 port parallel adaptor + 8040 VScom 400L 4 port serial adaptor + 8080 VScom 800L 8 port serial adaptor + a000 VScom 010H 1 port parallel adaptor + a001 VScom 100H 1 port serial adaptor + a003 VScom 400H 4 port serial adaptor + a004 VScom 400HF1 4 port serial adaptor + a005 VScom 200H 2 port serial adaptor + e001 VScom 010HV2 1 port parallel adaptor + e010 VScom 100HV2 1 port serial adaptor + e020 VScom 200HV2 2 port serial adaptor 14d3 CIRTECH (UK) Ltd 14d4 Panacom Technology Corp 14d5 Nitsuko Corporation @@ -4210,62 +4997,75 @@ 0007 PCI224 0008 PCI234 0009 PCI236 + 000a PCI272 + 000b PCI215 14dd Boulder Design Labs Inc 14de Applied Integration Corporation 14df ASIC Communications Corp 14e1 INVERTEX 14e2 INFOLIBRIA 14e3 AMTELCO -14e4 BROADCOM Corporation +14e4 Broadcom Corporation 1644 NetXtreme BCM5700 Gigabit Ethernet - 1014 0277 Broadcom Vigil B5700 1000Base-T + 1014 0277 Broadcom Vigil B5700 1000BaseTX 1028 00d1 Broadcom BCM5700 1028 0106 Broadcom BCM5700 - 1028 0109 Broadcom BCM5700 - 1028 010a Broadcom BCM5700 + 1028 0109 Broadcom BCM5700 1000BaseTX + 1028 010a Broadcom BCM5700 1000BaseTX 10b7 1000 3C996-T 1000BaseTX 10b7 1001 3C996B-T 1000BaseTX 10b7 1002 3C996C-T 1000BaseTX - 10b7 1003 3C997-T 1000BaseTX + 10b7 1003 3C997-T 1000BaseTX Dual Port 10b7 1004 3C996-SX 1000BaseSX - 10b7 1005 3C997-SX 1000BaseSX + 10b7 1005 3C997-SX 1000BaseSX Dual Port 10b7 1008 3C942 Gigabit LOM (31X31) 14e4 0002 NetXtreme 1000BaseSX 14e4 0003 NetXtreme 1000BaseSX 14e4 0004 NetXtreme 1000BaseTX - 14e4 1644 NetXtreme BCM5700 1000BaseTX + 14e4 1028 NetXtreme 1000BaseTX + 14e4 1644 BCM5700 1000BaseTX 1645 NetXtreme BCM5701 Gigabit Ethernet - 0e11 007c NC7770 1000BaseTX - 0e11 007d NC6770 1000BaseSX - 0e11 0085 NC7780 1000BaseTX - 1028 0121 Broadcom BCM5701 + 0e11 007c NC7770 Gigabit Server Adapter (PCI-X, 10/100/1000-T) + 0e11 007d NC6770 Gigabit Server Adapter (PCI-X, 1000-SX) + 0e11 0085 NC7780 Gigabit Server Adapter (embedded, WOL) + 0e11 0099 NC7780 Gigabit Server Adapter (embedded, WOL) + 0e11 009a NC7770 Gigabit Server Adapter (PCI-X, 10/100/1000-T) + 1028 0121 Broadcom BCM5701 1000BaseTX 10b7 1004 3C996-SX 1000BaseSX 10b7 1006 3C996B-T 1000BaseTX 10b7 1007 3C1000-T 1000BaseTX 10b7 1008 3C940-BR01 1000BaseTX - 14e4 0001 NetXtreme BCM5701 1000BaseTX - 14e4 0005 NetXtreme BCM5701 1000BaseTX - 14e4 0006 NetXtreme BCM5701 1000BaseTX - 14e4 0007 NetXtreme BCM5701 1000BaseSX - 14e4 0008 NetXtreme BCM5701 1000BaseTX - 14e4 8008 NetXtreme BCM5701 1000BaseTX + 14e4 0001 BCM5701 1000BaseTX + 14e4 0005 BCM5701 1000BaseTX + 14e4 0006 BCM5701 1000BaseTX + 14e4 0007 BCM5701 1000BaseSX + 14e4 0008 BCM5701 1000BaseTX + 14e4 8008 BCM5701 1000BaseTX 1646 NetXtreme BCM5702 Gigabit Ethernet - 14e4 8009 Broadcom BCM5702 + 0e11 00bb NC7760 1000BaseTX + 1028 0126 Broadcom BCM5702 1000BaseTX + 14e4 8009 BCM5702 1000BaseTX 1647 NetXtreme BCM5703 Gigabit Ethernet - 0e11 009a NC7770 - 0e11 0099 NC7780 - 14e4 0009 Broadcom BCM5703 - 14e4 8009 Broadcom BCM5703 + 0e11 0099 NC7780 1000BaseTX + 0e11 009a NC7770 1000BaseTX + 14e4 0009 BCM5703 1000BaseTX + 14e4 000a BCM5703 1000BaseSX + 14e4 000b BCM5703 1000BaseTX + 14e4 8009 BCM5703 1000BaseTX + 14e4 800a BCM5703 1000BaseTX 164d NetXtreme BCM5702FE Gigabit Ethernet 16a6 NetXtreme BCM5702X Gigabit Ethernet 16a7 NetXtreme BCM5703X Gigabit Ethernet + 4212 BCM v.90 56k modem 5820 BCM5820 Crypto Accelerator + 5821 BCM5821 Crypto Accelerator 14e5 Pixelfusion Ltd 14e6 SHINING Technology Inc 14e7 3CX 14e8 RAYCER Inc 14e9 GARNETS System CO Ltd -14ea PLANEX COMMUNICATIONS Inc +14ea Planex Communications, Inc + ab06 FNW-3603-TX CardBus Fast Ethernet 14eb SEIKO EPSON Corp 14ec ACQIRIS 14ed DATAKINETICS Ltd @@ -4273,14 +5073,30 @@ 14ef CARRY Computer ENG. CO Ltd 14f0 CANON RESEACH CENTRE FRANCE 14f1 Conexant + 1002 HCF 56k Modem + 1003 HCF 56k Modem + 1004 HCF 56k Modem + 1005 HCF 56k Modem + 1006 HCF 56k Modem + 1022 HCF 56k Modem + 1023 HCF 56k Modem + 1024 HCF 56k Modem + 1025 HCF 56k Modem + 1026 HCF 56k Modem + 1032 HCF 56k Modem 1033 HCF 56k Data/Fax Modem + 1033 8077 NEC 122d 4027 Dell Zeus - MDP3880-W(B) Data Fax Modem 122d 4030 Dell Mercury - MDP3880-U(B) Data Fax Modem 122d 4034 Dell Thor - MDP3880-W(U) Data Fax Modem 13e0 020d Dell Copper 13e0 020e Dell Silver + 13e0 0261 IBM 13e0 0290 Compaq Goldwing + 13e0 02a0 IBM + 13e0 02b0 IBM 13e0 02c0 Compaq Scooter + 13e0 02d0 IBM 144f 1500 IBM P85-DF (1) 144f 1501 IBM P85-DF (2) 144f 150a IBM P85-DF (3) @@ -4290,6 +5106,7 @@ 1035 HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem 10cf 1098 Fujitsu P85-DFSV 1036 HCF 56k Data/Fax/Voice/Spkp Modem + 104d 8067 HCF 56k Modem 122d 4029 MDP3880SP-W 122d 4031 MDP3880SP-U 13e0 0209 Dell Titanium @@ -4313,6 +5130,8 @@ 1435 HCF 56k Data/Fax/Voice/Spkp (w/Handset) Modem 1436 HCF 56k Data/Fax Modem 1453 HCF 56k Data/Fax Modem + 13e0 0240 IBM + 13e0 0250 IBM 144f 1502 IBM P95-DF (1) 144f 1503 IBM P95-DF (2) 1454 HCF 56k Data/Fax/Voice Modem @@ -4320,6 +5139,8 @@ 1456 HCF 56k Data/Fax/Voice/Spkp Modem 122d 4035 Dell Europa - MDP3900V-W 122d 4302 Dell MP3930V-W(C) MiniPCI + 1610 ADSL AccessRunner PCI Arbitration Device + 1611 AccessRunner PCI ADSL Interface Device 1803 HCF 56k Modem 0e11 0023 623-LAN Grizzly 0e11 0043 623-LAN Yogi @@ -4334,6 +5155,9 @@ 0e11 b195 Bear 0e11 b196 Seminole 1 0e11 b1be Seminole 2 + 1025 8013 Acer + 1033 809d NEC + 1033 80bc NEC 155d 6793 HP 155d 8850 E Machines 2014 HSF 56k Data/Fax/Voice Modem @@ -4366,6 +5190,9 @@ 2365 HSF 56k Data/Fax/Voice/Spkp (w/HS) CardBus Modem (Mob SmartDAA) 2366 HSF 56k Data/Fax/Voice/Spkp CardBus Modem (Mob SmartDAA) 2443 HSF 56k Data/Fax Modem (Mob WorldW SmartDAA) + 104d 8075 Modem + 104d 8083 Modem + 104d 8097 Modem 2444 HSF 56k Data/Fax/Voice Modem (Mob WorldW SmartDAA) 2445 HSF 56k Data/Fax/Voice/Spkp (w/HS) Modem (Mob WorldW SmartDAA) 2446 HSF 56k Data/Fax/Voice/Spkp Modem (Mob WorldW SmartDAA) @@ -4376,6 +5203,8 @@ 2f00 HSF 56k HSFi Modem 13e0 8d84 IBM HSFi V.90 13e0 8d85 Compaq Stinger + 14f1 2004 Dynalink 56PMi + 8234 RS8234 ATM SAR Controller [ServiceSAR Plus] 14f2 MOBILITY Electronics 14f3 BROADLOGIC 14f4 TOKYO Electronic Industry CO Ltd @@ -4423,6 +5252,8 @@ 1514 TFL LAN Inc 1515 Advent design 1516 MYSON Technology Inc + 0803 SURECOM EP-320X-S 100/10M Ethernet PCI Adapter + 1320 10bd SURECOM EP-320X-S 100/10M Ethernet PCI Adapter 1517 ECHOTEK Corp 1518 PEP MODULAR Computers GmbH 1519 TELEFON AKTIEBOLAGET LM Ericsson @@ -4435,9 +5266,16 @@ 151d Fujitsu Computer Products Of America 151e MATRIX Corp 151f TOPIC SEMICONDUCTOR Corp + 0000 TP560 Data/Fax/Voice 56k modem 1520 CHAPLET System Inc 1521 BELL Corp 1522 MainPine Ltd + 0100 PCI <-> IOBus Bridge + 1522 0200 RockForceDUO 2 Port V.92/V.44 Data/Fax/Voice Modem + 1522 0300 RockForceQUATRO 4 Port V.92/V.44 Data/Fax/Voice Modem + 1522 0400 RockForceDUO+ 2 Port V.92/V.44 Data/Fax/Voice Modem + 1522 0500 RockForceQUATRO+ 4 Port V.92/V.44 Data/Fax/Voice Modem + 1522 0600 RockForce+ 2 Port V.90 Data/Fax/Voice Modem 1523 MUSIC Semiconductors 1524 ENE Technology Inc 1525 IMPACT Technologies @@ -4592,6 +5430,7 @@ 15a0 Compumaster SRL 15a1 Geocast Network Systems 15a2 Catalyst Enterprises Inc + 0001 TA700 PCI Bus Analyzer/Exerciser 15a3 Italtel 15a4 X-Net OY 15a5 Toyota Macs Inc @@ -4608,6 +5447,7 @@ 15b1 Source Technology Inc 15b2 Mosaid Technologies Inc 15b3 Mellanox Technology + 5274 MT21108 InfiniBridge 15b4 CCI/TRIAD 15b5 Cimetrics Inc 15b6 Texas Memory Systems Inc @@ -4617,6 +5457,7 @@ 15ba Impacct Technology Corp 15bb Portwell Inc 15bc Agilent Technologies + 2929 E2929A PCI/PCI-X Bus Analyzer 15bd DFI Inc 15be Sola Electronics 15bf High Tech Computer Corp (HTC) @@ -4627,7 +5468,8 @@ 15c4 EVSX Inc 15c5 Procomp Informatics Ltd 15c6 Technical University of Budapest -15c7 Tateyama Dystem Laboratory Co Ltd +15c7 Tateyama System Laboratory Co Ltd + 0349 Tateyama C-PCI PLC/NC card Rev.01A 15c8 Penta Media Co Ltd 15c9 Serome Technology Inc 15ca Bitboys OY @@ -4661,6 +5503,7 @@ 15e6 Agere Inc 15e7 Get Engineering Corp 15e8 National Datacomm Corp + 0130 Wireless PCI Card 15e9 Pacific Digital Corp 15ea Tokyo Denshi Sekei K.K. 15eb Drsearch GmbH @@ -4697,10 +5540,35 @@ 1619 FarSite Communications Ltd 0400 FarSync T2P (2 port X.21/V.35/V.24) 0440 FarSync T4P (4 port X.21/V.35/V.24) +1629 Kongsberg Spacetec AS + 1003 Format synchronizer v3.0 + 2002 Fast Universal Data Output +1638 Standard Microsystems Corp [SMC] + 1100 SMC2602W EZConnect / Addtron AWA-100 +1657 Brocade Communications Systems, Inc. +165d Hsing Tech. Enterprise Co., Ltd. +1661 Worldspace Corp. 1668 Action Tec Electronics Inc -173b Altima (nee BroadCom) +16ec U.S. Robotics + 3685 Wireless Access PCI Adapter Model 022415 +16f6 VideoTele.com, Inc. +170b NetOctave Inc +170c YottaYotta Inc. +173b Altima (nee Broadcom) 03e8 AC1000 Gigabit Ethernet 03ea AC9100 Gigabit Ethernet +1743 Peppercon AG + 8139 ROL/F-100 Fast Ethernet Adapter with ROL +174b PC Partner Limited +175e Sanera Systems, Inc. +# also used by Struck Innovative Systeme for joint developments +1796 Research Centre Juelich + 0001 SIS1100 [Gigabit link] + 0002 HOTlink + 0003 Counter Timer + 0004 CAMAC Controller + 0005 PROFIBUS + 0006 AMCC HOTlink 1813 Ambient Technologies Inc 1a08 Sierra semiconductor 0000 SC15064 @@ -4726,11 +5594,12 @@ 3000 Hansol Electronics Inc. 3142 Post Impression Systems. 3388 Hint Corp + 0021 HB1-SE33 PCI-PCI Bridge 8011 VXPro II Chipset 3388 8011 VXPro II Chipset CPU to PCI Bridge 8012 VXPro II Chipset 3388 8012 VXPro II Chipset PCI to ISA Bridge - 8013 VXPro II Chipset + 8013 VXPro II IDE 3388 8013 VXPro II Chipset EIDE Controller 3411 Quantum Designs (H.K.) Inc 3513 ARCOM Control Systems Ltd @@ -4745,6 +5614,7 @@ 0007 3D Extreme 0008 GLINT Gamma G1 0009 Permedia II 2D+3D + 1040 0011 AccelStar II 3d3d 0100 AccelStar II 3D Accelerator 3d3d 0111 Permedia 3:16 3d3d 0114 Santa Ana @@ -4774,6 +5644,7 @@ 2501 ALG-2564A/25128A 4000 ALS4000 Audio Chipset 4005 4000 ALS4000 Audio Chipset + 4710 ALC200/200P 4033 Addtron Technology Co, Inc. 1360 RTL8139 Ethernet 4143 Digital Equipment Corp @@ -4795,11 +5666,31 @@ 4a14 5000 RT8029-Based Ethernet Adapter 4b10 Buslogic Inc. 4c48 LUNG HWA Electronics +4c53 SBS Technologies 4ca1 Seanix Technology Inc 4d51 MediaQ Inc. 0200 MQ-200 4d54 Microtechnica Co Ltd 4ddc ILC Data Device Corp + 0100 DD-42924I5-300 (ARINC 429 Data Bus) + 0801 BU-65570I1 MIL-STD-1553 Test and Simulation + 0802 BU-65570I2 MIL-STD-1553 Test and Simulation + 0811 BU-65572I1 MIL-STD-1553 Test and Simulation + 0812 BU-65572I2 MIL-STD-1553 Test and Simulation + 0881 BU-65570T1 MIL-STD-1553 Test and Simulation + 0882 BU-65570T2 MIL-STD-1553 Test and Simulation + 0891 BU-65572T1 MIL-STD-1553 Test and Simulation + 0892 BU-65572T2 MIL-STD-1553 Test and Simulation + 0901 BU-65565C1 MIL-STD-1553 Data Bus + 0902 BU-65565C2 MIL-STD-1553 Data Bus + 0903 BU-65565C3 MIL-STD-1553 Data Bus + 0904 BU-65565C4 MIL-STD-1553 Data Bus + 0b01 BU-65569I1 MIL-STD-1553 Data Bus + 0b02 BU-65569I2 MIL-STD-1553 Data Bus + 0b03 BU-65569I3 MIL-STD-1553 Data Bus + 0b04 BU-65569I4 MIL-STD-1553 Data Bus +5046 GemTek Technology Corporation + 1001 PCI Radio 5053 Voyetra Technologies 2010 Daytona Audio Adapter 5136 S S Technologies @@ -4843,7 +5734,7 @@ 88f3 86c968 [Vision 968 VRAM] rev 3 8900 86c755 [Trio 64V2/DX] 5333 8900 86C775 Trio64V2/DX - 8901 Trio 64V2/DX or /GX + 8901 86c775/86c785 [Trio 64V2/DX or /GX] 5333 8901 86C775 Trio64V2/DX, 86C785 Trio64V2/GX 8902 Plato/PX 8903 Trio 3D business multimedia @@ -4872,9 +5763,11 @@ 5333 8a13 Trio3D/2X 8a20 86c794 [Savage 3D] 5333 8a20 86C391 Savage3D - 8a21 86c795 [Savage 3D/MV] + 8a21 86c390 [Savage 3D/MV] 5333 8a21 86C390 Savage3D/MV 8a22 Savage 4 + 1033 8068 Savage 4 + 1033 8069 Savage 4 105d 0018 SR9 8Mb SDRAM 105d 002a SR9 Pro 16Mb SDRAM 105d 003a SR9 Pro 32Mb SDRAM @@ -4906,12 +5799,27 @@ 8a26 ProSavage KM133 8c00 ViRGE/M3 8c01 ViRGE/MX + 1179 0001 ViRGE/MX 8c02 ViRGE/MX+ 8c03 ViRGE/MX+MV 8c10 86C270-294 Savage/MX-MV 8c11 82C270-294 Savage/MX 8c12 86C270-294 Savage/IX-MV 8c13 86C270-294 Savage/IX + 8c22 SuperSavage MX/128 + 8c24 SuperSavage MX/64 + 8c26 SuperSavage MX/64C + 8c2a SuperSavage IX/128 SDR + 8c2b SuperSavage IX/128 DDR + 8c2c SuperSavage IX/64 SDR + 8c2d SuperSavage IX/64 DDR + 8c2e SuperSavage IX/C SDR + 1014 01fc ThinkPad T23 (2647-4MG) + 8c2f SuperSavage IX/C DDR +# Integrated in VIA ProSavage PN133 North Bridge + 8d01 VT8603 [ProSavage PN133] AGP4X VGA Controller (Twister) + 8d02 VT8636A [ProSavage KN133] AGP4X VGA Controller (TwisterK) + 8d04 VT8751 [ProSavageDDR P4M266] VGA Controller 9102 86C410 Savage 2000 1092 5932 Viper II Z200 1092 5934 Viper II Z200 @@ -4946,6 +5854,7 @@ 0011 PWDOG2 [PCI-Watchdog 2] 8086 Intel Corp. 0007 82379AB + 0008 Extended Express System Support Controller 0039 21145 0122 82437FX 0482 82375EB @@ -4956,31 +5865,85 @@ 04d0 82437FX [Triton FX] 0600 RAID Controller 0960 80960RP [i960 RP Microprocessor/Bridge] + 0962 80960RM [i960RM Bridge] 0964 80960RP [i960 RP Microprocessor/Bridge] 1000 82542 Gigabit Ethernet Controller - 0e11 b0df NC1632 Gigabit Ethernet Adapter - 0e11 b0e0 NC1633 Gigabit Ethernet Adapter - 0e11 b123 NC1634 Gigabit Ethernet Adapter + 0e11 b0df NC1632 Gigabit Ethernet Adapter (1000-SX) + 0e11 b0e0 NC1633 Gigabit Ethernet Adapter (1000-LX) + 0e11 b123 NC1634 Gigabit Ethernet Adapter (1000-SX) 1014 0119 Netfinity Gigabit Ethernet SX Adapter + 8086 1000 PRO/1000 Gigabit Server Adapter 1001 82543GC Gigabit Ethernet Controller + 0e11 004a NC6136 Gigabit Server Adapter + 1014 01ea Netfinity Gigabit Ethernet SX Adapter + 8086 1003 PRO/1000 F Server Adapter + 1002 Pro 100 LAN+Modem 56 Cardbus II + 8086 200e Pro 100 LAN+Modem 56 Cardbus II + 8086 2013 Pro 100 SR Mobile Combo Adapter + 8086 2017 Pro 100 S Combo Mobile Adapter 1004 82543GC Gigabit Ethernet Controller + 0e11 0049 NC7132 Gigabit Upgrade Module + 0e11 b1a4 NC7131 Gigabit Server Adapter + 1014 10f2 Gigabit Ethernet Server Adapter + 8086 1004 PRO/1000 T Server Adapter + 8086 2004 PRO/1000 T Server Adapter 1008 82544EI Gigabit Ethernet Controller + 8086 1107 PRO/1000 XT Server Adapter + 8086 2107 PRO/1000 XT Server Adapter + 8086 2110 PRO/1000 XT Server Adapter 1009 82544EI Gigabit Ethernet Controller + 8086 1109 PRO/1000 XF Server Adapter + 8086 2109 PRO/1000 XF Server Adapter 100c 82544GC Gigabit Ethernet Controller + 8086 1112 PRO/1000 T Desktop Adapter + 8086 2112 PRO/1000 T Desktop Adapter 100d 82544GC Gigabit Ethernet Controller + 100e 82540EM Gigabit Ethernet Controller + 8086 001e PRO/1000 MT Desktop Adapter + 8086 002e PRO/1000 MT Desktop Adapter + 100f 82545EM Gigabit Ethernet Controller + 8086 1001 PRO/1000 MT Server Adapter + 1010 82546EB Gigabit Ethernet Controller + 8086 1011 PRO/1000 MT Dual Port Server Adapter + 1011 82545EM Gigabit Ethernet Controller + 8086 1002 PRO/1000 MF Server Adapter + 1012 82546EB Gigabit Ethernet Controller + 8086 1012 PRO/1000 MF Dual Port Server Adapter 1029 82559 Ethernet Controller 1030 82559 InBusiness 10/100 - 1031 82801CAM (ICH3) Chipset Ethernet Controller - 1032 82801CAM (ICH3) Chipset Ethernet Controller - 1033 82801CAM (ICH3) Chipset Ethernet Controller - 1034 82801CAM (ICH3) Chipset Ethernet Controller - 1035 82801CAM (ICH3) Chipset Ethernet Controller - 1036 82801CAM (ICH3) Chipset Ethernet Controller + 1031 82801CAM (ICH3) PRO/100 VE (LOM) Ethernet Controller + 1014 0209 ThinkPad A30p (2653-64G) + 104d 80e7 Vaio PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP + 107b 5350 EtherExpress PRO/100 VE + 1179 0001 EtherExpress PRO/100 VE + 144d c000 EtherExpress PRO/100 VE + 144d c001 EtherExpress PRO/100 VE + 144d c003 EtherExpress PRO/100 VE + 1032 82801CAM (ICH3) PRO/100 VE Ethernet Controller + 1033 82801CAM (ICH3) PRO/100 VM (LOM) Ethernet Controller + 1034 82801CAM (ICH3) PRO/100 VM Ethernet Controller + 1035 82801CAM (ICH3)/82562EH (LOM) Ethernet Controller + 1036 82801CAM (ICH3) 82562EH Ethernet Controller 1037 82801CAM (ICH3) Chipset Ethernet Controller - 1038 82801CAM (ICH3) Chipset Ethernet Controller + 1038 82801CAM (ICH3) PRO/100 VM (KM) Ethernet Controller + 1039 82801BD PRO/100 VE (LOM) Ethernet Controller + 103a 82801BD PRO/100 VE (CNR) Ethernet Controller + 103b 82801BD PRO/100 VM (LOM) Ethernet Controller + 103c 82801BD PRO/100 VM (CNR) Ethernet Controller + 103d 82801BD PRO/100 VE (MOB) Ethernet Controller + 103e 82801BD PRO/100 VM (MOB) Ethernet Controller + 1059 82551QM Ethernet Controller 1130 82815 815 Chipset Host Bridge and Memory Controller Hub + 1043 8027 TUSL2-C Mainboard + 104d 80df Vaio PCG-FX403 + 1131 82815 815 Chipset AGP Bridge 1132 82815 CGC [Chipset Graphics Controller] + 1025 1016 Travelmate 612 TX + 104d 80df Vaio PCG-FX403 1161 82806AA PCI64 Hub Advanced Programmable Interrupt Controller + 8086 1161 82806AA PCI64 Hub APIC + 1200 Intel IXP1200 Network Processor + 172a 0000 AEP SSL Accelerator 1209 82559ER 1221 82092AA_0 1222 82092AA_1 @@ -4989,31 +5952,69 @@ 1226 82596 PRO/10 PCI 1227 82865 EtherExpress PRO/100A 1228 82556 EtherExpress PRO/100 Smart - 1229 82557 [Ethernet Pro 100] - 0e11 b01e NC3120 - 0e11 b01f NC3122 - 0e11 b02f NC1120 +# the revision field differentiates between them (1-3 is 82557, 4-5 is 82558, 6-8 is 82559, 9 is 82559ER) + 1229 82557/8/9 [Ethernet Pro 100] + 0e11 3001 82559 Fast Ethernet LOM with Alert on LAN* + 0e11 3002 82559 Fast Ethernet LOM with Alert on LAN* + 0e11 3003 82559 Fast Ethernet LOM with Alert on LAN* + 0e11 3004 82559 Fast Ethernet LOM with Alert on LAN* + 0e11 3005 82559 Fast Ethernet LOM with Alert on LAN* + 0e11 3006 82559 Fast Ethernet LOM with Alert on LAN* + 0e11 3007 82559 Fast Ethernet LOM with Alert on LAN* + 0e11 b01e NC3120 Fast Ethernet NIC + 0e11 b01f NC3122 Fast Ethernet NIC (dual port) + 0e11 b02f NC1120 Ethernet NIC 0e11 b04a Netelligent 10/100TX NIC with Wake on LAN - 0e11 b0c6 Embedded NC3120 with Wake on LAN - 0e11 b0c7 Embedded NC3121 - 0e11 b0d7 NC3121 with Wake on LAN - 0e11 b0dd NC3131 (82558B) - 0e11 b0de NC3132 - 0e11 b0e1 NC3133 - 0e11 b144 NC3123 (82559) + 0e11 b0c6 NC3161 Fast Ethernet NIC (embedded, WOL) + 0e11 b0c7 NC3160 Fast Ethernet NIC (embedded) + 0e11 b0d7 NC3121 Fast Ethernet NIC (WOL) + 0e11 b0dd NC3131 Fast Ethernet NIC (dual port) + 0e11 b0de NC3132 Fast Ethernet Module (dual port) + 0e11 b0e1 NC3133 Fast Ethernet Module (100-FX) + 0e11 b134 NC3163 Fast Ethernet NIC (embedded, WOL) + 0e11 b13c NC3162 Fast Ethernet NIC (embedded) + 0e11 b144 NC3123 Fast Ethernet NIC (WOL) + 0e11 b163 NC3134 Fast Ethernet NIC (dual port) + 0e11 b164 NC3135 Fast Ethernet Upgrade Module (dual port) + 0e11 b1a4 NC7131 Gigabit Server Adapter 1014 005c 82558B Ethernet Pro 10/100 + 1014 01bc 82559 Fast Ethernet LAN On Motherboard + 1014 01f1 10/100 Ethernet Server Adapter + 1014 01f2 10/100 Ethernet Server Adapter + 1014 0207 Ethernet Pro/100 S + 1014 0232 10/100 Dual Port Server Adapter 1014 105c Netfinity 10/100 + 1014 305c 10/100 EtherJet Management Adapter + 1014 405c 10/100 EtherJet Adapter with Alert on LAN + 1014 505c 10/100 EtherJet Secure Management Adapter + 1014 605c 10/100 EtherJet Secure Management Adapter + 1014 705c 10/100 Netfinity 10/100 Ethernet Security Adapter + 1014 805c 10/100 Netfinity 10/100 Ethernet Security Adapter 1033 8000 PC-9821X-B06 1033 8016 PK-UG-X006 1033 801f PK-UG-X006 - 103c 10c0 Ethernet Pro 10/100TX - 103c 10c3 Ethernet Pro 10/100TX - 103c 1200 Ethernet Pro 10/100TX + 1033 8026 PK-UG-X006 + 1033 8063 82559-based Fast Ethernet Adapter + 1033 8064 82559-based Fast Ethernet Adapter + 103c 10c0 NetServer 10/100TX + 103c 10c3 NetServer 10/100TX + 103c 10ca NetServer 10/100TX + 103c 10cb NetServer 10/100TX + 103c 10e3 NetServer 10/100TX + 103c 10e4 NetServer 10/100TX + 103c 1200 NetServer 10/100TX 10c3 1100 SmartEther100 SC1100 + 10cf 1115 8255x-based Ethernet Adapter (10/100) + 10cf 1143 8255x-based Ethernet Adapter (10/100) + 1179 0001 8255x-based Ethernet Adapter (10/100) 1179 0002 PCI FastEther LAN on Docker + 1179 0003 8255x-based Fast Ethernet 1259 2560 AT-2560 100 1259 2561 AT-2560 100 FX Ethernet Adapter 1266 0001 NE10/100 Adapter + 144d 2501 SEM-2000 MiniPCI LAN Adapter + 144d 2502 SEM-2100IL MiniPCI LAN Adapter + 1668 1100 EtherExpress PRO/100B (TX) (MiniPCI Ethernet+Modem) 8086 0001 EtherExpress PRO/100B (TX) 8086 0002 EtherExpress PRO/100B (T4) 8086 0003 EtherExpress PRO/10+ @@ -5028,14 +6029,76 @@ 8086 000c EtherExpress PRO/100+ Management Adapter 8086 000d EtherExpress PRO/100+ Alert On LAN II* Adapter 8086 000e EtherExpress PRO/100+ Management Adapter with Alert On LAN* + 8086 000f EtherExpress PRO/100 Desktop Adapter + 8086 0010 EtherExpress PRO/100 S Management Adapter + 8086 0011 EtherExpress PRO/100 S Management Adapter + 8086 0012 EtherExpress PRO/100 S Advanced Management Adapter (D) + 8086 0013 EtherExpress PRO/100 S Advanced Management Adapter (E) + 8086 0030 EtherExpress PRO/100 Management Adapter with Alert On LAN* GC + 8086 0031 EtherExpress PRO/100 Desktop Adapter + 8086 0040 EtherExpress PRO/100 S Desktop Adapter + 8086 0041 EtherExpress PRO/100 S Desktop Adapter + 8086 0042 EtherExpress PRO/100 Desktop Adapter + 8086 0050 EtherExpress PRO/100 S Desktop Adapter 8086 1009 EtherExpress PRO/100+ Server Adapter 8086 100c EtherExpress PRO/100+ Server Adapter (PILA8470B) + 8086 1012 EtherExpress PRO/100 S Server Adapter (D) + 8086 1013 EtherExpress PRO/100 S Server Adapter (E) + 8086 1015 EtherExpress PRO/100 S Dual Port Server Adapter + 8086 1017 EtherExpress PRO/100+ Dual Port Server Adapter + 8086 1030 EtherExpress PRO/100+ Management Adapter with Alert On LAN* G Server + 8086 1040 EtherExpress PRO/100 S Server Adapter + 8086 1041 EtherExpress PRO/100 S Server Adapter + 8086 1042 EtherExpress PRO/100 Server Adapter + 8086 1050 EtherExpress PRO/100 S Server Adapter + 8086 1051 EtherExpress PRO/100 Server Adapter + 8086 1052 EtherExpress PRO/100 Server Adapter 8086 10f0 EtherExpress PRO/100+ Dual Port Adapter + 8086 2009 EtherExpress PRO/100 S Mobile Adapter 8086 200d EtherExpress PRO/100 Cardbus 8086 200e EtherExpress PRO/100 LAN+V90 Cardbus Modem + 8086 200f EtherExpress PRO/100 SR Mobile Adapter + 8086 2010 EtherExpress PRO/100 S Mobile Combo Adapter + 8086 2013 EtherExpress PRO/100 SR Mobile Combo Adapter + 8086 2016 EtherExpress PRO/100 S Mobile Adapter + 8086 2017 EtherExpress PRO/100 S Combo Mobile Adapter + 8086 2018 EtherExpress PRO/100 SR Mobile Adapter + 8086 2019 EtherExpress PRO/100 SR Combo Mobile Adapter + 8086 2101 EtherExpress PRO/100 P Mobile Adapter + 8086 2102 EtherExpress PRO/100 SP Mobile Adapter + 8086 2103 EtherExpress PRO/100 SP Mobile Adapter + 8086 2104 EtherExpress PRO/100 SP Mobile Adapter + 8086 2105 EtherExpress PRO/100 SP Mobile Adapter + 8086 2106 EtherExpress PRO/100 P Mobile Adapter + 8086 2107 EtherExpress PRO/100 Network Connection + 8086 2108 EtherExpress PRO/100 Network Connection + 8086 2200 EtherExpress PRO/100 P Mobile Combo Adapter + 8086 2201 EtherExpress PRO/100 P Mobile Combo Adapter + 8086 2202 EtherExpress PRO/100 SP Mobile Combo Adapter + 8086 2203 EtherExpress PRO/100+ MiniPCI + 8086 2204 EtherExpress PRO/100+ MiniPCI + 8086 2205 EtherExpress PRO/100 SP Mobile Combo Adapter + 8086 2206 EtherExpress PRO/100 SP Mobile Combo Adapter + 8086 2207 EtherExpress PRO/100 SP Mobile Combo Adapter + 8086 2208 EtherExpress PRO/100 P Mobile Combo Adapter + 8086 2402 EtherExpress PRO/100+ MiniPCI + 8086 2407 EtherExpress PRO/100+ MiniPCI + 8086 2408 EtherExpress PRO/100+ MiniPCI + 8086 2409 EtherExpress PRO/100+ MiniPCI + 8086 240f EtherExpress PRO/100+ MiniPCI + 8086 2410 EtherExpress PRO/100+ MiniPCI + 8086 2411 EtherExpress PRO/100+ MiniPCI + 8086 2412 EtherExpress PRO/100+ MiniPCI + 8086 2413 EtherExpress PRO/100+ MiniPCI 8086 3000 82559 Fast Ethernet LAN on Motherboard 8086 3001 82559 Fast Ethernet LOM with Basic Alert on LAN* 8086 3002 82559 Fast Ethernet LOM with Alert on LAN II* + 8086 3006 EtherExpress PRO/100 S Network Connection + 8086 3007 EtherExpress PRO/100 S Network Connection + 8086 3008 EtherExpress PRO/100 Network Connection + 8086 3010 EtherExpress PRO/100 S Network Connection + 8086 3011 EtherExpress PRO/100 S Network Connection + 8086 3012 EtherExpress PRO/100 Network Connection 122d 430FX - 82437FX TSC [Triton I] 122e 82371FB PIIX ISA [Triton I] 1230 82371FB PIIX IDE [Triton I] @@ -5047,24 +6110,40 @@ 123b 82380PB 123c 82380AB 123d 683053 Programmable Interrupt Device + 123f 82466GX Integrated Hot-Plug Controller (IHPC) 1240 752 AGP 124b 82380FB 1250 430HX - 82439HX TXC [Triton II] 1360 82806AA PCI64 Hub PCI Bridge 1361 82806AA PCI64 Hub Controller (HRes) + 8086 1361 82806AA PCI64 Hub Controller (HRes) + 8086 8000 82806AA PCI64 Hub Controller (HRes) + 1460 82870P2 P64H2 Hub PCI Bridge + 1461 82870P2 P64H2 I/OxAPIC + 1462 82870P2 P64H2 Hot Plug Controller 1960 80960RP [i960RP Microprocessor] - 101e 0438 MegaRaid 438 - 101e 0466 MegaRaid 466 - 101e 0467 MegaRaid 467 + 101e 0431 MegaRAID 431 RAID Controller + 101e 0438 MegaRAID 438 Ultra2 LVD RAID Controller + 101e 0466 MegaRAID 466 Express Plus RAID Controller + 101e 0467 MegaRAID 467 Enterprise 1500 RAID Controller + 101e 0490 MegaRAID 490 Express 300 RAID Controller + 101e 0762 MegaRAID 762 Express RAID Controller 101e 09a0 PowerEdge Expandable RAID Controller 2/SC 1028 0467 PowerEdge Expandable RAID Controller 2/DC 1028 1111 PowerEdge Expandable RAID Controller 2/SC - 103c 03a2 MegaRaid - 103c 10c6 MegaRaid 438 - 103c 10c7 MegaRaid T5 - 103c 10cc MegaRaid - 1111 1111 MegaRaid 466 - 113c 03a2 MegaRaid + 103c 03a2 MegaRAID + 103c 10c6 MegaRAID 438, HP NetRAID-3Si + 103c 10c7 MegaRAID T5, Integrated HP NetRAID + 103c 10cc MegaRAID, Integrated HP NetRAID + 103c 10cd HP NetRAID-1Si + 105a 0000 SuperTrak + 105a 2168 SuperTrak Pro + 105a 5168 SuperTrak66/100 + 1111 1111 MegaRAID 466, PowerEdge Expandable RAID Controller 2/SC + 1111 1112 PowerEdge Expandable RAID Controller 2/SC + 113c 03a2 MegaRAID + 1962 80960RM [i960RM Microprocessor] + 105a 0000 SuperTrak SX6000 I2O CPU 1a21 82840 840 (Carmel) Chipset Host Bridge (Hub A) 1a23 82840 840 (Carmel) Chipset AGP Bridge 1a24 82840 840 (Carmel) Chipset PCI Bridge (Hub B) @@ -5075,6 +6154,7 @@ 2412 82801AA USB 2413 82801AA SMBus 2415 82801AA AC'97 Audio + 1028 0095 Precision Workstation 220 Integrated Digital Audio 11d4 0040 SoundMAX Integrated Digital Audio 11d4 0048 SoundMAX Integrated Digital Audio 11d4 5340 SoundMAX Integrated Digital Audio @@ -5091,16 +6171,57 @@ 2428 82801AB PCI Bridge 2440 82801BA ISA Bridge (LPC) 2442 82801BA/BAM USB (Hub #1) + 104d 80df Vaio PCG-FX403 + 147b 0507 TH7II-RAID 2443 82801BA/BAM SMBus + 1043 8027 TUSL2-C Mainboard + 104d 80df Vaio PCG-FX403 + 147b 0507 TH7II-RAID 2444 82801BA/BAM USB (Hub #2) + 104d 80df Vaio PCG-FX403 + 147b 0507 TH7II-RAID 2445 82801BA/BAM AC'97 Audio + 104d 80df Vaio PCG-FX403 + 1462 3370 STAC9721 AC + 147b 0507 TH7II-RAID 2446 82801BA/BAM AC'97 Modem + 104d 80df Vaio PCG-FX403 2448 82801BAM/CAM PCI Bridge 2449 82801BA/BAM/CA/CAM Ethernet Controller + 0e11 0012 EtherExpress PRO/100 VM + 0e11 0091 EtherExpress PRO/100 VE + 1014 01ce EtherExpress PRO/100 VE + 1014 01dc EtherExpress PRO/100 VE + 1014 01eb EtherExpress PRO/100 VE + 1014 01ec EtherExpress PRO/100 VE + 1014 0202 EtherExpress PRO/100 VE + 1014 0205 EtherExpress PRO/100 VE + 1014 0217 EtherExpress PRO/100 VE + 1014 0234 EtherExpress PRO/100 VE + 1014 023d EtherExpress PRO/100 VE + 1014 0244 EtherExpress PRO/100 VE + 1014 0245 EtherExpress PRO/100 VE + 109f 315d EtherExpress PRO/100 VE + 109f 3181 EtherExpress PRO/100 VE + 1186 7801 EtherExpress PRO/100 VE + 144d 2602 HomePNA 1M CNR + 8086 3010 EtherExpress PRO/100 VE + 8086 3011 EtherExpress PRO/100 VM + 8086 3012 82562EH based Phoneline + 8086 3013 EtherExpress PRO/100 VE + 8086 3014 EtherExpress PRO/100 VM + 8086 3015 82562EH based Phoneline + 8086 3016 EtherExpress PRO/100 P Mobile Combo + 8086 3017 EtherExpress PRO/100 P Mobile + 8086 3018 EtherExpress PRO/100 244a 82801BAM IDE U100 + 1025 1016 Travelmate 612TX + 104d 80df Vaio PCG-FX403 244b 82801BA IDE U100 + 1043 8027 TUSL2-C Mainboard + 147b 0507 TH7II-RAID 244c 82801BAM ISA Bridge (LPC) - 244e 82801BA/CA PCI Bridge + 244e 82801BA/CA/DB PCI Bridge 2450 82801E ISA Bridge (LPC) 2452 82801E USB 2453 82801E SMBus @@ -5110,28 +6231,72 @@ 245e 82801E PCI Bridge 2480 82801CA ISA Bridge (LPC) 2482 82801CA/CAM USB (Hub #1) + 1014 0220 ThinkPad T23 (2647-4MG) or A30p (2653-64G) + 104d 80e7 VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP 2483 82801CA/CAM SMBus + 1014 0220 ThinkPad T23 (2647-4MG) or A30p (2653-64G) + 104d 80e7 VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP 2484 82801CA/CAM USB (Hub #2) + 1014 0220 ThinkPad T23 (2647-4MG) or A30p (2653-64G) + 104d 80e7 VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP 2485 82801CA/CAM AC'97 Audio + 1014 0222 ThinkPad T23 (2647-4MG) + 104d 80e7 VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP 2486 82801CA/CAM AC'97 Modem + 1014 0223 ThinkPad A30p (2653-64G) + 1014 0503 ThinkPad R31 2656BBG + 104d 80e7 VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP + 134d 4c21 Dell Inspiron 2100 internal modem 2487 82801CA/CAM USB (Hub #3) + 1014 0220 ThinkPad T23 (2647-4MG) or A30p (2653-64G) + 104d 80e7 VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP 248a 82801CAM IDE U100 + 1014 0220 ThinkPad T23 (2647-4MG) or A30p (2653-64G) + 104d 80e7 VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP 248b 82801CA IDE U100 248c 82801CAM ISA Bridge (LPC) + 24c0 82801DB ISA Bridge (LPC) + 24c2 82801DB USB (Hub #1) + 24c3 82801DB SMBus + 24c4 82801DB USB (Hub #2) + 24c5 82801DB AC'97 Audio + 24c6 82801DB AC'97 Modem + 24c7 82801DB USB (Hub #3) 24cb 82801DB ICH4 IDE + 24cd 82801DB USB EHCI Controller 2500 82820 820 (Camino) Chipset Host Bridge (MCH) + 1028 0095 Precision Workstation 220 Chipset 1043 801c P3C-2000 system chipset 2501 82820 820 (Camino) Chipset Host Bridge (MCH) 1043 801c P3C-2000 system chipset 250b 82820 820 (Camino) Chipset Host Bridge - 250f 82820 820 (Camino) Chipset PCI to AGP Bridge + 250f 82820 820 (Camino) Chipset AGP Bridge 2520 82805AA MTH Memory Translator Hub 2521 82804AA MRH-S Memory Repeater Hub for SDRAM 2530 82850 850 (Tehama) Chipset Host Bridge (MCH) - 2531 82850 860 (Wombat) Chipset Host Bridge (MCH) + 147b 0507 TH7II-RAID + 2531 82860 860 (Wombat) Chipset Host Bridge (MCH) 2532 82850 850 (Tehama) Chipset AGP Bridge 2533 82860 860 (Wombat) Chipset AGP Bridge + 2534 82860 860 (Wombat) Chipset PCI Bridge + 2540 e7500 [Plumas] DRAM Controller + 2541 e7500 [Plumas] DRAM Controller Error Reporting + 2543 e7500 [Plumas] HI_B Virtual PCI Bridge (F0) + 2544 e7500 [Plumas] HI_B Virtual PCI Bridge (F1) + 2545 e7500 [Plumas] HI_C Virtual PCI Bridge (F0) + 2546 e7500 [Plumas] HI_C Virtual PCI Bridge (F1) + 2547 e7500 [Plumas] HI_D Virtual PCI Bridge (F0) + 2548 e7500 [Plumas] HI_D Virtual PCI Bridge (F1) + 2560 82845G/GL [Brookdale-G] Chipset Host Bridge + 2561 82845G/GL [Brookdale-G] Chipset AGP Bridge + 2562 82845G/GL [Brookdale-G] Chipset Integrated Graphics Device 3092 Integrated RAID + 3575 82830 830 Chipset Host Bridge + 1014 021d ThinkPad T23 (2647-4MG) or A30p (2653-64G) + 104d 80e7 VAIO PCG-GR214EP/GR214MP/GR215MP/GR314MP/GR315MP + 3576 82830 830 Chipset AGP Bridge + 3577 82830 CGC [Chipset Graphics Controller] + 3578 82830 830 Chipset Host Bridge 5200 EtherExpress PRO/100 Intelligent Server 5201 EtherExpress PRO/100 Intelligent Server 8086 0001 EtherExpress PRO/100 Server Ethernet Adapter @@ -5141,30 +6306,34 @@ 7020 82371SB PIIX3 USB [Natoma/Triton II] 7030 430VX - 82437VX TVX [Triton VX] 7100 430TX - 82439TX MTXC - 7110 82371AB PIIX4 ISA - 7111 82371AB PIIX4 IDE - 7112 82371AB PIIX4 USB - 7113 82371AB PIIX4 ACPI + 7110 82371AB/EB/MB PIIX4 ISA + 7111 82371AB/EB/MB PIIX4 IDE + 7112 82371AB/EB/MB PIIX4 USB + 7113 82371AB/EB/MB PIIX4 ACPI 7120 82810 GMCH [Graphics Memory Controller Hub] 7121 82810 CGC [Chipset Graphics Controller] - 7122 82810-DC100 GMCH [Graphics Memory Controller Hub] - 7123 82810-DC100 CGC [Chipset Graphics Controller] - 7124 82810E GMCH [Graphics Memory Controller Hub] - 7125 82810E CGC [Chipset Graphics Controller] - 7126 82810 810 Chipset Host Bridge and Memory Controller Hub + 7122 82810 DC-100 GMCH [Graphics Memory Controller Hub] + 7123 82810 DC-100 CGC [Chipset Graphics Controller] + 7124 82810E DC-133 GMCH [Graphics Memory Controller Hub] + 7125 82810E DC-133 CGC [Chipset Graphics Controller] + 7126 82810 DC-133 System and Graphics Controller + 7128 82810-M DC-100 System and Graphics Controller + 712a 82810-M DC-133 System and Graphics Controller 7180 440LX/EX - 82443LX/EX Host bridge 7181 440LX/EX - 82443LX/EX AGP bridge - 7190 440BX/ZX - 82443BX/ZX Host bridge + 7190 440BX/ZX/DX - 82443BX/ZX/DX Host bridge 0e11 0500 Armada 1750 Laptop System Chipset - 7191 440BX/ZX - 82443BX/ZX AGP bridge - 7192 440BX/ZX - 82443BX/ZX Host bridge (AGP disabled) + 1179 0001 Toshiba Tecra 8100 Laptop System Chipset + 7191 440BX/ZX/DX - 82443BX/ZX/DX AGP bridge + 7192 440BX/ZX/DX - 82443BX/ZX/DX Host bridge (AGP disabled) 0e11 0460 Armada 1700 Laptop System Chipset - 7194 82440MX I/O Controller + 7194 82440MX Host Bridge 7195 82440MX AC'97 Audio Controller 10cf 1099 QSound_SigmaTel Stac97 PCI Audio 11d4 0040 SoundMAX Integrated Digital Audio 11d4 0048 SoundMAX Integrated Digital Audio - 7198 82440MX PCI to ISA Bridge + 7196 82440MX AC'97 Modem Controller + 7198 82440MX ISA Bridge 7199 82440MX EIDE Controller 719a 82440MX USB Universal Host Controller 719b 82440MX Power Management Controller @@ -5176,7 +6345,12 @@ 7602 82372FB PIIX5 USB 7603 82372FB PIIX5 SMBus 7800 i740 + 003d 0008 Starfighter AGP + 003d 000b Starfighter AGP 1092 0100 Stealth II G460 + 10b4 201a Lightspeed 740 + 10b4 202f Lightspeed 740 + 8086 0000 Terminator 2x/i 8086 0100 Intel740 Graphics Accelerator 84c4 450KX/GX [Orion] - 82454KX/GX PCI bridge 84c5 450KX/GX [Orion] - 82453KX/GX Memory controller @@ -5184,13 +6358,20 @@ 84cb 450NX - 82454NX/84460GX PCI Expander Bridge 84e0 460GX - 84460GX System Address Controller (SAC) 84e1 460GX - 84460GX System Data Controller (SDC) - 84e2 460GX - 84460GX AGP Bridge (GXB) + 84e2 460GX - 84460GX AGP Bridge (GXB function 2) 84e3 460GX - 84460GX Memory Address Controller (MAC) 84e4 460GX - 84460GX Memory Data Controller (MDC) + 84e6 460GX - 82466GX Wide and fast PCI eXpander Bridge (WXB) + 84ea 460GX - 84460GX AGP Bridge (GXB function 1) 9621 Integrated RAID 9622 Integrated RAID 9641 Integrated RAID 96a1 Integrated RAID + b152 21152 PCI-to-PCI Bridge +# observed, and documented in Intel revision note; new mask of 1011:0026 + b154 21154 PCI-to-PCI Bridge + b555 21555 Non transparent PCI-to-PCI Bridge + e4bf 1000 CC8-1-BLUES ffff 450NX/GX [Orion] - 82453KX/GX Memory controller [BUG] 8800 Trigem Computer Inc. 2008 Video assistent component @@ -5214,12 +6395,13 @@ 5278 AIC-7852 5375 AIC-755x 5378 AIC-7850 - 5475 AIC-2930 + 5475 AIC-755x 5478 AIC-7850 5575 AVA-2930 5578 AIC-7855 + 5647 ANA-7711 TCP Offload Engine 5675 AIC-755x - 5678 AIC-7850 + 5678 AIC-7856 5775 AIC-755x 5778 AIC-7850 5800 AIC-5800 @@ -5227,37 +6409,38 @@ 5905 ANA-5910A/5930A/5940A ATM Adapter 6038 AIC-3860 6075 AIC-1480 / APA-1480 + 9004 7560 AIC-1480 / APA-1480 Cardbus 6078 AIC-7860 6178 AIC-7861 9004 7861 AHA-2940AU Single 6278 AIC-7860 6378 AIC-7860 - 6478 AIC-786 + 6478 AIC-786x 6578 AIC-786x - 6678 AIC-786 + 6678 AIC-786x 6778 AIC-786x 6915 ANA620xx/ANA69011A 9004 0008 ANA69011A/TX 10/100 9004 0009 ANA69011A/TX 10/100 9004 0010 ANA62022 2-port 10/100 9004 0018 ANA62044 4-port 10/100 + 9004 0019 ANA62044 4-port 10/100 9004 0020 ANA62022 2-port 10/100 9004 0028 ANA69011A/TX 10/100 9004 8008 ANA69011A/TX 64 bit 10/100 9004 8009 ANA69011A/TX 64 bit 10/100 9004 8010 ANA62022 2-port 64 bit 10/100 9004 8018 ANA62044 4-port 64 bit 10/100 + 9004 8019 ANA62044 4-port 64 bit 10/100 9004 8020 ANA62022 2-port 64 bit 10/100 9004 8028 ANA69011A/TX 64 bit 10/100 7078 AHA-294x / AIC-7870 - 7178 AHA-294x / AIC-7871 - 7278 AHA-3940 / AIC-7872 + 7178 AHA-2940/2940W / AIC-7871 + 7278 AHA-3940/3940W / AIC-7872 7378 AHA-3985 / AIC-7873 - 7478 AHA-2944 / AIC-7874 -# DJ: Where did the 3rd number come from? - 7578 AHA-3944 / AHA-3944W / 7875 -# DJ: Where did the 3rd number come from? - 7678 AHA-4944W/UW / 7876 + 7478 AHA-2944/2944W / AIC-7874 + 7578 AHA-3944/3944W / AIC-7875 + 7678 AHA-4944W/UW / AIC-7876 7778 AIC-787x 7810 AIC-7810 7815 AIC-7815 RAID+Memory Controller IC @@ -5278,49 +6461,71 @@ 7893 AIC-789x 7894 AIC-789x 7895 AHA-2940U/UW / AHA-39xx / AIC-7895 + 9004 7890 AHA-2940U/2940UW Dual AHA-394xAU/AUW/AUWD AIC-7895B + 9004 7891 AHA-2940U/2940UW Dual + 9004 7892 AHA-3940AU/AUW/AUWD/UWD + 9004 7894 AHA-3944AUWD 9004 7895 AHA-2940U/2940UW Dual AHA-394xAU/AUW/AUWD AIC-7895B + 9004 7896 AHA-2940U/2940UW Dual AHA-394xAU/AUW/AUWD AIC-7895B + 9004 7897 AHA-2940U/2940UW Dual AHA-394xAU/AUW/AUWD AIC-7895B 7896 AIC-789x 7897 AIC-789x 8078 AIC-7880U 9004 7880 AIC-7880P Ultra/Ultra Wide SCSI Chipset - 8178 AIC-7881U + 8178 AHA-2940U/UW/D / AIC-7881U 9004 7881 AHA-2940UW SCSI Host Adapter - 8278 AHA-3940U/UW / AIC-7882U + 8278 AHA-3940U/UW/UWD / AIC-7882U 8378 AHA-3940U/UW / AIC-7883U - 8478 AHA-294x / AIC-7884U - 8578 AHA-3944U / AHA-3944UWD / 7885 - 8678 AHA-4944UW / 7886 - 8778 AIC-788x + 8478 AHA-2944UW / AIC-7884U + 8578 AHA-3944U/UWD / AIC-7885 + 8678 AHA-4944UW / AIC-7886 + 8778 AHA-2940UW Pro / AIC-788x 9004 7887 2940UW Pro Ultra-Wide SCSI Controller - 8878 7888 + 8878 AHA-2930UW / AIC-7888 + 9004 7888 AHA-2930UW SCSI Controller 8b78 ABA-1030 ec78 AHA-4944W/UW 9005 Adaptec - 0010 AHA-2940U2/W - 0011 2930U2 + 0010 AHA-2940U2/U2W + 9005 2180 AHA-2940U2 SCSI Controller + 9005 8100 AHA-2940U2B SCSI Controller + 9005 a180 AHA-2940U2W SCSI Controller + 9005 e100 AHA-2950U2B SCSI Controller + 0011 AHA-2930U2 0013 78902 9005 0003 AAA-131U2 Array1000 1 Channel RAID Controller - 001f AHA-2940U2/W / 7890 + 001f AHA-2940U2/U2W / 7890/7891 9005 000f 2940U2W SCSI Controller 9005 a180 2940U2W SCSI Controller 0020 AIC-7890 002f AIC-7890 0030 AIC-7890 003f AIC-7890 - 0050 3940U2 - 0051 3950U2D + 0050 AHA-3940U2x/395U2x + 9005 f500 AHA-3950U2B + 0051 AHA-3950U2D + 9005 b500 AHA-3950U2D 0053 AIC-7896 SCSI Controller 9005 ffff AIC-7896 SCSI Controller mainboard implementation - 005f 7896 - 0080 7892A - 0081 7892B - 0083 7892D - 008f 7892P - 00c0 7899A - 00c1 7899B - 00c3 7899D + 005f AIC-7896U2/7897U2 + 0080 AIC-7892A U160/m + 0e11 e2a0 Compaq 64-Bit/66MHz Wide Ultra3 SCSI Adapter + 9005 62a0 29160N Ultra160 SCSI Controller + 9005 e220 29160LP Low Profile Ultra160 SCSI Controller + 9005 e2a0 29160 Ultra160 SCSI Controller + 0081 AIC-7892B U160/m + 9005 62a1 19160 Ultra160 SCSI Controller + 0083 AIC-7892D U160/m + 008f AIC-7892P U160/m + 00c0 AHA-3960D / AIC-7899A U160/m + 0e11 f620 Compaq 64-Bit/66MHz Dual Channel Wide Ultra3 SCSI Adapter + 9005 f620 AHA-3960D U160/m + 00c1 AIC-7899B U160/m + 00c3 AIC-7899D U160/m 00c5 RAID subsystem HBA - 00cf 7899P + 00cf AIC-7899P U160/m + 0285 AAC-RAID + 1028 0287 PowerEdge Expandable RAID Controller 320/DC 907f Atronics 2015 IDE-2015PL 919a Gigapixel Corp @@ -5328,6 +6533,9 @@ 6565 6565 9699 Omni Media Technology Inc 6565 6565 +9710 NetMos Technology + 9815 VScom 021H-EP2 2 port parallel adaptor + 9835 222N-2 I/O Card (2S+1P) a0a0 AOPEN Inc. a0f1 UNISYS Corporation a200 NEC Corporation @@ -5347,16 +6555,33 @@ cccc Catapult Communications d4d4 Dy4 Systems Inc 0601 PCI Mezzanine Card +d531 I+ME ACTIA GmbH d84d Exsys +dead Indigita Corporation e000 Winbond e000 W89C940 e159 Tiger Jet Network Inc. 0001 Model 300 128k 0059 0001 128k ISDN-S/T Adapter 0059 0003 128k ISDN-U Adapter + 0002 Tiger100APC ISDN chipset e4bf EKF Elektronik GmbH ea01 Eagle Technology eabb Aashima Technology B.V. +eace Endace Measurement Systems, Ltd + 3100 DAG 3.10 OC-3/OC-12 + 3200 DAG 3.2x OC-3/OC-12 + 320e DAG 3.2E Fast Ethernet + 340e DAG 3.4E Fast Ethernet + 341e DAG 3.41E Fast Ethernet + 3500 DAG 3.5 OC-3/OC-12 + 351c DAG 3.5ECM Fast Ethernet + 4100 DAG 4.10 OC-48 + 4110 DAG 4.11 OC-48 + 4220 DAG 4.2 OC-48 + 422e DAG 4.2E Dual Gigabit Ethernet +ec80 Belkin Corporation + ec00 F5D6000 ecc0 Echo Corporation edd8 ARK Logic Inc a091 1000PV [Stingray] @@ -5364,6 +6589,7 @@ a0a1 2000MT a0a9 2000MI fa57 Fast Search & Transfer ASA +febd Ultraview Corp. feda Epigram Inc fffe VMWare Inc 0710 Virtual SVGA diff -Nru a/drivers/pci/proc.c b/drivers/pci/proc.c --- a/drivers/pci/proc.c Fri Aug 16 14:34:52 2002 +++ b/drivers/pci/proc.c Fri Aug 16 14:34:52 2002 @@ -378,7 +378,7 @@ return off ? 0 : sprintf(buf,"%u\n",pci_dev->irq); } -static DEVICE_ATTR(irq,"irq",S_IRUGO,pci_show_irq,NULL); +static DEVICE_ATTR(irq,S_IRUGO,pci_show_irq,NULL); static ssize_t pci_show_resources(struct device * dev, char * buf, size_t count, loff_t off) { @@ -402,7 +402,7 @@ return (str - buf); } -static DEVICE_ATTR(resource,"resource",S_IRUGO,pci_show_resources,NULL); +static DEVICE_ATTR(resource,S_IRUGO,pci_show_resources,NULL); int pci_proc_attach_device(struct pci_dev *dev) { diff -Nru a/drivers/pnp/pnpbios_core.c b/drivers/pnp/pnpbios_core.c --- a/drivers/pnp/pnpbios_core.c Fri Aug 16 14:34:52 2002 +++ b/drivers/pnp/pnpbios_core.c Fri Aug 16 14:34:52 2002 @@ -90,7 +90,8 @@ static union pnp_bios_expansion_header * pnp_bios_hdr = NULL; /* The PnP BIOS entries in the GDT */ -#define PNP_GDT (0x0060) +#define PNP_GDT (GDT_ENTRY_PNPBIOS_BASE * 8) + #define PNP_CS32 (PNP_GDT+0x00) /* segment for calling fn */ #define PNP_CS16 (PNP_GDT+0x08) /* code segment for BIOS */ #define PNP_DS (PNP_GDT+0x10) /* data segment for BIOS */ diff -Nru a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c --- a/drivers/s390/block/dasd.c Fri Aug 16 14:34:57 2002 +++ b/drivers/s390/block/dasd.c Fri Aug 16 14:34:57 2002 @@ -290,11 +290,9 @@ gdp = dasd_gendisk_from_devindex(devmap->devindex); if (gdp == NULL) return -ENODEV; - minor = devmap->devindex % DASD_PER_MAJOR; - /* Set kdev and the device name. */ - device->kdev = mk_kdev(gdp->major, minor << DASD_PARTN_BITS); - dasd_device_name(device->name, minor, 0, gdp); + device->kdev = mk_kdev(gdp->major, gdp->first_minor); + strcpy(device->name, gdp->major_name); /* Find a discipline for the device. */ rc = dasd_find_disc(device); @@ -304,7 +302,7 @@ /* Add a proc directory and the dasd device entry to devfs. */ sprintf(buffer, "%04x", device->devinfo.devno); dir = devfs_mk_dir(dasd_devfs_handle, buffer, device); - gdp->de_arr[minor(device->kdev) >> DASD_PARTN_BITS] = dir; + gdp->de_arr[0] = dir; if (devmap->features & DASD_FEATURE_READONLY) devfs_perm = S_IFBLK | S_IRUSR; else @@ -324,19 +322,13 @@ static inline void dasd_state_known_to_new(dasd_device_t * device) { - struct gendisk *gdp; - dasd_devmap_t *devmap; - int minor; - - devmap = dasd_devmap_from_devno(device->devinfo.devno); - gdp = dasd_gendisk_from_devindex(devmap->devindex); + dasd_devmap_t *devmap = dasd_devmap_from_devno(device->devinfo.devno); + struct gendisk *gdp = dasd_gendisk_from_devindex(devmap->devindex); if (gdp == NULL) return; - minor = devmap->devindex % DASD_PER_MAJOR; - /* Remove device entry and devfs directory. */ devfs_unregister(device->devfs_entry); - devfs_unregister(gdp->de_arr[minor]); + devfs_unregister(gdp->de_arr[0]); /* Forget the discipline information. */ device->discipline = NULL; diff -Nru a/drivers/s390/block/dasd_genhd.c b/drivers/s390/block/dasd_genhd.c --- a/drivers/s390/block/dasd_genhd.c Fri Aug 16 14:34:59 2002 +++ b/drivers/s390/block/dasd_genhd.c Fri Aug 16 14:34:59 2002 @@ -32,7 +32,12 @@ struct major_info { struct list_head list; - struct gendisk gendisk; /* actually contains the major number */ + int major; + struct gendisk disks[DASD_PER_MAJOR]; + devfs_handle_t de_arr[DASD_PER_MAJOR]; + char flags[DASD_PER_MAJOR]; + char names[DASD_PER_MAJOR * 8]; + struct hd_struct part[1<gendisk.major = new_major; - mi->gendisk.major_name = "dasd"; - mi->gendisk.minor_shift = DASD_PARTN_BITS; - mi->gendisk.nr_real = DASD_PER_MAJOR; - mi->gendisk.fops = &dasd_device_operations; - mi->gendisk.de_arr = gd_de_arr; - mi->gendisk.flags = gd_flags; - mi->gendisk.part = gd_part; - - /* Initialize the gendisk arrays. */ - memset(gd_de_arr, 0, DASD_PER_MAJOR * sizeof(devfs_handle_t)); - memset(gd_flags, 0, DASD_PER_MAJOR * sizeof (char)); - memset(gd_part, 0, sizeof (struct hd_struct) << MINORBITS); + mi->major = new_major; + for (i = 0; i < DASD_PER_MAJOR; i++) { + struct gendisk *disk = mi->disks + i; + disk->major = new_major; + disk->first_minor = i << DASD_PARTN_BITS; + disk->minor_shift = DASD_PARTN_BITS; + disk->nr_real = 1; + disk->fops = &dasd_device_operations; + disk->de_arr = mi->de_arr + i; + disk->flags = mi->flags + i; + disk->part = mi->part + (i << DASD_PARTN_BITS); + } /* Setup block device pointers for the new major. */ blk_dev[new_major].queue = dasd_get_queue; - /* Insert the new major info structure into dasd_major_info list. */ spin_lock(&dasd_major_lock); + index = 0; + list_for_each(l, &dasd_major_info) + index += DASD_PER_MAJOR; + for (i = 0; i < DASD_PER_MAJOR; i++, index++) { + char *name = mi->names + i * 8; + mi->disks[i].major_name = name; + sprintf(name, "dasd"); + name += 4; + if (index > 701) + *name++ = 'a' + (((index - 702) / 676) % 26); + if (index > 25) + *name++ = 'a' + (((index - 26) / 26) % 26); + sprintf(name, "%c", 'a' + (index % 26)); + } list_add_tail(&mi->list, &dasd_major_info); spin_unlock(&dasd_major_lock); - /* Make the gendisk known. */ - add_gendisk(&mi->gendisk); return 0; /* Something failed. Do the cleanup and return rc. */ out_error: /* We rely on kfree to do the != NULL check. */ - kfree(gd_part); - kfree(gd_flags); - kfree(gd_de_arr); kfree(mi); return rc; } @@ -146,16 +151,13 @@ if (mi == NULL) return; - /* Remove gendisk information. */ - del_gendisk(&mi->gendisk); - /* Delete the major info from dasd_major_info. */ spin_lock(&dasd_major_lock); list_del(&mi->list); spin_unlock(&dasd_major_lock); /* Clear block device pointers. */ - major = mi->gendisk.major; + major = mi->major; blk_dev[major].queue = NULL; blk_clear(major); @@ -166,9 +168,6 @@ major, rc); /* Free memory. */ - kfree(mi->gendisk.part); - kfree(mi->gendisk.flags); - kfree(mi->gendisk.de_arr); kfree(mi); } @@ -189,19 +188,19 @@ /* * Return pointer to gendisk structure by kdev. */ -struct gendisk * -dasd_gendisk_from_major(int major) +static struct gendisk *dasd_gendisk_by_dev(kdev_t dev) { struct list_head *l; struct major_info *mi; struct gendisk *gdp; + int major = major(dev); spin_lock(&dasd_major_lock); gdp = NULL; list_for_each(l, &dasd_major_info) { mi = list_entry(l, struct major_info, list); - if (mi->gendisk.major == major) { - gdp = &mi->gendisk; + if (mi->major == major) { + gdp = &mi->disks[minor(dev) >> DASD_PARTN_BITS]; break; } } @@ -224,7 +223,7 @@ list_for_each(l, &dasd_major_info) { mi = list_entry(l, struct major_info, list); if (devindex < DASD_PER_MAJOR) { - gdp = &mi->gendisk; + gdp = &mi->disks[devindex]; break; } devindex -= DASD_PER_MAJOR; @@ -247,7 +246,7 @@ devindex = 0; list_for_each(l, &dasd_major_info) { mi = list_entry(l, struct major_info, list); - if (mi->gendisk.major == major) { + if (mi->major == major) { rc = devindex; break; } @@ -257,62 +256,19 @@ return rc; } - -/* - * This one is needed for naming 18000+ possible dasd devices. - * dasda - dasdz : 26 devices - * dasdaa - dasdzz : 676 devices, added up = 702 - * dasdaaa - dasdzzz : 17576 devices, added up = 18278 - * This function is called from the partition detection code (see disk_name) - * via the genhd_dasd_name hook. As mentioned in partition/check.c this - * is ugly... - */ -int -dasd_device_name(char *str, int index, int partition, struct gendisk *hd) -{ - struct list_head *l; - int len, found; - - /* Check if this is on of our gendisk structures. */ - found = 0; - spin_lock(&dasd_major_lock); - list_for_each(l, &dasd_major_info) { - struct major_info *mi; - mi = list_entry(l, struct major_info, list); - if (&mi->gendisk == hd) { - found = 1; - break; - } - index += DASD_PER_MAJOR; - } - spin_unlock(&dasd_major_lock); - if (!found) - /* Not one of our structures. Can't be a dasd. */ - return -EINVAL; - len = sprintf(str, "dasd"); - if (index > 25) { - if (index > 701) - len += sprintf(str + len, "%c", - 'a' + (((index - 702) / 676) % 26)); - len += sprintf(str + len, "%c", - 'a' + (((index - 26) / 26) % 26)); - } - len += sprintf(str + len, "%c", 'a' + (index % 26)); - - if (partition > DASD_PARTN_MASK) - return -EINVAL; - if (partition) - len += sprintf(str + len, "%d", partition); - return 0; -} - /* * Register disk to genhd. This will trigger a partition detection. */ void dasd_setup_partitions(dasd_device_t * device) { - grok_partitions(device->kdev, device->blocks << device->s2b_shift); + struct gendisk *disk = dasd_gendisk_by_dev(device->kdev); + if (disk == NULL) + return; + add_gendisk(disk); + register_disk(disk, mk_kdev(disk->major, disk->first_minor), + 1<minor_shift, disk->fops, + device->blocks << device->s2b_shift); } /* @@ -322,11 +278,10 @@ void dasd_destroy_partitions(dasd_device_t * device) { - struct gendisk *gdp; + struct gendisk *disk = dasd_gendisk_by_dev(device->kdev); int minor, i; - gdp = dasd_gendisk_from_major(major(device->kdev)); - if (gdp == NULL) + if (disk == NULL) return; wipe_partitions(device->kdev); @@ -336,13 +291,10 @@ * but the 1 as third parameter makes it do an unregister... * FIXME: there must be a better way to get rid of the devfs entries */ - devfs_register_partitions(gdp, minor(device->kdev), 1); + devfs_register_partitions(disk, minor(device->kdev), 1); + del_gendisk(disk); } -extern int (*genhd_dasd_name)(char *, int, int, struct gendisk *); -extern int (*genhd_dasd_ioctl) (struct inode *inp, struct file *filp, - unsigned int no, unsigned long data); - int dasd_gendisk_init(void) { @@ -350,25 +302,17 @@ /* Register to static dasd major 94 */ rc = dasd_register_major(DASD_MAJOR); - if (rc != 0) { + if (rc != 0) MESSAGE(KERN_WARNING, "Couldn't register successfully to " "major no %d", DASD_MAJOR); - return rc; - } - genhd_dasd_name = dasd_device_name; - genhd_dasd_ioctl = dasd_ioctl; - return 0; - + return rc; } void dasd_gendisk_exit(void) { struct list_head *l, *n; - - genhd_dasd_ioctl = NULL; - genhd_dasd_name = NULL; spin_lock(&dasd_major_lock); list_for_each_safe(l, n, &dasd_major_info) dasd_unregister_major(list_entry(l, struct major_info, list)); diff -Nru a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h --- a/drivers/s390/block/dasd_int.h Fri Aug 16 14:34:59 2002 +++ b/drivers/s390/block/dasd_int.h Fri Aug 16 14:34:59 2002 @@ -480,9 +480,7 @@ void dasd_gendisk_exit(void); int dasd_gendisk_new_major(void); int dasd_gendisk_major_index(int); -struct gendisk *dasd_gendisk_from_major(int); struct gendisk *dasd_gendisk_from_devindex(int); -int dasd_device_name(char *, int, int, struct gendisk *); void dasd_setup_partitions(dasd_device_t *); void dasd_destroy_partitions(dasd_device_t *); diff -Nru a/drivers/s390/block/dasd_proc.c b/drivers/s390/block/dasd_proc.c --- a/drivers/s390/block/dasd_proc.c Fri Aug 16 14:34:54 2002 +++ b/drivers/s390/block/dasd_proc.c Fri Aug 16 14:34:54 2002 @@ -173,12 +173,7 @@ minor = devmap->devindex % DASD_PER_MAJOR; len += sprintf(str + len, " at (%3d:%3d)", gdp->major, minor); /* Print device name. */ - if (device == NULL) { - dasd_device_name(buffer, minor, 0, gdp); - substr = buffer; - } else - substr = device->name; - len += sprintf(str + len, " is %-7s", substr); + len += sprintf(str + len, " is %-7s", gdp->major_name); /* Print devices features. */ substr = (devmap->features & DASD_FEATURE_READONLY) ? "(ro)" : " "; len += sprintf(str + len, "%4s: ", substr); diff -Nru a/drivers/sbus/char/jsflash.c b/drivers/sbus/char/jsflash.c --- a/drivers/sbus/char/jsflash.c Fri Aug 16 14:34:57 2002 +++ b/drivers/sbus/char/jsflash.c Fri Aug 16 14:34:57 2002 @@ -674,10 +674,9 @@ return 0; } -#ifdef MODULE MODULE_LICENSE("GPL"); -int init_module(void) { +static int __init jsflash_init_module(void) { int rc; if ((rc = jsflash_init()) == 0) { @@ -687,7 +686,7 @@ return rc; } -void cleanup_module(void) { +static void __exit jsflash_cleanup_module(void) { /* for (all probed units) { } */ if (jsf0.busy) @@ -700,4 +699,6 @@ printk("jsfd: cleanup_module failed\n"); blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); } -#endif + +module_init(jsflash_init_module); +module_exit(jsflash_cleanup_module); diff -Nru a/drivers/sbus/sbus.c b/drivers/sbus/sbus.c --- a/drivers/sbus/sbus.c Fri Aug 16 14:34:56 2002 +++ b/drivers/sbus/sbus.c Fri Aug 16 14:34:56 2002 @@ -16,14 +16,15 @@ #include #include #include -#ifdef CONFIG_SPARC32 -#include /* pcic_present */ -#endif struct sbus_bus *sbus_root = NULL; static struct linux_prom_irqs irqs[PROMINTR_MAX] __initdata = { { 0 } }; +#ifdef CONFIG_PCI +extern int pcic_present(void); +#endif + /* Perhaps when I figure out more about the iommu we'll put a * device registration routine here that probe_sbus() calls to * setup the iommu for each Sbus. @@ -336,17 +337,10 @@ (nd = prom_getchild(iommund)) == 0 || (nd = prom_searchsiblings(nd, "sbus")) == 0) { #ifdef CONFIG_PCI -#ifdef CONFIG_SPARC32 if (!pcic_present()) { prom_printf("Neither SBUS nor PCI found.\n"); prom_halt(); } -#else - if (!pcibios_present()) { - prom_printf("Neither SBUS nor PCI found.\n"); - prom_halt(); - } -#endif return 0; #else /* No reason to run further - the data access trap will occur. */ diff -Nru a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c --- a/drivers/scsi/53c700.c Fri Aug 16 14:34:54 2002 +++ b/drivers/scsi/53c700.c Fri Aug 16 14:34:54 2002 @@ -1360,6 +1360,8 @@ } +/* The queue lock with interrupts disabled must be held on entry to + * this function */ STATIC int NCR_700_start_command(Scsi_Cmnd *SCp) { @@ -1367,17 +1369,13 @@ (struct NCR_700_command_slot *)SCp->host_scribble; struct NCR_700_Host_Parameters *hostdata = (struct NCR_700_Host_Parameters *)SCp->host->hostdata[0]; - unsigned long flags; __u16 count = 1; /* for IDENTIFY message */ - save_flags(flags); - cli(); if(hostdata->state != NCR_700_HOST_FREE) { /* keep this inside the lock to close the race window where * the running command finishes on another CPU while we don't * change the state to queued on this one */ slot->state = NCR_700_SLOT_QUEUED; - restore_flags(flags); DEBUG(("scsi%d: host busy, queueing command %p, slot %p\n", SCp->host->host_no, slot->cmnd, slot)); @@ -1443,12 +1441,6 @@ SCp->host, SXFER_REG); NCR_700_writel(slot->temp, SCp->host, TEMP_REG); NCR_700_writel(slot->resume_offset, SCp->host, DSP_REG); - - /* allow interrupts here so that if we're selected we can take - * a selection interrupt. The script start may not be - * effective in this case, but the selection interrupt will - * save our command in that case */ - restore_flags(flags); return 1; } diff -Nru a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c --- a/drivers/scsi/aic7xxx_old.c Fri Aug 16 14:34:56 2002 +++ b/drivers/scsi/aic7xxx_old.c Fri Aug 16 14:34:56 2002 @@ -5077,7 +5077,6 @@ } else { - sti(); panic("aic7xxx: AWAITING_MSG for an SCB that does " "not have a waiting message.\n"); } @@ -6933,7 +6932,6 @@ #endif if (errno & (SQPARERR | ILLOPCODE | ILLSADDR)) { - sti(); panic("aic7xxx: unrecoverable BRKADRINT.\n"); } if (errno & ILLHADDR) diff -Nru a/drivers/scsi/cpqfc.Readme b/drivers/scsi/cpqfc.Readme --- a/drivers/scsi/cpqfc.Readme Fri Aug 16 14:35:01 2002 +++ b/drivers/scsi/cpqfc.Readme Fri Aug 16 14:35:01 2002 @@ -7,10 +7,17 @@ SEST size 512 Exchanges (simultaneous I/Os) limited by module kmalloc() max of 128k bytes contiguous. +Ver 2.5.3 Aug 01, 2002 + * fix the passthru ioctl to handle the Scsi_Cmnd->request being a pointer +Ver 2.5.1 Jul 30, 2002 + * fix ioctl to pay attention to the specified LUN. Ver 2.5.0 Nov 29, 2001 * eliminated io_request_lock. This change makes the driver specific to the 2.5.x kernels. * silenced excessively noisy printks. + +Ver 2.1.2 July 23, 2002 + * initialize DumCmnd->lun in cpqfcTS_ioctl (used in fcFindLoggedInPorts as LUN index) Ver 2.1.1 Oct 18, 2001 * reinitialize Cmnd->SCp.sent_command (used to identify commands as diff -Nru a/drivers/scsi/cpqfcTSinit.c b/drivers/scsi/cpqfcTSinit.c --- a/drivers/scsi/cpqfcTSinit.c Fri Aug 16 14:34:52 2002 +++ b/drivers/scsi/cpqfcTSinit.c Fri Aug 16 14:34:52 2002 @@ -68,7 +68,7 @@ /* Embedded module documentation macros - see module.h */ MODULE_AUTHOR("Compaq Computer Corporation"); -MODULE_DESCRIPTION("Driver for Compaq 64-bit/66Mhz PCI Fibre Channel HBA v. 2.1.1"); +MODULE_DESCRIPTION("Driver for Compaq 64-bit/66Mhz PCI Fibre Channel HBA v. 2.5.3"); MODULE_LICENSE("GPL"); int cpqfcTS_TargetDeviceReset( Scsi_Device *ScsiDev, unsigned int reset_flags); @@ -105,15 +105,16 @@ # define CPQFC_WAIT_FOR_COMPLETION(x) down(x) #endif +static int cpqfc_alloc_private_data_pool(CPQFCHBA *hba); + /* local function to load our per-HBA (local) data for chip registers, FC link state, all FC exchanges, etc. We allocate space and compute address offsets for the most frequently accessed addresses; others (like World Wide Name) are not necessary. - */ -static void Cpqfc_initHBAdata( CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev ) +static void Cpqfc_initHBAdata(CPQFCHBA *cpqfcHBAdata, struct pci_dev *PciDev ) { cpqfcHBAdata->PciDev = PciDev; // copy PCI info ptr @@ -226,8 +227,11 @@ cpqfcHBAdata->fcChip.ReadWriteWWN = CpqTsReadWriteWWN; cpqfcHBAdata->fcChip.ReadWriteNVRAM = CpqTsReadWriteNVRAM; - - + if (cpqfc_alloc_private_data_pool(cpqfcHBAdata) != 0) { + printk(KERN_WARNING + "cpqfc: unable to allocate pool for passthru ioctls. " + "Passthru ioctls disabled.\n"); + } } @@ -483,6 +487,75 @@ } +static int cpqfc_alloc_private_data_pool(CPQFCHBA *hba) +{ + hba->private_data_bits = NULL; + hba->private_data_pool = NULL; + hba->private_data_bits = + kmalloc(((CPQFC_MAX_PASSTHRU_CMDS+BITS_PER_LONG-1) / + BITS_PER_LONG)*sizeof(unsigned long), + GFP_KERNEL); + if (hba->private_data_bits == NULL) + return -1; + memset(hba->private_data_bits, 0, + ((CPQFC_MAX_PASSTHRU_CMDS+BITS_PER_LONG-1) / + BITS_PER_LONG)*sizeof(unsigned long)); + hba->private_data_pool = kmalloc(sizeof(cpqfc_passthru_private_t) * + CPQFC_MAX_PASSTHRU_CMDS, GFP_KERNEL); + if (hba->private_data_pool == NULL) { + kfree(hba->private_data_bits); + hba->private_data_bits = NULL; + return -1; + } + return 0; +} + +static void cpqfc_free_private_data_pool(CPQFCHBA *hba) +{ + kfree(hba->private_data_bits); + kfree(hba->private_data_pool); +} + +int is_private_data_of_cpqfc(CPQFCHBA *hba, void *pointer) +{ + /* Is pointer within our private data pool? + We use Scsi_Request->upper_private_data (normally + reserved for upper layer drivers, e.g. the sg driver) + We check to see if the pointer is ours by looking at + its address. Is this ok? Hmm, it occurs to me that + a user app might do something bad by using sg to send + a cpqfc passthrough ioctl with upper_data_private + forged to be somewhere in our pool..., though they'd + normally have to be root already to do this. */ + + return (pointer != NULL && + pointer >= (void *) hba->private_data_pool && + pointer < (void *) hba->private_data_pool + + sizeof(*hba->private_data_pool) * + CPQFC_MAX_PASSTHRU_CMDS); +} + +cpqfc_passthru_private_t *cpqfc_alloc_private_data(CPQFCHBA *hba) +{ + int i; + + do { + i = find_first_zero_bit(hba->private_data_bits, + CPQFC_MAX_PASSTHRU_CMDS); + if (i == CPQFC_MAX_PASSTHRU_CMDS) + return NULL; + } while ( test_and_set_bit(i & (BITS_PER_LONG - 1), + hba->private_data_bits+(i/BITS_PER_LONG)) != 0); + return &hba->private_data_pool[i]; +} + +void cpqfc_free_private_data(CPQFCHBA *hba, cpqfc_passthru_private_t *data) +{ + int i; + i = data - hba->private_data_pool; + clear_bit(i&(BITS_PER_LONG-1), + hba->private_data_bits+(i/BITS_PER_LONG)); +} int cpqfcTS_ioctl( Scsi_Device *ScsiDev, int Cmnd, void *arg) { @@ -490,35 +563,19 @@ struct Scsi_Host *HostAdapter = ScsiDev->host; CPQFCHBA *cpqfcHBAdata = (CPQFCHBA *)HostAdapter->hostdata; PTACHYON fcChip = &cpqfcHBAdata->fcChip; - PFC_LOGGEDIN_PORT pLoggedInPort; + PFC_LOGGEDIN_PORT pLoggedInPort = NULL; Scsi_Cmnd DumCmnd; int i, j; VENDOR_IOCTL_REQ ioc; cpqfc_passthru_t *vendor_cmd; Scsi_Device *SDpnt; - Scsi_Cmnd *ScsiPassThruCmnd; + Scsi_Request *ScsiPassThruReq; + cpqfc_passthru_private_t *privatedata; ENTER("cpqfcTS_ioctl "); - - // can we find an FC device mapping to this SCSI target? - DumCmnd.channel = ScsiDev->channel; // For searching - DumCmnd.target = ScsiDev->id; - pLoggedInPort = fcFindLoggedInPort( fcChip, - &DumCmnd, // search Scsi Nexus - 0, // DON'T search linked list for FC port id - NULL, // DON'T search linked list for FC WWN - NULL); // DON'T care about end of list - - if( pLoggedInPort == NULL ) // not found! - { - result = -ENXIO; - } - - else // we know what FC device to operate on... - { - // printk("ioctl CMND %d", Cmnd); - switch (Cmnd) - { + + // printk("ioctl CMND %d", Cmnd); + switch (Cmnd) { // Passthrough provides a mechanism to bypass the RAID // or other controller and talk directly to the devices // (e.g. physical disk drive) @@ -527,6 +584,10 @@ case CPQFCTS_SCSI_PASSTHRU: { void *buf = NULL; // for kernel space buffer for user data + + /* Check that our pool got allocated ok. */ + if (cpqfcHBAdata->private_data_pool == NULL) + return -ENOMEM; if( !arg) return -EINVAL; @@ -549,83 +610,70 @@ if( !buf) return -ENOMEM; } + // Now build a Scsi_Request to pass down... + ScsiPassThruReq = scsi_allocate_request(ScsiDev); + if (ScsiPassThruReq == NULL) { + kfree(buf); + return -ENOMEM; + } + ScsiPassThruReq->upper_private_data = + cpqfc_alloc_private_data(cpqfcHBAdata); + if (ScsiPassThruReq->upper_private_data == NULL) { + kfree(buf); + scsi_release_request(ScsiPassThruReq); // "de-allocate" + return -ENOMEM; + } - // Now build a SCSI_CMND to pass down... - // This function allocates and sets Scsi_Cmnd ptrs such as - // ->channel, ->target, ->host - ScsiPassThruCmnd = scsi_allocate_device(ScsiDev, 1, 1); - - // Need data from user? - // make sure caller's buffer is in kernel space. - if( (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) && - vendor_cmd->len) - if( copy_from_user( buf, vendor_cmd->bufp, vendor_cmd->len)) - return( -EFAULT); + if (vendor_cmd->rw_flag == VENDOR_WRITE_OPCODE) { + if (vendor_cmd->len) { // Need data from user? + if (copy_from_user(buf, vendor_cmd->bufp, + vendor_cmd->len)) { + kfree(buf); + cpqfc_free_private_data(cpqfcHBAdata, + ScsiPassThruReq->upper_private_data); + scsi_release_request(ScsiPassThruReq); + return( -EFAULT); + } + } + ScsiPassThruReq->sr_data_direction = SCSI_DATA_WRITE; + } else if (vendor_cmd->rw_flag == VENDOR_READ_OPCODE) { + ScsiPassThruReq->sr_data_direction = SCSI_DATA_READ; + } else + // maybe this means a bug in the user app + ScsiPassThruReq->sr_data_direction = SCSI_DATA_NONE; - // copy the CDB (if/when MAX_COMMAND_SIZE is 16, remove copy below) - memcpy( &ScsiPassThruCmnd->cmnd[0], - &vendor_cmd->cdb[0], - MAX_COMMAND_SIZE); - // we want to copy all 16 bytes into the FCP-SCSI CDB, - // although the actual passthru only uses up to the - // first 12. - - ScsiPassThruCmnd->cmd_len = 16; // sizeof FCP-SCSI CDB + ScsiPassThruReq->sr_cmd_len = 0; // set correctly by scsi_do_req() + ScsiPassThruReq->sr_sense_buffer[0] = 0; + ScsiPassThruReq->sr_sense_buffer[2] = 0; - // Unfortunately, the SCSI command cmnd[] field has only - // 12 bytes. Ideally the MAX_COMMAND_SIZE should be increased - // to 16 for newer Fibre Channel and SCSI-3 larger CDBs. - // However, to avoid a mandatory kernel rebuild, we use the SCp - // spare field to store the extra 4 bytes ( ugly :-( - - if( MAX_COMMAND_SIZE < 16) - { - memcpy( &ScsiPassThruCmnd->SCp.buffers_residual, - &vendor_cmd->cdb[12], 4); - } - - - ScsiPassThruCmnd->SCp.sent_command = 1; // PASSTHRU! - // suppress LUN masking - // and VSA logic - - // Use spare fields to copy FCP-SCSI LUN address info... - ScsiPassThruCmnd->SCp.phase = vendor_cmd->bus; - ScsiPassThruCmnd->SCp.have_data_in = vendor_cmd->pdrive; - - // We copy the scheme used by scsi.c to submit commands + // We copy the scheme used by sd.c:spinup_disk() to submit commands // to our own HBA. We do this in order to stall the // thread calling the IOCTL until it completes, and use // the same "_quecommand" function for synchronizing // FC Link events with our "worker thread". - { - CPQFC_DECLARE_COMPLETION(wait); - ScsiPassThruCmnd->request->CPQFC_WAITING = &wait; - // eventually gets us to our own _quecommand routine - scsi_do_cmd( ScsiPassThruCmnd, &vendor_cmd->cdb[0], - buf, - vendor_cmd->len, - my_ioctl_done, - 10*HZ, 1);// timeout,retries - // Other I/Os can now resume; we wait for our ioctl - // command to complete - CPQFC_WAIT_FOR_COMPLETION(&wait); - ScsiPassThruCmnd->request->CPQFC_WAITING = NULL; - } + privatedata = ScsiPassThruReq->upper_private_data; + privatedata->bus = vendor_cmd->bus; + privatedata->pdrive = vendor_cmd->pdrive; - result = ScsiPassThruCmnd->result; + // eventually gets us to our own _quecommand routine + scsi_wait_req(ScsiPassThruReq, + &vendor_cmd->cdb[0], buf, vendor_cmd->len, + 10*HZ, // timeout + 1); // retries + result = ScsiPassThruReq->sr_result; // copy any sense data back to caller if( result != 0 ) { memcpy( vendor_cmd->sense_data, // see struct def - size=40 - ScsiPassThruCmnd->sense_buffer, - sizeof(ScsiPassThruCmnd->sense_buffer)); + ScsiPassThruReq->sr_sense_buffer, + sizeof(ScsiPassThruReq->sr_sense_buffer)); } - SDpnt = ScsiPassThruCmnd->device; - scsi_release_command(ScsiPassThruCmnd); // "de-allocate" - ScsiPassThruCmnd = NULL; + SDpnt = ScsiPassThruReq->sr_device; + /* upper_private_data is already freed in call_scsi_done() */ + scsi_release_request(ScsiPassThruReq); // "de-allocate" + ScsiPassThruReq = NULL; // if (!SDpnt->was_reset && SDpnt->scsi_request_fn) // (*SDpnt->scsi_request_fn)(); @@ -679,10 +727,21 @@ case CPQFC_IOCTL_FC_TARGET_ADDRESS: - result = - verify_area(VERIFY_WRITE, arg, sizeof(Scsi_FCTargAddress)); - if (result) - break; + // can we find an FC device mapping to this SCSI target? + DumCmnd.channel = ScsiDev->channel; // For searching + DumCmnd.target = ScsiDev->id; + DumCmnd.lun = ScsiDev->lun; + pLoggedInPort = fcFindLoggedInPort( fcChip, + &DumCmnd, // search Scsi Nexus + 0, // DON'T search linked list for FC port id + NULL, // DON'T search linked list for FC WWN + NULL); // DON'T care about end of list + if (pLoggedInPort == NULL) { + result = -ENXIO; + break; + } + result = verify_area(VERIFY_WRITE, arg, sizeof(Scsi_FCTargAddress)); + if (result) break; put_user(pLoggedInPort->port_id, &((Scsi_FCTargAddress *) arg)->host_port_id); @@ -709,7 +768,6 @@ result = -EINVAL; break; } - } LEAVE("cpqfcTS_ioctl"); return result; @@ -747,6 +805,7 @@ } + cpqfc_free_private_data_pool(cpqfcHBAdata); // free Linux resources DEBUG_PCI( printk(" cpqfcTS: freeing resources...\n")); free_irq( HostAdapter->irq, HostAdapter); @@ -1527,6 +1586,12 @@ Scsi_Cmnd * SCpnt; Scsi_Device * SDpnt; +// FIXME, cpqfcTS_TargetDeviceReset needs to be fixed +// similarly to how the passthrough ioctl was fixed +// around the 2.5.30 kernel. Scsi_Cmnd replaced with +// Scsi_Request, etc. +// For now, so people don't fall into a hole... +return -ENOTSUPP; // printk(" ENTERING cpqfcTS_TargetDeviceReset() - flag=%d \n",reset_flags); @@ -1540,9 +1605,10 @@ SCpnt = scsi_allocate_device(ScsiDev, 1, 0); { CPQFC_DECLARE_COMPLETION(wait); - + SCpnt->SCp.buffers_residual = FCP_TARGET_RESET; + // FIXME: this would panic, SCpnt->request would be NULL. SCpnt->request->CPQFC_WAITING = &wait; scsi_do_cmd(SCpnt, scsi_cdb, NULL, 0, my_ioctl_done, timeout, retries); CPQFC_WAIT_FOR_COMPLETION(&wait); @@ -1701,6 +1767,18 @@ UCHAR IntStat; printk(" cpqfcTS adapter PCI error detected\n"); IntStat = readb( cpqfcHBA->fcChip.Registers.INTSTAT.address); + printk("cpqfc: ISR = 0x%02x\n", IntStat); + if (IntStat & 0x1) { + __u16 pcistat; + /* read the pci status register */ + pci_read_config_word(cpqfcHBA->PciDev, 0x06, &pcistat); + printk("PCI status register is 0x%04x\n", pcistat); + if (pcistat & 0x8000) printk("Parity Error Detected.\n"); + if (pcistat & 0x4000) printk("Signalled System Error\n"); + if (pcistat & 0x2000) printk("Received Master Abort\n"); + if (pcistat & 0x1000) printk("Received Target Abort\n"); + if (pcistat & 0x0800) printk("Signalled Target Abort\n"); + } if (IntStat & 0x4) printk("(INT)\n"); if (IntStat & 0x8) printk("CRS: PCI master address crossed 46 bit bouandary\n"); diff -Nru a/drivers/scsi/cpqfcTSstructs.h b/drivers/scsi/cpqfcTSstructs.h --- a/drivers/scsi/cpqfcTSstructs.h Fri Aug 16 14:34:57 2002 +++ b/drivers/scsi/cpqfcTSstructs.h Fri Aug 16 14:34:57 2002 @@ -33,7 +33,7 @@ // don't forget to also change MODULE_DESCRIPTION in cpqfcTSinit.c #define VER_MAJOR 2 #define VER_MINOR 5 -#define VER_SUBMINOR 0 +#define VER_SUBMINOR 3 // Macros for kernel (esp. SMP) tracing using a PCI analyzer // (e.g. x86). @@ -907,9 +907,17 @@ } FC_SCSI_QUE, *PFC_SCSI_QUE; +typedef struct { + /* This is tacked on to a Scsi_Request in upper_private_data + for pasthrough ioctls, as a place to hold data that can't + be stashed anywhere else in the Scsi_Request. We differentiate + this from _real_ upper_private_data by checking if the virt addr + is within our special pool. */ + ushort bus; + ushort pdrive; +} cpqfc_passthru_private_t; - - +#define CPQFC_MAX_PASSTHRU_CMDS 100 #define DYNAMIC_ALLOCATIONS 4 // Tachyon aligned allocations: ERQ,IMQ,SFQ,SEST @@ -949,6 +957,8 @@ PFC_LINK_QUE fcLQ; // the WorkerThread operates on this spinlock_t hba_spinlock; // held/released by WorkerThread + cpqfc_passthru_private_t *private_data_pool; + unsigned long *private_data_bits; } CPQFCHBA; @@ -1404,6 +1414,7 @@ __u32 uba:13; /* upper bus address bits 18-31 */ __u32 lba; /* lower bus address bits 0-31 */ }; + // J. McCarty's LINK.H // diff -Nru a/drivers/scsi/cpqfcTSworker.c b/drivers/scsi/cpqfcTSworker.c --- a/drivers/scsi/cpqfcTSworker.c Fri Aug 16 14:34:59 2002 +++ b/drivers/scsi/cpqfcTSworker.c Fri Aug 16 14:34:59 2002 @@ -2887,15 +2887,22 @@ Done: } +extern int is_private_data_of_cpqfc(CPQFCHBA *hba, void * pointer); +extern void cpqfc_free_private_data(CPQFCHBA *hba, cpqfc_passthru_private_t *data); + static void call_scsi_done(Scsi_Cmnd *Cmnd) { - // We have to reinitialize sent_command here, so the scsi-mid - // layer won't re-use the scsi command leaving it set incorrectly. - // (incorrectly for our purposes...it's normally unused.) - - if (Cmnd->SCp.sent_command != 0) { // was it a passthru? - Cmnd->SCp.sent_command = 0; + CPQFCHBA *hba; + hba = (CPQFCHBA *) Cmnd->host->hostdata; + // Was this command a cpqfc passthru ioctl ? + if (Cmnd->sc_request != NULL && Cmnd->host != NULL && + Cmnd->host->hostdata != NULL && + is_private_data_of_cpqfc((CPQFCHBA *) Cmnd->host->hostdata, + Cmnd->sc_request->upper_private_data)) { + cpqfc_free_private_data(hba, + Cmnd->sc_request->upper_private_data); + Cmnd->sc_request->upper_private_data = NULL; Cmnd->result &= 0xff00ffff; Cmnd->result |= (DID_PASSTHROUGH << 16); // prevents retry } @@ -3293,6 +3300,7 @@ } #endif +extern cpqfc_passthru_private_t *cpqfc_private(Scsi_Request *sr); // Search the singly (forward) linked list "fcPorts" looking for // either the SCSI target (if != -1), port_id (if not NULL), @@ -3366,8 +3374,18 @@ { // For "passthru" modes, the IOCTL caller is responsible // for setting the FCP-LUN addressing - if( !Cmnd->SCp.sent_command ) // NOT passthru? - { + if (Cmnd->sc_request != NULL && Cmnd->host != NULL && + Cmnd->host->hostdata != NULL && + is_private_data_of_cpqfc((CPQFCHBA *) Cmnd->host->hostdata, + Cmnd->sc_request->upper_private_data)) { + /* This is a passthru... */ + cpqfc_passthru_private_t *pd; + pd = Cmnd->sc_request->upper_private_data; + Cmnd->SCp.phase = pd->bus; + // Cmnd->SCp.have_data_in = pd->pdrive; + Cmnd->SCp.have_data_in = Cmnd->lun; + } else { + /* This is not a passthru... */ // set the FCP-LUN addressing type Cmnd->SCp.phase = pLoggedInPort->ScsiNexus.VolumeSetAddressing; @@ -3380,6 +3398,8 @@ // Report Luns command if( pLoggedInPort->ScsiNexus.LunMasking == 1) { + if (Cmnd->lun > sizeof(pLoggedInPort->ScsiNexus.lun)) + return NULL; // we KNOW all the valid LUNs... 0xFF is invalid! Cmnd->SCp.have_data_in = pLoggedInPort->ScsiNexus.lun[Cmnd->lun]; if (pLoggedInPort->ScsiNexus.lun[Cmnd->lun] == 0xFF) @@ -3504,7 +3524,6 @@ { printk("LinkDnCmnd scsi_done ptr null, port_id %Xh\n", pLoggedInPort->port_id); - Cmnd->SCp.sent_command = 0; } else call_scsi_done(Cmnd); @@ -5232,7 +5251,6 @@ sgl = (struct scatterlist*)Cmnd->request_buffer; sg_count = pci_map_sg(pcidev, sgl, Cmnd->use_sg, scsi_to_pci_dma_dir(Cmnd->sc_data_direction)); - // printk("sgl = %p, sg_count = %d\n", (void *) sgl, sg_count); if( sg_count <= 3 ) { // we need to be careful here that no individual mapping @@ -5261,7 +5279,6 @@ // printk("totalsgs = %d, sgcount=%d\n",totalsgs,sg_count); } - // printk("totalsgs = %d, sgcount=%d\n", totalsgs, sg_count); if( totalsgs <= 3 ) // can (must) use "local" SEST list { while( bytes_to_go) @@ -6164,13 +6181,11 @@ } else { - Exchanges->fcExchange[ x_ID ].Cmnd->SCp.sent_command = 0; // printk(" not calling scsi_done on x_ID %Xh, Cmnd %p\n", // x_ID, Exchanges->fcExchange[ x_ID ].Cmnd); } } else{ - Exchanges->fcExchange[ x_ID ].Cmnd->SCp.sent_command = 0; printk(" x_ID %Xh, type %Xh, Cdb0 %Xh\n", x_ID, Exchanges->fcExchange[ x_ID ].type, Exchanges->fcExchange[ x_ID ].Cmnd->cmnd[0]); @@ -6463,10 +6478,10 @@ for( i=0; (i < Cmnd->cmd_len) && i < MAX_COMMAND_SIZE; i++) *payload++ = Cmnd->cmnd[i]; - if( Cmnd->cmd_len == 16 ) - { - memcpy( payload, &Cmnd->SCp.buffers_residual, 4); - } + // if( Cmnd->cmd_len == 16 ) + // { + // memcpy( payload, &Cmnd->SCp.buffers_residual, 4); + // } payload+= (16 - i); // FCP_DL is largest number of expected data bytes diff -Nru a/drivers/scsi/esp.c b/drivers/scsi/esp.c --- a/drivers/scsi/esp.c Fri Aug 16 14:35:01 2002 +++ b/drivers/scsi/esp.c Fri Aug 16 14:35:01 2002 @@ -467,7 +467,6 @@ /* Resetting various pieces of the ESP scsi driver chipset/buses. */ static void esp_reset_dma(struct esp *esp) { - unsigned long flags; int can_do_burst16, can_do_burst32, can_do_burst64; int can_do_sbus64; u32 tmp; diff -Nru a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/scsi/ide-scsi.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,885 @@ +/* + * linux/drivers/scsi/ide-scsi.c Version 0.9 Jul 4, 1999 + * + * Copyright (C) 1996 - 1999 Gadi Oxman + */ + +/* + * Emulation of a SCSI host adapter for IDE ATAPI devices. + * + * With this driver, one can use the Linux SCSI drivers instead of the + * native IDE ATAPI drivers. + * + * Ver 0.1 Dec 3 96 Initial version. + * Ver 0.2 Jan 26 97 Fixed bug in cleanup_module() and added emulation + * of MODE_SENSE_6/MODE_SELECT_6 for cdroms. Thanks + * to Janos Farkas for pointing this out. + * Avoid using bitfields in structures for m68k. + * Added Scatter/Gather and DMA support. + * Ver 0.4 Dec 7 97 Add support for ATAPI PD/CD drives. + * Use variable timeout for each command. + * Ver 0.5 Jan 2 98 Fix previous PD/CD support. + * Allow disabling of SCSI-6 to SCSI-10 transformation. + * Ver 0.6 Jan 27 98 Allow disabling of SCSI command translation layer + * for access through /dev/sg. + * Fix MODE_SENSE_6/MODE_SELECT_6/INQUIRY translation. + * Ver 0.7 Dec 04 98 Ignore commands where lun != 0 to avoid multiple + * detection of devices with CONFIG_SCSI_MULTI_LUN + * Ver 0.8 Feb 05 99 Optical media need translation too. Reverse 0.7. + * Ver 0.9 Jul 04 99 Fix a bug in SG_SET_TRANSFORM. + * Ver 0.91 Jun 10 02 Fix "off by one" error in transforms + */ + +#define IDESCSI_VERSION "0.9" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "scsi.h" +#include "hosts.h" +#include "sd.h" +#include + +#define IDESCSI_DEBUG_LOG 0 + +typedef struct idescsi_pc_s { + u8 c[12]; /* Actual packet bytes */ + int request_transfer; /* Bytes to transfer */ + int actually_transferred; /* Bytes actually transferred */ + int buffer_size; /* Size of our data buffer */ + struct request *rq; /* The corresponding request */ + byte *buffer; /* Data buffer */ + byte *current_position; /* Pointer into the above buffer */ + struct scatterlist *sg; /* Scatter gather table */ + int b_count; /* Bytes transferred from current entry */ + Scsi_Cmnd *scsi_cmd; /* SCSI command */ + void (*done)(Scsi_Cmnd *); /* Scsi completion routine */ + unsigned long flags; /* Status/Action flags */ + unsigned long timeout; /* Command timeout */ +} idescsi_pc_t; + +/* + * Packet command status bits. + */ +#define PC_DMA_IN_PROGRESS 0 /* 1 while DMA in progress */ +#define PC_WRITING 1 /* Data direction */ +#define PC_TRANSFORM 2 /* transform SCSI commands */ + +/* + * SCSI command transformation layer + */ +#define IDESCSI_TRANSFORM 0 /* Enable/Disable transformation */ +#define IDESCSI_SG_TRANSFORM 1 /* /dev/sg transformation */ + +/* + * Log flags + */ +#define IDESCSI_LOG_CMD 0 /* Log SCSI commands */ + +typedef struct { + ide_drive_t *drive; + idescsi_pc_t *pc; /* Current packet command */ + unsigned long flags; /* Status/Action flags */ + unsigned long transform; /* SCSI cmd translation layer */ + unsigned long log; /* log flags */ +} idescsi_scsi_t; + +/* + * Per ATAPI device status bits. + */ +#define IDESCSI_DRQ_INTERRUPT 0 /* DRQ interrupt device */ + +/* + * ide-scsi requests. + */ +#define IDESCSI_PC_RQ 90 + +/* + * Bits of the interrupt reason register. + */ +#define IDESCSI_IREASON_COD 0x1 /* Information transferred is command */ +#define IDESCSI_IREASON_IO 0x2 /* The device requests us to read */ + +static void idescsi_discard_data (ide_drive_t *drive, unsigned int bcount) +{ + while (bcount--) + IN_BYTE (IDE_DATA_REG); +} + +static void idescsi_output_zeros (ide_drive_t *drive, unsigned int bcount) +{ + while (bcount--) + OUT_BYTE (0, IDE_DATA_REG); +} + +/* + * PIO data transfer routines using the scatter gather table. + */ +static void idescsi_input_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount) +{ + int count; + char *buf; + + while (bcount) { + if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) { + printk (KERN_ERR "ide-scsi: scatter gather table too small, discarding data\n"); + idescsi_discard_data (drive, bcount); + return; + } + count = IDE_MIN (pc->sg->length - pc->b_count, bcount); + buf = page_address(pc->sg->page) + pc->sg->offset; + atapi_input_bytes (drive, buf + pc->b_count, count); + bcount -= count; pc->b_count += count; + if (pc->b_count == pc->sg->length) { + pc->sg++; + pc->b_count = 0; + } + } +} + +static void idescsi_output_buffers (ide_drive_t *drive, idescsi_pc_t *pc, unsigned int bcount) +{ + int count; + char *buf; + + while (bcount) { + if (pc->sg - (struct scatterlist *) pc->scsi_cmd->request_buffer > pc->scsi_cmd->use_sg) { + printk (KERN_ERR "ide-scsi: scatter gather table too small, padding with zeros\n"); + idescsi_output_zeros (drive, bcount); + return; + } + count = IDE_MIN (pc->sg->length - pc->b_count, bcount); + buf = page_address(pc->sg->page) + pc->sg->offset; + atapi_output_bytes (drive, buf + pc->b_count, count); + bcount -= count; pc->b_count += count; + if (pc->b_count == pc->sg->length) { + pc->sg++; + pc->b_count = 0; + } + } +} + +/* + * Most of the SCSI commands are supported directly by ATAPI devices. + * idescsi_transform_pc handles the few exceptions. + */ +static inline void idescsi_transform_pc1 (ide_drive_t *drive, idescsi_pc_t *pc) +{ + u8 *c = pc->c, *scsi_buf = pc->buffer, *sc = pc->scsi_cmd->cmnd; + char *atapi_buf; + + if (!test_bit(PC_TRANSFORM, &pc->flags)) + return; + if (drive->media == ide_cdrom || drive->media == ide_optical) { + if (c[0] == READ_6 || c[0] == WRITE_6) { + c[8] = c[4]; c[5] = c[3]; c[4] = c[2]; + c[3] = c[1] & 0x1f; c[2] = 0; c[1] &= 0xe0; + c[0] += (READ_10 - READ_6); + } + if (c[0] == MODE_SENSE || c[0] == MODE_SELECT) { + unsigned short new_len; + if (!scsi_buf) + return; + if ((atapi_buf = kmalloc(pc->buffer_size + 4, GFP_ATOMIC)) == NULL) + return; + memset(atapi_buf, 0, pc->buffer_size + 4); + memset (c, 0, 12); + c[0] = sc[0] | 0x40; + c[1] = sc[1]; + c[2] = sc[2]; + new_len = sc[4] + 4; + c[8] = new_len; + c[9] = sc[5]; + c[7] = new_len >> 8; + if (c[0] == MODE_SELECT_10) { + atapi_buf[1] = scsi_buf[0]; /* Mode data length */ + atapi_buf[2] = scsi_buf[1]; /* Medium type */ + atapi_buf[3] = scsi_buf[2]; /* Device specific parameter */ + atapi_buf[7] = scsi_buf[3]; /* Block descriptor length */ + memcpy(atapi_buf + 8, scsi_buf + 4, pc->buffer_size - 4); + } + pc->buffer = atapi_buf; + pc->request_transfer += 4; + pc->buffer_size += 4; + } + } +} + +static inline void idescsi_transform_pc2 (ide_drive_t *drive, idescsi_pc_t *pc) +{ + u8 *atapi_buf = pc->buffer; + u8 *sc = pc->scsi_cmd->cmnd; + u8 *scsi_buf = pc->scsi_cmd->request_buffer; + + if (!test_bit(PC_TRANSFORM, &pc->flags)) + return; + if (drive->media == ide_cdrom || drive->media == ide_optical) { + if (pc->c[0] == MODE_SENSE_10 && sc[0] == MODE_SENSE) { + scsi_buf[0] = atapi_buf[1]; /* Mode data length */ + scsi_buf[1] = atapi_buf[2]; /* Medium type */ + scsi_buf[2] = atapi_buf[3]; /* Device specific parameter */ + scsi_buf[3] = atapi_buf[7]; /* Block descriptor length */ + memcpy(scsi_buf + 4, atapi_buf + 8, pc->request_transfer - 8); + } + if (pc->c[0] == INQUIRY) { + scsi_buf[2] |= 2; /* ansi_revision */ + scsi_buf[3] = (scsi_buf[3] & 0xf0) | 2; /* response data format */ + } + } + if (atapi_buf && atapi_buf != scsi_buf) + kfree(atapi_buf); +} + +static inline void idescsi_free_bio (struct bio *bio) +{ + struct bio *bhp; + + while (bio) { + bhp = bio; + bio = bio->bi_next; + bio_put(bhp); + } +} + +static void hexdump(u8 *x, int len) +{ + int i; + + printk("[ "); + for (i = 0; i < len; i++) + printk("%x ", x[i]); + printk("]\n"); +} + +static int idescsi_end_request (ide_drive_t *drive, int uptodate) +{ + idescsi_scsi_t *scsi = drive->driver_data; + struct request *rq = HWGROUP(drive)->rq; + idescsi_pc_t *pc = (idescsi_pc_t *) rq->special; + int log = test_bit(IDESCSI_LOG_CMD, &scsi->log); + struct Scsi_Host *host; + u8 *scsi_buf; + unsigned long flags; + + if (!(rq->flags & REQ_SPECIAL)) { + ide_end_request(drive, uptodate); + return 0; + } + ide_end_drive_cmd (drive, 0, 0); + if (rq->errors >= ERROR_MAX) { + pc->scsi_cmd->result = DID_ERROR << 16; + if (log) + printk ("ide-scsi: %s: I/O error for %lu\n", drive->name, pc->scsi_cmd->serial_number); + } else if (rq->errors) { + pc->scsi_cmd->result = (CHECK_CONDITION << 1) | (DID_OK << 16); + if (log) + printk ("ide-scsi: %s: check condition for %lu\n", drive->name, pc->scsi_cmd->serial_number); + } else { + pc->scsi_cmd->result = DID_OK << 16; + idescsi_transform_pc2 (drive, pc); + if (log) { + printk ("ide-scsi: %s: suc %lu", drive->name, pc->scsi_cmd->serial_number); + if (!test_bit(PC_WRITING, &pc->flags) && pc->actually_transferred && pc->actually_transferred <= 1024 && pc->buffer) { + printk(", rst = "); + scsi_buf = pc->scsi_cmd->request_buffer; + hexdump(scsi_buf, IDE_MIN(16, pc->scsi_cmd->request_bufflen)); + } else printk("\n"); + } + } + host = pc->scsi_cmd->host; + spin_lock_irqsave(host->host_lock, flags); + pc->done(pc->scsi_cmd); + spin_unlock_irqrestore(host->host_lock, flags); + idescsi_free_bio (rq->bio); + kfree(pc); kfree(rq); + scsi->pc = NULL; + return 0; +} + +static inline unsigned long get_timeout(idescsi_pc_t *pc) +{ + return IDE_MAX(WAIT_CMD, pc->timeout - jiffies); +} + +/* + * Our interrupt handler. + */ +static ide_startstop_t idescsi_pc_intr (ide_drive_t *drive) +{ + idescsi_scsi_t *scsi = drive->driver_data; + byte status, ireason; + int bcount; + idescsi_pc_t *pc=scsi->pc; + struct request *rq = pc->rq; + unsigned int temp; + +#if IDESCSI_DEBUG_LOG + printk (KERN_INFO "ide-scsi: Reached idescsi_pc_intr interrupt handler\n"); +#endif /* IDESCSI_DEBUG_LOG */ + + if (test_and_clear_bit (PC_DMA_IN_PROGRESS, &pc->flags)) { +#if IDESCSI_DEBUG_LOG + printk ("ide-scsi: %s: DMA complete\n", drive->name); +#endif /* IDESCSI_DEBUG_LOG */ + pc->actually_transferred=pc->request_transfer; + (void) (HWIF(drive)->dmaproc(ide_dma_end, drive)); + } + + status = GET_STAT(); /* Clear the interrupt */ + + if ((status & DRQ_STAT) == 0) { /* No more interrupts */ + if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) + printk (KERN_INFO "Packet command completed, %d bytes transferred\n", pc->actually_transferred); + local_irq_enable(); + if (status & ERR_STAT) + rq->errors++; + idescsi_end_request (drive, 1); + return ide_stopped; + } + bcount = IN_BYTE (IDE_BCOUNTH_REG) << 8 | IN_BYTE (IDE_BCOUNTL_REG); + ireason = IN_BYTE (IDE_IREASON_REG); + + if (ireason & IDESCSI_IREASON_COD) { + printk (KERN_ERR "ide-scsi: CoD != 0 in idescsi_pc_intr\n"); + return ide_do_reset (drive); + } + if (ireason & IDESCSI_IREASON_IO) { + temp = pc->actually_transferred + bcount; + if ( temp > pc->request_transfer) { + if (temp > pc->buffer_size) { + printk (KERN_ERR "ide-scsi: The scsi wants to send us more data than expected - discarding data\n"); + temp = pc->buffer_size - pc->actually_transferred; + if (temp) { + clear_bit(PC_WRITING, &pc->flags); + if (pc->sg) + idescsi_input_buffers(drive, pc, temp); + else + atapi_input_bytes(drive, pc->current_position, temp); + printk(KERN_ERR "ide-scsi: transferred %d of %d bytes\n", temp, bcount); + } + pc->actually_transferred += temp; + pc->current_position += temp; + idescsi_discard_data (drive,bcount - temp); + ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), NULL); + return ide_started; + } +#if IDESCSI_DEBUG_LOG + printk (KERN_NOTICE "ide-scsi: The scsi wants to send us more data than expected - allowing transfer\n"); +#endif /* IDESCSI_DEBUG_LOG */ + } + } + if (ireason & IDESCSI_IREASON_IO) { + clear_bit(PC_WRITING, &pc->flags); + if (pc->sg) + idescsi_input_buffers (drive, pc, bcount); + else + atapi_input_bytes (drive,pc->current_position,bcount); + } else { + set_bit(PC_WRITING, &pc->flags); + if (pc->sg) + idescsi_output_buffers (drive, pc, bcount); + else + atapi_output_bytes (drive,pc->current_position,bcount); + } + pc->actually_transferred+=bcount; /* Update the current position */ + pc->current_position+=bcount; + + ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), NULL); /* And set the interrupt handler again */ + return ide_started; +} + +static ide_startstop_t idescsi_transfer_pc (ide_drive_t *drive) +{ + idescsi_scsi_t *scsi = drive->driver_data; + idescsi_pc_t *pc = scsi->pc; + byte ireason; + ide_startstop_t startstop; + + if (ide_wait_stat (&startstop,drive,DRQ_STAT,BUSY_STAT,WAIT_READY)) { + printk (KERN_ERR "ide-scsi: Strange, packet command initiated yet DRQ isn't asserted\n"); + return startstop; + } + ireason = IN_BYTE (IDE_IREASON_REG); + if ((ireason & (IDESCSI_IREASON_IO | IDESCSI_IREASON_COD)) != IDESCSI_IREASON_COD) { + printk (KERN_ERR "ide-scsi: (IO,CoD) != (0,1) while issuing a packet command\n"); + return ide_do_reset (drive); + } + ide_set_handler(drive, &idescsi_pc_intr, get_timeout(pc), NULL); /* Set the interrupt routine */ + atapi_output_bytes (drive, scsi->pc->c, 12); /* Send the actual packet */ + return ide_started; +} + +/* + * Issue a packet command + */ +static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc) +{ + idescsi_scsi_t *scsi = drive->driver_data; + int bcount; + struct request *rq = pc->rq; + int dma_ok = 0; + + scsi->pc=pc; /* Set the current packet command */ + pc->actually_transferred=0; /* We haven't transferred any data yet */ + pc->current_position=pc->buffer; + bcount = IDE_MIN (pc->request_transfer, 63 * 1024); /* Request to transfer the entire buffer at once */ + + if (drive->using_dma && rq->bio) + dma_ok=!HWIF(drive)->dmaproc(test_bit (PC_WRITING, &pc->flags) ? ide_dma_write : ide_dma_read, drive); + + SELECT_DRIVE(HWIF(drive), drive); + if (IDE_CONTROL_REG) + OUT_BYTE (drive->ctl,IDE_CONTROL_REG); + OUT_BYTE (dma_ok,IDE_FEATURE_REG); + OUT_BYTE (bcount >> 8,IDE_BCOUNTH_REG); + OUT_BYTE (bcount & 0xff,IDE_BCOUNTL_REG); + + if (dma_ok) { + set_bit (PC_DMA_IN_PROGRESS, &pc->flags); + (void) (HWIF(drive)->dmaproc(ide_dma_begin, drive)); + } + if (test_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags)) { + ide_set_handler (drive, &idescsi_transfer_pc, get_timeout(pc), NULL); + OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* Issue the packet command */ + return ide_started; + } else { + OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); + return idescsi_transfer_pc (drive); + } +} + +/* + * idescsi_do_request is our request handling function. + */ +static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *rq, unsigned long block) +{ +#if IDESCSI_DEBUG_LOG + printk (KERN_INFO "rq_status: %d, rq_dev: %u, cmd: %d, errors: %d\n",rq->rq_status,(unsigned int) rq->rq_dev,rq->cmd,rq->errors); + printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %ld\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors); +#endif /* IDESCSI_DEBUG_LOG */ + + if (rq->flags & REQ_SPECIAL) { + return idescsi_issue_pc (drive, (idescsi_pc_t *) rq->special); + } + blk_dump_rq_flags(rq, "ide-scsi: unsup command"); + idescsi_end_request (drive, 0); + return ide_stopped; +} + +static int idescsi_open (struct inode *inode, struct file *filp, ide_drive_t *drive) +{ + MOD_INC_USE_COUNT; + return 0; +} + +static void idescsi_ide_release (struct inode *inode, struct file *filp, ide_drive_t *drive) +{ + MOD_DEC_USE_COUNT; +} + +static ide_drive_t *idescsi_drives[MAX_HWIFS * MAX_DRIVES]; +static int idescsi_initialized = 0; + +static void idescsi_add_settings(ide_drive_t *drive) +{ + idescsi_scsi_t *scsi = drive->driver_data; + +/* + * drive setting name read/write ioctl ioctl data type min max mul_factor div_factor data pointer set function + */ + ide_add_setting(drive, "bios_cyl", SETTING_RW, -1, -1, TYPE_INT, 0, 1023, 1, 1, &drive->bios_cyl, NULL); + ide_add_setting(drive, "bios_head", SETTING_RW, -1, -1, TYPE_BYTE, 0, 255, 1, 1, &drive->bios_head, NULL); + ide_add_setting(drive, "bios_sect", SETTING_RW, -1, -1, TYPE_BYTE, 0, 63, 1, 1, &drive->bios_sect, NULL); + ide_add_setting(drive, "transform", SETTING_RW, -1, -1, TYPE_INT, 0, 3, 1, 1, &scsi->transform, NULL); + ide_add_setting(drive, "log", SETTING_RW, -1, -1, TYPE_INT, 0, 1, 1, 1, &scsi->log, NULL); +} + +/* + * Driver initialization. + */ +static void idescsi_setup (ide_drive_t *drive, idescsi_scsi_t *scsi, int id) +{ + DRIVER(drive)->busy++; + idescsi_drives[id] = drive; + drive->driver_data = scsi; + drive->ready_stat = 0; + memset (scsi, 0, sizeof (idescsi_scsi_t)); + scsi->drive = drive; + if (drive->id && (drive->id->config & 0x0060) == 0x20) + set_bit (IDESCSI_DRQ_INTERRUPT, &scsi->flags); + set_bit(IDESCSI_TRANSFORM, &scsi->transform); + clear_bit(IDESCSI_SG_TRANSFORM, &scsi->transform); +#if IDESCSI_DEBUG_LOG + set_bit(IDESCSI_LOG_CMD, &scsi->log); +#endif /* IDESCSI_DEBUG_LOG */ + idescsi_add_settings(drive); +} + +static int idescsi_cleanup (ide_drive_t *drive) +{ + idescsi_scsi_t *scsi = drive->driver_data; + + if (ide_unregister_subdriver (drive)) + return 1; + drive->driver_data = NULL; + kfree (scsi); + return 0; +} + +/* + * IDE subdriver functions, registered with ide.c + */ +static ide_driver_t idescsi_driver = { + name: "ide-scsi", + version: IDESCSI_VERSION, + media: ide_scsi, + busy: 0, + supports_dma: 1, + supports_dsc_overlap: 0, + cleanup: idescsi_cleanup, + standby: NULL, + flushcache: NULL, + do_request: idescsi_do_request, + end_request: idescsi_end_request, + ioctl: NULL, + open: idescsi_open, + release: idescsi_ide_release, + media_change: NULL, + revalidate: NULL, + pre_reset: NULL, + capacity: NULL, + special: NULL, + proc: NULL, +}; + +int idescsi_init (void); +static ide_module_t idescsi_module = { + IDE_DRIVER_MODULE, + idescsi_init, + &idescsi_driver, + NULL +}; + +/* + * idescsi_init will register the driver for each scsi. + */ +int idescsi_init (void) +{ + ide_drive_t *drive; + idescsi_scsi_t *scsi; + byte media[] = {TYPE_DISK, TYPE_TAPE, TYPE_PROCESSOR, TYPE_WORM, TYPE_ROM, TYPE_SCANNER, TYPE_MOD, 255}; + int i, failed, id; + + if (idescsi_initialized) + return 0; + idescsi_initialized = 1; + for (i = 0; i < MAX_HWIFS * MAX_DRIVES; i++) + idescsi_drives[i] = NULL; + MOD_INC_USE_COUNT; + for (i = 0; media[i] != 255; i++) { + failed = 0; + while ((drive = ide_scan_devices (media[i], idescsi_driver.name, NULL, failed++)) != NULL) { + + if ((scsi = (idescsi_scsi_t *) kmalloc (sizeof (idescsi_scsi_t), GFP_KERNEL)) == NULL) { + printk (KERN_ERR "ide-scsi: %s: Can't allocate a scsi structure\n", drive->name); + continue; + } + if (ide_register_subdriver (drive, &idescsi_driver, IDE_SUBDRIVER_VERSION)) { + printk (KERN_ERR "ide-scsi: %s: Failed to register the driver with ide.c\n", drive->name); + kfree (scsi); + continue; + } + for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++); + idescsi_setup (drive, scsi, id); + failed--; + } + } + ide_register_module(&idescsi_module); + MOD_DEC_USE_COUNT; + return 0; +} + +int idescsi_detect (Scsi_Host_Template *host_template) +{ + struct Scsi_Host *host; + int id; + int last_lun = 0; + + host_template->proc_name = "ide-scsi"; + host = scsi_register(host_template, 0); + if(host == NULL) + return 0; + + for (id = 0; id < MAX_HWIFS * MAX_DRIVES && idescsi_drives[id]; id++) + last_lun = IDE_MAX(last_lun, idescsi_drives[id]->last_lun); + host->max_id = id; + host->max_lun = last_lun + 1; + host->can_queue = host->cmd_per_lun * id; + return 1; +} + +int idescsi_release (struct Scsi_Host *host) +{ + ide_drive_t *drive; + int id; + + for (id = 0; id < MAX_HWIFS * MAX_DRIVES; id++) { + drive = idescsi_drives[id]; + if (drive) + DRIVER(drive)->busy--; + } + return 0; +} + +const char *idescsi_info (struct Scsi_Host *host) +{ + return "SCSI host adapter emulation for IDE ATAPI devices"; +} + +int idescsi_ioctl (Scsi_Device *dev, int cmd, void *arg) +{ + ide_drive_t *drive = idescsi_drives[dev->id]; + idescsi_scsi_t *scsi = drive->driver_data; + + if (cmd == SG_SET_TRANSFORM) { + if (arg) + set_bit(IDESCSI_SG_TRANSFORM, &scsi->transform); + else + clear_bit(IDESCSI_SG_TRANSFORM, &scsi->transform); + return 0; + } else if (cmd == SG_GET_TRANSFORM) + return put_user(test_bit(IDESCSI_SG_TRANSFORM, &scsi->transform), (int *) arg); + return -EINVAL; +} + +static inline struct bio *idescsi_kmalloc_bio (int count) +{ + struct bio *bh, *bhp, *first_bh; + + if ((first_bh = bhp = bh = bio_alloc(GFP_ATOMIC, 1)) == NULL) + goto abort; + bio_init(bh); + bh->bi_vcnt = 1; + while (--count) { + if ((bh = bio_alloc(GFP_ATOMIC, 1)) == NULL) + goto abort; + bio_init(bh); + bh->bi_vcnt = 1; + bhp->bi_next = bh; + bhp = bh; + bh->bi_next = NULL; + } + return first_bh; +abort: + idescsi_free_bio (first_bh); + return NULL; +} + +static inline int idescsi_set_direction (idescsi_pc_t *pc) +{ + switch (pc->c[0]) { + case READ_6: case READ_10: case READ_12: + clear_bit (PC_WRITING, &pc->flags); + return 0; + case WRITE_6: case WRITE_10: case WRITE_12: + set_bit (PC_WRITING, &pc->flags); + return 0; + default: + return 1; + } +} + +static inline struct bio *idescsi_dma_bio(ide_drive_t *drive, idescsi_pc_t *pc) +{ + struct bio *bh = NULL, *first_bh = NULL; + int segments = pc->scsi_cmd->use_sg; + struct scatterlist *sg = pc->scsi_cmd->request_buffer; + + if (!drive->using_dma || !pc->request_transfer || pc->request_transfer % 1024) + return NULL; + if (idescsi_set_direction(pc)) + return NULL; + if (segments) { + if ((first_bh = bh = idescsi_kmalloc_bio (segments)) == NULL) + return NULL; +#if IDESCSI_DEBUG_LOG + printk ("ide-scsi: %s: building DMA table, %d segments, %dkB total\n", drive->name, segments, pc->request_transfer >> 10); +#endif /* IDESCSI_DEBUG_LOG */ + while (segments--) { + bh->bi_io_vec[0].bv_page = sg->page; + bh->bi_io_vec[0].bv_len = sg->length; + bh->bi_io_vec[0].bv_offset = sg->offset; + bh->bi_size = sg->length; + bh = bh->bi_next; + sg++; + } + } else { + if ((first_bh = bh = idescsi_kmalloc_bio (1)) == NULL) + return NULL; +#if IDESCSI_DEBUG_LOG + printk ("ide-scsi: %s: building DMA table for a single buffer (%dkB)\n", drive->name, pc->request_transfer >> 10); +#endif /* IDESCSI_DEBUG_LOG */ + bh->bi_io_vec[0].bv_page = virt_to_page(pc->scsi_cmd->request_buffer); + bh->bi_io_vec[0].bv_len = pc->request_transfer; + bh->bi_io_vec[0].bv_offset = (unsigned long) pc->scsi_cmd->request_buffer & ~PAGE_MASK; + bh->bi_size = pc->request_transfer; + } + return first_bh; +} + +static inline int should_transform(ide_drive_t *drive, Scsi_Cmnd *cmd) +{ + idescsi_scsi_t *scsi = drive->driver_data; + + if (major(cmd->request->rq_dev) == SCSI_GENERIC_MAJOR) + return test_bit(IDESCSI_SG_TRANSFORM, &scsi->transform); + return test_bit(IDESCSI_TRANSFORM, &scsi->transform); +} + +int idescsi_queue (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) +{ + ide_drive_t *drive = idescsi_drives[cmd->target]; + idescsi_scsi_t *scsi; + struct request *rq = NULL; + idescsi_pc_t *pc = NULL; + + if (!drive) { + printk (KERN_ERR "ide-scsi: drive id %d not present\n", cmd->target); + goto abort; + } + scsi = drive->driver_data; + pc = kmalloc (sizeof (idescsi_pc_t), GFP_ATOMIC); + rq = kmalloc (sizeof (struct request), GFP_ATOMIC); + if (rq == NULL || pc == NULL) { + printk (KERN_ERR "ide-scsi: %s: out of memory\n", drive->name); + goto abort; + } + + memset (pc->c, 0, 12); + pc->flags = 0; + pc->rq = rq; + memcpy (pc->c, cmd->cmnd, cmd->cmd_len); + if (cmd->use_sg) { + pc->buffer = NULL; + pc->sg = cmd->request_buffer; + } else { + pc->buffer = cmd->request_buffer; + pc->sg = NULL; + } + pc->b_count = 0; + pc->request_transfer = pc->buffer_size = cmd->request_bufflen; + pc->scsi_cmd = cmd; + pc->done = done; + pc->timeout = jiffies + cmd->timeout_per_command; + + if (should_transform(drive, cmd)) + set_bit(PC_TRANSFORM, &pc->flags); + idescsi_transform_pc1 (drive, pc); + + if (test_bit(IDESCSI_LOG_CMD, &scsi->log)) { + printk ("ide-scsi: %s: que %lu, cmd = ", drive->name, cmd->serial_number); + hexdump(cmd->cmnd, cmd->cmd_len); + if (memcmp(pc->c, cmd->cmnd, cmd->cmd_len)) { + printk ("ide-scsi: %s: que %lu, tsl = ", drive->name, cmd->serial_number); + hexdump(pc->c, 12); + } + } + + ide_init_drive_cmd (rq); + rq->special = (char *) pc; + rq->bio = idescsi_dma_bio (drive, pc); + rq->flags = REQ_SPECIAL; + spin_unlock_irq(cmd->host->host_lock); + (void) ide_do_drive_cmd (drive, rq, ide_end); + spin_lock_irq(cmd->host->host_lock); + return 0; +abort: + if (pc) kfree (pc); + if (rq) kfree (rq); + cmd->result = DID_ERROR << 16; + done(cmd); + return 0; +} + +int idescsi_abort (Scsi_Cmnd *cmd) +{ + return SCSI_ABORT_SNOOZE; +} + +int idescsi_reset (Scsi_Cmnd *cmd, unsigned int resetflags) +{ + return SCSI_RESET_SUCCESS; +} + +static int idescsi_bios(Disk *disk, struct block_device *dev, int *parm) +{ + ide_drive_t *drive = idescsi_drives[disk->device->id]; + + if (drive->bios_cyl && drive->bios_head && drive->bios_sect) { + parm[0] = drive->bios_head; + parm[1] = drive->bios_sect; + parm[2] = drive->bios_cyl; + } + return 0; +} + +static Scsi_Host_Template idescsi_template = { + module: THIS_MODULE, + name: "idescsi", + detect: idescsi_detect, + release: idescsi_release, + info: idescsi_info, + ioctl: idescsi_ioctl, + queuecommand: idescsi_queue, + abort: idescsi_abort, + reset: idescsi_reset, + bios_param: idescsi_bios, + can_queue: 10, + this_id: -1, + sg_tablesize: 256, + cmd_per_lun: 5, + use_clustering: DISABLE_CLUSTERING, + emulated: 1, +}; + +static int __init init_idescsi_module(void) +{ + idescsi_init(); + scsi_register_host(&idescsi_template); + return 0; +} + +static void __exit exit_idescsi_module(void) +{ + ide_drive_t *drive; + byte media[] = {TYPE_DISK, TYPE_TAPE, TYPE_PROCESSOR, TYPE_WORM, TYPE_ROM, TYPE_SCANNER, TYPE_MOD, 255}; + int i, failed; + + scsi_unregister_host(&idescsi_template); + for (i = 0; media[i] != 255; i++) { + failed = 0; + while ((drive = ide_scan_devices (media[i], idescsi_driver.name, &idescsi_driver, failed)) != NULL) + if (idescsi_cleanup (drive)) { + printk ("%s: exit_idescsi_module() called while still busy\n", drive->name); + failed++; + } + } + ide_unregister_module(&idescsi_module); +} + +module_init(init_idescsi_module); +module_exit(exit_idescsi_module); +MODULE_LICENSE("GPL"); diff -Nru a/drivers/scsi/qlogicisp.c b/drivers/scsi/qlogicisp.c --- a/drivers/scsi/qlogicisp.c Fri Aug 16 14:34:57 2002 +++ b/drivers/scsi/qlogicisp.c Fri Aug 16 14:34:57 2002 @@ -1695,7 +1695,6 @@ u_short param[6]; #endif u_short isp_cfg1, hwrev; - unsigned long flags; struct isp1020_hostdata *hostdata = (struct isp1020_hostdata *) host->hostdata; diff -Nru a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c --- a/drivers/scsi/scsi_debug.c Fri Aug 16 14:34:58 2002 +++ b/drivers/scsi/scsi_debug.c Fri Aug 16 14:34:58 2002 @@ -1,4 +1,4 @@ -/* $Id: scsi_debug.c,v 1.1 1992/07/24 06:27:38 root Exp root $ +/* * linux/kernel/scsi_debug.c * * Copyright (C) 1992 Eric Youngdale @@ -12,10 +12,11 @@ * * For documentation see http://www.torque.net/sg/sdebug.html * - * D. Gilbert (dpg) work for MOD device test [20010421] - * dpg, work for devfs large number of disks [20010809] - * dpg, forked for lk 2.5 series [20011216, 20020101] - * dpg, use vmalloc() more inquiry+mode_sense [20020302] + * D. Gilbert (dpg) work for Magneto-Optical device test [20010421] + * dpg: work for devfs large number of disks [20010809] + * forked for lk 2.5 series [20011216, 20020101] + * use vmalloc() more inquiry+mode_sense [20020302] + * add timers for delayed responses [20020721] */ #include @@ -46,7 +47,12 @@ #include #endif -static char scsi_debug_version_str[] = "Version: 1.59 (20020302)"; +#include "scsi_debug.h" + +static const char * scsi_debug_version_str = "Version: 1.62 (20020812)"; + +#define DRIVERFS_SUPPORT 1 /* comment out whole line to disable */ + #ifndef SCSI_CMD_READ_16 #define SCSI_CMD_READ_16 0x88 @@ -54,29 +60,36 @@ #ifndef SCSI_CMD_WRITE_16 #define SCSI_CMD_WRITE_16 0x8a #endif +#ifndef REPORT_LUNS +#define REPORT_LUNS 0xa0 +#endif /* A few options that we want selected */ #define DEF_NR_FAKE_DEVS 1 #define DEF_DEV_SIZE_MB 8 #define DEF_FAKE_BLK0 0 +#define DEF_EVERY_NTH 100 +#define DEF_DELAY 1 #define DEF_OPTS 0 #define SCSI_DEBUG_OPT_NOISE 1 #define SCSI_DEBUG_OPT_MEDIUM_ERR 2 +#define SCSI_DEBUG_OPT_EVERY_NTH 4 #define OPT_MEDIUM_ERR_ADDR 0x1234 static int scsi_debug_num_devs = DEF_NR_FAKE_DEVS; static int scsi_debug_opts = DEF_OPTS; +static int scsi_debug_every_nth = DEF_EVERY_NTH; +static int scsi_debug_cmnd_count = 0; +static int scsi_debug_delay = DEF_DELAY; #define NR_HOSTS_PRESENT (((scsi_debug_num_devs - 1) / 7) + 1) #define N_HEAD 8 #define N_SECTOR 32 #define DEV_READONLY(TGT) (0) #define DEV_REMOVEABLE(TGT) (0) -#define DEVICE_TYPE(TGT) (TYPE_DISK); - -#define SCSI_DEBUG_MAILBOXES (scsi_debug_num_devs + 1) +#define PERIPH_DEVICE_TYPE(TGT) (TYPE_DISK); static int scsi_debug_dev_size_mb = DEF_DEV_SIZE_MB; #define STORE_SIZE (scsi_debug_dev_size_mb * 1024 * 1024) @@ -87,81 +100,79 @@ #define N_CYLINDER (STORE_SIZE / (SECT_SIZE * N_SECTOR * N_HEAD)) -/* Do not attempt to use a timer to simulate a real disk with latency */ -/* Only use this in the actual kernel, not in the simulator. */ -#define IMMEDIATE - -#define START_PARTITION 4 - /* Time to wait before completing a command */ -#define DISK_SPEED (HZ/10) /* 100ms */ #define CAPACITY (N_HEAD * N_SECTOR * N_CYLINDER) #define SECT_SIZE_PER(TGT) SECT_SIZE -static int starts[] = -{N_SECTOR, - N_HEAD * N_SECTOR, /* Single cylinder */ - N_HEAD * N_SECTOR * 4, - 0 /* CAPACITY */, 0}; -static unsigned char * fake_storep; +#define SDEBUG_SENSE_LEN 32 -typedef struct sdebug_dev_info { +struct sdebug_dev_info { Scsi_Device * sdp; - unsigned short host_no; - unsigned short id; + unsigned char sense_buff[SDEBUG_SENSE_LEN]; /* weak nexus */ char reset; - char sb_index; -} Sdebug_dev_info; -static Sdebug_dev_info * devInfop; +}; +static struct sdebug_dev_info * devInfop; + +typedef void (* done_funct_t) (Scsi_Cmnd *); + +struct sdebug_queued_cmd { + int in_use; + struct timer_list cmnd_timer; + done_funct_t done_funct; + struct scsi_cmnd * a_cmnd; + int scsi_result; +}; +static struct sdebug_queued_cmd queued_arr[SCSI_DEBUG_CANQUEUE]; + +static unsigned char * fake_storep; /* ramdisk storage */ + +static unsigned char broken_buff[SDEBUG_SENSE_LEN]; static int num_aborts = 0; static int num_dev_resets = 0; static int num_bus_resets = 0; static int num_host_resets = 0; -static spinlock_t mailbox_lock = SPIN_LOCK_UNLOCKED; -static rwlock_t sdebug_atomic_rw = RW_LOCK_UNLOCKED; - -#include "scsi_debug.h" - -typedef void (*done_fct_t) (Scsi_Cmnd *); - -static volatile done_fct_t * do_done = 0; +static spinlock_t queued_arr_lock = SPIN_LOCK_UNLOCKED; +static rwlock_t atomic_rw = RW_LOCK_UNLOCKED; -static struct Scsi_Host * SHpnt = NULL; +#ifdef DRIVERFS_SUPPORT +static struct device_driver sdebug_driverfs_driver; +#endif -static int scsi_debug_inquiry(unsigned char * cmd, int target, - unsigned char * buff, int bufflen, - Sdebug_dev_info * devip); -static int scsi_debug_mode_sense(unsigned char * cmd, int target, - unsigned char * buff, int bufflen, - Sdebug_dev_info * devip); -static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, - int num, int * errstsp, Sdebug_dev_info * devip); -static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, - int num, int * errstsp, Sdebug_dev_info * devip); -static void scsi_debug_intr_handle(unsigned long); -static Sdebug_dev_info * devInfoReg(Scsi_Device * sdp); -static void mk_sense_buffer(Sdebug_dev_info * devip, int index, int key, +/* function declarations */ +static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff, + int bufflen, struct sdebug_dev_info * devip); +static int resp_mode_sense(unsigned char * cmd, int target, + unsigned char * buff, int bufflen, + struct sdebug_dev_info * devip); +static int resp_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, + int num, struct sdebug_dev_info * devip); +static int resp_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, int num, + struct sdebug_dev_info * devip); +static int resp_report_luns(unsigned char * cmd, unsigned char * buff, + int bufflen, struct sdebug_dev_info * devip); +static void timer_intr_handler(unsigned long); +static struct sdebug_dev_info * devInfoReg(Scsi_Device * sdp); +static void mk_sense_buffer(struct sdebug_dev_info * devip, int key, int asc, int asq, int inbandLen); -static int check_reset(Scsi_Cmnd * SCpnt, Sdebug_dev_info * devip); - -static struct timer_list * timeout = 0; -static Scsi_Cmnd ** SCint = 0; - -/* - * Semaphore used to simulate bus lockups. - */ -static int scsi_debug_lockup = 0; - -#define NUM_SENSE_BUFFS 4 -#define SENSE_BUFF_LEN 32 -static char sense_buffers[NUM_SENSE_BUFFS][SENSE_BUFF_LEN]; +static int check_reset(Scsi_Cmnd * SCpnt, struct sdebug_dev_info * devip); +static int schedule_resp(struct scsi_cmnd * cmnd, + struct sdebug_dev_info * devip, + done_funct_t done, int scsi_result, int delta_jiff); +static void init_all_queued(void); +static void stop_all_queued(void); +static int stop_queued_cmnd(struct scsi_cmnd * cmnd); +static int inquiry_evpd_83(unsigned char * arr, int dev_id_num, + const char * dev_id_str, int dev_id_str_len); +#ifdef DRIVERFS_SUPPORT +static void do_create_driverfs_files(void); +static void do_remove_driverfs_files(void); +#endif -static inline -unsigned char * sdebug_scatg2virt(const struct scatterlist * sclp) +static unsigned char * scatg2virt(const struct scatterlist * sclp) { if (NULL == sclp) return NULL; @@ -173,61 +184,47 @@ } static -int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done) (Scsi_Cmnd *)) +int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, done_funct_t done) { unsigned char *cmd = (unsigned char *) SCpnt->cmnd; int block; int upper_blk; unsigned char *buff; - int scsi_debug_errsts; + int errsts = 0; int target = SCpnt->target; int bufflen = SCpnt->request_bufflen; - unsigned long iflags; - int i, num, capac; - Sdebug_dev_info * devip = NULL; - char * sbuff; + int num, capac; + struct sdebug_dev_info * devip = NULL; + unsigned char * sbuff; - if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) { - printk(KERN_INFO "scsi_debug: queue_command: cmd "); - for (i = 0, num = SCpnt->cmd_len; i < num; ++i) - printk("%02x ", cmd[i]); - printk(" use_sg=%d\n", SCpnt->use_sg); - } - /* - * If we are being notified of the mid-level reposessing a command - * due to timeout, just return. - */ - if (done == NULL) { - return 0; - } + if (done == NULL) + return 0; /* assume mid level reprocessing command */ if (SCpnt->use_sg) { /* just use first element */ struct scatterlist *sgpnt = (struct scatterlist *) SCpnt->request_buffer; - buff = sdebug_scatg2virt(&sgpnt[0]); + buff = scatg2virt(&sgpnt[0]); bufflen = sgpnt[0].length; /* READ and WRITE process scatterlist themselves */ } else buff = (unsigned char *) SCpnt->request_buffer; + if (NULL == buff) { + printk(KERN_WARNING "scsi_debug:qc: buff was NULL??\n"); + buff = broken_buff; /* just point at dummy */ + bufflen = SDEBUG_SENSE_LEN; + } - /* - * If a command comes for the ID of the host itself, just print - * a silly message and return. - */ - if(target == 7) { - printk(KERN_WARNING "How do you do!\n"); - SCpnt->result = 0; - done(SCpnt); - return 0; + if(target == driver_template.this_id) { + printk(KERN_WARNING + "scsi_debug: initiator's id used as target!\n"); + return schedule_resp(SCpnt, NULL, done, 0, 0); } - if ((target > 7) || (SCpnt->lun != 0)) { - SCpnt->result = DID_NO_CONNECT << 16; - done(SCpnt); - return 0; - } + if ((target > driver_template.this_id) || (SCpnt->lun != 0)) + return schedule_resp(SCpnt, NULL, done, + DID_NO_CONNECT << 16, 0); #if 0 printk(KERN_INFO "sdebug:qc: host_no=%d, id=%d, sdp=%p, cmd=0x%x\n", (int)SCpnt->device->host->host_no, (int)SCpnt->device->id, @@ -235,101 +232,93 @@ #endif if (NULL == SCpnt->device->hostdata) { devip = devInfoReg(SCpnt->device); - if (NULL == devip) { - SCpnt->result = DID_NO_CONNECT << 16; - done(SCpnt); - return 0; - } + if (NULL == devip) + return schedule_resp(SCpnt, NULL, done, + DID_NO_CONNECT << 16, 0); SCpnt->device->hostdata = devip; } devip = SCpnt->device->hostdata; + if ((SCSI_DEBUG_OPT_EVERY_NTH & scsi_debug_opts) && + (scsi_debug_every_nth > 0) && + (++scsi_debug_cmnd_count >= scsi_debug_every_nth)) { + scsi_debug_cmnd_count =0; + return 0; /* ignore command causing timeout */ + } + switch (*cmd) { case INQUIRY: /* mandatory */ - scsi_debug_errsts = scsi_debug_inquiry(cmd, target, buff, - bufflen, devip); /* assume INQUIRY called first so setup max_cmd_len */ if (SCpnt->host->max_cmd_len != SCSI_DEBUG_MAX_CMD_LEN) SCpnt->host->max_cmd_len = SCSI_DEBUG_MAX_CMD_LEN; + errsts = resp_inquiry(cmd, target, buff, bufflen, devip); break; case REQUEST_SENSE: /* mandatory */ - SCSI_LOG_LLQUEUE(3, printk("Request sense...\n")); + /* Since this driver indicates autosense by placing the + * sense buffer in the scsi_cmnd structure in the response + * (when CHECK_CONDITION is set), the mid level shouldn't + * need to call REQUEST_SENSE */ if (devip) { - sbuff = &sense_buffers[(int)devip->sb_index][0]; - devip->sb_index = 0; + sbuff = devip->sense_buff; + memcpy(buff, sbuff, (bufflen < SDEBUG_SENSE_LEN) ? + bufflen : SDEBUG_SENSE_LEN); + mk_sense_buffer(devip, 0, 0x0, 0, 7); + } else { + memset(buff, 0, bufflen); + buff[0] = 0x70; } - else - sbuff = &sense_buffers[0][0]; - memcpy(buff, sbuff, (bufflen < SENSE_BUFF_LEN) ? - bufflen : SENSE_BUFF_LEN); - memset(sbuff, 0, SENSE_BUFF_LEN); - sbuff[0] = 0x70; - SCpnt->result = 0; - done(SCpnt); - return 0; + break; case START_STOP: - if (check_reset(SCpnt, devip)) { - done(SCpnt); - return 0; - } - SCSI_LOG_LLQUEUE(3, printk("START_STOP\n")); - scsi_debug_errsts = 0; + errsts = check_reset(SCpnt, devip); break; case ALLOW_MEDIUM_REMOVAL: - if (check_reset(SCpnt, devip)) { - done(SCpnt); - return 0; - } - if (cmd[4]) { - SCSI_LOG_LLQUEUE(2, printk( - "Medium removal inhibited...")); - } else { - SCSI_LOG_LLQUEUE(2, - printk("Medium removal enabled...")); - } - scsi_debug_errsts = 0; + if ((errsts = check_reset(SCpnt, devip))) + break; + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) + printk("\tMedium removal %s\n", + cmd[4] ? "inhibited" : "enabled"); break; case SEND_DIAGNOSTIC: /* mandatory */ - SCSI_LOG_LLQUEUE(3, printk("Send Diagnostic\n")); - if (buff) - memset(buff, 0, bufflen); - scsi_debug_errsts = 0; + memset(buff, 0, bufflen); break; case TEST_UNIT_READY: /* mandatory */ - SCSI_LOG_LLQUEUE(3, printk("Test unit ready(%p %d)\n", - buff, bufflen)); - if (buff) - memset(buff, 0, bufflen); - scsi_debug_errsts = 0; + memset(buff, 0, bufflen); break; + case RESERVE: + errsts = check_reset(SCpnt, devip); + memset(buff, 0, bufflen); + break; + case RESERVE_10: + errsts = check_reset(SCpnt, devip); + memset(buff, 0, bufflen); + break; + case RELEASE: + errsts = check_reset(SCpnt, devip); + memset(buff, 0, bufflen); + break; + case RELEASE_10: + errsts = check_reset(SCpnt, devip); + memset(buff, 0, bufflen); + break; case READ_CAPACITY: - if (check_reset(SCpnt, devip)) { - done(SCpnt); - return 0; - } - SCSI_LOG_LLQUEUE(3, printk("Read Capacity\n")); - SHpnt = SCpnt->host; + errsts = check_reset(SCpnt, devip); memset(buff, 0, bufflen); - capac = CAPACITY - 1; - buff[0] = (capac >> 24); - buff[1] = (capac >> 16) & 0xff; - buff[2] = (capac >> 8) & 0xff; - buff[3] = capac & 0xff; - buff[4] = 0; - buff[5] = 0; - buff[6] = (SECT_SIZE_PER(target) >> 8) & 0xff; - buff[7] = SECT_SIZE_PER(target) & 0xff; - - scsi_debug_errsts = 0; + if (bufflen > 7) { + capac = CAPACITY - 1; + buff[0] = (capac >> 24); + buff[1] = (capac >> 16) & 0xff; + buff[2] = (capac >> 8) & 0xff; + buff[3] = capac & 0xff; + buff[6] = (SECT_SIZE_PER(target) >> 8) & 0xff; + buff[7] = SECT_SIZE_PER(target) & 0xff; + } break; case SCSI_CMD_READ_16: /* SBC-2 */ case READ_12: case READ_10: case READ_6: - if (check_reset(SCpnt, devip)) { - done(SCpnt); - return 0; - } + if ((errsts = check_reset(SCpnt, devip))) + break; upper_blk = 0; if ((*cmd) == SCSI_CMD_READ_16) { upper_blk = cmd[5] + (cmd[4] << 8) + @@ -338,38 +327,31 @@ (cmd[7] << 16) + (cmd[6] << 24); num = cmd[13] + (cmd[12] << 8) + (cmd[11] << 16) + (cmd[10] << 24); - } - else if ((*cmd) == READ_12) { + } else if ((*cmd) == READ_12) { block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); num = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); - } - else if ((*cmd) == READ_10) { + } else if ((*cmd) == READ_10) { block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); num = cmd[8] + (cmd[7] << 8); - } - else { + } else { block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16); num = cmd[4]; } - if (scsi_debug_read(SCpnt, upper_blk, block, num, - &scsi_debug_errsts, devip)) - break; - SCpnt->result = 0; -/* calls bottom half in upper layers before return from scsi_do_...() */ - (done) (SCpnt); - return 0; + errsts = resp_read(SCpnt, upper_blk, block, num, devip); + break; + case REPORT_LUNS: + errsts = resp_report_luns(cmd, buff, bufflen, devip); + break; case SCSI_CMD_WRITE_16: /* SBC-2 */ case WRITE_12: case WRITE_10: case WRITE_6: - if (check_reset(SCpnt, devip)) { - done(SCpnt); - return 0; - } + if ((errsts = check_reset(SCpnt, devip))) + break; upper_blk = 0; if ((*cmd) == SCSI_CMD_WRITE_16) { upper_blk = cmd[5] + (cmd[4] << 8) + @@ -378,97 +360,38 @@ (cmd[7] << 16) + (cmd[6] << 24); num = cmd[13] + (cmd[12] << 8) + (cmd[11] << 16) + (cmd[10] << 24); - } - else if ((*cmd) == WRITE_12) { + } else if ((*cmd) == WRITE_12) { block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); num = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); - } - else if ((*cmd) == WRITE_10) { + } else if ((*cmd) == WRITE_10) { block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24); num = cmd[8] + (cmd[7] << 8); - } - else { + } else { block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16); num = cmd[4]; } - if (scsi_debug_write(SCpnt, upper_blk, block, num, - &scsi_debug_errsts, devip)) - break; - SCpnt->result = 0; -/* calls bottom half in upper layers before return from scsi_do_...() */ - (done) (SCpnt); - return 0; + errsts = resp_write(SCpnt, upper_blk, block, num, devip); + break; case MODE_SENSE: case MODE_SENSE_10: - scsi_debug_errsts = - scsi_debug_mode_sense(cmd, target, buff, bufflen, devip); + errsts = resp_mode_sense(cmd, target, buff, bufflen, devip); break; default: #if 0 printk(KERN_INFO "scsi_debug: Unsupported command, " "opcode=0x%x\n", (int)cmd[0]); #endif - if (check_reset(SCpnt, devip)) { - done(SCpnt); - return 0; - } - scsi_debug_errsts = (COMMAND_COMPLETE << 8) | - (CHECK_CONDITION << 1); - mk_sense_buffer(devip, 2, ILLEGAL_REQUEST, 0x20, 0, 14); - break; - } - - spin_lock_irqsave(&mailbox_lock, iflags); - for (i = 0; i < SCSI_DEBUG_MAILBOXES; i++) { - if (timeout[i].function == NULL) + if ((errsts = check_reset(SCpnt, devip))) break; + mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x20, 0, 14); + errsts = (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); + break; } - - /* - * If all of the slots are full, just return 1. The new error - * handling scheme allows this, and the mid-level should queue things. - */ - if (i >= SCSI_DEBUG_MAILBOXES || timeout[i].function != 0) { - SCSI_LOG_LLQUEUE(1, printk("Command rejected - host busy\n")); - spin_unlock_irqrestore(&mailbox_lock, iflags); - return 1; - } - SCSI_LOG_LLQUEUE(1, printk("Command accepted - slot %d\n", i)); - -#ifdef IMMEDIATE - if (!scsi_debug_lockup) { - SCpnt->result = scsi_debug_errsts; - SCint[i] = SCpnt; - do_done[i] = done; - scsi_debug_intr_handle(i); /* No timer - do this one right away */ - } - spin_unlock_irqrestore(&mailbox_lock, iflags); -#else - - SCpnt->result = scsi_debug_errsts; - timeout[i].function = scsi_debug_intr_handle; - timeout[i].data = i; - timeout[i].expires = jiffies + DISK_SPEED; - SCint[i] = SCpnt; - do_done[i] = done; - - spin_unlock_irqrestore(&mailbox_lock, iflags); - add_timer(&timeout[i]); - if (!done) - printk(KERN_ERR "scsi_debug_queuecommand: " - "done can't be NULL\n"); - -#if 0 - printk(KERN_INFO "Sending command (%d %x %d %d)...", i, done, - timeout[i].expires, jiffies); -#endif -#endif - - return 0; + return schedule_resp(SCpnt, devip, done, errsts, scsi_debug_delay); } static int scsi_debug_ioctl(Scsi_Device *dev, int cmd, void *arg) @@ -476,58 +399,101 @@ if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) { printk(KERN_INFO "scsi_debug: ioctl: cmd=0x%x\n", cmd); } - return -ENOTTY; + return -EINVAL; + /* return -ENOTTY; // correct return but upsets fdisk */ } -static int check_reset(Scsi_Cmnd * SCpnt, Sdebug_dev_info * devip) +static int check_reset(Scsi_Cmnd * SCpnt, struct sdebug_dev_info * devip) { if (devip->reset) { devip->reset = 0; - mk_sense_buffer(devip, 3, UNIT_ATTENTION, 0x29, 0, 14); - SCpnt->result = (COMMAND_COMPLETE << 8) | - (CHECK_CONDITION << 1); - return 1; + mk_sense_buffer(devip, UNIT_ATTENTION, 0x29, 0, 14); + return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); } return 0; } -#define SDEBUG_MAX_INQ_SZ 58 +#define SDEBUG_LONG_INQ_SZ 58 +#define SDEBUG_MAX_INQ_ARR_SZ 128 + +static const char * vendor_id = "Linux "; +static const char * product_id = "scsi_debug "; +static const char * product_rev = "0004"; + +static int inquiry_evpd_83(unsigned char * arr, int dev_id_num, + const char * dev_id_str, int dev_id_str_len) +{ + int num; + + /* Two identification descriptors: */ + /* T10 vendor identifier field format (faked) */ + arr[0] = 0x2; /* ASCII */ + arr[1] = 0x1; + arr[2] = 0x0; + memcpy(&arr[4], vendor_id, 8); + memcpy(&arr[12], product_id, 16); + memcpy(&arr[28], dev_id_str, dev_id_str_len); + num = 8 + 16 + dev_id_str_len; + arr[3] = num; + num += 4; + /* NAA IEEE registered identifier (faked) */ + arr[num] = 0x1; /* binary */ + arr[num + 1] = 0x3; + arr[num + 2] = 0x0; + arr[num + 3] = 0x8; + arr[num + 4] = 0x51; /* ieee company id=0x123456 (faked) */ + arr[num + 5] = 0x23; + arr[num + 6] = 0x45; + arr[num + 7] = 0x60; + arr[num + 8] = (dev_id_num >> 24); + arr[num + 9] = (dev_id_num >> 16) & 0xff; + arr[num + 10] = (dev_id_num >> 8) & 0xff; + arr[num + 11] = dev_id_num & 0xff; + return num + 12; +} -static int scsi_debug_inquiry(unsigned char * cmd, int target, - unsigned char * buff, int bufflen, - Sdebug_dev_info * devip) +static int resp_inquiry(unsigned char * cmd, int target, unsigned char * buff, + int bufflen, struct sdebug_dev_info * devip) { unsigned char pq_pdt; - unsigned char arr[SDEBUG_MAX_INQ_SZ]; - int min_len = bufflen > SDEBUG_MAX_INQ_SZ ? - SDEBUG_MAX_INQ_SZ : bufflen; + unsigned char arr[SDEBUG_MAX_INQ_ARR_SZ]; + int min_len = bufflen > SDEBUG_MAX_INQ_ARR_SZ ? + SDEBUG_MAX_INQ_ARR_SZ : bufflen; - SCSI_LOG_LLQUEUE(3, printk("Inquiry...(%p %d)\n", buff, bufflen)); if (bufflen < cmd[4]) printk(KERN_INFO "scsi_debug: inquiry: bufflen=%d " "< alloc_length=%d\n", bufflen, (int)cmd[4]); memset(buff, 0, bufflen); - memset(arr, 0, SDEBUG_MAX_INQ_SZ); - pq_pdt = DEVICE_TYPE(target); + memset(arr, 0, SDEBUG_MAX_INQ_ARR_SZ); + pq_pdt = PERIPH_DEVICE_TYPE(target); arr[0] = pq_pdt; if (0x2 & cmd[1]) { /* CMDDT bit set */ - mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14); + mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14); return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); - } - else if (0x1 & cmd[1]) { /* EVPD bit set */ + } else if (0x1 & cmd[1]) { /* EVPD bit set */ + int dev_id_num, len; + char dev_id_str[6]; + + dev_id_num = ((devip->sdp->host->host_no + 1) * 1000) + + devip->sdp->id; + len = snprintf(dev_id_str, 6, "%d", dev_id_num); + len = (len > 6) ? 6 : len; if (0 == cmd[2]) { /* supported vital product data pages */ - arr[3] = 1; - arr[4] = 0x80; /* ... only unit serial number */ - } - else if (0x80 == cmd[2]) { /* unit serial number */ + arr[3] = 3; + arr[4] = 0x0; /* this page */ + arr[5] = 0x80; /* unit serial number */ + arr[6] = 0x83; /* device identification */ + } else if (0x80 == cmd[2]) { /* unit serial number */ arr[1] = 0x80; - arr[3] = 4; - arr[4] = '1'; arr[5] = '2'; arr[6] = '3'; - arr[7] = '4'; - } - else { + arr[3] = len; + memcpy(&arr[4], dev_id_str, len); + } else if (0x83 == cmd[2]) { /* device identification */ + arr[1] = 0x83; + arr[3] = inquiry_evpd_83(&arr[4], dev_id_num, + dev_id_str, len); + } else { /* Illegal request, invalid field in cdb */ - mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14); + mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14); return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); } memcpy(buff, arr, min_len); @@ -536,18 +502,18 @@ /* drops through here for a standard inquiry */ arr[1] = DEV_REMOVEABLE(target) ? 0x80 : 0; /* Removable disk */ arr[2] = 3; /* claim SCSI 3 */ - arr[4] = SDEBUG_MAX_INQ_SZ - 5; + arr[4] = SDEBUG_LONG_INQ_SZ - 5; arr[7] = 0x3a; /* claim: WBUS16, SYNC, LINKED + CMDQUE */ - memcpy(&arr[8], "Linux ", 8); - memcpy(&arr[16], "scsi_debug ", 16); - memcpy(&arr[32], "0003", 4); + memcpy(&arr[8], vendor_id, 8); + memcpy(&arr[16], product_id, 16); + memcpy(&arr[32], product_rev, 4); memcpy(buff, arr, min_len); return 0; } /* <> */ -static int sdebug_err_recov_pg(unsigned char * p, int pcontrol, int target) +static int resp_err_recov_pg(unsigned char * p, int pcontrol, int target) { /* Read-Write Error Recovery page for mode_sense */ unsigned char err_recov_pg[] = {0x1, 0xa, 0xc0, 11, 240, 0, 0, 0, 5, 0, 0xff, 0xff}; @@ -558,7 +524,7 @@ return sizeof(err_recov_pg); } -static int sdebug_disconnect_pg(unsigned char * p, int pcontrol, int target) +static int resp_disconnect_pg(unsigned char * p, int pcontrol, int target) { /* Disconnect-Reconnect page for mode_sense */ unsigned char disconnect_pg[] = {0x2, 0xe, 128, 128, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; @@ -569,7 +535,25 @@ return sizeof(disconnect_pg); } -static int sdebug_caching_pg(unsigned char * p, int pcontrol, int target) +static int resp_format_pg(unsigned char * p, int pcontrol, int target) +{ /* Format device page for mode_sense */ + unsigned char format_pg[] = {0x3, 0x16, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0x40, 0, 0, 0}; + + memcpy(p, format_pg, sizeof(format_pg)); + p[10] = (N_SECTOR >> 8) & 0xff; + p[11] = N_SECTOR & 0xff; + p[12] = (SECT_SIZE >> 8) & 0xff; + p[13] = SECT_SIZE & 0xff; + if (DEV_REMOVEABLE(target)) + p[20] |= 0x20; /* should agree with INQUIRY */ + if (1 == pcontrol) + memset(p + 2, 0, sizeof(format_pg) - 2); + return sizeof(format_pg); +} + +static int resp_caching_pg(unsigned char * p, int pcontrol, int target) { /* Caching page for mode_sense */ unsigned char caching_pg[] = {0x8, 18, 0x14, 0, 0xff, 0xff, 0, 0, 0xff, 0xff, 0xff, 0xff, 0x80, 0x14, 0, 0, 0, 0, 0, 0}; @@ -580,7 +564,7 @@ return sizeof(caching_pg); } -static int sdebug_ctrl_m_pg(unsigned char * p, int pcontrol, int target) +static int resp_ctrl_m_pg(unsigned char * p, int pcontrol, int target) { /* Control mode page for mode_sense */ unsigned char ctrl_m_pg[] = {0xa, 10, 2, 0, 0, 0, 0, 0, 0, 0, 0x2, 0x4b}; @@ -594,9 +578,9 @@ #define SDEBUG_MAX_MSENSE_SZ 256 -static int scsi_debug_mode_sense(unsigned char * cmd, int target, - unsigned char * buff, int bufflen, - Sdebug_dev_info * devip) +static int resp_mode_sense(unsigned char * cmd, int target, + unsigned char * buff, int bufflen, + struct sdebug_dev_info * devip) { unsigned char dbd; int pcontrol, pcode; @@ -622,15 +606,14 @@ memset(buff, 0, bufflen); memset(arr, 0, SDEBUG_MAX_MSENSE_SZ); if (0x3 == pcontrol) { /* Saving values not supported */ - mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x39, 0, 14); + mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x39, 0, 14); return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); } dev_spec = DEV_READONLY(target) ? 0x80 : 0x0; if (msense_6) { arr[2] = dev_spec; offset = 4; - } - else { + } else { arr[3] = dev_spec; offset = 8; } @@ -638,30 +621,34 @@ switch (pcode) { case 0x1: /* Read-Write error recovery page, direct access */ - len = sdebug_err_recov_pg(ap, pcontrol, target); + len = resp_err_recov_pg(ap, pcontrol, target); offset += len; break; case 0x2: /* Disconnect-Reconnect page, all devices */ - len = sdebug_disconnect_pg(ap, pcontrol, target); + len = resp_disconnect_pg(ap, pcontrol, target); offset += len; break; + case 0x3: /* Format device page, direct access */ + len = resp_format_pg(ap, pcontrol, target); + offset += len; + break; case 0x8: /* Caching page, direct access */ - len = sdebug_caching_pg(ap, pcontrol, target); + len = resp_caching_pg(ap, pcontrol, target); offset += len; break; case 0xa: /* Control Mode page, all devices */ - len = sdebug_ctrl_m_pg(ap, pcontrol, target); + len = resp_ctrl_m_pg(ap, pcontrol, target); offset += len; break; case 0x3f: /* Read all Mode pages */ - len = sdebug_err_recov_pg(ap, pcontrol, target); - len += sdebug_disconnect_pg(ap + len, pcontrol, target); - len += sdebug_caching_pg(ap + len, pcontrol, target); - len += sdebug_ctrl_m_pg(ap + len, pcontrol, target); + len = resp_err_recov_pg(ap, pcontrol, target); + len += resp_disconnect_pg(ap + len, pcontrol, target); + len += resp_caching_pg(ap + len, pcontrol, target); + len += resp_ctrl_m_pg(ap + len, pcontrol, target); offset += len; break; default: - mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x24, 0, 14); + mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14); return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); } if (msense_6) @@ -675,8 +662,8 @@ return 0; } -static int scsi_debug_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, - int num, int * errstsp, Sdebug_dev_info * devip) +static int resp_read(Scsi_Cmnd * SCpnt, int upper_blk, int block, int num, + struct sdebug_dev_info * devip) { unsigned char *buff = (unsigned char *) SCpnt->request_buffer; int nbytes, sgcount; @@ -685,30 +672,17 @@ unsigned long iflags; if (upper_blk || (block + num > CAPACITY)) { - *errstsp = (COMMAND_COMPLETE << 8) | - (CHECK_CONDITION << 1); - mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x21, 0, 14); - return 1; - } -#if defined(SCSI_SETUP_LATENCY) || defined(SCSI_DATARATE) - { - int delay = SCSI_SETUP_LATENCY; - - delay += SCpnt->request->nr_sectors * SCSI_DATARATE; - if (delay) - usleep(delay); + mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x21, 0, 14); + return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); } -#endif if ((SCSI_DEBUG_OPT_MEDIUM_ERR & scsi_debug_opts) && (block >= OPT_MEDIUM_ERR_ADDR) && (block < (OPT_MEDIUM_ERR_ADDR + num))) { - *errstsp = (COMMAND_COMPLETE << 8) | - (CHECK_CONDITION << 1); - mk_sense_buffer(devip, 1, MEDIUM_ERROR, 0x11, 0, 14); + mk_sense_buffer(devip, MEDIUM_ERROR, 0x11, 0, 14); /* claim unrecoverable read error */ - return 1; + return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); } - read_lock_irqsave(&sdebug_atomic_rw, iflags); + read_lock_irqsave(&atomic_rw, iflags); sgcount = 0; nbytes = bufflen; /* printk(KERN_INFO "scsi_debug_read: block=%d, tot_bufflen=%d\n", @@ -716,46 +690,29 @@ if (SCpnt->use_sg) { sgcount = 0; sgpnt = (struct scatterlist *) buff; - buff = sdebug_scatg2virt(&sgpnt[sgcount]); + buff = scatg2virt(&sgpnt[sgcount]); bufflen = sgpnt[sgcount].length; } - *errstsp = 0; do { memcpy(buff, fake_storep + (block * SECT_SIZE), bufflen); -#if 0 - /* Simulate a disk change */ - if (block == 0xfff0) { - sense_buffer[0] = 0x70; - sense_buffer[2] = UNIT_ATTENTION; - starts[0] += 10; - starts[1] += 10; - starts[2] += 10; - - *errstsp = (COMMAND_COMPLETE << 8) | - (CHECK_CONDITION << 1); - read_unlock_irqrestore(&sdebug_atomic_rw, iflags); - return 1; - } /* End phony disk change code */ -#endif nbytes -= bufflen; if (SCpnt->use_sg) { block += bufflen >> POW2_SECT_SIZE; sgcount++; if (nbytes) { - buff = sdebug_scatg2virt(&sgpnt[sgcount]); + buff = scatg2virt(&sgpnt[sgcount]); bufflen = sgpnt[sgcount].length; } - } - else if (nbytes > 0) - printk(KERN_WARNING "sdebug_read: unexpected " + } else if (nbytes > 0) + printk(KERN_WARNING "scsi_debug:resp_read: unexpected " "nbytes=%d\n", nbytes); } while (nbytes); - read_unlock_irqrestore(&sdebug_atomic_rw, iflags); + read_unlock_irqrestore(&atomic_rw, iflags); return 0; } -static int scsi_debug_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, - int num, int * errstsp, Sdebug_dev_info * devip) +static int resp_write(Scsi_Cmnd * SCpnt, int upper_blk, int block, int num, + struct sdebug_dev_info * devip) { unsigned char *buff = (unsigned char *) SCpnt->request_buffer; int nbytes, sgcount; @@ -764,22 +721,19 @@ unsigned long iflags; if (upper_blk || (block + num > CAPACITY)) { - *errstsp = (COMMAND_COMPLETE << 8) | - (CHECK_CONDITION << 1); - mk_sense_buffer(devip, 1, ILLEGAL_REQUEST, 0x21, 0, 14); - return 1; + mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x21, 0, 14); + return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); } - write_lock_irqsave(&sdebug_atomic_rw, iflags); + write_lock_irqsave(&atomic_rw, iflags); sgcount = 0; nbytes = bufflen; if (SCpnt->use_sg) { sgcount = 0; sgpnt = (struct scatterlist *) buff; - buff = sdebug_scatg2virt(&sgpnt[sgcount]); + buff = scatg2virt(&sgpnt[sgcount]); bufflen = sgpnt[sgcount].length; } - *errstsp = 0; do { memcpy(fake_storep + (block * SECT_SIZE), buff, bufflen); @@ -788,104 +742,64 @@ block += bufflen >> POW2_SECT_SIZE; sgcount++; if (nbytes) { - buff = sdebug_scatg2virt(&sgpnt[sgcount]); + buff = scatg2virt(&sgpnt[sgcount]); bufflen = sgpnt[sgcount].length; } - } - else if (nbytes > 0) - printk(KERN_WARNING "sdebug_write: " + } else if (nbytes > 0) + printk(KERN_WARNING "scsi_debug:resp_write: " "unexpected nbytes=%d\n", nbytes); } while (nbytes); - write_unlock_irqrestore(&sdebug_atomic_rw, iflags); + write_unlock_irqrestore(&atomic_rw, iflags); return 0; } -/* A "high" level interrupt handler. This should be called once per jiffy - * to simulate a regular scsi disk. We use a timer to do this. */ - -static void scsi_debug_intr_handle(unsigned long indx) +static int resp_report_luns(unsigned char * cmd, unsigned char * buff, + int bufflen, struct sdebug_dev_info * devip) { - Scsi_Cmnd *SCtmp; - void (*my_done) (Scsi_Cmnd *); -#if 0 - del_timer(&timeout[indx]); -#endif + unsigned int alloc_len; + int select_report = (int)cmd[2]; - SCtmp = (Scsi_Cmnd *) SCint[indx]; - my_done = do_done[indx]; - do_done[indx] = NULL; - timeout[indx].function = NULL; - SCint[indx] = NULL; - - if (!my_done) { - printk(KERN_ERR "scsi_debug_intr_handle: Unexpected " - "interrupt\n"); - return; + alloc_len = cmd[9] + (cmd[8] << 8) + (cmd[7] << 16) + (cmd[6] << 24); + if ((alloc_len < 16) || (select_report > 2)) { + mk_sense_buffer(devip, ILLEGAL_REQUEST, 0x24, 0, 14); + return (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1); } -#if 0 - printk(KERN_INFO "In intr_handle..."); - printk(KERN_INFO "...done %d %x %d %d\n", i, my_done, to, jiffies); - printk(KERN_INFO "In intr_handle: %d %x %x\n", i, SCtmp, my_done); -#endif - - my_done(SCtmp); -#if 0 - printk(KERN_INFO "Called done.\n"); -#endif + if (bufflen > 3) { + memset(buff, 0, bufflen); + buff[3] = 8; + } + return 0; } -static int initialized = 0; - -static int do_init(void) +/* When timer goes off this function is called. */ +static void timer_intr_handler(unsigned long indx) { - int sz = STORE_SIZE; - - starts[3] = CAPACITY; - fake_storep = vmalloc(sz); - if (NULL == fake_storep) - return 1; - memset(fake_storep, 0, sz); - - sz = sizeof(done_fct_t) * SCSI_DEBUG_MAILBOXES; - do_done = kmalloc(sz, GFP_ATOMIC); - if (NULL == do_done) - goto out; - memset((void *)do_done, 0, sz); - - sz = sizeof(struct timer_list) * SCSI_DEBUG_MAILBOXES; - timeout = kmalloc(sz, GFP_ATOMIC); - if (NULL == timeout) - goto out; - memset(timeout, 0, sz); - - sz = sizeof(Scsi_Cmnd *) * SCSI_DEBUG_MAILBOXES; - SCint = kmalloc(sz, GFP_ATOMIC); - if (NULL == SCint) - goto out; - memset(SCint, 0, sz); - - return 0; - -out: - if (fake_storep) - vfree(fake_storep); - if (do_done) - kfree((void *)do_done); - if (timeout) - kfree(timeout); - if (SCint) - kfree(SCint); - return 1; -} + struct sdebug_queued_cmd * sqcp; + unsigned int iflags; -static void do_end(void) -{ - kfree(SCint); - kfree(timeout); - kfree((void *)do_done); - vfree(fake_storep); + if (indx >= SCSI_DEBUG_CANQUEUE) { + printk(KERN_ERR "scsi_debug:timer_intr_handler: indx too " + "large\n"); + return; + } + spin_lock_irqsave(&queued_arr_lock, iflags); + sqcp = &queued_arr[(int)indx]; + if (! sqcp->in_use) { + printk(KERN_ERR "scsi_debug:timer_intr_handler: Unexpected " + "interrupt\n"); + spin_unlock_irqrestore(&queued_arr_lock, iflags); + return; + } + sqcp->in_use = 0; + if (sqcp->done_funct) + sqcp->done_funct(sqcp->a_cmnd); /* callback to mid level */ + sqcp->done_funct = NULL; + spin_unlock_irqrestore(&queued_arr_lock, iflags); } +static int initialized = 0; +static int num_present = 0; +static const char * sdebug_proc_name = "scsi_debug"; static int scsi_debug_detect(Scsi_Host_Template * tpnt) { @@ -895,28 +809,41 @@ printk(KERN_INFO "scsi_debug: detect\n"); if (0 == initialized) { ++initialized; - sz = sizeof(Sdebug_dev_info) * scsi_debug_num_devs; - devInfop = kmalloc(sz, GFP_ATOMIC); + sz = sizeof(struct sdebug_dev_info) * scsi_debug_num_devs; + devInfop = vmalloc(sz); if (NULL == devInfop) { printk(KERN_ERR "scsi_debug_detect: out of " "memory, 0.5\n"); return 0; } memset(devInfop, 0, sz); - if (do_init()) { + sz = STORE_SIZE; + fake_storep = vmalloc(sz); + if (NULL == fake_storep) { printk(KERN_ERR "scsi_debug_detect: out of memory" ", 0\n"); return 0; } - for (k = 0; k < NUM_SENSE_BUFFS; ++k) - sense_buffers[k][0] = 0x70; - for (k = 0; k < NR_HOSTS_PRESENT; k++) { - tpnt->proc_name = "scsi_debug"; /* In the loop??? */ - scsi_register(tpnt, 0); + memset(fake_storep, 0, sz); + init_all_queued(); + tpnt->proc_name = (char *)sdebug_proc_name; + for (num_present = 0, k = 0; k < NR_HOSTS_PRESENT; k++) { + if (NULL == scsi_register(tpnt, 0)) + printk(KERN_ERR "scsi_debug_detect: " + "scsi_register failed k=%d\n", k); + else + ++num_present; + } +#ifdef DRIVERFS_SUPPORT + if (num_present) { + sdebug_driverfs_driver.name = (char *)sdebug_proc_name; + sdebug_driverfs_driver.bus = &scsi_driverfs_bus_type; + driver_register(&sdebug_driverfs_driver); + do_create_driverfs_files(); } - return NR_HOSTS_PRESENT; - } - else { +#endif + return num_present; + } else { printk(KERN_WARNING "scsi_debug_detect: called again\n"); return 0; } @@ -929,51 +856,52 @@ { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: release\n"); + stop_all_queued(); scsi_unregister(hpnt); - if (++num_releases != NR_HOSTS_PRESENT) - return 0; - do_end(); - kfree(devInfop); + if (++num_releases == num_present) { +#ifdef DRIVERFS_SUPPORT + do_remove_driverfs_files(); + remove_driver(&sdebug_driverfs_driver); + // driver_unregister(&sdebug_driverfs_driver); +#endif + vfree(fake_storep); + vfree(devInfop); + } return 0; } -static Sdebug_dev_info * devInfoReg(Scsi_Device * sdp) +static struct sdebug_dev_info * devInfoReg(Scsi_Device * sdp) { int k; - unsigned short host_no, id; - Sdebug_dev_info * devip; + struct sdebug_dev_info * devip; - host_no = sdp->host->host_no; - id = (unsigned short)sdp->id; for (k = 0; k < scsi_debug_num_devs; ++k) { devip = &devInfop[k]; - if (devip->sdp && (host_no == devip->host_no) && - (id == devip->id)) { - devip->sdp = sdp; /* overwrite previous sdp */ + if (devip->sdp == sdp) return devip; - } + } + for (k = 0; k < scsi_debug_num_devs; ++k) { + devip = &devInfop[k]; if (NULL == devip->sdp) { devip->sdp = sdp; - devip->host_no = host_no; - devip->id = id; devip->reset = 1; - devip->sb_index = 0; + memset(devip->sense_buff, 0, SDEBUG_SENSE_LEN); + devip->sense_buff[0] = 0x70; return devip; } } return NULL; } -static void mk_sense_buffer(Sdebug_dev_info * devip, int index, int key, +static void mk_sense_buffer(struct sdebug_dev_info * devip, int key, int asc, int asq, int inbandLen) { - char * sbuff; - if ((index < 0) || (index >= NUM_SENSE_BUFFS)) - return; - if (devip) - devip->sb_index = index; - sbuff = &sense_buffers[index][0]; - memset(sbuff, 0, SENSE_BUFF_LEN); + unsigned char * sbuff; + + sbuff = devip->sense_buff; + memset(sbuff, 0, SDEBUG_SENSE_LEN); + if (inbandLen > SDEBUG_SENSE_LEN) + inbandLen = SDEBUG_SENSE_LEN; sbuff[0] = 0x70; sbuff[2] = key; sbuff[7] = (inbandLen > 7) ? (inbandLen - 8) : 0; @@ -985,30 +913,13 @@ { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: abort\n"); -#if 1 ++num_aborts; + stop_queued_cmnd(SCpnt); return SUCCESS; -#else - int j; - void (*my_done) (Scsi_Cmnd *); - unsigned long iflags; - SCpnt->result = SCpnt->abort_reason << 16; - for (j = 0; j < SCSI_DEBUG_MAILBOXES; j++) { - if (SCpnt == SCint[j]) { - my_done = do_done[j]; - my_done(SCpnt); - spin_lock_irqsave(&mailbox_lock, iflags); - timeout[j] = 0; - SCint[j] = NULL; - do_done[j] = NULL; - spin_unlock_irqrestore(&mailbox_lock, iflags); - } - } - return SCSI_ABORT_SNOOZE; -#endif } -static int scsi_debug_biosparam(Disk * disk, struct block_device *dev, int *info) +static int scsi_debug_biosparam(Disk * disk, struct block_device * bdev, + int *info) { if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) printk(KERN_INFO "scsi_debug: biosparam\n"); @@ -1067,12 +978,124 @@ ++num_host_resets; for (k = 0; k < scsi_debug_num_devs; ++k) devInfop[k].reset = 1; + stop_all_queued(); return SUCCESS; } +/* Returns 1 if found 'cmnd' and deleted its timer. else returns 0 */ +static int stop_queued_cmnd(struct scsi_cmnd * cmnd) +{ + unsigned long iflags; + int k; + struct sdebug_queued_cmd * sqcp; + + spin_lock_irqsave(&queued_arr_lock, iflags); + for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) { + sqcp = &queued_arr[k]; + if (sqcp->in_use && (cmnd == sqcp->a_cmnd)) { + del_timer_sync(&sqcp->cmnd_timer); + sqcp->in_use = 0; + sqcp->a_cmnd = NULL; + break; + } + } + spin_unlock_irqrestore(&queued_arr_lock, iflags); + return (k < SCSI_DEBUG_CANQUEUE) ? 1 : 0; +} + +/* Deletes (stops) timers of all queued commands */ +static void stop_all_queued(void) +{ + unsigned long iflags; + int k; + struct sdebug_queued_cmd * sqcp; + + spin_lock_irqsave(&queued_arr_lock, iflags); + for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) { + sqcp = &queued_arr[k]; + if (sqcp->in_use && sqcp->a_cmnd) { + del_timer_sync(&sqcp->cmnd_timer); + sqcp->in_use = 0; + sqcp->a_cmnd = NULL; + } + } + spin_unlock_irqrestore(&queued_arr_lock, iflags); +} + +/* Initializes timers in queued array */ +static void init_all_queued(void) +{ + unsigned long iflags; + int k; + struct sdebug_queued_cmd * sqcp; + + spin_lock_irqsave(&queued_arr_lock, iflags); + for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) { + sqcp = &queued_arr[k]; + init_timer(&sqcp->cmnd_timer); + sqcp->in_use = 0; + sqcp->a_cmnd = NULL; + } + spin_unlock_irqrestore(&queued_arr_lock, iflags); +} + +static int schedule_resp(struct scsi_cmnd * cmnd, + struct sdebug_dev_info * devip, + done_funct_t done, int scsi_result, int delta_jiff) +{ + int k, num; + + if (SCSI_DEBUG_OPT_NOISE & scsi_debug_opts) { + printk(KERN_INFO "scsi_debug: cmd "); + for (k = 0, num = cmnd->cmd_len; k < num; ++k) + printk("%02x ", (int)cmnd->cmnd[k]); + printk("result=0x%x\n", scsi_result); + } + if (cmnd && devip) { + /* simulate autosense by this driver */ + if (CHECK_CONDITION == status_byte(scsi_result)) + memcpy(cmnd->sense_buffer, devip->sense_buff, + (SCSI_SENSE_BUFFERSIZE > SDEBUG_SENSE_LEN) ? + SDEBUG_SENSE_LEN : SCSI_SENSE_BUFFERSIZE); + } + if (delta_jiff <= 0) { + if (cmnd) + cmnd->result = scsi_result; + if (done) + done(cmnd); + return 0; + } else { + unsigned long iflags; + int k; + struct sdebug_queued_cmd * sqcp = NULL; + + spin_lock_irqsave(&queued_arr_lock, iflags); + for (k = 0; k < SCSI_DEBUG_CANQUEUE; ++k) { + sqcp = &queued_arr[k]; + if (! sqcp->in_use) + break; + } + if (k >= SCSI_DEBUG_CANQUEUE) { + spin_unlock_irqrestore(&queued_arr_lock, iflags); + printk(KERN_WARNING "scsi_debug: can_queue exceeded\n"); + return 1; /* report busy to mid level */ + } + sqcp->in_use = 1; + sqcp->a_cmnd = cmnd; + sqcp->scsi_result = scsi_result; + sqcp->done_funct = done; + sqcp->cmnd_timer.function = timer_intr_handler; + sqcp->cmnd_timer.data = k; + sqcp->cmnd_timer.expires = jiffies + delta_jiff; + add_timer(&sqcp->cmnd_timer); + spin_unlock_irqrestore(&queued_arr_lock, iflags); + return 0; + } +} + #ifndef MODULE -static int __init scsi_debug_num_devs_setup(char *str) +static int __init num_devs_setup(char *str) { int tmp; @@ -1086,10 +1109,9 @@ return 0; } } +__setup("scsi_debug_num_devs=", num_devs_setup); -__setup("scsi_debug_num_devs=", scsi_debug_num_devs_setup); - -static int __init scsi_debug_dev_size_mb_setup(char *str) +static int __init dev_size_mb_setup(char *str) { int tmp; @@ -1104,10 +1126,9 @@ return 0; } } +__setup("scsi_debug_dev_size_mb=", dev_size_mb_setup); -__setup("scsi_debug_dev_size_mb=", scsi_debug_dev_size_mb_setup); - -static int __init scsi_debug_opts_setup(char *str) +static int __init opts_setup(char *str) { int tmp; @@ -1122,8 +1143,41 @@ return 0; } } +__setup("scsi_debug_opts=", opts_setup); + +static int __init every_nth_setup(char *str) +{ + int tmp; + + if (get_option(&str, &tmp) == 1) { + if (tmp > 0) + scsi_debug_every_nth = tmp; + return 1; + } else { + printk(KERN_INFO "scsi_debug_every_nth: usage " + "scsi_debug_every_nth=\n" + " timeout every nth command (when ...)\n"); + return 0; + } +} +__setup("scsi_debug_every_nth=", every_nth_setup); + +static int __init delay_setup(char *str) +{ + int tmp; + + if (get_option(&str, &tmp) == 1) { + scsi_debug_delay = tmp; + return 1; + } else { + printk(KERN_INFO "scsi_debug_delay: usage " + "scsi_debug_delay=\n" + " delay response jiffies\n"); + return 0; + } +} +__setup("scsi_debug_delay=", delay_setup); -__setup("scsi_debug_opts=", scsi_debug_opts_setup); #endif MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert"); @@ -1134,10 +1188,11 @@ MODULE_PARM_DESC(scsi_debug_dev_size_mb, "size in MB of ram shared by devs"); MODULE_PARM(scsi_debug_opts, "i"); MODULE_PARM_DESC(scsi_debug_opts, "1->noise, 2->medium_error, 4->..."); - -#ifdef MODULE_LICENSE +MODULE_PARM(scsi_debug_every_nth, "i"); +MODULE_PARM_DESC(scsi_debug_every_nth, "timeout every nth command(def=100)"); +MODULE_PARM(scsi_debug_delay, "i"); +MODULE_PARM_DESC(scsi_debug_delay, "# of jiffies to delay response(def=1)"); MODULE_LICENSE("GPL"); -#endif static char sdebug_info[256]; @@ -1172,17 +1227,21 @@ if (1 != sscanf(arr, "%d", &pos)) return -EINVAL; scsi_debug_opts = pos; + if (SCSI_DEBUG_OPT_EVERY_NTH & scsi_debug_opts) + scsi_debug_cmnd_count = 0; return length; } begin = 0; pos = len = sprintf(buffer, "scsi_debug adapter driver, %s\n" - "num_devs=%d, shared (ram) size=%d MB, opts=0x%x\n" - "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d\n" - "number of aborts=%d, device_reset=%d, bus_resets=%d, " + "num_devs=%d, shared (ram) size=%d MB, opts=0x%x, " + "every_nth=%d(curr:%d)\n" + "sector_size=%d bytes, cylinders=%d, heads=%d, sectors=%d, " + "delay=%d\nnumber of aborts=%d, device_reset=%d, bus_resets=%d, " "host_resets=%d\n", scsi_debug_version_str, scsi_debug_num_devs, - scsi_debug_dev_size_mb, scsi_debug_opts, SECT_SIZE, - N_CYLINDER, N_HEAD, N_SECTOR, + scsi_debug_dev_size_mb, scsi_debug_opts, scsi_debug_every_nth, + scsi_debug_cmnd_count, + SECT_SIZE, N_CYLINDER, N_HEAD, N_SECTOR, scsi_debug_delay, num_aborts, num_dev_resets, num_bus_resets, num_host_resets); if (pos < offset) { len = 0; @@ -1196,8 +1255,86 @@ return (len); } -/* Eventually this will go into an include file, but this will be later */ -static Scsi_Host_Template driver_template = SCSI_DEBUG_TEMPLATE; +#ifdef DRIVERFS_SUPPORT +static ssize_t sdebug_delay_read(struct device_driver * ddp, char * buf, + size_t count, loff_t off) +{ + return off ? 0 : snprintf(buf, count, "%d\n", scsi_debug_delay); +} -#include "scsi_module.c" +static ssize_t sdebug_delay_write(struct device_driver * ddp, + const char * buf, size_t count, loff_t off) +{ + int delay; + char work[20]; + if (off) + return 0; + if (1 == sscanf(buf, "%10s", work)) { + if ((1 == sscanf(work, "%d", &delay)) && (delay >= 0)) { + scsi_debug_delay = delay; + return count; + } + } + return -EINVAL; +} + +DRIVER_ATTR(delay, S_IRUGO | S_IWUSR, sdebug_delay_read, + sdebug_delay_write) + +static ssize_t sdebug_opts_read(struct device_driver * ddp, char * buf, + size_t count, loff_t off) +{ + return off ? 0 : snprintf(buf, count, "0x%x\n", scsi_debug_opts); +} + +static ssize_t sdebug_opts_write(struct device_driver * ddp, + const char * buf, size_t count, loff_t off) +{ + int opts; + char work[20]; + + if (off) + return 0; + if (1 == sscanf(buf, "%10s", work)) { + if (0 == strnicmp(work,"0x", 2)) { + if (1 == sscanf(&work[2], "%x", &opts)) + goto opts_done; + } else { + if (1 == sscanf(work, "%d", &opts)) + goto opts_done; + } + } + return -EINVAL; +opts_done: + scsi_debug_opts = opts; + return count; +} + +DRIVER_ATTR(opts, S_IRUGO | S_IWUSR, sdebug_opts_read, + sdebug_opts_write) + +static ssize_t sdebug_num_devs_read(struct device_driver * ddp, char * buf, + size_t count, loff_t off) +{ + return off ? 0 : snprintf(buf, count, "%d\n", scsi_debug_num_devs); +} + +DRIVER_ATTR(num_devs, S_IRUGO, sdebug_num_devs_read, NULL) + +static void do_create_driverfs_files() +{ + driver_create_file(&sdebug_driverfs_driver, &driver_attr_delay); + driver_create_file(&sdebug_driverfs_driver, &driver_attr_opts); + driver_create_file(&sdebug_driverfs_driver, &driver_attr_num_devs); +} + +static void do_remove_driverfs_files() +{ + driver_remove_file(&sdebug_driverfs_driver, &driver_attr_num_devs); + driver_remove_file(&sdebug_driverfs_driver, &driver_attr_opts); + driver_remove_file(&sdebug_driverfs_driver, &driver_attr_delay); +} +#endif + +#include "scsi_module.c" diff -Nru a/drivers/scsi/scsi_debug.h b/drivers/scsi/scsi_debug.h --- a/drivers/scsi/scsi_debug.h Fri Aug 16 14:34:57 2002 +++ b/drivers/scsi/scsi_debug.h Fri Aug 16 14:34:57 2002 @@ -1,12 +1,15 @@ #ifndef _SCSI_DEBUG_H #include +#include static int scsi_debug_detect(Scsi_Host_Template *); +static int scsi_debug_release(struct Scsi_Host *); /* static int scsi_debug_command(Scsi_Cmnd *); */ static int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done) (Scsi_Cmnd *)); -static int scsi_debug_abort(Scsi_Cmnd *); +static int scsi_debug_ioctl(Scsi_Device *, int, void *); static int scsi_debug_biosparam(Disk *, struct block_device *, int[]); +static int scsi_debug_abort(Scsi_Cmnd *); static int scsi_debug_bus_reset(Scsi_Cmnd *); static int scsi_debug_device_reset(Scsi_Cmnd *); static int scsi_debug_host_reset(Scsi_Cmnd *); @@ -20,29 +23,29 @@ /* * This driver is written for the lk 2.5 series */ -#define SCSI_DEBUG_CANQUEUE 255 +#define SCSI_DEBUG_CANQUEUE 255 /* needs to be >= 1 */ #define SCSI_DEBUG_MAX_CMD_LEN 16 -#define SCSI_DEBUG_TEMPLATE \ - {proc_info: scsi_debug_proc_info, \ - name: "SCSI DEBUG", \ - info: scsi_debug_info, \ - detect: scsi_debug_detect, \ - release: scsi_debug_release, \ - ioctl: scsi_debug_ioctl, \ - queuecommand: scsi_debug_queuecommand, \ - eh_abort_handler: scsi_debug_abort, \ - eh_bus_reset_handler: scsi_debug_bus_reset, \ - eh_device_reset_handler: scsi_debug_device_reset, \ - eh_host_reset_handler: scsi_debug_host_reset, \ - bios_param: scsi_debug_biosparam, \ - can_queue: SCSI_DEBUG_CANQUEUE, \ - this_id: 7, \ - sg_tablesize: 64, \ - cmd_per_lun: 3, \ - unchecked_isa_dma: 0, \ - use_clustering: ENABLE_CLUSTERING, \ -} +static Scsi_Host_Template driver_template = { + .proc_info = scsi_debug_proc_info, + .name = "SCSI DEBUG", + .info = scsi_debug_info, + .detect = scsi_debug_detect, + .release = scsi_debug_release, + .ioctl = scsi_debug_ioctl, + .queuecommand = scsi_debug_queuecommand, + .eh_abort_handler = scsi_debug_abort, + .eh_bus_reset_handler = scsi_debug_bus_reset, + .eh_device_reset_handler = scsi_debug_device_reset, + .eh_host_reset_handler = scsi_debug_host_reset, + .bios_param = scsi_debug_biosparam, + .can_queue = SCSI_DEBUG_CANQUEUE, + .this_id = 7, + .sg_tablesize = 64, + .cmd_per_lun = 3, + .unchecked_isa_dma = 0, + .use_clustering = ENABLE_CLUSTERING, +}; /* the name 'driver_template' is used by scsi_module.c */ #endif diff -Nru a/drivers/scsi/scsi_mid_low_api.txt b/drivers/scsi/scsi_mid_low_api.txt --- a/drivers/scsi/scsi_mid_low_api.txt Fri Aug 16 14:34:59 2002 +++ b/drivers/scsi/scsi_mid_low_api.txt Fri Aug 16 14:34:59 2002 @@ -34,12 +34,15 @@ level driver. For it to work a declaration like this is needed before it is included: static Scsi_Host_Template driver_template = DRIVER_TEMPLATE; - /* DRIVER_TEMPLATE should contain pointers to supported interface - functions. Scsi_Host_Template is defined in hosts.h */ #include "scsi_module.c" +In this case "DRIVER_TEMPLATE" is defined to be a structure initializer +that is placed in the driver header file by convention. It contains +pointers to supported interface functions and other values. +Scsi_Host_Template is defined in hosts.h . + The scsi_module.c assumes the name "driver_template" is appropriately -defined. It contains 2 functions: +defined. scsi_module.c contains 2 functions: 1) init_this_scsi_driver() called during builtin and module driver initialization: invokes mid level's scsi_register_host() 2) exit_this_scsi_driver() called during closedown: invokes @@ -68,7 +71,7 @@ /** * bios_param - fetch head, sector, cylinder info for a disk * @sdkp: pointer to disk structure (defined in sd.h) - * @dev: corresponds to dev_t of device file name (e.g. /dev/sdb) + * @bdev: pointer to block device context (defined in fs.h) * @params: three element array to place output: * params[0] number of heads * params[1] number of sectors @@ -267,6 +270,8 @@ * unsupported ioctl() 'cmd' numbers should return -ENOTTY. * However the mid level returns -EINVAL for unrecognized 'cmd' * numbers when this function is not supplied by the driver. + * Unfortunately some applications expect -EINVAL and react badly + * when -ENOTTY is returned; stick with -EINVAL. **/ int ioctl(Scsi_Device *sdp, int cmd, void *arg); @@ -304,7 +309,10 @@ * @scp: pointer to scsi command object * @done: function pointer to be invoked on completion * - * Returns 1 if the adapter is busy, else returns 0. + * Returns 1 if the adapter (host) is busy, else returns 0. One + * reason for an adapter to be busy is that the number + * of outstanding queued commands is already equal to + * Scsi_Host::can_queue . * * Required: if Scsi_Host::can_queue is ever non-zero * then this function is required. @@ -324,6 +332,9 @@ * return value should be generated by this function. However, in * this case, it should be placed in scp->result before this function * returns. + * If a status of CHECK CONDITION is placed in "result" when the + * 'done' callback is invoked, then the lower level driver should + * perform autosense and fill in the Scsi_Cmnd::sense_buffer array. **/ int queuecommand(Scsi_Cmnd * scp, void (*done)(Scsi_Cmnd *)); @@ -505,6 +516,28 @@ (e.g. per scsi device) may be possible by juggling locks in queuecommand(). +Autosense +========= +Autosense (or auto-sense) is defined in the SAM-2 document as "the +automatic return of sense data to the application client coincident +with the completion of a SCSI command" when a status of CHECK CONDITION +occurs. Lower level drivers should perform autosense. This should be +done when the lower level driver detects a CHECK CONDITION status by either: + a) instructing the SCSI protocol (e.g. SCSI Parallel Interface (SPI)) + to perform an extra data in phase on such responses + b) or, the lower level driver issuing a REQUEST SENSE command itself + +Either way, the mid level decides whether the lower level driver has +performed autosense by checking Scsi_Cmnd::sense_buffer[0] . If this +byte has an upper nibble of 7 then autosense is assumed to have taken +place. If it has another value (and this byte is initialized to 0 before +each command) then the mid level will issue a REQUEST SENSE command. + +In the presence of queued commands the "nexus" that maintains sense +buffer data from the command that failed until a following REQUEST SENSE +may get out of synchronization. This is why it is best for the lower +level driver to perform autosense. + Changes since lk 2.4 series =========================== @@ -514,6 +547,7 @@ The older error handling mechanism has been removed. This means the lower level interface functions abort() and reset() have been removed. +The Scsi_Host_Template::use_new_eh_code flag has been removed. In the 2.4 series the scsi subsystem configuration descriptions were aggregated with the configuration descriptions from all other Linux @@ -532,4 +566,4 @@ Douglas Gilbert dgilbert@interlog.com -27th April 2002 +13th August 2002 diff -Nru a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c --- a/drivers/scsi/scsi_scan.c Fri Aug 16 14:34:59 2002 +++ b/drivers/scsi/scsi_scan.c Fri Aug 16 14:34:59 2002 @@ -1,11 +1,28 @@ /* - * scsi_scan.c Copyright (C) 2000 Eric Youngdale + * scsi_scan.c * - * Bus scan logic. + * Copyright (C) 2000 Eric Youngdale, + * Copyright (C) 2002 Patrick Mansfield * - * This used to live in scsi.c, but that file was just a laundry basket - * full of misc stuff. This got separated out in order to make things - * clearer. + * The general scanning/probing algorithm is as follows, exceptions are + * made to it depending on device specific flags, compilation options, and + * global variable (boot or module load time) settings. + * + * A specific LUN is scanned via an INQUIRY command; if the LUN has a + * device attached, a Scsi_Device is allocated and setup for it. + * + * For every id of every channel on the given host: + * + * Scan LUN 0; if the target responds to LUN 0 (even if there is no + * device or storage attached to LUN 0): + * + * If LUN 0 has a device attached, allocate and setup a + * Scsi_Device for it. + * + * If target is SCSI-3 or up, issue a REPORT LUN, and scan + * all of the LUNs returned by the REPORT LUN; else, + * sequentially scan LUNs up until some maximum is reached, + * or a LUN is seen that cannot have a device attached to it. */ #include @@ -22,172 +39,180 @@ #endif /* - * Flags for irregular SCSI devices that need special treatment + * Flags for SCSI devices that need special treatment */ -#define BLIST_NOLUN 0x001 /* Don't scan for LUNs */ -#define BLIST_FORCELUN 0x002 /* Known to have LUNs, force sanning */ -#define BLIST_BORKEN 0x004 /* Flag for broken handshaking */ -#define BLIST_KEY 0x008 /* Needs to be unlocked by special command */ -#define BLIST_SINGLELUN 0x010 /* LUNs should better not be used in parallel */ +#define BLIST_NOLUN 0x001 /* Only scan LUN 0 */ +#define BLIST_FORCELUN 0x002 /* Known to have LUNs, force scanning */ +#define BLIST_BORKEN 0x004 /* Flag for broken handshaking */ +#define BLIST_KEY 0x008 /* unlock by special command */ +#define BLIST_SINGLELUN 0x010 /* Do not use LUNs in parallel */ #define BLIST_NOTQ 0x020 /* Buggy Tagged Command Queuing */ -#define BLIST_SPARSELUN 0x040 /* Non consecutive LUN numbering */ +#define BLIST_SPARSELUN 0x040 /* Non consecutive LUN numbering */ #define BLIST_MAX5LUN 0x080 /* Avoid LUNS >= 5 */ -#define BLIST_ISDISK 0x100 /* Treat as (removable) disk */ -#define BLIST_ISROM 0x200 /* Treat as (removable) CD-ROM */ -#define BLIST_LARGELUN 0x400 /* LUNs larger than 7 despite reporting as SCSI 2 */ -#define BLIST_INQUIRY_36 0x800 /* override additional length field */ -#define BLIST_INQUIRY_58 0x1000 /* ... for broken inquiry responses */ - -/* - * scan_scsis_single() return values. - */ -#define SCSI_SCAN_NO_RESPONSE 0 -#define SCSI_SCAN_DEVICE_PRESENT 1 -#define SCSI_SCAN_DEVICE_ADDED 2 - -static void print_inquiry(unsigned char *data); -static int scan_scsis_single(unsigned int channel, unsigned int dev, - unsigned int lun, int scsi_level, Scsi_Device ** SDpnt2, - struct Scsi_Host *shpnt, char *scsi_result); -static void scan_scsis_target(unsigned int channel, unsigned int dev, - Scsi_Device ** SDpnt2, struct Scsi_Host *shpnt, - char *scsi_result); -static int find_lun0_scsi_level(unsigned int channel, unsigned int dev, - struct Scsi_Host *shpnt); -static void scsi_load_identifier(Scsi_Device *SDpnt, Scsi_Request * SRpnt); +#define BLIST_ISROM 0x100 /* Treat as (removable) CD-ROM */ +#define BLIST_LARGELUN 0x200 /* LUNs past 7 on a SCSI-2 device */ +#define BLIST_INQUIRY_36 0x400 /* override additional length field */ +#define BLIST_INQUIRY_58 0x800 /* ... for broken inquiry responses */ struct dev_info { const char *vendor; const char *model; - const char *revision; /* Latest revision known to be bad. Not used yet */ + const char *revision; /* revision known to be bad, unused */ unsigned flags; }; /* - * This is what was previously known as the blacklist. The concept - * has been expanded so that we can specify other types of things we - * need to be aware of. + * device_list: devices that require settings that differ from the + * default, includes black-listed (broken) devices. */ -static struct dev_info device_list[] = -{ -/* The following devices are known not to tolerate a lun != 0 scan for - * one reason or another. Some will respond to all luns, others will - * lock up. - */ - {"Aashima", "IMAGERY 2400SP", "1.03", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ - {"CHINON", "CD-ROM CDS-431", "H42", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ - {"CHINON", "CD-ROM CDS-535", "Q14", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ - {"DENON", "DRD-25X", "V", BLIST_NOLUN}, /* Locks up if probed for lun != 0 */ - {"HITACHI", "DK312C", "CM81", BLIST_NOLUN}, /* Responds to all lun - dtg */ - {"HITACHI", "DK314C", "CR21", BLIST_NOLUN}, /* responds to all lun */ - {"IMS", "CDD521/10", "2.06", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */ - {"MAXTOR", "XT-3280", "PR02", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */ - {"MAXTOR", "XT-4380S", "B3C", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */ - {"MAXTOR", "MXT-1240S", "I1.2", BLIST_NOLUN}, /* Locks up when LUN>0 polled */ - {"MAXTOR", "XT-4170S", "B5A", BLIST_NOLUN}, /* Locks-up sometimes when LUN>0 polled. */ - {"MAXTOR", "XT-8760S", "B7B", BLIST_NOLUN}, /* guess what? */ - {"MEDIAVIS", "RENO CD-ROMX2A", "2.03", BLIST_NOLUN}, /* Responds to all lun */ - {"NEC", "CD-ROM DRIVE:841", "1.0", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */ - {"PHILIPS", "PCA80SC", "V4-2", BLIST_NOLUN}, /* Responds to all lun */ - {"RODIME", "RO3000S", "2.33", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ - {"SANYO", "CRD-250S", "1.20", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1 - * for aha152x controller, which causes - * SCSI code to reset bus.*/ - {"SEAGATE", "ST157N", "\004|j", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1 - * for aha152x controller, which causes - * SCSI code to reset bus.*/ - {"SEAGATE", "ST296", "921", BLIST_NOLUN}, /* Responds to all lun */ - {"SEAGATE", "ST1581", "6538", BLIST_NOLUN}, /* Responds to all lun */ +static struct dev_info device_list[] = { + /* + * The following devices are known not to tolerate a lun != 0 scan + * for one reason or another. Some will respond to all luns, + * others will lock up. + */ + {"Aashima", "IMAGERY 2400SP", "1.03", BLIST_NOLUN}, /* locks up */ + {"CHINON", "CD-ROM CDS-431", "H42", BLIST_NOLUN}, /* locks up */ + {"CHINON", "CD-ROM CDS-535", "Q14", BLIST_NOLUN}, /* locks up */ + {"DENON", "DRD-25X", "V", BLIST_NOLUN}, /* locks up */ + {"HITACHI", "DK312C", "CM81", BLIST_NOLUN}, /* responds to all lun */ + {"HITACHI", "DK314C", "CR21", BLIST_NOLUN}, /* responds to all lun */ + {"IMS", "CDD521/10", "2.06", BLIST_NOLUN}, /* locks up */ + {"MAXTOR", "XT-3280", "PR02", BLIST_NOLUN}, /* locks up */ + {"MAXTOR", "XT-4380S", "B3C", BLIST_NOLUN}, /* locks up */ + {"MAXTOR", "MXT-1240S", "I1.2", BLIST_NOLUN}, /* locks up */ + {"MAXTOR", "XT-4170S", "B5A", BLIST_NOLUN}, /* locks up */ + {"MAXTOR", "XT-8760S", "B7B", BLIST_NOLUN}, /* locks up */ + {"MEDIAVIS", "RENO CD-ROMX2A", "2.03", BLIST_NOLUN}, /* responds to all lun */ + {"NEC", "CD-ROM DRIVE:841", "1.0", BLIST_NOLUN},/* locks up */ + {"PHILIPS", "PCA80SC", "V4-2", BLIST_NOLUN}, /* responds to all lun */ + {"RODIME", "RO3000S", "2.33", BLIST_NOLUN}, /* locks up */ + /* + * The following causes a failed REQUEST SENSE on lun 1 for + * aha152x controller, which causes SCSI code to reset bus. + */ + {"SANYO", "CRD-250S", "1.20", BLIST_NOLUN}, + /* + * The following causes a failed REQUEST SENSE on lun 1 for + * aha152x controller, which causes SCSI code to reset bus. + */ + {"SEAGATE", "ST157N", "\004|j", BLIST_NOLUN}, + {"SEAGATE", "ST296", "921", BLIST_NOLUN}, /* responds to all lun */ + {"SEAGATE", "ST1581", "6538", BLIST_NOLUN}, /* responds to all lun */ {"SONY", "CD-ROM CDU-541", "4.3d", BLIST_NOLUN}, {"SONY", "CD-ROM CDU-55S", "1.0i", BLIST_NOLUN}, {"SONY", "CD-ROM CDU-561", "1.7x", BLIST_NOLUN}, - {"SONY", "CD-ROM CDU-8012", "*", BLIST_NOLUN}, - {"TANDBERG", "TDC 3600", "U07", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ - {"TEAC", "CD-R55S", "1.0H", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ - {"TEAC", "CD-ROM", "1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1 - * for seagate controller, which causes - * SCSI code to reset bus.*/ - {"TEAC", "MT-2ST/45S2-27", "RV M", BLIST_NOLUN}, /* Responds to all lun */ - {"TEXEL", "CD-ROM", "1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1 - * for seagate controller, which causes - * SCSI code to reset bus.*/ - {"QUANTUM", "LPS525S", "3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */ - {"QUANTUM", "PD1225S", "3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */ - {"QUANTUM", "FIREBALL ST4.3S", "0F0C", BLIST_NOLUN}, /* Locks up when polled for lun != 0 */ - {"MEDIAVIS", "CDR-H93MV", "1.31", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ - {"SANKYO", "CP525", "6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */ - {"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */ - {"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */ - {"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */ - {"YAMAHA", "CDR100", "1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ - {"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* Locks up if polled for lun != 0 extra reset */ - {"YAMAHA", "CRW8424S", "1.0", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ - {"YAMAHA", "CRW6416S", "1.0c", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ - {"MITSUMI", "CD-R CR-2201CS", "6119", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */ - {"RELISYS", "Scorpio", "*", BLIST_NOLUN}, /* responds to all LUN */ - {"MICROTEK", "ScanMaker II", "5.61", BLIST_NOLUN}, /* responds to all LUN */ - -/* - * Other types of devices that have special flags. - */ - {"SONY", "CD-ROM CDU-8001", "*", BLIST_BORKEN}, - {"TEXEL", "CD-ROM", "1.06", BLIST_BORKEN}, - {"IOMEGA", "Io20S *F", "*", BLIST_KEY}, - {"INSITE", "Floptical F*8I", "*", BLIST_KEY}, - {"INSITE", "I325VM", "*", BLIST_KEY}, - {"LASOUND","CDX7405","3.10", BLIST_MAX5LUN | BLIST_SINGLELUN}, - {"MICROP", "4110", "*", BLIST_NOTQ}, /* Buggy Tagged Queuing */ - {"NRC", "MBR-7", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, - {"NRC", "MBR-7.4", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, - {"REGAL", "CDC-4X", "*", BLIST_MAX5LUN | BLIST_SINGLELUN}, - {"NAKAMICH", "MJ-4.8S", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, - {"NAKAMICH", "MJ-5.16S", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, - {"PIONEER", "CD-ROM DRM-600", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, - {"PIONEER", "CD-ROM DRM-602X", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, - {"PIONEER", "CD-ROM DRM-604X", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, - {"EMULEX", "MD21/S2 ESDI", "*", BLIST_SINGLELUN}, - {"CANON", "IPUBJD", "*", BLIST_SPARSELUN}, - {"nCipher", "Fastness Crypto", "*", BLIST_FORCELUN}, - {"DEC","HSG80","*", BLIST_FORCELUN}, - {"COMPAQ","LOGICAL VOLUME","*", BLIST_FORCELUN}, - {"COMPAQ","CR3500","*", BLIST_FORCELUN}, - {"NEC", "PD-1 ODX654P", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, - {"MATSHITA", "PD-1", "*", BLIST_FORCELUN | BLIST_SINGLELUN}, - {"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN}, - {"TOSHIBA","CDROM","*", BLIST_ISROM}, - {"TOSHIBA","CD-ROM","*", BLIST_ISROM}, - {"MegaRAID", "LD", "*", BLIST_FORCELUN}, - {"DGC", "RAID", "*", BLIST_SPARSELUN}, // Dell PV 650F (tgt @ LUN 0) - {"DGC", "DISK", "*", BLIST_SPARSELUN}, // Dell PV 650F (no tgt @ LUN 0) - {"DELL", "PV660F", "*", BLIST_SPARSELUN}, - {"DELL", "PV660F PSEUDO", "*", BLIST_SPARSELUN}, - {"DELL", "PSEUDO DEVICE .", "*", BLIST_SPARSELUN}, // Dell PV 530F - {"DELL", "PV530F", "*", BLIST_SPARSELUN}, // Dell PV 530F - {"EMC", "SYMMETRIX", "*", BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_FORCELUN}, - {"HP", "A6189A", "*", BLIST_SPARSELUN | BLIST_LARGELUN}, // HP VA7400, by Alar Aun - {"CMD", "CRA-7280", "*", BLIST_SPARSELUN}, // CMD RAID Controller - {"CNSI", "G7324", "*", BLIST_SPARSELUN}, // Chaparral G7324 RAID - {"CNSi", "G8324", "*", BLIST_SPARSELUN}, // Chaparral G8324 RAID - {"Zzyzx", "RocketStor 500S", "*", BLIST_SPARSELUN}, - {"Zzyzx", "RocketStor 2000", "*", BLIST_SPARSELUN}, - {"SONY", "TSL", "*", BLIST_FORCELUN}, // DDS3 & DDS4 autoloaders - {"DELL", "PERCRAID", "*", BLIST_FORCELUN}, - {"HP", "NetRAID-4M", "*", BLIST_FORCELUN}, - {"ADAPTEC", "AACRAID", "*", BLIST_FORCELUN}, - {"ADAPTEC", "Adaptec 5400S", "*", BLIST_FORCELUN}, - {"COMPAQ", "MSA1000", "*", BLIST_FORCELUN}, - {"HP", "C1557A", "*", BLIST_FORCELUN}, - {"IBM", "AuSaV1S2", "*", BLIST_FORCELUN}, + {"SONY", "CD-ROM CDU-8012", NULL, BLIST_NOLUN}, + {"TANDBERG", "TDC 3600", "U07", BLIST_NOLUN}, /* locks up */ + {"TEAC", "CD-R55S", "1.0H", BLIST_NOLUN}, /* locks up */ + /* + * The following causes a failed REQUEST SENSE on lun 1 for + * seagate controller, which causes SCSI code to reset bus. + */ + {"TEAC", "CD-ROM", "1.06", BLIST_NOLUN}, + {"TEAC", "MT-2ST/45S2-27", "RV M", BLIST_NOLUN}, /* responds to all lun */ + /* + * The following causes a failed REQUEST SENSE on lun 1 for + * seagate controller, which causes SCSI code to reset bus. + */ + {"TEXEL", "CD-ROM", "1.06", BLIST_NOLUN}, + {"QUANTUM", "LPS525S", "3110", BLIST_NOLUN}, /* locks up */ + {"QUANTUM", "PD1225S", "3110", BLIST_NOLUN}, /* locks up */ + {"QUANTUM", "FIREBALL ST4.3S", "0F0C", BLIST_NOLUN}, /* locks up */ + {"MEDIAVIS", "CDR-H93MV", "1.31", BLIST_NOLUN}, /* locks up */ + {"SANKYO", "CP525", "6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */ + {"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */ + {"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */ + {"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */ + {"YAMAHA", "CDR100", "1.00", BLIST_NOLUN}, /* locks up */ + {"YAMAHA", "CDR102", "1.00", BLIST_NOLUN}, /* locks up */ + {"YAMAHA", "CRW8424S", "1.0", BLIST_NOLUN}, /* locks up */ + {"YAMAHA", "CRW6416S", "1.0c", BLIST_NOLUN}, /* locks up */ + {"MITSUMI", "CD-R CR-2201CS", "6119", BLIST_NOLUN}, /* locks up */ + {"RELISYS", "Scorpio", NULL, BLIST_NOLUN}, /* responds to all lun */ + {"MICROTEK", "ScanMaker II", "5.61", BLIST_NOLUN}, /* responds to all lun */ /* - * Must be at end of list... + * Other types of devices that have special flags. */ - {NULL, NULL, NULL} + {"SONY", "CD-ROM CDU-8001", NULL, BLIST_BORKEN}, + {"TEXEL", "CD-ROM", "1.06", BLIST_BORKEN}, + {"IOMEGA", "Io20S *F", NULL, BLIST_KEY}, + {"INSITE", "Floptical F*8I", NULL, BLIST_KEY}, + {"INSITE", "I325VM", NULL, BLIST_KEY}, + {"LASOUND", "CDX7405", "3.10", BLIST_MAX5LUN | BLIST_SINGLELUN}, + {"MICROP", "4110", NULL, BLIST_NOTQ}, + {"NRC", "MBR-7", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"NRC", "MBR-7.4", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"REGAL", "CDC-4X", NULL, BLIST_MAX5LUN | BLIST_SINGLELUN}, + {"NAKAMICH", "MJ-4.8S", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"NAKAMICH", "MJ-5.16S", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"PIONEER", "CD-ROM DRM-600", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"PIONEER", "CD-ROM DRM-602X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"PIONEER", "CD-ROM DRM-604X", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"EMULEX", "MD21/S2 ESDI", NULL, BLIST_SINGLELUN}, + {"CANON", "IPUBJD", NULL, BLIST_SPARSELUN}, + {"nCipher", "Fastness Crypto", NULL, BLIST_FORCELUN}, + {"DEC", "HSG80", NULL, BLIST_FORCELUN}, + {"COMPAQ", "LOGICAL VOLUME", NULL, BLIST_FORCELUN}, + {"COMPAQ", "CR3500", NULL, BLIST_FORCELUN}, + {"NEC", "PD-1 ODX654P", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"MATSHITA", "PD-1", NULL, BLIST_FORCELUN | BLIST_SINGLELUN}, + {"iomega", "jaz 1GB", "J.86", BLIST_NOTQ | BLIST_NOLUN}, + {"TOSHIBA", "CDROM", NULL, BLIST_ISROM}, + {"TOSHIBA", "CD-ROM", NULL, BLIST_ISROM}, + {"MegaRAID", "LD", NULL, BLIST_FORCELUN}, + {"DGC", "RAID", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, storage on LUN 0 */ + {"DGC", "DISK", NULL, BLIST_SPARSELUN}, /* Dell PV 650F, no storage on LUN 0 */ + {"DELL", "PV660F", NULL, BLIST_SPARSELUN}, + {"DELL", "PV660F PSEUDO", NULL, BLIST_SPARSELUN}, + {"DELL", "PSEUDO DEVICE .", NULL, BLIST_SPARSELUN}, /* Dell PV 530F */ + {"DELL", "PV530F", NULL, BLIST_SPARSELUN}, + {"EMC", "SYMMETRIX", NULL, BLIST_SPARSELUN | BLIST_LARGELUN | BLIST_FORCELUN}, + {"HP", "A6189A", NULL, BLIST_SPARSELUN | BLIST_LARGELUN}, /* HP VA7400 */ + {"CMD", "CRA-7280", NULL, BLIST_SPARSELUN}, /* CMD RAID Controller */ + {"CNSI", "G7324", NULL, BLIST_SPARSELUN}, /* Chaparral G7324 RAID */ + {"CNSi", "G8324", NULL, BLIST_SPARSELUN}, /* Chaparral G8324 RAID */ + {"Zzyzx", "RocketStor 500S", NULL, BLIST_SPARSELUN}, + {"Zzyzx", "RocketStor 2000", NULL, BLIST_SPARSELUN}, + {"SONY", "TSL", NULL, BLIST_FORCELUN}, /* DDS3 & DDS4 autoloaders */ + {"DELL", "PERCRAID", NULL, BLIST_FORCELUN}, + {"HP", "NetRAID-4M", NULL, BLIST_FORCELUN}, + {"ADAPTEC", "AACRAID", NULL, BLIST_FORCELUN}, + {"ADAPTEC", "Adaptec 5400S", NULL, BLIST_FORCELUN}, + {"COMPAQ", "MSA1000", NULL, BLIST_FORCELUN}, + {"HP", "C1557A", NULL, BLIST_FORCELUN}, + {"IBM", "AuSaV1S2", NULL, BLIST_FORCELUN}, }; -static char * scsi_null_device_strs = "nullnullnullnull"; +#define ALLOC_FAILURE_MSG KERN_ERR "%s: Allocation failure during" \ + " SCSI scanning, some SCSI devices might not be configured\n" + +/* + * Prefix values for the SCSI id's (stored in driverfs name field) + */ +#define SCSI_UID_SER_NUM 'S' +#define SCSI_UID_UNKNOWN 'Z' + +/* + * Return values of some of the scanning functions. + * + * SCSI_SCAN_NO_RESPONSE: no valid response received from the target, this + * includes allocation or general failures preventing IO from being sent. + * + * SCSI_SCAN_TARGET_PRESENT: target responded, but no device is available + * on the given LUN. + * + * SCSI_SCAN_LUN_PRESENT: target responded, and a device is available on a + * given LUN. + */ +#define SCSI_SCAN_NO_RESPONSE 0 +#define SCSI_SCAN_TARGET_PRESENT 1 +#define SCSI_SCAN_LUN_PRESENT 2 + +static char *scsi_null_device_strs = "nullnullnullnull"; -#define MAX_SCSI_LUNS 0xFFFFFFFF +#define MAX_SCSI_LUNS 512 #ifdef CONFIG_SCSI_MULTI_LUN static unsigned int max_scsi_luns = MAX_SCSI_LUNS; @@ -195,27 +220,10 @@ static unsigned int max_scsi_luns = 1; #endif -#ifdef CONFIG_SCSI_REPORT_LUNS -/* - * max_scsi_report_luns: the maximum number of LUNS that will be - * returned from the REPORT LUNS command. 8 times this value must - * be allocated. In theory this could be up to an 8 byte value, but - * in practice, the maximum number of LUNs suppored by any device - * is about 16k. - */ -static unsigned int max_scsi_report_luns = 128; -#endif - #ifdef MODULE - MODULE_PARM(max_scsi_luns, "i"); -MODULE_PARM_DESC(max_scsi_luns, "last scsi LUN (should be between 1 and 2^32-1)"); - -#ifdef CONFIG_SCSI_REPORT_LUNS -MODULE_PARM(max_scsi_report_luns, "i"); -MODULE_PARM_DESC(max_scsi_report_luns, "REPORT LUNS maximum number of LUNS received (should be between 1 and 16384)"); -#endif - +MODULE_PARM_DESC(max_scsi_luns, + "last scsi LUN (should be between 1 and 2^32-1)"); #else static int __init scsi_luns_setup(char *str) @@ -226,7 +234,7 @@ max_scsi_luns = tmp; return 1; } else { - printk("scsi_luns_setup : usage max_scsi_luns=n " + printk(KERN_WARNING "scsi_luns_setup: usage max_scsi_luns=n " "(n should be between 1 and 2^32-1)\n"); return 0; } @@ -234,8 +242,25 @@ __setup("max_scsi_luns=", scsi_luns_setup); +#endif + #ifdef CONFIG_SCSI_REPORT_LUNS -static int __init max_scsi_report_luns_setup(char *str) +/* + * max_scsi_report_luns: the maximum number of LUNS that will be + * returned from the REPORT LUNS command. 8 times this value must + * be allocated. In theory this could be up to an 8 byte value, but + * in practice, the maximum number of LUNs suppored by any device + * is about 16k. + */ +static unsigned int max_scsi_report_luns = 128; + +#ifdef MODULE +MODULE_PARM(max_scsi_report_luns, "i"); +MODULE_PARM_DESC(max_scsi_report_luns, + "REPORT LUNS maximum number of LUNS received (should be" + " between 1 and 16384)"); +#else +static int __init scsi_report_luns_setup(char *str) { unsigned int tmp; @@ -243,541 +268,1098 @@ max_scsi_report_luns = tmp; return 1; } else { - printk("scsi_report_luns_setup : usage max_scsi_report_luns=n " - "(n should be between 1 and 16384)\n"); + printk(KERN_WARNING "scsi_report_luns_setup: usage" + " max_scsi_report_luns=n (n should be between 1" + " and 16384)\n"); return 0; } } -__setup("max_scsi_report_luns=", max_scsi_report_luns_setup); -#endif /* CONFIG_SCSI_REPORT_LUNS */ - +__setup("max_scsi_report_luns=", scsi_report_luns_setup); +#endif #endif -#ifdef CONFIG_SCSI_REPORT_LUNS -/* - * Function: scsilun_to_int - * - * Purpose: Convert ScsiLun (8 byte LUN) to an int. - * - * Arguments: scsilun_pnt - pointer to a ScsiLun to be converted - * - * Lock status: None - * - * Returns: cpu ordered integer containing the truncated LUN value - * - * Notes: The ScsiLun is assumed to be four levels, with each level - * effectively containing a SCSI byte-ordered (big endidan) - * short; the addressing bits of each level are ignored (the - * highest two bits). For a description of the LUN format, post - * SCSI-3 see the SCSI Architecture Model, for SCSI-3 see the - * SCSI Controller Commands. - * - * Given a ScsiLun of: 0a 04 0b 03 00 00 00 00, this function - * returns the integer: 0x0b030a04 - */ -static int scsilun_to_int(ScsiLun *scsilun_pnt) +/** + * scsi_unlock_floptical - unlock device via a special MODE SENSE command + * @sreq: used to send the command + * @result: area to store the result of the MODE SENSE + * + * Description: + * Send a vendor specific MODE SENSE (not a MODE SELECT) command using + * @sreq to unlock a device, storing the (unused) results into result. + * Called for BLIST_KEY devices. + **/ +static void scsi_unlock_floptical(Scsi_Request *sreq, unsigned char *result) { - int i; - unsigned int lun; + Scsi_Device *sdscan = sreq->sr_device; + unsigned char scsi_cmd[MAX_COMMAND_SIZE]; - lun = 0; - for (i = 0; i < sizeof(lun); i += 2) - lun = lun | (((scsilun_pnt->scsi_lun[i] << 8) | - scsilun_pnt->scsi_lun[i + 1]) << (i * 8)); - return lun; + printk(KERN_NOTICE "scsi: unlocking floptical drive\n"); + scsi_cmd[0] = MODE_SENSE; + if (sdscan->scsi_level <= SCSI_2) + scsi_cmd[1] = (sdscan->lun << 5) & 0xe0; + else + scsi_cmd[1] = 0; + scsi_cmd[2] = 0x2e; + scsi_cmd[3] = 0; + scsi_cmd[4] = 0x2a; /* size */ + scsi_cmd[5] = 0; + sreq->sr_cmd_len = 0; + sreq->sr_data_direction = SCSI_DATA_READ; + scsi_wait_req(sreq, (void *) scsi_cmd, (void *) result, 0x2a /* size */, + SCSI_TIMEOUT, 3); } -#endif -/* Driverfs file content handlers */ -static ssize_t scsi_device_type_read(struct device *driverfs_dev, char *page, +/** + * scsi_device_type_read - copy out the SCSI type + * @driverfs_dev: driverfs device to check + * @page: copy data into this area + * @count: number of bytes to copy + * @off: start at this offset in page + * + * Description: + * Called via driverfs when the "type" (in scsi_device_type_file) + * field is read. Copy the appropriate SCSI type string into @page, + * followed by a newline and a '\0'. Go through gyrations so we don't + * write more than @count, and we don't write past @off. + * + * Notes: + * This is for the top-most scsi entry in driverfs, the upper-level + * drivers have their own type file. XXX This is not part of scanning, + * other than we reference the attr struct in this file, move to + * scsi.c or scsi_lib.c. + * + * Return: + * number of bytes written into page. + **/ +static ssize_t scsi_device_type_read(struct device *driverfs_dev, char *page, size_t count, loff_t off) { - struct scsi_device *SDpnt = to_scsi_device(driverfs_dev); - - if ((SDpnt->type <= MAX_SCSI_DEVICE_CODE) && - (scsi_device_types[(int)SDpnt->type] != NULL)) - return off ? 0 : - sprintf(page, "%s\n", - scsi_device_types[(int)SDpnt->type]); + struct scsi_device *sdev = to_scsi_device(driverfs_dev); + const char *type; + size_t size, len; + + if ((sdev->type > MAX_SCSI_DEVICE_CODE) || + (scsi_device_types[(int)sdev->type] == NULL)) + type = "Unknown"; else - return off ? 0 : sprintf(page, "UNKNOWN\n"); - - return 0; + type = scsi_device_types[(int)sdev->type]; + size = strlen(type); + /* + * Check if off is past size + 1 for newline + 1 for a '\0'. + */ + if (off >= (size + 2)) + return 0; + if (size > off) { + len = min((size_t) (size - off), count); + memcpy(page + off, type + off, len); + } else + len = 0; + if (((len + off) == size) && (len < count)) + /* + * We are at the end of the string and have space, add a + * new line. + */ + *(page + off + len++) = '\n'; + if (((len + off) == (size + 1)) && (len < count)) + /* + * We are past the newline and have space, add a + * terminating '\0'. + */ + *(page + off + len++) = '\0'; + return len; } -static DEVICE_ATTR(type,"type",S_IRUGO,scsi_device_type_read,NULL); -/* end content handlers */ +/* + * Create dev_attr_type. This is different from the dev_attr_type in scsi + * upper level drivers. + */ +static DEVICE_ATTR(type,S_IRUGO,scsi_device_type_read,NULL); -static void print_inquiry(unsigned char *data) + +/** + * print_inquiry - printk the inquiry information + * @inq_result: printk this SCSI INQUIRY + * + * Description: + * printk the vendor, model, and other information found in the + * INQUIRY data in @inq_result. + * + * Notes: + * Remove this, and replace with a hotplug event that logs any + * relevant information. + **/ +static void print_inquiry(unsigned char *inq_result) { int i; - printk(" Vendor: "); - for (i = 8; i < 16; i++) { - if (data[i] >= 0x20 && i < data[4] + 5) - printk("%c", data[i]); + printk(KERN_NOTICE " Vendor: "); + for (i = 8; i < 16; i++) + if (inq_result[i] >= 0x20 && i < inq_result[4] + 5) + printk("%c", inq_result[i]); else printk(" "); - } printk(" Model: "); - for (i = 16; i < 32; i++) { - if (data[i] >= 0x20 && i < data[4] + 5) - printk("%c", data[i]); + for (i = 16; i < 32; i++) + if (inq_result[i] >= 0x20 && i < inq_result[4] + 5) + printk("%c", inq_result[i]); else printk(" "); - } printk(" Rev: "); - for (i = 32; i < 36; i++) { - if (data[i] >= 0x20 && i < data[4] + 5) - printk("%c", data[i]); + for (i = 32; i < 36; i++) + if (inq_result[i] >= 0x20 && i < inq_result[4] + 5) + printk("%c", inq_result[i]); else printk(" "); - } printk("\n"); - i = data[0] & 0x1f; + i = inq_result[0] & 0x1f; - printk(" Type: %s ", - i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : "Unknown "); - printk(" ANSI SCSI revision: %02x", data[2] & 0x07); - if ((data[2] & 0x07) == 1 && (data[3] & 0x0f) == 1) + printk(KERN_NOTICE " Type: %s ", + i < + MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : + "Unknown "); + printk(" ANSI SCSI revision: %02x", + inq_result[2] & 0x07); + if ((inq_result[2] & 0x07) == 1 && (inq_result[3] & 0x0f) == 1) printk(" CCS\n"); else printk("\n"); } -static int get_device_flags(unsigned char *vendor_pnt, unsigned char *model_pnt) +/** + * get_device_flags - get device specific flags from the device_list + * @vendor: vendor name + * @model: model name + * + * Description: + * Search device_list for an entry matching @vendor and @model, if + * found, return the matching flags value, else return 0. + * Partial matches count as success - good for @model, but maybe not + * @vendor. + **/ +static int get_device_flags(unsigned char *vendor, unsigned char *model) { - int i = 0; - for (i = 0; 1; i++) { - if (device_list[i].vendor == NULL) - return 0; - while (*vendor_pnt && *vendor_pnt == ' ') - vendor_pnt++; - if (memcmp(device_list[i].vendor, vendor_pnt, - strlen(device_list[i].vendor))) + int i; + size_t max; + + for (i = 0; i < ARRAY_SIZE(device_list); i++) { + /* + * XXX why skip leading spaces? If an odd INQUIRY value, + * that should have been part of the device_list[] entry, + * such as " FOO" rather than "FOO". Since this code is + * already here, and we don't know what device it is + * trying to work with, leave it as-is. + */ + max = 8; /* max length of vendor */ + while ((max > 0) && *vendor == ' ') { + max--; + vendor++; + } + /* + * XXX removing the following strlen() would be good, + * using it means that for a an entry not in the list, we + * scan every byte of every vendor listed in + * device_list[], and never match a single one (and still + * have to compare at least the first byte of each + * vendor). + */ + if (memcmp(device_list[i].vendor, vendor, + min(max, strlen(device_list[i].vendor)))) continue; - while (*model_pnt && *model_pnt == ' ') - model_pnt++; - if (memcmp(device_list[i].model, model_pnt, - strlen(device_list[i].model))) + /* + * Skip spaces again. + */ + max = 16; /* max length of model */ + while ((max > 0) && *model == ' ') { + max--; + model++; + } + if (memcmp(device_list[i].model, model, + min(max, strlen(device_list[i].model)))) continue; return device_list[i].flags; } return 0; } -/* - * Detecting SCSI devices : - * We scan all present host adapter's busses, from ID 0 to ID (max_id). - * We use the INQUIRY command, determine device type, and pass the ID / - * lun address of all sequential devices to the tape driver, all random - * devices to the disk driver. - */ -void scan_scsis(struct Scsi_Host *shpnt, - uint hardcoded, - uint hchannel, - uint hid, - uint hlun) +/** + * scsi_alloc_sdev - allocate and setup a Scsi_Device + * + * Description: + * Allocate, initialize for io, and return a pointer to a Scsi_Device. + * Stores the @shost, @channel, @id, and @lun in the Scsi_Device, and + * adds Scsi_Device to the appropriate list. + * + * Return value: + * Scsi_Device pointer, or NULL on failure. + **/ +static Scsi_Device *scsi_alloc_sdev(struct Scsi_Host *shost, uint channel, + uint id, uint lun) { - uint channel; - unsigned int dev; - unsigned int lun; - unsigned char *scsi_result; - unsigned char scsi_result0[256]; - Scsi_Device *SDpnt; - Scsi_Device *SDtail; - - scsi_result = NULL; - - SDpnt = (Scsi_Device *) kmalloc(sizeof(Scsi_Device), - GFP_ATOMIC); - if (SDpnt) { - memset(SDpnt, 0, sizeof(Scsi_Device)); - SDpnt->vendor = scsi_null_device_strs; - SDpnt->model = scsi_null_device_strs; - SDpnt->rev = scsi_null_device_strs; - /* - * Register the queue for the device. All I/O requests will - * come in through here. We also need to register a pointer to - * ourselves, since the queue handler won't know what device - * the queue actually represents. We could look it up, but it - * is pointless work. - */ - scsi_initialize_queue(SDpnt, shpnt); - SDpnt->request_queue.queuedata = (void *) SDpnt; - /* Make sure we have something that is valid for DMA purposes */ - scsi_result = ((!shpnt->unchecked_isa_dma) - ? &scsi_result0[0] : kmalloc(512, GFP_DMA)); + Scsi_Device *sdev; + + sdev = (Scsi_Device *) kmalloc(sizeof(Scsi_Device), GFP_ATOMIC); + if (sdev == NULL) + printk(ALLOC_FAILURE_MSG, __FUNCTION__); + else { + memset(sdev, 0, sizeof(Scsi_Device)); + sdev->vendor = scsi_null_device_strs; + sdev->model = scsi_null_device_strs; + sdev->rev = scsi_null_device_strs; + sdev->host = shost; + sdev->id = id; + sdev->lun = lun; + sdev->channel = channel; + sdev->online = TRUE; + /* + * Some low level driver could use device->type + */ + sdev->type = -1; + /* + * Assume that the device will have handshaking problems, + * and then fix this field later if it turns out it + * doesn't + */ + sdev->borken = 1; + scsi_initialize_queue(sdev, shost); + sdev->request_queue.queuedata = (void *) sdev; + + scsi_initialize_merge_fn(sdev); + init_waitqueue_head(&sdev->scpnt_wait); + + /* + * Add it to the end of the shost->host_queue list. + */ + if (shost->host_queue != NULL) { + sdev->prev = shost->host_queue; + while (sdev->prev->next != NULL) + sdev->prev = sdev->prev->next; + sdev->prev->next = sdev; + } else + shost->host_queue = sdev; + } + return (sdev); +} - if (scsi_result == NULL) { - printk("Unable to obtain scsi_result buffer\n"); - goto leave; +/** + * scsi_free_sdev - cleanup and free a Scsi_Device + * @sdev: cleanup and free this Scsi_Device + * + * Description: + * Undo the actions in scsi_alloc_sdev, including removing @sdev from + * the list, and freeing @sdev. + **/ +static void scsi_free_sdev(Scsi_Device *sdev) +{ + if (sdev->prev != NULL) + sdev->prev->next = sdev->next; + else + sdev->host->host_queue = sdev->next; + if (sdev->next != NULL) + sdev->next->prev = sdev->prev; + + blk_cleanup_queue(&sdev->request_queue); + if (sdev->inquiry != NULL) + kfree(sdev->inquiry); + kfree(sdev); +} + +/** + * scsi_check_id_size - check if size fits in the driverfs name + * @sdev: Scsi_Device to use for error message + * @size: the length of the id we want to store + * + * Description: + * Use a function for this since the same code is used in various + * places, and we only create one string and call to printk. + * + * Return: + * 0 - fits + * 1 - size too large + **/ +static int scsi_check_id_size(Scsi_Device *sdev, int size) +{ + if (size > DEVICE_NAME_SIZE) { + printk(KERN_WARNING "scsi scan: host %d channel %d id %d lun %d" + " identifier too long, length %d, max %d. Device might" + " be improperly identified.\n", sdev->host->host_no, + sdev->channel, sdev->id, sdev->lun, size, + DEVICE_NAME_SIZE); + return 1; + } else + return 0; +} + +/** + * scsi_get_evpd_page - get a list of supported vpd pages + * @sdev: Scsi_Device to send an INQUIRY VPD + * @sreq: Scsi_Request associated with @sdev + * + * Description: + * Get SCSI INQUIRY Vital Product Data page 0 - a list of supported + * VPD pages. + * + * Return: + * A pointer to data containing the results on success, else NULL. + **/ +unsigned char *scsi_get_evpd_page(Scsi_Device *sdev, Scsi_Request *sreq) +{ + unsigned char *evpd_page; + unsigned char scsi_cmd[MAX_COMMAND_SIZE]; + int lun = sdev->lun; + int scsi_level = sdev->scsi_level; + int max_lgth = 255; + +retry: + evpd_page = kmalloc(max_lgth, GFP_ATOMIC | + (sdev->host->unchecked_isa_dma) ? + GFP_DMA : 0); + if (!evpd_page) { + printk(KERN_WARNING "scsi scan: Allocation failure identifying" + " host %d channel %d id %d lun %d, device might be" + " improperly identified.\n", sdev->host->host_no, + sdev->channel, sdev->id, sdev->lun); + return NULL; } + + memset(scsi_cmd, 0, MAX_COMMAND_SIZE); + scsi_cmd[0] = INQUIRY; + if ((lun > 0) && (scsi_level <= SCSI_2)) + scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; + else + scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ + scsi_cmd[4] = max_lgth; + sreq->sr_cmd_len = 0; + sreq->sr_sense_buffer[0] = 0; + sreq->sr_sense_buffer[2] = 0; + sreq->sr_data_direction = SCSI_DATA_READ; + scsi_wait_req(sreq, (void *) scsi_cmd, (void *) evpd_page, + max_lgth, SCSI_TIMEOUT+4*HZ, 3); + + if (sreq->sr_result) { + kfree(evpd_page); + return NULL; + } + /* - * We must chain ourself in the host_queue, so commands can time out + * check to see if response was truncated */ - SDpnt->queue_depth = 1; - SDpnt->host = shpnt; - SDpnt->online = TRUE; + if (evpd_page[3] > max_lgth) { + max_lgth = evpd_page[3] + 4; + kfree(evpd_page); + goto retry; + } - scsi_initialize_merge_fn(SDpnt); + /* + * Some ill behaved devices return the standard inquiry here + * rather than the evpd data, snoop the data to verify. + */ + if (evpd_page[3] > 16) { + /* + * If the vendor id appears in the evpd page assume the + * page is invalid. + */ + if (!strncmp(&evpd_page[8], sdev->vendor, 8)) { + kfree(evpd_page); + return NULL; + } + } + return evpd_page; +} - /* - * Initialize the object that we will use to wait for command blocks. - */ - init_waitqueue_head(&SDpnt->scpnt_wait); + +/* + * INQUIRY VPD page 0x83 identifier descriptor related values. Reference the + * SCSI Primary Commands specification for details. + * + * XXX The following defines should be in scsi.h + */ + +/* + * id type values of id descriptors. These are assumed to fit in 4 bits, + * else the code using hex_str[id_type] needs modification. + */ +#define SCSI_ID_VENDOR_SPECIFIC 0 +#define SCSI_ID_T10_VENDOR 1 +#define SCSI_ID_EUI_64 2 +#define SCSI_ID_NAA 3 + +/* + * Supported NAA values. These fit in 4 bits, so the don't care value + * cannot conflict with real values. + * + */ +#define SCSI_ID_NAA_DONT_CARE 0xff +#define SCSI_ID_NAA_IEEE_REG 5 +#define SCSI_ID_NAA_IEEE_REG_EXTENDED 6 + +/* + * Supported Code Set values. + */ +#define SCSI_ID_BINARY 1 +#define SCSI_ID_ASCII 2 + +/* + * Use a priority based list of id, naa, and binary/ascii for the + * identifier descriptor in VPD page 0x83. + * + * Brute force search for a match starting with the first value in + * id_search_list. This is not a performance issue, since there + * is normally one or some small number of descriptors. + */ +struct scsi_id_search_values { + int id_type; + int naa_type; + int code_set; +}; + +static const struct scsi_id_search_values id_search_list[] = { + { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_BINARY }, + { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG_EXTENDED, SCSI_ID_ASCII }, + { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_BINARY }, + { SCSI_ID_NAA, SCSI_ID_NAA_IEEE_REG, SCSI_ID_ASCII }, + /* + * Devices already exist using NAA values that are now marked + * reserved. These should not conflict with other values, or it is + * a bug in the device. As long as we find the IEEE extended one + * first, we really don't care what other ones are used. Using + * don't care here means that a device that returns multiple + * non-IEEE descriptors in a random order will get different + * names. + */ + { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_NAA, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, + { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_EUI_64, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, + { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_T10_VENDOR, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, + { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_BINARY }, + { SCSI_ID_VENDOR_SPECIFIC, SCSI_ID_NAA_DONT_CARE, SCSI_ID_ASCII }, +}; + +/** + * scsi_check_fill_deviceid - check the id and if OK fill it + * @sdev: device to use for error messages + * @id_page: id descriptor for INQUIRY VPD DEVICE ID, page 0x83 + * @name: store the id in name + * @id_search: store if the id_page matches these values + * + * Description: + * Check if @id_page matches the @id_search, if so store an id (uid) + * into name. + * + * Return: + * 0: Success + * 1: No match + * 2: Failure due to size constraints + **/ +static int scsi_check_fill_deviceid(Scsi_Device *sdev, char *id_page, + char *name, const struct scsi_id_search_values *id_search) +{ + static const char hex_str[]="0123456789abcdef"; + int i, j; /* - * Next, hook the device to the host in question. + * ASSOCIATION must be with the device (value 0) */ - SDpnt->prev = NULL; - SDpnt->next = NULL; - if (shpnt->host_queue != NULL) { - SDtail = shpnt->host_queue; - while (SDtail->next != NULL) - SDtail = SDtail->next; + if ((id_page[1] & 0x30) != 0) + return 1; - SDtail->next = SDpnt; - SDpnt->prev = SDtail; - } else { - shpnt->host_queue = SDpnt; + if ((id_page[1] & 0x0f) != id_search->id_type) + return 1; + /* + * Possibly check NAA sub-type. + */ + if ((id_search->naa_type != SCSI_ID_NAA_DONT_CARE) && + (id_search->naa_type != (id_page[4] & 0xf0) >> 4)) { + return 1; } /* - * We need to increment the counter for this one device so we can track - * when things are quiet. + * Check for matching code set - ASCII or BINARY. */ - if (hardcoded == 1) { - Scsi_Device *oldSDpnt = SDpnt; - struct Scsi_Device_Template *sdtpnt; - unsigned int lun0_sl; - - channel = hchannel; - if (channel > shpnt->max_channel) - goto leave; - dev = hid; - if (dev >= shpnt->max_id) - goto leave; - lun = hlun; - if (lun >= shpnt->max_lun) - goto leave; - if ((0 == lun) || (lun > 7)) - lun0_sl = SCSI_3; /* actually don't care for 0 == lun */ - else - lun0_sl = find_lun0_scsi_level(channel, dev, shpnt); - scan_scsis_single(channel, dev, lun, lun0_sl, &SDpnt, shpnt, - scsi_result); - if (SDpnt != oldSDpnt) { - - /* it could happen the blockdevice hasn't yet been inited */ - /* queue_depth() moved from scsi_proc_info() so that - it is called before scsi_build_commandblocks() */ - if (shpnt->select_queue_depths != NULL) - (shpnt->select_queue_depths)(shpnt, - shpnt->host_queue); - - for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) - if (sdtpnt->init && sdtpnt->dev_noticed) - (*sdtpnt->init) (); - - for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) { - if (sdtpnt->attach) { - (*sdtpnt->attach) (oldSDpnt); - if (oldSDpnt->attached) { - scsi_build_commandblocks(oldSDpnt); - if (0 == oldSDpnt->has_cmdblocks) { - printk("scan_scsis: DANGER, no command blocks\n"); - /* What to do now ?? */ - } - } - } - } - for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) { - if (sdtpnt->finish && sdtpnt->nr_dev) { - (*sdtpnt->finish) (); - } - } - } - } else { - /* Actual LUN. PC ordering is 0->n IBM/spec ordering is n->0 */ - int order_dev; - - for (channel = 0; channel <= shpnt->max_channel; channel++) { - for (dev = 0; dev < shpnt->max_id; ++dev) { - if (shpnt->reverse_ordering) - /* Shift to scanning 15,14,13... or 7,6,5,4, */ - order_dev = shpnt->max_id - dev - 1; - else - order_dev = dev; + if ((id_page[0] & 0x0f) != id_search->code_set) + return 1; - if (shpnt->this_id != order_dev) { - scan_scsis_target(channel, order_dev, - &SDpnt, shpnt, scsi_result); - } + name[0] = hex_str[id_search->id_type]; + if ((id_page[0] & 0x0f) == SCSI_ID_ASCII) { + /* + * ASCII descriptor. + */ + if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) { + /* + * Prepend the vendor and model before the id, + * since the id might not be unique across all + * vendors and models. The same code is used + * below, with a differnt size. + * + * Need 1 byte for the idtype, 1 for trailing + * '\0', 8 for vendor, 16 for model total 26, plus + * the name descriptor length. + */ + if (scsi_check_id_size(sdev, 26 + id_page[3])) + return 2; + else { + strncat(name, sdev->vendor, 8); + strncat(name, sdev->model, 16); } + } else if (scsi_check_id_size (sdev, (2 + id_page[3]))) + /* + * Need 1 byte for the idtype, 1 byte for + * the trailing '\0', plus the descriptor length. + */ + return 2; + memcpy(&name[strlen(name)], &id_page[4], id_page[3]); + return 0; + } else if ((id_page[0] & 0x0f) == SCSI_ID_BINARY) { + if (id_search->id_type == SCSI_ID_VENDOR_SPECIFIC) { + /* + * Prepend the vendor and model before the id. + */ + if (scsi_check_id_size(sdev, 26 + (id_page[3] * 2))) + return 2; + else { + strncat(name, sdev->vendor, 8); + strncat(name, sdev->model, 16); + } + } else if (scsi_check_id_size(sdev, 2 + (id_page[3] * 2))) + /* + * Need 1 byte for the idtype, 1 for trailing + * '\0', 8 for vendor, 16 for model total 26, plus + * the name descriptor length. + */ + return 2; + /* + * Binary descriptor, convert to ASCII, using two bytes of + * ASCII for each byte in the id_page. Store starting at + * the end of name. + */ + for(i = 4, j = strlen(name); i < 4 + id_page[3]; i++) { + name[j++] = hex_str[(id_page[i] & 0xf0) >> 4]; + name[j++] = hex_str[id_page[i] & 0x0f]; } - } /* if/else hardcoded */ - - leave: + return 0; + } + /* + * Code set must have already matched. + */ + printk(KERN_ERR "scsi scan: scsi_check_fill_deviceid unexpected state.\n"); + return 1; +} - { /* Unchain SRpnt from host_queue */ - Scsi_Device *prev, *next; - Scsi_Device *dqptr; +/** + * scsi_get_deviceid - get a device id using INQUIRY VPD page 0x83 + * @sdev: get the identifer of this device + * @sreq: Scsi_Requeset associated with @sdev + * + * Description: + * Try to get an id (serial number) for device @sdev using a SCSI + * Vital Product Data page 0x83 (device id). + * + * Return: + * 0: Failure + * 1: Success + **/ +int scsi_get_deviceid(Scsi_Device *sdev, Scsi_Request *sreq) +{ + unsigned char *id_page; + unsigned char scsi_cmd[MAX_COMMAND_SIZE]; + int id_idx, scnt, ret; + int lun = sdev->lun; + int scsi_level = sdev->scsi_level; + int max_lgth = 255; + +retry: + id_page = kmalloc(max_lgth, GFP_ATOMIC | + (sdev->host->unchecked_isa_dma) ? + GFP_DMA : 0); + if (!id_page) { + printk(KERN_WARNING "scsi scan: Allocation failure identifying" + " host %d channel %d id %d lun %d, device might be" + " improperly identified.\n", sdev->host->host_no, + sdev->channel, sdev->id, sdev->lun); + return 0; + } - for (dqptr = shpnt->host_queue; dqptr != SDpnt; dqptr = dqptr->next) - continue; - if (dqptr) { - prev = dqptr->prev; - next = dqptr->next; - if (prev) - prev->next = next; - else - shpnt->host_queue = next; - if (next) - next->prev = prev; - } + memset(scsi_cmd, 0, MAX_COMMAND_SIZE); + scsi_cmd[0] = INQUIRY; + if ((lun > 0) && (scsi_level <= SCSI_2)) + scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; + else + scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ + scsi_cmd[2] = 0x83; + scsi_cmd[4] = max_lgth; + sreq->sr_cmd_len = 0; + sreq->sr_sense_buffer[0] = 0; + sreq->sr_sense_buffer[2] = 0; + sreq->sr_data_direction = SCSI_DATA_READ; + scsi_wait_req(sreq, (void *) scsi_cmd, (void *) id_page, + max_lgth, SCSI_TIMEOUT+4*HZ, 3); + if (sreq->sr_result) { + ret = 0; + goto leave; } - /* Last device block does not exist. Free memory. */ - if (SDpnt != NULL) { - blk_cleanup_queue(&SDpnt->request_queue); - if (SDpnt->inquiry) - kfree(SDpnt->inquiry); - kfree((char *) SDpnt); + /* + * check to see if response was truncated + */ + if (id_page[3] > max_lgth) { + max_lgth = id_page[3] + 4; + kfree(id_page); + goto retry; } - /* If we allocated a buffer so we could do DMA, free it now */ - if (scsi_result != &scsi_result0[0] && scsi_result != NULL) { - kfree(scsi_result); - } { - Scsi_Device *sdev; - Scsi_Cmnd *scmd; - - SCSI_LOG_SCAN_BUS(4, printk("Host status for host %p:\n", shpnt)); - for (sdev = shpnt->host_queue; sdev; sdev = sdev->next) { - SCSI_LOG_SCAN_BUS(4, printk("Device %d %p: ", sdev->id, sdev)); - for (scmd = sdev->device_queue; scmd; scmd = scmd->next) { - SCSI_LOG_SCAN_BUS(4, printk("%p ", scmd)); + /* + * Search for a match in the proiritized id_search_list. + */ + for (id_idx = 0; id_idx < ARRAY_SIZE(id_search_list); id_idx++) { + /* + * Examine each descriptor returned. There is normally only + * one or a small number of descriptors. + */ + for(scnt = 4; scnt <= id_page[3] + 3; + scnt += id_page[scnt + 3] + 4) { + if ((scsi_check_fill_deviceid(sdev, &id_page[scnt], + sdev->sdev_driverfs_dev.name, + &id_search_list[id_idx])) == 0) { + SCSI_LOG_SCAN_BUS(4, printk(KERN_INFO + "scsi scan: host %d channel %d id %d lun %d" + " used id desc %d/%d/%d\n", + sdev->host->host_no, sdev->channel, + sdev->id, sdev->lun, + id_search_list[id_idx].id_type, + id_search_list[id_idx].naa_type, + id_search_list[id_idx].code_set)); + ret = 1; + goto leave; + } else { + SCSI_LOG_SCAN_BUS(4, printk(KERN_INFO + "scsi scan: host %d channel %d id %d lun %d" + " no match/error id desc %d/%d/%d\n", + sdev->host->host_no, sdev->channel, + sdev->id, sdev->lun, + id_search_list[id_idx].id_type, + id_search_list[id_idx].naa_type, + id_search_list[id_idx].code_set)); } - SCSI_LOG_SCAN_BUS(4, printk("\n")); + /* + * scsi_check_fill_deviceid can fill the first + * byte of name with a non-zero value, reset it. + */ + sdev->sdev_driverfs_dev.name[0] = '\0'; } } + ret = 0; + + leave: + kfree(id_page); + return ret; } -/* - * Function: scan_scsis_single - * - * Purpose: Determine if a SCSI device (a single LUN) exists, and - * configure it into the system. - * - * Arguments: channel - the host's channel - * dev - target dev (target id) - * lun - LUN - * scsi_level - SCSI 1, 2 or 3 - * SDpnt2 - pointer to pointer of a preallocated Scsi_Device - * shpnt - host device to use - * scsi_result - preallocated buffer for the SCSI command result - * - * Lock status: None - * - * Returns: SCSI_SCAN_NO_RESPONSE - no valid response received from the - * device, this includes allocation failures preventing IO from - * being sent, or any general failures. - * - * SCSI_SCAN_DEVICE_PRESENT - device responded, SDpnt2 has all - * values needed to send IO set, plus scsi_level is set, but no - * new Scsi_Device was added/allocated. - * - * SCSI_SCAN_DEVICE_ADDED - device responded, and added to list; - * SDpnt2 filled, and pointed to new allocated Scsi_Device. - * - * Notes: This could be cleaned up more by moving SDpnt2 and Scsi_Device - * allocation into scan_scsis_target. - */ -static int scan_scsis_single(unsigned int channel, unsigned int dev, - unsigned int lun, int scsi_level, Scsi_Device ** SDpnt2, - struct Scsi_Host *shpnt, char *scsi_result) +/** + * scsi_get_serialnumber - get a serial number using INQUIRY page 0x80 + * @sdev: get the serial number of this device + * @sreq: Scsi_Requeset associated with @sdev + * + * Description: + * Send a SCSI INQUIRY page 0x80 to @sdev to get a serial number. + * + * Return: + * 0: Failure + * 1: Success + **/ +int scsi_get_serialnumber(Scsi_Device *sdev, Scsi_Request *sreq) { - char devname[64]; + unsigned char *serialnumber_page; unsigned char scsi_cmd[MAX_COMMAND_SIZE]; - struct Scsi_Device_Template *sdtpnt; - Scsi_Device *SDtail, *SDpnt = *SDpnt2; - Scsi_Request * SRpnt; - int bflags, type = -1; - int possible_inq_resp_len; - extern devfs_handle_t scsi_devfs_handle; + int lun = sdev->lun; + int scsi_level = sdev->scsi_level; + int max_lgth = 255; + + retry: + serialnumber_page = kmalloc(max_lgth, GFP_ATOMIC | + (sdev->host->unchecked_isa_dma) ? + GFP_DMA : 0); + if (!serialnumber_page) { + printk(KERN_WARNING "scsi scan: Allocation failure identifying" + " host %d channel %d id %d lun %d, device might be" + " improperly identified.\n", sdev->host->host_no, + sdev->channel, sdev->id, sdev->lun); + return 0; + } - SDpnt->host = shpnt; - SDpnt->id = dev; - SDpnt->lun = lun; - SDpnt->channel = channel; - SDpnt->online = TRUE; - - scsi_build_commandblocks(SDpnt); - - /* Some low level driver could use device->type (DB) */ - SDpnt->type = -1; - - /* - * Assume that the device will have handshaking problems, and then fix - * this field later if it turns out it doesn't - */ - SDpnt->borken = 1; - SDpnt->was_reset = 0; - SDpnt->expecting_cc_ua = 0; - SDpnt->starved = 0; - - if (NULL == (SRpnt = scsi_allocate_request(SDpnt))) { - printk("scan_scsis_single: no memory\n"); - scsi_release_commandblocks(SDpnt); - return SCSI_SCAN_NO_RESPONSE; + memset(scsi_cmd, 0, MAX_COMMAND_SIZE); + scsi_cmd[0] = INQUIRY; + if ((lun > 0) && (scsi_level <= SCSI_2)) + scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; + else + scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ + scsi_cmd[2] = 0x80; + scsi_cmd[4] = max_lgth; + sreq->sr_cmd_len = 0; + sreq->sr_sense_buffer[0] = 0; + sreq->sr_sense_buffer[2] = 0; + sreq->sr_data_direction = SCSI_DATA_READ; + scsi_wait_req(sreq, (void *) scsi_cmd, (void *) serialnumber_page, + max_lgth, SCSI_TIMEOUT+4*HZ, 3); + + if (sreq->sr_result) + goto leave; + /* + * check to see if response was truncated + */ + if (serialnumber_page[3] > max_lgth) { + max_lgth = serialnumber_page[3] + 4; + kfree(serialnumber_page); + goto retry; } /* - * We used to do a TEST_UNIT_READY before the INQUIRY but that was - * not really necessary. Spec recommends using INQUIRY to scan for - * devices (and TEST_UNIT_READY to poll for media change). - Paul G. + * Need 1 byte for SCSI_UID_SER_NUM, 1 for trailing '\0', 8 for + * vendor, 16 for model = 26, plus serial number size. */ + if (scsi_check_id_size (sdev, (26 + serialnumber_page[3]))) + goto leave; + sdev->sdev_driverfs_dev.name[0] = SCSI_UID_SER_NUM; + strncat(sdev->sdev_driverfs_dev.name, sdev->vendor, 8); + strncat(sdev->sdev_driverfs_dev.name, sdev->model, 16); + strncat(sdev->sdev_driverfs_dev.name, &serialnumber_page[4], + serialnumber_page[3]); + kfree(serialnumber_page); + return 1; + leave: + memset(sdev->sdev_driverfs_dev.name, 0, DEVICE_NAME_SIZE); + kfree(serialnumber_page); + return 0; +} + +/** + * scsi_get_default_name - get a default name + * @sdev: get a default name for this device + * + * Description: + * Set the name of @sdev to the concatenation of the vendor, model, + * and revision found in @sdev. + * + * Return: + * 1: Success + **/ +int scsi_get_default_name(Scsi_Device *sdev) +{ + if (scsi_check_id_size(sdev, 29)) + return 0; + else { + sdev->sdev_driverfs_dev.name[0] = SCSI_UID_UNKNOWN; + strncpy(&sdev->sdev_driverfs_dev.name[1], sdev->vendor, 8); + strncat(sdev->sdev_driverfs_dev.name, sdev->model, 16); + strncat(sdev->sdev_driverfs_dev.name, sdev->rev, 4); + return 1; + } +} - SCSI_LOG_SCAN_BUS(3, - printk("scsi scan: INQUIRY to host %d channel %d id %d lun %d\n", - shpnt->host_no, channel, dev, lun) - ); +/** + * scsi_load_identifier: + * @sdev: get an identifier (name) of this device + * @sreq: Scsi_Requeset associated with @sdev + * + * Description: + * Determine what INQUIRY pages are supported by @sdev, and try the + * different pages until we get an identifier, or no other pages are + * left. Start with page 0x83 (device id) and then try page 0x80 + * (serial number). If neither of these pages gets an id, use the + * default naming convention. + * + * The first character of sdev_driverfs_dev.name is SCSI_UID_SER_NUM + * (S) if we used page 0x80, SCSI_UID_UNKNOWN (Z) if we used the + * default name, otherwise it starts with the page 0x83 id type + * (see the SCSI Primary Commands specification for details). + * + * Notes: + * If a device returns the same serial number for different LUNs or + * even for different LUNs on different devices, special handling must + * be added to get an id, or a new black list flag must to added and + * used in device_list[] (so we use the default name, or add a way to + * prefix the id/name with SCSI_UID_UNKNOWN - and change the define to + * something meaningful like SCSI_UID_NOT_UNIQUE). Complete user level + * scanning would be nice for such devices, so we do not need device + * specific code in the kernel. + **/ +static void scsi_load_identifier(Scsi_Device *sdev, Scsi_Request *sreq) +{ + unsigned char *evpd_page = NULL; + int cnt; + memset(sdev->sdev_driverfs_dev.name, 0, DEVICE_NAME_SIZE); + evpd_page = scsi_get_evpd_page(sdev, sreq); + if (evpd_page == NULL) { + /* + * try to obtain serial number anyway + */ + (void)scsi_get_serialnumber(sdev, sreq); + } else { + /* + * XXX search high to low, since the pages are lowest to + * highest - page 0x83 will be after page 0x80. + */ + for(cnt = 4; cnt <= evpd_page[3] + 3; cnt++) + if (evpd_page[cnt] == 0x83) + if (scsi_get_deviceid(sdev, sreq)) + goto leave; + + for(cnt = 4; cnt <= evpd_page[3] + 3; cnt++) + if (evpd_page[cnt] == 0x80) + if (scsi_get_serialnumber(sdev, sreq)) + goto leave; + + if (sdev->sdev_driverfs_dev.name[0] == 0) + scsi_get_default_name(sdev); + + } +leave: + if (evpd_page) kfree(evpd_page); + SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: host %d channel %d" + " id %d lun %d name/id: '%s'\n", sdev->host->host_no, + sdev->channel, sdev->id, sdev->lun, sdev->sdev_driverfs_dev.name)); + return; +} + +/** + * scsi_find_scsi_level - return the scsi_level of a matching target + * + * Description: + * Return the scsi_level of any Scsi_Device matching @channel, @id, + * and @shost. + * Notes: + * Needs to issue an INQUIRY to LUN 0 if no Scsi_Device matches, and + * if the INQUIRY can't be sent return a failure. + **/ +static int scsi_find_scsi_level(unsigned int channel, unsigned int id, + struct Scsi_Host *shost) +{ + int res = SCSI_2; + Scsi_Device *sdev; + + for (sdev = shost->host_queue; sdev; sdev = sdev->next) + if ((id == sdev->id) && (channel == sdev->channel)) + return (int) sdev->scsi_level; /* - * Build an INQUIRY command block. + * FIXME No matching target id is configured, this needs to get + * the INQUIRY for LUN 0, and use it to determine the scsi_level. */ + return res; +} + +/** + * scsi_probe_lun - probe a single LUN using a SCSI INQUIRY + * @sreq: used to send the INQUIRY + * @inq_result: area to store the INQUIRY result + * @bflags: store any bflags found here + * + * Description: + * Probe the lun associated with @sreq using a standard SCSI INQUIRY; + * + * If the INQUIRY is successful, sreq->sr_result is zero and: the + * INQUIRY data is in @inq_result; the scsi_level and INQUIRY length + * are copied to the Scsi_Device at @sreq->sr_device (sdev); + * any device_list flags value is stored in *@bflags. + **/ +static void scsi_probe_lun(Scsi_Request *sreq, char *inq_result, + int *bflags) +{ + Scsi_Device *sdev = sreq->sr_device; /* a bit ugly */ + unsigned char scsi_cmd[MAX_COMMAND_SIZE]; + int possible_inq_resp_len; + + SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY to host %d" + " channel %d id %d lun %d\n", sdev->host->host_no, + sdev->channel, sdev->id, sdev->lun)); + memset(scsi_cmd, 0, 6); scsi_cmd[0] = INQUIRY; - if ((lun > 0) && (scsi_level <= SCSI_2)) - scsi_cmd[1] = (lun << 5) & 0xe0; + if ((sdev->lun > 0) && (sdev->scsi_level <= SCSI_2)) + scsi_cmd[1] = (sdev->lun << 5) & 0xe0; scsi_cmd[4] = 36; /* issue conservative alloc_length */ - SRpnt->sr_cmd_len = 0; - SRpnt->sr_data_direction = SCSI_DATA_READ; + sreq->sr_cmd_len = 0; + sreq->sr_data_direction = SCSI_DATA_READ; - memset(scsi_result, 0, 36); - scsi_wait_req (SRpnt, (void *) scsi_cmd, - (void *) scsi_result, - 36, SCSI_TIMEOUT+4*HZ, 3); - - SCSI_LOG_SCAN_BUS(3, printk("scsi: INQUIRY %s with code 0x%x\n", - SRpnt->sr_result ? "failed" : "successful", SRpnt->sr_result)); - - /* - * Now that we don't do TEST_UNIT_READY anymore, we must be prepared - * for media change conditions here, so cannot require zero result. - */ - if (SRpnt->sr_result) { - if ((driver_byte(SRpnt->sr_result) & DRIVER_SENSE) != 0 && - (SRpnt->sr_sense_buffer[2] & 0xf) == UNIT_ATTENTION && - SRpnt->sr_sense_buffer[12] == 0x28 && - SRpnt->sr_sense_buffer[13] == 0) { + memset(inq_result, 0, 36); + scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result, 36, + SCSI_TIMEOUT + 4 * HZ, 3); + + SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 1st INQUIRY %s with" + " code 0x%x\n", sreq->sr_result ? + "failed" : "successful", sreq->sr_result)); + + if (sreq->sr_result) { + if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) != 0 && + (sreq->sr_sense_buffer[2] & 0xf) == UNIT_ATTENTION && + sreq->sr_sense_buffer[12] == 0x28 && + sreq->sr_sense_buffer[13] == 0) { /* not-ready to ready transition - good */ - /* dpg: bogus? INQUIRY never returns UNIT_ATTENTION */ - } else { - /* assume no peripheral if any other sort of error */ - scsi_release_request(SRpnt); - return 0; - } + /* dpg: bogus? INQUIRY never returns UNIT_ATTENTION */ + } else + /* + * assume no peripheral if any other sort of error + */ + return; } /* - * Get any flags for this device. + * Get any flags for this device. + * + * XXX add a bflags to Scsi_Device, and replace the corresponding + * bit fields in Scsi_Device, so bflags need not be passed as an + * argument. */ - bflags = get_device_flags (&scsi_result[8], &scsi_result[16]); + BUG_ON(bflags == NULL); + *bflags = get_device_flags(&inq_result[8], &inq_result[16]); - possible_inq_resp_len = (unsigned char)scsi_result[4] + 5; - if (BLIST_INQUIRY_36 & bflags) + possible_inq_resp_len = (unsigned char) inq_result[4] + 5; + if (BLIST_INQUIRY_36 & *bflags) possible_inq_resp_len = 36; - else if (BLIST_INQUIRY_58 & bflags) + else if (BLIST_INQUIRY_58 & *bflags) possible_inq_resp_len = 58; else if (possible_inq_resp_len > 255) possible_inq_resp_len = 36; /* sanity */ - if (possible_inq_resp_len > 36) { /* do additional INQUIRY */ + if (possible_inq_resp_len > 36) { /* do additional INQUIRY */ memset(scsi_cmd, 0, 6); scsi_cmd[0] = INQUIRY; - if ((lun > 0) && (scsi_level <= SCSI_2)) - scsi_cmd[1] = (lun << 5) & 0xe0; - scsi_cmd[4] = (unsigned char)possible_inq_resp_len; - SRpnt->sr_cmd_len = 0; - SRpnt->sr_data_direction = SCSI_DATA_READ; - - scsi_wait_req (SRpnt, (void *) scsi_cmd, - (void *) scsi_result, - scsi_cmd[4], SCSI_TIMEOUT+4*HZ, 3); - /* assume successful */ - } - SDpnt->inquiry_len = possible_inq_resp_len; - SDpnt->inquiry = kmalloc(possible_inq_resp_len, GFP_ATOMIC); - if (NULL == SDpnt->inquiry) { - scsi_release_commandblocks(SDpnt); - scsi_release_request(SRpnt); + if ((sdev->lun > 0) && (sdev->scsi_level <= SCSI_2)) + scsi_cmd[1] = (sdev->lun << 5) & 0xe0; + scsi_cmd[4] = (unsigned char) possible_inq_resp_len; + sreq->sr_cmd_len = 0; + sreq->sr_data_direction = SCSI_DATA_READ; + /* + * re-zero inq_result just to be safe. + */ + memset(inq_result, 0, possible_inq_resp_len); + scsi_wait_req(sreq, (void *) scsi_cmd, + (void *) inq_result, + possible_inq_resp_len, SCSI_TIMEOUT + 4 * HZ, 3); + SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 2nd INQUIRY" + " %s with code 0x%x\n", sreq->sr_result ? + "failed" : "successful", sreq->sr_result)); + if (sreq->sr_result) + return; + + /* + * The INQUIRY can change, this means the length can change. + */ + possible_inq_resp_len = (unsigned char) inq_result[4] + 5; + if (BLIST_INQUIRY_58 & *bflags) + possible_inq_resp_len = 58; + else if (possible_inq_resp_len > 255) + possible_inq_resp_len = 36; /* sanity */ + } + + sdev->inquiry_len = possible_inq_resp_len; + + /* + * XXX Abort if the response length is less than 36? If less than + * 32, the lookup of the device flags (above) could be invalid, + * and it would be possible to take an incorrect action - we do + * not want to hang because of a short INQUIRY. On the flip side, + * if the device is spun down or becoming ready (and so it gives a + * short INQUIRY), an abort here prevents any further use of the + * device, including spin up. + * + * Related to the above issue: + * + * XXX Devices (disk or all?) should be sent a TEST UNIT READY, + * and if not ready, sent a START_STOP to start (maybe spin up) and + * then send the INQUIRY again, since the INQUIRY can change after + * a device is initialized. + * + * Ideally, start a device if explicitly asked to do so. This + * assumes that a device is spun up on power on, spun down on + * request, and then spun up on request. + */ + + /* + * The scanning code needs to know the scsi_level, even if no + * device is attached at LUN 0 (SCSI_SCAN_TARGET_PRESENT) so + * non-zero LUNs can be scanned. + */ + sdev->scsi_level = inq_result[2] & 0x07; + if (sdev->scsi_level >= 2 || + (sdev->scsi_level == 1 && (inq_result[3] & 0x0f) == 1)) + sdev->scsi_level++; + + return; +} + +/** + * scsi_add_lun - allocate and fully initialze a Scsi_Device + * @sdevscan: holds information to be stored in the new Scsi_Device + * @sdevnew: store the address of the newly allocated Scsi_Device + * @sreq: scsi request used when getting an identifier + * @inq_result: holds the result of a previous INQUIRY to the LUN + * @bflags: flags value from device_list + * + * Description: + * Allocate and initialize a Scsi_Device matching sdevscan. Optionally + * set fields based on values in *@bflags. If @sdevnew is not + * NULL, store the address of the new Scsi_Device in *@sdevnew (needed + * when scanning a particular LUN). + * + * Return: + * SCSI_SCAN_NO_RESPONSE: could not allocate or setup a Scsi_Device + * SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized + **/ +static int scsi_add_lun(Scsi_Device *sdevscan, Scsi_Device **sdevnew, + Scsi_Request *sreq, char *inq_result, int *bflags) +{ + Scsi_Device *sdev; + struct Scsi_Device_Template *sdt; + char devname[64]; + extern devfs_handle_t scsi_devfs_handle; + + sdev = scsi_alloc_sdev(sdevscan->host, sdevscan->channel, + sdevscan->id, sdevscan->lun); + if (sdev == NULL) + return SCSI_SCAN_NO_RESPONSE; + + sdev->scsi_level = sdevscan->scsi_level; + /* + * XXX do not save the inquiry, since it can change underneath us, + * save just vendor/model/rev. + * + * Rather than save it and have an ioctl that retrieves the saved + * value, have an ioctl that executes the same INQUIRY code used + * in scsi_probe_lun, let user level programs doing INQUIRY + * scanning run at their own risk, or supply a user level program + * that can correctly scan. + */ + sdev->inquiry_len = sdevscan->inquiry_len; + sdev->inquiry = kmalloc(sdev->inquiry_len, GFP_ATOMIC); + if (sdev->inquiry == NULL) { + scsi_free_sdev(sdev); return SCSI_SCAN_NO_RESPONSE; } - memcpy(SDpnt->inquiry, scsi_result, possible_inq_resp_len); - SDpnt->vendor = (char *)(SDpnt->inquiry + 8); - SDpnt->model = (char *)(SDpnt->inquiry + 16); - SDpnt->rev = (char *)(SDpnt->inquiry + 32); - - SDpnt->scsi_level = scsi_result[2] & 0x07; - if (SDpnt->scsi_level >= 2 || - (SDpnt->scsi_level == 1 && - (scsi_result[3] & 0x0f) == 1)) - SDpnt->scsi_level++; - - /* - * Check the peripheral qualifier field - this tells us whether LUNS - * are supported here or not. - */ - if ((scsi_result[0] >> 5) == 3) { - /* - * Peripheral qualifier 3 (011b): The device server is not - * capable of supporting a physical device on this logical - * unit. - */ - scsi_release_commandblocks(SDpnt); - scsi_release_request(SRpnt); - return SCSI_SCAN_DEVICE_PRESENT; - } - /* The Toshiba ROM was "gender-changed" here as an inline hack. - This is now much more generic. - This is a mess: What we really want is to leave the scsi_result - alone, and just change the SDpnt structure. And the SDpnt is what - we want print_inquiry to print. -- REW - */ - if (bflags & BLIST_ISDISK) { - scsi_result[0] = TYPE_DISK; - scsi_result[1] |= 0x80; /* removable */ - } - - if (bflags & BLIST_ISROM) { - scsi_result[0] = TYPE_ROM; - scsi_result[1] |= 0x80; /* removable */ - } - - - SDpnt->removable = (0x80 & scsi_result[1]) >> 7; - /* Use the peripheral qualifier field to determine online/offline */ - if (((scsi_result[0] >> 5) & 7) == 1) SDpnt->online = FALSE; - else SDpnt->online = TRUE; - SDpnt->lockable = SDpnt->removable; - SDpnt->changed = 0; - SDpnt->access_count = 0; - SDpnt->busy = 0; - SDpnt->has_cmdblocks = 0; - /* - * Currently, all sequential devices are assumed to be tapes, all random - * devices disk, with the appropriate read only flags set for ROM / WORM - * treated as RO. - */ - switch (type = (scsi_result[0] & 0x1f)) { + + memcpy(sdev->inquiry, inq_result, sdev->inquiry_len); + sdev->vendor = (char *) (sdev->inquiry + 8); + sdev->model = (char *) (sdev->inquiry + 16); + sdev->rev = (char *) (sdev->inquiry + 32); + + if (*bflags & BLIST_ISROM) { + /* + * It would be better to modify sdev->type, and set + * sdev->removable, but then the print_inquiry() output + * would not show TYPE_ROM; if print_inquiry() is removed + * the issue goes away. + */ + inq_result[0] = TYPE_ROM; + inq_result[1] |= 0x80; /* removable */ + } + + switch (sdev->type = (inq_result[0] & 0x1f)) { case TYPE_TAPE: case TYPE_DISK: case TYPE_PRINTER: @@ -787,201 +1369,357 @@ case TYPE_MEDIUM_CHANGER: case TYPE_ENCLOSURE: case TYPE_COMM: - SDpnt->writeable = 1; + sdev->writeable = 1; break; case TYPE_WORM: case TYPE_ROM: - SDpnt->writeable = 0; + sdev->writeable = 0; break; default: - printk("scsi: unknown type %d\n", type); + printk(KERN_INFO "scsi: unknown device type %d\n", sdev->type); } - SDpnt->device_blocked = FALSE; - SDpnt->device_busy = 0; - SDpnt->single_lun = 0; - SDpnt->soft_reset = - (scsi_result[7] & 1) && ((scsi_result[3] & 7) == 2); - SDpnt->random = (type == TYPE_TAPE) ? 0 : 1; - SDpnt->type = (type & 0x1f); - - print_inquiry(scsi_result); + sdev->random = (sdev->type == TYPE_TAPE) ? 0 : 1; - /* interrogate scsi target to provide device identifier */ - scsi_load_identifier(SDpnt, SRpnt); + print_inquiry(inq_result); - /* create driverfs files */ - sprintf(SDpnt->sdev_driverfs_dev.bus_id,"%d:%d:%d:%d", - SDpnt->host->host_no, SDpnt->channel, SDpnt->id, SDpnt->lun); - - SDpnt->sdev_driverfs_dev.parent = &SDpnt->host->host_driverfs_dev; - SDpnt->sdev_driverfs_dev.bus = &scsi_driverfs_bus_type; - - device_register(&SDpnt->sdev_driverfs_dev); - - /* Create driverfs file entries */ - device_create_file(&SDpnt->sdev_driverfs_dev, - &dev_attr_type); + /* + * For a peripheral qualifier (PQ) value of 1 (001b), the SCSI + * spec says: The device server is capable of supporting the + * specified peripheral device type on this logical unit. However, + * the physical device is not currently connected to this logical + * unit. + * + * The above is vague, as it implies that we could treat 001 and + * 011 the same. Stay compatible with previous code, and create a + * Scsi_Device for a PQ of 1 + * + * XXX Save the PQ field let the upper layers figure out if they + * want to attach or not to this device, do not set online FALSE; + * otherwise, offline devices still get an sd allocated, and they + * use up an sd slot. + */ + if (((inq_result[0] >> 5) & 7) == 1) { + SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: peripheral" + " qualifier of 1, device offlined\n")); + sdev->online = FALSE; + } - sprintf (devname, "host%d/bus%d/target%d/lun%d", - SDpnt->host->host_no, SDpnt->channel, SDpnt->id, SDpnt->lun); - if (SDpnt->de) printk ("DEBUG: dir: \"%s\" already exists\n", devname); - else SDpnt->de = devfs_mk_dir (scsi_devfs_handle, devname, NULL); + sdev->removable = (0x80 & inq_result[1]) >> 7; + sdev->lockable = sdev->removable; + sdev->soft_reset = (inq_result[7] & 1) && ((inq_result[3] & 7) == 2); - for (sdtpnt = scsi_devicelist; sdtpnt; - sdtpnt = sdtpnt->next) - if (sdtpnt->detect) - SDpnt->attached += - (*sdtpnt->detect) (SDpnt); + /* + * XXX maybe move the identifier and driverfs/devfs setup to a new + * function, and call them after this function is called. + * + * scsi_load_identifier is the only reason sreq is needed in this + * function. + */ + scsi_load_identifier(sdev, sreq); /* - * Accommodate drivers that want to sleep when they should be in a polling - * loop. + * create driverfs files */ - SDpnt->disconnect = 0; + sprintf(sdev->sdev_driverfs_dev.bus_id,"%d:%d:%d:%d", + sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); + sdev->sdev_driverfs_dev.parent = &sdev->host->host_driverfs_dev; + sdev->sdev_driverfs_dev.bus = &scsi_driverfs_bus_type; + device_register(&sdev->sdev_driverfs_dev); + /* + * Create driverfs file entries + */ + device_create_file(&sdev->sdev_driverfs_dev, &dev_attr_type); + sprintf(devname, "host%d/bus%d/target%d/lun%d", + sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); + if (sdev->de) + printk(KERN_WARNING "scsi devfs dir: \"%s\" already exists\n", + devname); + else + sdev->de = devfs_mk_dir(scsi_devfs_handle, devname, NULL); /* - * Set the tagged_queue flag for SCSI-II devices that purport to support - * tagged queuing in the INQUIRY data. + * End driverfs/devfs code. */ - SDpnt->tagged_queue = 0; - if ((SDpnt->scsi_level >= SCSI_2) && - (scsi_result[7] & 2) && - !(bflags & BLIST_NOTQ)) { - SDpnt->tagged_supported = 1; - SDpnt->current_tag = 0; - } + + if ((sdev->scsi_level >= SCSI_2) && (inq_result[7] & 2) && + !(*bflags & BLIST_NOTQ)) + sdev->tagged_supported = 1; /* - * Some revisions of the Texel CD ROM drives have handshaking problems when - * used with the Seagate controllers. Before we know what type of device - * we're talking to, we assume it's borken and then change it here if it - * turns out that it isn't a TEXEL drive. + * Some devices (Texel CD ROM drives) have handshaking problems + * when used with the Seagate controllers. borken is initialized + * to 1, and then set it to 0 here. */ - if ((bflags & BLIST_BORKEN) == 0) - SDpnt->borken = 0; + if ((*bflags & BLIST_BORKEN) == 0) + sdev->borken = 0; /* - * If we want to only allow I/O to one of the luns attached to this device - * at a time, then we set this flag. + * If we need to allow I/O to only one of the luns attached to + * this target id at a time, then we set this flag. */ - if (bflags & BLIST_SINGLELUN) - SDpnt->single_lun = 1; + if (*bflags & BLIST_SINGLELUN) + sdev->single_lun = 1; + + for (sdt = scsi_devicelist; sdt; sdt = sdt->next) + if (sdt->detect) + sdev->attached += (*sdt->detect) (sdev); + + if (sdevnew != NULL) + *sdevnew = sdev; + + return SCSI_SCAN_LUN_PRESENT; +} + +/** + * scsi_probe_and_add_lun - probe a LUN, if a LUN is found add it + * @sdevscan: probe the LUN corresponding to this Scsi_Device + * @sdevnew: store the value of any new Scsi_Device allocated + * @bflagsp: store bflags here if not NULL + * + * Description: + * Call scsi_probe_lun, if a LUN with an attached device is found, + * allocate and set it up by calling scsi_add_lun. + * + * Return: + * SCSI_SCAN_NO_RESPONSE: could not allocate or setup a Scsi_Device + * SCSI_SCAN_TARGET_PRESENT: target responded, but no device is + * attached at the LUN + * SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized + **/ +static int scsi_probe_and_add_lun(Scsi_Device *sdevscan, Scsi_Device **sdevnew, + int *bflagsp) +{ + Scsi_Device *sdev = NULL; + Scsi_Request *sreq = NULL; + unsigned char *scsi_result = NULL; + int bflags; + int res; /* - * These devices need this "key" to unlock the devices so we can use it + * Any command blocks allocated are fixed to use sdevscan->lun, + * so they must be allocated and released if sdevscan->lun + * changes. + * + * XXX optimize and don't call build/release commandblocks, instead + * modify the LUN value of the existing command block - this means + * the build/release calls would be moved to the alloc/free of + * sdevscan, and the modifying function would be called here. + * + * XXX maybe change scsi_release_commandblocks to not reset + * queue_depth to 0. */ - if ((bflags & BLIST_KEY) != 0) { - printk("Unlocked floptical drive.\n"); - SDpnt->lockable = 0; - scsi_cmd[0] = MODE_SENSE; - if (shpnt->max_lun <= 8) - scsi_cmd[1] = (lun << 5) & 0xe0; - else scsi_cmd[1] = 0; /* any other idea? */ - scsi_cmd[2] = 0x2e; - scsi_cmd[3] = 0; - scsi_cmd[4] = 0x2a; - scsi_cmd[5] = 0; - SRpnt->sr_cmd_len = 0; - SRpnt->sr_data_direction = SCSI_DATA_READ; - scsi_wait_req (SRpnt, (void *) scsi_cmd, - (void *) scsi_result, 0x2a, - SCSI_TIMEOUT, 3); + sdevscan->queue_depth = 1; + scsi_build_commandblocks(sdevscan); + if (sdevscan->has_cmdblocks == 0) + goto alloc_failed; + + sreq = scsi_allocate_request(sdevscan); + if (sreq == NULL) + goto alloc_failed; + /* + * The sreq is for use only with sdevscan. + */ + + scsi_result = kmalloc(256, GFP_ATOMIC | + (sdevscan->host->unchecked_isa_dma) ? + GFP_DMA : 0); + if (scsi_result == NULL) + goto alloc_failed; + + scsi_probe_lun(sreq, scsi_result, &bflags); + if (sreq->sr_result) + res = SCSI_SCAN_NO_RESPONSE; + else { /* - * scsi_result no longer holds inquiry data. + * scsi_result contains valid SCSI INQUIRY data. */ + if ((scsi_result[0] >> 5) == 3) { + /* + * For a Peripheral qualifier 3 (011b), the SCSI + * spec says: The device server is not capable of + * supporting a physical device on this logical + * unit. + * + * For disks, this implies that there is no + * logical disk configured at sdev->lun, but there + * is a target id responding. + */ + SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO + "scsi scan: peripheral qualifier of 3," + " no device added\n")); + res = SCSI_SCAN_TARGET_PRESENT; + } else { + res = scsi_add_lun(sdevscan, &sdev, sreq, scsi_result, + &bflags); + if (res == SCSI_SCAN_LUN_PRESENT) { + BUG_ON(sdev == NULL); + if ((bflags & BLIST_KEY) != 0) { + sdev->lockable = 0; + scsi_unlock_floptical(sreq, + scsi_result); + /* + * scsi_result no longer contains + * the INQUIRY data. + */ + } + /* + * "hardcoded" scans of a single LUN need + * to know the sdev just allocated. + */ + if (sdevnew != NULL) + *sdevnew = sdev; + if (bflagsp != NULL) + *bflagsp = bflags; + } + } } + kfree(scsi_result); + scsi_release_request(sreq); + scsi_release_commandblocks(sdevscan); + return res; - scsi_release_request(SRpnt); - SRpnt = NULL; +alloc_failed: + printk(ALLOC_FAILURE_MSG, __FUNCTION__); + if (scsi_result != NULL) + kfree(scsi_result); + if (sreq != NULL) + scsi_release_request(sreq); + if (sdevscan->has_cmdblocks != 0) + scsi_release_commandblocks(sdevscan); + return SCSI_SCAN_NO_RESPONSE; +} - scsi_release_commandblocks(SDpnt); +/** + * scsi_sequential_lun_scan - sequentially scan a SCSI target + * @sdevscan: scan the host, channel, and id of this Scsi_Device + * @bflags: flags from device_list for LUN 0 + * @lun0_res: result of scanning LUN 0 + * + * Description: + * Generally, scan from LUN 1 (LUN 0 is assumed to already have been + * scanned) to some maximum lun until a LUN is found with no device + * attached. Use the bflags to figure out any oddities. + * + * Modifies sdevscan->lun. + **/ +static void scsi_sequential_lun_scan(Scsi_Device *sdevscan, int bflags, + int lun0_res) +{ + struct Scsi_Host *shost = sdevscan->host; + unsigned int sparse_lun; + unsigned int max_dev_lun; + + SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: Sequential scan of" + " host %d channel %d id %d\n", sdevscan->host->host_no, + sdevscan->channel, sdevscan->id)); + max_dev_lun = min(max_scsi_luns, shost->max_lun); /* - * This device was already hooked up to the host in question, - * so at this point we just let go of it and it should be fine. We do need to - * allocate a new one and attach it to the host so that we can further scan the bus. + * If this device is known to support sparse multiple units, + * override the other settings, and scan all of them. Normally, + * SCSI-3 devices should be scanned via the REPORT LUNS. */ - SDpnt = (Scsi_Device *) kmalloc(sizeof(Scsi_Device), GFP_ATOMIC); - if (!SDpnt) { - printk("scsi: scan_scsis_single: Cannot malloc\n"); - return SCSI_SCAN_NO_RESPONSE; - } - memset(SDpnt, 0, sizeof(Scsi_Device)); - SDpnt->vendor = scsi_null_device_strs; - SDpnt->model = scsi_null_device_strs; - SDpnt->rev = scsi_null_device_strs; - - *SDpnt2 = SDpnt; - SDpnt->queue_depth = 1; - SDpnt->host = shpnt; - SDpnt->online = TRUE; - SDpnt->scsi_level = scsi_level; + if (bflags & BLIST_SPARSELUN) { + max_dev_lun = shost->max_lun; + sparse_lun = 1; + } else + sparse_lun = 0; /* - * Register the queue for the device. All I/O requests will come - * in through here. We also need to register a pointer to - * ourselves, since the queue handler won't know what device - * the queue actually represents. We could look it up, but it - * is pointless work. + * If not sparse lun and no device attached at LUN 0 do not scan + * any further. */ - scsi_initialize_queue(SDpnt, shpnt); - SDpnt->host = shpnt; - scsi_initialize_merge_fn(SDpnt); + if (!sparse_lun && (lun0_res != SCSI_SCAN_LUN_PRESENT)) + return; /* - * Mark this device as online, or otherwise we won't be able to do much with it. + * If less than SCSI_1_CSS, and no special lun scaning, stop + * scanning; this matches 2.4 behaviour, but could just be a bug + * (to continue scanning a SCSI_1_CSS device). + */ + if ((sdevscan->scsi_level < SCSI_1_CCS) && + ((bflags & (BLIST_FORCELUN | BLIST_SPARSELUN | BLIST_MAX5LUN)) + == 0)) + return; + /* + * If this device is known to support multiple units, override + * the other settings, and scan all of them. + */ + if (bflags & BLIST_FORCELUN) + max_dev_lun = shost->max_lun; + /* + * REGAL CDC-4X: avoid hang after LUN 4 */ - SDpnt->online = TRUE; - - /* - * Initialize the object that we will use to wait for command blocks. - */ - init_waitqueue_head(&SDpnt->scpnt_wait); - + if (bflags & BLIST_MAX5LUN) + max_dev_lun = min(5U, max_dev_lun); /* - * Since we just found one device, there had damn well better be one in the list - * already. + * Do not scan SCSI-2 or lower device past LUN 7, unless + * BLIST_LARGELUN. */ - if (shpnt->host_queue == NULL) - panic("scan_scsis_single: Host queue == NULL\n"); + if ((sdevscan->scsi_level < SCSI_3) && !(bflags & BLIST_LARGELUN)) + max_dev_lun = min(8U, max_dev_lun); - SDtail = shpnt->host_queue; - while (SDtail->next) { - SDtail = SDtail->next; - } + /* + * We have already scanned LUN 0, so start at LUN 1. Keep scanning + * until we reach the max, or no LUN is found and we are not + * sparse_lun. + */ + for (sdevscan->lun = 1; sdevscan->lun < max_dev_lun; ++sdevscan->lun) + if ((scsi_probe_and_add_lun(sdevscan, NULL, NULL) + != SCSI_SCAN_LUN_PRESENT) && !sparse_lun) + return; +} - /* Add this device to the linked list at the end */ - SDtail->next = SDpnt; - SDpnt->prev = SDtail; - SDpnt->next = NULL; +#ifdef CONFIG_SCSI_REPORT_LUNS +/** + * scsilun_to_int: convert a ScsiLun to an int + * @scsilun: ScsiLun to be converted. + * + * Description: + * Convert @scsilun from a ScsiLun to a four byte host byte-ordered + * integer, and return the result. The caller must check for + * truncation before using this function. + * + * Notes: + * The ScsiLun is assumed to be four levels, with each level + * effectively containing a SCSI byte-ordered (big endian) short; the + * addressing bits of each level are ignored (the highest two bits). + * For a description of the LUN format, post SCSI-3 see the SCSI + * Architecture Model, for SCSI-3 see the SCSI Controller Commands. + * + * Given a ScsiLun of: 0a 04 0b 03 00 00 00 00, this function returns + * the integer: 0x0b030a04 + **/ +static int scsilun_to_int(ScsiLun *scsilun) +{ + int i; + unsigned int lun; - return SCSI_SCAN_DEVICE_ADDED; + lun = 0; + for (i = 0; i < sizeof(lun); i += 2) + lun = lun | (((scsilun->scsi_lun[i] << 8) | + scsilun->scsi_lun[i + 1]) << (i * 8)); + return lun; } +#endif -/* - * Function: scsi_report_lun_scan - * - * Purpose: Use a SCSI REPORT LUN to determine what LUNs to scan. - * - * Arguments: SDlun0_pnt - pointer to a Scsi_Device for LUN 0 - * channel - the host's channel - * dev - target dev (target id) - * SDpnt2 - pointer to pointer of a preallocated Scsi_Device - * shpnt - host device to use - * scsi_result - preallocated buffer for the SCSI command result - * - * Lock status: None - * - * Returns: If the LUNs for device at shpnt/channel/dev are scanned, - * return 0, else return 1. - * - * Notes: This code copies and truncates the 8 byte LUN into the - * current 4 byte (int) lun. - */ -static int scsi_report_lun_scan(Scsi_Device *SDlun0_pnt, unsigned - int channel, unsigned int dev, Scsi_Device **SDpnt2, - struct Scsi_Host *shpnt, char *scsi_result) +/** + * scsi_report_lun_scan - Scan using SCSI REPORT LUN results + * @sdevscan: scan the host, channel, and id of this Scsi_Device + * + * Description: + * If @sdevscan is for a SCSI-3 or up device, send a REPORT LUN + * command, and scan the resulting list of LUNs by calling + * scsi_probe_and_add_lun. + * + * Modifies sdevscan->lun. + * + * Return: + * 0: scan completed (or no memory, so further scanning is futile) + * 1: no report lun scan, or not configured + **/ +static int scsi_report_lun_scan(Scsi_Device *sdevscan) { #ifdef CONFIG_SCSI_REPORT_LUNS @@ -991,53 +1729,50 @@ unsigned int lun; unsigned int num_luns; unsigned int retries; - ScsiLun *fcp_cur_lun_pnt, *lun_data_pnt; - Scsi_Request *SRpnt; - int scsi_level; - char *byte_pnt; - int got_command_blocks = 0; + ScsiLun *fcp_cur_lun, *lun_data; + Scsi_Request *sreq; + char *data; /* * Only support SCSI-3 and up devices. */ - if (SDlun0_pnt->scsi_level < SCSI_3) + if (sdevscan->scsi_level < SCSI_3) return 1; - /* - * Note SDlun0_pnt might be invalid after scan_scsis_single is called. - */ - - /* - * Command blocks might be built depending on whether LUN 0 was - * configured or not. Checking has_cmdblocks here is ugly. - */ - if (SDlun0_pnt->has_cmdblocks == 0) { - got_command_blocks = 1; - scsi_build_commandblocks(SDlun0_pnt); + sdevscan->queue_depth = 1; + scsi_build_commandblocks(sdevscan); + if (sdevscan->has_cmdblocks == 0) { + printk(ALLOC_FAILURE_MSG, __FUNCTION__); + /* + * We are out of memory, don't try scanning any further. + */ + return 0; } - SRpnt = scsi_allocate_request(SDlun0_pnt); + sreq = scsi_allocate_request(sdevscan); - sprintf (devname, "host %d channel %d id %d", - SDlun0_pnt->host->host_no, SDlun0_pnt->channel, - SDlun0_pnt->id); + sprintf(devname, "host %d channel %d id %d", sdevscan->host->host_no, + sdevscan->channel, sdevscan->id); /* * Allocate enough to hold the header (the same size as one ScsiLun) * plus the max number of luns we are requesting. * - * XXX: Maybe allocate this once, like scsi_result, and pass it down. - * scsi_result can't be used, as it is needed for the scan INQUIRY - * data. In addition, reallocating and trying again (with the exact - * amount we need) would be nice, but then we need to somehow limit the - * size allocated based on the available memory (and limits of kmalloc). + * Reallocating and trying again (with the exact amount we need) + * would be nice, but then we need to somehow limit the size + * allocated based on the available memory and the limits of + * kmalloc - we don't want a kmalloc() failure of a huge value to + * prevent us from finding any LUNs on this target. */ length = (max_scsi_report_luns + 1) * sizeof(ScsiLun); - lun_data_pnt = (ScsiLun *) kmalloc(length, - (shpnt->unchecked_isa_dma ? GFP_DMA : GFP_ATOMIC)); - if (lun_data_pnt == NULL) { - printk("scsi: scsi_report_lun_scan: Cannot malloc\n"); - if (got_command_blocks) - scsi_release_commandblocks(SDlun0_pnt); - return 1; + lun_data = (ScsiLun *) kmalloc(length, GFP_ATOMIC | + (sdevscan->host->unchecked_isa_dma ? + GFP_DMA : 0)); + if (lun_data == NULL) { + printk(ALLOC_FAILURE_MSG, __FUNCTION__); + scsi_release_commandblocks(sdevscan); + /* + * We are out of memory, don't try scanning any further. + */ + return 0; } scsi_cmd[0] = REPORT_LUNS; @@ -1053,83 +1788,78 @@ scsi_cmd[8] = (unsigned char) (length >> 8) & 0xff; scsi_cmd[9] = (unsigned char) length & 0xff; - scsi_cmd[10] = 0; /* reserved */ - scsi_cmd[11] = 0; /* control */ - SRpnt->sr_cmd_len = 0; - SRpnt->sr_data_direction = SCSI_DATA_READ; + scsi_cmd[10] = 0; /* reserved */ + scsi_cmd[11] = 0; /* control */ + sreq->sr_cmd_len = 0; + sreq->sr_data_direction = SCSI_DATA_READ; /* - * We can get a UNIT ATTENTION, for example a power on/reset, so retry - * a few times (like sd.c does for TEST UNIT READY). Experience shows - * some combinations of adapter/devices get at least two power - * on/resets. + * We can get a UNIT ATTENTION, for example a power on/reset, so + * retry a few times (like sd.c does for TEST UNIT READY). + * Experience shows some combinations of adapter/devices get at + * least two power on/resets. * * Illegal requests (for devices that do not support REPORT LUNS) - * should come through as a check condition, and will not generate a - * retry. + * should come through as a check condition, and will not generate + * a retry. */ retries = 0; while (retries++ < 3) { - SCSI_LOG_SCAN_BUS(3, - printk("scsi: Sending REPORT LUNS to %s (try %d)\n", - devname, retries)); - - scsi_wait_req (SRpnt, (void *) scsi_cmd, (void *) lun_data_pnt, - length, SCSI_TIMEOUT+4*HZ, 3); - - SCSI_LOG_SCAN_BUS(3, - printk("scsi: REPORT LUNS %s (try %d) result 0x%x\n", - SRpnt->sr_result ? "failed" : "successful", retries, - SRpnt->sr_result)); - - if (SRpnt->sr_result == 0 - || SRpnt->sr_sense_buffer[2] != UNIT_ATTENTION) + SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: Sending" + " REPORT LUNS to %s (try %d)\n", devname, + retries)); + scsi_wait_req(sreq, (void *) scsi_cmd, (void *) lun_data, + length, SCSI_TIMEOUT + 4 * HZ, 3); + SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUNS" + " %s (try %d) result 0x%x\n", sreq->sr_result + ? "failed" : "successful", retries, + sreq->sr_result)); + if (sreq->sr_result == 0 + || sreq->sr_sense_buffer[2] != UNIT_ATTENTION) break; } + scsi_release_commandblocks(sdevscan); - scsi_release_request(SRpnt); - if (got_command_blocks) - scsi_release_commandblocks(SDlun0_pnt); - - if (SRpnt->sr_result) { - kfree((char *) lun_data_pnt); + if (sreq->sr_result) { + /* + * The device probably does not support a REPORT LUN command + */ + kfree((char *) lun_data); + scsi_release_request(sreq); return 1; } + scsi_release_request(sreq); /* - * Get the length from the first four bytes of lun_data_pnt. + * Get the length from the first four bytes of lun_data. */ - byte_pnt = (char*) lun_data_pnt->scsi_lun; - length = ((byte_pnt[0] << 24) | (byte_pnt[1] << 16) | - (byte_pnt[2] << 8) | (byte_pnt[3] << 0)); + data = (char *) lun_data->scsi_lun; + length = ((data[0] << 24) | (data[1] << 16) | + (data[2] << 8) | (data[3] << 0)); if ((length / sizeof(ScsiLun)) > max_scsi_report_luns) { - printk("scsi: On %s only %d (max_scsi_report_luns) of %d luns" - " reported, try increasing max_scsi_report_luns.\n", - devname, max_scsi_report_luns, - length / sizeof(ScsiLun)); + printk(KERN_WARNING "scsi: On %s only %d (max_scsi_report_luns)" + " of %d luns reported, try increasing" + " max_scsi_report_luns.\n", devname, + max_scsi_report_luns, length / sizeof(ScsiLun)); num_luns = max_scsi_report_luns; } else num_luns = (length / sizeof(ScsiLun)); - scsi_level = SDlun0_pnt->scsi_level; - - SCSI_LOG_SCAN_BUS(3, - printk("scsi: REPORT LUN scan of host %d channel %d id %d\n", - SDlun0_pnt->host->host_no, SDlun0_pnt->channel, - SDlun0_pnt->id)); + SCSI_LOG_SCAN_BUS(3, printk (KERN_INFO "scsi scan: REPORT LUN scan of" + " host %d channel %d id %d\n", sdevscan->host->host_no, + sdevscan->channel, sdevscan->id)); /* - * Scan the luns in lun_data_pnt. The entry at offset 0 is really + * Scan the luns in lun_data. The entry at offset 0 is really * the header, so start at 1 and go up to and including num_luns. */ - for (fcp_cur_lun_pnt = &lun_data_pnt[1]; - fcp_cur_lun_pnt <= &lun_data_pnt[num_luns]; - fcp_cur_lun_pnt++) { - lun = scsilun_to_int(fcp_cur_lun_pnt); + for (fcp_cur_lun = &lun_data[1]; + fcp_cur_lun <= &lun_data[num_luns]; fcp_cur_lun++) { + lun = scsilun_to_int(fcp_cur_lun); /* - * Check if the unused part of fcp_cur_lun_pnt is non-zero, + * Check if the unused part of fcp_cur_lun is non-zero, * and so does not fit in lun. */ - if (memcmp(&fcp_cur_lun_pnt->scsi_lun[sizeof(lun)], + if (memcmp(&fcp_cur_lun->scsi_lun[sizeof(lun)], "\0\0\0\0", 4) != 0) { int i; @@ -1138,40 +1868,37 @@ * this differs from what linux would print for the * integer LUN value. */ - printk("scsi: %s lun 0x", devname); - byte_pnt = (char*) fcp_cur_lun_pnt->scsi_lun; + printk(KERN_WARNING "scsi: %s lun 0x", devname); + data = (char *) fcp_cur_lun->scsi_lun; for (i = 0; i < sizeof(ScsiLun); i++) - printk("%02x", byte_pnt[i]); - printk(" has a LUN larger than that supported by" - " the kernel\n"); + printk("%02x", data[i]); + printk(" has a LUN larger than currently supported.\n"); } else if (lun == 0) { /* * LUN 0 has already been scanned. */ - } else if (lun > shpnt->max_lun) { - printk("scsi: %s lun %d has a LUN larger than allowed" - " by the host adapter\n", devname, lun); + } else if (lun > sdevscan->host->max_lun) { + printk(KERN_WARNING "scsi: %s lun%d has a LUN larger" + " than allowed by the host adapter\n", + devname, lun); } else { - /* - * Don't use SDlun0_pnt after this call - it can be - * overwritten via SDpnt2 if there was no real device - * at LUN 0. - */ - if (scan_scsis_single(channel, dev, lun, - scsi_level, SDpnt2, shpnt, scsi_result) - == SCSI_SCAN_NO_RESPONSE) { + int res; + + sdevscan->lun = lun; + res = scsi_probe_and_add_lun(sdevscan, NULL, NULL); + if (res == SCSI_SCAN_NO_RESPONSE) { /* * Got some results, but now none, abort. */ - printk("scsi: no response from %s lun %d while" - " scanning, scan aborted\n", devname, - lun); + printk(KERN_ERR "scsi: Unexpected response" + " from %s lun %d while scanning, scan" + " aborted\n", devname, sdevscan->lun); break; } } } - kfree((char *) lun_data_pnt); + kfree((char *) lun_data); return 0; #else @@ -1180,537 +1907,210 @@ } -/* - * Function: scan_scsis_target - * - * Purpose: Scan the given scsi target dev, and as needed all LUNs - * on the target dev. - * - * Arguments: channel - the host's channel - * dev - target dev (target id) - * SDpnt2 - pointer to pointer of a preallocated Scsi_Device - * shpnt - host device to use - * scsi_result - preallocated buffer for the SCSI command result - * - * Lock status: None - * - * Returns: void - * - * Notes: This tries to be compatible with linux 2.4.x. This function - * relies on scan_scsis_single to setup SDlun0_pnt. - * - * It would be better if the Scsi_Device allocation and freeing - * was done here, rather than oddly embedded in scan_scsis_single - * and scan_scsis. - */ -static void scan_scsis_target(unsigned int channel, unsigned int dev, - Scsi_Device **SDpnt2, struct Scsi_Host *shpnt, - char *scsi_result) -{ - int bflags, scsi_level; - Scsi_Device *SDlun0_pnt; - unsigned int sparse_lun = 0; - unsigned int max_dev_lun, lun; - unsigned int sdlun0_res; - - /* - * Scan lun 0, use the results to determine whether to scan further. - * Ideally, we would not configure LUN 0 until we scan. - */ - SDlun0_pnt = *SDpnt2; - sdlun0_res = scan_scsis_single(channel, dev, 0 /* LUN 0 */, SCSI_2, - SDpnt2, shpnt, scsi_result); - if (sdlun0_res == SCSI_SCAN_NO_RESPONSE) - return; - - /* - * If no new SDpnt was allocated (SCSI_SCAN_DEVICE_PRESENT), SDlun0_pnt - * can later be modified. It is unlikely the lun level would change, - * but save it just in case. - */ - scsi_level = SDlun0_pnt->scsi_level; - - /* - * We could probably use and save the bflags from lun 0 for all luns - * on a target, but be safe and match current behaviour. (LUN 0 - * bflags controls the target settings checked within this function.) - */ - bflags = get_device_flags (SDlun0_pnt->vendor, SDlun0_pnt->model); - - /* - * Some scsi devices cannot be polled for lun != 0 due to firmware bugs - */ - if (bflags & BLIST_NOLUN) - return; - - /* - * Ending the scan here if max_scsi_luns == 1 breaks scanning of - * SPARSE, FORCE, MAX5 LUN devices, and the report lun scans. - */ - - if (scsi_report_lun_scan(SDlun0_pnt, channel, dev, SDpnt2, shpnt, - scsi_result) == 0) - return; - - SCSI_LOG_SCAN_BUS(3, - printk("scsi: Sequential scan of host %d channel %d id %d\n", - SDlun0_pnt->host->host_no, SDlun0_pnt->channel, - SDlun0_pnt->id)); +/** + * scsi_scan_target - scan a target id, possibly including all LUNs on the + * target. + * @sdevsca: Scsi_Device handle for scanning + * @shost: host to scan + * @channel: channel to scan + * @id: target id to scan + * + * Description: + * Scan the target id on @shost, @channel, and @id. Scan at least LUN + * 0, and possibly all LUNs on the target id. + * + * Use the pre-allocated @sdevscan as a handle for the scanning. This + * function sets sdevscan->host, sdevscan->id and sdevscan->lun; the + * scanning functions modify sdevscan->lun. + * + * First try a REPORT LUN scan, if that does not scan the target, do a + * sequential scan of LUNs on the target id. + **/ +static void scsi_scan_target(Scsi_Device *sdevscan, struct Scsi_Host *shost, + unsigned int channel, unsigned int id) +{ + int bflags; + int res; - max_dev_lun = (max_scsi_luns < shpnt->max_lun ? - max_scsi_luns : shpnt->max_lun); - /* - * If this device is known to support sparse multiple units, - * override the other settings, and scan all of them. - */ - if (bflags & BLIST_SPARSELUN) { - max_dev_lun = shpnt->max_lun; - sparse_lun = 1; - } else if (sdlun0_res == SCSI_SCAN_DEVICE_PRESENT) { + if (shost->this_id == id) /* - * LUN 0 responded, but no LUN 0 was added, don't scan any - * further. This matches linux 2.4.x behaviour. + * Don't scan the host adapter */ return; - } - /* - * If less than SCSI_1_CSS, and not a forced lun scan, stop - * scanning, this matches 2.4 behaviour, but it could be a bug - * to scan SCSI_1_CSS devices past LUN 0. - */ - if ((scsi_level < SCSI_1_CCS) && ((bflags & - (BLIST_FORCELUN | BLIST_SPARSELUN | BLIST_MAX5LUN)) == 0)) - return; - /* - * If this device is known to support multiple units, override - * the other settings, and scan all of them. - */ - if (bflags & BLIST_FORCELUN) - max_dev_lun = shpnt->max_lun; - /* - * REGAL CDC-4X: avoid hang after LUN 4 - */ - if (bflags & BLIST_MAX5LUN) - max_dev_lun = min(5U, max_dev_lun); - /* - * Do not scan past LUN 7. - */ - if (scsi_level < SCSI_3) - max_dev_lun = min(8U, max_dev_lun); + sdevscan->host = shost; + sdevscan->id = id; + sdevscan->channel = channel; /* - * We have already scanned lun 0. + * Scan LUN 0, if there is some response, scan further. Ideally, we + * would not configure LUN 0 until all LUNs are scanned. + * + * The scsi_level is set (in scsi_probe_lun) if a target responds. */ - for (lun = 1; lun < max_dev_lun; ++lun) { - int res; + sdevscan->lun = 0; + res = scsi_probe_and_add_lun(sdevscan, NULL, &bflags); + if (res != SCSI_SCAN_NO_RESPONSE) { /* - * Scan until scan_scsis_single says stop, - * unless sparse_lun is set. + * Some scsi devices cannot properly handle a lun != 0. + * BLIST_NOLUN also prevents a REPORT LUN from being sent. + * Any multi-lun SCSI-3 device that hangs because of a + * REPORT LUN command is seriously broken. */ - res = scan_scsis_single(channel, dev, lun, - scsi_level, SDpnt2, shpnt, scsi_result); - if (res == SCSI_SCAN_NO_RESPONSE) { + if (!(bflags & BLIST_NOLUN)) /* - * Got a response on LUN 0, but now no response. + * Ending the scan here if max_scsi_luns == 1 + * breaks scanning of SPARSE, FORCE, MAX5 LUN + * devices, and the report lun scan. */ - printk("scsi: no response from device" - " host%d/bus%d/target%d/lun%d" - " while scanning, scan aborted\n", - shpnt->host_no, channel, dev, lun); - return; - } else if ((res == SCSI_SCAN_DEVICE_PRESENT) - && !sparse_lun) - return; - } -} - -/* - * Returns the scsi_level of lun0 on this host, channel and dev (if already - * known), otherwise returns SCSI_2. - */ -static int find_lun0_scsi_level(unsigned int channel, unsigned int dev, - struct Scsi_Host *shpnt) -{ - int res = SCSI_2; - Scsi_Device *SDpnt; - - for (SDpnt = shpnt->host_queue; SDpnt; SDpnt = SDpnt->next) - { - if ((0 == SDpnt->lun) && (dev == SDpnt->id) && - (channel == SDpnt->channel)) - return (int)SDpnt->scsi_level; - } - /* haven't found lun0, should send INQUIRY but take easy route */ - return res; -} - -#define SCSI_UID_DEV_ID 'U' -#define SCSI_UID_SER_NUM 'S' -#define SCSI_UID_UNKNOWN 'Z' - -unsigned char *scsi_get_evpd_page(Scsi_Device *SDpnt, Scsi_Request * SRpnt) -{ - unsigned char *evpd_page = NULL; - unsigned char *scsi_cmd = NULL; - int lun = SDpnt->lun; - int scsi_level = SDpnt->scsi_level; - - evpd_page = kmalloc(255, - (SDpnt->host->unchecked_isa_dma ? GFP_DMA : GFP_ATOMIC)); - if (!evpd_page) - return NULL; - - scsi_cmd = kmalloc(MAX_COMMAND_SIZE, GFP_ATOMIC); - if (!scsi_cmd) { - kfree(evpd_page); - return NULL; - } - - /* Use vital product pages to determine serial number */ - /* Try Supported vital product data pages 0x00 first */ - scsi_cmd[0] = INQUIRY; - if ((lun > 0) && (scsi_level <= SCSI_2)) - scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; - else - scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ - scsi_cmd[2] = 0x00; - scsi_cmd[3] = 0; - scsi_cmd[4] = 255; - scsi_cmd[5] = 0; - SRpnt->sr_cmd_len = 0; - SRpnt->sr_sense_buffer[0] = 0; - SRpnt->sr_sense_buffer[2] = 0; - SRpnt->sr_data_direction = SCSI_DATA_READ; - scsi_wait_req(SRpnt, (void *) scsi_cmd, (void *) evpd_page, - 255, SCSI_TIMEOUT+4*HZ, 3); - - if (SRpnt->sr_result) { - kfree(scsi_cmd); - kfree(evpd_page); - return NULL; - } - - /* check to see if response was truncated */ - if (evpd_page[3] > 255) { - int max_lgth = evpd_page[3] + 4; - - kfree(evpd_page); - evpd_page = kmalloc(max_lgth, (SDpnt->host->unchecked_isa_dma ? - GFP_DMA : GFP_ATOMIC)); - if (!evpd_page) { - kfree(scsi_cmd); - return NULL; - } - memset(scsi_cmd, 0, MAX_COMMAND_SIZE); - scsi_cmd[0] = INQUIRY; - if ((lun > 0) && (scsi_level <= SCSI_2)) - scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; - else - scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ - scsi_cmd[2] = 0x00; - scsi_cmd[3] = 0; - scsi_cmd[4] = max_lgth; - scsi_cmd[5] = 0; - SRpnt->sr_cmd_len = 0; - SRpnt->sr_sense_buffer[0] = 0; - SRpnt->sr_sense_buffer[2] = 0; - SRpnt->sr_data_direction = SCSI_DATA_READ; - scsi_wait_req(SRpnt, (void *) scsi_cmd, (void *) evpd_page, - max_lgth, SCSI_TIMEOUT+4*HZ, 3); - if (SRpnt->sr_result) { - kfree(scsi_cmd); - kfree(evpd_page); - return NULL; - } - } - kfree(scsi_cmd); - /* some ill behaved devices return the std inquiry here rather than - the evpd data. snoop the data to verify */ - if (evpd_page[3] > 16) { - /* if vend id appears in the evpd page assume evpd is invalid */ - if (!strncmp(&evpd_page[8], SDpnt->vendor, 8)) { - kfree(evpd_page); - return NULL; - } + if (scsi_report_lun_scan(sdevscan) != 0) + /* + * The REPORT LUN did not scan the target, + * do a sequential scan. + */ + scsi_sequential_lun_scan(sdevscan, bflags, res); } - return evpd_page; } -int scsi_get_deviceid(Scsi_Device *SDpnt,Scsi_Request * SRpnt) +/** + * scsi_scan_selected_lun - probe and add one LUN + * + * Description: + * Probe a single LUN on @shost, @channel, @id and @lun. If the LUN is + * found, set the queue depth, allocate command blocks, and call + * init/attach/finish of the upper level (sd, sg, etc.) drivers. + **/ +static void scsi_scan_selected_lun(struct Scsi_Host *shost, uint channel, + uint id, uint lun) { - unsigned char *id_page = NULL; - unsigned char *scsi_cmd = NULL; - int scnt, i, j, idtype; - char * id = SDpnt->sdev_driverfs_dev.name; - int lun = SDpnt->lun; - int scsi_level = SDpnt->scsi_level; - - id_page = kmalloc(255, - (SDpnt->host->unchecked_isa_dma ? GFP_DMA : GFP_ATOMIC)); - if (!id_page) - return 0; + Scsi_Device *sdevscan, *sdev = NULL; + struct Scsi_Device_Template *sdt; + int res; - scsi_cmd = kmalloc(MAX_COMMAND_SIZE, GFP_ATOMIC); - if (!scsi_cmd) - goto leave; - - /* Use vital product pages to determine serial number */ - /* Try Supported vital product data pages 0x00 first */ - scsi_cmd[0] = INQUIRY; - if ((lun > 0) && (scsi_level <= SCSI_2)) - scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; - else - scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ - scsi_cmd[2] = 0x83; - scsi_cmd[3] = 0; - scsi_cmd[4] = 255; - scsi_cmd[5] = 0; - SRpnt->sr_cmd_len = 0; - SRpnt->sr_sense_buffer[0] = 0; - SRpnt->sr_sense_buffer[2] = 0; - SRpnt->sr_data_direction = SCSI_DATA_READ; - scsi_wait_req(SRpnt, (void *) scsi_cmd, (void *) id_page, - 255, SCSI_TIMEOUT+4*HZ, 3); - if (SRpnt->sr_result) { - kfree(scsi_cmd); - goto leave; - } + if ((channel > shost->max_channel) || (id >= shost->max_id) || + (lun >= shost->max_lun)) + return; - /* check to see if response was truncated */ - if (id_page[3] > 255) { - int max_lgth = id_page[3] + 4; + sdevscan = scsi_alloc_sdev(shost, channel, id, lun); + if (sdevscan == NULL) + return; - kfree(id_page); - id_page = kmalloc(max_lgth, - (SDpnt->host->unchecked_isa_dma ? - GFP_DMA : GFP_ATOMIC)); - if (!id_page) { - kfree(scsi_cmd); - return 0; - } - memset(scsi_cmd, 0, MAX_COMMAND_SIZE); - scsi_cmd[0] = INQUIRY; - if ((lun > 0) && (scsi_level <= SCSI_2)) - scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; - else - scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ - scsi_cmd[2] = 0x83; - scsi_cmd[3] = 0; - scsi_cmd[4] = max_lgth; - scsi_cmd[5] = 0; - SRpnt->sr_cmd_len = 0; - SRpnt->sr_sense_buffer[0] = 0; - SRpnt->sr_sense_buffer[2] = 0; - SRpnt->sr_data_direction = SCSI_DATA_READ; - scsi_wait_req(SRpnt, (void *) scsi_cmd, (void *) id_page, - max_lgth, SCSI_TIMEOUT+4*HZ, 3); - if (SRpnt->sr_result) { - kfree(scsi_cmd); - goto leave; - } - } - kfree(scsi_cmd); + sdevscan->scsi_level = scsi_find_scsi_level(channel, id, shost); + res = scsi_probe_and_add_lun(sdevscan, &sdev, NULL); + scsi_free_sdev(sdevscan); + if (res == SCSI_SCAN_LUN_PRESENT) { + BUG_ON(sdev == NULL); + /* + * FIXME calling select_queue_depths is wrong for adapters + * that modify queue depths of all scsi devices - the + * adapter might change a queue depth (not for this sdev), + * but the mid-layer will not change the queue depth. This + * does not cause an oops, but queue_depth will not match + * the actual queue depth used. + * + * Perhaps use a default queue depth, and allow them to be + * modified at boot/insmod time, and/or via sysctl/ioctl/proc; + * plus have dynamic queue depth adjustment like the + * aic7xxx driver. + */ + if (shost->select_queue_depths != NULL) + (shost->select_queue_depths) (shost, shost->host_queue); - idtype = 3; - while (idtype > 0) { - for(scnt = 4; scnt <= id_page[3] + 3; - scnt += id_page[scnt + 3] + 4) { - if ((id_page[scnt + 1] & 0x0f) != idtype) { - continue; - } - if ((id_page[scnt] & 0x0f) == 2) { - for(i = scnt + 4, j = 1; - i < scnt + 4 + id_page[scnt + 3]; - i++) { - if (id_page[i] > 0x20) { - if (j == DEVICE_NAME_SIZE) { - memset(id, 0, - DEVICE_NAME_SIZE); - break; - } - id[j++] = id_page[i]; - } - } - } else if ((id_page[scnt] & 0x0f) == 1) { - static const char hex_str[]="0123456789abcdef"; - for(i = scnt + 4, j = 1; - i < scnt + 4 + id_page[scnt + 3]; - i++) { - if ((j + 1) == DEVICE_NAME_SIZE) { - memset(id, 0, DEVICE_NAME_SIZE); - break; - } - id[j++] = hex_str[(id_page[i] & 0xf0) >> - 4]; - id[j++] = hex_str[id_page[i] & 0x0f]; + for (sdt = scsi_devicelist; sdt; sdt = sdt->next) + if (sdt->init && sdt->dev_noticed) + (*sdt->init) (); + + for (sdt = scsi_devicelist; sdt; sdt = sdt->next) + if (sdt->attach) { + (*sdt->attach) (sdev); + if (sdev->attached) { + scsi_build_commandblocks(sdev); + if (sdev->has_cmdblocks == 0) + printk(ALLOC_FAILURE_MSG, + __FUNCTION__); } } - if (id[1] != 0) goto leave; - } - idtype--; - } - leave: - kfree(id_page); - if (id[1] != 0) { - id[0] = SCSI_UID_DEV_ID; - return 1; - } - return 0; -} - -int scsi_get_serialnumber(Scsi_Device *SDpnt, Scsi_Request * SRpnt) -{ - unsigned char *serialnumber_page = NULL; - unsigned char *scsi_cmd = NULL; - int lun = SDpnt->lun; - int scsi_level = SDpnt->scsi_level; - int i, j; - - serialnumber_page = kmalloc(255, (SDpnt->host->unchecked_isa_dma ? - GFP_DMA : GFP_ATOMIC)); - if (!serialnumber_page) - return 0; - - scsi_cmd = kmalloc(MAX_COMMAND_SIZE, GFP_ATOMIC); - if (!scsi_cmd) - goto leave; - - /* Use vital product pages to determine serial number */ - /* Try Supported vital product data pages 0x00 first */ - scsi_cmd[0] = INQUIRY; - if ((lun > 0) && (scsi_level <= SCSI_2)) - scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; - else - scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ - scsi_cmd[2] = 0x80; - scsi_cmd[3] = 0; - scsi_cmd[4] = 255; - scsi_cmd[5] = 0; - SRpnt->sr_cmd_len = 0; - SRpnt->sr_sense_buffer[0] = 0; - SRpnt->sr_sense_buffer[2] = 0; - SRpnt->sr_data_direction = SCSI_DATA_READ; - scsi_wait_req(SRpnt, (void *) scsi_cmd, (void *) serialnumber_page, - 255, SCSI_TIMEOUT+4*HZ, 3); - - if (SRpnt->sr_result) { - kfree(scsi_cmd); - goto leave; - } - /* check to see if response was truncated */ - if (serialnumber_page[3] > 255) { - int max_lgth = serialnumber_page[3] + 4; - - kfree(serialnumber_page); - serialnumber_page = kmalloc(max_lgth, - (SDpnt->host->unchecked_isa_dma ? - GFP_DMA : GFP_ATOMIC)); - if (!serialnumber_page) { - kfree(scsi_cmd); - return 0; - } - memset(scsi_cmd, 0, MAX_COMMAND_SIZE); - scsi_cmd[0] = INQUIRY; - if ((lun > 0) && (scsi_level <= SCSI_2)) - scsi_cmd[1] = ((lun << 5) & 0xe0) | 0x01; - else - scsi_cmd[1] = 0x01; /* SCSI_3 and higher, don't touch */ - scsi_cmd[2] = 0x80; - scsi_cmd[3] = 0; - scsi_cmd[4] = max_lgth; - scsi_cmd[5] = 0; - SRpnt->sr_cmd_len = 0; - SRpnt->sr_sense_buffer[0] = 0; - SRpnt->sr_sense_buffer[2] = 0; - SRpnt->sr_data_direction = SCSI_DATA_READ; - scsi_wait_req(SRpnt, (void *) scsi_cmd, - (void *) serialnumber_page, - max_lgth, SCSI_TIMEOUT+4*HZ, 3); - if (SRpnt->sr_result) { - kfree(scsi_cmd); - goto leave; - } - } - kfree(scsi_cmd); - - SDpnt->sdev_driverfs_dev.name[0] = SCSI_UID_SER_NUM; - for(i = 0, j = 1; i < serialnumber_page[3]; i++) { - if (serialnumber_page[4 + i] > 0x20) - SDpnt->sdev_driverfs_dev.name[j++] = - serialnumber_page[4 + i]; - } - for(i = 0, j = strlen(SDpnt->sdev_driverfs_dev.name); i < 8; i++) { - if (SDpnt->vendor[i] > 0x20) { - SDpnt->sdev_driverfs_dev.name[j++] = SDpnt->vendor[i]; - } - } - kfree(serialnumber_page); - return 1; - leave: - memset(SDpnt->sdev_driverfs_dev.name, 0, DEVICE_NAME_SIZE); - kfree(serialnumber_page); - return 0; -} -int scsi_get_default_name(Scsi_Device *SDpnt) -{ - int i, j; + for (sdt = scsi_devicelist; sdt; sdt = sdt->next) + if (sdt->finish && sdt->nr_dev) + (*sdt->finish) (); - SDpnt->sdev_driverfs_dev.name[0] = SCSI_UID_UNKNOWN; - for(i = 0, j = 1; i < 8; i++) { - if (SDpnt->vendor[i] > 0x20) { - SDpnt->sdev_driverfs_dev.name[j++] = - SDpnt->vendor[i]; - } - } - for(i = 0, j = strlen(SDpnt->sdev_driverfs_dev.name); - i < 16; i++) { - if (SDpnt->model[i] > 0x20) { - SDpnt->sdev_driverfs_dev.name[j++] = - SDpnt->model[i]; - } } - for(i = 0, j = strlen(SDpnt->sdev_driverfs_dev.name); i < 4; i++) { - if (SDpnt->rev[i] > 0x20) { - SDpnt->sdev_driverfs_dev.name[j++] = SDpnt->rev[i]; - } - } - return 1; } - -static void scsi_load_identifier(Scsi_Device *SDpnt, Scsi_Request * SRpnt) +/** + * scan_scsis - scan the given adapter, or scan a single LUN + * @shost: adapter to scan + * @hardcoded: 1 if a single channel/id/lun should be scanned, else 0 + * @hchannel: channel to scan for hardcoded case + * @hid: target id to scan for hardcoded case + * @hlun: lun to scan for hardcoded case + * + * Description: + * If @hardcoded is 1, call scsi_scan_selected_lun to scan a single + * LUN; else, iterate and call scsi_scan_target to scan all possible + * target id's on all possible channels. + **/ +void scan_scsis(struct Scsi_Host *shost, uint hardcoded, uint hchannel, + uint hid, uint hlun) { - unsigned char *evpd_page = NULL; - int cnt; - - memset(SDpnt->sdev_driverfs_dev.name, 0, DEVICE_NAME_SIZE); - evpd_page = scsi_get_evpd_page(SDpnt, SRpnt); - if (!evpd_page) { - /* try to obtain serial number anyway */ - if (!scsi_get_serialnumber(SDpnt, SRpnt)) - goto leave; - goto leave; - } - - for(cnt = 4; cnt <= evpd_page[3] + 3; cnt++) { - if (evpd_page[cnt] == 0x83) { - if (scsi_get_deviceid(SDpnt, SRpnt)) - goto leave; - } - } + if (hardcoded == 1) { + /* + * XXX Overload hchannel/hid/hlun to figure out what to + * scan, and use the standard scanning code rather than + * this function - that way, an entire bus (or fabric), or + * target id can be scanned. There are problems with queue + * depth and the init/attach/finish that must be resolved + * before (re-)scanning can handle finding more than one new + * LUN. + * + * For example, set hchannel 0 and hid to 5, and hlun to -1 + * in order to scan all LUNs on channel 0, target id 5. + */ + scsi_scan_selected_lun(shost, hchannel, hid, hlun); + } else { + Scsi_Device *sdevscan; + uint channel; + unsigned int id, order_id; - for(cnt = 4; cnt <= evpd_page[3] + 3; cnt++) { - if (evpd_page[cnt] == 0x80) { - if (scsi_get_serialnumber(SDpnt, SRpnt)) - goto leave; + /* + * The blk layer queue allocation is a bit expensive to + * repeat for each channel and id - for FCP max_id is near + * 255: each call to scsi_alloc_sdev() implies a call to + * blk_init_queue, and then blk_init_free_list, where 2 * + * queue_nr_requests requests are allocated. Don't do so + * here for scsi_scan_selected_lun, since we end up + * calling select_queue_depths with an extra Scsi_Device + * on the host_queue list. + */ + sdevscan = scsi_alloc_sdev(shost, 0, 0, 0); + if (sdevscan == NULL) + return; + /* + * The sdevscan host, channel, id and lun are filled in as + * needed to scan. + */ + for (channel = 0; channel <= shost->max_channel; channel++) { + /* + * XXX adapter drivers when possible (FCP, iSCSI) + * could modify max_id to match the current max, + * not the absolute max. + * + * XXX add a shost id iterator, so for example, + * the FC ID can be the same as a target id + * without a huge overhead of sparse id's. + */ + for (id = 0; id < shost->max_id; ++id) { + if (shost->reverse_ordering) + /* + * Scan from high to low id. + */ + order_id = shost->max_id - id - 1; + else + order_id = id; + scsi_scan_target(sdevscan, shost, channel, + order_id); + } } + scsi_free_sdev(sdevscan); } - -leave: - if (SDpnt->sdev_driverfs_dev.name[0] == 0) - scsi_get_default_name(SDpnt); - - if (evpd_page) kfree(evpd_page); - return; } diff -Nru a/drivers/scsi/sg.c b/drivers/scsi/sg.c --- a/drivers/scsi/sg.c Fri Aug 16 14:34:57 2002 +++ b/drivers/scsi/sg.c Fri Aug 16 14:34:57 2002 @@ -19,9 +19,9 @@ */ #include #ifdef CONFIG_PROC_FS - static char * sg_version_str = "Version: 3.5.26 (20020708)"; +static char *sg_version_str = "Version: 3.5.27 (20020812)"; #endif - static int sg_version_num = 30525; /* 2 digits for each component */ +static int sg_version_num = 30527; /* 2 digits for each component */ /* * D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au), notes: * - scsi logging is available via SCSI_LOG_TIMEOUT macros. First @@ -74,15 +74,10 @@ #ifndef LINUX_VERSION_CODE #include -#endif /* LINUX_VERSION_CODE */ +#endif /* LINUX_VERSION_CODE */ #define SG_ALLOW_DIO_DEF 0 -/* #define SG_ALLOW_DIO_CODE */ /* compile out be commenting this define */ -#ifdef SG_ALLOW_DIO_CODE -#include -#define SG_NEW_KIOVEC 0 /* use alloc_kiovec(), not alloc_kiovec_sz() */ -#endif - +#define SG_ALLOW_DIO_CODE /* compile out by commenting this define */ #define SG_MAX_DEVS_MASK ((1U << KDEV_MINOR_BITS) - 1) @@ -93,1484 +88,1494 @@ for use by this file descriptor. [Deprecated usage: this variable is also readable via /proc/sys/kernel/sg-big-buff if the sg driver is built into the kernel (i.e. it is not a module).] */ -static int def_reserved_size = -1; /* picks up init parameter */ +static int def_reserved_size = -1; /* picks up init parameter */ static int sg_allow_dio = SG_ALLOW_DIO_DEF; #define SG_SECTOR_SZ 512 #define SG_SECTOR_MSK (SG_SECTOR_SZ - 1) -#define SG_HEAP_PAGE 1 /* heap from kernel via get_free_pages() */ -#define SG_HEAP_KMAL 2 /* heap from kernel via kmalloc() */ -#define SG_USER_MEM 4 /* memory belongs to user space */ - -#define SG_DEV_ARR_LUMP 6 /* amount to over allocate sg_dev_arr by */ - +#define SG_DEV_ARR_LUMP 6 /* amount to over allocate sg_dev_arr by */ static int sg_init(void); static int sg_attach(Scsi_Device *); -static void sg_finish(void); static int sg_detect(Scsi_Device *); static void sg_detach(Scsi_Device *); -static Scsi_Request * dummy_cmdp; /* only used for sizeof */ +static Scsi_Request *dummy_cmdp; /* only used for sizeof */ -static rwlock_t sg_dev_arr_lock = RW_LOCK_UNLOCKED; /* Also used to lock - file descriptor list for device */ +static rwlock_t sg_dev_arr_lock = RW_LOCK_UNLOCKED; /* Also used to lock + file descriptor list for device */ -static struct Scsi_Device_Template sg_template = -{ - module:THIS_MODULE, - tag:"sg", - scsi_type:0xff, - major:SCSI_GENERIC_MAJOR, - detect:sg_detect, - init:sg_init, - finish:sg_finish, - attach:sg_attach, - detach:sg_detach +static struct Scsi_Device_Template sg_template = { + .module = THIS_MODULE, + .name = "generic", + .tag = "sg", + .scsi_type = 0xff, + .major = SCSI_GENERIC_MAJOR, + .detect = sg_detect, + .init = sg_init, + .attach = sg_attach, + .detach = sg_detach }; +typedef struct sg_scatter_hold { /* holding area for scsi scatter gather info */ + unsigned short k_use_sg; /* Count of kernel scatter-gather pieces */ + unsigned short sglist_len; /* size of malloc'd scatter-gather list ++ */ + unsigned bufflen; /* Size of (aggregate) data buffer */ + unsigned b_malloc_len; /* actual len malloc'ed in buffer */ + void *buffer; /* Data buffer or scatter list (k_use_sg>0) */ + char dio_in_use; /* 0->indirect IO (or mmap), 1->dio */ + unsigned char cmd_opcode; /* first byte of command */ +} Sg_scatter_hold; -typedef struct sg_scatter_hold /* holding area for scsi scatter gather info */ -{ - unsigned short k_use_sg; /* Count of kernel scatter-gather pieces */ - unsigned short sglist_len; /* size of malloc'd scatter-gather list ++ */ - unsigned bufflen; /* Size of (aggregate) data buffer */ - unsigned b_malloc_len; /* actual len malloc'ed in buffer */ - void * buffer; /* Data buffer or scatter list + mem_src_arr */ -#ifdef SG_ALLOW_DIO_CODE - struct kiobuf * kiobp; /* for direct IO information */ - char mapped; /* indicates kiobp has locked pages */ -#endif - char buffer_mem_src; /* heap whereabouts of 'buffer' */ - unsigned char cmd_opcode; /* first byte of command */ -} Sg_scatter_hold; /* 24 bytes long on i386 */ - -struct sg_device; /* forward declarations */ +struct sg_device; /* forward declarations */ struct sg_fd; -typedef struct sg_request /* SG_MAX_QUEUE requests outstanding per file */ -{ - Scsi_Request * my_cmdp; /* != 0 when request with lower levels */ - struct sg_request * nextrp; /* NULL -> tail request (slist) */ - struct sg_fd * parentfp; /* NULL -> not in use */ - Sg_scatter_hold data; /* hold buffer, perhaps scatter list */ - sg_io_hdr_t header; /* scsi command+info, see */ - unsigned char sense_b[sizeof(dummy_cmdp->sr_sense_buffer)]; - char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ - char orphan; /* 1 -> drop on sight, 0 -> normal */ - char sg_io_owned; /* 1 -> packet belongs to SG_IO */ - volatile char done; /* 0->before bh, 1->before read, 2->read */ -} Sg_request; /* 168 bytes long on i386 */ - -typedef struct sg_fd /* holds the state of a file descriptor */ -{ - struct sg_fd * nextfp; /* NULL when last opened fd on this device */ - struct sg_device * parentdp; /* owning device */ - wait_queue_head_t read_wait; /* queue read until command done */ - rwlock_t rq_list_lock; /* protect access to list in req_arr */ - int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ - Sg_scatter_hold reserve; /* buffer held for this file descriptor */ - unsigned save_scat_len; /* original length of trunc. scat. element */ - Sg_request * headrp; /* head of request slist, NULL->empty */ - struct fasync_struct * async_qp; /* used by asynchronous notification */ - Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */ - char low_dma; /* as in parent but possibly overridden to 1 */ - char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ - volatile char closed; /* 1 -> fd closed but request(s) outstanding */ - char fd_mem_src; /* heap whereabouts of this Sg_fd object */ - char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ - char next_cmd_len; /* 0 -> automatic (def), >0 -> use on next write() */ - char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */ - char mmap_called; /* 0 -> mmap() never called on this fd */ -} Sg_fd; /* 2760 bytes long on i386 */ - -typedef struct sg_device /* holds the state of each scsi generic device */ -{ - Scsi_Device * device; - wait_queue_head_t o_excl_wait; /* queue open() when O_EXCL in use */ - int sg_tablesize; /* adapter's max scatter-gather table size */ - Sg_fd * headfp; /* first open fd belonging to this device */ - devfs_handle_t de; - kdev_t i_rdev; /* holds device major+minor number */ - volatile char detached; /* 0->attached, 1->detached pending removal */ - volatile char exclude; /* opened for exclusive access */ - char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ - struct device sg_driverfs_dev; -} Sg_device; /* 36 bytes long on i386 */ - +typedef struct sg_request { /* SG_MAX_QUEUE requests outstanding per file */ + Scsi_Request *my_cmdp; /* != 0 when request with lower levels */ + struct sg_request *nextrp; /* NULL -> tail request (slist) */ + struct sg_fd *parentfp; /* NULL -> not in use */ + Sg_scatter_hold data; /* hold buffer, perhaps scatter list */ + sg_io_hdr_t header; /* scsi command+info, see */ + unsigned char sense_b[sizeof (dummy_cmdp->sr_sense_buffer)]; + char res_used; /* 1 -> using reserve buffer, 0 -> not ... */ + char orphan; /* 1 -> drop on sight, 0 -> normal */ + char sg_io_owned; /* 1 -> packet belongs to SG_IO */ + volatile char done; /* 0->before bh, 1->before read, 2->read */ +} Sg_request; + +typedef struct sg_fd { /* holds the state of a file descriptor */ + struct sg_fd *nextfp; /* NULL when last opened fd on this device */ + struct sg_device *parentdp; /* owning device */ + wait_queue_head_t read_wait; /* queue read until command done */ + rwlock_t rq_list_lock; /* protect access to list in req_arr */ + int timeout; /* defaults to SG_DEFAULT_TIMEOUT */ + Sg_scatter_hold reserve; /* buffer held for this file descriptor */ + unsigned save_scat_len; /* original length of trunc. scat. element */ + Sg_request *headrp; /* head of request slist, NULL->empty */ + struct fasync_struct *async_qp; /* used by asynchronous notification */ + Sg_request req_arr[SG_MAX_QUEUE]; /* used as singly-linked list */ + char low_dma; /* as in parent but possibly overridden to 1 */ + char force_packid; /* 1 -> pack_id input to read(), 0 -> ignored */ + volatile char closed; /* 1 -> fd closed but request(s) outstanding */ + char cmd_q; /* 1 -> allow command queuing, 0 -> don't */ + char next_cmd_len; /* 0 -> automatic (def), >0 -> use on next write() */ + char keep_orphan; /* 0 -> drop orphan (def), 1 -> keep for read() */ + char mmap_called; /* 0 -> mmap() never called on this fd */ +} Sg_fd; + +typedef struct sg_device { /* holds the state of each scsi generic device */ + Scsi_Device *device; + wait_queue_head_t o_excl_wait; /* queue open() when O_EXCL in use */ + int sg_tablesize; /* adapter's max scatter-gather table size */ + Sg_fd *headfp; /* first open fd belonging to this device */ + devfs_handle_t de; + kdev_t i_rdev; /* holds device major+minor number */ + volatile char detached; /* 0->attached, 1->detached pending removal */ + volatile char exclude; /* opened for exclusive access */ + char sgdebug; /* 0->off, 1->sense, 9->dump dev, 10-> all devs */ + struct device sg_driverfs_dev; +} Sg_device; -static int sg_fasync(int fd, struct file * filp, int mode); -static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt); +static int sg_fasync(int fd, struct file *filp, int mode); +static void sg_cmd_done(Scsi_Cmnd * SCpnt); /* tasklet or soft irq callback */ static int sg_start_req(Sg_request * srp); static void sg_finish_rem_req(Sg_request * srp); -static int sg_build_indi(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size); +static int sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size); static int sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize); -static ssize_t sg_new_read(Sg_fd * sfp, char * buf, size_t count, +static ssize_t sg_new_read(Sg_fd * sfp, char *buf, size_t count, Sg_request * srp); -static ssize_t sg_new_write(Sg_fd * sfp, const char * buf, size_t count, - int blocking, int read_only, Sg_request ** o_srp); +static ssize_t sg_new_write(Sg_fd * sfp, const char *buf, size_t count, + int blocking, int read_only, Sg_request ** o_srp); static int sg_common_write(Sg_fd * sfp, Sg_request * srp, - unsigned char * cmnd, int timeout, int blocking); + unsigned char *cmnd, int timeout, int blocking); static int sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind, - int wr_xf, int * countp, unsigned char ** up); + int wr_xf, int *countp, unsigned char **up); static int sg_write_xfer(Sg_request * srp); static int sg_read_xfer(Sg_request * srp); -static int sg_read_oxfer(Sg_request * srp, char * outp, int num_read_xfer); +static int sg_read_oxfer(Sg_request * srp, char *outp, int num_read_xfer); static void sg_remove_scat(Sg_scatter_hold * schp); -static char * sg_get_sgat_msa(Sg_scatter_hold * schp); static void sg_build_reserve(Sg_fd * sfp, int req_size); static void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size); static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp); -static char * sg_malloc(const Sg_fd * sfp, int size, int * retSzp, - int * mem_srcp); -static void sg_free(char * buff, int size, int mem_src); -static char * sg_low_malloc(int rqSz, int lowDma, int mem_src, - int * retSzp); -static void sg_low_free(char * buff, int size, int mem_src); -static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev); +static char *sg_page_malloc(int rqSz, int lowDma, int *retSzp); +static void sg_page_free(char *buff, int size); +static Sg_fd *sg_add_sfp(Sg_device * sdp, int dev); static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp); static void __sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp); -static Sg_request * sg_get_rq_mark(Sg_fd * sfp, int pack_id); -static Sg_request * sg_add_request(Sg_fd * sfp); +static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id); +static Sg_request *sg_add_request(Sg_fd * sfp); static int sg_remove_request(Sg_fd * sfp, Sg_request * srp); static int sg_res_in_use(Sg_fd * sfp); static int sg_ms_to_jif(unsigned int msecs); static inline unsigned sg_jif_to_ms(int jifs); static int sg_allow_access(unsigned char opcode, char dev_type); -static int sg_build_dir(Sg_request * srp, Sg_fd * sfp, int dxfer_len); -static void sg_unmap_and(Sg_scatter_hold * schp, int free_also); -static Sg_device * sg_get_dev(int dev); -static inline unsigned char * sg_scatg2virt(const struct scatterlist * sclp); +static int sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len); +// static void sg_unmap_and(Sg_scatter_hold * schp, int free_also); +static Sg_device *sg_get_dev(int dev); +static inline unsigned char *sg_scatg2virt(const struct scatterlist *sclp); #ifdef CONFIG_PROC_FS static int sg_last_dev(void); #endif -#ifdef SG_ALLOW_DIO_CODE -static inline int sg_alloc_kiovec(int nr, struct kiobuf **bufp, int *szp); -static inline void sg_free_kiovec(int nr, struct kiobuf **bufp, int *szp); -#endif -static Sg_device ** sg_dev_arr = NULL; +static Sg_device **sg_dev_arr = NULL; #define SZ_SG_HEADER sizeof(struct sg_header) #define SZ_SG_IO_HDR sizeof(sg_io_hdr_t) #define SZ_SG_IOVEC sizeof(sg_iovec_t) #define SZ_SG_REQ_INFO sizeof(sg_req_info_t) - -static int sg_open(struct inode * inode, struct file * filp) +static int +sg_open(struct inode *inode, struct file *filp) { - int dev = minor(inode->i_rdev); - int flags = filp->f_flags; - Sg_device * sdp; - Sg_fd * sfp; - int res; - int retval = -EBUSY; - - SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags)); - sdp = sg_get_dev(dev); - if ((! sdp) || (! sdp->device)) - return -ENXIO; - if (sdp->detached) - return -ENODEV; - - /* This driver's module count bumped by fops_get in */ - /* Prevent the device driver from vanishing while we sleep */ - if (sdp->device->host->hostt->module) - __MOD_INC_USE_COUNT(sdp->device->host->hostt->module); - sdp->device->access_count++; - - if (! ((flags & O_NONBLOCK) || - scsi_block_when_processing_errors(sdp->device))) { - retval = -ENXIO; - /* we are in error recovery for this device */ - goto error_out; - } - - if (flags & O_EXCL) { - if (O_RDONLY == (flags & O_ACCMODE)) { - retval = -EPERM; /* Can't lock it with read only access */ - goto error_out; - } - if (sdp->headfp && (flags & O_NONBLOCK)) - goto error_out; - res = 0; - __wait_event_interruptible(sdp->o_excl_wait, - ((sdp->headfp || sdp->exclude) ? 0 : (sdp->exclude = 1)), - res); - if (res) { - retval = res; /* -ERESTARTSYS because signal hit process */ - goto error_out; - } - } - else if (sdp->exclude) { /* some other fd has an exclusive lock on dev */ - if (flags & O_NONBLOCK) - goto error_out; - res = 0; - __wait_event_interruptible(sdp->o_excl_wait, (! sdp->exclude), res); - if (res) { - retval = res; /* -ERESTARTSYS because signal hit process */ - goto error_out; - } - } - if (sdp->detached) { - retval = -ENODEV; - goto error_out; - } - if (! sdp->headfp) { /* no existing opens on this device */ - sdp->sgdebug = 0; - sdp->sg_tablesize = sdp->device->host->sg_tablesize; - } - if ((sfp = sg_add_sfp(sdp, dev))) - filp->private_data = sfp; - else { - if (flags & O_EXCL) sdp->exclude = 0; /* undo if error */ - retval = -ENOMEM; - goto error_out; - } - return 0; - -error_out: - sdp->device->access_count--; - if ((! sdp->detached) && sdp->device->host->hostt->module) - __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); - return retval; + int dev = minor(inode->i_rdev); + int flags = filp->f_flags; + Sg_device *sdp; + Sg_fd *sfp; + int res; + int retval = -EBUSY; + + SCSI_LOG_TIMEOUT(3, printk("sg_open: dev=%d, flags=0x%x\n", dev, flags)); + sdp = sg_get_dev(dev); + if ((!sdp) || (!sdp->device)) + return -ENXIO; + if (sdp->detached) + return -ENODEV; + + /* This driver's module count bumped by fops_get in */ + /* Prevent the device driver from vanishing while we sleep */ + if (sdp->device->host->hostt->module) + __MOD_INC_USE_COUNT(sdp->device->host->hostt->module); + sdp->device->access_count++; + + if (!((flags & O_NONBLOCK) || + scsi_block_when_processing_errors(sdp->device))) { + retval = -ENXIO; + /* we are in error recovery for this device */ + goto error_out; + } + + if (flags & O_EXCL) { + if (O_RDONLY == (flags & O_ACCMODE)) { + retval = -EPERM; /* Can't lock it with read only access */ + goto error_out; + } + if (sdp->headfp && (flags & O_NONBLOCK)) + goto error_out; + res = 0; + __wait_event_interruptible(sdp->o_excl_wait, + ((sdp->headfp || sdp->exclude) ? 0 : (sdp->exclude = 1)), res); + if (res) { + retval = res; /* -ERESTARTSYS because signal hit process */ + goto error_out; + } + } else if (sdp->exclude) { /* some other fd has an exclusive lock on dev */ + if (flags & O_NONBLOCK) + goto error_out; + res = 0; + __wait_event_interruptible(sdp->o_excl_wait, (!sdp->exclude), + res); + if (res) { + retval = res; /* -ERESTARTSYS because signal hit process */ + goto error_out; + } + } + if (sdp->detached) { + retval = -ENODEV; + goto error_out; + } + if (!sdp->headfp) { /* no existing opens on this device */ + sdp->sgdebug = 0; + sdp->sg_tablesize = sdp->device->host->sg_tablesize; + } + if ((sfp = sg_add_sfp(sdp, dev))) + filp->private_data = sfp; + else { + if (flags & O_EXCL) + sdp->exclude = 0; /* undo if error */ + retval = -ENOMEM; + goto error_out; + } + return 0; + + error_out: + sdp->device->access_count--; + if ((!sdp->detached) && sdp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); + return retval; } /* Following function was formerly called 'sg_close' */ -static int sg_release(struct inode * inode, struct file * filp) +static int +sg_release(struct inode *inode, struct file *filp) { - Sg_device * sdp; - Sg_fd * sfp; + Sg_device *sdp; + Sg_fd *sfp; - if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp))) - return -ENXIO; - SCSI_LOG_TIMEOUT(3, printk("sg_release: dev=%d\n", minor(sdp->i_rdev))); - sg_fasync(-1, filp, 0); /* remove filp from async notification list */ - if (0 == sg_remove_sfp(sdp, sfp)) { /* Returns 1 when sdp gone */ - if (! sdp->detached) { - sdp->device->access_count--; - if (sdp->device->host->hostt->module) - __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); - } - sdp->exclude = 0; - wake_up_interruptible(&sdp->o_excl_wait); - } - return 0; -} - -static ssize_t sg_read(struct file * filp, char * buf, - size_t count, loff_t *ppos) -{ - int k, res; - Sg_device * sdp; - Sg_fd * sfp; - Sg_request * srp; - int req_pack_id = -1; - struct sg_header old_hdr; - sg_io_hdr_t new_hdr; - sg_io_hdr_t * hp; - - if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp))) - return -ENXIO; - SCSI_LOG_TIMEOUT(3, printk("sg_read: dev=%d, count=%d\n", - minor(sdp->i_rdev), (int)count)); - if (ppos != &filp->f_pos) - ; /* FIXME: Hmm. Seek to the right place, or fail? */ - if ((k = verify_area(VERIFY_WRITE, buf, count))) - return k; - if (sfp->force_packid && (count >= SZ_SG_HEADER)) { - if (__copy_from_user(&old_hdr, buf, SZ_SG_HEADER)) - return -EFAULT; - if (old_hdr.reply_len < 0) { - if (count >= SZ_SG_IO_HDR) { - if (__copy_from_user(&new_hdr, buf, SZ_SG_IO_HDR)) - return -EFAULT; - req_pack_id = new_hdr.pack_id; - } + if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) + return -ENXIO; + SCSI_LOG_TIMEOUT(3, printk("sg_release: dev=%d\n", minor(sdp->i_rdev))); + sg_fasync(-1, filp, 0); /* remove filp from async notification list */ + if (0 == sg_remove_sfp(sdp, sfp)) { /* Returns 1 when sdp gone */ + if (!sdp->detached) { + sdp->device->access_count--; + if (sdp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(sdp->device->host->hostt-> + module); + } + sdp->exclude = 0; + wake_up_interruptible(&sdp->o_excl_wait); } - else - req_pack_id = old_hdr.pack_id; - } - srp = sg_get_rq_mark(sfp, req_pack_id); - if (! srp) { /* now wait on packet to arrive */ - if (sdp->detached) - return -ENODEV; - if (filp->f_flags & O_NONBLOCK) - return -EAGAIN; - while (1) { - res = 0; /* following is a macro that beats race condition */ - __wait_event_interruptible(sfp->read_wait, (sdp->detached || - (srp = sg_get_rq_mark(sfp, req_pack_id))), res); - if (sdp->detached) - return -ENODEV; - if (0 == res) - break; - return res; /* -ERESTARTSYS because signal hit process */ + return 0; +} + +static ssize_t +sg_read(struct file *filp, char *buf, size_t count, loff_t * ppos) +{ + int k, res; + Sg_device *sdp; + Sg_fd *sfp; + Sg_request *srp; + int req_pack_id = -1; + struct sg_header old_hdr; + sg_io_hdr_t new_hdr; + sg_io_hdr_t *hp; + + if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) + return -ENXIO; + SCSI_LOG_TIMEOUT(3, printk("sg_read: dev=%d, count=%d\n", + minor(sdp->i_rdev), (int) count)); + if (ppos != &filp->f_pos) ; /* FIXME: Hmm. Seek to the right place, or fail? */ + if ((k = verify_area(VERIFY_WRITE, buf, count))) + return k; + if (sfp->force_packid && (count >= SZ_SG_HEADER)) { + if (__copy_from_user(&old_hdr, buf, SZ_SG_HEADER)) + return -EFAULT; + if (old_hdr.reply_len < 0) { + if (count >= SZ_SG_IO_HDR) { + if (__copy_from_user + (&new_hdr, buf, SZ_SG_IO_HDR)) + return -EFAULT; + req_pack_id = new_hdr.pack_id; + } + } else + req_pack_id = old_hdr.pack_id; } - } - if (srp->header.interface_id != '\0') - return sg_new_read(sfp, buf, count, srp); - - hp = &srp->header; - memset(&old_hdr, 0, SZ_SG_HEADER); - old_hdr.reply_len = (int)hp->timeout; - old_hdr.pack_len = old_hdr.reply_len; /* very old, strange behaviour */ - old_hdr.pack_id = hp->pack_id; - old_hdr.twelve_byte = + srp = sg_get_rq_mark(sfp, req_pack_id); + if (!srp) { /* now wait on packet to arrive */ + if (sdp->detached) + return -ENODEV; + if (filp->f_flags & O_NONBLOCK) + return -EAGAIN; + while (1) { + res = 0; /* following is a macro that beats race condition */ + __wait_event_interruptible(sfp->read_wait, + (sdp->detached || (srp = sg_get_rq_mark(sfp, req_pack_id))), + res); + if (sdp->detached) + return -ENODEV; + if (0 == res) + break; + return res; /* -ERESTARTSYS because signal hit process */ + } + } + if (srp->header.interface_id != '\0') + return sg_new_read(sfp, buf, count, srp); + + hp = &srp->header; + memset(&old_hdr, 0, SZ_SG_HEADER); + old_hdr.reply_len = (int) hp->timeout; + old_hdr.pack_len = old_hdr.reply_len; /* very old, strange behaviour */ + old_hdr.pack_id = hp->pack_id; + old_hdr.twelve_byte = ((srp->data.cmd_opcode >= 0xc0) && (12 == hp->cmd_len)) ? 1 : 0; - old_hdr.target_status = hp->masked_status; - old_hdr.host_status = hp->host_status; - old_hdr.driver_status = hp->driver_status; - if ((CHECK_CONDITION & hp->masked_status) || - (DRIVER_SENSE & hp->driver_status)) - memcpy(old_hdr.sense_buffer, srp->sense_b, - sizeof(old_hdr.sense_buffer)); - switch (hp->host_status) - { /* This setup of 'result' is for backward compatibility and is best - ignored by the user who should use target, host + driver status */ + old_hdr.target_status = hp->masked_status; + old_hdr.host_status = hp->host_status; + old_hdr.driver_status = hp->driver_status; + if ((CHECK_CONDITION & hp->masked_status) || + (DRIVER_SENSE & hp->driver_status)) + memcpy(old_hdr.sense_buffer, srp->sense_b, + sizeof (old_hdr.sense_buffer)); + switch (hp->host_status) { + /* This setup of 'result' is for backward compatibility and is best + ignored by the user who should use target, host + driver status */ case DID_OK: case DID_PASSTHROUGH: case DID_SOFT_ERROR: - old_hdr.result = 0; - break; + old_hdr.result = 0; + break; case DID_NO_CONNECT: case DID_BUS_BUSY: case DID_TIME_OUT: - old_hdr.result = EBUSY; - break; + old_hdr.result = EBUSY; + break; case DID_BAD_TARGET: case DID_ABORT: case DID_PARITY: case DID_RESET: case DID_BAD_INTR: - old_hdr.result = EIO; - break; + old_hdr.result = EIO; + break; case DID_ERROR: - old_hdr.result = - (srp->sense_b[0] == 0 && hp->masked_status == GOOD) ? 0 : EIO; - break; + old_hdr.result = (srp->sense_b[0] == 0 && + hp->masked_status == GOOD) ? 0 : EIO; + break; default: - old_hdr.result = EIO; - break; - } - - /* Now copy the result back to the user buffer. */ - if (count >= SZ_SG_HEADER) { - if (__copy_to_user(buf, &old_hdr, SZ_SG_HEADER)) - return -EFAULT; - buf += SZ_SG_HEADER; - if (count > old_hdr.reply_len) - count = old_hdr.reply_len; - if (count > SZ_SG_HEADER) { - if ((res = sg_read_oxfer(srp, buf, count - SZ_SG_HEADER))) - return -EFAULT; + old_hdr.result = EIO; + break; } - } - else - count = (old_hdr.result == 0) ? 0 : -EIO; - sg_finish_rem_req(srp); - return count; -} - -static ssize_t sg_new_read(Sg_fd * sfp, char * buf, size_t count, - Sg_request * srp) -{ - sg_io_hdr_t * hp = &srp->header; - int err = 0; - int len; - - if (count < SZ_SG_IO_HDR) { - err = -EINVAL; - goto err_out; - } - hp->sb_len_wr = 0; - if ((hp->mx_sb_len > 0) && hp->sbp) { - if ((CHECK_CONDITION & hp->masked_status) || - (DRIVER_SENSE & hp->driver_status)) { - int sb_len = sizeof(dummy_cmdp->sr_sense_buffer); - sb_len = (hp->mx_sb_len > sb_len) ? sb_len : hp->mx_sb_len; - len = 8 + (int)srp->sense_b[7]; /* Additional sense length field */ - len = (len > sb_len) ? sb_len : len; - if ((err = verify_area(VERIFY_WRITE, hp->sbp, len))) + + /* Now copy the result back to the user buffer. */ + if (count >= SZ_SG_HEADER) { + if (__copy_to_user(buf, &old_hdr, SZ_SG_HEADER)) + return -EFAULT; + buf += SZ_SG_HEADER; + if (count > old_hdr.reply_len) + count = old_hdr.reply_len; + if (count > SZ_SG_HEADER) { + if ((res = + sg_read_oxfer(srp, buf, count - SZ_SG_HEADER))) + return -EFAULT; + } + } else + count = (old_hdr.result == 0) ? 0 : -EIO; + sg_finish_rem_req(srp); + return count; +} + +static ssize_t +sg_new_read(Sg_fd * sfp, char *buf, size_t count, Sg_request * srp) +{ + sg_io_hdr_t *hp = &srp->header; + int err = 0; + int len; + + if (count < SZ_SG_IO_HDR) { + err = -EINVAL; goto err_out; - if (__copy_to_user(hp->sbp, srp->sense_b, len)) { + } + hp->sb_len_wr = 0; + if ((hp->mx_sb_len > 0) && hp->sbp) { + if ((CHECK_CONDITION & hp->masked_status) || + (DRIVER_SENSE & hp->driver_status)) { + int sb_len = sizeof (dummy_cmdp->sr_sense_buffer); + sb_len = (hp->mx_sb_len > sb_len) ? sb_len : hp->mx_sb_len; + len = 8 + (int) srp->sense_b[7]; /* Additional sense length field */ + len = (len > sb_len) ? sb_len : len; + if ((err = verify_area(VERIFY_WRITE, hp->sbp, len))) + goto err_out; + if (__copy_to_user(hp->sbp, srp->sense_b, len)) { + err = -EFAULT; + goto err_out; + } + hp->sb_len_wr = len; + } + } + if (hp->masked_status || hp->host_status || hp->driver_status) + hp->info |= SG_INFO_CHECK; + if (copy_to_user(buf, hp, SZ_SG_IO_HDR)) { err = -EFAULT; goto err_out; - } - hp->sb_len_wr = len; } - } - if (hp->masked_status || hp->host_status || hp->driver_status) - hp->info |= SG_INFO_CHECK; - if (copy_to_user(buf, hp, SZ_SG_IO_HDR)) { - err = -EFAULT; - goto err_out; - } - err = sg_read_xfer(srp); -err_out: - sg_finish_rem_req(srp); - return (0 == err) ? count : err; -} - - -static ssize_t sg_write(struct file * filp, const char * buf, - size_t count, loff_t *ppos) -{ - int mxsize, cmd_size, k; - int input_size, blocking; - unsigned char opcode; - Sg_device * sdp; - Sg_fd * sfp; - Sg_request * srp; - struct sg_header old_hdr; - sg_io_hdr_t * hp; - unsigned char cmnd[sizeof(dummy_cmdp->sr_cmnd)]; - - if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp))) - return -ENXIO; - SCSI_LOG_TIMEOUT(3, printk("sg_write: dev=%d, count=%d\n", - minor(sdp->i_rdev), (int)count)); - if (sdp->detached) - return -ENODEV; - if (! ((filp->f_flags & O_NONBLOCK) || - scsi_block_when_processing_errors(sdp->device))) - return -ENXIO; - if (ppos != &filp->f_pos) - ; /* FIXME: Hmm. Seek to the right place, or fail? */ - - if ((k = verify_area(VERIFY_READ, buf, count))) - return k; /* protects following copy_from_user()s + get_user()s */ - if (count < SZ_SG_HEADER) - return -EIO; - if (__copy_from_user(&old_hdr, buf, SZ_SG_HEADER)) - return -EFAULT; - blocking = !(filp->f_flags & O_NONBLOCK); - if (old_hdr.reply_len < 0) - return sg_new_write(sfp, buf, count, blocking, 0, NULL); - if (count < (SZ_SG_HEADER + 6)) - return -EIO; /* The minimum scsi command length is 6 bytes. */ - - if (! (srp = sg_add_request(sfp))) { - SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n")); - return -EDOM; - } - buf += SZ_SG_HEADER; - __get_user(opcode, buf); - if (sfp->next_cmd_len > 0) { - if (sfp->next_cmd_len > MAX_COMMAND_SIZE) { - SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n")); - sfp->next_cmd_len = 0; - sg_remove_request(sfp, srp); - return -EIO; - } - cmd_size = sfp->next_cmd_len; - sfp->next_cmd_len = 0; /* reset so only this write() effected */ - } - else { - cmd_size = COMMAND_SIZE(opcode); /* based on SCSI command group */ - if ((opcode >= 0xc0) && old_hdr.twelve_byte) - cmd_size = 12; - } - SCSI_LOG_TIMEOUT(4, printk("sg_write: scsi opcode=0x%02x, cmd_size=%d\n", - (int)opcode, cmd_size)); -/* Determine buffer size. */ - input_size = count - cmd_size; - mxsize = (input_size > old_hdr.reply_len) ? input_size : - old_hdr.reply_len; - mxsize -= SZ_SG_HEADER; - input_size -= SZ_SG_HEADER; - if (input_size < 0) { - sg_remove_request(sfp, srp); - return -EIO; /* User did not pass enough bytes for this command. */ - } - hp = &srp->header; - hp->interface_id = '\0'; /* indicator of old interface tunnelled */ - hp->cmd_len = (unsigned char)cmd_size; - hp->iovec_count = 0; - hp->mx_sb_len = 0; - if (input_size > 0) - hp->dxfer_direction = (old_hdr.reply_len > SZ_SG_HEADER) ? - SG_DXFER_TO_FROM_DEV : SG_DXFER_TO_DEV; - else - hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : - SG_DXFER_NONE; - hp->dxfer_len = mxsize; - hp->dxferp = (unsigned char *)buf + cmd_size; - hp->sbp = NULL; - hp->timeout = old_hdr.reply_len; /* structure abuse ... */ - hp->flags = input_size; /* structure abuse ... */ - hp->pack_id = old_hdr.pack_id; - hp->usr_ptr = NULL; - if (__copy_from_user(cmnd, buf, cmd_size)) - return -EFAULT; - k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking); - return (k < 0) ? k : count; -} - -static ssize_t sg_new_write(Sg_fd * sfp, const char * buf, size_t count, - int blocking, int read_only, Sg_request ** o_srp) -{ - int k; - Sg_request * srp; - sg_io_hdr_t * hp; - unsigned char cmnd[sizeof(dummy_cmdp->sr_cmnd)]; - int timeout; - - if (count < SZ_SG_IO_HDR) - return -EINVAL; - if ((k = verify_area(VERIFY_READ, buf, count))) - return k; /* protects following copy_from_user()s + get_user()s */ - - sfp->cmd_q = 1; /* when sg_io_hdr seen, set command queuing on */ - if (! (srp = sg_add_request(sfp))) { - SCSI_LOG_TIMEOUT(1, printk("sg_new_write: queue full\n")); - return -EDOM; - } - hp = &srp->header; - if (__copy_from_user(hp, buf, SZ_SG_IO_HDR)) { - sg_remove_request(sfp, srp); - return -EFAULT; - } - if (hp->interface_id != 'S') { - sg_remove_request(sfp, srp); - return -ENOSYS; - } - if (hp->flags & SG_FLAG_MMAP_IO) { - if (hp->dxfer_len > sfp->reserve.bufflen) { - sg_remove_request(sfp, srp); - return -ENOMEM; /* MMAP_IO size must fit in reserve buffer */ - } - if (hp->flags & SG_FLAG_DIRECT_IO) { - sg_remove_request(sfp, srp); - return -EINVAL; /* either MMAP_IO or DIRECT_IO (not both) */ - } - if (sg_res_in_use(sfp)) { - sg_remove_request(sfp, srp); - return -EBUSY; /* reserve buffer already being used */ - } - } - timeout = sg_ms_to_jif(srp->header.timeout); - if ((! hp->cmdp) || (hp->cmd_len < 6) || (hp->cmd_len > sizeof(cmnd))) { - sg_remove_request(sfp, srp); - return -EMSGSIZE; - } - if ((k = verify_area(VERIFY_READ, hp->cmdp, hp->cmd_len))) { - sg_remove_request(sfp, srp); - return k; /* protects following copy_from_user()s + get_user()s */ - } - if (__copy_from_user(cmnd, hp->cmdp, hp->cmd_len)) { - sg_remove_request(sfp, srp); - return -EFAULT; - } - if (read_only && - (! sg_allow_access(cmnd[0], sfp->parentdp->device->type))) { - sg_remove_request(sfp, srp); - return -EPERM; - } - k = sg_common_write(sfp, srp, cmnd, timeout, blocking); - if (k < 0) return k; - if (o_srp) *o_srp = srp; - return count; + err = sg_read_xfer(srp); + err_out: + sg_finish_rem_req(srp); + return (0 == err) ? count : err; } -static int sg_common_write(Sg_fd * sfp, Sg_request * srp, - unsigned char * cmnd, int timeout, int blocking) +static ssize_t +sg_write(struct file *filp, const char *buf, size_t count, loff_t * ppos) { - int k; - Scsi_Request * SRpnt; - Sg_device * sdp = sfp->parentdp; - sg_io_hdr_t * hp = &srp->header; - request_queue_t * q; - - srp->data.cmd_opcode = cmnd[0]; /* hold opcode of command */ - hp->status = 0; - hp->masked_status = 0; - hp->msg_status = 0; - hp->info = 0; - hp->host_status = 0; - hp->driver_status = 0; - hp->resid = 0; - SCSI_LOG_TIMEOUT(4, - printk("sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n", - (int)cmnd[0], (int)hp->cmd_len)); + int mxsize, cmd_size, k; + int input_size, blocking; + unsigned char opcode; + Sg_device *sdp; + Sg_fd *sfp; + Sg_request *srp; + struct sg_header old_hdr; + sg_io_hdr_t *hp; + unsigned char cmnd[sizeof (dummy_cmdp->sr_cmnd)]; - if ((k = sg_start_req(srp))) { - SCSI_LOG_TIMEOUT(1, printk("sg_write: start_req err=%d\n", k)); - sg_finish_rem_req(srp); - return k; /* probably out of space --> ENOMEM */ - } - if ((k = sg_write_xfer(srp))) { - SCSI_LOG_TIMEOUT(1, printk("sg_write: write_xfer, bad address\n")); - sg_finish_rem_req(srp); - return k; - } - if (sdp->detached) { - sg_finish_rem_req(srp); - return -ENODEV; - } - SRpnt = scsi_allocate_request(sdp->device); - if(SRpnt == NULL) { - SCSI_LOG_TIMEOUT(1, printk("sg_write: no mem\n")); - sg_finish_rem_req(srp); - return -ENOMEM; - } - - srp->my_cmdp = SRpnt; - q = &SRpnt->sr_device->request_queue; - SRpnt->sr_request->rq_dev = sdp->i_rdev; - SRpnt->sr_sense_buffer[0] = 0; - SRpnt->sr_cmd_len = hp->cmd_len; - if (! (hp->flags & SG_FLAG_LUN_INHIBIT)) { - if (sdp->device->scsi_level <= SCSI_2) - cmnd[1] = (cmnd[1] & 0x1f) | (sdp->device->lun << 5); - } - SRpnt->sr_use_sg = srp->data.k_use_sg; - SRpnt->sr_sglist_len = srp->data.sglist_len; - SRpnt->sr_bufflen = srp->data.bufflen; - SRpnt->sr_underflow = 0; - SRpnt->sr_buffer = srp->data.buffer; - switch (hp->dxfer_direction) { - case SG_DXFER_TO_FROM_DEV: - case SG_DXFER_FROM_DEV: - SRpnt->sr_data_direction = SCSI_DATA_READ; break; - case SG_DXFER_TO_DEV: - SRpnt->sr_data_direction = SCSI_DATA_WRITE; break; - case SG_DXFER_UNKNOWN: - SRpnt->sr_data_direction = SCSI_DATA_UNKNOWN; break; - default: - SRpnt->sr_data_direction = SCSI_DATA_NONE; break; - } - SRpnt->upper_private_data = srp; - srp->data.k_use_sg = 0; - srp->data.sglist_len = 0; - srp->data.bufflen = 0; - srp->data.buffer = NULL; - hp->duration = jiffies; /* unit jiffies now, millisecs after done */ -/* Now send everything of to mid-level. The next time we hear about this - packet is when sg_cmd_done_bh() is called (i.e. a callback). */ - scsi_do_req(SRpnt, (void *)cmnd, - (void *)SRpnt->sr_buffer, hp->dxfer_len, - sg_cmd_done_bh, timeout, SG_DEFAULT_RETRIES); - /* dxfer_len overwrites SRpnt->sr_bufflen, hence need for b_malloc_len */ - generic_unplug_device(q); - return 0; -} - -static int sg_ioctl(struct inode * inode, struct file * filp, - unsigned int cmd_in, unsigned long arg) -{ - int result, val, read_only; - Sg_device * sdp; - Sg_fd * sfp; - Sg_request * srp; - unsigned long iflags; - - if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp))) - return -ENXIO; - SCSI_LOG_TIMEOUT(3, printk("sg_ioctl: dev=%d, cmd=0x%x\n", - minor(sdp->i_rdev), (int)cmd_in)); - read_only = (O_RDWR != (filp->f_flags & O_ACCMODE)); - - switch(cmd_in) - { - case SG_IO: - { - int blocking = 1; /* ignore O_NONBLOCK flag */ - - if (sdp->detached) - return -ENODEV; - if(! scsi_block_when_processing_errors(sdp->device) ) + if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) return -ENXIO; - result = verify_area(VERIFY_WRITE, (void *)arg, SZ_SG_IO_HDR); - if (result) return result; - result = sg_new_write(sfp, (const char *)arg, SZ_SG_IO_HDR, - blocking, read_only, &srp); - if (result < 0) return result; - srp->sg_io_owned = 1; - while (1) { - result = 0; /* following macro to beat race condition */ - __wait_event_interruptible(sfp->read_wait, - (sdp->detached || sfp->closed || srp->done), result); - if (sdp->detached) - return -ENODEV; - if (sfp->closed) - return 0; /* request packet dropped already */ - if (0 == result) - break; - srp->orphan = 1; - return result; /* -ERESTARTSYS because signal hit process */ - } - srp->done = 2; - result = sg_new_read(sfp, (char *)arg, SZ_SG_IO_HDR, srp); - return (result < 0) ? result : 0; - } - case SG_SET_TIMEOUT: - result = get_user(val, (int *)arg); - if (result) return result; - if (val < 0) - return -EIO; - sfp->timeout = val; - return 0; - case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */ - return sfp->timeout; /* strange ..., for backward compatibility */ - case SG_SET_FORCE_LOW_DMA: - result = get_user(val, (int *)arg); - if (result) return result; - if (val) { - sfp->low_dma = 1; - if ((0 == sfp->low_dma) && (0 == sg_res_in_use(sfp))) { - val = (int)sfp->reserve.bufflen; - sg_remove_scat(&sfp->reserve); - sg_build_reserve(sfp, val); - } - } - else { - if (sdp->detached) + SCSI_LOG_TIMEOUT(3, printk("sg_write: dev=%d, count=%d\n", + minor(sdp->i_rdev), (int) count)); + if (sdp->detached) return -ENODEV; - sfp->low_dma = sdp->device->host->unchecked_isa_dma; - } - return 0; - case SG_GET_LOW_DMA: - return put_user((int)sfp->low_dma, (int *)arg); - case SG_GET_SCSI_ID: - result = verify_area(VERIFY_WRITE, (void *)arg, sizeof(sg_scsi_id_t)); - if (result) return result; - else { - sg_scsi_id_t * sg_idp = (sg_scsi_id_t *)arg; + if (!((filp->f_flags & O_NONBLOCK) || + scsi_block_when_processing_errors(sdp->device))) + return -ENXIO; + if (ppos != &filp->f_pos) ; /* FIXME: Hmm. Seek to the right place, or fail? */ - if (sdp->detached) - return -ENODEV; - __put_user((int)sdp->device->host->host_no, &sg_idp->host_no); - __put_user((int)sdp->device->channel, &sg_idp->channel); - __put_user((int)sdp->device->id, &sg_idp->scsi_id); - __put_user((int)sdp->device->lun, &sg_idp->lun); - __put_user((int)sdp->device->type, &sg_idp->scsi_type); - __put_user((short)sdp->device->host->cmd_per_lun, - &sg_idp->h_cmd_per_lun); - __put_user((short)sdp->device->queue_depth, - &sg_idp->d_queue_depth); - __put_user(0, &sg_idp->unused[0]); - __put_user(0, &sg_idp->unused[1]); - return 0; - } - case SG_SET_FORCE_PACK_ID: - result = get_user(val, (int *)arg); - if (result) return result; - sfp->force_packid = val ? 1 : 0; - return 0; - case SG_GET_PACK_ID: - result = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int)); - if (result) return result; - read_lock_irqsave(&sfp->rq_list_lock, iflags); - for (srp = sfp->headrp; srp; srp = srp->nextrp) { - if ((1 == srp->done) && (! srp->sg_io_owned)) { - read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - __put_user(srp->header.pack_id, (int *)arg); - return 0; - } - } - read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - __put_user(-1, (int *)arg); - return 0; - case SG_GET_NUM_WAITING: - read_lock_irqsave(&sfp->rq_list_lock, iflags); - for (val = 0, srp = sfp->headrp; srp; srp = srp->nextrp) { - if ((1 == srp->done) && (! srp->sg_io_owned)) - ++val; - } - read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return put_user(val, (int *)arg); - case SG_GET_SG_TABLESIZE: - return put_user(sdp->sg_tablesize, (int *)arg); - case SG_SET_RESERVED_SIZE: - result = get_user(val, (int *)arg); - if (result) return result; - if (val != sfp->reserve.bufflen) { - if (sg_res_in_use(sfp) || sfp->mmap_called) - return -EBUSY; - sg_remove_scat(&sfp->reserve); - sg_build_reserve(sfp, val); - } - return 0; - case SG_GET_RESERVED_SIZE: - val = (int)sfp->reserve.bufflen; - return put_user(val, (int *)arg); - case SG_SET_COMMAND_Q: - result = get_user(val, (int *)arg); - if (result) return result; - sfp->cmd_q = val ? 1 : 0; - return 0; - case SG_GET_COMMAND_Q: - return put_user((int)sfp->cmd_q, (int *)arg); - case SG_SET_KEEP_ORPHAN: - result = get_user(val, (int *)arg); - if (result) return result; - sfp->keep_orphan = val; - return 0; - case SG_GET_KEEP_ORPHAN: - return put_user((int)sfp->keep_orphan, (int *)arg); - case SG_NEXT_CMD_LEN: - result = get_user(val, (int *)arg); - if (result) return result; - sfp->next_cmd_len = (val > 0) ? val : 0; - return 0; - case SG_GET_VERSION_NUM: - return put_user(sg_version_num, (int *)arg); - case SG_GET_ACCESS_COUNT: - val = (sdp->device ? sdp->device->access_count : 0); - return put_user(val, (int *)arg); - case SG_GET_REQUEST_TABLE: - result = verify_area(VERIFY_WRITE, (void *) arg, - SZ_SG_REQ_INFO * SG_MAX_QUEUE); - if (result) return result; - else { - sg_req_info_t rinfo[SG_MAX_QUEUE]; - Sg_request * srp; - read_lock_irqsave(&sfp->rq_list_lock, iflags); - for (srp = sfp->headrp, val = 0; val < SG_MAX_QUEUE; - ++val, srp = srp ? srp->nextrp : srp) { - memset(&rinfo[val], 0, SZ_SG_REQ_INFO); - if (srp) { - rinfo[val].req_state = srp->done + 1; - rinfo[val].problem = srp->header.masked_status & - srp->header.host_status & srp->header.driver_status; - rinfo[val].duration = srp->done ? - srp->header.duration : - sg_jif_to_ms(jiffies - srp->header.duration); - rinfo[val].orphan = srp->orphan; - rinfo[val].sg_io_owned = srp->sg_io_owned; - rinfo[val].pack_id = srp->header.pack_id; - rinfo[val].usr_ptr = srp->header.usr_ptr; - } - } - read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return (__copy_to_user((void *)arg, rinfo, - SZ_SG_REQ_INFO * SG_MAX_QUEUE) ? -EFAULT : 0); + if ((k = verify_area(VERIFY_READ, buf, count))) + return k; /* protects following copy_from_user()s + get_user()s */ + if (count < SZ_SG_HEADER) + return -EIO; + if (__copy_from_user(&old_hdr, buf, SZ_SG_HEADER)) + return -EFAULT; + blocking = !(filp->f_flags & O_NONBLOCK); + if (old_hdr.reply_len < 0) + return sg_new_write(sfp, buf, count, blocking, 0, NULL); + if (count < (SZ_SG_HEADER + 6)) + return -EIO; /* The minimum scsi command length is 6 bytes. */ + + if (!(srp = sg_add_request(sfp))) { + SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full\n")); + return -EDOM; } - case SG_EMULATED_HOST: - if (sdp->detached) - return -ENODEV; - return put_user(sdp->device->host->hostt->emulated, (int *)arg); - case SG_SCSI_RESET: - if (sdp->detached) - return -ENODEV; - if (filp->f_flags & O_NONBLOCK) { - if (sdp->device->host->in_recovery) - return -EBUSY; - } - else if (! scsi_block_when_processing_errors(sdp->device)) - return -EBUSY; - result = get_user(val, (int *)arg); - if (result) return result; - if (SG_SCSI_RESET_NOTHING == val) - return 0; -#ifdef SCSI_TRY_RESET_DEVICE - switch (val) - { - case SG_SCSI_RESET_DEVICE: - val = SCSI_TRY_RESET_DEVICE; - break; - case SG_SCSI_RESET_BUS: - val = SCSI_TRY_RESET_BUS; - break; - case SG_SCSI_RESET_HOST: - val = SCSI_TRY_RESET_HOST; - break; - default: - return -EINVAL; + buf += SZ_SG_HEADER; + __get_user(opcode, buf); + if (sfp->next_cmd_len > 0) { + if (sfp->next_cmd_len > MAX_COMMAND_SIZE) { + SCSI_LOG_TIMEOUT(1, printk("sg_write: command length too long\n")); + sfp->next_cmd_len = 0; + sg_remove_request(sfp, srp); + return -EIO; + } + cmd_size = sfp->next_cmd_len; + sfp->next_cmd_len = 0; /* reset so only this write() effected */ + } else { + cmd_size = COMMAND_SIZE(opcode); /* based on SCSI command group */ + if ((opcode >= 0xc0) && old_hdr.twelve_byte) + cmd_size = 12; } - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - return -EACCES; - return (scsi_reset_provider(sdp->device, val) == SUCCESS) ? 0 : -EIO; -#else - SCSI_LOG_TIMEOUT(1, printk("sg_ioctl: SG_RESET_SCSI not supported\n")); - result = -EINVAL; -#endif - case SCSI_IOCTL_SEND_COMMAND: - if (sdp->detached) - return -ENODEV; - if (read_only) { - unsigned char opcode = WRITE_6; - Scsi_Ioctl_Command * siocp = (void *)arg; + SCSI_LOG_TIMEOUT(4, printk( + "sg_write: scsi opcode=0x%02x, cmd_size=%d\n", (int) opcode, cmd_size)); +/* Determine buffer size. */ + input_size = count - cmd_size; + mxsize = (input_size > old_hdr.reply_len) ? input_size : old_hdr.reply_len; + mxsize -= SZ_SG_HEADER; + input_size -= SZ_SG_HEADER; + if (input_size < 0) { + sg_remove_request(sfp, srp); + return -EIO; /* User did not pass enough bytes for this command. */ + } + hp = &srp->header; + hp->interface_id = '\0'; /* indicator of old interface tunnelled */ + hp->cmd_len = (unsigned char) cmd_size; + hp->iovec_count = 0; + hp->mx_sb_len = 0; + if (input_size > 0) + hp->dxfer_direction = (old_hdr.reply_len > SZ_SG_HEADER) ? + SG_DXFER_TO_FROM_DEV : SG_DXFER_TO_DEV; + else + hp->dxfer_direction = (mxsize > 0) ? SG_DXFER_FROM_DEV : SG_DXFER_NONE; + hp->dxfer_len = mxsize; + hp->dxferp = (unsigned char *) buf + cmd_size; + hp->sbp = NULL; + hp->timeout = old_hdr.reply_len; /* structure abuse ... */ + hp->flags = input_size; /* structure abuse ... */ + hp->pack_id = old_hdr.pack_id; + hp->usr_ptr = NULL; + if (__copy_from_user(cmnd, buf, cmd_size)) + return -EFAULT; + k = sg_common_write(sfp, srp, cmnd, sfp->timeout, blocking); + return (k < 0) ? k : count; +} - if (copy_from_user(&opcode, siocp->data, 1)) +static ssize_t +sg_new_write(Sg_fd * sfp, const char *buf, size_t count, + int blocking, int read_only, Sg_request ** o_srp) +{ + int k; + Sg_request *srp; + sg_io_hdr_t *hp; + unsigned char cmnd[sizeof (dummy_cmdp->sr_cmnd)]; + int timeout; + + if (count < SZ_SG_IO_HDR) + return -EINVAL; + if ((k = verify_area(VERIFY_READ, buf, count))) + return k; /* protects following copy_from_user()s + get_user()s */ + + sfp->cmd_q = 1; /* when sg_io_hdr seen, set command queuing on */ + if (!(srp = sg_add_request(sfp))) { + SCSI_LOG_TIMEOUT(1, printk("sg_new_write: queue full\n")); + return -EDOM; + } + hp = &srp->header; + if (__copy_from_user(hp, buf, SZ_SG_IO_HDR)) { + sg_remove_request(sfp, srp); + return -EFAULT; + } + if (hp->interface_id != 'S') { + sg_remove_request(sfp, srp); + return -ENOSYS; + } + if (hp->flags & SG_FLAG_MMAP_IO) { + if (hp->dxfer_len > sfp->reserve.bufflen) { + sg_remove_request(sfp, srp); + return -ENOMEM; /* MMAP_IO size must fit in reserve buffer */ + } + if (hp->flags & SG_FLAG_DIRECT_IO) { + sg_remove_request(sfp, srp); + return -EINVAL; /* either MMAP_IO or DIRECT_IO (not both) */ + } + if (sg_res_in_use(sfp)) { + sg_remove_request(sfp, srp); + return -EBUSY; /* reserve buffer already being used */ + } + } + timeout = sg_ms_to_jif(srp->header.timeout); + if ((!hp->cmdp) || (hp->cmd_len < 6) || (hp->cmd_len > sizeof (cmnd))) { + sg_remove_request(sfp, srp); + return -EMSGSIZE; + } + if ((k = verify_area(VERIFY_READ, hp->cmdp, hp->cmd_len))) { + sg_remove_request(sfp, srp); + return k; /* protects following copy_from_user()s + get_user()s */ + } + if (__copy_from_user(cmnd, hp->cmdp, hp->cmd_len)) { + sg_remove_request(sfp, srp); return -EFAULT; - if (! sg_allow_access(opcode, sdp->device->type)) + } + if (read_only && + (!sg_allow_access(cmnd[0], sfp->parentdp->device->type))) { + sg_remove_request(sfp, srp); return -EPERM; } - return scsi_ioctl_send_command(sdp->device, (void *)arg); - case SG_SET_DEBUG: - result = get_user(val, (int *)arg); - if (result) return result; - sdp->sgdebug = (char)val; - return 0; - case SCSI_IOCTL_GET_IDLUN: - case SCSI_IOCTL_GET_BUS_NUMBER: - case SCSI_IOCTL_PROBE_HOST: - case SG_GET_TRANSFORM: - if (sdp->detached) - return -ENODEV; - return scsi_ioctl(sdp->device, cmd_in, (void *)arg); - default: - if (read_only) - return -EPERM; /* don't know so take safe approach */ - return scsi_ioctl(sdp->device, cmd_in, (void *)arg); - } -} - -static unsigned int sg_poll(struct file * filp, poll_table * wait) -{ - unsigned int res = 0; - Sg_device * sdp; - Sg_fd * sfp; - Sg_request * srp; - int count = 0; - unsigned long iflags; - - if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp)) - || sfp->closed) - return POLLERR; - poll_wait(filp, &sfp->read_wait, wait); - read_lock_irqsave(&sfp->rq_list_lock, iflags); - for (srp = sfp->headrp; srp; srp = srp->nextrp) { - /* if any read waiting, flag it */ - if ((0 == res) && (1 == srp->done) && (! srp->sg_io_owned)) - res = POLLIN | POLLRDNORM; - ++count; - } - read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - - if (sdp->detached) - res |= POLLHUP; - else if (! sfp->cmd_q) { - if (0 == count) - res |= POLLOUT | POLLWRNORM; - } - else if (count < SG_MAX_QUEUE) - res |= POLLOUT | POLLWRNORM; - SCSI_LOG_TIMEOUT(3, printk("sg_poll: dev=%d, res=0x%x\n", - minor(sdp->i_rdev), (int)res)); - return res; -} - -static int sg_fasync(int fd, struct file * filp, int mode) -{ - int retval; - Sg_device * sdp; - Sg_fd * sfp; - - if ((! (sfp = (Sg_fd *)filp->private_data)) || (! (sdp = sfp->parentdp))) - return -ENXIO; - SCSI_LOG_TIMEOUT(3, printk("sg_fasync: dev=%d, mode=%d\n", - minor(sdp->i_rdev), mode)); - - retval = fasync_helper(fd, filp, mode, &sfp->async_qp); - return (retval < 0) ? retval : 0; -} - -static inline unsigned char * sg_scatg2virt(const struct scatterlist * sclp) -{ - return (sclp && sclp->page) ? - (unsigned char *)page_address(sclp->page) + sclp->offset : - NULL; -} - -static void sg_rb_correct4mmap(Sg_scatter_hold * rsv_schp, int startFinish) -{ - void * page_ptr; - struct page * page; - int k, m; - - SCSI_LOG_TIMEOUT(3, printk("sg_rb_correct4mmap: startFinish=%d, " - "scatg=%d\n", startFinish, rsv_schp->k_use_sg)); - /* N.B. correction _not_ applied to base page of aech allocation */ - if (rsv_schp->k_use_sg) { /* reserve buffer is a scatter gather list */ - struct scatterlist * sclp = rsv_schp->buffer; - - for (k = 0; k < rsv_schp->k_use_sg; ++k, ++sclp) { - for (m = PAGE_SIZE; m < sclp->length; m += PAGE_SIZE) { - page_ptr = sg_scatg2virt(sclp) + m; - page = virt_to_page(page_ptr); - if (startFinish) - get_page(page); /* increment page count */ + k = sg_common_write(sfp, srp, cmnd, timeout, blocking); + if (k < 0) + return k; + if (o_srp) + *o_srp = srp; + return count; +} + +static int +sg_common_write(Sg_fd * sfp, Sg_request * srp, + unsigned char *cmnd, int timeout, int blocking) +{ + int k; + Scsi_Request *SRpnt; + Sg_device *sdp = sfp->parentdp; + sg_io_hdr_t *hp = &srp->header; + request_queue_t *q; + + srp->data.cmd_opcode = cmnd[0]; /* hold opcode of command */ + hp->status = 0; + hp->masked_status = 0; + hp->msg_status = 0; + hp->info = 0; + hp->host_status = 0; + hp->driver_status = 0; + hp->resid = 0; + SCSI_LOG_TIMEOUT(4, printk("sg_common_write: scsi opcode=0x%02x, cmd_size=%d\n", + (int) cmnd[0], (int) hp->cmd_len)); + + if ((k = sg_start_req(srp))) { + SCSI_LOG_TIMEOUT(1, printk("sg_write: start_req err=%d\n", k)); + sg_finish_rem_req(srp); + return k; /* probably out of space --> ENOMEM */ + } + if ((k = sg_write_xfer(srp))) { + SCSI_LOG_TIMEOUT(1, printk("sg_write: write_xfer, bad address\n")); + sg_finish_rem_req(srp); + return k; + } + if (sdp->detached) { + sg_finish_rem_req(srp); + return -ENODEV; + } + SRpnt = scsi_allocate_request(sdp->device); + if (SRpnt == NULL) { + SCSI_LOG_TIMEOUT(1, printk("sg_write: no mem\n")); + sg_finish_rem_req(srp); + return -ENOMEM; + } + + srp->my_cmdp = SRpnt; + q = &SRpnt->sr_device->request_queue; + SRpnt->sr_request->rq_dev = sdp->i_rdev; + SRpnt->sr_sense_buffer[0] = 0; + SRpnt->sr_cmd_len = hp->cmd_len; + if (!(hp->flags & SG_FLAG_LUN_INHIBIT)) { + if (sdp->device->scsi_level <= SCSI_2) + cmnd[1] = (cmnd[1] & 0x1f) | (sdp->device->lun << 5); + } + SRpnt->sr_use_sg = srp->data.k_use_sg; + SRpnt->sr_sglist_len = srp->data.sglist_len; + SRpnt->sr_bufflen = srp->data.bufflen; + SRpnt->sr_underflow = 0; + SRpnt->sr_buffer = srp->data.buffer; + switch (hp->dxfer_direction) { + case SG_DXFER_TO_FROM_DEV: + case SG_DXFER_FROM_DEV: + SRpnt->sr_data_direction = SCSI_DATA_READ; + break; + case SG_DXFER_TO_DEV: + SRpnt->sr_data_direction = SCSI_DATA_WRITE; + break; + case SG_DXFER_UNKNOWN: + SRpnt->sr_data_direction = SCSI_DATA_UNKNOWN; + break; + default: + SRpnt->sr_data_direction = SCSI_DATA_NONE; + break; + } + SRpnt->upper_private_data = srp; + srp->data.k_use_sg = 0; + srp->data.sglist_len = 0; + srp->data.bufflen = 0; + srp->data.buffer = NULL; + hp->duration = jiffies; /* unit jiffies now, millisecs after done */ +/* Now send everything of to mid-level. The next time we hear about this + packet is when sg_cmd_done() is called (i.e. a callback). */ + scsi_do_req(SRpnt, (void *) cmnd, + (void *) SRpnt->sr_buffer, hp->dxfer_len, + sg_cmd_done, timeout, SG_DEFAULT_RETRIES); + /* dxfer_len overwrites SRpnt->sr_bufflen, hence need for b_malloc_len */ + generic_unplug_device(q); + return 0; +} + +static int +sg_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd_in, unsigned long arg) +{ + int result, val, read_only; + Sg_device *sdp; + Sg_fd *sfp; + Sg_request *srp; + unsigned long iflags; + + if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) + return -ENXIO; + SCSI_LOG_TIMEOUT(3, printk("sg_ioctl: dev=%d, cmd=0x%x\n", + minor(sdp->i_rdev), (int) cmd_in)); + read_only = (O_RDWR != (filp->f_flags & O_ACCMODE)); + + switch (cmd_in) { + case SG_IO: + { + int blocking = 1; /* ignore O_NONBLOCK flag */ + + if (sdp->detached) + return -ENODEV; + if (!scsi_block_when_processing_errors(sdp->device)) + return -ENXIO; + result = verify_area(VERIFY_WRITE, (void *) arg, + SZ_SG_IO_HDR); + if (result) + return result; + result = + sg_new_write(sfp, (const char *) arg, SZ_SG_IO_HDR, + blocking, read_only, &srp); + if (result < 0) + return result; + srp->sg_io_owned = 1; + while (1) { + result = 0; /* following macro to beat race condition */ + __wait_event_interruptible(sfp->read_wait, + (sdp->detached || sfp->closed || srp->done), + result); + if (sdp->detached) + return -ENODEV; + if (sfp->closed) + return 0; /* request packet dropped already */ + if (0 == result) + break; + srp->orphan = 1; + return result; /* -ERESTARTSYS because signal hit process */ + } + srp->done = 2; + result = sg_new_read(sfp, (char *) arg, SZ_SG_IO_HDR, srp); + return (result < 0) ? result : 0; + } + case SG_SET_TIMEOUT: + result = get_user(val, (int *) arg); + if (result) + return result; + if (val < 0) + return -EIO; + sfp->timeout = val; + return 0; + case SG_GET_TIMEOUT: /* N.B. User receives timeout as return value */ + return sfp->timeout; /* strange ..., for backward compatibility */ + case SG_SET_FORCE_LOW_DMA: + result = get_user(val, (int *) arg); + if (result) + return result; + if (val) { + sfp->low_dma = 1; + if ((0 == sfp->low_dma) && (0 == sg_res_in_use(sfp))) { + val = (int) sfp->reserve.bufflen; + sg_remove_scat(&sfp->reserve); + sg_build_reserve(sfp, val); + } + } else { + if (sdp->detached) + return -ENODEV; + sfp->low_dma = sdp->device->host->unchecked_isa_dma; + } + return 0; + case SG_GET_LOW_DMA: + return put_user((int) sfp->low_dma, (int *) arg); + case SG_GET_SCSI_ID: + result = + verify_area(VERIFY_WRITE, (void *) arg, + sizeof (sg_scsi_id_t)); + if (result) + return result; else { - if (page_count(page) > 0) - put_page_testzero(page); /* decrement page count */ + sg_scsi_id_t *sg_idp = (sg_scsi_id_t *) arg; + + if (sdp->detached) + return -ENODEV; + __put_user((int) sdp->device->host->host_no, + &sg_idp->host_no); + __put_user((int) sdp->device->channel, + &sg_idp->channel); + __put_user((int) sdp->device->id, &sg_idp->scsi_id); + __put_user((int) sdp->device->lun, &sg_idp->lun); + __put_user((int) sdp->device->type, &sg_idp->scsi_type); + __put_user((short) sdp->device->host->cmd_per_lun, + &sg_idp->h_cmd_per_lun); + __put_user((short) sdp->device->queue_depth, + &sg_idp->d_queue_depth); + __put_user(0, &sg_idp->unused[0]); + __put_user(0, &sg_idp->unused[1]); + return 0; } - } - } - } - else { /* reserve buffer is just a single allocation */ - for (m = PAGE_SIZE; m < rsv_schp->bufflen; m += PAGE_SIZE) { - page_ptr = (unsigned char *)rsv_schp->buffer + m; - page = virt_to_page(page_ptr); - if (startFinish) - get_page(page); /* increment page count */ - else { - if (page_count(page) > 0) - put_page_testzero(page); /* decrement page count */ - } + case SG_SET_FORCE_PACK_ID: + result = get_user(val, (int *) arg); + if (result) + return result; + sfp->force_packid = val ? 1 : 0; + return 0; + case SG_GET_PACK_ID: + result = verify_area(VERIFY_WRITE, (void *) arg, sizeof (int)); + if (result) + return result; + read_lock_irqsave(&sfp->rq_list_lock, iflags); + for (srp = sfp->headrp; srp; srp = srp->nextrp) { + if ((1 == srp->done) && (!srp->sg_io_owned)) { + read_unlock_irqrestore(&sfp->rq_list_lock, + iflags); + __put_user(srp->header.pack_id, (int *) arg); + return 0; + } + } + read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + __put_user(-1, (int *) arg); + return 0; + case SG_GET_NUM_WAITING: + read_lock_irqsave(&sfp->rq_list_lock, iflags); + for (val = 0, srp = sfp->headrp; srp; srp = srp->nextrp) { + if ((1 == srp->done) && (!srp->sg_io_owned)) + ++val; + } + read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return put_user(val, (int *) arg); + case SG_GET_SG_TABLESIZE: + return put_user(sdp->sg_tablesize, (int *) arg); + case SG_SET_RESERVED_SIZE: + result = get_user(val, (int *) arg); + if (result) + return result; + if (val != sfp->reserve.bufflen) { + if (sg_res_in_use(sfp) || sfp->mmap_called) + return -EBUSY; + sg_remove_scat(&sfp->reserve); + sg_build_reserve(sfp, val); + } + return 0; + case SG_GET_RESERVED_SIZE: + val = (int) sfp->reserve.bufflen; + return put_user(val, (int *) arg); + case SG_SET_COMMAND_Q: + result = get_user(val, (int *) arg); + if (result) + return result; + sfp->cmd_q = val ? 1 : 0; + return 0; + case SG_GET_COMMAND_Q: + return put_user((int) sfp->cmd_q, (int *) arg); + case SG_SET_KEEP_ORPHAN: + result = get_user(val, (int *) arg); + if (result) + return result; + sfp->keep_orphan = val; + return 0; + case SG_GET_KEEP_ORPHAN: + return put_user((int) sfp->keep_orphan, (int *) arg); + case SG_NEXT_CMD_LEN: + result = get_user(val, (int *) arg); + if (result) + return result; + sfp->next_cmd_len = (val > 0) ? val : 0; + return 0; + case SG_GET_VERSION_NUM: + return put_user(sg_version_num, (int *) arg); + case SG_GET_ACCESS_COUNT: + val = (sdp->device ? sdp->device->access_count : 0); + return put_user(val, (int *) arg); + case SG_GET_REQUEST_TABLE: + result = verify_area(VERIFY_WRITE, (void *) arg, + SZ_SG_REQ_INFO * SG_MAX_QUEUE); + if (result) + return result; + else { + sg_req_info_t rinfo[SG_MAX_QUEUE]; + Sg_request *srp; + read_lock_irqsave(&sfp->rq_list_lock, iflags); + for (srp = sfp->headrp, val = 0; val < SG_MAX_QUEUE; + ++val, srp = srp ? srp->nextrp : srp) { + memset(&rinfo[val], 0, SZ_SG_REQ_INFO); + if (srp) { + rinfo[val].req_state = srp->done + 1; + rinfo[val].problem = + srp->header.masked_status & + srp->header.host_status & + srp->header.driver_status; + rinfo[val].duration = + srp->done ? srp->header.duration : + sg_jif_to_ms( + jiffies - srp->header.duration); + rinfo[val].orphan = srp->orphan; + rinfo[val].sg_io_owned = srp->sg_io_owned; + rinfo[val].pack_id = srp->header.pack_id; + rinfo[val].usr_ptr = srp->header.usr_ptr; + } + } + read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return (__copy_to_user((void *) arg, rinfo, + SZ_SG_REQ_INFO * SG_MAX_QUEUE) ? -EFAULT : 0); + } + case SG_EMULATED_HOST: + if (sdp->detached) + return -ENODEV; + return put_user(sdp->device->host->hostt->emulated, (int *) arg); + case SG_SCSI_RESET: + if (sdp->detached) + return -ENODEV; + if (filp->f_flags & O_NONBLOCK) { + if (sdp->device->host->in_recovery) + return -EBUSY; + } else if (!scsi_block_when_processing_errors(sdp->device)) + return -EBUSY; + result = get_user(val, (int *) arg); + if (result) + return result; + if (SG_SCSI_RESET_NOTHING == val) + return 0; + switch (val) { + case SG_SCSI_RESET_DEVICE: + val = SCSI_TRY_RESET_DEVICE; + break; + case SG_SCSI_RESET_BUS: + val = SCSI_TRY_RESET_BUS; + break; + case SG_SCSI_RESET_HOST: + val = SCSI_TRY_RESET_HOST; + break; + default: + return -EINVAL; + } + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + return (scsi_reset_provider(sdp->device, val) == + SUCCESS) ? 0 : -EIO; + case SCSI_IOCTL_SEND_COMMAND: + if (sdp->detached) + return -ENODEV; + if (read_only) { + unsigned char opcode = WRITE_6; + Scsi_Ioctl_Command *siocp = (void *) arg; + + if (copy_from_user(&opcode, siocp->data, 1)) + return -EFAULT; + if (!sg_allow_access(opcode, sdp->device->type)) + return -EPERM; + } + return scsi_ioctl_send_command(sdp->device, (void *) arg); + case SG_SET_DEBUG: + result = get_user(val, (int *) arg); + if (result) + return result; + sdp->sgdebug = (char) val; + return 0; + case SCSI_IOCTL_GET_IDLUN: + case SCSI_IOCTL_GET_BUS_NUMBER: + case SCSI_IOCTL_PROBE_HOST: + case SG_GET_TRANSFORM: + if (sdp->detached) + return -ENODEV; + return scsi_ioctl(sdp->device, cmd_in, (void *) arg); + default: + if (read_only) + return -EPERM; /* don't know so take safe approach */ + return scsi_ioctl(sdp->device, cmd_in, (void *) arg); } - } } -static struct page * sg_vma_nopage(struct vm_area_struct *vma, - unsigned long addr, int unused) +static unsigned int +sg_poll(struct file *filp, poll_table * wait) { - Sg_fd * sfp; - struct page * page = NOPAGE_SIGBUS; - void * page_ptr = NULL; - unsigned long offset; - Sg_scatter_hold * rsv_schp; + unsigned int res = 0; + Sg_device *sdp; + Sg_fd *sfp; + Sg_request *srp; + int count = 0; + unsigned long iflags; - if ((NULL == vma) || (! (sfp = (Sg_fd *)vma->vm_private_data))) - return page; - rsv_schp = &sfp->reserve; - offset = addr - vma->vm_start; - if (offset >= rsv_schp->bufflen) - return page; - SCSI_LOG_TIMEOUT(3, printk("sg_vma_nopage: offset=%lu, scatg=%d\n", - offset, rsv_schp->k_use_sg)); - if (rsv_schp->k_use_sg) { /* reserve buffer is a scatter gather list */ - int k; - unsigned long sa = vma->vm_start; - unsigned long len; - struct scatterlist * sclp = rsv_schp->buffer; - - for (k = 0; (k < rsv_schp->k_use_sg) && (sa < vma->vm_end); - ++k, ++sclp) { - len = vma->vm_end - sa; - len = (len < sclp->length) ? len : sclp->length; - if (offset < len) { - page_ptr = sg_scatg2virt(sclp) + offset; + if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp)) + || sfp->closed) + return POLLERR; + poll_wait(filp, &sfp->read_wait, wait); + read_lock_irqsave(&sfp->rq_list_lock, iflags); + for (srp = sfp->headrp; srp; srp = srp->nextrp) { + /* if any read waiting, flag it */ + if ((0 == res) && (1 == srp->done) && (!srp->sg_io_owned)) + res = POLLIN | POLLRDNORM; + ++count; + } + read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + + if (sdp->detached) + res |= POLLHUP; + else if (!sfp->cmd_q) { + if (0 == count) + res |= POLLOUT | POLLWRNORM; + } else if (count < SG_MAX_QUEUE) + res |= POLLOUT | POLLWRNORM; + SCSI_LOG_TIMEOUT(3, printk("sg_poll: dev=%d, res=0x%x\n", + minor(sdp->i_rdev), (int) res)); + return res; +} + +static int +sg_fasync(int fd, struct file *filp, int mode) +{ + int retval; + Sg_device *sdp; + Sg_fd *sfp; + + if ((!(sfp = (Sg_fd *) filp->private_data)) || (!(sdp = sfp->parentdp))) + return -ENXIO; + SCSI_LOG_TIMEOUT(3, printk("sg_fasync: dev=%d, mode=%d\n", + minor(sdp->i_rdev), mode)); + + retval = fasync_helper(fd, filp, mode, &sfp->async_qp); + return (retval < 0) ? retval : 0; +} + +static inline unsigned char * +sg_scatg2virt(const struct scatterlist *sclp) +{ + return (sclp && sclp->page) ? + (unsigned char *) page_address(sclp->page) + sclp->offset : NULL; +} + +/* When startFinish==1 increments page counts for pages other than the + first of scatter gather elements obtained from __get_free_pages(). + When startFinish==0 decrements ... */ +static void +sg_rb_correct4mmap(Sg_scatter_hold * rsv_schp, int startFinish) +{ + void *page_ptr; + struct page *page; + int k, m; + + SCSI_LOG_TIMEOUT(3, printk("sg_rb_correct4mmap: startFinish=%d, scatg=%d\n", + startFinish, rsv_schp->k_use_sg)); + /* N.B. correction _not_ applied to base page of each allocation */ + if (rsv_schp->k_use_sg) { /* reserve buffer is a scatter gather list */ + struct scatterlist *sclp = rsv_schp->buffer; + + for (k = 0; k < rsv_schp->k_use_sg; ++k, ++sclp) { + for (m = PAGE_SIZE; m < sclp->length; m += PAGE_SIZE) { + page_ptr = sg_scatg2virt(sclp) + m; + page = virt_to_page(page_ptr); + if (startFinish) + atomic_inc(&page->count); + else { + if (page_count(page) > 0) + atomic_dec(&page->count); + } + } + } + } else { /* reserve buffer is just a single allocation */ + for (m = PAGE_SIZE; m < rsv_schp->bufflen; m += PAGE_SIZE) { + page_ptr = (unsigned char *) rsv_schp->buffer + m; + page = virt_to_page(page_ptr); + if (startFinish) + atomic_inc(&page->count); + else { + if (page_count(page) > 0) + atomic_dec(&page->count); + } + } + } +} + +static struct page * +sg_vma_nopage(struct vm_area_struct *vma, unsigned long addr, int unused) +{ + Sg_fd *sfp; + struct page *page = NOPAGE_SIGBUS; + void *page_ptr = NULL; + unsigned long offset; + Sg_scatter_hold *rsv_schp; + + if ((NULL == vma) || (!(sfp = (Sg_fd *) vma->vm_private_data))) + return page; + rsv_schp = &sfp->reserve; + offset = addr - vma->vm_start; + if (offset >= rsv_schp->bufflen) + return page; + SCSI_LOG_TIMEOUT(3, printk("sg_vma_nopage: offset=%lu, scatg=%d\n", + offset, rsv_schp->k_use_sg)); + if (rsv_schp->k_use_sg) { /* reserve buffer is a scatter gather list */ + int k; + unsigned long sa = vma->vm_start; + unsigned long len; + struct scatterlist *sclp = rsv_schp->buffer; + + for (k = 0; (k < rsv_schp->k_use_sg) && (sa < vma->vm_end); + ++k, ++sclp) { + len = vma->vm_end - sa; + len = (len < sclp->length) ? len : sclp->length; + if (offset < len) { + page_ptr = sg_scatg2virt(sclp) + offset; + page = virt_to_page(page_ptr); + get_page(page); /* increment page count */ + break; + } + sa += len; + offset -= len; + } + } else { /* reserve buffer is just a single allocation */ + page_ptr = (unsigned char *) rsv_schp->buffer + offset; page = virt_to_page(page_ptr); get_page(page); /* increment page count */ - break; - } - sa += len; - offset -= len; - } - } - else { /* reserve buffer is just a single allocation */ - page_ptr = (unsigned char *)rsv_schp->buffer + offset; - page = virt_to_page(page_ptr); - get_page(page); /* increment page count */ - } - return page; + } + return page; } static struct vm_operations_struct sg_mmap_vm_ops = { - nopage : sg_vma_nopage, + .nopage = sg_vma_nopage, }; -static int sg_mmap(struct file * filp, struct vm_area_struct *vma) +static int +sg_mmap(struct file *filp, struct vm_area_struct *vma) { - Sg_fd * sfp; - unsigned long req_sz = vma->vm_end - vma->vm_start; - Sg_scatter_hold * rsv_schp; - - if ((! filp) || (! vma) || (! (sfp = (Sg_fd *)filp->private_data))) - return -ENXIO; - SCSI_LOG_TIMEOUT(3, printk("sg_mmap starting, vm_start=%p, len=%d\n", - (void *)vma->vm_start, (int)req_sz)); - if (vma->vm_pgoff) - return -EINVAL; /* want no offset */ - rsv_schp = &sfp->reserve; - if (req_sz > rsv_schp->bufflen) - return -ENOMEM; /* cannot map more than reserved buffer */ - - if (rsv_schp->k_use_sg) { /* reserve buffer is a scatter gather list */ - int k; - unsigned long sa = vma->vm_start; - unsigned long len; - struct scatterlist * sclp = rsv_schp->buffer; - - for (k = 0; (k < rsv_schp->k_use_sg) && (sa < vma->vm_end); - ++k, ++sclp) { - if (0 != sclp->offset) - return -EFAULT; /* non page aligned memory ?? */ - len = vma->vm_end - sa; - len = (len < sclp->length) ? len : sclp->length; - sa += len; - } - } - else { /* reserve buffer is just a single allocation */ - if ((unsigned long)rsv_schp->buffer & (PAGE_SIZE - 1)) - return -EFAULT; /* non page aligned memory ?? */ - } - if (0 == sfp->mmap_called) { - sg_rb_correct4mmap(rsv_schp, 1); /* do only once per fd lifetime */ - sfp->mmap_called = 1; - } - vma->vm_flags |= (VM_RESERVED | VM_IO); - vma->vm_private_data = sfp; - vma->vm_ops = &sg_mmap_vm_ops; - return 0; + Sg_fd *sfp; + unsigned long req_sz = vma->vm_end - vma->vm_start; + Sg_scatter_hold *rsv_schp; + + if ((!filp) || (!vma) || (!(sfp = (Sg_fd *) filp->private_data))) + return -ENXIO; + SCSI_LOG_TIMEOUT(3, printk("sg_mmap starting, vm_start=%p, len=%d\n", + (void *) vma->vm_start, (int) req_sz)); + if (vma->vm_pgoff) + return -EINVAL; /* want no offset */ + rsv_schp = &sfp->reserve; + if (req_sz > rsv_schp->bufflen) + return -ENOMEM; /* cannot map more than reserved buffer */ + + if (rsv_schp->k_use_sg) { /* reserve buffer is a scatter gather list */ + int k; + unsigned long sa = vma->vm_start; + unsigned long len; + struct scatterlist *sclp = rsv_schp->buffer; + + for (k = 0; (k < rsv_schp->k_use_sg) && (sa < vma->vm_end); + ++k, ++sclp) { + if (0 != sclp->offset) + return -EFAULT; /* non page aligned memory ?? */ + len = vma->vm_end - sa; + len = (len < sclp->length) ? len : sclp->length; + sa += len; + } + } else { /* reserve buffer is just a single allocation */ + if ((unsigned long) rsv_schp->buffer & (PAGE_SIZE - 1)) + return -EFAULT; /* non page aligned memory ?? */ + } + if (0 == sfp->mmap_called) { + sg_rb_correct4mmap(rsv_schp, 1); /* do only once per fd lifetime */ + sfp->mmap_called = 1; + } + vma->vm_flags |= (VM_RESERVED | VM_IO); + vma->vm_private_data = sfp; + vma->vm_ops = &sg_mmap_vm_ops; + return 0; } /* This function is a "bottom half" handler that is called by the * mid level when a command is completed (or has failed). */ -static void sg_cmd_done_bh(Scsi_Cmnd * SCpnt) +static void +sg_cmd_done(Scsi_Cmnd * SCpnt) { - Scsi_Request * SRpnt = NULL; - Sg_device * sdp = NULL; - Sg_fd * sfp; - Sg_request * srp = NULL; - - if (SCpnt && (SRpnt = SCpnt->sc_request)) - srp = (Sg_request *)SRpnt->upper_private_data; - if (NULL == srp) { - printk(KERN_ERR "sg_cmd_done_bh: NULL request\n"); - if (SRpnt) - scsi_release_request(SRpnt); - return; - } - sfp = srp->parentfp; - if (sfp) - sdp = sfp->parentdp; - if ((NULL == sdp) || sdp->detached) { - printk(KERN_INFO "sg_cmd_done_bh: device detached\n"); - scsi_release_request(SRpnt); - return; - } - - /* First transfer ownership of data buffers to sg_device object. */ - srp->data.k_use_sg = SRpnt->sr_use_sg; - srp->data.sglist_len = SRpnt->sr_sglist_len; - srp->data.bufflen = SRpnt->sr_bufflen; - srp->data.buffer = SRpnt->sr_buffer; - /* now clear out request structure */ - SRpnt->sr_use_sg = 0; - SRpnt->sr_sglist_len = 0; - SRpnt->sr_bufflen = 0; - SRpnt->sr_buffer = NULL; - SRpnt->sr_underflow = 0; - SRpnt->sr_request->rq_dev = mk_kdev(0, 0); /* "sg" _disowns_ request blk */ - - srp->my_cmdp = NULL; - srp->done = 1; - - SCSI_LOG_TIMEOUT(4, printk("sg...bh: dev=%d, pack_id=%d, res=0x%x\n", - minor(sdp->i_rdev), srp->header.pack_id, - (int)SRpnt->sr_result)); - srp->header.resid = SCpnt->resid; - /* sg_unmap_and(&srp->data, 0); */ /* unmap locked pages a.s.a.p. */ - /* N.B. unit of duration changes here from jiffies to millisecs */ - srp->header.duration = sg_jif_to_ms(jiffies - (int)srp->header.duration); - if (0 != SRpnt->sr_result) { - memcpy(srp->sense_b, SRpnt->sr_sense_buffer, sizeof(srp->sense_b)); - srp->header.status = 0xff & SRpnt->sr_result; - srp->header.masked_status = status_byte(SRpnt->sr_result); - srp->header.msg_status = msg_byte(SRpnt->sr_result); - srp->header.host_status = host_byte(SRpnt->sr_result); - srp->header.driver_status = driver_byte(SRpnt->sr_result); - if ((sdp->sgdebug > 0) && - ((CHECK_CONDITION == srp->header.masked_status) || - (COMMAND_TERMINATED == srp->header.masked_status))) - print_req_sense("sg_cmd_done_bh", SRpnt); - - /* Following if statement is a patch supplied by Eric Youngdale */ - if (driver_byte(SRpnt->sr_result) != 0 - && (SRpnt->sr_sense_buffer[0] & 0x7f) == 0x70 - && (SRpnt->sr_sense_buffer[2] & 0xf) == UNIT_ATTENTION - && sdp->device->removable) { - /* Detected disc change. Set the bit - this may be used if */ - /* there are filesystems using this device. */ - sdp->device->changed = 1; - } - } - /* Rely on write phase to clean out srp status values, so no "else" */ - - scsi_release_request(SRpnt); - SRpnt = NULL; - if (sfp->closed) { /* whoops this fd already released, cleanup */ - SCSI_LOG_TIMEOUT(1, - printk("sg...bh: already closed, freeing ...\n")); - sg_finish_rem_req(srp); - srp = NULL; - if (NULL == sfp->headrp) { - SCSI_LOG_TIMEOUT(1, - printk("sg...bh: already closed, final cleanup\n")); - if (0 == sg_remove_sfp(sdp, sfp)) { /* device still present */ - sdp->device->access_count--; - if (sdp->device->host->hostt->module) - __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); - } - if (sg_template.module) - __MOD_DEC_USE_COUNT(sg_template.module); - sfp = NULL; - } - } - else if (srp && srp->orphan) { - if (sfp->keep_orphan) - srp->sg_io_owned = 0; - else { - sg_finish_rem_req(srp); - srp = NULL; - } - } - if (sfp && srp) { - /* Now wake up any sg_read() that is waiting for this packet. */ - wake_up_interruptible(&sfp->read_wait); - kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN); - } + Scsi_Request *SRpnt = NULL; + Sg_device *sdp = NULL; + Sg_fd *sfp; + Sg_request *srp = NULL; + + if (SCpnt && (SRpnt = SCpnt->sc_request)) + srp = (Sg_request *) SRpnt->upper_private_data; + if (NULL == srp) { + printk(KERN_ERR "sg_cmd_done: NULL request\n"); + if (SRpnt) + scsi_release_request(SRpnt); + return; + } + sfp = srp->parentfp; + if (sfp) + sdp = sfp->parentdp; + if ((NULL == sdp) || sdp->detached) { + printk(KERN_INFO "sg_cmd_done: device detached\n"); + scsi_release_request(SRpnt); + return; + } + + /* First transfer ownership of data buffers to sg_device object. */ + srp->data.k_use_sg = SRpnt->sr_use_sg; + srp->data.sglist_len = SRpnt->sr_sglist_len; + srp->data.bufflen = SRpnt->sr_bufflen; + srp->data.buffer = SRpnt->sr_buffer; + /* now clear out request structure */ + SRpnt->sr_use_sg = 0; + SRpnt->sr_sglist_len = 0; + SRpnt->sr_bufflen = 0; + SRpnt->sr_buffer = NULL; + SRpnt->sr_underflow = 0; + SRpnt->sr_request->rq_dev = mk_kdev(0, 0); /* "sg" _disowns_ request blk */ + + srp->my_cmdp = NULL; + srp->done = 1; + + SCSI_LOG_TIMEOUT(4, printk("sg_cmd_done: dev=%d, pack_id=%d, res=0x%x\n", + minor(sdp->i_rdev), srp->header.pack_id, (int) SRpnt->sr_result)); + srp->header.resid = SCpnt->resid; + /* N.B. unit of duration changes here from jiffies to millisecs */ + srp->header.duration = + sg_jif_to_ms(jiffies - (int) srp->header.duration); + if (0 != SRpnt->sr_result) { + memcpy(srp->sense_b, SRpnt->sr_sense_buffer, + sizeof (srp->sense_b)); + srp->header.status = 0xff & SRpnt->sr_result; + srp->header.masked_status = status_byte(SRpnt->sr_result); + srp->header.msg_status = msg_byte(SRpnt->sr_result); + srp->header.host_status = host_byte(SRpnt->sr_result); + srp->header.driver_status = driver_byte(SRpnt->sr_result); + if ((sdp->sgdebug > 0) && + ((CHECK_CONDITION == srp->header.masked_status) || + (COMMAND_TERMINATED == srp->header.masked_status))) + print_req_sense("sg_cmd_done", SRpnt); + + /* Following if statement is a patch supplied by Eric Youngdale */ + if (driver_byte(SRpnt->sr_result) != 0 + && (SRpnt->sr_sense_buffer[0] & 0x7f) == 0x70 + && (SRpnt->sr_sense_buffer[2] & 0xf) == UNIT_ATTENTION + && sdp->device->removable) { + /* Detected disc change. Set the bit - this may be used if */ + /* there are filesystems using this device. */ + sdp->device->changed = 1; + } + } + /* Rely on write phase to clean out srp status values, so no "else" */ + + scsi_release_request(SRpnt); + SRpnt = NULL; + if (sfp->closed) { /* whoops this fd already released, cleanup */ + SCSI_LOG_TIMEOUT(1, printk("sg_cmd_done: already closed, freeing ...\n")); + sg_finish_rem_req(srp); + srp = NULL; + if (NULL == sfp->headrp) { + SCSI_LOG_TIMEOUT(1, printk("sg...bh: already closed, final cleanup\n")); + if (0 == sg_remove_sfp(sdp, sfp)) { /* device still present */ + sdp->device->access_count--; + if (sdp->device->host->hostt->module) + __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); + } + if (sg_template.module) + __MOD_DEC_USE_COUNT(sg_template.module); + sfp = NULL; + } + } else if (srp && srp->orphan) { + if (sfp->keep_orphan) + srp->sg_io_owned = 0; + else { + sg_finish_rem_req(srp); + srp = NULL; + } + } + if (sfp && srp) { + /* Now wake up any sg_read() that is waiting for this packet. */ + wake_up_interruptible(&sfp->read_wait); + kill_fasync(&sfp->async_qp, SIGPOLL, POLL_IN); + } } static struct file_operations sg_fops = { - owner: THIS_MODULE, - read: sg_read, - write: sg_write, - poll: sg_poll, - ioctl: sg_ioctl, - open: sg_open, - mmap: sg_mmap, - release: sg_release, - fasync: sg_fasync, + .owner = THIS_MODULE, + .read = sg_read, + .write = sg_write, + .poll = sg_poll, + .ioctl = sg_ioctl, + .open = sg_open, + .mmap = sg_mmap, + .release = sg_release, + .fasync = sg_fasync, }; - -static int sg_detect(Scsi_Device * scsidp) +static int +sg_detect(Scsi_Device * scsidp) { - sg_template.dev_noticed++; - return 1; + sg_template.dev_noticed++; + return 1; } /* Driver initialization */ -static int sg_init() +static int +sg_init() { - static int sg_registered = 0; - unsigned long iflags; + static int sg_registered = 0; + unsigned long iflags; - if ((sg_template.dev_noticed == 0) || sg_dev_arr) - return 0; + if ((sg_template.dev_noticed == 0) || sg_dev_arr) + return 0; - write_lock_irqsave(&sg_dev_arr_lock, iflags); - if(!sg_registered) { - if (register_chrdev(SCSI_GENERIC_MAJOR,"sg",&sg_fops)) { - printk(KERN_ERR "Unable to get major %d for generic SCSI device\n", - SCSI_GENERIC_MAJOR); - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - return 1; - } - sg_registered++; - } + write_lock_irqsave(&sg_dev_arr_lock, iflags); + if (!sg_registered) { + if (register_chrdev(SCSI_GENERIC_MAJOR, "sg", &sg_fops)) { + printk(KERN_ERR + "Unable to get major %d for generic SCSI device\n", + SCSI_GENERIC_MAJOR); + write_unlock_irqrestore(&sg_dev_arr_lock, iflags); + return 1; + } + sg_registered++; + } - SCSI_LOG_TIMEOUT(3, printk("sg_init\n")); - sg_template.dev_max = sg_template.dev_noticed + SG_DEV_ARR_LUMP; - sg_dev_arr = (Sg_device **)kmalloc(sg_template.dev_max * - sizeof(Sg_device *), GFP_ATOMIC); - if (NULL == sg_dev_arr) { - printk(KERN_ERR "sg_init: no space for sg_dev_arr\n"); + SCSI_LOG_TIMEOUT(3, printk("sg_init\n")); + sg_template.dev_max = sg_template.dev_noticed + SG_DEV_ARR_LUMP; + sg_dev_arr = (Sg_device **)vmalloc( + sg_template.dev_max * sizeof(Sg_device *)); + if (NULL == sg_dev_arr) { + printk(KERN_ERR "sg_init: no space for sg_dev_arr\n"); + write_unlock_irqrestore(&sg_dev_arr_lock, iflags); + return 1; + } + memset(sg_dev_arr, 0, sg_template.dev_max * sizeof (Sg_device *)); write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - return 1; - } - memset(sg_dev_arr, 0, sg_template.dev_max * sizeof(Sg_device *)); - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); #ifdef CONFIG_PROC_FS - sg_proc_init(); -#endif /* CONFIG_PROC_FS */ - return 0; + sg_proc_init(); +#endif /* CONFIG_PROC_FS */ + return 0; } #ifndef MODULE -static int __init sg_def_reserved_size_setup(char *str) +static int __init +sg_def_reserved_size_setup(char *str) { - int tmp; + int tmp; - if (get_option(&str, &tmp) == 1) { - def_reserved_size = tmp; - if (tmp >= 0) - sg_big_buff = tmp; - return 1; - } else { - printk(KERN_WARNING "sg_def_reserved_size : usage " - "sg_def_reserved_size=n (n could be 65536, 131072 or 262144)\n"); - return 0; - } + if (get_option(&str, &tmp) == 1) { + def_reserved_size = tmp; + if (tmp >= 0) + sg_big_buff = tmp; + return 1; + } else { + printk(KERN_WARNING "sg_def_reserved_size : usage " + "sg_def_reserved_size=n (n could be 65536, 131072 or 262144)\n"); + return 0; + } } __setup("sg_def_reserved_size=", sg_def_reserved_size_setup); #endif /* Driverfs file support */ -static ssize_t sg_device_kdev_read(struct device *driverfs_dev, char *page, - size_t count, loff_t off) +static ssize_t +sg_device_kdev_read(struct device *driverfs_dev, char *page, + size_t count, loff_t off) { - Sg_device * sdp=list_entry(driverfs_dev, Sg_device, sg_driverfs_dev); - return off ? 0 : sprintf(page, "%x\n",sdp->i_rdev.value); + Sg_device *sdp = list_entry(driverfs_dev, Sg_device, sg_driverfs_dev); + return off ? 0 : sprintf(page, "%x\n", sdp->i_rdev.value); } -static DEVICE_ATTR(kdev,"kdev",S_IRUGO,sg_device_kdev_read,NULL); +static DEVICE_ATTR(kdev,S_IRUGO,sg_device_kdev_read,NULL); -static ssize_t sg_device_type_read(struct device *driverfs_dev, char *page, - size_t count, loff_t off) -{ - return off ? 0 : sprintf (page, "CHR\n"); -} -static DEVICE_ATTR(type,"type",S_IRUGO,sg_device_type_read,NULL); - -static int sg_attach(Scsi_Device * scsidp) -{ - Sg_device * sdp; - unsigned long iflags; - int k; - - write_lock_irqsave(&sg_dev_arr_lock, iflags); - if (sg_template.nr_dev >= sg_template.dev_max) { /* try to resize */ - Sg_device ** tmp_da; - int tmp_dev_max = sg_template.nr_dev + SG_DEV_ARR_LUMP; - - tmp_da = (Sg_device **)kmalloc(tmp_dev_max * - sizeof(Sg_device *), GFP_ATOMIC); - if (NULL == tmp_da) { - scsidp->attached--; - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - printk(KERN_ERR "sg_attach: device array cannot be resized\n"); - return 1; - } - memset(tmp_da, 0, tmp_dev_max * sizeof(Sg_device *)); - memcpy(tmp_da, sg_dev_arr, sg_template.dev_max * sizeof(Sg_device *)); - kfree((char *)sg_dev_arr); - sg_dev_arr = tmp_da; - sg_template.dev_max = tmp_dev_max; - } - - for(k = 0; k < sg_template.dev_max; k++) - if(! sg_dev_arr[k]) break; - if (k > SG_MAX_DEVS_MASK) { - scsidp->attached--; - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - printk(KERN_WARNING "Unable to attach sg device <%d, %d, %d, %d>" - " type=%d, minor number exceed %d\n", scsidp->host->host_no, - scsidp->channel, scsidp->id, scsidp->lun, scsidp->type, - SG_MAX_DEVS_MASK); - return 1; - } - if(k < sg_template.dev_max) - sdp = (Sg_device *)kmalloc(sizeof(Sg_device), GFP_ATOMIC); - else - sdp = NULL; - if (NULL == sdp) { - scsidp->attached--; - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - printk(KERN_ERR "sg_attach: Sg_device cannot be allocated\n"); - return 1; - } +static ssize_t +sg_device_type_read(struct device *driverfs_dev, char *page, + size_t count, loff_t off) +{ + return off ? 0 : sprintf(page, "CHR\n"); +} + +static DEVICE_ATTR(type,S_IRUGO,sg_device_type_read,NULL); - SCSI_LOG_TIMEOUT(3, printk("sg_attach: dev=%d \n", k)); - sdp->device = scsidp; - init_waitqueue_head(&sdp->o_excl_wait); - sdp->headfp= NULL; - sdp->exclude = 0; - sdp->sgdebug = 0; - sdp->detached = 0; - sdp->sg_tablesize = scsidp->host ? scsidp->host->sg_tablesize : 0; - sdp->i_rdev = mk_kdev(SCSI_GENERIC_MAJOR, k); - - memset(&sdp->sg_driverfs_dev, 0, sizeof(struct device)); - sprintf(sdp->sg_driverfs_dev.bus_id, "%s:gen", - scsidp->sdev_driverfs_dev.bus_id); - sprintf(sdp->sg_driverfs_dev.name, "%sgeneric", - scsidp->sdev_driverfs_dev.name); - sdp->sg_driverfs_dev.parent = &scsidp->sdev_driverfs_dev; - sdp->sg_driverfs_dev.bus = &scsi_driverfs_bus_type; - device_register(&sdp->sg_driverfs_dev); - device_create_file(&sdp->sg_driverfs_dev, &dev_attr_type); - device_create_file(&sdp->sg_driverfs_dev, &dev_attr_kdev); - - sdp->de = devfs_register (scsidp->de, "generic", DEVFS_FL_DEFAULT, - SCSI_GENERIC_MAJOR, k, - S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, - &sg_fops, sdp); - sg_template.nr_dev++; - sg_dev_arr[k] = sdp; - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - switch (scsidp->type) { +static int +sg_attach(Scsi_Device * scsidp) +{ + Sg_device *sdp; + unsigned long iflags; + int k; + + write_lock_irqsave(&sg_dev_arr_lock, iflags); + if (sg_template.nr_dev >= sg_template.dev_max) { /* try to resize */ + Sg_device **tmp_da; + int tmp_dev_max = sg_template.nr_dev + SG_DEV_ARR_LUMP; + + tmp_da = (Sg_device **)vmalloc( + tmp_dev_max * sizeof(Sg_device *)); + if (NULL == tmp_da) { + scsidp->attached--; + write_unlock_irqrestore(&sg_dev_arr_lock, iflags); + printk(KERN_ERR + "sg_attach: device array cannot be resized\n"); + return 1; + } + memset(tmp_da, 0, tmp_dev_max * sizeof (Sg_device *)); + memcpy(tmp_da, sg_dev_arr, + sg_template.dev_max * sizeof (Sg_device *)); + vfree((char *) sg_dev_arr); + sg_dev_arr = tmp_da; + sg_template.dev_max = tmp_dev_max; + } + + for (k = 0; k < sg_template.dev_max; k++) + if (!sg_dev_arr[k]) + break; + if (k > SG_MAX_DEVS_MASK) { + scsidp->attached--; + write_unlock_irqrestore(&sg_dev_arr_lock, iflags); + printk(KERN_WARNING + "Unable to attach sg device <%d, %d, %d, %d>" + " type=%d, minor number exceed %d\n", + scsidp->host->host_no, scsidp->channel, scsidp->id, + scsidp->lun, scsidp->type, SG_MAX_DEVS_MASK); + return 1; + } + if (k < sg_template.dev_max) + sdp = (Sg_device *)vmalloc(sizeof(Sg_device)); + else + sdp = NULL; + if (NULL == sdp) { + scsidp->attached--; + write_unlock_irqrestore(&sg_dev_arr_lock, iflags); + printk(KERN_ERR "sg_attach: Sg_device cannot be allocated\n"); + return 1; + } + + SCSI_LOG_TIMEOUT(3, printk("sg_attach: dev=%d \n", k)); + sdp->device = scsidp; + init_waitqueue_head(&sdp->o_excl_wait); + sdp->headfp = NULL; + sdp->exclude = 0; + sdp->sgdebug = 0; + sdp->detached = 0; + sdp->sg_tablesize = scsidp->host ? scsidp->host->sg_tablesize : 0; + sdp->i_rdev = mk_kdev(SCSI_GENERIC_MAJOR, k); + + memset(&sdp->sg_driverfs_dev, 0, sizeof (struct device)); + sprintf(sdp->sg_driverfs_dev.bus_id, "%s:gen", + scsidp->sdev_driverfs_dev.bus_id); + sprintf(sdp->sg_driverfs_dev.name, "%sgeneric", + scsidp->sdev_driverfs_dev.name); + sdp->sg_driverfs_dev.parent = &scsidp->sdev_driverfs_dev; + sdp->sg_driverfs_dev.bus = &scsi_driverfs_bus_type; + device_register(&sdp->sg_driverfs_dev); + device_create_file(&sdp->sg_driverfs_dev, &dev_attr_type); + device_create_file(&sdp->sg_driverfs_dev, &dev_attr_kdev); + + sdp->de = devfs_register(scsidp->de, "generic", DEVFS_FL_DEFAULT, + SCSI_GENERIC_MAJOR, k, + S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP, + &sg_fops, sdp); + sg_template.nr_dev++; + sg_dev_arr[k] = sdp; + write_unlock_irqrestore(&sg_dev_arr_lock, iflags); + switch (scsidp->type) { case TYPE_DISK: case TYPE_MOD: case TYPE_ROM: case TYPE_WORM: - case TYPE_TAPE: break; + case TYPE_TAPE: + break; default: - printk(KERN_NOTICE "Attached scsi generic sg%d at scsi%d, channel" - " %d, id %d, lun %d, type %d\n", k, scsidp->host->host_no, - scsidp->channel, scsidp->id, scsidp->lun, scsidp->type); - } - return 0; -} - -/* Called at 'finish' of init process, after all attaches */ -static void sg_finish(void) -{ } - -static void sg_detach(Scsi_Device * scsidp) -{ - Sg_device * sdp; - unsigned long iflags; - Sg_fd * sfp; - Sg_fd * tsfp; - Sg_request * srp; - Sg_request * tsrp; - int k, delay; - - if (NULL == sg_dev_arr) - return; - delay = 0; - write_lock_irqsave(&sg_dev_arr_lock, iflags); - for (k = 0; k < sg_template.dev_max; k++) { - sdp = sg_dev_arr[k]; - if ((NULL == sdp) || (sdp->device != scsidp)) - continue; /* dirty but lowers nesting */ - if (sdp->headfp) { - sdp->detached = 1; - for (sfp = sdp->headfp; sfp; sfp = tsfp) { - tsfp = sfp->nextfp; - for (srp = sfp->headrp; srp; srp = tsrp) { - tsrp = srp->nextrp; - if (sfp->closed || (0 == srp->done)) - sg_finish_rem_req(srp); - } - if (sfp->closed) { - sdp->device->access_count--; - if (sg_template.module) - __MOD_DEC_USE_COUNT(sg_template.module); - if (sdp->device->host->hostt->module) - __MOD_DEC_USE_COUNT(sdp->device->host->hostt->module); - __sg_remove_sfp(sdp, sfp); + printk(KERN_NOTICE + "Attached scsi generic sg%d at scsi%d, channel" + " %d, id %d, lun %d, type %d\n", k, + scsidp->host->host_no, scsidp->channel, scsidp->id, + scsidp->lun, scsidp->type); + } + return 0; +} + +static void +sg_detach(Scsi_Device * scsidp) +{ + Sg_device *sdp; + unsigned long iflags; + Sg_fd *sfp; + Sg_fd *tsfp; + Sg_request *srp; + Sg_request *tsrp; + int k, delay; + + if (NULL == sg_dev_arr) + return; + delay = 0; + write_lock_irqsave(&sg_dev_arr_lock, iflags); + for (k = 0; k < sg_template.dev_max; k++) { + sdp = sg_dev_arr[k]; + if ((NULL == sdp) || (sdp->device != scsidp)) + continue; /* dirty but lowers nesting */ + if (sdp->headfp) { + sdp->detached = 1; + for (sfp = sdp->headfp; sfp; sfp = tsfp) { + tsfp = sfp->nextfp; + for (srp = sfp->headrp; srp; srp = tsrp) { + tsrp = srp->nextrp; + if (sfp->closed || (0 == srp->done)) + sg_finish_rem_req(srp); + } + if (sfp->closed) { + sdp->device->access_count--; + if (sg_template.module) + __MOD_DEC_USE_COUNT(sg_template.module); + if (sdp->device->host->hostt->module) + __MOD_DEC_USE_COUNT( + sdp->device->host-> + hostt->module); + __sg_remove_sfp(sdp, sfp); + } else { + delay = 1; + wake_up_interruptible(&sfp->read_wait); + kill_fasync(&sfp->async_qp, SIGPOLL, + POLL_HUP); + } + } + SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d, dirty\n", k)); + devfs_unregister(sdp->de); + device_remove_file(&sdp->sg_driverfs_dev, + &dev_attr_type); + device_remove_file(&sdp->sg_driverfs_dev, + &dev_attr_kdev); + put_device(&sdp->sg_driverfs_dev); + sdp->de = NULL; + if (NULL == sdp->headfp) { + vfree((char *) sdp); + sg_dev_arr[k] = NULL; + } + } else { /* nothing active, simple case */ + SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d\n", k)); + devfs_unregister(sdp->de); + put_device(&sdp->sg_driverfs_dev); + vfree((char *) sdp); + sg_dev_arr[k] = NULL; } - else { - delay = 1; - wake_up_interruptible(&sfp->read_wait); - kill_fasync(&sfp->async_qp, SIGPOLL, POLL_HUP); - } - } - SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d, dirty\n", k)); - devfs_unregister (sdp->de); - device_remove_file(&sdp->sg_driverfs_dev,&dev_attr_type); - device_remove_file(&sdp->sg_driverfs_dev,&dev_attr_kdev); - put_device(&sdp->sg_driverfs_dev); - sdp->de = NULL; - if (NULL == sdp->headfp) { - kfree((char *)sdp); - sg_dev_arr[k] = NULL; - } - } - else { /* nothing active, simple case */ - SCSI_LOG_TIMEOUT(3, printk("sg_detach: dev=%d\n", k)); - devfs_unregister (sdp->de); - put_device(&sdp->sg_driverfs_dev); - kfree((char *)sdp); - sg_dev_arr[k] = NULL; - } - scsidp->attached--; - sg_template.nr_dev--; - sg_template.dev_noticed--; /* from */ - break; - } - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - if (delay) - scsi_sleep(2); /* dirty detach so delay device destruction */ + scsidp->attached--; + sg_template.nr_dev--; + sg_template.dev_noticed--; /* from */ + break; + } + write_unlock_irqrestore(&sg_dev_arr_lock, iflags); + if (delay) + scsi_sleep(2); /* dirty detach so delay device destruction */ } MODULE_AUTHOR("Douglas Gilbert"); @@ -1583,1215 +1588,1144 @@ MODULE_PARM(def_reserved_size, "i"); MODULE_PARM_DESC(def_reserved_size, "size of buffer reserved for each fd"); -static int __init init_sg(void) { - int rc; - if (def_reserved_size >= 0) - sg_big_buff = def_reserved_size; - rc = scsi_register_device(&sg_template); - if (!rc) { - sg_template.scsi_driverfs_driver.name = (char *)sg_template.tag; - sg_template.scsi_driverfs_driver.bus = &scsi_driverfs_bus_type; - driver_register(&sg_template.scsi_driverfs_driver); - } - return rc; +static int __init +init_sg(void) +{ + int rc; + if (def_reserved_size >= 0) + sg_big_buff = def_reserved_size; + rc = scsi_register_device(&sg_template); + if (!rc) { + sg_template.scsi_driverfs_driver.name = + (char *) sg_template.tag; + sg_template.scsi_driverfs_driver.bus = &scsi_driverfs_bus_type; + driver_register(&sg_template.scsi_driverfs_driver); + } + return rc; } -static void __exit exit_sg( void) +static void __exit +exit_sg(void) { #ifdef CONFIG_PROC_FS - sg_proc_cleanup(); -#endif /* CONFIG_PROC_FS */ - scsi_unregister_device(&sg_template); - unregister_chrdev(SCSI_GENERIC_MAJOR, "sg"); - if(sg_dev_arr != NULL) { - kfree((char *)sg_dev_arr); - sg_dev_arr = NULL; - } - sg_template.dev_max = 0; - remove_driver(&sg_template.scsi_driverfs_driver); -} - - -static int sg_start_req(Sg_request * srp) -{ - int res; - Sg_fd * sfp = srp->parentfp; - sg_io_hdr_t * hp = &srp->header; - int dxfer_len = (int)hp->dxfer_len; - int dxfer_dir = hp->dxfer_direction; - Sg_scatter_hold * req_schp = &srp->data; - Sg_scatter_hold * rsv_schp = &sfp->reserve; - - SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len)); - if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) - return 0; - if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) && - (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) && - (! sfp->parentdp->device->host->unchecked_isa_dma)) { - res = sg_build_dir(srp, sfp, dxfer_len); - if (res <= 0) /* -ve -> error, 0 -> done, 1 -> try indirect */ - return res; - } - if ((! sg_res_in_use(sfp)) && (dxfer_len <= rsv_schp->bufflen)) - sg_link_reserve(sfp, srp, dxfer_len); - else { - res = sg_build_indi(req_schp, sfp, dxfer_len); - if (res) { - sg_remove_scat(req_schp); - return res; - } - } - return 0; + sg_proc_cleanup(); +#endif /* CONFIG_PROC_FS */ + scsi_unregister_device(&sg_template); + unregister_chrdev(SCSI_GENERIC_MAJOR, "sg"); + if (sg_dev_arr != NULL) { + vfree((char *) sg_dev_arr); + sg_dev_arr = NULL; + } + sg_template.dev_max = 0; + remove_driver(&sg_template.scsi_driverfs_driver); +} + +static int +sg_start_req(Sg_request * srp) +{ + int res; + Sg_fd *sfp = srp->parentfp; + sg_io_hdr_t *hp = &srp->header; + int dxfer_len = (int) hp->dxfer_len; + int dxfer_dir = hp->dxfer_direction; + Sg_scatter_hold *req_schp = &srp->data; + Sg_scatter_hold *rsv_schp = &sfp->reserve; + + SCSI_LOG_TIMEOUT(4, printk("sg_start_req: dxfer_len=%d\n", dxfer_len)); + if ((dxfer_len <= 0) || (dxfer_dir == SG_DXFER_NONE)) + return 0; + if (sg_allow_dio && (hp->flags & SG_FLAG_DIRECT_IO) && + (dxfer_dir != SG_DXFER_UNKNOWN) && (0 == hp->iovec_count) && + (!sfp->parentdp->device->host->unchecked_isa_dma)) { + res = sg_build_direct(srp, sfp, dxfer_len); + if (res <= 0) /* -ve -> error, 0 -> done, 1 -> try indirect */ + return res; + } + if ((!sg_res_in_use(sfp)) && (dxfer_len <= rsv_schp->bufflen)) + sg_link_reserve(sfp, srp, dxfer_len); + else { + res = sg_build_indirect(req_schp, sfp, dxfer_len); + if (res) { + sg_remove_scat(req_schp); + return res; + } + } + return 0; } -static void sg_finish_rem_req(Sg_request * srp) +static void +sg_finish_rem_req(Sg_request * srp) { - Sg_fd * sfp = srp->parentfp; - Sg_scatter_hold * req_schp = &srp->data; + Sg_fd *sfp = srp->parentfp; + Sg_scatter_hold *req_schp = &srp->data; - SCSI_LOG_TIMEOUT(4, printk("sg_finish_rem_req: res_used=%d\n", - (int)srp->res_used)); - sg_unmap_and(&srp->data, 1); - if (srp->res_used) - sg_unlink_reserve(sfp, srp); - else - sg_remove_scat(req_schp); - sg_remove_request(sfp, srp); + SCSI_LOG_TIMEOUT(4, printk("sg_finish_rem_req: res_used=%d\n", (int) srp->res_used)); + // sg_unmap_and(&srp->data, 1); + if (srp->res_used) + sg_unlink_reserve(sfp, srp); + else + sg_remove_scat(req_schp); + sg_remove_request(sfp, srp); } -static int sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, - int tablesize) +static int +sg_build_sgat(Sg_scatter_hold * schp, const Sg_fd * sfp, int tablesize) { - int mem_src, ret_sz; - int elem_sz = sizeof(struct scatterlist) + sizeof(char); - /* scatter gather array, followed by mem_src_arr (array of chars) */ - int sg_bufflen = tablesize * elem_sz; - int mx_sc_elems = tablesize; - - mem_src = SG_HEAP_KMAL; - schp->buffer = sg_malloc(sfp, sg_bufflen, &ret_sz, &mem_src); - if (! schp->buffer) - return -ENOMEM; - else if (ret_sz != sg_bufflen) { - sg_bufflen = ret_sz; - mx_sc_elems = sg_bufflen / elem_sz; - } - schp->buffer_mem_src = (char)mem_src; - schp->sglist_len = sg_bufflen; - memset(schp->buffer, 0, sg_bufflen); - return mx_sc_elems; /* number of scat_gath elements allocated */ + int ret_sz; + int elem_sz = sizeof (struct scatterlist); + int sg_bufflen = tablesize * elem_sz; + int mx_sc_elems = tablesize; + + schp->buffer = sg_page_malloc(sg_bufflen, sfp->low_dma, &ret_sz); + if (!schp->buffer) + return -ENOMEM; + else if (ret_sz != sg_bufflen) { + sg_bufflen = ret_sz; + mx_sc_elems = sg_bufflen / elem_sz; + } + schp->sglist_len = sg_bufflen; + memset(schp->buffer, 0, sg_bufflen); + return mx_sc_elems; /* number of scat_gath elements allocated */ } -static void sg_unmap_and(Sg_scatter_hold * schp, int free_also) -{ #ifdef SG_ALLOW_DIO_CODE - int nbhs = 0; +/* vvvvvvvv following code borrowed from st driver's direct IO vvvvvvvvv */ + /* hopefully this generic code will moved to a library */ - if (schp && schp->kiobp) { - if (schp->mapped) { - unmap_kiobuf(schp->kiobp); - schp->mapped = 0; - } - if (free_also) { - sg_free_kiovec(1, &schp->kiobp, &nbhs); - schp->kiobp = NULL; +/* Pin down user pages and put them into a scatter gather list. Returns <= 0 if + - mapping of all pages not successful + - any page is above max_pfn + (i.e., either completely successful or fails) +*/ +static int +st_map_user_pages(struct scatterlist *sgl, const unsigned int max_pages, + unsigned long uaddr, size_t count, int rw, + unsigned long max_pfn) +{ + int res, i, j; + unsigned int nr_pages; + struct page **pages; + + nr_pages = ((uaddr & ~PAGE_MASK) + count - 1 + ~PAGE_MASK) >> PAGE_SHIFT; + + /* User attempted Overflow! */ + if ((uaddr + count) < uaddr) + return -EINVAL; + + /* Too big */ + if (nr_pages > max_pages) + return -ENOMEM; + + /* Hmm? */ + if (count == 0) + return 0; + + if ((pages = kmalloc(max_pages * sizeof(*pages), GFP_ATOMIC)) == NULL) + return -ENOMEM; + + /* Try to fault in all of the necessary pages */ + down_read(¤t->mm->mmap_sem); + /* rw==READ means read from drive, write into memory area */ + res = get_user_pages( + current, + current->mm, + uaddr, + nr_pages, + rw == READ, + 0, /* don't force */ + pages, + NULL); + up_read(¤t->mm->mmap_sem); + + /* Errors and no page mapped should return here */ + if (res < nr_pages) + goto out_unmap; + + for (i=0; i < nr_pages; i++) { + /* FIXME: flush superflous for rw==READ, + * probably wrong function for rw==WRITE + */ + flush_dcache_page(pages[i]); + if (page_to_pfn(pages[i]) > max_pfn) + goto out_unlock; + /* ?? Is locking needed? I don't think so */ + /* if (TestSetPageLocked(pages[i])) + goto out_unlock; */ + } + + /* Populate the scatter/gather list */ + sgl[0].page = pages[0]; + sgl[0].offset = uaddr & ~PAGE_MASK; + if (nr_pages > 1) { + sgl[0].length = PAGE_SIZE - sgl[0].offset; + count -= sgl[0].length; + for (i=1; i < nr_pages ; i++) { + sgl[i].offset = 0; + sgl[i].page = pages[i]; + sgl[i].length = count < PAGE_SIZE ? count : PAGE_SIZE; + count -= PAGE_SIZE; + } } - } -#endif + else { + sgl[0].length = count; + } + + kfree(pages); + return nr_pages; + + out_unlock: + /* for (j=0; j < i; j++) + unlock_page(pages[j]); */ + res = 0; + out_unmap: + if (res > 0) + for (j=0; j < res; j++) + page_cache_release(pages[j]); + kfree(pages); + return res; } -static int sg_build_dir(Sg_request * srp, Sg_fd * sfp, int dxfer_len) + +/* And unmap them... */ +static int +st_unmap_user_pages(struct scatterlist *sgl, const unsigned int nr_pages, + int dirtied) +{ + int i; + + for (i=0; i < nr_pages; i++) { + if (dirtied && !PageReserved(sgl[i].page)) + SetPageDirty(sgl[i].page); + /* unlock_page(sgl[i].page); */ + /* FIXME: cache flush missing for rw==READ + * FIXME: call the correct reference counting function + */ + page_cache_release(sgl[i].page); + } + + return 0; +} + +/* ^^^^^^^^ above code borrowed from st driver's direct IO ^^^^^^^^^ */ +#endif + + +/* Returns: -ve -> error, 0 -> done, 1 -> try indirect */ +static int +sg_build_direct(Sg_request * srp, Sg_fd * sfp, int dxfer_len) { #ifdef SG_ALLOW_DIO_CODE - int res, k, j, split, offset, num, mx_sc_elems, rem_sz; - struct kiobuf * kp; - char * mem_src_arr; - struct scatterlist * sclp; - unsigned long addr, prev_addr; - sg_io_hdr_t * hp = &srp->header; - Sg_scatter_hold * schp = &srp->data; - int sg_tablesize = sfp->parentdp->sg_tablesize; - unsigned char * pg_addr; - unsigned char * hold_pg_addr = NULL; - int nbhs = 0; - - res = sg_alloc_kiovec(1, &schp->kiobp, &nbhs); - if (0 != res) { - SCSI_LOG_TIMEOUT(5, printk("sg_build_dir: sg_alloc_kiovec res=%d\n", - res)); - return 1; - } - res = map_user_kiobuf((SG_DXFER_TO_DEV == hp->dxfer_direction) ? 1 : 0, - schp->kiobp, (unsigned long)hp->dxferp, dxfer_len); - if (0 != res) { - SCSI_LOG_TIMEOUT(5, - printk("sg_build_dir: map_user_kiobuf res=%d\n", res)); - sg_unmap_and(schp, 1); - return 1; - } - schp->mapped = 1; - kp = schp->kiobp; - prev_addr = (unsigned long) page_address(kp->maplist[0]); - for (k = 1, split = 0; k < kp->nr_pages; ++k, prev_addr = addr) { - addr = (unsigned long) page_address(kp->maplist[k]); - if ((prev_addr + PAGE_SIZE) != addr) { - split = k; - break; - } - } - if (! split) { - schp->k_use_sg = 0; - schp->buffer = page_address(kp->maplist[0]) + kp->offset; - schp->bufflen = dxfer_len; - schp->buffer_mem_src = SG_USER_MEM; - schp->b_malloc_len = dxfer_len; + sg_io_hdr_t *hp = &srp->header; + Sg_scatter_hold *schp = &srp->data; + int sg_tablesize = sfp->parentdp->sg_tablesize; + struct scatterlist *sgl; + int mx_sc_elems, res; + + mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize); + if (mx_sc_elems <= 0) { + return 1; + } + sgl = (struct scatterlist *)schp->buffer; + res = st_map_user_pages(sgl, mx_sc_elems, (unsigned long)hp->dxferp, dxfer_len, + (SG_DXFER_TO_DEV == hp->dxfer_direction) ? 1 : 0, ULONG_MAX); + if (res <= 0) + return 1; + schp->k_use_sg = res; + schp->dio_in_use = 1; hp->info |= SG_INFO_DIRECT_IO; return 0; - } - mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize); - if (mx_sc_elems <= 1) { - sg_unmap_and(schp, 1); - sg_remove_scat(schp); - return 1; - } - mem_src_arr = schp->buffer + (mx_sc_elems * sizeof(struct scatterlist)); - for (k = 0, j = 0, sclp = schp->buffer, rem_sz = dxfer_len; - (rem_sz > 0) && (j < mx_sc_elems); ++k) { - offset = (0 == k) ? kp->offset : 0; - num = (rem_sz > (PAGE_SIZE - offset)) ? (PAGE_SIZE - offset) : - rem_sz; - pg_addr = page_address(kp->maplist[k]); - if ((k > 0) && ((hold_pg_addr + PAGE_SIZE) == pg_addr)) - (sclp - 1)->length += num; - else { - sclp->page = kp->maplist[k]; - sclp->offset = offset; - sclp->length = num; - mem_src_arr[j] = SG_USER_MEM; - ++j; - ++sclp; - } - hold_pg_addr = pg_addr; - rem_sz -= num; - } - schp->k_use_sg = j; - SCSI_LOG_TIMEOUT(5, printk("sg_build_dir: k_use_sg=%d, k=%d, rem_sz=%d\n", - j, k, rem_sz)); - schp->bufflen = dxfer_len; - if (rem_sz > 0) { /* must have failed */ - sg_unmap_and(schp, 1); - sg_remove_scat(schp); - return 1; /* out of scatter gather elements, try indirect */ - } - hp->info |= SG_INFO_DIRECT_IO; - return 0; #else - return 1; -#endif /* SG_ALLOW_DIO_CODE */ + return 1; +#endif } -static int sg_build_indi(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) +static int +sg_build_indirect(Sg_scatter_hold * schp, Sg_fd * sfp, int buff_size) { - int ret_sz, mem_src; - int blk_size = buff_size; - unsigned char * p = NULL; + int ret_sz; + int blk_size = buff_size; + unsigned char *p = NULL; - if ((blk_size < 0) || (! sfp)) - return -EFAULT; - if (0 == blk_size) - ++blk_size; /* don't know why */ + if ((blk_size < 0) || (!sfp)) + return -EFAULT; + if (0 == blk_size) + ++blk_size; /* don't know why */ /* round request up to next highest SG_SECTOR_SZ byte boundary */ - blk_size = (blk_size + SG_SECTOR_MSK) & (~SG_SECTOR_MSK); - SCSI_LOG_TIMEOUT(4, printk("sg_build_indi: buff_size=%d, blk_size=%d\n", - buff_size, blk_size)); - if (blk_size <= SG_SCATTER_SZ) { - mem_src = SG_HEAP_PAGE; - p = sg_malloc(sfp, blk_size, &ret_sz, &mem_src); - if (! p) - return -ENOMEM; - if (blk_size == ret_sz) { /* got it on the first attempt */ - schp->k_use_sg = 0; - schp->buffer = p; - schp->bufflen = blk_size; - schp->buffer_mem_src = (char)mem_src; - schp->b_malloc_len = blk_size; - return 0; - } - } - else { - mem_src = SG_HEAP_PAGE; - p = sg_malloc(sfp, SG_SCATTER_SZ, &ret_sz, &mem_src); - if (! p) - return -ENOMEM; - } + blk_size = (blk_size + SG_SECTOR_MSK) & (~SG_SECTOR_MSK); + SCSI_LOG_TIMEOUT(4, printk("sg_build_indirect: buff_size=%d, blk_size=%d\n", + buff_size, blk_size)); + if (blk_size <= SG_SCATTER_SZ) { + p = sg_page_malloc(blk_size, sfp->low_dma, &ret_sz); + if (!p) + return -ENOMEM; + if (blk_size == ret_sz) { /* got it on the first attempt */ + schp->k_use_sg = 0; + schp->buffer = p; + schp->bufflen = blk_size; + schp->b_malloc_len = blk_size; + return 0; + } + } else { + p = sg_page_malloc(SG_SCATTER_SZ, sfp->low_dma, &ret_sz); + if (!p) + return -ENOMEM; + } /* Want some local declarations, so start new block ... */ - { /* lets try and build a scatter gather list */ - struct scatterlist * sclp; - int k, rem_sz, num; - int mx_sc_elems; - int sg_tablesize = sfp->parentdp->sg_tablesize; - int first = 1; - char * mem_src_arr; - - /* N.B. ret_sz and mem_src carried into this block ... */ - mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize); - if (mx_sc_elems < 0) - return mx_sc_elems; /* most likely -ENOMEM */ - mem_src_arr = schp->buffer + - (mx_sc_elems * sizeof(struct scatterlist)); - - for (k = 0, sclp = schp->buffer, rem_sz = blk_size; - (rem_sz > 0) && (k < mx_sc_elems); - ++k, rem_sz -= ret_sz, ++sclp) { - if (first) - first = 0; - else { - num = (rem_sz > SG_SCATTER_SZ) ? SG_SCATTER_SZ : rem_sz; - mem_src = SG_HEAP_PAGE; - p = sg_malloc(sfp, num, &ret_sz, &mem_src); - if (! p) - break; - } - sclp->page = virt_to_page(p); - sclp->offset = (unsigned long)p & ~PAGE_MASK; - sclp->length = ret_sz; - mem_src_arr[k] = mem_src; - - SCSI_LOG_TIMEOUT(5, - printk("sg_build_build: k=%d, a=0x%p, len=%d, ms=%d\n", - k, sg_scatg2virt(sclp), ret_sz, mem_src)); - } /* end of for loop */ - schp->k_use_sg = k; - SCSI_LOG_TIMEOUT(5, - printk("sg_build_indi: k_use_sg=%d, rem_sz=%d\n", k, rem_sz)); - schp->bufflen = blk_size; - if (rem_sz > 0) /* must have failed */ - return -ENOMEM; - } - return 0; -} - -static int sg_write_xfer(Sg_request * srp) -{ - sg_io_hdr_t * hp = &srp->header; - Sg_scatter_hold * schp = &srp->data; - int num_xfer = 0; - int j, k, onum, usglen, ksglen, res, ok; - int iovec_count = (int)hp->iovec_count; - int dxfer_dir = hp->dxfer_direction; - unsigned char * p; - unsigned char * up; - int new_interface = ('\0' == hp->interface_id) ? 0 : 1; - - if ((SG_DXFER_UNKNOWN == dxfer_dir) || (SG_DXFER_TO_DEV == dxfer_dir) || - (SG_DXFER_TO_FROM_DEV == dxfer_dir)) { - num_xfer = (int)(new_interface ? hp->dxfer_len : hp->flags); - if (schp->bufflen < num_xfer) - num_xfer = schp->bufflen; - } - if ((num_xfer <= 0) || - (new_interface && ((SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO) & hp->flags))) + { /* lets try and build a scatter gather list */ + struct scatterlist *sclp; + int k, rem_sz, num; + int mx_sc_elems; + int sg_tablesize = sfp->parentdp->sg_tablesize; + int first = 1; + + /* N.B. ret_sz carried into this block ... */ + mx_sc_elems = sg_build_sgat(schp, sfp, sg_tablesize); + if (mx_sc_elems < 0) + return mx_sc_elems; /* most likely -ENOMEM */ + + for (k = 0, sclp = schp->buffer, rem_sz = blk_size; + (rem_sz > 0) && (k < mx_sc_elems); + ++k, rem_sz -= ret_sz, ++sclp) { + if (first) + first = 0; + else { + num = + (rem_sz > + SG_SCATTER_SZ) ? SG_SCATTER_SZ : rem_sz; + p = sg_page_malloc(num, sfp->low_dma, &ret_sz); + if (!p) + break; + } + sclp->page = virt_to_page(p); + sclp->offset = (unsigned long) p & ~PAGE_MASK; + sclp->length = ret_sz; + + SCSI_LOG_TIMEOUT(5, printk("sg_build_build: k=%d, a=0x%p, len=%d\n", + k, sg_scatg2virt(sclp), ret_sz)); + } /* end of for loop */ + schp->k_use_sg = k; + SCSI_LOG_TIMEOUT(5, printk("sg_build_indirect: k_use_sg=%d, rem_sz=%d\n", k, rem_sz)); + schp->bufflen = blk_size; + if (rem_sz > 0) /* must have failed */ + return -ENOMEM; + } return 0; +} - SCSI_LOG_TIMEOUT(4, - printk("sg_write_xfer: num_xfer=%d, iovec_count=%d, k_use_sg=%d\n", - num_xfer, iovec_count, schp->k_use_sg)); - if (iovec_count) { - onum = iovec_count; - if ((k = verify_area(VERIFY_READ, hp->dxferp, - SZ_SG_IOVEC * onum))) - return k; - } - else - onum = 1; - - if (0 == schp->k_use_sg) { /* kernel has single buffer */ - if (SG_USER_MEM != schp->buffer_mem_src) { /* else nothing to do */ - - for (j = 0, p = schp->buffer; j < onum; ++j) { - res = sg_u_iovec(hp, iovec_count, j, 1, &usglen, &up); - if (res) return res; - usglen = (num_xfer > usglen) ? usglen : num_xfer; - if (__copy_from_user(p, up, usglen)) - return -EFAULT; - p += usglen; - num_xfer -= usglen; - if (num_xfer <= 0) - return 0; - } - } - } - else { /* kernel using scatter gather list */ - struct scatterlist * sclp = (struct scatterlist *)schp->buffer; - char * mem_src_arr = sg_get_sgat_msa(schp); - - ksglen = (int)sclp->length; - p = sg_scatg2virt(sclp); - for (j = 0, k = 0; j < onum; ++j) { - res = sg_u_iovec(hp, iovec_count, j, 1, &usglen, &up); - if (res) return res; - - for (; k < schp->k_use_sg; ++k, ++sclp) { - ksglen = (int)sclp->length; - p = sg_scatg2virt(sclp); - if (NULL == p) - break; - ok = (SG_USER_MEM != mem_src_arr[k]); - if (usglen <= 0) - break; - if (ksglen > usglen) { - if (usglen >= num_xfer) { - if (ok) { - if (__copy_from_user(p, up, num_xfer)) - return -EFAULT; - } - return 0; - } - if (ok) { +static int +sg_write_xfer(Sg_request * srp) +{ + sg_io_hdr_t *hp = &srp->header; + Sg_scatter_hold *schp = &srp->data; + int num_xfer = 0; + int j, k, onum, usglen, ksglen, res; + int iovec_count = (int) hp->iovec_count; + int dxfer_dir = hp->dxfer_direction; + unsigned char *p; + unsigned char *up; + int new_interface = ('\0' == hp->interface_id) ? 0 : 1; + + if ((SG_DXFER_UNKNOWN == dxfer_dir) || (SG_DXFER_TO_DEV == dxfer_dir) || + (SG_DXFER_TO_FROM_DEV == dxfer_dir)) { + num_xfer = (int) (new_interface ? hp->dxfer_len : hp->flags); + if (schp->bufflen < num_xfer) + num_xfer = schp->bufflen; + } + if ((num_xfer <= 0) || (schp->dio_in_use) || + (new_interface + && ((SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO) & hp->flags))) + return 0; + + SCSI_LOG_TIMEOUT(4, printk("sg_write_xfer: num_xfer=%d, iovec_count=%d, k_use_sg=%d\n", + num_xfer, iovec_count, schp->k_use_sg)); + if (iovec_count) { + onum = iovec_count; + if ((k = verify_area(VERIFY_READ, hp->dxferp, + SZ_SG_IOVEC * onum))) + return k; + } else + onum = 1; + + if (0 == schp->k_use_sg) { /* kernel has single buffer */ + for (j = 0, p = schp->buffer; j < onum; ++j) { + res = sg_u_iovec(hp, iovec_count, j, 1, &usglen, &up); + if (res) + return res; + usglen = (num_xfer > usglen) ? usglen : num_xfer; if (__copy_from_user(p, up, usglen)) - return -EFAULT; - } - p += usglen; - ksglen -= usglen; - break; - } - else { - if (ksglen >= num_xfer) { - if (ok) { - if (__copy_from_user(p, up, num_xfer)) return -EFAULT; + p += usglen; + num_xfer -= usglen; + if (num_xfer <= 0) + return 0; + } + } else { /* kernel using scatter gather list */ + struct scatterlist *sclp = (struct scatterlist *) schp->buffer; + + ksglen = (int) sclp->length; + p = sg_scatg2virt(sclp); + for (j = 0, k = 0; j < onum; ++j) { + res = sg_u_iovec(hp, iovec_count, j, 1, &usglen, &up); + if (res) + return res; + + for (; k < schp->k_use_sg; ++k, ++sclp) { + ksglen = (int) sclp->length; + p = sg_scatg2virt(sclp); + if (NULL == p) + break; + if (usglen <= 0) + break; + if (ksglen > usglen) { + if (usglen >= num_xfer) { + if (__copy_from_user + (p, up, num_xfer)) + return -EFAULT; + return 0; + } + if (__copy_from_user(p, up, usglen)) + return -EFAULT; + p += usglen; + ksglen -= usglen; + break; + } else { + if (ksglen >= num_xfer) { + if (__copy_from_user + (p, up, num_xfer)) + return -EFAULT; + return 0; + } + if (__copy_from_user(p, up, ksglen)) + return -EFAULT; + up += ksglen; + usglen -= ksglen; + } } - return 0; - } - if (ok) { - if (__copy_from_user(p, up, ksglen)) - return -EFAULT; - } - up += ksglen; - usglen -= ksglen; } - } - } - } - return 0; + } + return 0; } -static int sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind, - int wr_xf, int * countp, unsigned char ** up) -{ - int num_xfer = (int)hp->dxfer_len; - unsigned char * p; - int count, k; - sg_iovec_t u_iovec; - - if (0 == sg_num) { - p = (unsigned char *)hp->dxferp; - if (wr_xf && ('\0' == hp->interface_id)) - count = (int)hp->flags; /* holds "old" input_size */ - else - count = num_xfer; - } - else { - if (__copy_from_user(&u_iovec, - (unsigned char *)hp->dxferp + (ind * SZ_SG_IOVEC), - SZ_SG_IOVEC)) - return -EFAULT; - p = (unsigned char *)u_iovec.iov_base; - count = (int)u_iovec.iov_len; - } - if ((k = verify_area(wr_xf ? VERIFY_READ : VERIFY_WRITE, p, count))) - return k; - if (up) *up = p; - if (countp) *countp = count; - return 0; -} - -static char * sg_get_sgat_msa(Sg_scatter_hold * schp) -{ - int elem_sz = sizeof(struct scatterlist) + sizeof(char); - int mx_sc_elems = schp->sglist_len / elem_sz; - return schp->buffer + (sizeof(struct scatterlist) * mx_sc_elems); -} - -static void sg_remove_scat(Sg_scatter_hold * schp) -{ - SCSI_LOG_TIMEOUT(4, printk("sg_remove_scat: k_use_sg=%d\n", - schp->k_use_sg)); - if (schp->buffer && schp->sglist_len) { - int k, mem_src; - struct scatterlist * sclp = (struct scatterlist *)schp->buffer; - char * mem_src_arr = sg_get_sgat_msa(schp); - - for (k = 0; (k < schp->k_use_sg) && sg_scatg2virt(sclp); ++k, ++sclp) { - mem_src = mem_src_arr[k]; - SCSI_LOG_TIMEOUT(5, - printk("sg_remove_scat: k=%d, a=0x%p, len=%d, ms=%d\n", - k, sg_scatg2virt(sclp), sclp->length, mem_src)); - sg_free(sg_scatg2virt(sclp), sclp->length, mem_src); - sclp->page = NULL; - sclp->offset = 0; - sclp->length = 0; - } - sg_free(schp->buffer, schp->sglist_len, schp->buffer_mem_src); - } - else if (schp->buffer) - sg_free(schp->buffer, schp->b_malloc_len, schp->buffer_mem_src); - memset(schp, 0, sizeof(*schp)); -} - -static int sg_read_xfer(Sg_request * srp) -{ - sg_io_hdr_t * hp = &srp->header; - Sg_scatter_hold * schp = &srp->data; - int num_xfer = 0; - int j, k, onum, usglen, ksglen, res, ok; - int iovec_count = (int)hp->iovec_count; - int dxfer_dir = hp->dxfer_direction; - unsigned char * p; - unsigned char * up; - int new_interface = ('\0' == hp->interface_id) ? 0 : 1; - - if ((SG_DXFER_UNKNOWN == dxfer_dir) || (SG_DXFER_FROM_DEV == dxfer_dir) || - (SG_DXFER_TO_FROM_DEV == dxfer_dir)) { - num_xfer = hp->dxfer_len; - if (schp->bufflen < num_xfer) - num_xfer = schp->bufflen; - } - if ((num_xfer <= 0) || - (new_interface && ((SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO) & hp->flags))) +static int +sg_u_iovec(sg_io_hdr_t * hp, int sg_num, int ind, + int wr_xf, int *countp, unsigned char **up) +{ + int num_xfer = (int) hp->dxfer_len; + unsigned char *p; + int count, k; + sg_iovec_t u_iovec; + + if (0 == sg_num) { + p = (unsigned char *) hp->dxferp; + if (wr_xf && ('\0' == hp->interface_id)) + count = (int) hp->flags; /* holds "old" input_size */ + else + count = num_xfer; + } else { + if (__copy_from_user(&u_iovec, + (unsigned char *) hp->dxferp + + (ind * SZ_SG_IOVEC), SZ_SG_IOVEC)) + return -EFAULT; + p = (unsigned char *) u_iovec.iov_base; + count = (int) u_iovec.iov_len; + } + if ((k = verify_area(wr_xf ? VERIFY_READ : VERIFY_WRITE, p, count))) + return k; + if (up) + *up = p; + if (countp) + *countp = count; return 0; +} - SCSI_LOG_TIMEOUT(4, - printk("sg_read_xfer: num_xfer=%d, iovec_count=%d, k_use_sg=%d\n", - num_xfer, iovec_count, schp->k_use_sg)); - if (iovec_count) { - onum = iovec_count; - if ((k = verify_area(VERIFY_READ, hp->dxferp, - SZ_SG_IOVEC * onum))) - return k; - } - else - onum = 1; - - if (0 == schp->k_use_sg) { /* kernel has single buffer */ - if (SG_USER_MEM != schp->buffer_mem_src) { /* else nothing to do */ - for (j = 0, p = schp->buffer; j < onum; ++j) { - res = sg_u_iovec(hp, iovec_count, j, 0, &usglen, &up); - if (res) return res; - usglen = (num_xfer > usglen) ? usglen : num_xfer; - if (__copy_to_user(up, p, usglen)) - return -EFAULT; - p += usglen; - num_xfer -= usglen; - if (num_xfer <= 0) - return 0; - } - } - } - else { /* kernel using scatter gather list */ - struct scatterlist * sclp = (struct scatterlist *)schp->buffer; - char * mem_src_arr = sg_get_sgat_msa(schp); - - ksglen = (int)sclp->length; - p = sg_scatg2virt(sclp); - for (j = 0, k = 0; j < onum; ++j) { - res = sg_u_iovec(hp, iovec_count, j, 0, &usglen, &up); - if (res) return res; +static void +sg_remove_scat(Sg_scatter_hold * schp) +{ + SCSI_LOG_TIMEOUT(4, printk("sg_remove_scat: k_use_sg=%d\n", schp->k_use_sg)); + if (schp->buffer && (schp->sglist_len > 0)) { + struct scatterlist *sclp = (struct scatterlist *) schp->buffer; - for (; k < schp->k_use_sg; ++k, ++sclp) { - ksglen = (int)sclp->length; - p = sg_scatg2virt(sclp); - if (NULL == p) - break; - ok = (SG_USER_MEM != mem_src_arr[k]); - if (usglen <= 0) - break; - if (ksglen > usglen) { - if (usglen >= num_xfer) { - if (ok) { - if (__copy_to_user(up, p, num_xfer)) - return -EFAULT; + if (schp->dio_in_use) { +#ifdef SG_ALLOW_DIO_CODE + st_unmap_user_pages(sclp, schp->k_use_sg, TRUE); +#endif + } else { + int k; + + for (k = 0; (k < schp->k_use_sg) && sg_scatg2virt(sclp); + ++k, ++sclp) { + SCSI_LOG_TIMEOUT(5, printk( + "sg_remove_scat: k=%d, a=0x%p, len=%d\n", + k, sg_scatg2virt(sclp), sclp->length)); + sg_page_free(sg_scatg2virt(sclp), sclp->length); + sclp->page = NULL; + sclp->offset = 0; + sclp->length = 0; } - return 0; - } - if (ok) { - if (__copy_to_user(up, p, usglen)) - return -EFAULT; - } - p += usglen; - ksglen -= usglen; - break; } - else { - if (ksglen >= num_xfer) { - if (ok) { - if (__copy_to_user(up, p, num_xfer)) + sg_page_free(schp->buffer, schp->sglist_len); + } else if (schp->buffer) + sg_page_free(schp->buffer, schp->b_malloc_len); + memset(schp, 0, sizeof (*schp)); +} + +static int +sg_read_xfer(Sg_request * srp) +{ + sg_io_hdr_t *hp = &srp->header; + Sg_scatter_hold *schp = &srp->data; + int num_xfer = 0; + int j, k, onum, usglen, ksglen, res; + int iovec_count = (int) hp->iovec_count; + int dxfer_dir = hp->dxfer_direction; + unsigned char *p; + unsigned char *up; + int new_interface = ('\0' == hp->interface_id) ? 0 : 1; + + if ((SG_DXFER_UNKNOWN == dxfer_dir) || (SG_DXFER_FROM_DEV == dxfer_dir) + || (SG_DXFER_TO_FROM_DEV == dxfer_dir)) { + num_xfer = hp->dxfer_len; + if (schp->bufflen < num_xfer) + num_xfer = schp->bufflen; + } + if ((num_xfer <= 0) || (schp->dio_in_use) || + (new_interface + && ((SG_FLAG_NO_DXFER | SG_FLAG_MMAP_IO) & hp->flags))) + return 0; + + SCSI_LOG_TIMEOUT(4, printk("sg_read_xfer: num_xfer=%d, iovec_count=%d, k_use_sg=%d\n", + num_xfer, iovec_count, schp->k_use_sg)); + if (iovec_count) { + onum = iovec_count; + if ((k = verify_area(VERIFY_READ, hp->dxferp, + SZ_SG_IOVEC * onum))) + return k; + } else + onum = 1; + + if (0 == schp->k_use_sg) { /* kernel has single buffer */ + for (j = 0, p = schp->buffer; j < onum; ++j) { + res = sg_u_iovec(hp, iovec_count, j, 0, &usglen, &up); + if (res) + return res; + usglen = (num_xfer > usglen) ? usglen : num_xfer; + if (__copy_to_user(up, p, usglen)) return -EFAULT; + p += usglen; + num_xfer -= usglen; + if (num_xfer <= 0) + return 0; + } + } else { /* kernel using scatter gather list */ + struct scatterlist *sclp = (struct scatterlist *) schp->buffer; + + ksglen = (int) sclp->length; + p = sg_scatg2virt(sclp); + for (j = 0, k = 0; j < onum; ++j) { + res = sg_u_iovec(hp, iovec_count, j, 0, &usglen, &up); + if (res) + return res; + + for (; k < schp->k_use_sg; ++k, ++sclp) { + ksglen = (int) sclp->length; + p = sg_scatg2virt(sclp); + if (NULL == p) + break; + if (usglen <= 0) + break; + if (ksglen > usglen) { + if (usglen >= num_xfer) { + if (__copy_to_user + (up, p, num_xfer)) + return -EFAULT; + return 0; + } + if (__copy_to_user(up, p, usglen)) + return -EFAULT; + p += usglen; + ksglen -= usglen; + break; + } else { + if (ksglen >= num_xfer) { + if (__copy_to_user + (up, p, num_xfer)) + return -EFAULT; + return 0; + } + if (__copy_to_user(up, p, ksglen)) + return -EFAULT; + up += ksglen; + usglen -= ksglen; + } } - return 0; - } - if (ok) { - if (__copy_to_user(up, p, ksglen)) - return -EFAULT; - } - up += ksglen; - usglen -= ksglen; - } - } - } - } - return 0; -} - -static int sg_read_oxfer(Sg_request * srp, char * outp, int num_read_xfer) -{ - Sg_scatter_hold * schp = &srp->data; - - SCSI_LOG_TIMEOUT(4, printk("sg_read_oxfer: num_read_xfer=%d\n", - num_read_xfer)); - if ((! outp) || (num_read_xfer <= 0)) - return 0; - if(schp->k_use_sg > 0) { - int k, num; - struct scatterlist * sclp = (struct scatterlist *)schp->buffer; - - for (k = 0; (k < schp->k_use_sg) && sg_scatg2virt(sclp); ++k, ++sclp) { - num = (int)sclp->length; - if (num > num_read_xfer) { - if (__copy_to_user(outp, sg_scatg2virt(sclp), num_read_xfer)) - return -EFAULT; - break; - } - else { - if (__copy_to_user(outp, sg_scatg2virt(sclp), num)) - return -EFAULT; - num_read_xfer -= num; - if (num_read_xfer <= 0) - break; - outp += num; - } - } - } - else { - if (__copy_to_user(outp, schp->buffer, num_read_xfer)) - return -EFAULT; - } - return 0; -} - -static void sg_build_reserve(Sg_fd * sfp, int req_size) -{ - Sg_scatter_hold * schp = &sfp->reserve; - - SCSI_LOG_TIMEOUT(4, printk("sg_build_reserve: req_size=%d\n", req_size)); - do { - if (req_size < PAGE_SIZE) - req_size = PAGE_SIZE; - if (0 == sg_build_indi(schp, sfp, req_size)) - return; - else - sg_remove_scat(schp); - req_size >>= 1; /* divide by 2 */ - } while (req_size > (PAGE_SIZE / 2)); -} - -static void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size) -{ - Sg_scatter_hold * req_schp = &srp->data; - Sg_scatter_hold * rsv_schp = &sfp->reserve; - - srp->res_used = 1; - SCSI_LOG_TIMEOUT(4, printk("sg_link_reserve: size=%d\n", size)); - size = (size + 1) & (~1); /* round to even for aha1542 */ - if (rsv_schp->k_use_sg > 0) { - int k, num; - int rem = size; - struct scatterlist * sclp = (struct scatterlist *)rsv_schp->buffer; - - for (k = 0; k < rsv_schp->k_use_sg; ++k, ++sclp) { - num = (int)sclp->length; - if (rem <= num) { - if (0 == k) { - req_schp->k_use_sg = 0; - req_schp->buffer = sg_scatg2virt(sclp); } - else { - sfp->save_scat_len = num; - sclp->length = (unsigned)rem; - req_schp->k_use_sg = k + 1; - req_schp->sglist_len = rsv_schp->sglist_len; - req_schp->buffer = rsv_schp->buffer; + } + return 0; +} + +static int +sg_read_oxfer(Sg_request * srp, char *outp, int num_read_xfer) +{ + Sg_scatter_hold *schp = &srp->data; + + SCSI_LOG_TIMEOUT(4, printk("sg_read_oxfer: num_read_xfer=%d\n", + num_read_xfer)); + if ((!outp) || (num_read_xfer <= 0)) + return 0; + if (schp->k_use_sg > 0) { + int k, num; + struct scatterlist *sclp = (struct scatterlist *) schp->buffer; + + for (k = 0; (k < schp->k_use_sg) && sg_scatg2virt(sclp); + ++k, ++sclp) { + num = (int) sclp->length; + if (num > num_read_xfer) { + if (__copy_to_user + (outp, sg_scatg2virt(sclp), num_read_xfer)) + return -EFAULT; + break; + } else { + if (__copy_to_user + (outp, sg_scatg2virt(sclp), num)) + return -EFAULT; + num_read_xfer -= num; + if (num_read_xfer <= 0) + break; + outp += num; + } } + } else { + if (__copy_to_user(outp, schp->buffer, num_read_xfer)) + return -EFAULT; + } + return 0; +} + +static void +sg_build_reserve(Sg_fd * sfp, int req_size) +{ + Sg_scatter_hold *schp = &sfp->reserve; + + SCSI_LOG_TIMEOUT(4, printk("sg_build_reserve: req_size=%d\n", req_size)); + do { + if (req_size < PAGE_SIZE) + req_size = PAGE_SIZE; + if (0 == sg_build_indirect(schp, sfp, req_size)) + return; + else + sg_remove_scat(schp); + req_size >>= 1; /* divide by 2 */ + } while (req_size > (PAGE_SIZE / 2)); +} + +static void +sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size) +{ + Sg_scatter_hold *req_schp = &srp->data; + Sg_scatter_hold *rsv_schp = &sfp->reserve; + + srp->res_used = 1; + SCSI_LOG_TIMEOUT(4, printk("sg_link_reserve: size=%d\n", size)); + size = (size + 1) & (~1); /* round to even for aha1542 */ + if (rsv_schp->k_use_sg > 0) { + int k, num; + int rem = size; + struct scatterlist *sclp = + (struct scatterlist *) rsv_schp->buffer; + + for (k = 0; k < rsv_schp->k_use_sg; ++k, ++sclp) { + num = (int) sclp->length; + if (rem <= num) { + if (0 == k) { + req_schp->k_use_sg = 0; + req_schp->buffer = sg_scatg2virt(sclp); + } else { + sfp->save_scat_len = num; + sclp->length = (unsigned) rem; + req_schp->k_use_sg = k + 1; + req_schp->sglist_len = + rsv_schp->sglist_len; + req_schp->buffer = rsv_schp->buffer; + } + req_schp->bufflen = size; + req_schp->b_malloc_len = rsv_schp->b_malloc_len; + break; + } else + rem -= num; + } + if (k >= rsv_schp->k_use_sg) + SCSI_LOG_TIMEOUT(1, printk("sg_link_reserve: BAD size\n")); + } else { + req_schp->k_use_sg = 0; req_schp->bufflen = size; - req_schp->buffer_mem_src = rsv_schp->buffer_mem_src; + req_schp->buffer = rsv_schp->buffer; req_schp->b_malloc_len = rsv_schp->b_malloc_len; - break; - } - else - rem -= num; - } - if (k >= rsv_schp->k_use_sg) - SCSI_LOG_TIMEOUT(1, printk("sg_link_reserve: BAD size\n")); - } - else { + } +} + +static void +sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp) +{ + Sg_scatter_hold *req_schp = &srp->data; + Sg_scatter_hold *rsv_schp = &sfp->reserve; + + SCSI_LOG_TIMEOUT(4, printk("sg_unlink_reserve: req->k_use_sg=%d\n", + (int) req_schp->k_use_sg)); + if ((rsv_schp->k_use_sg > 0) && (req_schp->k_use_sg > 0)) { + struct scatterlist *sclp = + (struct scatterlist *) rsv_schp->buffer; + + if (sfp->save_scat_len > 0) + (sclp + (req_schp->k_use_sg - 1))->length = + (unsigned) sfp->save_scat_len; + else + SCSI_LOG_TIMEOUT(1, printk ("sg_unlink_reserve: BAD save_scat_len\n")); + } req_schp->k_use_sg = 0; - req_schp->bufflen = size; - req_schp->buffer = rsv_schp->buffer; - req_schp->buffer_mem_src = rsv_schp->buffer_mem_src; - req_schp->b_malloc_len = rsv_schp->b_malloc_len; - } -} - -static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp) -{ - Sg_scatter_hold * req_schp = &srp->data; - Sg_scatter_hold * rsv_schp = &sfp->reserve; - - SCSI_LOG_TIMEOUT(4, printk("sg_unlink_reserve: req->k_use_sg=%d\n", - (int)req_schp->k_use_sg)); - if ((rsv_schp->k_use_sg > 0) && (req_schp->k_use_sg > 0)) { - struct scatterlist * sclp = (struct scatterlist *)rsv_schp->buffer; - - if (sfp->save_scat_len > 0) - (sclp + (req_schp->k_use_sg - 1))->length = - (unsigned)sfp->save_scat_len; - else - SCSI_LOG_TIMEOUT(1, printk( - "sg_unlink_reserve: BAD save_scat_len\n")); - } - req_schp->k_use_sg = 0; - req_schp->bufflen = 0; - req_schp->buffer = NULL; - req_schp->sglist_len = 0; - sfp->save_scat_len = 0; - srp->res_used = 0; -} - -static Sg_request * sg_get_rq_mark(Sg_fd * sfp, int pack_id) -{ - Sg_request * resp; - unsigned long iflags; - - write_lock_irqsave(&sfp->rq_list_lock, iflags); - for (resp = sfp->headrp; resp; resp = resp->nextrp) { - /* look for requests that are ready + not SG_IO owned */ - if ((1 == resp->done) && (! resp->sg_io_owned) && - ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { - resp->done = 2; /* guard against other readers */ - break; - } - } - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return resp; + req_schp->bufflen = 0; + req_schp->buffer = NULL; + req_schp->sglist_len = 0; + sfp->save_scat_len = 0; + srp->res_used = 0; +} + +static Sg_request * +sg_get_rq_mark(Sg_fd * sfp, int pack_id) +{ + Sg_request *resp; + unsigned long iflags; + + write_lock_irqsave(&sfp->rq_list_lock, iflags); + for (resp = sfp->headrp; resp; resp = resp->nextrp) { + /* look for requests that are ready + not SG_IO owned */ + if ((1 == resp->done) && (!resp->sg_io_owned) && + ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { + resp->done = 2; /* guard against other readers */ + break; + } + } + write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return resp; } #ifdef CONFIG_PROC_FS -static Sg_request * sg_get_nth_request(Sg_fd * sfp, int nth) +static Sg_request * +sg_get_nth_request(Sg_fd * sfp, int nth) { - Sg_request * resp; - unsigned long iflags; - int k; - - read_lock_irqsave(&sfp->rq_list_lock, iflags); - for (k = 0, resp = sfp->headrp; resp && (k < nth); - ++k, resp = resp->nextrp) - ; - read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return resp; + Sg_request *resp; + unsigned long iflags; + int k; + + read_lock_irqsave(&sfp->rq_list_lock, iflags); + for (k = 0, resp = sfp->headrp; resp && (k < nth); + ++k, resp = resp->nextrp) ; + read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return resp; } #endif /* always adds to end of list */ -static Sg_request * sg_add_request(Sg_fd * sfp) +static Sg_request * +sg_add_request(Sg_fd * sfp) { - int k; - unsigned long iflags; - Sg_request * resp; - Sg_request * rp = sfp->req_arr; - - write_lock_irqsave(&sfp->rq_list_lock, iflags); - resp = sfp->headrp; - if (! resp) { - memset(rp, 0, sizeof(Sg_request)); - rp->parentfp = sfp; - resp = rp; - sfp->headrp = resp; - } - else { - if (0 == sfp->cmd_q) - resp = NULL; /* command queuing disallowed */ - else { - for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) { - if (! rp->parentfp) - break; - } - if (k < SG_MAX_QUEUE) { - memset(rp, 0, sizeof(Sg_request)); + int k; + unsigned long iflags; + Sg_request *resp; + Sg_request *rp = sfp->req_arr; + + write_lock_irqsave(&sfp->rq_list_lock, iflags); + resp = sfp->headrp; + if (!resp) { + memset(rp, 0, sizeof (Sg_request)); rp->parentfp = sfp; - while (resp->nextrp) - resp = resp->nextrp; - resp->nextrp = rp; resp = rp; - } - else - resp = NULL; - } - } - if (resp) { - resp->nextrp = NULL; - resp->header.duration = jiffies; - resp->my_cmdp = NULL; -#ifdef SG_ALLOW_DIO_CODE - resp->data.kiobp = NULL; -#endif - } - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return resp; + sfp->headrp = resp; + } else { + if (0 == sfp->cmd_q) + resp = NULL; /* command queuing disallowed */ + else { + for (k = 0; k < SG_MAX_QUEUE; ++k, ++rp) { + if (!rp->parentfp) + break; + } + if (k < SG_MAX_QUEUE) { + memset(rp, 0, sizeof (Sg_request)); + rp->parentfp = sfp; + while (resp->nextrp) + resp = resp->nextrp; + resp->nextrp = rp; + resp = rp; + } else + resp = NULL; + } + } + if (resp) { + resp->nextrp = NULL; + resp->header.duration = jiffies; + resp->my_cmdp = NULL; + } + write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return resp; } /* Return of 1 for found; 0 for not found */ -static int sg_remove_request(Sg_fd * sfp, Sg_request * srp) +static int +sg_remove_request(Sg_fd * sfp, Sg_request * srp) { - Sg_request * prev_rp; - Sg_request * rp; - unsigned long iflags; - int res = 0; - - if ((! sfp) || (! srp) || (! sfp->headrp)) - return res; - write_lock_irqsave(&sfp->rq_list_lock, iflags); - prev_rp = sfp->headrp; - if (srp == prev_rp) { - sfp->headrp = prev_rp->nextrp; - prev_rp->parentfp = NULL; - res = 1; - } - else { - while ((rp = prev_rp->nextrp)) { - if (srp == rp) { - prev_rp->nextrp = rp->nextrp; - rp->parentfp = NULL; + Sg_request *prev_rp; + Sg_request *rp; + unsigned long iflags; + int res = 0; + + if ((!sfp) || (!srp) || (!sfp->headrp)) + return res; + write_lock_irqsave(&sfp->rq_list_lock, iflags); + prev_rp = sfp->headrp; + if (srp == prev_rp) { + sfp->headrp = prev_rp->nextrp; + prev_rp->parentfp = NULL; res = 1; - break; - } - prev_rp = rp; + } else { + while ((rp = prev_rp->nextrp)) { + if (srp == rp) { + prev_rp->nextrp = rp->nextrp; + rp->parentfp = NULL; + res = 1; + break; + } + prev_rp = rp; + } } - } - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return res; + write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return res; } #ifdef CONFIG_PROC_FS -static Sg_fd * sg_get_nth_sfp(Sg_device * sdp, int nth) +static Sg_fd * +sg_get_nth_sfp(Sg_device * sdp, int nth) { - Sg_fd * resp; - unsigned long iflags; - int k; - - read_lock_irqsave(&sg_dev_arr_lock, iflags); - for (k = 0, resp = sdp->headfp; resp && (k < nth); - ++k, resp = resp->nextfp) - ; - read_unlock_irqrestore(&sg_dev_arr_lock, iflags); - return resp; + Sg_fd *resp; + unsigned long iflags; + int k; + + read_lock_irqsave(&sg_dev_arr_lock, iflags); + for (k = 0, resp = sdp->headfp; resp && (k < nth); + ++k, resp = resp->nextfp) ; + read_unlock_irqrestore(&sg_dev_arr_lock, iflags); + return resp; } #endif -static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev) +static Sg_fd * +sg_add_sfp(Sg_device * sdp, int dev) { - Sg_fd * sfp; - unsigned long iflags; + Sg_fd *sfp; + unsigned long iflags; - sfp = (Sg_fd *)sg_low_malloc(sizeof(Sg_fd), 0, SG_HEAP_KMAL, 0); - if (! sfp) - return NULL; - memset(sfp, 0, sizeof(Sg_fd)); - sfp->fd_mem_src = SG_HEAP_KMAL; - init_waitqueue_head(&sfp->read_wait); - sfp->rq_list_lock = RW_LOCK_UNLOCKED; - - sfp->timeout = SG_DEFAULT_TIMEOUT; - sfp->force_packid = SG_DEF_FORCE_PACK_ID; - sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ? - sdp->device->host->unchecked_isa_dma : 1; - sfp->cmd_q = SG_DEF_COMMAND_Q; - sfp->keep_orphan = SG_DEF_KEEP_ORPHAN; - sfp->parentdp = sdp; - write_lock_irqsave(&sg_dev_arr_lock, iflags); - if (! sdp->headfp) - sdp->headfp = sfp; - else { /* add to tail of existing list */ - Sg_fd * pfp = sdp->headfp; - while (pfp->nextfp) - pfp = pfp->nextfp; - pfp->nextfp = sfp; - } - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p, m_s=%d\n", - sfp, (int)sfp->fd_mem_src)); - sg_build_reserve(sfp, sg_big_buff); - SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: bufflen=%d, k_use_sg=%d\n", + sfp = (Sg_fd *) sg_page_malloc(sizeof (Sg_fd), 0, 0); + if (!sfp) + return NULL; + memset(sfp, 0, sizeof (Sg_fd)); + init_waitqueue_head(&sfp->read_wait); + sfp->rq_list_lock = RW_LOCK_UNLOCKED; + + sfp->timeout = SG_DEFAULT_TIMEOUT; + sfp->force_packid = SG_DEF_FORCE_PACK_ID; + sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ? + sdp->device->host->unchecked_isa_dma : 1; + sfp->cmd_q = SG_DEF_COMMAND_Q; + sfp->keep_orphan = SG_DEF_KEEP_ORPHAN; + sfp->parentdp = sdp; + write_lock_irqsave(&sg_dev_arr_lock, iflags); + if (!sdp->headfp) + sdp->headfp = sfp; + else { /* add to tail of existing list */ + Sg_fd *pfp = sdp->headfp; + while (pfp->nextfp) + pfp = pfp->nextfp; + pfp->nextfp = sfp; + } + write_unlock_irqrestore(&sg_dev_arr_lock, iflags); + SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p\n", sfp)); + sg_build_reserve(sfp, sg_big_buff); + SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: bufflen=%d, k_use_sg=%d\n", sfp->reserve.bufflen, sfp->reserve.k_use_sg)); - return sfp; + return sfp; } -static void __sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp) +static void +__sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp) { - Sg_fd * fp; - Sg_fd * prev_fp; + Sg_fd *fp; + Sg_fd *prev_fp; - prev_fp = sdp->headfp; - if (sfp == prev_fp) - sdp->headfp = prev_fp->nextfp; - else { - while ((fp = prev_fp->nextfp)) { - if (sfp == fp) { - prev_fp->nextfp = fp->nextfp; - break; - } - prev_fp = fp; + prev_fp = sdp->headfp; + if (sfp == prev_fp) + sdp->headfp = prev_fp->nextfp; + else { + while ((fp = prev_fp->nextfp)) { + if (sfp == fp) { + prev_fp->nextfp = fp->nextfp; + break; + } + prev_fp = fp; + } } - } - if (sfp->reserve.bufflen > 0) { - SCSI_LOG_TIMEOUT(6, printk("__sg_remove_sfp: bufflen=%d, k_use_sg=%d\n", - (int)sfp->reserve.bufflen, (int)sfp->reserve.k_use_sg)); - if (sfp->mmap_called) - sg_rb_correct4mmap(&sfp->reserve, 0); /* undo correction */ - sg_remove_scat(&sfp->reserve); - } - sfp->parentdp = NULL; - SCSI_LOG_TIMEOUT(6, printk("__sg_remove_sfp: sfp=0x%p\n", sfp)); - sg_low_free((char *)sfp, sizeof(Sg_fd), sfp->fd_mem_src); + if (sfp->reserve.bufflen > 0) { + SCSI_LOG_TIMEOUT(6, + printk("__sg_remove_sfp: bufflen=%d, k_use_sg=%d\n", + (int) sfp->reserve.bufflen, (int) sfp->reserve.k_use_sg)); + if (sfp->mmap_called) + sg_rb_correct4mmap(&sfp->reserve, 0); /* undo correction */ + sg_remove_scat(&sfp->reserve); + } + sfp->parentdp = NULL; + SCSI_LOG_TIMEOUT(6, printk("__sg_remove_sfp: sfp=0x%p\n", sfp)); + sg_page_free((char *) sfp, sizeof (Sg_fd)); } /* Returns 0 in normal case, 1 when detached and sdp object removed */ -static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp) +static int +sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp) { - Sg_request * srp; - Sg_request * tsrp; - int dirty = 0; - int res = 0; - - for (srp = sfp->headrp; srp; srp = tsrp) { - tsrp = srp->nextrp; - if (srp->done) - sg_finish_rem_req(srp); - else - ++dirty; - } - if (0 == dirty) { - unsigned long iflags; + Sg_request *srp; + Sg_request *tsrp; + int dirty = 0; + int res = 0; + + for (srp = sfp->headrp; srp; srp = tsrp) { + tsrp = srp->nextrp; + if (srp->done) + sg_finish_rem_req(srp); + else + ++dirty; + } + if (0 == dirty) { + unsigned long iflags; - write_lock_irqsave(&sg_dev_arr_lock, iflags); - __sg_remove_sfp(sdp, sfp); - if (sdp->detached && (NULL == sdp->headfp)) { - int k, maxd; - - maxd = sg_template.dev_max; - for (k = 0; k < maxd; ++k) { - if (sdp == sg_dev_arr[k]) - break; - } - if (k < maxd) - sg_dev_arr[k] = NULL; - kfree((char *)sdp); - res = 1; + write_lock_irqsave(&sg_dev_arr_lock, iflags); + __sg_remove_sfp(sdp, sfp); + if (sdp->detached && (NULL == sdp->headfp)) { + int k, maxd; + + maxd = sg_template.dev_max; + for (k = 0; k < maxd; ++k) { + if (sdp == sg_dev_arr[k]) + break; + } + if (k < maxd) + sg_dev_arr[k] = NULL; + vfree((char *) sdp); + res = 1; + } + write_unlock_irqrestore(&sg_dev_arr_lock, iflags); + } else { + sfp->closed = 1; /* flag dirty state on this fd */ + sdp->device->access_count++; + /* MOD_INC's to inhibit unloading sg and associated adapter driver */ + if (sg_template.module) + __MOD_INC_USE_COUNT(sg_template.module); + if (sdp->device->host->hostt->module) + __MOD_INC_USE_COUNT(sdp->device->host->hostt->module); + SCSI_LOG_TIMEOUT(1, printk("sg_remove_sfp: worrisome, %d writes pending\n", + dirty)); } - write_unlock_irqrestore(&sg_dev_arr_lock, iflags); - } - else { - sfp->closed = 1; /* flag dirty state on this fd */ - sdp->device->access_count++; - /* MOD_INC's to inhibit unloading sg and associated adapter driver */ - if (sg_template.module) - __MOD_INC_USE_COUNT(sg_template.module); - if (sdp->device->host->hostt->module) - __MOD_INC_USE_COUNT(sdp->device->host->hostt->module); - SCSI_LOG_TIMEOUT(1, printk( - "sg_remove_sfp: worrisome, %d writes pending\n", dirty)); - } - return res; -} - -static int sg_res_in_use(Sg_fd * sfp) -{ - const Sg_request * srp; - unsigned long iflags; - - read_lock_irqsave(&sfp->rq_list_lock, iflags); - for (srp = sfp->headrp; srp; srp = srp->nextrp) - if (srp->res_used) break; - read_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return srp ? 1 : 0; + return res; } -/* If retSzp==NULL want exact size or fail */ -static char * sg_low_malloc(int rqSz, int lowDma, int mem_src, int * retSzp) +static int +sg_res_in_use(Sg_fd * sfp) { - char * resp = NULL; - int page_mask = lowDma ? (GFP_ATOMIC | GFP_DMA) : GFP_ATOMIC; + const Sg_request *srp; + unsigned long iflags; - if (rqSz <= 0) - return resp; - if (SG_HEAP_KMAL == mem_src) { - resp = kmalloc(rqSz, page_mask); - if (resp) { - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - memset(resp, 0, rqSz); - if (retSzp) *retSzp = rqSz; - } - return resp; - } - else if (SG_HEAP_PAGE == mem_src) { - int order, a_size; - int resSz = rqSz; - - for (order = 0, a_size = PAGE_SIZE; - a_size < rqSz; order++, a_size <<= 1) - ; - resp = (char *)__get_free_pages(page_mask, order); - while ((! resp) && order && retSzp) { - --order; - a_size >>= 1; /* divide by 2, until PAGE_SIZE */ - resp = (char *)__get_free_pages(page_mask, order); /* try half */ - resSz = a_size; - } - if (resp) { - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - memset(resp, 0, resSz); - if (retSzp) *retSzp = resSz; - } - } - else - printk(KERN_ERR "sg_low_malloc: bad mem_src=%d, rqSz=%df\n", - mem_src, rqSz); - return resp; -} - -static char * sg_malloc(const Sg_fd * sfp, int size, int * retSzp, - int * mem_srcp) -{ - char * resp = NULL; - - if (retSzp) *retSzp = size; - if (size <= 0) - ; - else { - int low_dma = sfp->low_dma; - int l_ms = -1; /* invalid value */ - - switch (*mem_srcp) - { - case SG_HEAP_PAGE: - l_ms = (size < PAGE_SIZE) ? SG_HEAP_KMAL : SG_HEAP_PAGE; - resp = sg_low_malloc(size, low_dma, l_ms, 0); - if (resp) - break; - resp = sg_low_malloc(size, low_dma, l_ms, &size); - if (! resp) { - l_ms = (SG_HEAP_KMAL == l_ms) ? SG_HEAP_PAGE : SG_HEAP_KMAL; - resp = sg_low_malloc(size, low_dma, l_ms, &size); - } - if (resp && retSzp) *retSzp = size; - break; - case SG_HEAP_KMAL: - l_ms = SG_HEAP_KMAL; /* was SG_HEAP_PAGE */ - resp = sg_low_malloc(size, low_dma, l_ms, 0); - break; - default: - SCSI_LOG_TIMEOUT(1, printk("sg_malloc: bad ms=%d\n", *mem_srcp)); - break; - } - if (resp) *mem_srcp = l_ms; - } - SCSI_LOG_TIMEOUT(6, printk("sg_malloc: size=%d, ms=%d, ret=0x%p\n", - size, *mem_srcp, resp)); - return resp; + read_lock_irqsave(&sfp->rq_list_lock, iflags); + for (srp = sfp->headrp; srp; srp = srp->nextrp) + if (srp->res_used) + break; + read_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return srp ? 1 : 0; } -#ifdef SG_ALLOW_DIO_CODE -static inline int sg_alloc_kiovec(int nr, struct kiobuf **bufp, int *szp) +/* If retSzp==NULL want exact size or fail */ +static char * +sg_page_malloc(int rqSz, int lowDma, int *retSzp) { -#if SG_NEW_KIOVEC - return alloc_kiovec_sz(nr, bufp, szp); -#else - return alloc_kiovec(nr, bufp); -#endif + char *resp = NULL; + int page_mask = lowDma ? (GFP_ATOMIC | GFP_DMA) : GFP_ATOMIC; + int order, a_size; + int resSz = rqSz; + + if (rqSz <= 0) + return resp; + + for (order = 0, a_size = PAGE_SIZE; a_size < rqSz; + order++, a_size <<= 1) ; + resp = (char *) __get_free_pages(page_mask, order); + while ((!resp) && order && retSzp) { + --order; + a_size >>= 1; /* divide by 2, until PAGE_SIZE */ + resp = (char *) __get_free_pages(page_mask, order); /* try half */ + resSz = a_size; + } + if (resp) { + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + memset(resp, 0, resSz); + if (retSzp) + *retSzp = resSz; + } + return resp; } -#endif -static void sg_low_free(char * buff, int size, int mem_src) +static void +sg_page_free(char *buff, int size) { - if (! buff) return; - switch (mem_src) { - case SG_HEAP_KMAL: - kfree(buff); /* size not used */ - break; - case SG_HEAP_PAGE: - { - int order, a_size; - for (order = 0, a_size = PAGE_SIZE; - a_size < size; order++, a_size <<= 1) - ; - free_pages((unsigned long)buff, order); - } - break; - case SG_USER_MEM: - break; /* nothing to do */ - default: - printk(KERN_ERR "sg_low_free: bad mem_src=%d, buff=0x%p, rqSz=%d\n", - mem_src, buff, size); - break; - } -} - -static void sg_free(char * buff, int size, int mem_src) -{ - SCSI_LOG_TIMEOUT(6, - printk("sg_free: buff=0x%p, size=%d\n", buff, size)); - if ((! buff) || (size <= 0)) - ; - else - sg_low_free(buff, size, mem_src); -} + int order, a_size; -#ifdef SG_ALLOW_DIO_CODE -static inline void sg_free_kiovec(int nr, struct kiobuf **bufp, int *szp) -{ -#if SG_NEW_KIOVEC - free_kiovec_sz(nr, bufp, szp); -#else - free_kiovec(nr, bufp); -#endif + if (!buff) + return; + for (order = 0, a_size = PAGE_SIZE; a_size < size; + order++, a_size <<= 1) ; + free_pages((unsigned long) buff, order); } -#endif -static int sg_ms_to_jif(unsigned int msecs) +static int +sg_ms_to_jif(unsigned int msecs) { - if ((UINT_MAX / 2U) < msecs) - return INT_MAX; /* special case, set largest possible */ - else - return ((int)msecs < (INT_MAX / 1000)) ? (((int)msecs * HZ) / 1000) - : (((int)msecs / 1000) * HZ); + if ((UINT_MAX / 2U) < msecs) + return INT_MAX; /* special case, set largest possible */ + else + return ((int) msecs < + (INT_MAX / 1000)) ? (((int) msecs * HZ) / 1000) + : (((int) msecs / 1000) * HZ); } -static inline unsigned sg_jif_to_ms(int jifs) +static inline unsigned +sg_jif_to_ms(int jifs) { - if (jifs <= 0) - return 0U; - else { - unsigned int j = (unsigned int)jifs; - return (j < (UINT_MAX / 1000)) ? ((j * 1000) / HZ) : ((j / HZ) * 1000); - } + if (jifs <= 0) + return 0U; + else { + unsigned int j = (unsigned int) jifs; + return (j < + (UINT_MAX / 1000)) ? ((j * 1000) / HZ) : ((j / HZ) * + 1000); + } } -static unsigned char allow_ops[] = {TEST_UNIT_READY, REQUEST_SENSE, -INQUIRY, READ_CAPACITY, READ_BUFFER, READ_6, READ_10, READ_12, -MODE_SENSE, MODE_SENSE_10, LOG_SENSE}; +static unsigned char allow_ops[] = { TEST_UNIT_READY, REQUEST_SENSE, + INQUIRY, READ_CAPACITY, READ_BUFFER, READ_6, READ_10, READ_12, + MODE_SENSE, MODE_SENSE_10, LOG_SENSE +}; -static int sg_allow_access(unsigned char opcode, char dev_type) +static int +sg_allow_access(unsigned char opcode, char dev_type) { - int k; + int k; - if (TYPE_SCANNER == dev_type) /* TYPE_ROM maybe burner */ - return 1; - for (k = 0; k < sizeof(allow_ops); ++k) { - if (opcode == allow_ops[k]) - return 1; - } - return 0; + if (TYPE_SCANNER == dev_type) /* TYPE_ROM maybe burner */ + return 1; + for (k = 0; k < sizeof (allow_ops); ++k) { + if (opcode == allow_ops[k]) + return 1; + } + return 0; } - #ifdef CONFIG_PROC_FS -static int sg_last_dev() +static int +sg_last_dev() { - int k; - unsigned long iflags; + int k; + unsigned long iflags; - read_lock_irqsave(&sg_dev_arr_lock, iflags); - for (k = sg_template.dev_max - 1; k >= 0; --k) - if (sg_dev_arr[k] && sg_dev_arr[k]->device) break; - read_unlock_irqrestore(&sg_dev_arr_lock, iflags); - return k + 1; /* origin 1 */ + read_lock_irqsave(&sg_dev_arr_lock, iflags); + for (k = sg_template.dev_max - 1; k >= 0; --k) + if (sg_dev_arr[k] && sg_dev_arr[k]->device) + break; + read_unlock_irqrestore(&sg_dev_arr_lock, iflags); + return k + 1; /* origin 1 */ } #endif -static Sg_device * sg_get_dev(int dev) +static Sg_device * +sg_get_dev(int dev) { - Sg_device * sdp = NULL; - unsigned long iflags; + Sg_device *sdp = NULL; + unsigned long iflags; - if (sg_dev_arr && (dev >= 0)) - { - read_lock_irqsave(&sg_dev_arr_lock, iflags); - if (dev < sg_template.dev_max) - sdp = sg_dev_arr[dev]; - read_unlock_irqrestore(&sg_dev_arr_lock, iflags); - } - return sdp; + if (sg_dev_arr && (dev >= 0)) { + read_lock_irqsave(&sg_dev_arr_lock, iflags); + if (dev < sg_template.dev_max) + sdp = sg_dev_arr[dev]; + read_unlock_irqrestore(&sg_dev_arr_lock, iflags); + } + return sdp; } #ifdef CONFIG_PROC_FS -static struct proc_dir_entry * sg_proc_sgp = NULL; +static struct proc_dir_entry *sg_proc_sgp = NULL; static char sg_proc_sg_dirname[] = "sg"; -static const char * sg_proc_leaf_names[] = {"allow_dio", "def_reserved_size", - "debug", "devices", "device_hdr", "device_strs", - "hosts", "host_hdr", "host_strs", "version"}; - -static int sg_proc_adio_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data); -static int sg_proc_adio_info(char * buffer, int * len, off_t * begin, +static const char *sg_proc_leaf_names[] = { "allow_dio", "def_reserved_size", + "debug", "devices", "device_hdr", "device_strs", + "hosts", "host_hdr", "host_strs", "version" +}; + +static int sg_proc_adio_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data); +static int sg_proc_adio_info(char *buffer, int *len, off_t * begin, off_t offset, int size); -static int sg_proc_adio_write(struct file * filp, const char * buffer, - unsigned long count, void * data); -static int sg_proc_dressz_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data); -static int sg_proc_dressz_info(char * buffer, int * len, off_t * begin, +static int sg_proc_adio_write(struct file *filp, const char *buffer, + unsigned long count, void *data); +static int sg_proc_dressz_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data); +static int sg_proc_dressz_info(char *buffer, int *len, off_t * begin, off_t offset, int size); -static int sg_proc_dressz_write(struct file * filp, const char * buffer, - unsigned long count, void * data); -static int sg_proc_debug_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data); -static int sg_proc_debug_info(char * buffer, int * len, off_t * begin, +static int sg_proc_dressz_write(struct file *filp, const char *buffer, + unsigned long count, void *data); +static int sg_proc_debug_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data); +static int sg_proc_debug_info(char *buffer, int *len, off_t * begin, off_t offset, int size); -static int sg_proc_dev_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data); -static int sg_proc_dev_info(char * buffer, int * len, off_t * begin, +static int sg_proc_dev_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data); +static int sg_proc_dev_info(char *buffer, int *len, off_t * begin, off_t offset, int size); -static int sg_proc_devhdr_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data); -static int sg_proc_devhdr_info(char * buffer, int * len, off_t * begin, +static int sg_proc_devhdr_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data); +static int sg_proc_devhdr_info(char *buffer, int *len, off_t * begin, off_t offset, int size); -static int sg_proc_devstrs_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data); -static int sg_proc_devstrs_info(char * buffer, int * len, off_t * begin, +static int sg_proc_devstrs_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data); +static int sg_proc_devstrs_info(char *buffer, int *len, off_t * begin, off_t offset, int size); -static int sg_proc_host_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data); -static int sg_proc_host_info(char * buffer, int * len, off_t * begin, +static int sg_proc_host_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data); +static int sg_proc_host_info(char *buffer, int *len, off_t * begin, off_t offset, int size); -static int sg_proc_hosthdr_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data); -static int sg_proc_hosthdr_info(char * buffer, int * len, off_t * begin, +static int sg_proc_hosthdr_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data); +static int sg_proc_hosthdr_info(char *buffer, int *len, off_t * begin, off_t offset, int size); -static int sg_proc_hoststrs_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data); -static int sg_proc_hoststrs_info(char * buffer, int * len, off_t * begin, +static int sg_proc_hoststrs_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data); +static int sg_proc_hoststrs_info(char *buffer, int *len, off_t * begin, off_t offset, int size); -static int sg_proc_version_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data); -static int sg_proc_version_info(char * buffer, int * len, off_t * begin, +static int sg_proc_version_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data); +static int sg_proc_version_info(char *buffer, int *len, off_t * begin, off_t offset, int size); -static read_proc_t * sg_proc_leaf_reads[] = { - sg_proc_adio_read, sg_proc_dressz_read, sg_proc_debug_read, - sg_proc_dev_read, sg_proc_devhdr_read, sg_proc_devstrs_read, - sg_proc_host_read, sg_proc_hosthdr_read, sg_proc_hoststrs_read, - sg_proc_version_read}; -static write_proc_t * sg_proc_leaf_writes[] = { - sg_proc_adio_write, sg_proc_dressz_write, 0, 0, 0, 0, 0, 0, 0, 0}; +static read_proc_t *sg_proc_leaf_reads[] = { + sg_proc_adio_read, sg_proc_dressz_read, sg_proc_debug_read, + sg_proc_dev_read, sg_proc_devhdr_read, sg_proc_devstrs_read, + sg_proc_host_read, sg_proc_hosthdr_read, sg_proc_hoststrs_read, + sg_proc_version_read +}; +static write_proc_t *sg_proc_leaf_writes[] = { + sg_proc_adio_write, sg_proc_dressz_write, 0, 0, 0, 0, 0, 0, 0, 0 +}; #define PRINT_PROC(fmt,args...) \ do { \ @@ -2816,317 +2750,391 @@ size : begin + len - offset; \ } while(0) - -static int sg_proc_init() +static int +sg_proc_init() { - int k, mask; - int leaves = sizeof(sg_proc_leaf_names) / sizeof(sg_proc_leaf_names[0]); - struct proc_dir_entry * pdep; - - if (! proc_scsi) - return 1; - sg_proc_sgp = create_proc_entry(sg_proc_sg_dirname, - S_IFDIR | S_IRUGO | S_IXUGO, proc_scsi); - if (! sg_proc_sgp) - return 1; - for (k = 0; k < leaves; ++k) { - mask = sg_proc_leaf_writes[k] ? S_IRUGO | S_IWUSR : S_IRUGO; - pdep = create_proc_entry(sg_proc_leaf_names[k], mask, sg_proc_sgp); - if (pdep) { - pdep->read_proc = sg_proc_leaf_reads[k]; - if (sg_proc_leaf_writes[k]) - pdep->write_proc = sg_proc_leaf_writes[k]; + int k, mask; + int leaves = + sizeof (sg_proc_leaf_names) / sizeof (sg_proc_leaf_names[0]); + struct proc_dir_entry *pdep; + + if (!proc_scsi) + return 1; + sg_proc_sgp = create_proc_entry(sg_proc_sg_dirname, + S_IFDIR | S_IRUGO | S_IXUGO, proc_scsi); + if (!sg_proc_sgp) + return 1; + for (k = 0; k < leaves; ++k) { + mask = sg_proc_leaf_writes[k] ? S_IRUGO | S_IWUSR : S_IRUGO; + pdep = + create_proc_entry(sg_proc_leaf_names[k], mask, sg_proc_sgp); + if (pdep) { + pdep->read_proc = sg_proc_leaf_reads[k]; + if (sg_proc_leaf_writes[k]) + pdep->write_proc = sg_proc_leaf_writes[k]; + } } - } - return 0; + return 0; } -static void sg_proc_cleanup() +static void +sg_proc_cleanup() { - int k; - int leaves = sizeof(sg_proc_leaf_names) / sizeof(sg_proc_leaf_names[0]); + int k; + int leaves = + sizeof (sg_proc_leaf_names) / sizeof (sg_proc_leaf_names[0]); - if ((! proc_scsi) || (! sg_proc_sgp)) - return; - for (k = 0; k < leaves; ++k) - remove_proc_entry(sg_proc_leaf_names[k], sg_proc_sgp); - remove_proc_entry(sg_proc_sg_dirname, proc_scsi); + if ((!proc_scsi) || (!sg_proc_sgp)) + return; + for (k = 0; k < leaves; ++k) + remove_proc_entry(sg_proc_leaf_names[k], sg_proc_sgp); + remove_proc_entry(sg_proc_sg_dirname, proc_scsi); } -static int sg_proc_adio_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data) -{ SG_PROC_READ_FN(sg_proc_adio_info); } +static int +sg_proc_adio_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data) +{ + SG_PROC_READ_FN(sg_proc_adio_info); +} -static int sg_proc_adio_info(char * buffer, int * len, off_t * begin, - off_t offset, int size) +static int +sg_proc_adio_info(char *buffer, int *len, off_t * begin, off_t offset, int size) { - PRINT_PROC("%d\n", sg_allow_dio); - return 1; + PRINT_PROC("%d\n", sg_allow_dio); + return 1; } -static int sg_proc_adio_write(struct file * filp, const char * buffer, - unsigned long count, void * data) +static int +sg_proc_adio_write(struct file *filp, const char *buffer, + unsigned long count, void *data) { - int num; - char buff[11]; + int num; + char buff[11]; - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - return -EACCES; - num = (count < 10) ? count : 10; - if (copy_from_user(buff, buffer, num)) - return -EFAULT; - buff[num] = '\0'; - sg_allow_dio = simple_strtoul(buff, 0, 10) ? 1 : 0; - return count; + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + num = (count < 10) ? count : 10; + if (copy_from_user(buff, buffer, num)) + return -EFAULT; + buff[num] = '\0'; + sg_allow_dio = simple_strtoul(buff, 0, 10) ? 1 : 0; + return count; } -static int sg_proc_dressz_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data) -{ SG_PROC_READ_FN(sg_proc_dressz_info); } - -static int sg_proc_dressz_info(char * buffer, int * len, off_t * begin, - off_t offset, int size) +static int +sg_proc_dressz_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data) { - PRINT_PROC("%d\n", sg_big_buff); - return 1; + SG_PROC_READ_FN(sg_proc_dressz_info); } -static int sg_proc_dressz_write(struct file * filp, const char * buffer, - unsigned long count, void * data) +static int +sg_proc_dressz_info(char *buffer, int *len, off_t * begin, + off_t offset, int size) { - int num; - unsigned long k = ULONG_MAX; - char buff[11]; - - if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) - return -EACCES; - num = (count < 10) ? count : 10; - if (copy_from_user(buff, buffer, num)) - return -EFAULT; - buff[num] = '\0'; - k = simple_strtoul(buff, 0, 10); - if (k <= 1048576) { /* limit "big buff" to 1 MB */ - sg_big_buff = k; - return count; - } - return -ERANGE; + PRINT_PROC("%d\n", sg_big_buff); + return 1; } -static int sg_proc_debug_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data) -{ SG_PROC_READ_FN(sg_proc_debug_info); } +static int +sg_proc_dressz_write(struct file *filp, const char *buffer, + unsigned long count, void *data) +{ + int num; + unsigned long k = ULONG_MAX; + char buff[11]; -static int sg_proc_debug_info(char * buffer, int * len, off_t * begin, - off_t offset, int size) + if (!capable(CAP_SYS_ADMIN) || !capable(CAP_SYS_RAWIO)) + return -EACCES; + num = (count < 10) ? count : 10; + if (copy_from_user(buff, buffer, num)) + return -EFAULT; + buff[num] = '\0'; + k = simple_strtoul(buff, 0, 10); + if (k <= 1048576) { /* limit "big buff" to 1 MB */ + sg_big_buff = k; + return count; + } + return -ERANGE; +} + +static int +sg_proc_debug_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data) { - Sg_device * sdp; - const sg_io_hdr_t * hp; - int j, max_dev, new_interface; + SG_PROC_READ_FN(sg_proc_debug_info); +} - if (NULL == sg_dev_arr) { - PRINT_PROC("sg_dev_arr NULL, driver not initialized\n"); - return 1; - } - max_dev = sg_last_dev(); - PRINT_PROC("dev_max(currently)=%d max_active_device=%d (origin 1)\n", - sg_template.dev_max, max_dev); - PRINT_PROC(" def_reserved_size=%d\n", sg_big_buff); - for (j = 0; j < max_dev; ++j) { - if ((sdp = sg_get_dev(j))) { - Sg_fd * fp; - Sg_request * srp; - struct scsi_device * scsidp; - int dev, k, m, blen, usg; - - scsidp = sdp->device; - if (NULL == scsidp) { - PRINT_PROC("device %d detached ??\n", j); - continue; - } - dev = minor(sdp->i_rdev); +static int sg_proc_debug_helper(char *buffer, int *len, off_t * begin, + off_t offset, int size, Sg_device * sdp) +{ + int k, m, new_interface, blen, usg; + Sg_request *srp; + Sg_fd *fp; + const sg_io_hdr_t *hp; + const char * cp; - if (sg_get_nth_sfp(sdp, 0)) { - PRINT_PROC(" >>> device=sg%d ", dev); - if (sdp->detached) - PRINT_PROC("detached pending close "); - else - PRINT_PROC("scsi%d chan=%d id=%d lun=%d em=%d", - scsidp->host->host_no, scsidp->channel, - scsidp->id, scsidp->lun, scsidp->host->hostt->emulated); - PRINT_PROC(" sg_tablesize=%d excl=%d\n", sdp->sg_tablesize, - sdp->exclude); - } - for (k = 0; (fp = sg_get_nth_sfp(sdp, k)); ++k) { + for (k = 0; (fp = sg_get_nth_sfp(sdp, k)); ++k) { PRINT_PROC(" FD(%d): timeout=%dms bufflen=%d " "(res)sgat=%d low_dma=%d\n", k + 1, - sg_jif_to_ms(fp->timeout), fp->reserve.bufflen, - (int)fp->reserve.k_use_sg, (int)fp->low_dma); + sg_jif_to_ms(fp->timeout), + fp->reserve.bufflen, + (int) fp->reserve.k_use_sg, + (int) fp->low_dma); PRINT_PROC(" cmd_q=%d f_packid=%d k_orphan=%d closed=%d\n", - (int)fp->cmd_q, (int)fp->force_packid, - (int)fp->keep_orphan, (int)fp->closed); + (int) fp->cmd_q, (int) fp->force_packid, + (int) fp->keep_orphan, (int) fp->closed); for (m = 0; (srp = sg_get_nth_request(fp, m)); ++m) { - hp = &srp->header; - new_interface = (hp->interface_id == '\0') ? 0 : 1; -/* stop indenting so far ... */ - PRINT_PROC(srp->res_used ? ((new_interface && - (SG_FLAG_MMAP_IO & hp->flags)) ? " mmap>> " : " rb>> ") : - ((SG_INFO_DIRECT_IO_MASK & hp->info) ? " dio>> " : " ")); - blen = srp->my_cmdp ? srp->my_cmdp->sr_bufflen : srp->data.bufflen; - usg = srp->my_cmdp ? srp->my_cmdp->sr_use_sg : srp->data.k_use_sg; - PRINT_PROC(srp->done ? ((1 == srp->done) ? "rcv:" : "fin:") - : (srp->my_cmdp ? "act:" : "prior:")); - PRINT_PROC(" id=%d blen=%d", srp->header.pack_id, blen); - if (srp->done) - PRINT_PROC(" dur=%d", hp->duration); - else - PRINT_PROC(" t_o/elap=%d/%d", new_interface ? hp->timeout : - sg_jif_to_ms(fp->timeout), - sg_jif_to_ms(hp->duration ? (jiffies - hp->duration) : 0)); - PRINT_PROC("ms sgat=%d op=0x%02x\n", usg, (int)srp->data.cmd_opcode); -/* reset indenting */ + hp = &srp->header; + new_interface = (hp->interface_id == '\0') ? 0 : 1; + if (srp->res_used) { + if (new_interface && + (SG_FLAG_MMAP_IO & hp->flags)) + cp = " mmap>> "; + else + cp = " rb>> "; + } else { + if (SG_INFO_DIRECT_IO_MASK & hp->info) + cp = " dio>> "; + else + cp = " "; + } + PRINT_PROC(cp); + blen = srp->my_cmdp ? + srp->my_cmdp->sr_bufflen : srp->data.bufflen; + usg = srp->my_cmdp ? + srp->my_cmdp->sr_use_sg : srp->data.k_use_sg; + PRINT_PROC(srp->done ? + ((1 == srp->done) ? "rcv:" : "fin:") + : (srp->my_cmdp ? "act:" : "prior:")); + PRINT_PROC(" id=%d blen=%d", + srp->header.pack_id, blen); + if (srp->done) + PRINT_PROC(" dur=%d", hp->duration); + else + PRINT_PROC(" t_o/elap=%d/%d", + new_interface ? hp->timeout : sg_jif_to_ms(fp->timeout), + sg_jif_to_ms(hp->duration ? (jiffies - hp->duration) : 0)); + PRINT_PROC("ms sgat=%d op=0x%02x\n", usg, + (int) srp->data.cmd_opcode); } if (0 == m) - PRINT_PROC(" No requests active\n"); - } + PRINT_PROC(" No requests active\n"); } - } - return 1; + return 1; } -static int sg_proc_dev_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data) -{ SG_PROC_READ_FN(sg_proc_dev_info); } - -static int sg_proc_dev_info(char * buffer, int * len, off_t * begin, - off_t offset, int size) -{ - Sg_device * sdp; - int j, max_dev; - struct scsi_device * scsidp; - - max_dev = sg_last_dev(); - for (j = 0; j < max_dev; ++j) { - sdp = sg_get_dev(j); - if (sdp && (scsidp = sdp->device) && (! sdp->detached)) - PRINT_PROC("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", - scsidp->host->host_no, scsidp->channel, scsidp->id, - scsidp->lun, (int)scsidp->type, (int)scsidp->access_count, - (int)scsidp->queue_depth, (int)scsidp->device_busy, - (int)scsidp->online); - else - PRINT_PROC("-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n"); - } - return 1; +static int +sg_proc_debug_info(char *buffer, int *len, off_t * begin, + off_t offset, int size) +{ + Sg_device *sdp; + int j, max_dev; + + if (NULL == sg_dev_arr) { + PRINT_PROC("sg_dev_arr NULL, driver not initialized\n"); + return 1; + } + max_dev = sg_last_dev(); + PRINT_PROC("dev_max(currently)=%d max_active_device=%d (origin 1)\n", + sg_template.dev_max, max_dev); + PRINT_PROC(" def_reserved_size=%d\n", sg_big_buff); + for (j = 0; j < max_dev; ++j) { + if ((sdp = sg_get_dev(j))) { + struct scsi_device *scsidp; + int dev; + + scsidp = sdp->device; + if (NULL == scsidp) { + PRINT_PROC("device %d detached ??\n", j); + continue; + } + dev = minor(sdp->i_rdev); + + if (sg_get_nth_sfp(sdp, 0)) { + PRINT_PROC(" >>> device=sg%d ", dev); + if (sdp->detached) + PRINT_PROC("detached pending close "); + else + PRINT_PROC + ("scsi%d chan=%d id=%d lun=%d em=%d", + scsidp->host->host_no, + scsidp->channel, scsidp->id, + scsidp->lun, + scsidp->host->hostt->emulated); + PRINT_PROC(" sg_tablesize=%d excl=%d\n", + sdp->sg_tablesize, sdp->exclude); + } + if (0 == sg_proc_debug_helper(buffer, len, begin, + offset, size, sdp)) + return 0; + } + } + return 1; } -static int sg_proc_devhdr_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data) -{ SG_PROC_READ_FN(sg_proc_devhdr_info); } +static int +sg_proc_dev_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data) +{ + SG_PROC_READ_FN(sg_proc_dev_info); +} + +static int +sg_proc_dev_info(char *buffer, int *len, off_t * begin, off_t offset, int size) +{ + Sg_device *sdp; + int j, max_dev; + struct scsi_device *scsidp; + + max_dev = sg_last_dev(); + for (j = 0; j < max_dev; ++j) { + sdp = sg_get_dev(j); + if (sdp && (scsidp = sdp->device) && (!sdp->detached)) + PRINT_PROC("%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\t%d\n", + scsidp->host->host_no, scsidp->channel, + scsidp->id, scsidp->lun, (int) scsidp->type, + (int) scsidp->access_count, + (int) scsidp->queue_depth, + (int) scsidp->device_busy, + (int) scsidp->online); + else + PRINT_PROC("-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\t-1\n"); + } + return 1; +} -static int sg_proc_devhdr_info(char * buffer, int * len, off_t * begin, - off_t offset, int size) +static int +sg_proc_devhdr_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data) { - PRINT_PROC("host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\tonline\n"); - return 1; + SG_PROC_READ_FN(sg_proc_devhdr_info); } -static int sg_proc_devstrs_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data) -{ SG_PROC_READ_FN(sg_proc_devstrs_info); } - -static int sg_proc_devstrs_info(char * buffer, int * len, off_t * begin, - off_t offset, int size) +static int +sg_proc_devhdr_info(char *buffer, int *len, off_t * begin, + off_t offset, int size) { - Sg_device * sdp; - int j, max_dev; - struct scsi_device * scsidp; + PRINT_PROC("host\tchan\tid\tlun\ttype\topens\tqdepth\tbusy\tonline\n"); + return 1; +} - max_dev = sg_last_dev(); - for (j = 0; j < max_dev; ++j) { - sdp = sg_get_dev(j); - if (sdp && (scsidp = sdp->device) && (! sdp->detached)) - PRINT_PROC("%8.8s\t%16.16s\t%4.4s\n", - scsidp->vendor, scsidp->model, scsidp->rev); - else - PRINT_PROC("\n"); - } - return 1; +static int +sg_proc_devstrs_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data) +{ + SG_PROC_READ_FN(sg_proc_devstrs_info); +} + +static int +sg_proc_devstrs_info(char *buffer, int *len, off_t * begin, + off_t offset, int size) +{ + Sg_device *sdp; + int j, max_dev; + struct scsi_device *scsidp; + + max_dev = sg_last_dev(); + for (j = 0; j < max_dev; ++j) { + sdp = sg_get_dev(j); + if (sdp && (scsidp = sdp->device) && (!sdp->detached)) + PRINT_PROC("%8.8s\t%16.16s\t%4.4s\n", + scsidp->vendor, scsidp->model, scsidp->rev); + else + PRINT_PROC("\n"); + } + return 1; } -static int sg_proc_host_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data) -{ SG_PROC_READ_FN(sg_proc_host_info); } +static int +sg_proc_host_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data) +{ + SG_PROC_READ_FN(sg_proc_host_info); +} + +static int +sg_proc_host_info(char *buffer, int *len, off_t * begin, off_t offset, int size) +{ + struct Scsi_Host *shp; + int k; + + for (k = 0, shp = scsi_hostlist; shp; shp = shp->next, ++k) { + for (; k < shp->host_no; ++k) + PRINT_PROC("-1\t-1\t-1\t-1\t-1\t-1\n"); + PRINT_PROC("%u\t%hu\t%hd\t%hu\t%d\t%d\n", + shp->unique_id, shp->host_busy, shp->cmd_per_lun, + shp->sg_tablesize, (int) shp->unchecked_isa_dma, + (int) shp->hostt->emulated); + } + return 1; +} -static int sg_proc_host_info(char * buffer, int * len, off_t * begin, - off_t offset, int size) +static int +sg_proc_hosthdr_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data) { - struct Scsi_Host * shp; - int k; - - for (k = 0, shp = scsi_hostlist; shp; shp = shp->next, ++k) { - for ( ; k < shp->host_no; ++k) - PRINT_PROC("-1\t-1\t-1\t-1\t-1\t-1\n"); - PRINT_PROC("%u\t%hu\t%hd\t%hu\t%d\t%d\n", - shp->unique_id, shp->host_busy, shp->cmd_per_lun, - shp->sg_tablesize, (int)shp->unchecked_isa_dma, - (int)shp->hostt->emulated); - } - return 1; + SG_PROC_READ_FN(sg_proc_hosthdr_info); } -static int sg_proc_hosthdr_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data) -{ SG_PROC_READ_FN(sg_proc_hosthdr_info); } - -static int sg_proc_hosthdr_info(char * buffer, int * len, off_t * begin, - off_t offset, int size) +static int +sg_proc_hosthdr_info(char *buffer, int *len, off_t * begin, + off_t offset, int size) { - PRINT_PROC("uid\tbusy\tcpl\tscatg\tisa\temul\n"); - return 1; + PRINT_PROC("uid\tbusy\tcpl\tscatg\tisa\temul\n"); + return 1; } -static int sg_proc_hoststrs_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data) -{ SG_PROC_READ_FN(sg_proc_hoststrs_info); } +static int +sg_proc_hoststrs_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data) +{ + SG_PROC_READ_FN(sg_proc_hoststrs_info); +} #define SG_MAX_HOST_STR_LEN 256 -static int sg_proc_hoststrs_info(char * buffer, int * len, off_t * begin, - off_t offset, int size) -{ - struct Scsi_Host * shp; - int k; - char buff[SG_MAX_HOST_STR_LEN]; - char * cp; - - for (k = 0, shp = scsi_hostlist; shp; shp = shp->next, ++k) { - for ( ; k < shp->host_no; ++k) - PRINT_PROC("\n"); - strncpy(buff, shp->hostt->info ? shp->hostt->info(shp) : - (shp->hostt->name ? shp->hostt->name : ""), - SG_MAX_HOST_STR_LEN); - buff[SG_MAX_HOST_STR_LEN - 1] = '\0'; - for (cp = buff; *cp; ++cp) { - if ('\n' == *cp) - *cp = ' '; /* suppress imbedded newlines */ - } - PRINT_PROC("%s\n", buff); - } - return 1; +static int +sg_proc_hoststrs_info(char *buffer, int *len, off_t * begin, + off_t offset, int size) +{ + struct Scsi_Host *shp; + int k; + char buff[SG_MAX_HOST_STR_LEN]; + char *cp; + + for (k = 0, shp = scsi_hostlist; shp; shp = shp->next, ++k) { + for (; k < shp->host_no; ++k) + PRINT_PROC("\n"); + strncpy(buff, shp->hostt->info ? shp->hostt->info(shp) : + (shp->hostt->name ? shp->hostt->name : ""), + SG_MAX_HOST_STR_LEN); + buff[SG_MAX_HOST_STR_LEN - 1] = '\0'; + for (cp = buff; *cp; ++cp) { + if ('\n' == *cp) + *cp = ' '; /* suppress imbedded newlines */ + } + PRINT_PROC("%s\n", buff); + } + return 1; } -static int sg_proc_version_read(char * buffer, char ** start, off_t offset, - int size, int * eof, void * data) -{ SG_PROC_READ_FN(sg_proc_version_info); } - -static int sg_proc_version_info(char * buffer, int * len, off_t * begin, - off_t offset, int size) +static int +sg_proc_version_read(char *buffer, char **start, off_t offset, + int size, int *eof, void *data) { - PRINT_PROC("%d\t%s\n", sg_version_num, sg_version_str); - return 1; + SG_PROC_READ_FN(sg_proc_version_info); } -#endif /* CONFIG_PROC_FS */ +static int +sg_proc_version_info(char *buffer, int *len, off_t * begin, + off_t offset, int size) +{ + PRINT_PROC("%d\t%s\n", sg_version_num, sg_version_str); + return 1; +} +#endif /* CONFIG_PROC_FS */ module_init(init_sg); module_exit(exit_sg); diff -Nru a/drivers/scsi/sr.c b/drivers/scsi/sr.c --- a/drivers/scsi/sr.c Fri Aug 16 14:34:55 2002 +++ b/drivers/scsi/sr.c Fri Aug 16 14:34:55 2002 @@ -734,17 +734,17 @@ char *page, size_t count, loff_t off) { kdev_t kdev; - kdev.value=(int)driverfs_dev->driver_data; + kdev.value=(int)(long)driverfs_dev->driver_data; return off ? 0 : sprintf(page, "%x\n",kdev.value); } -static DEVICE_ATTR(kdev,"kdev",S_IRUGO,sr_device_kdev_read,NULL); +static DEVICE_ATTR(kdev,S_IRUGO,sr_device_kdev_read,NULL); static ssize_t sr_device_type_read(struct device *driverfs_dev, char *page, size_t count, loff_t off) { return off ? 0 : sprintf (page, "CHR\n"); } -static DEVICE_ATTR(type,"type",S_IRUGO,sr_device_type_read,NULL); +static DEVICE_ATTR(type,S_IRUGO,sr_device_type_read,NULL); void sr_finish() @@ -800,7 +800,7 @@ &SCp->device->sdev_driverfs_dev; SCp->cdi.cdrom_driverfs_dev.bus = &scsi_driverfs_bus_type; SCp->cdi.cdrom_driverfs_dev.driver_data = - (void *)__mkdev(MAJOR_NR, i); + (void *)(long)__mkdev(MAJOR_NR, i); device_register(&SCp->cdi.cdrom_driverfs_dev); device_create_file(&SCp->cdi.cdrom_driverfs_dev, &dev_attr_type); diff -Nru a/drivers/scsi/st.c b/drivers/scsi/st.c --- a/drivers/scsi/st.c Fri Aug 16 14:34:59 2002 +++ b/drivers/scsi/st.c Fri Aug 16 14:34:59 2002 @@ -3530,17 +3530,17 @@ char *page, size_t count, loff_t off) { kdev_t kdev; - kdev.value=(int)driverfs_dev->driver_data; + kdev.value=(int)(long)driverfs_dev->driver_data; return off ? 0 : sprintf(page, "%x\n",kdev.value); } -static DEVICE_ATTR(kdev,"kdev",S_IRUGO,st_device_kdev_read,NULL); +static DEVICE_ATTR(kdev,S_IRUGO,st_device_kdev_read,NULL); static ssize_t st_device_type_read(struct device *driverfs_dev, char *page, size_t count, loff_t off) { return off ? 0 : sprintf (page, "CHR\n"); } -static DEVICE_ATTR(type,"type",S_IRUGO,st_device_type_read,NULL); +static DEVICE_ATTR(type,S_IRUGO,st_device_type_read,NULL); static struct file_operations st_fops = @@ -3653,7 +3653,7 @@ tpnt->driverfs_dev_r[mode].parent = &SDp->sdev_driverfs_dev; tpnt->driverfs_dev_r[mode].bus = &scsi_driverfs_bus_type; tpnt->driverfs_dev_r[mode].driver_data = - (void *)__mkdev(MAJOR_NR, i + (mode << 5)); + (void *)(long)__mkdev(MAJOR_NR, i + (mode << 5)); device_register(&tpnt->driverfs_dev_r[mode]); device_create_file(&tpnt->driverfs_dev_r[mode], &dev_attr_type); @@ -3672,7 +3672,7 @@ tpnt->driverfs_dev_n[mode].parent= &SDp->sdev_driverfs_dev; tpnt->driverfs_dev_n[mode].bus = &scsi_driverfs_bus_type; tpnt->driverfs_dev_n[mode].driver_data = - (void *)__mkdev(MAJOR_NR, i + (mode << 5) + 128); + (void *)(long)__mkdev(MAJOR_NR, i + (mode << 5) + 128); device_register(&tpnt->driverfs_dev_n[mode]); device_create_file(&tpnt->driverfs_dev_n[mode], &dev_attr_type); diff -Nru a/drivers/scsi/sym53c8xx_2/sym_glue.c b/drivers/scsi/sym53c8xx_2/sym_glue.c --- a/drivers/scsi/sym53c8xx_2/sym_glue.c Fri Aug 16 14:34:58 2002 +++ b/drivers/scsi/sym53c8xx_2/sym_glue.c Fri Aug 16 14:34:58 2002 @@ -1107,7 +1107,6 @@ static int sym_eh_handler(int op, char *opname, Scsi_Cmnd *cmd) { hcb_p np = SYM_SOFTC_PTR(cmd); - unsigned long flags; SYM_QUEHEAD *qp; int to_do = SYM_EH_DO_IGNORE; int sts = -1; @@ -1118,8 +1117,6 @@ printf_warning("%s: %s operation started.\n", devname, opname); - SYM_LOCK_HCB(np, flags); - #if 0 /* This one should be the result of some race, thus to ignore */ if (cmd->serial_number != cmd->serial_number_at_timeout) @@ -1197,8 +1194,6 @@ /* Complete the command with locks held as required by the driver */ if (to_do == SYM_EH_DO_COMPLETE) sym_xpt_done2(np, cmd, CAM_REQ_ABORTED); - - SYM_UNLOCK_HCB(np, flags); /* Wait for completion with locks released, as required by kernel */ if (to_do == SYM_EH_DO_WAIT) { diff -Nru a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile --- a/drivers/usb/core/Makefile Fri Aug 16 14:35:01 2002 +++ b/drivers/usb/core/Makefile Fri Aug 16 14:35:01 2002 @@ -2,10 +2,10 @@ # Makefile for USB Core files and filesystem # -export-objs := usb.o hcd.o hcd-pci.o urb.o message.o file.o +export-objs := usb.o hcd.o hcd-pci.o urb.o message.o file.o buffer.o usbcore-objs := usb.o usb-debug.o hub.o hcd.o urb.o message.o \ - config.o file.o + config.o file.o buffer.o ifeq ($(CONFIG_PCI),y) usbcore-objs += hcd-pci.o diff -Nru a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c --- /dev/null Wed Dec 31 16:00:00 1969 +++ b/drivers/usb/core/buffer.c Fri Aug 16 14:35:01 2002 @@ -0,0 +1,186 @@ +/* + * DMA memory management for framework level HCD code (hc_driver) + * + * This implementation plugs in through generic "usb_bus" level methods, + * and works with real PCI, or when "pci device == null" makes sense. + */ + +#include +#include +#include +#include +#include +#include + + +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include +#include "hcd.h" + + +/* + * DMA-Consistent Buffers + */ + +/* FIXME tune these based on pool statistics ... */ +static const size_t pool_max [HCD_BUFFER_POOLS] = { + 32, + 128, + 512, + PAGE_SIZE / 2 + /* bigger --> allocate pages */ +}; + + +/* SETUP primitives */ + +/** + * hcd_buffer_create - initialize buffer pools + * @hcd: the bus whose buffer pools are to be initialized + * + * Call this as part of initializing a host controller that uses the pci dma + * memory allocators. It initializes some pools of dma-consistent memory that + * will be shared by all drivers using that controller, or returns a negative + * errno value on error. + * + * Call hcd_buffer_destroy() to clean up after using those pools. + */ +int hcd_buffer_create (struct usb_hcd *hcd) +{ + char name [16]; + int i, size; + + for (i = 0; i < HCD_BUFFER_POOLS; i++) { + if (!(size = pool_max [i])) + continue; + snprintf (name, sizeof name, "buffer-%d", size); + hcd->pool [i] = pci_pool_create (name, hcd->pdev, + size, size, 0, SLAB_KERNEL); + if (!hcd->pool [i]) { + hcd_buffer_destroy (hcd); + return -ENOMEM; + } + } + return 0; +} +EXPORT_SYMBOL (hcd_buffer_create); + + +/** + * hcd_buffer_destroy - deallocate buffer pools + * @hcd: the bus whose buffer pools are to be destroyed + * + * This frees the buffer pools created by hcd_buffer_create(). + */ +void hcd_buffer_destroy (struct usb_hcd *hcd) +{ + int i; + + for (i = 0; i < HCD_BUFFER_POOLS; i++) { + struct pci_pool *pool = hcd->pool [i]; + if (pool) { + pci_pool_destroy (pool); + hcd->pool [i] = 0; + } + } +} +EXPORT_SYMBOL (hcd_buffer_destroy); + + +/* sometimes alloc/free could use kmalloc with SLAB_DMA, for + * better sharing and to leverage mm/slab.c intelligence. + */ + +void *hcd_buffer_alloc ( + struct usb_bus *bus, + size_t size, + int mem_flags, + dma_addr_t *dma +) +{ + struct usb_hcd *hcd = bus->hcpriv; + int i; + + for (i = 0; i < HCD_BUFFER_POOLS; i++) { + if (size <= pool_max [i]) + return pci_pool_alloc (hcd->pool [i], mem_flags, dma); + } + return pci_alloc_consistent (hcd->pdev, size, dma); +} + +void hcd_buffer_free ( + struct usb_bus *bus, + size_t size, + void *addr, + dma_addr_t dma +) +{ + struct usb_hcd *hcd = bus->hcpriv; + int i; + + for (i = 0; i < HCD_BUFFER_POOLS; i++) { + if (size <= pool_max [i]) { + pci_pool_free (hcd->pool [i], addr, dma); + return; + } + } + pci_free_consistent (hcd->pdev, size, addr, dma); +} + + +/* + * DMA-Mappings for arbitrary memory buffers + */ + +int hcd_buffer_map ( + struct usb_bus *bus, + void *addr, + dma_addr_t *dma, + size_t size, + int direction +) { + struct usb_hcd *hcd = bus->hcpriv; + + // FIXME pci_map_single() has no standard failure mode! + *dma = pci_map_single (hcd->pdev, addr, size, + (direction == USB_DIR_IN) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); + return 0; +} + +void hcd_buffer_dmasync ( + struct usb_bus *bus, + dma_addr_t dma, + size_t size, + int direction +) { + struct usb_hcd *hcd = bus->hcpriv; + + pci_dma_sync_single (hcd->pdev, dma, size, + (direction == USB_DIR_IN) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); +} + +void hcd_buffer_unmap ( + struct usb_bus *bus, + dma_addr_t dma, + size_t size, + int direction +) { + struct usb_hcd *hcd = bus->hcpriv; + + pci_unmap_single (hcd->pdev, dma, size, + (direction == USB_DIR_IN) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); +} + + +// FIXME DMA-Mappings for struct scatterlist diff -Nru a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c --- a/drivers/usb/core/devio.c Fri Aug 16 14:34:58 2002 +++ b/drivers/usb/core/devio.c Fri Aug 16 14:34:58 2002 @@ -46,6 +46,7 @@ #include #include +#include "hcd.h" /* for usbcore internals */ struct async { struct list_head asynclist; @@ -724,7 +725,7 @@ lock_kernel(); if (intf->driver && ps->dev) { - usb_bind_driver(intf->driver,ps->dev, i); + usb_bind_driver (intf->driver, intf); } unlock_kernel(); } @@ -1062,8 +1063,8 @@ int size; void *buf = 0; int retval = 0; - struct usb_interface *ifp = 0; - struct usb_driver *driver = 0; + struct usb_interface *ifp = 0; + struct usb_driver *driver = 0; /* get input parameters and alloc buffer */ if (copy_from_user(&ctrl, (void *) arg, sizeof (ctrl))) @@ -1102,10 +1103,10 @@ unlock_kernel(); break; - /* let kernel drivers try to (re)bind to the interface */ - case USBDEVFS_CONNECT: - usb_find_interface_driver_for_ifnum (ps->dev, ctrl.ifno); - break; + /* let kernel drivers try to (re)bind to the interface */ + case USBDEVFS_CONNECT: + usb_find_interface_driver (ps->dev, ifp); + break; /* talk directly to the interface's driver */ default: @@ -1144,6 +1145,11 @@ return retval; } +/* + * NOTE: All requests here that have interface numbers as parameters + * are assuming that somehow the configuration has been prevented from + * changing. But there's no mechanism to ensure that... + */ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct dev_state *ps = (struct dev_state *)file->private_data; diff -Nru a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c --- a/drivers/usb/core/hcd-pci.c Fri Aug 16 14:34:57 2002 +++ b/drivers/usb/core/hcd-pci.c Fri Aug 16 14:34:57 2002 @@ -130,10 +130,19 @@ return retval; } } - pci_set_drvdata(dev, hcd); + pci_set_drvdata (dev, hcd); hcd->driver = driver; hcd->description = driver->description; hcd->pdev = dev; + hcd->self.bus_name = dev->slot_name; + hcd->product_desc = dev->name; + + if ((retval = hcd_buffer_create (hcd)) != 0) { +clean_3: + driver->hcd_free (hcd); + goto clean_2; + } + info ("%s @ %s, %s", hcd->description, dev->slot_name, dev->name); pci_read_config_byte (dev, PCI_LATENCY_TIMER, &latency); @@ -154,8 +163,7 @@ != 0) { err ("request interrupt %s failed", bufp); retval = -EBUSY; - driver->hcd_free (hcd); - goto clean_2; + goto clean_3; } hcd->irq = dev->irq; @@ -168,8 +176,6 @@ usb_bus_init (&hcd->self); hcd->self.op = &usb_hcd_operations; hcd->self.hcpriv = (void *) hcd; - hcd->self.bus_name = dev->slot_name; - hcd->product_desc = dev->name; INIT_LIST_HEAD (&hcd->dev_list); @@ -216,6 +222,7 @@ usb_disconnect (&hub); hcd->driver->stop (hcd); + hcd_buffer_destroy (hcd); hcd->state = USB_STATE_HALT; free_irq (hcd->irq, hcd); diff -Nru a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c --- a/drivers/usb/core/hcd.c Fri Aug 16 14:34:58 2002 +++ b/drivers/usb/core/hcd.c Fri Aug 16 14:34:58 2002 @@ -29,6 +29,7 @@ #include #include #include /* for UTS_SYSNAME */ +#include /* for hcd->pdev and dma addressing */ #include @@ -454,7 +455,6 @@ /* rh_timer protected by hcd_data_lock */ if (timer_pending (&hcd->rh_timer) || urb->status != -EINPROGRESS - || !HCD_IS_RUNNING (hcd->state) || urb->transfer_buffer_length < len) { dbg ("not queuing status urb, stat %d", urb->status); return -EINVAL; @@ -508,8 +508,12 @@ BUG (); } spin_unlock_irqrestore (&hcd_data_lock, flags); - } else + } else { spin_unlock_irqrestore (&urb->lock, flags); + spin_lock_irqsave (&hcd_data_lock, flags); + rh_status_urb (hcd, urb); + spin_unlock_irqrestore (&hcd_data_lock, flags); + } } else { /* this urb's been unlinked */ urb->hcpriv = 0; @@ -1018,6 +1022,24 @@ if (status) return status; + /* lower level hcd code should use *_dma exclusively */ + if (!(urb->transfer_flags & URB_NO_DMA_MAP)) { + if (usb_pipecontrol (urb->pipe)) + urb->setup_dma = pci_map_single ( + hcd->pdev, + urb->setup_packet, + sizeof (struct usb_ctrlrequest), + PCI_DMA_TODEVICE); + if (urb->transfer_buffer_length != 0) + urb->transfer_dma = pci_map_single ( + hcd->pdev, + urb->transfer_buffer, + urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); + } + /* increment urb's reference count as part of giving it to the HCD * (which now controls it). HCD guarantees that it either returns * an error or calls giveback(), but not both. @@ -1245,6 +1267,11 @@ .submit_urb = hcd_submit_urb, .unlink_urb = hcd_unlink_urb, .deallocate = hcd_free_dev, + .buffer_alloc = hcd_buffer_alloc, + .buffer_free = hcd_buffer_free, + .buffer_map = hcd_buffer_map, + .buffer_dmasync = hcd_buffer_dmasync, + .buffer_unmap = hcd_buffer_unmap, }; EXPORT_SYMBOL (usb_hcd_operations); @@ -1280,6 +1307,20 @@ if (urb->status) dbg ("giveback urb %p status %d len %d", urb, urb->status, urb->actual_length); + + /* lower level hcd code should use *_dma exclusively */ + if (!(urb->transfer_flags & URB_NO_DMA_MAP)) { + if (usb_pipecontrol (urb->pipe)) + pci_unmap_single (hcd->pdev, urb->setup_dma, + sizeof (struct usb_ctrlrequest), + PCI_DMA_TODEVICE); + if (urb->transfer_buffer_length != 0) + pci_unmap_single (hcd->pdev, urb->transfer_dma, + urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? PCI_DMA_FROMDEVICE + : PCI_DMA_TODEVICE); + } /* pass ownership to the completion handler */ urb->complete (urb); diff -Nru a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h --- a/drivers/usb/core/hcd.h Fri Aug 16 14:34:56 2002 +++ b/drivers/usb/core/hcd.h Fri Aug 16 14:34:56 2002 @@ -58,6 +58,9 @@ atomic_t resume_count; /* multiple resumes issue */ #endif +#define HCD_BUFFER_POOLS 4 + struct pci_pool *pool [HCD_BUFFER_POOLS]; + int state; # define __ACTIVE 0x01 # define __SLEEPY 0x02 @@ -109,6 +112,25 @@ int (*get_frame_number) (struct usb_device *usb_dev); int (*submit_urb) (struct urb *urb, int mem_flags); int (*unlink_urb) (struct urb *urb); + + /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */ + void *(*buffer_alloc)(struct usb_bus *bus, size_t size, + int mem_flags, + dma_addr_t *dma); + void (*buffer_free)(struct usb_bus *bus, size_t size, + void *addr, dma_addr_t dma); + + int (*buffer_map) (struct usb_bus *bus, + void *addr, dma_addr_t *dma, + size_t size, int direction); + void (*buffer_dmasync) (struct usb_bus *bus, + dma_addr_t dma, + size_t size, int direction); + void (*buffer_unmap) (struct usb_bus *bus, + dma_addr_t dma, + size_t size, int direction); + + // FIXME also: buffer_sg_map (), buffer_sg_unmap () }; /* each driver provides one of these, and hardware init support */ @@ -181,6 +203,25 @@ #endif /* CONFIG_PCI */ +/* pci-ish (pdev null is ok) buffer alloc/mapping support */ +int hcd_buffer_create (struct usb_hcd *hcd); +void hcd_buffer_destroy (struct usb_hcd *hcd); + +void *hcd_buffer_alloc (struct usb_bus *bus, size_t size, + int mem_flags, dma_addr_t *dma); +void hcd_buffer_free (struct usb_bus *bus, size_t size, + void *addr, dma_addr_t dma); + +int hcd_buffer_map (struct usb_bus *bus, + void *addr, dma_addr_t *dma, + size_t size, int direction); +void hcd_buffer_dmasync (struct usb_bus *bus, + dma_addr_t dma, + size_t size, int direction); +void hcd_buffer_unmap (struct usb_bus *bus, + dma_addr_t dma, + size_t size, int direction); + /* generic bus glue, needed for host controllers that don't use PCI */ extern struct usb_operations usb_hcd_operations; extern void usb_hcd_irq (int irq, void *__hcd, struct pt_regs *r); @@ -306,6 +347,16 @@ extern void usb_bus_get (struct usb_bus *bus); extern void usb_bus_put (struct usb_bus *bus); + +extern struct usb_interface *usb_ifnum_to_if (struct usb_device *dev, + unsigned ifnum); + +extern int usb_find_interface_driver (struct usb_device *dev, + struct usb_interface *interface); + +/* for probe/disconnect with correct module usage counting */ +void *usb_bind_driver(struct usb_driver *driver, struct usb_interface *intf); +void usb_unbind_driver(struct usb_device *device, struct usb_interface *intf); /*-------------------------------------------------------------------------*/ diff -Nru a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h --- a/drivers/usb/core/hub.h Fri Aug 16 14:34:53 2002 +++ b/drivers/usb/core/hub.h Fri Aug 16 14:34:53 2002 @@ -123,6 +123,10 @@ * Hub descriptor * See USB 2.0 spec Table 11-13 */ + +#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) +#define USB_DT_HUB_NONVAR_SIZE 7 + struct usb_hub_descriptor { __u8 bDescLength; __u8 bDescriptorType; diff -Nru a/drivers/usb/core/message.c b/drivers/usb/core/message.c --- a/drivers/usb/core/message.c Fri Aug 16 14:34:55 2002 +++ b/drivers/usb/core/message.c Fri Aug 16 14:34:55 2002 @@ -8,6 +8,8 @@ #include #include +#include "hcd.h" /* for usbcore internals */ + struct usb_api_data { wait_queue_head_t wqh; int done; diff -Nru a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c --- a/drivers/usb/core/usb.c Fri Aug 16 14:34:58 2002 +++ b/drivers/usb/core/usb.c Fri Aug 16 14:34:58 2002 @@ -52,7 +52,6 @@ * Prototypes for the device driver probing/loading functions */ static void usb_find_drivers(struct usb_device *); -static int usb_find_interface_driver(struct usb_device *, unsigned int); static void usb_check_support(struct usb_device *); /* @@ -119,7 +118,7 @@ } /** - * usb_unbind_driver - disconnects a driver from a device + * usb_unbind_driver - disconnects a driver from a device (usbcore-internal) * @device: usb device to be disconnected * @intf: interface of the device to be disconnected * Context: BKL held @@ -137,7 +136,7 @@ driver = intf->driver; priv = intf->private_data; - if (!driver) + if (!driver || !driver->disconnect) return; /* as soon as we increase the module use count we drop the BKL @@ -164,21 +163,27 @@ } /** - * usb_bind_driver - connect a driver to a device's interface - * @driver: device driver to be bound to a devices interface - * @dev: device to be bound - * @ifnum: index number of the interface to be used + * usb_bind_driver - connect a driver to a device's interface (usbcore-internal) + * @driver: device driver to be bound to interface + * @interface: interface that the driver will be using + * Context: BKL held * - * Does a save binding of a driver to a device's interface - * Returns a pointer to the drivers private description of the binding + * Does a safe binding of a driver to one of a device's interfaces. + * Returns the driver's data for the binding, or null indicating + * that the driver did not bind to this interface. + * + * This differs from usb_driver_claim_interface(), which is called from + * drivers and neither calls the driver's probe() entry nor does any + * locking to guard against removing driver modules. */ - -void *usb_bind_driver(struct usb_driver *driver, struct usb_device *dev, unsigned int ifnum) +void * +usb_bind_driver (struct usb_driver *driver, struct usb_interface *interface) { int i,m; void *private = NULL; const struct usb_device_id *id; - struct usb_interface *interface; + struct usb_device *dev = interface_to_usbdev (interface); + int ifnum; if (driver->owner) { m = try_inc_mod_count(driver->owner); @@ -187,14 +192,21 @@ unlock_kernel(); } - interface = &dev->actconfig->interface[ifnum]; + // START TEMPORARY + // driver->probe() hasn't yet changed to take interface not dev+ifnum, + // so we still need ifnum here. + for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) + if (&dev->actconfig->interface [ifnum] == interface) + break; + BUG_ON (ifnum == dev->actconfig->bNumInterfaces); + // END TEMPORARY id = driver->id_table; /* new style driver? */ if (id) { for (i = 0; i < interface->num_altsetting; i++) { interface->act_altsetting = i; - id = usb_match_id(dev, interface, id); + id = usb_match_id(interface, id); if (id) { down(&driver->serialize); private = driver->probe(dev,ifnum,id); @@ -252,7 +264,7 @@ * This will go through the list looking for another * driver that can handle the device */ - usb_find_interface_driver(dev, i); + usb_find_interface_driver(dev, interface); } } } @@ -294,29 +306,7 @@ } /** - * usb_ifnum_to_ifpos - convert the interface number to the interface position - * @dev: the device to use - * @ifnum: the interface number (bInterfaceNumber); not interface position - * - * This is used to convert the interface _number_ (as in - * interface.bInterfaceNumber) to the interface _position_ (as in - * dev->actconfig->interface + position). Note that the number is the same as - * the position for all interfaces _except_ devices with interfaces not - * sequentially numbered (e.g., 0, 2, 3, etc). - */ -int usb_ifnum_to_ifpos(struct usb_device *dev, unsigned ifnum) -{ - int i; - - for (i = 0; i < dev->actconfig->bNumInterfaces; i++) - if (dev->actconfig->interface[i].altsetting[0].bInterfaceNumber == ifnum) - return i; - - return -EINVAL; -} - -/** - * usb_ifnum_to_if - get the interface object with a given interface number + * usb_ifnum_to_if - get the interface object with a given interface number (usbcore-internal) * @dev: the device whose current configuration is considered * @ifnum: the desired interface * @@ -392,7 +382,8 @@ /* now we check this device */ if (dev->devnum > 0) for (i = 0; i < dev->actconfig->bNumInterfaces; i++) - usb_find_interface_driver(dev, i); + usb_find_interface_driver (dev, + dev->actconfig->interface + i); } @@ -475,7 +466,6 @@ /** * usb_match_id - find first usb_device_id matching device or interface - * @dev: the device whose descriptors are considered when matching * @interface: the interface of interest * @id: array of usb_device_id structures, terminated by zero entry * @@ -537,15 +527,18 @@ * its associated class and subclass. */ const struct usb_device_id * -usb_match_id(struct usb_device *dev, struct usb_interface *interface, - const struct usb_device_id *id) +usb_match_id(struct usb_interface *interface, const struct usb_device_id *id) { - struct usb_interface_descriptor *intf = 0; + struct usb_interface_descriptor *intf; + struct usb_device *dev; /* proc_connectinfo in devio.c may call us with id == NULL. */ if (id == NULL) return NULL; + intf = &interface->altsetting [interface->act_altsetting]; + dev = interface_to_usbdev(interface); + /* It is important to check that id->driver_info is nonzero, since an entry that is all zeroes except for a nonzero id->driver_info is the way to create an entry that @@ -584,19 +577,17 @@ (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) continue; - intf = &interface->altsetting [interface->act_altsetting]; - if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_CLASS) && (id->bInterfaceClass != intf->bInterfaceClass)) continue; if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_SUBCLASS) && (id->bInterfaceSubClass != intf->bInterfaceSubClass)) - continue; + continue; if ((id->match_flags & USB_DEVICE_ID_MATCH_INT_PROTOCOL) && (id->bInterfaceProtocol != intf->bInterfaceProtocol)) - continue; + continue; return id; } @@ -605,7 +596,7 @@ } /* - * This entrypoint gets called for each new device. + * This entrypoint gets called for unclaimed interfaces. * * We now walk the list of registered USB drivers, * looking for one that will accept this interface. @@ -620,21 +611,27 @@ * * Returns: 0 if a driver accepted the interface, -1 otherwise */ -static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) +int usb_find_interface_driver ( + struct usb_device *dev, + struct usb_interface *interface +) { struct list_head *tmp; - struct usb_interface *interface; void *private; struct usb_driver *driver; + int ifnum; - if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) { - err("bad find_interface_driver params"); - return -1; - } - down(&dev->serialize); - interface = dev->actconfig->interface + ifnum; + /* FIXME It's just luck that for some devices with drivers that set + * configuration in probe(), the interface numbers still make sense. + * That's one of several unsafe assumptions involved in configuring + * devices, and in binding drivers to their interfaces. + */ + for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) + if (&dev->actconfig->interface [ifnum] == interface) + break; + BUG_ON (ifnum == dev->actconfig->bNumInterfaces); if (usb_interface_claimed(interface)) goto out_err; @@ -645,7 +642,7 @@ driver = list_entry(tmp, struct usb_driver, driver_list); tmp = tmp->next; - private = usb_bind_driver(driver, dev, ifnum); + private = usb_bind_driver(driver, interface); /* probe() may have changed the config on us */ interface = dev->actconfig->interface + ifnum; @@ -664,25 +661,6 @@ return -1; } -/** - * usb_find_interface_driver_for_ifnum - finds a usb interface driver for the specified ifnum - * @dev: the device to use - * @ifnum: the interface number (bInterfaceNumber); not interface position! - * - * This converts a ifnum to ifpos via a call to usb_ifnum_to_ifpos and then - * calls usb_find_interface_driver() with the found ifpos. Note - * usb_find_interface_driver's ifnum parameter is actually interface position. - */ -int usb_find_interface_driver_for_ifnum(struct usb_device *dev, unsigned ifnum) -{ - int ifpos = usb_ifnum_to_ifpos(dev, ifnum); - - if (0 > ifpos) - return -EINVAL; - - return usb_find_interface_driver(dev, ifpos); -} - #ifdef CONFIG_HOTPLUG /* @@ -836,7 +814,7 @@ return sprintf (buf, "%u\n", udev->actconfig->bConfigurationValue); } -static DEVICE_ATTR(config,"configuration",S_IRUGO,show_config,NULL); +static DEVICE_ATTR(configuration,S_IRUGO,show_config,NULL); /* interfaces have one current setting; alternates * can have different endpoints and class info. @@ -851,7 +829,7 @@ interface = to_usb_interface (dev); return sprintf (buf, "%u\n", interface->altsetting->bAlternateSetting); } -static DEVICE_ATTR(altsetting,"altsetting",S_IRUGO,show_altsetting,NULL); +static DEVICE_ATTR(altsetting,S_IRUGO,show_altsetting,NULL); /* product driverfs file */ static ssize_t show_product (struct device *dev, char *buf, size_t count, loff_t off) @@ -863,12 +841,14 @@ return 0; udev = to_usb_device (dev); - len = usb_string(udev, udev->descriptor.iProduct, buf, PAGE_SIZE); + len = usb_string(udev, udev->descriptor.iProduct, buf, PAGE_SIZE); + if (len < 0) + return 0; buf[len] = '\n'; - buf[len+1] = 0x00; + buf[len+1] = 0; return len+1; } -static DEVICE_ATTR(product,"product",S_IRUGO,show_product,NULL); +static DEVICE_ATTR(product,S_IRUGO,show_product,NULL); /* manufacturer driverfs file */ static ssize_t @@ -881,12 +861,14 @@ return 0; udev = to_usb_device (dev); - len = usb_string(udev, udev->descriptor.iManufacturer, buf, PAGE_SIZE); + len = usb_string(udev, udev->descriptor.iManufacturer, buf, PAGE_SIZE); + if (len < 0) + return 0; buf[len] = '\n'; - buf[len+1] = 0x00; + buf[len+1] = 0; return len+1; } -static DEVICE_ATTR(manufacturer,"manufacturer",S_IRUGO,show_manufacturer,NULL); +static DEVICE_ATTR(manufacturer,S_IRUGO,show_manufacturer,NULL); /* serial number driverfs file */ static ssize_t @@ -899,12 +881,14 @@ return 0; udev = to_usb_device (dev); - len = usb_string(udev, udev->descriptor.iSerialNumber, buf, PAGE_SIZE); + len = usb_string(udev, udev->descriptor.iSerialNumber, buf, PAGE_SIZE); + if (len < 0) + return 0; buf[len] = '\n'; - buf[len+1] = 0x00; + buf[len+1] = 0; return len+1; } -static DEVICE_ATTR(serial,"serial",S_IRUGO,show_serial,NULL); +static DEVICE_ATTR(serial,S_IRUGO,show_serial,NULL); /* * This entrypoint gets called for each new device. @@ -918,13 +902,13 @@ unsigned claimed = 0; /* FIXME should get called for each new configuration not just the - * first one for a device. switching configs (or altesettings) should + * first one for a device. switching configs (or altsettings) should * undo driverfs and HCD state for the previous interfaces. */ for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) { struct usb_interface *interface = &dev->actconfig->interface[ifnum]; struct usb_interface_descriptor *desc = interface->altsetting; - + /* register this interface with driverfs */ interface->dev.parent = &dev->dev; interface->dev.bus = &usb_bus_type; @@ -950,7 +934,7 @@ /* if this interface hasn't already been claimed */ if (!usb_interface_claimed(interface)) { - if (usb_find_interface_driver(dev, ifnum)) + if (usb_find_interface_driver(dev, interface)) rejected++; else claimed++; @@ -1434,7 +1418,7 @@ err = device_register (&dev->dev); if (err) return err; - device_create_file (&dev->dev, &dev_attr_config); + device_create_file (&dev->dev, &dev_attr_configuration); if (dev->descriptor.iManufacturer) device_create_file (&dev->dev, &dev_attr_manufacturer); if (dev->descriptor.iProduct) @@ -1455,6 +1439,152 @@ } +/** + * usb_buffer_alloc - allocate dma-consistent buffer for URB_NO_DMA_MAP + * @dev: device the buffer will be used with + * @size: requested buffer size + * @mem_flags: affect whether allocation may block + * @dma: used to return DMA address of buffer + * + * Return value is either null (indicating no buffer could be allocated), or + * the cpu-space pointer to a buffer that may be used to perform DMA to the + * specified device. Such cpu-space buffers are returned along with the DMA + * address (through the pointer provided). + * + * These buffers are used with URB_NO_DMA_MAP set in urb->transfer_flags to + * avoid behaviors like using "DMA bounce buffers", or tying down I/O mapping + * hardware for long idle periods. The implementation varies between + * platforms, depending on details of how DMA will work to this device. + * + * When the buffer is no longer used, free it with usb_buffer_free(). + */ +void *usb_buffer_alloc ( + struct usb_device *dev, + size_t size, + int mem_flags, + dma_addr_t *dma +) +{ + if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc) + return 0; + return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma); +} + +/** + * usb_buffer_free - free memory allocated with usb_buffer_alloc() + * @dev: device the buffer was used with + * @size: requested buffer size + * @addr: CPU address of buffer + * @dma: DMA address of buffer + * + * This reclaims an I/O buffer, letting it be reused. The memory must have + * been allocated using usb_buffer_alloc(), and the parameters must match + * those provided in that allocation request. + */ +void usb_buffer_free ( + struct usb_device *dev, + size_t size, + void *addr, + dma_addr_t dma +) +{ + if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free) + return; + dev->bus->op->buffer_free (dev->bus, size, addr, dma); +} + +/** + * usb_buffer_map - create DMA mapping(s) for an urb + * @urb: urb whose transfer_buffer will be mapped + * + * Return value is either null (indicating no buffer could be mapped), or + * the parameter. URB_NO_DMA_MAP is added to urb->transfer_flags if the + * operation succeeds. + * + * This call would normally be used for an urb which is reused, perhaps + * as the target of a large periodic transfer, with usb_buffer_dmasync() + * calls to synchronize memory and dma state. It may not be used for + * control requests. + * + * Reverse the effect of this call with usb_buffer_unmap(). + */ +struct urb *usb_buffer_map (struct urb *urb) +{ + struct usb_bus *bus; + struct usb_operations *op; + + if (!urb + || usb_pipecontrol (urb->pipe) + || !urb->dev + || !(bus = urb->dev->bus) + || !(op = bus->op) + || !op->buffer_map) + return 0; + + if (op->buffer_map (bus, + urb->transfer_buffer, + &urb->transfer_dma, + urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? USB_DIR_IN + : USB_DIR_OUT)) + return 0; + urb->transfer_flags |= URB_NO_DMA_MAP; + return urb; +} + +/** + * usb_buffer_dmasync - synchronize DMA and CPU view of buffer(s) + * @urb: urb whose transfer_buffer will be synchronized + */ +void usb_buffer_dmasync (struct urb *urb) +{ + struct usb_bus *bus; + struct usb_operations *op; + + if (!urb + || !(urb->transfer_flags & URB_NO_DMA_MAP) + || !urb->dev + || !(bus = urb->dev->bus) + || !(op = bus->op) + || !op->buffer_dmasync) + return; + + op->buffer_dmasync (bus, + urb->transfer_dma, + urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? USB_DIR_IN + : USB_DIR_OUT); +} + +/** + * usb_buffer_unmap - free DMA mapping(s) for an urb + * @urb: urb whose transfer_buffer will be unmapped + * + * Reverses the effect of usb_buffer_map(). + */ +void usb_buffer_unmap (struct urb *urb) +{ + struct usb_bus *bus; + struct usb_operations *op; + + if (!urb + || !(urb->transfer_flags & URB_NO_DMA_MAP) + || !urb->dev + || !(bus = urb->dev->bus) + || !(op = bus->op) + || !op->buffer_unmap) + return; + + op->buffer_unmap (bus, + urb->transfer_dma, + urb->transfer_buffer_length, + usb_pipein (urb->pipe) + ? USB_DIR_IN + : USB_DIR_OUT); +} + #ifdef CONFIG_PROC_FS struct list_head *usb_driver_get_list(void) { @@ -1489,10 +1619,10 @@ */ static void __exit usb_exit(void) { - put_bus(&usb_bus_type); usb_major_cleanup(); usbfs_cleanup(); usb_hub_cleanup(); + put_bus(&usb_bus_type); } subsys_initcall(usb_init); @@ -1503,8 +1633,6 @@ * These symbols are exported for device (or host controller) * driver modules to use. */ -EXPORT_SYMBOL(usb_ifnum_to_ifpos); -EXPORT_SYMBOL(usb_ifnum_to_if); EXPORT_SYMBOL(usb_epnum_to_ep_desc); EXPORT_SYMBOL(usb_register); @@ -1516,7 +1644,6 @@ EXPORT_SYMBOL(usb_get_dev); EXPORT_SYMBOL(usb_hub_tt_clear_buffer); -EXPORT_SYMBOL(usb_find_interface_driver_for_ifnum); EXPORT_SYMBOL(usb_driver_claim_interface); EXPORT_SYMBOL(usb_interface_claimed); EXPORT_SYMBOL(usb_driver_release_interface); @@ -1527,11 +1654,16 @@ EXPORT_SYMBOL(usb_reset_device); EXPORT_SYMBOL(usb_connect); EXPORT_SYMBOL(usb_disconnect); -EXPORT_SYMBOL(usb_bind_driver); -EXPORT_SYMBOL(usb_unbind_driver); EXPORT_SYMBOL(__usb_get_extra_descriptor); EXPORT_SYMBOL(usb_get_current_frame_number); + +EXPORT_SYMBOL (usb_buffer_alloc); +EXPORT_SYMBOL (usb_buffer_free); + +EXPORT_SYMBOL (usb_buffer_map); +EXPORT_SYMBOL (usb_buffer_dmasync); +EXPORT_SYMBOL (usb_buffer_unmap); MODULE_LICENSE("GPL"); diff -Nru a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c --- a/drivers/usb/host/ehci-dbg.c Fri Aug 16 14:34:55 2002 +++ b/drivers/usb/host/ehci-dbg.c Fri Aug 16 14:34:55 2002 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001 by David Brownell + * Copyright (c) 2001-2002 by David Brownell * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -175,3 +175,215 @@ (status & PORT_CONNECT) ? " CONNECT" : "" \ ) +#ifdef DEBUG + +#define speed_char(info1) ({ char tmp; \ + switch (info1 & (3 << 12)) { \ + case 0 << 12: tmp = 'f'; break; \ + case 1 << 12: tmp = 'l'; break; \ + case 2 << 12: tmp = 'h'; break; \ + default: tmp = '?'; break; \ + }; tmp; }) + +static ssize_t +show_async (struct device *dev, char *buf, size_t count, loff_t off) +{ + struct pci_dev *pdev; + struct ehci_hcd *ehci; + unsigned long flags; + unsigned temp, size; + char *next; + struct ehci_qh *qh; + + if (off != 0) + return 0; + + pdev = container_of (dev, struct pci_dev, dev); + ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); + next = buf; + size = count; + + /* dumps a snapshot of the async schedule. + * usually empty except for long-term bulk reads, or head. + * one QH per line, and TDs we know about + */ + spin_lock_irqsave (&ehci->lock, flags); + if (ehci->async) { + qh = ehci->async; + do { + u32 scratch; + struct list_head *entry; + struct ehci_qtd *td; + + scratch = cpu_to_le32p (&qh->hw_info1); + temp = snprintf (next, size, "qh %p dev%d %cs ep%d", + qh, scratch & 0x007f, + speed_char (scratch), + (scratch >> 8) & 0x000f); + size -= temp; + next += temp; + + list_for_each (entry, &qh->qtd_list) { + td = list_entry (entry, struct ehci_qtd, + qtd_list); + scratch = cpu_to_le32p (&td->hw_token); + temp = snprintf (next, size, + ", td %p len=%d %s", + td, scratch >> 16, + ({ char *tmp; + switch ((scratch>>8)&0x03) { + case 0: tmp = "out"; break; + case 1: tmp = "in"; break; + case 2: tmp = "setup"; break; + default: tmp = "?"; break; + } tmp;}) + ); + size -= temp; + next += temp; + } + + temp = snprintf (next, size, "\n"); + size -= temp; + next += temp; + + } while ((qh = qh->qh_next.qh) != ehci->async); + } + spin_unlock_irqrestore (&ehci->lock, flags); + + return count - size; +} +static DEVICE_ATTR (async, S_IRUSR, show_async, NULL); + +#define DBG_SCHED_LIMIT 64 + +static ssize_t +show_periodic (struct device *dev, char *buf, size_t count, loff_t off) +{ + struct pci_dev *pdev; + struct ehci_hcd *ehci; + unsigned long flags; + union ehci_shadow p, *seen; + unsigned temp, size, seen_count; + char *next; + unsigned i, tag; + + if (off != 0) + return 0; + if (!(seen = kmalloc (DBG_SCHED_LIMIT * sizeof *seen, SLAB_ATOMIC))) + return 0; + seen_count = 0; + + pdev = container_of (dev, struct pci_dev, dev); + ehci = container_of (pci_get_drvdata (pdev), struct ehci_hcd, hcd); + next = buf; + size = count; + + temp = snprintf (next, size, "size = %d\n", ehci->periodic_size); + size -= temp; + next += temp; + + /* dump a snapshot of the periodic schedule. + * iso changes, interrupt usually doesn't. + */ + spin_lock_irqsave (&ehci->lock, flags); + for (i = 0; i < ehci->periodic_size; i++) { + p = ehci->pshadow [i]; + if (!p.ptr) + continue; + tag = Q_NEXT_TYPE (ehci->periodic [i]); + + temp = snprintf (next, size, "%4d: ", i); + size -= temp; + next += temp; + + do { + switch (tag) { + case Q_TYPE_QH: + temp = snprintf (next, size, " intr-%d %p", + p.qh->period, p.qh); + size -= temp; + next += temp; + for (temp = 0; temp < seen_count; temp++) { + if (seen [temp].ptr == p.ptr) + break; + } + /* show more info the first time around */ + if (temp == seen_count) { + u32 scratch = cpu_to_le32p ( + &p.qh->hw_info1); + + temp = snprintf (next, size, + " (%cs dev%d ep%d)", + speed_char (scratch), + scratch & 0x007f, + (scratch >> 8) & 0x000f); + + /* FIXME TDs too */ + + if (seen_count < DBG_SCHED_LIMIT) + seen [seen_count++].qh = p.qh; + } else + temp = 0; + tag = Q_NEXT_TYPE (p.qh->hw_next); + p = p.qh->qh_next; + break; + case Q_TYPE_FSTN: + temp = snprintf (next, size, + " fstn-%8x/%p", p.fstn->hw_prev, + p.fstn); + tag = Q_NEXT_TYPE (p.fstn->hw_next); + p = p.fstn->fstn_next; + break; + case Q_TYPE_ITD: + temp = snprintf (next, size, + " itd/%p", p.itd); + tag = Q_NEXT_TYPE (p.itd->hw_next); + p = p.itd->itd_next; + break; + case Q_TYPE_SITD: + temp = snprintf (next, size, + " sitd/%p", p.sitd); + tag = Q_NEXT_TYPE (p.sitd->hw_next); + p = p.sitd->sitd_next; + break; + } + size -= temp; + next += temp; + } while (p.ptr); + + temp = snprintf (next, size, "\n"); + size -= temp; + next += temp; + } + spin_unlock_irqrestore (&ehci->lock, flags); + kfree (seen); + + return count - size; +} +static DEVICE_ATTR (periodic, S_IRUSR, show_periodic, NULL); + +#undef DBG_SCHED_LIMIT + +static inline void create_debug_files (struct ehci_hcd *bus) +{ + device_create_file (&bus->hcd.pdev->dev, &dev_attr_async); + device_create_file (&bus->hcd.pdev->dev, &dev_attr_periodic); +} + +static inline void remove_debug_files (struct ehci_hcd *bus) +{ + device_remove_file (&bus->hcd.pdev->dev, &dev_attr_async); + device_remove_file (&bus->hcd.pdev->dev, &dev_attr_periodic); +} + +#else /* DEBUG */ + +static inline void create_debug_files (struct ehci_hcd *bus) +{ +} + +static inline void remove_debug_files (struct ehci_hcd *bus) +{ +} + +#endif /* DEBUG */ diff -Nru a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c --- a/drivers/usb/host/ehci-hcd.c Fri Aug 16 14:34:59 2002 +++ b/drivers/usb/host/ehci-hcd.c Fri Aug 16 14:34:59 2002 @@ -65,6 +65,8 @@ * * HISTORY: * + * 2002-08-06 Handling for bulk and interrupt transfers is mostly shared; + * only scheduling is different, no arbitrary limitations. * 2002-07-25 Sanity check PCI reads, mostly for better cardbus support, * clean up HC run state handshaking. * 2002-05-24 Preliminary FS/LS interrupts, using scheduling shortcuts @@ -85,7 +87,7 @@ * 2001-June Works with usb-storage and NEC EHCI on 2.4 */ -#define DRIVER_VERSION "2002-Jul-25" +#define DRIVER_VERSION "2002-Aug-06" #define DRIVER_AUTHOR "David Brownell" #define DRIVER_DESC "USB 2.0 'Enhanced' Host Controller (EHCI) Driver" @@ -93,6 +95,8 @@ // #define EHCI_VERBOSE_DEBUG // #define have_split_iso +#define INTR_AUTOMAGIC /* to be removed later in 2.5 */ + /* magic numbers that can affect system performance */ #define EHCI_TUNE_CERR 3 /* 0-3 qtd retries; 0 == don't stop */ #define EHCI_TUNE_RL_HS 0 /* nak throttle; see 4.9 */ @@ -376,6 +380,8 @@ return -ENOMEM; } + create_debug_files (ehci); + /* * Start, enabling full USB 2.0 functionality ... usb 1.1 devices * are explicitly handed to companion controller(s), so no TT is @@ -429,6 +435,8 @@ ehci_ready (ehci); ehci_reset (ehci); + remove_debug_files (ehci); + /* root hub is shut down separately (first, when possible) */ tasklet_disable (&ehci->tasklet); ehci_tasklet ((unsigned long) ehci); @@ -614,7 +622,8 @@ * * hcd-specific init for hcpriv hasn't been done yet * - * NOTE: EHCI queues control and bulk requests transparently, like OHCI. + * NOTE: control, bulk, and interrupt share the same code to append TDs + * to a (possibly active) QH, and the same QH scanning code. */ static int ehci_urb_enqueue ( struct usb_hcd *hcd, @@ -626,10 +635,11 @@ urb->transfer_flags &= ~EHCI_STATE_UNLINK; INIT_LIST_HEAD (&qtd_list); - switch (usb_pipetype (urb->pipe)) { - case PIPE_CONTROL: - case PIPE_BULK: + switch (usb_pipetype (urb->pipe)) { + // case PIPE_CONTROL: + // case PIPE_BULK: + default: if (!qh_urb_transaction (ehci, urb, &qtd_list, mem_flags)) return -ENOMEM; return submit_async (ehci, urb, &qtd_list, mem_flags); @@ -649,9 +659,6 @@ dbg ("no split iso support yet"); return -ENOSYS; #endif /* have_split_iso */ - - default: /* can't happen */ - return -ENOSYS; } } @@ -665,15 +672,16 @@ struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; unsigned long flags; - dbg ("%s urb_dequeue %p qh state %d", - hcd->self.bus_name, urb, qh->qh_state); + dbg ("%s urb_dequeue %p qh %p state %d", + hcd->self.bus_name, urb, qh, qh->qh_state); switch (usb_pipetype (urb->pipe)) { - case PIPE_CONTROL: - case PIPE_BULK: + // case PIPE_CONTROL: + // case PIPE_BULK: + default: spin_lock_irqsave (&ehci->lock, flags); if (ehci->reclaim) { -dbg ("dq: reclaim busy, %s", RUN_CONTEXT); + dbg ("dq: reclaim busy, %s", RUN_CONTEXT); if (in_interrupt ()) { spin_unlock_irqrestore (&ehci->lock, flags); return -EAGAIN; @@ -683,28 +691,43 @@ && ehci->hcd.state != USB_STATE_HALT ) { spin_unlock_irqrestore (&ehci->lock, flags); -// yeech ... this could spin for up to two frames! -dbg ("wait for dequeue: state %d, reclaim %p, hcd state %d", - qh->qh_state, ehci->reclaim, ehci->hcd.state -); - udelay (100); + /* let pending unlinks complete */ + wait_ms (1); spin_lock_irqsave (&ehci->lock, flags); } } if (qh->qh_state == QH_STATE_LINKED) start_unlink_async (ehci, qh); spin_unlock_irqrestore (&ehci->lock, flags); - return 0; + break; case PIPE_INTERRUPT: - intr_deschedule (ehci, urb->start_frame, qh, - (urb->dev->speed == USB_SPEED_HIGH) - ? urb->interval - : (urb->interval << 3)); - if (ehci->hcd.state == USB_STATE_HALT) - urb->status = -ESHUTDOWN; - qh_completions (ehci, qh, 1); - return 0; + if (qh->qh_state == QH_STATE_LINKED) { + /* messy, can spin or block a microframe ... */ + intr_deschedule (ehci, qh, 1); + /* qh_state == IDLE */ + } + qh_completions (ehci, qh); + + /* reschedule QH iff another request is queued */ + if (!list_empty (&qh->qtd_list) + && HCD_IS_RUNNING (ehci->hcd.state)) { + int status; + + spin_lock_irqsave (&ehci->lock, flags); + status = qh_schedule (ehci, qh); + spin_unlock_irqrestore (&ehci->lock, flags); + + if (status != 0) { + // shouldn't happen often, but ... + // FIXME kill those tds' urbs + err ("can't reschedule qh %p, err %d", + qh, status); + } + return status; + } + + break; case PIPE_ISOCHRONOUS: // itd or sitd ... @@ -712,9 +735,9 @@ // wait till next completion, do it then. // completion irqs can wait up to 1024 msec, urb->transfer_flags |= EHCI_STATE_UNLINK; - return 0; + break; } - return -EINVAL; + return 0; } /*-------------------------------------------------------------------------*/ @@ -728,6 +751,7 @@ int i; unsigned long flags; + /* ASSERT: no requests/urbs are still linked (so no TDs) */ /* ASSERT: nobody can be submitting urbs for this any more */ dbg ("%s: free_config devnum %d", hcd->self.bus_name, udev->devnum); @@ -736,34 +760,57 @@ for (i = 0; i < 32; i++) { if (dev->ep [i]) { struct ehci_qh *qh; + char *why; /* dev->ep never has ITDs or SITDs */ qh = (struct ehci_qh *) dev->ep [i]; - vdbg ("free_config, ep 0x%02x qh %p", i, qh); - if (!list_empty (&qh->qtd_list)) { - dbg ("ep 0x%02x qh %p not empty!", i, qh); + + /* detect/report non-recoverable errors */ + if (in_interrupt ()) + why = "disconnect() didn't"; + else if ((qh->hw_info2 & cpu_to_le32 (0xffff)) != 0 + && qh->qh_state != QH_STATE_IDLE) + why = "(active periodic)"; + else + why = 0; + if (why) { + err ("dev %s-%s ep %d-%s error: %s", + hcd->self.bus_name, udev->devpath, + i & 0xf, (i & 0x10) ? "IN" : "OUT", + why); BUG (); } - dev->ep [i] = 0; - /* wait_ms() won't spin here -- we're a thread */ + dev->ep [i] = 0; + if (qh->qh_state == QH_STATE_IDLE) + goto idle; + dbg ("free_config, async ep 0x%02x qh %p", i, qh); + + /* scan_async() empties the ring as it does its work, + * using IAA, but doesn't (yet?) turn it off. if it + * doesn't empty this qh, likely it's the last entry. + */ while (qh->qh_state == QH_STATE_LINKED && ehci->reclaim && ehci->hcd.state != USB_STATE_HALT ) { spin_unlock_irqrestore (&ehci->lock, flags); + /* wait_ms() won't spin, we're a thread; + * and we know IRQ+tasklet can progress + */ wait_ms (1); spin_lock_irqsave (&ehci->lock, flags); } - if (qh->qh_state == QH_STATE_LINKED) { + if (qh->qh_state == QH_STATE_LINKED) start_unlink_async (ehci, qh); - while (qh->qh_state != QH_STATE_IDLE) { - spin_unlock_irqrestore (&ehci->lock, - flags); - wait_ms (1); - spin_lock_irqsave (&ehci->lock, flags); - } + while (qh->qh_state != QH_STATE_IDLE + && ehci->hcd.state != USB_STATE_HALT) { + spin_unlock_irqrestore (&ehci->lock, + flags); + wait_ms (1); + spin_lock_irqsave (&ehci->lock, flags); } +idle: qh_put (ehci, qh); } } diff -Nru a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c --- a/drivers/usb/host/ehci-q.c Fri Aug 16 14:34:55 2002 +++ b/drivers/usb/host/ehci-q.c Fri Aug 16 14:34:55 2002 @@ -47,9 +47,11 @@ qtd_fill (struct ehci_qtd *qtd, dma_addr_t buf, size_t len, int token) { int i, count; + u64 addr = buf; /* one buffer entry per 4K ... first might be short or unaligned */ - qtd->hw_buf [0] = cpu_to_le32 (buf); + qtd->hw_buf [0] = cpu_to_le32 ((u32)addr); + qtd->hw_buf_hi [0] = cpu_to_le32 ((u32)(addr >> 32)); count = 0x1000 - (buf & 0x0fff); /* rest of that page */ if (likely (len < count)) /* ... iff needed */ count = len; @@ -59,7 +61,7 @@ /* per-qtd limit: from 16K to 20K (best alignment) */ for (i = 1; count < len && i < 5; i++) { - u64 addr = buf; + addr = buf; qtd->hw_buf [i] = cpu_to_le32 ((u32)addr); qtd->hw_buf_hi [i] = cpu_to_le32 ((u32)(addr >> 32)); buf += 0x1000; @@ -157,46 +159,35 @@ } } -static void ehci_urb_complete ( - struct ehci_hcd *ehci, - dma_addr_t addr, - struct urb *urb -) { - if (urb->transfer_buffer_length && usb_pipein (urb->pipe)) - pci_dma_sync_single (ehci->hcd.pdev, addr, - urb->transfer_buffer_length, - PCI_DMA_FROMDEVICE); - - /* cleanse status if we saw no error */ - if (likely (urb->status == -EINPROGRESS)) { - if (urb->actual_length != urb->transfer_buffer_length - && (urb->transfer_flags & URB_SHORT_NOT_OK)) - urb->status = -EREMOTEIO; - else - urb->status = 0; - } - - /* only report unlinks once */ - if (likely (urb->status != -ENOENT && urb->status != -ENOTCONN)) - urb->complete (urb); -} - /* urb->lock ignored from here on (hcd is done with urb) */ static void ehci_urb_done ( struct ehci_hcd *ehci, - dma_addr_t addr, struct urb *urb ) { - if (urb->transfer_buffer_length) - pci_unmap_single (ehci->hcd.pdev, - addr, - urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); +#ifdef INTR_AUTOMAGIC + struct urb *resubmit = 0; + struct usb_device *dev = 0; +#endif + if (likely (urb->hcpriv != 0)) { - qh_put (ehci, (struct ehci_qh *) urb->hcpriv); + struct ehci_qh *qh = (struct ehci_qh *) urb->hcpriv; + + /* S-mask in a QH means it's an interrupt urb */ + if ((qh->hw_info2 & cpu_to_le32 (0x00ff)) != 0) { + + /* ... update hc-wide periodic stats (for usbfs) */ + ehci->hcd.self.bandwidth_int_reqs--; + +#ifdef INTR_AUTOMAGIC + if (!((urb->status == -ENOENT) + || (urb->status == -ECONNRESET))) { + resubmit = usb_get_urb (urb); + dev = urb->dev; + } +#endif + } + qh_put (ehci, qh); urb->hcpriv = 0; } @@ -208,33 +199,46 @@ urb->status = 0; } - /* hand off urb ownership */ usb_hcd_giveback_urb (&ehci->hcd, urb); + +#ifdef INTR_AUTOMAGIC + if (resubmit && ((urb->status == -ENOENT) + || (urb->status == -ECONNRESET))) { + usb_put_urb (resubmit); + resubmit = 0; + } + // device drivers will soon be doing something like this + if (resubmit) { + int status; + + resubmit->dev = dev; + status = usb_submit_urb (resubmit, SLAB_KERNEL); + if (status != 0) + err ("can't resubmit interrupt urb %p: status %d", + resubmit, status); + usb_put_urb (resubmit); + } +#endif } /* * Process completed qtds for a qh, issuing completions if needed. - * When freeing: frees qtds, unmaps buf, returns URB to driver. - * When not freeing (queued periodic qh): retain qtds, mapping, and urb. + * Frees qtds, unmaps buf, returns URB to driver. * Races up to qh->hw_current; returns number of urb completions. */ -static int -qh_completions ( - struct ehci_hcd *ehci, - struct ehci_qh *qh, - int freeing -) { +static void +qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ struct ehci_qtd *qtd, *last; struct list_head *next, *qtd_list = &qh->qtd_list; int unlink = 0, halted = 0; unsigned long flags; - int retval = 0; spin_lock_irqsave (&ehci->lock, flags); if (unlikely (list_empty (qtd_list))) { spin_unlock_irqrestore (&ehci->lock, flags); - return retval; + return; } /* scan QTDs till end of list, or we reach an active one */ @@ -251,14 +255,8 @@ if (likely (last->urb != urb)) { /* complete() can reenter this HCD */ spin_unlock_irqrestore (&ehci->lock, flags); - if (likely (freeing != 0)) - ehci_urb_done (ehci, last->buf_dma, - last->urb); - else - ehci_urb_complete (ehci, last->buf_dma, - last->urb); + ehci_urb_done (ehci, last->urb); spin_lock_irqsave (&ehci->lock, flags); - retval++; } /* qh overlays can have HC's old cached copies of @@ -270,8 +268,7 @@ qh->hw_qtd_next = last->hw_next; } - if (likely (freeing != 0)) - ehci_qtd_free (ehci, last); + ehci_qtd_free (ehci, last); last = 0; } next = qtd->qtd_list.next; @@ -288,7 +285,7 @@ /* fault: unlink the rest, since this qtd saw an error? */ if (unlikely ((token & QTD_STS_HALT) != 0)) { - freeing = unlink = 1; + unlink = 1; /* status copied below */ /* QH halts only because of fault (above) or unlink (here). */ @@ -296,13 +293,14 @@ /* unlinking everything because of HC shutdown? */ if (ehci->hcd.state == USB_STATE_HALT) { - freeing = unlink = 1; + unlink = 1; /* explicit unlink, maybe starting here? */ } else if (qh->qh_state == QH_STATE_IDLE && (urb->status == -ECONNRESET + || urb->status == -ESHUTDOWN || urb->status == -ENOENT)) { - freeing = unlink = 1; + unlink = 1; /* QH halted to unlink urbs _after_ this? */ } else if (!unlink && (token & QTD_STS_ACTIVE) != 0) { @@ -312,7 +310,7 @@ /* unlink the rest? once we start unlinking, after * a fault or explicit unlink, we unlink all later - * urbs. usb spec requires that. + * urbs. usb spec requires that for faults... */ if (unlink && urb->status == -EINPROGRESS) urb->status = -ECONNRESET; @@ -330,31 +328,7 @@ qtd_copy_status (urb, qtd->length, token); spin_unlock (&urb->lock); - /* - * NOTE: this won't work right with interrupt urbs that - * need multiple qtds ... only the first scan of qh->qtd_list - * starts at the right qtd, yet multiple scans could happen - * for transfers that are scheduled across multiple uframes. - * (Such schedules are not currently allowed!) - */ - if (likely (freeing != 0)) - list_del (&qtd->qtd_list); - else { - /* restore everything the HC could change - * from an interrupt QTD - */ - qtd->hw_token = (qtd->hw_token - & __constant_cpu_to_le32 (0x8300)) - | cpu_to_le32 (qtd->length << 16) - | __constant_cpu_to_le32 (QTD_STS_ACTIVE - | (EHCI_TUNE_CERR << 10)); - qtd->hw_buf [0] &= ~__constant_cpu_to_le32 (0x0fff); - - /* this offset, and the length above, - * are likely wrong on QTDs #2..N - */ - qtd->hw_buf [0] |= cpu_to_le32 (0x0fff & qtd->buf_dma); - } + list_del (&qtd->qtd_list); #if 0 if (urb->status == -EINPROGRESS) @@ -365,12 +339,6 @@ urb, urb->status, qtd, token, urb->actual_length); #endif - - /* SETUP for control urb? */ - if (unlikely (QTD_PID (token) == 2)) - pci_unmap_single (ehci->hcd.pdev, - qtd->buf_dma, sizeof (struct usb_ctrlrequest), - PCI_DMA_TODEVICE); } /* patch up list head? */ @@ -382,14 +350,9 @@ /* last urb's completion might still need calling */ if (likely (last != 0)) { - if (likely (freeing != 0)) { - ehci_urb_done (ehci, last->buf_dma, last->urb); - ehci_qtd_free (ehci, last); - } else - ehci_urb_complete (ehci, last->buf_dma, last->urb); - retval++; + ehci_urb_done (ehci, last->urb); + ehci_qtd_free (ehci, last); } - return retval; } /*-------------------------------------------------------------------------*/ @@ -428,10 +391,6 @@ size = qtd->urb->transfer_buffer_length; unmapped++; } - if (qtd->buf_dma) - pci_unmap_single (ehci->hcd.pdev, - qtd->buf_dma, - size, direction); } ehci_qtd_free (ehci, qtd); } @@ -448,8 +407,9 @@ int flags ) { struct ehci_qtd *qtd, *qtd_prev; - dma_addr_t buf, map_buf; + dma_addr_t buf; int len, maxpacket; + int is_input; u32 token; /* @@ -467,17 +427,8 @@ /* for split transactions, SplitXState initialized to zero */ if (usb_pipecontrol (urb->pipe)) { - /* control request data is passed in the "setup" pid */ - qtd->buf_dma = pci_map_single ( - ehci->hcd.pdev, - urb->setup_packet, - sizeof (struct usb_ctrlrequest), - PCI_DMA_TODEVICE); - if (unlikely (!qtd->buf_dma)) - goto cleanup; - /* SETUP pid */ - qtd_fill (qtd, qtd->buf_dma, sizeof (struct usb_ctrlrequest), + qtd_fill (qtd, urb->setup_dma, sizeof (struct usb_ctrlrequest), token | (2 /* "setup" */ << 8)); /* ... and always at least one more pid */ @@ -495,23 +446,17 @@ * data transfer stage: buffer setup */ len = urb->transfer_buffer_length; - if (likely (len > 0)) { - buf = map_buf = pci_map_single (ehci->hcd.pdev, - urb->transfer_buffer, len, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - if (unlikely (!buf)) - goto cleanup; - } else - buf = map_buf = 0; + is_input = usb_pipein (urb->pipe); + if (likely (len > 0)) + buf = urb->transfer_dma; + else + buf = 0; - if (!buf || usb_pipein (urb->pipe)) + if (!buf || is_input) token |= (1 /* "in" */ << 8); /* else it's already initted to "out" pid (0 << 8) */ - maxpacket = usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe)); + maxpacket = usb_maxpacket (urb->dev, urb->pipe, !is_input) & 0x03ff; /* * buffer gets wrapped in one or more qtds; @@ -522,7 +467,6 @@ int this_qtd_len; qtd->urb = urb; - qtd->buf_dma = map_buf; this_qtd_len = qtd_fill (qtd, buf, len, token); len -= this_qtd_len; buf += this_qtd_len; @@ -607,6 +551,11 @@ // That'd mean updating how usbcore talks to HCDs. (2.5?) +// high bandwidth multiplier, as encoded in highspeed endpoint descriptors +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) +// ... and packet size, for any kind of endpoint descriptor +#define max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x03ff) + /* * Each QH holds a qtd list; a QH is used for everything except iso. * @@ -624,6 +573,8 @@ ) { struct ehci_qh *qh = ehci_qh_alloc (ehci, flags); u32 info1 = 0, info2 = 0; + int is_input, type; + int maxp = 0; if (!qh) return qh; @@ -634,6 +585,53 @@ info1 |= usb_pipeendpoint (urb->pipe) << 8; info1 |= usb_pipedevice (urb->pipe) << 0; + is_input = usb_pipein (urb->pipe); + type = usb_pipetype (urb->pipe); + maxp = usb_maxpacket (urb->dev, urb->pipe, !is_input); + + /* Compute interrupt scheduling parameters just once, and save. + * - allowing for high bandwidth, how many nsec/uframe are used? + * - split transactions need a second CSPLIT uframe; same question + * - splits also need a schedule gap (for full/low speed I/O) + * - qh has a polling interval + * + * For control/bulk requests, the HC or TT handles these. + */ + if (type == PIPE_INTERRUPT) { + qh->usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0, + hb_mult (maxp) * max_packet (maxp)); + qh->start = NO_FRAME; + + if (urb->dev->speed == USB_SPEED_HIGH) { + qh->c_usecs = 0; + qh->gap_uf = 0; + + /* FIXME handle HS periods of less than 1 frame. */ + qh->period = urb->interval >> 3; + if (qh->period < 1) { + dbg ("intr period %d uframes, NYET!", + urb->interval); + qh = 0; + goto done; + } + } else { + /* gap is f(FS/LS transfer times) */ + qh->gap_uf = 1 + usb_calc_bus_time (urb->dev->speed, + is_input, 0, maxp) / (125 * 1000); + + /* FIXME this just approximates SPLIT/CSPLIT times */ + if (is_input) { // SPLIT, gap, CSPLIT+DATA + qh->c_usecs = qh->usecs + HS_USECS (0); + qh->usecs = HS_USECS (1); + } else { // SPLIT+DATA, gap, CSPLIT + qh->usecs += HS_USECS (1); + qh->c_usecs = HS_USECS (0); + } + + qh->period = urb->interval; + } + } + /* using TT? */ switch (urb->dev->speed) { case USB_SPEED_LOW: @@ -643,67 +641,63 @@ case USB_SPEED_FULL: /* EPS 0 means "full" */ info1 |= (EHCI_TUNE_RL_TT << 28); - if (usb_pipecontrol (urb->pipe)) { + if (type == PIPE_CONTROL) { info1 |= (1 << 27); /* for TT */ info1 |= 1 << 14; /* toggle from qtd */ } - info1 |= usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe)) << 16; + info1 |= maxp << 16; info2 |= (EHCI_TUNE_MULT_TT << 30); info2 |= urb->dev->ttport << 23; info2 |= urb->dev->tt->hub->devnum << 16; - /* NOTE: if (usb_pipeint (urb->pipe)) { scheduler sets c-mask } - * ... and a 0.96 scheduler might use FSTN nodes too - */ + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets c-mask } */ + break; case USB_SPEED_HIGH: /* no TT involved */ info1 |= (2 << 12); /* EPS "high" */ info1 |= (EHCI_TUNE_RL_HS << 28); - if (usb_pipecontrol (urb->pipe)) { + if (type == PIPE_CONTROL) { info1 |= 64 << 16; /* usb2 fixed maxpacket */ info1 |= 1 << 14; /* toggle from qtd */ info2 |= (EHCI_TUNE_MULT_HS << 30); - } else if (usb_pipebulk (urb->pipe)) { + } else if (type == PIPE_BULK) { info1 |= 512 << 16; /* usb2 fixed maxpacket */ info2 |= (EHCI_TUNE_MULT_HS << 30); - } else { - u32 temp; - temp = usb_maxpacket (urb->dev, urb->pipe, - usb_pipeout (urb->pipe)); - info1 |= (temp & 0x3ff) << 16; /* maxpacket */ - /* HS intr can be "high bandwidth" */ - temp = 1 + ((temp >> 11) & 0x03); - info2 |= temp << 30; /* mult */ + } else { /* PIPE_INTERRUPT */ + info1 |= max_packet (maxp) << 16; + info2 |= hb_mult (maxp) << 30; } break; - default: #ifdef DEBUG + default: BUG (); #endif } - /* NOTE: if (usb_pipeint (urb->pipe)) { scheduler sets s-mask } */ + /* NOTE: if (PIPE_INTERRUPT) { scheduler sets s-mask } */ qh->qh_state = QH_STATE_IDLE; qh->hw_info1 = cpu_to_le32 (info1); qh->hw_info2 = cpu_to_le32 (info2); /* initialize sw and hw queues with these qtds */ - list_splice (qtd_list, &qh->qtd_list); - qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list)); + if (!list_empty (qtd_list)) { + list_splice (qtd_list, &qh->qtd_list); + qh_update (qh, list_entry (qtd_list->next, struct ehci_qtd, qtd_list)); + } else { + qh->hw_qtd_next = qh->hw_alt_next = EHCI_LIST_END; + } /* initialize data toggle state */ - if (!usb_pipecontrol (urb->pipe)) - clear_toggle (urb->dev, - usb_pipeendpoint (urb->pipe), - usb_pipeout (urb->pipe), - qh); + clear_toggle (urb->dev, usb_pipeendpoint (urb->pipe), !is_input, qh); +done: return qh; } +#undef hb_mult +#undef hb_packet /*-------------------------------------------------------------------------*/ @@ -745,50 +739,48 @@ /*-------------------------------------------------------------------------*/ -static int -submit_async ( +/* + * For control/bulk/interrupt, return QH with these TDs appended. + * Allocates and initializes the QH if necessary. + * Returns null if it can't allocate a QH it needs to. + * If the QH has TDs (urbs) already, that's great. + */ +static struct ehci_qh *qh_append_tds ( struct ehci_hcd *ehci, struct urb *urb, struct list_head *qtd_list, - int mem_flags -) { - struct ehci_qtd *qtd; - struct hcd_dev *dev; - int epnum; - unsigned long flags; + int epnum, + void **ptr +) +{ struct ehci_qh *qh = 0; - qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); - dev = (struct hcd_dev *)urb->dev->hcpriv; - epnum = usb_pipeendpoint (urb->pipe); - if (usb_pipein (urb->pipe)) - epnum |= 0x10; - - vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]", - ehci->hcd.self.bus_name, urb, urb->transfer_buffer_length, - epnum & 0x0f, (epnum & 0x10) ? "in" : "out", - qtd, dev ? dev->ep [epnum] : (void *)~0); - - spin_lock_irqsave (&ehci->lock, flags); - - qh = (struct ehci_qh *) dev->ep [epnum]; + qh = (struct ehci_qh *) *ptr; if (likely (qh != 0)) { - u32 hw_next = QTD_NEXT (qtd->qtd_dma); + struct ehci_qtd *qtd; + + if (unlikely (list_empty (qtd_list))) + qtd = 0; + else + qtd = list_entry (qtd_list->next, struct ehci_qtd, + qtd_list); /* maybe patch the qh used for set_address */ if (unlikely (epnum == 0 && le32_to_cpu (qh->hw_info1 & 0x7f) == 0)) qh->hw_info1 |= cpu_to_le32 (usb_pipedevice(urb->pipe)); - /* is an URB is queued to this qh already? */ - if (unlikely (!list_empty (&qh->qtd_list))) { + /* append to tds already queued to this qh? */ + if (unlikely (!list_empty (&qh->qtd_list) && qtd)) { struct ehci_qtd *last_qtd; int short_rx = 0; + u32 hw_next; /* update the last qtd's "next" pointer */ // dbg_qh ("non-empty qh", ehci, qh); last_qtd = list_entry (qh->qtd_list.prev, struct ehci_qtd, qtd_list); + hw_next = QTD_NEXT (qtd->qtd_dma); last_qtd->hw_next = hw_next; /* previous urb allows short rx? maybe optimize. */ @@ -803,6 +795,7 @@ * Interrupt code must cope with case of HC having it * cached, and clobbering these updates. * ... complicates getting rid of extra interrupts! + * (Or: use dummy td, so cache always stays valid.) */ if (qh->hw_current == cpu_to_le32 (last_qtd->qtd_dma)) { wmb (); @@ -822,31 +815,61 @@ */ /* usb_clear_halt() means qh data toggle gets reset */ - if (usb_pipebulk (urb->pipe) - && unlikely (!usb_gettoggle (urb->dev, + if (unlikely (!usb_gettoggle (urb->dev, (epnum & 0x0f), !(epnum & 0x10)))) { clear_toggle (urb->dev, epnum & 0x0f, !(epnum & 0x10), qh); } - qh_update (qh, qtd); + if (qtd) + qh_update (qh, qtd); } list_splice (qtd_list, qh->qtd_list.prev); } else { /* can't sleep here, we have ehci->lock... */ qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC); - if (likely (qh != 0)) { - // dbg_qh ("new qh", ehci, qh); - dev->ep [epnum] = qh; - } + // if (qh) dbg_qh ("new qh", ehci, qh); + *ptr = qh; } + if (qh) + urb->hcpriv = qh_get (qh); + return qh; +} + +/*-------------------------------------------------------------------------*/ + +static int +submit_async ( + struct ehci_hcd *ehci, + struct urb *urb, + struct list_head *qtd_list, + int mem_flags +) { + struct ehci_qtd *qtd; + struct hcd_dev *dev; + int epnum; + unsigned long flags; + struct ehci_qh *qh = 0; + + qtd = list_entry (qtd_list->next, struct ehci_qtd, qtd_list); + dev = (struct hcd_dev *)urb->dev->hcpriv; + epnum = usb_pipeendpoint (urb->pipe); + if (usb_pipein (urb->pipe)) + epnum |= 0x10; + + vdbg ("%s: submit_async urb %p len %d ep %d-%s qtd %p [qh %p]", + ehci->hcd.self.bus_name, urb, urb->transfer_buffer_length, + epnum & 0x0f, (epnum & 0x10) ? "in" : "out", + qtd, dev ? dev->ep [epnum] : (void *)~0); + + spin_lock_irqsave (&ehci->lock, flags); + qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]); /* Control/bulk operations through TTs don't need scheduling, * the HC and TT handle it when the TT has a buffer ready. */ if (likely (qh != 0)) { - urb->hcpriv = qh_get (qh); if (likely (qh->qh_state == QH_STATE_IDLE)) qh_link_async (ehci, qh_get (qh)); } @@ -873,7 +896,7 @@ ehci->reclaim = 0; ehci->reclaim_ready = 0; - qh_completions (ehci, qh, 1); + qh_completions (ehci, qh); // unlink any urb should now unlink all following urbs, so that // relinking only happens for urbs before the unlinked ones. @@ -973,13 +996,15 @@ spin_unlock_irqrestore (&ehci->lock, flags); /* concurrent unlink could happen here */ - qh_completions (ehci, qh, 1); + qh_completions (ehci, qh); spin_lock_irqsave (&ehci->lock, flags); qh_put (ehci, qh); } - /* unlink idle entries (reduces PCI usage) */ + /* unlink idle entries, reducing HC PCI usage as + * well as HCD schedule-scanning costs + */ if (list_empty (&qh->qtd_list) && !ehci->reclaim) { if (qh->qh_next.qh != qh) { // dbg ("irq/empty"); @@ -987,6 +1012,7 @@ } else { // FIXME: arrange to stop // after it's been idle a while. + // stop/restart isn't free... } } qh = qh->qh_next.qh; diff -Nru a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c --- a/drivers/usb/host/ehci-sched.c Fri Aug 16 14:34:56 2002 +++ b/drivers/usb/host/ehci-sched.c Fri Aug 16 14:34:56 2002 @@ -220,31 +220,31 @@ /*-------------------------------------------------------------------------*/ +// FIXME microframe periods not yet handled + static void intr_deschedule ( struct ehci_hcd *ehci, - unsigned frame, struct ehci_qh *qh, - unsigned period + int wait ) { unsigned long flags; int status; - - period >>= 3; // FIXME microframe periods not handled yet + unsigned frame = qh->start; spin_lock_irqsave (&ehci->lock, flags); do { periodic_unlink (ehci, frame, qh); qh_put (ehci, qh); - frame += period; + frame += qh->period; } while (frame < ehci->periodic_size); qh->qh_state = QH_STATE_UNLINK; qh->qh_next.ptr = 0; - ehci->periodic_urbs--; + ehci->periodic_sched--; /* maybe turn off periodic schedule */ - if (!ehci->periodic_urbs) + if (!ehci->periodic_sched) status = disable_periodic (ehci); else { status = 0; @@ -258,21 +258,35 @@ * (yeech!) to be sure it's done. * No other threads may be mucking with this qh. */ - if (!status && ((ehci_get_frame (&ehci->hcd) - frame) % period) == 0) - udelay (125); + if (((ehci_get_frame (&ehci->hcd) - frame) % qh->period) == 0) { + if (wait) { + udelay (125); + qh->hw_next = EHCI_LIST_END; + } else { + /* we may not be IDLE yet, but if the qh is empty + * the race is very short. then if qh also isn't + * rescheduled soon, it won't matter. otherwise... + */ + vdbg ("intr_deschedule..."); + } + } else + qh->hw_next = EHCI_LIST_END; qh->qh_state = QH_STATE_IDLE; - qh->hw_next = EHCI_LIST_END; + + /* update per-qh bandwidth utilization (for usbfs) */ + ehci->hcd.self.bandwidth_allocated -= + (qh->usecs + qh->c_usecs) / qh->period; vdbg ("descheduled qh %p, per = %d frame = %d count = %d, urbs = %d", - qh, period, frame, - atomic_read (&qh->refcount), ehci->periodic_urbs); + qh, qh->period, frame, + atomic_read (&qh->refcount), ehci->periodic_sched); } static int check_period ( struct ehci_hcd *ehci, unsigned frame, - int uframe, + unsigned uframe, unsigned period, unsigned usecs ) { @@ -309,19 +323,142 @@ return 1; } +static int check_intr_schedule ( + struct ehci_hcd *ehci, + unsigned frame, + unsigned uframe, + const struct ehci_qh *qh, + u32 *c_maskp +) +{ + int retval = -ENOSPC; + + if (!check_period (ehci, frame, uframe, qh->period, qh->usecs)) + goto done; + if (!qh->c_usecs) { + retval = 0; + *c_maskp = cpu_to_le32 (0); + goto done; + } + + /* This is a split transaction; check the bandwidth available for + * the completion too. Check both worst and best case gaps: worst + * case is SPLIT near uframe end, and CSPLIT near start ... best is + * vice versa. Difference can be almost two uframe times, but we + * reserve unnecessary bandwidth (waste it) this way. (Actually + * even better cases exist, like immediate device NAK.) + * + * FIXME don't even bother unless we know this TT is idle in that + * range of uframes ... for now, check_period() allows only one + * interrupt transfer per frame, so needn't check "TT busy" status + * when scheduling a split (QH, SITD, or FSTN). + * + * FIXME ehci 0.96 and above can use FSTNs + */ + if (!check_period (ehci, frame, uframe + qh->gap_uf + 1, + qh->period, qh->c_usecs)) + goto done; + if (!check_period (ehci, frame, uframe + qh->gap_uf, + qh->period, qh->c_usecs)) + goto done; + + *c_maskp = cpu_to_le32 (0x03 << (8 + uframe + qh->gap_uf)); + retval = 0; +done: + return retval; +} + +static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh) +{ + int status; + unsigned uframe; + u32 c_mask; + unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */ + + qh->hw_next = EHCI_LIST_END; + frame = qh->start; + + /* reuse the previous schedule slots, if we can */ + if (frame < qh->period) { + uframe = ffs (le32_to_cpup (&qh->hw_info2) & 0x00ff); + status = check_intr_schedule (ehci, frame, --uframe, + qh, &c_mask); + } else { + uframe = 0; + c_mask = 0; + status = -ENOSPC; + } + + /* else scan the schedule to find a group of slots such that all + * uframes have enough periodic bandwidth available. + */ + if (status) { + frame = qh->period - 1; + do { + for (uframe = 0; uframe < 8; uframe++) { + status = check_intr_schedule (ehci, + frame, uframe, qh, + &c_mask); + if (status == 0) + break; + } + } while (status && --frame); + if (status) + goto done; + qh->start = frame; + + /* reset S-frame and (maybe) C-frame masks */ + qh->hw_info2 &= ~0xffff; + qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask; + } else + dbg ("reused previous qh %p schedule", qh); + + /* stuff into the periodic schedule */ + qh->qh_state = QH_STATE_LINKED; + dbg ("qh %p usecs %d/%d period %d.0 starting %d.%d (gap %d)", + qh, qh->usecs, qh->c_usecs, + qh->period, frame, uframe, qh->gap_uf); + do { + if (unlikely (ehci->pshadow [frame].ptr != 0)) { + +// FIXME -- just link toward the end, before any qh with a shorter period, +// AND accomodate it already having been linked here (after some other qh) +// AS WELL AS updating the schedule checking logic + + BUG (); + } else { + ehci->pshadow [frame].qh = qh_get (qh); + ehci->periodic [frame] = + QH_NEXT (qh->qh_dma); + } + wmb (); + frame += qh->period; + } while (frame < ehci->periodic_size); + + /* update per-qh bandwidth for usbfs */ + ehci->hcd.self.bandwidth_allocated += + (qh->usecs + qh->c_usecs) / qh->period; + + /* maybe enable periodic schedule processing */ + if (!ehci->periodic_sched++) + status = enable_periodic (ehci); +done: + return status; +} + static int intr_submit ( struct ehci_hcd *ehci, struct urb *urb, struct list_head *qtd_list, int mem_flags ) { - unsigned epnum, period; - unsigned short usecs, c_usecs, gap_uf; + unsigned epnum; unsigned long flags; struct ehci_qh *qh; struct hcd_dev *dev; int is_input; int status = 0; + struct list_head empty; /* get endpoint and transfer/schedule data */ epnum = usb_pipeendpoint (urb->pipe); @@ -329,198 +466,30 @@ if (is_input) epnum |= 0x10; - /* - * HS interrupt transfers are simple -- only one microframe. FS/LS - * interrupt transfers involve a SPLIT in one microframe and CSPLIT - * sometime later. We need to know how much time each will be - * needed in each microframe and, for FS/LS, how many microframes - * separate the two in the best case. - */ - usecs = usb_calc_bus_time (USB_SPEED_HIGH, is_input, 0, - urb->transfer_buffer_length); - if (urb->dev->speed == USB_SPEED_HIGH) { - gap_uf = 0; - c_usecs = 0; - - /* FIXME handle HS periods of less than 1 frame. */ - period = urb->interval >> 3; - if (period < 1) { - dbg ("intr period %d uframes, NYET!", urb->interval); - status = -EINVAL; - goto done; - } - } else { - /* gap is a function of full/low speed transfer times */ - gap_uf = 1 + usb_calc_bus_time (urb->dev->speed, is_input, 0, - urb->transfer_buffer_length) / (125 * 1000); - - /* FIXME this just approximates SPLIT/CSPLIT times */ - if (is_input) { // SPLIT, gap, CSPLIT+DATA - c_usecs = usecs + HS_USECS (0); - usecs = HS_USECS (1); - } else { // SPLIT+DATA, gap, CSPLIT - usecs = usecs + HS_USECS (1); - c_usecs = HS_USECS (0); - } - - period = urb->interval; - } + spin_lock_irqsave (&ehci->lock, flags); + dev = (struct hcd_dev *)urb->dev->hcpriv; - /* - * NOTE: current completion/restart logic doesn't handle more than - * one qtd in a periodic qh ... 16-20 KB/urb is pretty big for this. - * such big requests need many periods to transfer. - * - * FIXME want to change hcd core submit model to expect queuing - * for all transfer types ... not just ISO and (with flag) BULK. - * that means: getting rid of this check; handling the "interrupt - * urb already queued" case below like bulk queuing is handled (no - * errors possible!); and completly getting rid of that annoying - * qh restart logic. simpler/smaller overall, and more flexible. - */ - if (unlikely (qtd_list->next != qtd_list->prev)) { - dbg ("only one intr qtd per urb allowed"); - status = -EINVAL; + /* get qh and force any scheduling errors */ + INIT_LIST_HEAD (&empty); + qh = qh_append_tds (ehci, urb, &empty, epnum, &dev->ep [epnum]); + if (qh == 0) { + status = -ENOMEM; goto done; } - - spin_lock_irqsave (&ehci->lock, flags); - - /* get the qh (must be empty and idle) */ - dev = (struct hcd_dev *)urb->dev->hcpriv; - qh = (struct ehci_qh *) dev->ep [epnum]; - if (qh) { - /* only allow one queued interrupt urb per EP */ - if (unlikely (qh->qh_state != QH_STATE_IDLE - || !list_empty (&qh->qtd_list))) { - dbg ("interrupt urb already queued"); - status = -EBUSY; - } else { - /* maybe reset hardware's data toggle in the qh */ - if (unlikely (!usb_gettoggle (urb->dev, epnum & 0x0f, - !(epnum & 0x10)))) { - qh->hw_token |= - __constant_cpu_to_le32 (QTD_TOGGLE); - usb_settoggle (urb->dev, epnum & 0x0f, - !(epnum & 0x10), 1); - } - /* trust the QH was set up as interrupt ... */ - list_splice (qtd_list, &qh->qtd_list); - qh_update (qh, list_entry (qtd_list->next, - struct ehci_qtd, qtd_list)); - qtd_list = &qh->qtd_list; - } - } else { - /* can't sleep here, we have ehci->lock... */ - qh = ehci_qh_make (ehci, urb, qtd_list, SLAB_ATOMIC); - if (likely (qh != 0)) { - // dbg ("new INTR qh %p", qh); - dev->ep [epnum] = qh; - qtd_list = &qh->qtd_list; - } else - status = -ENOMEM; + if (qh->qh_state == QH_STATE_IDLE) { + if ((status = qh_schedule (ehci, qh)) != 0) + goto done; } - /* Schedule this periodic QH. */ - if (likely (status == 0)) { - unsigned frame = period; - - qh->hw_next = EHCI_LIST_END; - qh->usecs = usecs; - qh->c_usecs = c_usecs; - - urb->hcpriv = qh_get (qh); - status = -ENOSPC; - - /* pick a set of schedule slots, link the QH into them */ - do { - int uframe; - u32 c_mask = 0; + /* then queue the urb's tds to the qh */ + qh = qh_append_tds (ehci, urb, qtd_list, epnum, &dev->ep [epnum]); + BUG_ON (qh == 0); - /* pick a set of slots such that all uframes have - * enough periodic bandwidth available. - */ - frame--; - for (uframe = 0; uframe < 8; uframe++) { - if (check_period (ehci, frame, uframe, - period, usecs) == 0) - continue; - - /* If this is a split transaction, check the - * bandwidth available for the completion - * too. check both best and worst case gaps: - * worst case is SPLIT near uframe end, and - * CSPLIT near start ... best is vice versa. - * Difference can be almost two uframe times. - * - * FIXME don't even bother unless we know - * this TT is idle in that uframe ... right - * now we know only one interrupt transfer - * will be scheduled per frame, so we don't - * need to update/check TT state when we - * schedule a split (QH, SITD, or FSTN). - * - * FIXME ehci 0.96 and above can use FSTNs - */ - if (!c_usecs) - break; - if (check_period (ehci, frame, - uframe + gap_uf, - period, c_usecs) == 0) - continue; - if (check_period (ehci, frame, - uframe + gap_uf + 1, - period, c_usecs) == 0) - continue; + /* ... update usbfs periodic stats */ + ehci->hcd.self.bandwidth_int_reqs++; - c_mask = 0x03 << (8 + uframe + gap_uf); - c_mask = cpu_to_le32 (c_mask); - break; - } - if (uframe == 8) - continue; - - /* QH will run once each period, starting there */ - urb->start_frame = frame; - status = 0; - - /* reset S-frame and (maybe) C-frame masks */ - qh->hw_info2 &= ~0xffff; - qh->hw_info2 |= cpu_to_le32 (1 << uframe) | c_mask; - // dbg_qh ("Schedule INTR qh", ehci, qh); - - /* stuff into the periodic schedule */ - qh->qh_state = QH_STATE_LINKED; - vdbg ("qh %p usecs %d period %d.0 starting %d.%d", - qh, qh->usecs, period, frame, uframe); - do { - if (unlikely (ehci->pshadow [frame].ptr != 0)) { -// FIXME -- just link toward the end, before any qh with a shorter period, -// AND handle it already being (implicitly) linked into this frame -// AS WELL AS updating the check_period() logic - BUG (); - } else { - ehci->pshadow [frame].qh = qh_get (qh); - ehci->periodic [frame] = - QH_NEXT (qh->qh_dma); - } - wmb (); - frame += period; - } while (frame < ehci->periodic_size); - - /* update bandwidth utilization records (for usbfs) */ - usb_claim_bandwidth (urb->dev, urb, - (usecs + c_usecs) / period, 0); - - /* maybe enable periodic schedule processing */ - if (!ehci->periodic_urbs++) - status = enable_periodic (ehci); - break; - - } while (frame); - } - spin_unlock_irqrestore (&ehci->lock, flags); done: + spin_unlock_irqrestore (&ehci->lock, flags); if (status) qtd_list_free (ehci, urb, qtd_list); @@ -534,10 +503,6 @@ struct ehci_qh *qh, unsigned long flags /* caller owns ehci->lock ... */ ) { - struct ehci_qtd *qtd; - struct urb *urb; - int unlinking; - /* nothing to report? */ if (likely ((qh->hw_token & __constant_cpu_to_le32 (QTD_STS_ACTIVE)) != 0)) @@ -547,43 +512,14 @@ return flags; } - qtd = list_entry (qh->qtd_list.next, struct ehci_qtd, qtd_list); - urb = qtd->urb; - unlinking = (urb->status == -ENOENT) || (urb->status == -ECONNRESET); - - /* call any completions, after patching for reactivation */ + /* handle any completions */ spin_unlock_irqrestore (&ehci->lock, flags); - /* NOTE: currently restricted to one qtd per qh! */ - if (qh_completions (ehci, qh, 0) == 0) - urb = 0; + qh_completions (ehci, qh); spin_lock_irqsave (&ehci->lock, flags); - /* never reactivate requests that were unlinked ... */ - if (likely (urb != 0)) { - if (unlinking - || urb->status == -ECONNRESET - || urb->status == -ENOENT - // || (urb->dev == null) - || ehci->hcd.state == USB_STATE_HALT) - urb = 0; - // FIXME look at all those unlink cases ... we always - // need exactly one completion that reports unlink. - // the one above might not have been it! - } - - /* normally reactivate */ - if (likely (urb != 0)) { - if (usb_pipeout (urb->pipe)) - pci_dma_sync_single (ehci->hcd.pdev, - qtd->buf_dma, - urb->transfer_buffer_length, - PCI_DMA_TODEVICE); - urb->status = -EINPROGRESS; - urb->actual_length = 0; + if (unlikely (list_empty (&qh->qtd_list))) + intr_deschedule (ehci, qh, 0); - /* patch qh and restart */ - qh_update (qh, qtd); - } return flags; } @@ -594,11 +530,6 @@ { struct ehci_itd *first_itd = urb->hcpriv; - pci_unmap_single (ehci->hcd.pdev, - first_itd->buf_dma, urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); while (!list_empty (&first_itd->itd_list)) { struct ehci_itd *itd; @@ -694,16 +625,7 @@ int frame_index; struct ehci_itd *first_itd, *itd; int status; - dma_addr_t buf_dma, itd_dma; - - /* set up one dma mapping for this urb */ - buf_dma = pci_map_single (ehci->hcd.pdev, - urb->transfer_buffer, urb->transfer_buffer_length, - usb_pipein (urb->pipe) - ? PCI_DMA_FROMDEVICE - : PCI_DMA_TODEVICE); - if (buf_dma == 0) - return -ENOMEM; + dma_addr_t itd_dma; /* allocate/init ITDs */ for (frame_index = 0, first_itd = 0; @@ -717,7 +639,8 @@ memset (itd, 0, sizeof *itd); itd->itd_dma = itd_dma; - status = itd_fill (ehci, itd, urb, frame_index, buf_dma); + status = itd_fill (ehci, itd, urb, frame_index, + urb->transfer_dma); if (status != 0) goto fail; @@ -806,7 +729,7 @@ /* calculate the legal range [start,max) */ now = readl (&ehci->regs->frame_index) + 1; /* next uframe */ - if (!ehci->periodic_urbs) + if (!ehci->periodic_sched) now += 8; /* startup delay */ now %= mod; end = now + mod; @@ -926,7 +849,7 @@ usb_claim_bandwidth (urb->dev, urb, usecs, 1); /* maybe enable periodic schedule processing */ - if (!ehci->periodic_urbs++) { + if (!ehci->periodic_sched++) { if ((status = enable_periodic (ehci)) != 0) { // FIXME deschedule right away err ("itd_schedule, enable = %d", status); @@ -1009,8 +932,8 @@ spin_lock_irqsave (&ehci->lock, flags); /* defer stopping schedule; completion can submit */ - ehci->periodic_urbs--; - if (!ehci->periodic_urbs) + ehci->periodic_sched--; + if (!ehci->periodic_sched) (void) disable_periodic (ehci); return flags; diff -Nru a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h --- a/drivers/usb/host/ehci.h Fri Aug 16 14:35:00 2002 +++ b/drivers/usb/host/ehci.h Fri Aug 16 14:35:00 2002 @@ -50,7 +50,7 @@ union ehci_shadow *pshadow; /* mirror hw periodic table */ int next_uframe; /* scan periodic, start here */ - unsigned periodic_urbs; /* how many urbs scheduled? */ + unsigned periodic_sched; /* periodic activity count */ /* deferred work from IRQ, etc */ struct tasklet_struct tasklet; @@ -72,7 +72,7 @@ }; /* unwrap an HCD pointer to get an EHCI_HCD pointer */ -#define hcd_to_ehci(hcd_ptr) list_entry(hcd_ptr, struct ehci_hcd, hcd) +#define hcd_to_ehci(hcd_ptr) container_of(hcd_ptr, struct ehci_hcd, hcd) /* NOTE: urb->transfer_flags expected to not use this bit !!! */ #define EHCI_STATE_UNLINK 0x8000 /* urb being unlinked */ @@ -219,7 +219,6 @@ /* dma same in urb's qtds, except 1st control qtd (setup buffer) */ struct urb *urb; /* qtd's urb */ - dma_addr_t buf_dma; /* buffer address */ size_t length; /* length of buffer */ } __attribute__ ((aligned (32))); @@ -287,12 +286,20 @@ struct list_head qtd_list; /* sw qtd list */ atomic_t refcount; - unsigned short usecs; /* intr bandwidth */ - unsigned short c_usecs; /* ... split completion bw */ - short qh_state; + + u8 qh_state; #define QH_STATE_LINKED 1 /* HC sees this */ #define QH_STATE_UNLINK 2 /* HC may still see this */ #define QH_STATE_IDLE 3 /* HC doesn't see this */ + + /* periodic schedule info */ + u8 usecs; /* intr bandwidth */ + u8 gap_uf; /* uframes split/csplit gap */ + u8 c_usecs; /* ... split completion bw */ + unsigned short period; /* polling interval */ + unsigned short start; /* where polling starts */ +#define NO_FRAME ((unsigned short)~0) /* pick new start */ + } __attribute__ ((aligned (32))); /*-------------------------------------------------------------------------*/ diff -Nru a/drivers/usb/host/ohci-q.c b/drivers/usb/host/ohci-q.c --- a/drivers/usb/host/ohci-q.c Fri Aug 16 14:35:00 2002 +++ b/drivers/usb/host/ohci-q.c Fri Aug 16 14:35:00 2002 @@ -14,27 +14,8 @@ if (last >= 0) { int i; - struct td *td = urb_priv->td [0]; - int len = td->urb->transfer_buffer_length; - int dir = usb_pipeout (td->urb->pipe) - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE; - - /* unmap CTRL URB setup buffer (always td 0) */ - if (usb_pipecontrol (td->urb->pipe)) { - pci_unmap_single (hc->hcd.pdev, - td->data_dma, 8, PCI_DMA_TODEVICE); - - /* CTRL data buffer starts at td 1 if len > 0 */ - if (len && last > 0) - td = urb_priv->td [1]; - } - /* else: ISOC, BULK, INTR data buffer starts at td 0 */ - - /* unmap data buffer */ - if (len && td->data_dma) - pci_unmap_single (hc->hcd.pdev, - td->data_dma, len, dir); + struct td *td; + for (i = 0; i <= last; i++) { td = urb_priv->td [i]; if (td) @@ -85,15 +66,8 @@ struct urb_priv *urb_priv = urb->hcpriv; unsigned long flags; -// FIXME rewrite this resubmit path. use pci_dma_sync_single() -// and requeue more cheaply, and only if needed. -// Better yet ... abolish the notion of automagic resubmission. - pci_unmap_single (hc->hcd.pdev, - urb_priv->td [0]->data_dma, - urb->transfer_buffer_length, - usb_pipeout (urb->pipe) - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE); +// FIXME going away along with the rest of interrrupt automagic... + /* FIXME: MP race. If another CPU partially unlinks * this URB (urb->status was updated, hasn't yet told * us to dequeue) before we call complete() here, an @@ -612,13 +586,9 @@ urb_priv->td_cnt = 0; - if (data_len) { - data = pci_map_single (ohci->hcd.pdev, - urb->transfer_buffer, data_len, - is_out - ? PCI_DMA_TODEVICE - : PCI_DMA_FROMDEVICE); - } else + if (data_len) + data = urb->transfer_dma; + else data = 0; /* NOTE: TD_CC is set so we can tell which TDs the HC processed by @@ -665,11 +635,7 @@ */ case PIPE_CONTROL: info = TD_CC | TD_DP_SETUP | TD_T_DATA0; - td_fill (info, - pci_map_single (ohci->hcd.pdev, - urb->setup_packet, 8, - PCI_DMA_TODEVICE), - 8, urb, cnt++); + td_fill (info, urb->setup_dma, 8, urb, cnt++); if (data_len > 0) { info = TD_CC | TD_R | TD_T_DATA1; info |= is_out ? TD_DP_OUT : TD_DP_IN; @@ -938,7 +904,7 @@ /* ED's now officially unlinked, hc doesn't see */ ed->state = ED_IDLE; ed->hwINFO &= ~ED_SKIP; - ed->hwHeadP &= ~cpu_to_le32 (ED_H); + ed->hwHeadP &= ~ED_H; ed->hwNextED = 0; /* but if there's work queued, reschedule */ diff -Nru a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c --- a/drivers/usb/host/ohci-sa1111.c Fri Aug 16 14:34:58 2002 +++ b/drivers/usb/host/ohci-sa1111.c Fri Aug 16 14:34:58 2002 @@ -161,6 +161,12 @@ hcd->regs = (void *) &USB_OHCI_OP_BASE; hcd->pdev = SA1111_FAKE_PCIDEV; + retval = hcd_buffer_create (hcd); + if (retval != 0) { + dbg ("pool alloc fail"); + goto err1; + } + set_irq_type(NIRQHCIM, IRQT_RISING); retval = request_irq (NIRQHCIM, usb_hcd_sa1111_hcim_irq, SA_INTERRUPT, hcd->description, hcd); @@ -193,6 +199,7 @@ return 0; err2: + hcd_buffer_destroy (hcd); if (hcd) driver->hcd_free(hcd); err1: sa1111_stop_hc(); @@ -233,6 +240,7 @@ hcd->state = USB_STATE_HALT; free_irq (hcd->irq, hcd); + hcd_buffer_destroy (hcd); usb_deregister_bus (&hcd->self); if (atomic_read (&hcd->self.refcnt) != 1) diff -Nru a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c --- a/drivers/usb/host/uhci-hcd.c Fri Aug 16 14:35:00 2002 +++ b/drivers/usb/host/uhci-hcd.c Fri Aug 16 14:35:00 2002 @@ -646,23 +646,6 @@ urb->hcpriv = urbp; - if (urb->transfer_buffer_length) { - urbp->transfer_buffer_dma_handle = pci_map_single(uhci->dev, - urb->transfer_buffer, urb->transfer_buffer_length, - usb_pipein(urb->pipe) ? PCI_DMA_FROMDEVICE : - PCI_DMA_TODEVICE); - if (!urbp->transfer_buffer_dma_handle) - return NULL; - } - - if (usb_pipetype(urb->pipe) == PIPE_CONTROL && urb->setup_packet) { - urbp->setup_packet_dma_handle = pci_map_single(uhci->dev, - urb->setup_packet, sizeof(struct usb_ctrlrequest), - PCI_DMA_TODEVICE); - if (!urbp->setup_packet_dma_handle) - return NULL; - } - return urbp; } @@ -721,19 +704,6 @@ uhci_free_td(uhci, td); } - if (urbp->setup_packet_dma_handle) { - pci_unmap_single(uhci->dev, urbp->setup_packet_dma_handle, - sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); - urbp->setup_packet_dma_handle = 0; - } - - if (urbp->transfer_buffer_dma_handle) { - pci_unmap_single(uhci->dev, urbp->transfer_buffer_dma_handle, - urb->transfer_buffer_length, usb_pipein(urb->pipe) ? - PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); - urbp->transfer_buffer_dma_handle = 0; - } - urb->hcpriv = NULL; kmem_cache_free(uhci_up_cachep, urbp); } @@ -813,7 +783,7 @@ unsigned long destination, status; int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); int len = urb->transfer_buffer_length; - dma_addr_t data = urbp->transfer_buffer_dma_handle; + dma_addr_t data = urb->transfer_dma; /* The "pipe" thing contains the destination in bits 8--18 */ destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; @@ -832,7 +802,7 @@ uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(7), - urbp->setup_packet_dma_handle); + urb->setup_dma); /* * If direction is "send", change the frame from SETUP (0x2D) @@ -1072,7 +1042,6 @@ { struct uhci_td *td; unsigned long destination, status; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) return -EINVAL; @@ -1094,7 +1063,7 @@ usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); uhci_add_td_to_urb(urb, td); - uhci_fill_td(td, status, destination, urbp->transfer_buffer_dma_handle); + uhci_fill_td(td, status, destination, urb->transfer_dma); uhci_insert_td(uhci, uhci->skeltd[__interval_to_skel(urb->interval)], td); @@ -1196,7 +1165,7 @@ int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); int len = urb->transfer_buffer_length; struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - dma_addr_t data = urbp->transfer_buffer_dma_handle; + dma_addr_t data = urb->transfer_dma; if (len < 0) return -EINVAL; @@ -1358,7 +1327,6 @@ struct uhci_td *td; int i, ret, frame; int status, destination; - struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; status = TD_CTRL_ACTIVE | TD_CTRL_IOS; destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); @@ -1378,7 +1346,7 @@ uhci_add_td_to_urb(urb, td); uhci_fill_td(td, status, destination | uhci_explen(urb->iso_frame_desc[i].length - 1), - urbp->transfer_buffer_dma_handle + urb->iso_frame_desc[i].offset); + urb->transfer_dma + urb->iso_frame_desc[i].offset); if (i + 1 >= urb->number_of_packets) td->status |= cpu_to_le32(TD_CTRL_IOC); @@ -1831,15 +1799,6 @@ urb->status == -ECONNRESET); resubmit_interrupt = (usb_pipetype(urb->pipe) == PIPE_INTERRUPT && urb->interval); - - if (urbp->transfer_buffer_dma_handle) - pci_dma_sync_single(uhci->dev, urbp->transfer_buffer_dma_handle, - urb->transfer_buffer_length, usb_pipein(urb->pipe) ? - PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); - - if (urbp->setup_packet_dma_handle) - pci_dma_sync_single(uhci->dev, urbp->setup_packet_dma_handle, - sizeof(struct usb_ctrlrequest), PCI_DMA_TODEVICE); status = urbp->status; if (!resubmit_interrupt || killed) diff -Nru a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h --- a/drivers/usb/host/uhci-hcd.h Fri Aug 16 14:34:59 2002 +++ b/drivers/usb/host/uhci-hcd.h Fri Aug 16 14:34:59 2002 @@ -338,9 +338,6 @@ struct urb *urb; struct usb_device *dev; - dma_addr_t setup_packet_dma_handle; - dma_addr_t transfer_buffer_dma_handle; - struct uhci_qh *qh; /* QH for this URB */ struct list_head td_list; /* P: urb->lock */ diff -Nru a/drivers/usb/media/konicawc.c b/drivers/usb/media/konicawc.c --- a/drivers/usb/media/konicawc.c Fri Aug 16 14:34:51 2002 +++ b/drivers/usb/media/konicawc.c Fri Aug 16 14:34:51 2002 @@ -1,6 +1,4 @@ /* - * $Id$ - * * konicawc.c - konica webcam driver * * Author: Simon Evans @@ -8,7 +6,7 @@ * Copyright (C) 2002 Simon Evans * * Licence: GPL - * + * * Driver for USB webcams based on Konica chipset. This * chipset is used in Intel YC76 camera. * @@ -18,6 +16,7 @@ #include #include + #include "usbvideo.h" #define MAX_BRIGHTNESS 108 @@ -26,9 +25,11 @@ #define MAX_SHARPNESS 108 #define MAX_WHITEBAL 372 #define MAX_SPEED 6 + + #define MAX_CAMERAS 1 -#define DRIVER_VERSION "v1.1" +#define DRIVER_VERSION "v1.3" #define DRIVER_DESC "Konica Webcam driver" enum ctrl_req { @@ -41,18 +42,32 @@ enum frame_sizes { - SIZE_160X136 = 0, - SIZE_176X144 = 1, - SIZE_320X240 = 2, + SIZE_160X120 = 0, + SIZE_160X136 = 1, + SIZE_176X144 = 2, + SIZE_320X240 = 3, + }; +#define MAX_FRAME_SIZE SIZE_320X240 static usbvideo_t *cams; +#ifdef CONFIG_USB_DEBUG +static int debug; +#define DEBUG(n, format, arg...) \ + if (n <= debug) { \ + printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \ + } +#else +#define DEBUG(n, arg...) +static const int debug = 0; +#endif + + /* Some default values for inital camera settings, can be set by modprobe */ -static int debug; static enum frame_sizes size; static int speed = 6; /* Speed (fps) 0 (slowest) to 6 (fastest) */ static int brightness = MAX_BRIGHTNESS/2; @@ -61,31 +76,36 @@ static int sharpness = MAX_SHARPNESS/2; static int whitebal = 3*(MAX_WHITEBAL/4); -static int speed_to_interface[] = { 1, 0, 3, 2, 4, 5, 6 }; +static int spd_to_iface[] = { 1, 0, 3, 2, 4, 5, 6 }; /* These FPS speeds are from the windows config box. They are * indexed on size (0-2) and speed (0-6). Divide by 3 to get the * real fps. */ -static int speed_to_fps[3][7] = { { 24, 40, 48, 60, 72, 80, 100 }, - { 18, 30, 36, 45, 54, 60, 75 }, - { 6, 10, 12, 15, 18, 20, 25 } }; - - -static int camera_sizes[][2] = { { 160, 136 }, - { 176, 144 }, - { 320, 240 }, - { } /* List terminator */ +static int spd_to_fps[][7] = { { 24, 40, 48, 60, 72, 80, 100 }, + { 24, 40, 48, 60, 72, 80, 100 }, + { 18, 30, 36, 45, 54, 60, 75 }, + { 6, 10, 12, 15, 18, 21, 25 } }; + +struct cam_size { + u16 width; + u16 height; + u8 cmd; }; +static struct cam_size camera_sizes[] = { { 160, 120, 0x7 }, + { 160, 136, 0xa }, + { 176, 144, 0x4 }, + { 320, 240, 0x5 } }; + struct konicawc { u8 brightness; /* camera uses 0 - 9, x11 for real value */ u8 contrast; /* as above */ u8 saturation; /* as above */ u8 sharpness; /* as above */ u8 white_bal; /* 0 - 33, x11 for real value */ - u8 speed; /* Stored as 0 - 6, used as index in speed_to_* (above) */ + u8 speed; /* Stored as 0 - 6, used as index in spd_to_* (above) */ u8 size; /* Frame Size */ int height; int width; @@ -93,6 +113,10 @@ u8 sts_buf[USBVIDEO_NUMSBUF][FRAMES_PER_DESC]; struct urb *last_data_urb; int lastframe; + int cur_frame_size; /* number of bytes in current frame size */ + int maxline; /* number of lines per frame */ + int yplanesz; /* Number of bytes in the Y plane */ + unsigned int buttonsts:1; }; @@ -110,42 +134,56 @@ } +static inline void konicawc_camera_on(uvd_t *uvd) +{ + DEBUG(0, "camera on"); + konicawc_set_misc(uvd, 0x2, 1, 0x0b); +} + + +static inline void konicawc_camera_off(uvd_t *uvd) +{ + DEBUG(0, "camera off"); + konicawc_set_misc(uvd, 0x2, 0, 0x0b); +} + + +static void konicawc_set_camera_size(uvd_t *uvd) +{ + struct konicawc *cam = (struct konicawc *)uvd->user_data; + + konicawc_set_misc(uvd, 0x2, camera_sizes[cam->size].cmd, 0x08); + cam->width = camera_sizes[cam->size].width; + cam->height = camera_sizes[cam->size].height; + cam->yplanesz = cam->height * cam->width; + cam->cur_frame_size = (cam->yplanesz * 3) / 2; + cam->maxline = cam->yplanesz / 256; + uvd->videosize = VIDEOSIZE(cam->width, cam->height); +} + + static int konicawc_setup_on_open(uvd_t *uvd) { struct konicawc *cam = (struct konicawc *)uvd->user_data; - konicawc_set_misc(uvd, 0x2, 0, 0x0b); - dbg("setting brightness to %d (%d)", cam->brightness, + DEBUG(1, "setting brightness to %d (%d)", cam->brightness, cam->brightness * 11); konicawc_set_value(uvd, cam->brightness, SetBrightness); - dbg("setting white balance to %d (%d)", cam->white_bal, + DEBUG(1, "setting white balance to %d (%d)", cam->white_bal, cam->white_bal * 11); konicawc_set_value(uvd, cam->white_bal, SetWhitebal); - dbg("setting contrast to %d (%d)", cam->contrast, + DEBUG(1, "setting contrast to %d (%d)", cam->contrast, cam->contrast * 11); konicawc_set_value(uvd, cam->contrast, SetContrast); - dbg("setting saturation to %d (%d)", cam->saturation, + DEBUG(1, "setting saturation to %d (%d)", cam->saturation, cam->saturation * 11); konicawc_set_value(uvd, cam->saturation, SetSaturation); - dbg("setting sharpness to %d (%d)", cam->sharpness, + DEBUG(1, "setting sharpness to %d (%d)", cam->sharpness, cam->sharpness * 11); konicawc_set_value(uvd, cam->sharpness, SetSharpness); - dbg("setting size %d", cam->size); - switch(cam->size) { - case 0: - konicawc_set_misc(uvd, 0x2, 0xa, 0x08); - break; - - case 1: - konicawc_set_misc(uvd, 0x2, 4, 0x08); - break; - - case 2: - konicawc_set_misc(uvd, 0x2, 5, 0x08); - break; - } - konicawc_set_misc(uvd, 0x2, 1, 0x0b); - cam->lastframe = -1; + konicawc_set_camera_size(uvd); + cam->lastframe = -2; + cam->buttonsts = 0; return 0; } @@ -154,23 +192,25 @@ { struct konicawc *cam = (struct konicawc *)uvd->user_data; - dbg("new brightness: %d", uvd->vpic.brightness); + konicawc_camera_off(uvd); + DEBUG(1, "new brightness: %d", uvd->vpic.brightness); uvd->vpic.brightness = (uvd->vpic.brightness > MAX_BRIGHTNESS) ? MAX_BRIGHTNESS : uvd->vpic.brightness; if(cam->brightness != uvd->vpic.brightness / 11) { cam->brightness = uvd->vpic.brightness / 11; - dbg("setting brightness to %d (%d)", cam->brightness, + DEBUG(1, "setting brightness to %d (%d)", cam->brightness, cam->brightness * 11); konicawc_set_value(uvd, cam->brightness, SetBrightness); } - dbg("new contrast: %d", uvd->vpic.contrast); + DEBUG(1, "new contrast: %d", uvd->vpic.contrast); uvd->vpic.contrast = (uvd->vpic.contrast > MAX_CONTRAST) ? MAX_CONTRAST : uvd->vpic.contrast; if(cam->contrast != uvd->vpic.contrast / 11) { cam->contrast = uvd->vpic.contrast / 11; - dbg("setting contrast to %d (%d)", cam->contrast, + DEBUG(1, "setting contrast to %d (%d)", cam->contrast, cam->contrast * 11); konicawc_set_value(uvd, cam->contrast, SetContrast); } + konicawc_camera_on(uvd); } @@ -180,21 +220,20 @@ int i, totlen = 0; unsigned char *status = stsurb->transfer_buffer; int keep = 0, discard = 0, bad = 0; - static int buttonsts = 0; + struct konicawc *cam = (struct konicawc *)uvd->user_data; for (i = 0; i < dataurb->number_of_packets; i++) { - int button = buttonsts; + int button = cam->buttonsts; unsigned char sts; int n = dataurb->iso_frame_desc[i].actual_length; int st = dataurb->iso_frame_desc[i].status; - cdata = dataurb->transfer_buffer + + cdata = dataurb->transfer_buffer + dataurb->iso_frame_desc[i].offset; /* Detect and ignore errored packets */ if (st < 0) { - if (debug >= 1) - err("Data error: packet=%d. len=%d. status=%d.", - i, n, st); + DEBUG(1, "Data error: packet=%d. len=%d. status=%d.", + i, n, st); uvd->stats.iso_err_count++; continue; } @@ -210,8 +249,8 @@ /* sts: 0x80-0xff: frame start with frame number (ie 0-7f) * otherwise: - * bit 0 0:drop packet (padding data) - * 1 keep packet + * bit 0 0: keep packet + * 1: drop packet (padding data) * * bit 4 0 button not clicked * 1 button clicked @@ -225,10 +264,10 @@ /* work out the button status, but dont do anything with it for now */ - - if(button != buttonsts) { - dbg("button: %sclicked", button ? "" : "un"); - buttonsts = button; + + if(button != cam->buttonsts) { + DEBUG(2, "button: %sclicked", button ? "" : "un"); + cam->buttonsts = button; } if(sts == 0x01) { /* drop frame */ @@ -241,34 +280,52 @@ bad++; continue; } + if(!sts && cam->lastframe == -2) { + DEBUG(2, "dropping frame looking for image start"); + continue; + } keep++; - if(*(status+i) & 0x80) { /* frame start */ + if(sts & 0x80) { /* frame start */ unsigned char marker[] = { 0, 0xff, 0, 0x00 }; - if(debug > 1) - dbg("Adding Marker packet = %d, frame = %2.2x", - i, *(status+i)); - marker[3] = *(status+i) - 0x80; - RingQueue_Enqueue(&uvd->dp, marker, 4); + + if(cam->lastframe == -2) { + DEBUG(2, "found initial image"); + cam->lastframe = -1; + } + + marker[3] = sts & 0x7F; + RingQueue_Enqueue(&uvd->dp, marker, 4); totlen += 4; } + totlen += n; /* Little local accounting */ - if(debug > 5) - dbg("Adding packet %d, bytes = %d", i, n); RingQueue_Enqueue(&uvd->dp, cdata, n); - } - if(debug > 8) { - dbg("finished: keep = %d discard = %d bad = %d added %d bytes", + DEBUG(8, "finished: keep = %d discard = %d bad = %d added %d bytes", keep, discard, bad, totlen); - } return totlen; } +static void resubmit_urb(uvd_t *uvd, struct urb *urb) +{ + int i, ret; + for (i = 0; i < FRAMES_PER_DESC; i++) { + urb->iso_frame_desc[i].status = 0; + } + urb->dev = uvd->dev; + urb->status = 0; + ret = usb_submit_urb(urb, GFP_KERNEL); + DEBUG(3, "submitting urb of length %d", urb->transfer_buffer_length); + if(ret) + err("usb_submit_urb error (%d)", ret); + +} + + static void konicawc_isoc_irq(struct urb *urb) { - int i, ret, len = 0; uvd_t *uvd = urb->context; struct konicawc *cam = (struct konicawc *)uvd->user_data; @@ -277,42 +334,35 @@ return; if (!uvd->streaming) { - if (debug >= 1) - info("Not streaming, but interrupt!"); + DEBUG(1, "Not streaming, but interrupt!"); return; } - if (urb->actual_length > 32) { - cam->last_data_urb = urb; - goto urb_done_with; - } + DEBUG(3, "got frame %d len = %d buflen =%d", urb->start_frame, urb->actual_length, urb->transfer_buffer_length); uvd->stats.urb_count++; - if (urb->actual_length <= 0) - goto urb_done_with; + if (urb->transfer_buffer_length > 32) { + cam->last_data_urb = urb; + return; + } /* Copy the data received into ring queue */ if(cam->last_data_urb) { - len = konicawc_compress_iso(uvd, cam->last_data_urb, urb); - for (i = 0; i < FRAMES_PER_DESC; i++) { - cam->last_data_urb->iso_frame_desc[i].status = 0; - } + int len = 0; + if(urb->start_frame != cam->last_data_urb->start_frame) + err("Lost sync on frames"); + else if (!urb->status && !cam->last_data_urb->status) + len = konicawc_compress_iso(uvd, cam->last_data_urb, urb); + + resubmit_urb(uvd, urb); + resubmit_urb(uvd, cam->last_data_urb); cam->last_data_urb = NULL; + uvd->stats.urb_length = len; + uvd->stats.data_count += len; + if(len) + RingQueue_WakeUpInterruptible(&uvd->dp); + return; } - uvd->stats.urb_length = len; - uvd->stats.data_count += len; - if(len) - RingQueue_WakeUpInterruptible(&uvd->dp); - -urb_done_with: - for (i = 0; i < FRAMES_PER_DESC; i++) { - urb->iso_frame_desc[i].status = 0; - } - urb->dev = uvd->dev; - urb->status = 0; - ret = usb_submit_urb(urb, GFP_KERNEL); - if(ret) - err("usb_submit_urb error (%d)", ret); return; } @@ -322,13 +372,18 @@ struct usb_device *dev = uvd->dev; int i, errFlag; struct konicawc *cam = (struct konicawc *)uvd->user_data; + int pktsz; + struct usb_interface_descriptor *interface; + interface = &dev->actconfig->interface[uvd->iface].altsetting[spd_to_iface[cam->speed]]; + pktsz = interface->endpoint[1].wMaxPacketSize; + DEBUG(1, "pktsz = %d", pktsz); if (!CAMERA_IS_OPERATIONAL(uvd)) { err("Camera is not operational"); return -EFAULT; } uvd->curframe = -1; - + konicawc_camera_on(uvd); /* Alternate interface 1 is is the biggest frame size */ i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive); if (i < 0) { @@ -349,10 +404,10 @@ urb->transfer_buffer = uvd->sbuf[i].data; urb->complete = konicawc_isoc_irq; urb->number_of_packets = FRAMES_PER_DESC; - urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC; - for (j=k=0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len) { + urb->transfer_buffer_length = pktsz * FRAMES_PER_DESC; + for (j=k=0; j < FRAMES_PER_DESC; j++, k += pktsz) { urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = uvd->iso_packet_len; + urb->iso_frame_desc[j].length = pktsz; } urb = cam->sts_urb[i]; @@ -375,18 +430,17 @@ /* Submit all URBs */ for (i=0; i < USBVIDEO_NUMSBUF; i++) { - errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); - if (errFlag) - err ("usb_submit_isoc(%d) ret %d", i, errFlag); - errFlag = usb_submit_urb(cam->sts_urb[i], GFP_KERNEL); if (errFlag) err("usb_submit_isoc(%d) ret %d", i, errFlag); + + errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); + if (errFlag) + err ("usb_submit_isoc(%d) ret %d", i, errFlag); } uvd->streaming = 1; - if (debug > 1) - dbg("streaming=1 video_endp=$%02x", uvd->video_endp); + DEBUG(1, "streaming=1 video_endp=$%02x", uvd->video_endp); return 0; } @@ -399,6 +453,8 @@ if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) return; + konicawc_camera_off(uvd); + uvd->streaming = 0; cam = (struct konicawc *)uvd->user_data; cam->last_data_urb = NULL; @@ -413,8 +469,6 @@ err("usb_unlink_urb() error %d.", j); } - uvd->streaming = 0; - if (!uvd->remove_pending) { /* Set packet size to 0 */ j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive); @@ -428,42 +482,33 @@ static void konicawc_process_isoc(uvd_t *uvd, usbvideo_frame_t *frame) { - int n; - int maxline, yplanesz; struct konicawc *cam = (struct konicawc *)uvd->user_data; - assert(uvd != NULL); + int maxline = cam->maxline; + int yplanesz = cam->yplanesz; + assert(frame != NULL); - maxline = (cam->height * cam->width * 3) / (2 * 384); - yplanesz = cam->height * cam->width; - if(debug > 5) - dbg("maxline = %d yplanesz = %d", maxline, yplanesz); - - if(debug > 3) - dbg("Frame state = %d", frame->scanstate); + DEBUG(5, "maxline = %d yplanesz = %d", maxline, yplanesz); + DEBUG(3, "Frame state = %d", frame->scanstate); if(frame->scanstate == ScanState_Scanning) { int drop = 0; int curframe; int fdrops = 0; - if(debug > 3) - dbg("Searching for marker, queue len = %d", RingQueue_GetLength(&uvd->dp)); + DEBUG(3, "Searching for marker, queue len = %d", RingQueue_GetLength(&uvd->dp)); while(RingQueue_GetLength(&uvd->dp) >= 4) { if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) && (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xff) && (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00) && (RING_QUEUE_PEEK(&uvd->dp, 3) < 0x80)) { curframe = RING_QUEUE_PEEK(&uvd->dp, 3); - if(cam->lastframe != -1) { - if(curframe < cam->lastframe) { - fdrops = (curframe + 0x80) - cam->lastframe; - } else { - fdrops = curframe - cam->lastframe; - } + if(cam->lastframe >= 0) { + fdrops = (0x80 + curframe - cam->lastframe) & 0x7F; fdrops--; - if(fdrops) + if(fdrops) { info("Dropped %d frames (%d -> %d)", fdrops, cam->lastframe, curframe); + } } cam->lastframe = curframe; frame->curline = 0; @@ -474,18 +519,20 @@ RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1); drop++; } + if(drop) + DEBUG(2, "dropped %d bytes looking for new frame", drop); } if(frame->scanstate == ScanState_Scanning) return; - /* Try to move data from queue into frame buffer + /* Try to move data from queue into frame buffer * We get data in blocks of 384 bytes made up of: * 256 Y, 64 U, 64 V. * This needs to be written out as a Y plane, a U plane and a V plane. */ - while ( frame->curline < maxline && (n = RingQueue_GetLength(&uvd->dp)) >= 384) { + while ( frame->curline < maxline && (RingQueue_GetLength(&uvd->dp) >= 384)) { /* Y */ RingQueue_Dequeue(&uvd->dp, frame->data + (frame->curline * 256), 256); /* U */ @@ -497,8 +544,7 @@ } /* See if we filled the frame */ if (frame->curline == maxline) { - if(debug > 5) - dbg("got whole frame"); + DEBUG(5, "got whole frame"); frame->frameState = FrameState_Done_Hold; frame->curline = 0; @@ -510,10 +556,8 @@ static int konicawc_calculate_fps(uvd_t *uvd) { - struct konicawc *t = uvd->user_data; - dbg("fps = %d", speed_to_fps[t->size][t->speed]/3); - - return speed_to_fps[t->size][t->speed]/3; + struct konicawc *cam = uvd->user_data; + return spd_to_fps[cam->size][cam->speed]/3; } @@ -550,10 +594,10 @@ uvd->vcap.type = VID_TYPE_CAPTURE; uvd->vcap.channels = 1; uvd->vcap.audios = 0; - uvd->vcap.minwidth = camera_sizes[cam->size][0]; - uvd->vcap.minheight = camera_sizes[cam->size][1]; - uvd->vcap.maxwidth = camera_sizes[cam->size][0]; - uvd->vcap.maxheight = camera_sizes[cam->size][1]; + uvd->vcap.minwidth = camera_sizes[cam->size].width; + uvd->vcap.minheight = camera_sizes[cam->size].height; + uvd->vcap.maxwidth = camera_sizes[cam->size].width; + uvd->vcap.maxheight = camera_sizes[cam->size].height; memset(&uvd->vchan, 0, sizeof(uvd->vchan)); uvd->vchan.flags = 0 ; @@ -563,15 +607,14 @@ strcpy(uvd->vchan.name, "Camera"); /* Talk to device */ - dbg("device init"); + DEBUG(1, "device init"); if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2)) - dbg("3,10 -> %2.2x %2.2x", buf[0], buf[1]); + DEBUG(2, "3,10 -> %2.2x %2.2x", buf[0], buf[1]); if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2)) - dbg("3,10 -> %2.2x %2.2x", buf[0], buf[1]); + DEBUG(2, "3,10 -> %2.2x %2.2x", buf[0], buf[1]); if(konicawc_set_misc(uvd, 0x2, 0, 0xd)) - dbg("2,0,d failed"); - dbg("setting initial values"); - + DEBUG(2, "2,0,d failed"); + DEBUG(1, "setting initial values"); } @@ -582,8 +625,7 @@ int actInterface=-1, inactInterface=-1, maxPS=0; unsigned char video_ep = 0; - if (debug >= 1) - dbg("konicawc_probe(%p,%u.)", dev, ifnum); + DEBUG(1, "konicawc_probe(%p,%u.)", dev, ifnum); /* We don't handle multi-config cameras */ if (dev->descriptor.bNumConfigurations != 1) @@ -594,10 +636,8 @@ /* Validate found interface: must have one ISO endpoint */ nas = dev->actconfig->interface[ifnum].num_altsetting; - if (debug > 0) - info("Number of alternate settings=%d.", nas); - if (nas < 8) { - err("Too few alternate settings for this camera!"); + if (nas != 8) { + err("Incorrect number of alternate settings (%d) for this camera!", nas); return NULL; } /* Validate all alternate settings */ @@ -612,7 +652,7 @@ return NULL; } endpoint = &interface->endpoint[1]; - dbg("found endpoint: addr: 0x%2.2x maxps = 0x%4.4x", + DEBUG(1, "found endpoint: addr: 0x%2.2x maxps = 0x%4.4x", endpoint->bEndpointAddress, endpoint->wMaxPacketSize); if (video_ep == 0) video_ep = endpoint->bEndpointAddress; @@ -636,22 +676,20 @@ return NULL; } } else { - if (i == speed_to_interface[speed]) { + if (i == spd_to_iface[speed]) { /* This one is the requested one */ actInterface = i; - maxPS = endpoint->wMaxPacketSize; - if (debug > 0) { - info("Selecting requested active setting=%d. maxPS=%d.", - i, maxPS); - } } } + if(endpoint->wMaxPacketSize > maxPS) + maxPS = endpoint->wMaxPacketSize; } if(actInterface == -1) { err("Cant find required endpoint"); return NULL; } + DEBUG(1, "Selecting requested active setting=%d. maxPS=%d.", actInterface, maxPS); /* Code below may sleep, need to lock module while we are here */ MOD_INC_USE_COUNT; @@ -670,26 +708,10 @@ } } cam->speed = speed; - switch(size) { - case SIZE_160X136: - default: - cam->height = 136; - cam->width = 160; - cam->size = SIZE_160X136; - break; - - case SIZE_176X144: - cam->height = 144; - cam->width = 176; - cam->size = SIZE_176X144; - break; - - case SIZE_320X240: - cam->height = 240; - cam->width = 320; - cam->size = SIZE_320X240; - break; - } + RESTRICT_TO_RANGE(size, SIZE_160X120, SIZE_320X240); + cam->width = camera_sizes[size].width; + cam->height = camera_sizes[size].height; + cam->size = size; uvd->flags = 0; uvd->debug = debug; @@ -773,9 +795,9 @@ MODULE_AUTHOR("Simon Evans "); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_PARM(speed, "i"); -MODULE_PARM_DESC(speed, "FPS speed: 0 (slowest) - 6 (fastest)"); +MODULE_PARM_DESC(speed, "Initial speed: 0 (slowest) - 6 (fastest)"); MODULE_PARM(size, "i"); -MODULE_PARM_DESC(size, "Frame Size 0: 160x136 1: 176x144 2: 320x240"); +MODULE_PARM_DESC(size, "Initial Size 0: 160x120 1: 160x136 2: 176x144 3: 320x240"); MODULE_PARM(brightness, "i"); MODULE_PARM_DESC(brightness, "Initial brightness 0 - 108"); MODULE_PARM(contrast, "i"); @@ -786,7 +808,11 @@ MODULE_PARM_DESC(sharpness, "Initial brightness 0 - 108"); MODULE_PARM(whitebal, "i"); MODULE_PARM_DESC(whitebal, "Initial white balance 0 - 363"); + +#ifdef CONFIG_USB_DEBUG MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)"); +#endif + module_init(konicawc_init); module_exit(konicawc_cleanup); diff -Nru a/drivers/usb/misc/tiglusb.c b/drivers/usb/misc/tiglusb.c --- a/drivers/usb/misc/tiglusb.c Fri Aug 16 14:34:51 2002 +++ b/drivers/usb/misc/tiglusb.c Fri Aug 16 14:34:51 2002 @@ -41,10 +41,6 @@ #include #include "tiglusb.h" -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) -# define minor(x) MINOR(x) -#endif - /* * Version Information */ diff -Nru a/drivers/usb/net/cdc-ether.c b/drivers/usb/net/cdc-ether.c --- a/drivers/usb/net/cdc-ether.c Fri Aug 16 14:34:52 2002 +++ b/drivers/usb/net/cdc-ether.c Fri Aug 16 14:34:52 2002 @@ -1,4 +1,4 @@ -// Portions of this file taken from +// Portions of this file taken from // Petko Manolov - Petkan (petkan@dce.bg) // from his driver pegasus.c @@ -436,7 +436,10 @@ // Tell the kernel to stop sending us frames while we get this // all set up. - netif_stop_queue(net); +// netif_stop_queue(net); + +// FIXME: We hold xmit_lock. If you want to do the queue stuff you need +// to enable it from a completion handler /* Note: do not reorder, GCC is clever about common statements. */ if (net->flags & IFF_PROMISC) { @@ -469,7 +472,7 @@ MODE_FLAG_DIRECTED | MODE_FLAG_BROADCAST | MODE_FLAG_MULTICAST; - buff = kmalloc(6 * net->mc_count, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + buff = kmalloc(6 * net->mc_count, GFP_ATOMIC); for (i = 0, mclist = net->mc_list; mclist && i < net->mc_count; i++, mclist = mclist->next) { @@ -477,6 +480,7 @@ } #if 0 usb_control_msg(ether_dev->usb, +// FIXME: We hold a spinlock. You must not use a synchronous API usb_sndctrlpipe(ether_dev->usb, 0), SET_ETHERNET_MULTICAST_FILTER, /* request */ USB_TYPE_CLASS | USB_DIR_OUT | USB_RECIP_INTERFACE, /* request type */ @@ -493,7 +497,7 @@ CDC_SetEthernetPacketFilter(ether_dev); #endif // Tell the kernel to start giving frames to us again. - netif_wake_queue(net); +// netif_wake_queue(net); } ////////////////////////////////////////////////////////////////////////////// @@ -1170,23 +1174,20 @@ if (rc) { // Nope we couldn't find one we liked. // This device was not meant for us to control. - kfree( ether_dev ); - return NULL; + goto error_all; } - // Now that we FOUND a configuration. let's try to make the + // Now that we FOUND a configuration. let's try to make the // device go into it. if ( usb_set_configuration( usb, ether_dev->bConfigurationValue ) ) { err("usb_set_configuration() failed"); - kfree( ether_dev ); - return NULL; + goto error_all; } // Now set the communication interface up as required. if (usb_set_interface(usb, ether_dev->comm_bInterfaceNumber, ether_dev->comm_bAlternateSetting)) { err("usb_set_interface() failed"); - kfree( ether_dev ); - return NULL; + goto error_all; } // Only turn traffic on right now if we must... @@ -1194,23 +1195,21 @@ // We found an alternate setting for the data // interface that allows us to turn off traffic. // We should use it. - if (usb_set_interface( usb, - ether_dev->data_bInterfaceNumber, + if (usb_set_interface( usb, + ether_dev->data_bInterfaceNumber, ether_dev->data_bAlternateSetting_without_traffic)) { err("usb_set_interface() failed"); - kfree( ether_dev ); - return NULL; + goto error_all; } } else { // We didn't find an alternate setting for the data // interface that would let us turn off traffic. // Oh well, let's go ahead and do what we must... - if (usb_set_interface( usb, - ether_dev->data_bInterfaceNumber, + if (usb_set_interface( usb, + ether_dev->data_bInterfaceNumber, ether_dev->data_bAlternateSetting_with_traffic)) { err("usb_set_interface() failed"); - kfree( ether_dev ); - return NULL; + goto error_all; } } @@ -1220,8 +1219,7 @@ // Hmm... The kernel is not sharing today... // Fine, we didn't want it anyway... err( "Unable to initialize ethernet device" ); - kfree( ether_dev ); - return NULL; + goto error_all; } // Now that we have an ethernet device, let's set it up @@ -1241,7 +1239,7 @@ // We'll keep track of this information for later... ether_dev->usb = usb; ether_dev->net = net; - + // and don't forget the MAC address. set_ethernet_addr( ether_dev ); @@ -1249,12 +1247,12 @@ log_device_info( ether_dev ); // I claim this interface to be a CDC Ethernet Networking device - usb_driver_claim_interface( &CDCEther_driver, - &(usb->config[ether_dev->configuration_num].interface[ether_dev->comm_interface]), + usb_driver_claim_interface( &CDCEther_driver, + &(usb->config[ether_dev->configuration_num].interface[ether_dev->comm_interface]), ether_dev ); // I claim this interface to be a CDC Ethernet Networking device - usb_driver_claim_interface( &CDCEther_driver, - &(usb->config[ether_dev->configuration_num].interface[ether_dev->data_interface]), + usb_driver_claim_interface( &CDCEther_driver, + &(usb->config[ether_dev->configuration_num].interface[ether_dev->data_interface]), ether_dev ); // Does this REALLY do anything??? @@ -1265,6 +1263,14 @@ // Okay, we are finally done... return NULL; + + // bailing out with our tail between our knees +error_all: + usb_free_urb(ether_dev->tx_urb); + usb_free_urb(ether_dev->rx_urb); + usb_free_urb(ether_dev->intr_urb); + kfree( ether_dev ); + return NULL; } diff -Nru a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c --- a/drivers/usb/serial/usbserial.c Fri Aug 16 14:34:56 2002 +++ b/drivers/usb/serial/usbserial.c Fri Aug 16 14:34:56 2002 @@ -1193,7 +1193,7 @@ interface = &dev->actconfig->interface[ifnum]; list_for_each (tmp, &usb_serial_driver_list) { type = list_entry(tmp, struct usb_serial_device_type, driver_list); - id_pattern = usb_match_id(dev, interface, type->id_table); + id_pattern = usb_match_id(interface, type->id_table); if (id_pattern != NULL) { dbg("descriptor matches"); found = 1; diff -Nru a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c --- a/drivers/usb/storage/scsiglue.c Fri Aug 16 14:34:58 2002 +++ b/drivers/usb/storage/scsiglue.c Fri Aug 16 14:34:58 2002 @@ -147,7 +147,8 @@ srb->host_scribble = (unsigned char *)us; /* enqueue the command */ - BUG_ON(atomic_read(&us->sm_state) != US_STATE_IDLE || us->srb != NULL); + BUG_ON(atomic_read(&us->sm_state) != US_STATE_IDLE); + BUG_ON(us->srb != NULL); srb->scsi_done = done; us->srb = srb; @@ -264,7 +265,7 @@ US_DEBUGPX("simulating disconnect/reconnect.\n"); down(&intf->driver->serialize); intf->driver->disconnect(pusb_dev_save, intf->private_data); - id = usb_match_id(pusb_dev_save, intf, intf->driver->id_table); + id = usb_match_id(intf, intf->driver->id_table); intf->driver->probe(pusb_dev_save, i, id); up(&intf->driver->serialize); } diff -Nru a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h --- a/drivers/usb/storage/usb.h Fri Aug 16 14:34:59 2002 +++ b/drivers/usb/storage/usb.h Fri Aug 16 14:34:59 2002 @@ -203,16 +203,9 @@ /* The scsi_lock() and scsi_unlock() macros protect the sm_state and the * single queue element srb for write access */ -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,3) #define scsi_unlock(host) spin_unlock_irq(host->host_lock) #define scsi_lock(host) spin_lock_irq(host->host_lock) #define sg_address(psg) (page_address((psg)->page) + (psg)->offset) -#else -#define scsi_unlock(host) spin_unlock_irq(&io_request_lock) -#define scsi_lock(host) spin_lock_irq(&io_request_lock) - -#define sg_address(psg) ((psg)->address) -#endif #endif diff -Nru a/drivers/video/Config.in b/drivers/video/Config.in --- a/drivers/video/Config.in Fri Aug 16 14:34:58 2002 +++ b/drivers/video/Config.in Fri Aug 16 14:34:58 2002 @@ -265,7 +265,7 @@ "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ "$CONFIG_FB_IGA" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \ "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ - "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_SA1100" = "y" ]; then + "$CONFIG_FB_SA1100" = "y" ]; then define_tristate CONFIG_FBCON_CFB8 y else if [ "$CONFIG_FB_ACORN" = "m" -o "$CONFIG_FB_ATARI" = "m" -o \ @@ -292,7 +292,7 @@ "$CONFIG_FB_VALKYRIE" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ "$CONFIG_FB_CT65550" = "y" -o "$CONFIG_FB_MATROX" = "y" -o \ "$CONFIG_FB_PM2" = "y" -o "$CONFIG_FB_CYBER2000" = "y" -o \ - "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_SA1100" = "y" ]; then + "$CONFIG_FB_SA1100" = "y" ]; then define_tristate CONFIG_FBCON_CFB16 y else if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_SIS" = "m" -o \ @@ -331,7 +331,7 @@ "$CONFIG_FB_TGA" = "y" -o "$CONFIG_FB_PLATINUM" = "y" -o \ "$CONFIG_FB_MATROX" = "y" -o "$CONFIG_FB_PM2" = "y" -o \ "$CONFIG_FB_PVR2" = "y" -o "$CONFIG_FB_PM3" = "y" -o \ - "$CONFIG_FB_SIS" = "y" -o "$CONFIG_FB_ATY" = "y" ]; then + "$CONFIG_FB_SIS" = "y" ]; then define_tristate CONFIG_FBCON_CFB32 y else if [ "$CONFIG_FB_ATARI" = "m" -o "$CONFIG_FB_RADEON" = "m" -o \ @@ -352,7 +352,7 @@ "$CONFIG_FB_PMAG_BA" = "y" -o "$CONFIG_FB_PMAGB_B" = "y" -o \ "$CONFIG_FB_3DFX" = "y" -o "$CONFIG_FB_TX3912" = "y" -o \ "$CONFIG_FB_MAXINE" = "y" -o "$CONFIG_FB_APOLLO" = "y" -o \ - "$CONFIG_FB_ATY128" = "y" -o "$CONFIG_FB_MAC" = "y" -o \ + "$CONFIG_FB_ATY" = "y" -o "$CONFIG_FB_MAC" = "y" -o \ "$CONFIG_FB_RIVA" = "y" -o "$CONFIG_FB_OF" = "y" -o \ "$CONFIG_FB_SGIVW" = "y" ]; then define_tristate CONFIG_FBCON_ACCEL y diff -Nru a/drivers/video/Makefile b/drivers/video/Makefile --- a/drivers/video/Makefile Fri Aug 16 14:34:53 2002 +++ b/drivers/video/Makefile Fri Aug 16 14:34:53 2002 @@ -60,7 +60,7 @@ obj-$(CONFIG_FB_3DFX) += tdfxfb.o obj-$(CONFIG_FB_MAC) += macfb.o macmodes.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o obj-$(CONFIG_FB_HP300) += hpfb.o cfbfillrect.o cfbimgblt.o -obj-$(CONFIG_FB_OF) += offb.o +obj-$(CONFIG_FB_OF) += offb.o cfbfillrect.o cfbimgblit.o cfbcopyarea.o obj-$(CONFIG_FB_IMSTT) += imsttfb.o obj-$(CONFIG_FB_RETINAZ3) += retz3fb.o obj-$(CONFIG_FB_CLGEN) += clgenfb.o @@ -89,7 +89,7 @@ obj-$(CONFIG_FB_MATROX) += matrox/ obj-$(CONFIG_FB_RIVA) += riva/ obj-$(CONFIG_FB_SIS) += sis/ -obj-$(CONFIG_FB_ATY) += aty/ +obj-$(CONFIG_FB_ATY) += aty/ cfbimgblt.o obj-$(CONFIG_FB_SUN3) += sun3fb.o obj-$(CONFIG_FB_BWTWO) += bwtwofb.o diff -Nru a/drivers/video/S3triofb.c b/drivers/video/S3triofb.c --- a/drivers/video/S3triofb.c Fri Aug 16 14:34:57 2002 +++ b/drivers/video/S3triofb.c Fri Aug 16 14:34:57 2002 @@ -124,15 +124,15 @@ u_int *transp, struct fb_info *info); static struct fb_ops s3trio_ops = { - owner: THIS_MODULE, - fb_get_fix: s3trio_get_fix, - fb_get_var: s3trio_get_var, - fb_set_var: s3trio_set_var, - fb_get_cmap: s3trio_get_cmap, - fb_set_cmap: gen_set_cmap, - fb_setcolreg: s3trio_setcolreg, - fb_pan_display: s3trio_pan_display, - fb_blank: s3triofb_blank, + .owner = THIS_MODULE, + .fb_get_fix = s3trio_get_fix, + .fb_get_var = s3trio_get_var, + .fb_set_var = s3trio_set_var, + .fb_get_cmap = s3trio_get_cmap, + .fb_set_cmap = gen_set_cmap, + .fb_setcolreg = s3trio_setcolreg, + .fb_pan_display =s3trio_pan_display, + .fb_blank = s3triofb_blank, }; /* @@ -776,14 +776,14 @@ } static struct display_switch fbcon_trio8 = { - setup: fbcon_cfb8_setup, - bmove: fbcon_trio8_bmove, - clear: fbcon_trio8_clear, - putc: fbcon_trio8_putc, - putcs: fbcon_trio8_putcs, - revc: fbcon_trio8_revc, - clear_margins: fbcon_cfb8_clear_margins, - fontwidthmask: FONTWIDTH(8) + .setup = fbcon_cfb8_setup, + .bmove = fbcon_trio8_bmove, + .clear = fbcon_trio8_clear, + .putc = fbcon_trio8_putc, + .putcs = fbcon_trio8_putcs, + .revc = fbcon_trio8_revc, + .clear_margins = fbcon_cfb8_clear_margins, + .fontwidthmask = FONTWIDTH(8) }; #endif diff -Nru a/drivers/video/acornfb.c b/drivers/video/acornfb.c --- a/drivers/video/acornfb.c Fri Aug 16 14:34:57 2002 +++ b/drivers/video/acornfb.c Fri Aug 16 14:34:57 2002 @@ -1176,16 +1176,16 @@ } static struct fb_ops acornfb_ops = { - owner: THIS_MODULE, - fb_get_fix: acornfb_get_fix, - fb_get_var: acornfb_get_var, - fb_set_var: acornfb_set_var, - fb_get_cmap: acornfb_get_cmap, - fb_set_cmap: gen_set_cmap, - fb_setcolreg: acornfb_setcolreg, - fb_pan_display: acornfb_pan_display, - fb_blank: acornfb_blank, - fb_mmap: acornfb_mmap, + .owner = THIS_MODULE, + .fb_get_fix = acornfb_get_fix, + .fb_get_var = acornfb_get_var, + .fb_set_var = acornfb_set_var, + .fb_get_cmap = acornfb_get_cmap, + .fb_set_cmap = gen_set_cmap, + .fb_setcolreg = acornfb_setcolreg, + .fb_pan_display =acornfb_pan_display, + .fb_blank = acornfb_blank, + .fb_mmap = acornfb_mmap, }; static int @@ -1275,19 +1275,19 @@ static struct fb_videomode __initdata acornfb_default_mode = { - name: NULL, - refresh: 60, - xres: 640, - yres: 480, - pixclock: 39722, - left_margin: 56, - right_margin: 16, - upper_margin: 34, - lower_margin: 9, - hsync_len: 88, - vsync_len: 2, - sync: 0, - vmode: FB_VMODE_NONINTERLACED + .name = NULL, + .refresh = 60, + .xres = 640, + .yres = 480, + .pixclock = 39722, + .left_margin = 56, + .right_margin = 16, + .upper_margin = 34, + .lower_margin = 9, + .hsync_len = 88, + .vsync_len = 2, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED }; static void __init diff -Nru a/drivers/video/amifb.c b/drivers/video/amifb.c --- a/drivers/video/amifb.c Fri Aug 16 14:35:00 2002 +++ b/drivers/video/amifb.c Fri Aug 16 14:35:00 2002 @@ -1164,16 +1164,16 @@ static struct fb_ops amifb_ops = { - owner: THIS_MODULE, - fb_get_fix: amifb_get_fix, - fb_get_var: amifb_get_var, - fb_set_var: amifb_set_var, - fb_get_cmap: amifb_get_cmap, - fb_set_cmap: gen_set_cmap, - fb_setcolreg: amifb_setcolreg, - fb_pan_display: amifb_pan_display, - fb_blank: amifb_blank, - fb_ioctl: amifb_ioctl, + .owner = THIS_MODULE, + .fb_get_fix = amifb_get_fix, + .fb_get_var = amifb_get_var, + .fb_set_var = amifb_set_var, + .fb_get_cmap = amifb_get_cmap, + .fb_set_cmap = gen_set_cmap, + .fb_setcolreg = amifb_setcolreg, + .fb_pan_display = amifb_pan_display, + .fb_blank = amifb_blank, + .fb_ioctl = amifb_ioctl, }; static void __init amifb_setup_mcap(char *spec) diff -Nru a/drivers/video/anakinfb.c b/drivers/video/anakinfb.c --- a/drivers/video/anakinfb.c Fri Aug 16 14:34:58 2002 +++ b/drivers/video/anakinfb.c Fri Aug 16 14:34:58 2002 @@ -27,28 +27,28 @@ static struct display display; static struct fb_var_screeninfo anakinfb_var = { - xres: 400, - yres: 234, - xres_virtual: 400, - yres_virtual: 234, - bits_per_pixel: 16, - red: { 11, 5, 0 }, - green: { 5, 6, 0 }, - blue: { 0, 5, 0 }, - activate: FB_ACTIVATE_NOW, - height: -1, - width: -1, - vmode: FB_VMODE_NONINTERLACED, + .xres = 400, + .yres = 234, + .xres_virtual = 400, + .yres_virtual = 234, + .bits_per_pixel =16, + .red = { 11, 5, 0 }, + .green = { 5, 6, 0 }, + .blue = { 0, 5, 0 }, + .activate FB_ACTIVATE_NOW, + .height -1, + .width -1, + .vmode FB_VMODE_NONINTERLACED, }; static struct fb_fix_screeninfo anakinfb_fix = { - id: "AnakinFB", - smem_start: VGA_START, - smem_len: VGA_SIZE, - type: FB_TYPE_PACKED_PIXELS, - visual: FB_VISUAL_TRUECOLOR, - line_length: 400*2, - accel: FB_ACCEL_NONE, + .id = "AnakinFB", + .smem_start = VGA_START, + .smem_len = VGA_SIZE, + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .line_length = 400*2, + .accel = FB_ACCEL_NONE, }; static int @@ -64,16 +64,14 @@ } static struct fb_ops anakinfb_ops = { - owner: THIS_MODULE, - fb_get_fix: gen_get_fix, - fb_get_var: gen_get_var, - fb_set_var: gen_set_var, - fb_get_cmap: gen_get_cmap, - fb_set_cmap: gen_set_cmap, - fb_setcolreg: anakinfb_setcolreg, - fb_fillrect: cfb_fillrect, - fb_copyarea: cfb_copyarea, - fb_imageblit: cfb_imageblit, + .owner = THIS_MODULE, + .fb_set_var = gen_set_var, + .fb_get_cmap = gen_get_cmap, + .fb_set_cmap = gen_set_cmap, + .fb_setcolreg = anakinfb_setcolreg, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, }; int __init diff -Nru a/drivers/video/atafb.c b/drivers/video/atafb.c --- a/drivers/video/atafb.c Fri Aug 16 14:34:53 2002 +++ b/drivers/video/atafb.c Fri Aug 16 14:34:53 2002 @@ -2623,15 +2623,15 @@ } static struct fb_ops atafb_ops = { - owner: THIS_MODULE, - fb_get_fix: atafb_get_fix, - fb_get_var: atafb_get_var, - fb_set_var: atafb_set_var, - fb_get_cmap: atafb_get_cmap, - fb_set_cmap: gen_set_cmap, - fb_pan_display: atafb_pan_display, - fb_blank: atafb_blank, - fb_ioctl: atafb_ioctl, + .owner = THIS_MODULE, + .fb_get_fix = atafb_get_fix, + .fb_get_var = atafb_get_var, + .fb_set_var = atafb_set_var, + .fb_get_cmap = atafb_get_cmap, + .fb_set_cmap = gen_set_cmap, + .fb_pan_display =atafb_pan_display, + .fb_blank = atafb_blank, + .fb_ioctl = atafb_ioctl, }; static void diff -Nru a/drivers/video/aty/Makefile b/drivers/video/aty/Makefile --- a/drivers/video/aty/Makefile Fri Aug 16 14:34:56 2002 +++ b/drivers/video/aty/Makefile Fri Aug 16 14:34:56 2002 @@ -1,6 +1,6 @@ obj-$(CONFIG_FB_ATY) += atyfb.o -atyfb-y := atyfb_base.o mach64_accel.o ../cfbimgblt.o +atyfb-y := atyfb_base.o mach64_accel.o atyfb-$(CONFIG_FB_ATY_GX) += mach64_gx.o atyfb-$(CONFIG_FB_ATY_CT) += mach64_ct.o mach64_cursor.o atyfb-objs := $(atyfb-y) diff -Nru a/drivers/video/aty/atyfb.h b/drivers/video/aty/atyfb.h --- a/drivers/video/aty/atyfb.h Fri Aug 16 14:34:59 2002 +++ b/drivers/video/aty/atyfb.h Fri Aug 16 14:34:59 2002 @@ -3,7 +3,7 @@ */ #include - +#include