--- linux/drivers/block/cdu31a.c.old Fri May 13 08:09:55 1994 +++ linux/drivers/block/cdu31a.c Wed Jun 1 01:19:07 1994 @@ -58,8 +58,6 @@ * */ - - #include #include #include @@ -69,10 +67,12 @@ #include #include #include +#include #include #include #include +#include #include #include @@ -82,18 +82,27 @@ #define CDU31A_MAX_CONSECUTIVE_ATTENTIONS 10 -static unsigned short cdu31a_addresses[] = -{ - 0x340, /* Standard configuration Sony Interface */ - 0x1f88, /* Fusion CD-16 */ - 0x230, /* SoundBlaster 16 card */ - 0x360, /* Secondary standard Sony Interface */ - 0x320, /* Secondary standard Sony Interface */ - 0x330, /* Secondary standard Sony Interface */ - 0 +/* +** Edit the following data to change interrupts, DMA channels, etc. +** I need to get good default values for these. +*/ +static struct +{ + unsigned short base; /* I/O Base Address */ + short dma_num; /* DMA Number (-1 means no DMA) */ + short int_num; /* Interrupt Number (-1 means scan for it, + 0 means don't use) */ +} cdu31a_addresses[] = +{ + { 0x340, 3, 0 }, /* Standard configuration Sony Interface */ + { 0x1f88, -1, 0 }, /* Fusion CD-16 */ + { 0x230, -1, 0 }, /* SoundBlaster 16 card */ + { 0x360, 3, 0 }, /* Secondary standard Sony Interface */ + { 0x320, 3, 0 }, /* Secondary standard Sony Interface */ + { 0x330, 3, 0 }, /* Secondary standard Sony Interface */ + { 0 } }; - static int handle_sony_cd_attention(void); static int read_subcode(void); static void sony_get_toc(void); @@ -157,6 +166,8 @@ static struct task_struct *has_cd_task = NULL; /* The task that is currently using the CDROM drive, or NULL if none. */ +static int is_double_speed = 0; /* Is the drive a CDU33A? */ + /* * The audio status uses the values from read subchannel data as specified * in include/linux/cdrom.h. @@ -170,9 +181,16 @@ * position during a pause so a resume can restart it. It uses the * audio status variable above to tell if it is paused. */ -unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 }; -unsigned volatile char final_pos_msf[3] = { 0, 0, 0 }; +static unsigned volatile char cur_pos_msf[3] = { 0, 0, 0 }; +static unsigned volatile char final_pos_msf[3] = { 0, 0, 0 }; +static int irq_used = -1; +static int dma_channel = -1; +static struct wait_queue *cdu31a_irq_wait = NULL; + +static int curr_control_reg = 0; /* Current value of the control register */ + + /* * This routine returns 1 if the disk has been changed since the last * check or 0 if it hasn't. Setting flag to 0 resets the changed flag. @@ -199,6 +217,30 @@ return retval; } +static inline void +enable_interrupts(void) +{ + curr_control_reg |= ( SONY_ATTN_INT_EN_BIT + | SONY_RES_RDY_INT_EN_BIT + | SONY_DATA_RDY_INT_EN_BIT); + outb(curr_control_reg, sony_cd_control_reg); +} + +static inline void +disable_interrupts(void) +{ + curr_control_reg &= ~( SONY_ATTN_INT_EN_BIT + | SONY_RES_RDY_INT_EN_BIT + | SONY_DATA_RDY_INT_EN_BIT); + outb(curr_control_reg, sony_cd_control_reg); +} + +static void +cdu31a_interrupt(int unused) +{ + disable_interrupts(); + wake_up(&cdu31a_irq_wait); +} /* * Wait a little while (used for polling the drive). If in initialization, @@ -207,9 +249,19 @@ static inline void sony_sleep(void) { - current->state = TASK_INTERRUPTIBLE; - current->timeout = jiffies; - schedule(); + if (irq_used <= 0) + { + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies; + schedule(); + } + else /* Interrupt driven */ + { + cli(); + enable_interrupts(); + interruptible_sleep_on(&cdu31a_irq_wait); + sti(); + } } @@ -256,6 +308,7 @@ static inline void reset_drive(void) { + curr_control_reg = 0; outb(SONY_DRIVE_RESET_BIT, sony_cd_control_reg); } @@ -262,25 +315,25 @@ static inline void clear_attention(void) { - outb(SONY_ATTN_CLR_BIT, sony_cd_control_reg); + outb(curr_control_reg | SONY_ATTN_CLR_BIT, sony_cd_control_reg); } static inline void clear_result_ready(void) { - outb(SONY_RES_RDY_CLR_BIT, sony_cd_control_reg); + outb(curr_control_reg | SONY_RES_RDY_CLR_BIT, sony_cd_control_reg); } static inline void clear_data_ready(void) { - outb(SONY_DATA_RDY_CLR_BIT, sony_cd_control_reg); + outb(curr_control_reg | SONY_DATA_RDY_CLR_BIT, sony_cd_control_reg); } static inline void clear_param_reg(void) { - outb(SONY_PARAM_CLR_BIT, sony_cd_control_reg); + outb(curr_control_reg | SONY_PARAM_CLR_BIT, sony_cd_control_reg); } static inline unsigned char @@ -310,8 +363,8 @@ static inline void write_cmd(unsigned char cmd) { + outb(curr_control_reg | SONY_RES_RDY_INT_EN_BIT, sony_cd_control_reg); outb(cmd, sony_cd_cmd_reg); - outb(SONY_RES_RDY_INT_EN_BIT, sony_cd_control_reg); } /* @@ -327,7 +380,11 @@ params[0] = SONY_SD_MECH_CONTROL; - params[1] = 0x03; + params[1] = 0x03; /* Set auto spin up and auto eject */ + if (is_double_speed) + { + params[1] |= 0x04; /* Set the drive to double speed if possible */ + } do_sony_cd_cmd(SONY_SET_DRIVE_PARAM_CMD, params, 2, @@ -532,11 +589,49 @@ } } +static void +read_data_dma(unsigned char *data, + unsigned int data_size, + unsigned char *result_buffer, + unsigned int *result_size) +{ + unsigned int retry_count; + + + cli(); + disable_dma(dma_channel); + clear_dma_ff(dma_channel); + set_dma_mode(dma_channel, DMA_MODE_READ); + set_dma_addr(dma_channel, (int) data); + set_dma_count(dma_channel, data_size); + enable_dma(dma_channel); + sti(); + + retry_count = jiffies + SONY_JIFFIES_TIMEOUT; + while ( (retry_count > jiffies) + && (!is_data_ready()) + && (!is_result_ready())) + { + while (handle_sony_cd_attention()) + ; + + sony_sleep(); + } + if (!is_data_requested()) + { + result_buffer[0] = 0x20; + result_buffer[1] = SONY_TIMEOUT_OP_ERR; + *result_size = 2; + return; + } +} + /* * Read in a 2048 byte block of data. */ static void read_data_block(unsigned char *data, + unsigned int data_size, unsigned char *result_buffer, unsigned int *result_size) { @@ -543,7 +638,7 @@ int i; unsigned int retry_count; - for (i=0; i<2048; i++) + for (i=0; i jiffies) && (!is_data_requested())) @@ -679,14 +774,24 @@ result_read = 1; get_result(result_buffer, result_size); } + else if (dma_channel > 0) + { + clear_data_ready(); + read_data_dma(data, 2048, result_buffer, result_size); + data += 2048; + data_size -= 2048; + cur_offset = cur_offset + 2048; + num_sectors_read++; + } else /* Handle data next */ { /* * The drive has to be polled for status on a byte-by-byte basis - * to know if the data is ready. Yuck. I really wish I could use DMA. + * to know if the data is ready. Yuck. I really wish I could use + * DMA all the time. */ clear_data_ready(); - read_data_block(data, result_buffer, result_size); + read_data_block(data, 2048, result_buffer, result_size); data += 2048; data_size -= 2048; cur_offset = cur_offset + 2048; @@ -1771,6 +1876,15 @@ } +static struct sigaction cdu31a_sigaction = { + cdu31a_interrupt, + 0, + SA_INTERRUPT, + NULL +}; + +static int cdu31a_block_size; + /* * Initialize the driver. */ @@ -1781,6 +1895,7 @@ unsigned int res_size; int i; int drive_found; + int tmp_irq; /* @@ -1795,20 +1910,20 @@ i = 0; drive_found = 0; - while ( (cdu31a_addresses[i] != 0) + while ( (cdu31a_addresses[i].base != 0) && (!drive_found)) { - if (check_region(cdu31a_addresses[i], 4)) { + if (check_region(cdu31a_addresses[i].base, 4)) { i++; continue; } - get_drive_configuration(cdu31a_addresses[i], + get_drive_configuration(cdu31a_addresses[i].base, drive_config.exec_status, &res_size); if ((res_size > 2) && ((drive_config.exec_status[0] & 0x20) == 0x00)) { drive_found = 1; - snarf_region(cdu31a_addresses[i], 4); + snarf_region(cdu31a_addresses[i].base, 4); if (register_blkdev(MAJOR_NR,"cdu31a",&scd_fops)) { @@ -1816,6 +1931,49 @@ return mem_start; } + if (strncmp(drive_config.product_id, "CD-ROM CDU33A", 13) == 0) + { + is_double_speed = 1; + } + + tmp_irq = cdu31a_addresses[i].int_num; + if (tmp_irq < 0) + { + autoirq_setup(0); + enable_interrupts(); + reset_drive(); + tmp_irq = autoirq_report(10); + disable_interrupts(); + + set_drive_params(); + irq_used = tmp_irq; + } + else + { + set_drive_params(); + irq_used = tmp_irq; + } + + if (irq_used > 0) + { + if (irqaction(irq_used,&cdu31a_sigaction)) + { + irq_used = 0; + printk("Unable to grab IRQ%d for the CDU31A driver\n", irq_used); + } + } + + dma_channel = cdu31a_addresses[i].dma_num; + if (dma_channel > 0) + { + if (request_dma(dma_channel)) + { + dma_channel = -1; + printk("Unable to grab DMA%d for the CDU31A driver\n", + dma_channel); + } + } + sony_buffer_size = mem_size[SONY_HWC_GET_BUF_MEM_SIZE(drive_config)]; sony_buffer_sectors = sony_buffer_size / 2048; @@ -1829,17 +1987,40 @@ { printk(", capable of audio playback"); } + if (is_double_speed) + { + printk(", double speed"); + } + if (irq_used > 0) + { + printk(", irq %d", irq_used); + } + if (dma_channel > 0) + { + printk(", drq %d", dma_channel); + } printk("\n"); - set_drive_params(); - blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; - read_ahead[MAJOR_NR] = 8; /* 8 sector (4kB) read-ahead */ + read_ahead[MAJOR_NR] = 32; /* 32 sector (16kB) read-ahead */ + cdu31a_block_size = 2048; /* 2kB block size */ + /* use 'mount -o block=2048' */ + blksize_size[MAJOR_NR] = &cdu31a_block_size; sony_toc = (struct s_sony_toc *) mem_start; mem_start += sizeof(*sony_toc); last_sony_subcode = (struct s_sony_subcode *) mem_start; mem_start += sizeof(*last_sony_subcode); + + /* If memory will not fit into the current 64KB block, align it + so the block will not cross a 64KB boundary. This is + because DMA cannot cross 64KB boundaries. */ + if ( ((mem_start) & (~0xffff)) + != (((mem_start) + sony_buffer_size) & (~0xffff))) + { + mem_start = (((int)mem_start) + 0x10000) & (~0xffff); + } + sony_buffer = (unsigned char *) mem_start; mem_start += sony_buffer_size; }