2005-04-17 02:20:36 +04:00
/*
* linux / drivers / ide / legacy / 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 33 Mhz VLbus
* To activate controller support , use " ide0=qd65xx "
2007-03-03 19:48:55 +03:00
* To enable tuning , use " hda=autotune hdb=autotune "
* To enable 2 nd channel tuning ( qd6580 only ) , use " hdc=autotune hdd=autotune "
2005-04-17 02:20:36 +04:00
*/
/*
* Rewritten from the work of Colten Edwards < pje120 @ cs . usask . ca > by
* Samuel Thibault < samuel . thibault @ fnac . net >
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/timer.h>
# include <linux/mm.h>
# include <linux/ioport.h>
# include <linux/blkdev.h>
# include <linux/hdreg.h>
# include <linux/ide.h>
# include <linux/init.h>
# include <asm/system.h>
# include <asm/io.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 = < = 33 MHz ; 0 = > 33 MHz
* 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 ( u8 content , unsigned long reg )
{
unsigned long flags ;
spin_lock_irqsave ( & ide_lock , flags ) ;
outb ( content , reg ) ;
spin_unlock_irqrestore ( & ide_lock , flags ) ;
}
static u8 __init qd_read_reg ( unsigned long reg )
{
unsigned long flags ;
u8 read ;
spin_lock_irqsave ( & ide_lock , flags ) ;
read = inb ( 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 )
{
u8 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 u8 qd6500_compute_timing ( ide_hwif_t * hwif , int active_time , int recovery_time )
{
u8 active_cycle , recovery_cycle ;
if ( system_bus_clock ( ) < = 33 ) {
active_cycle = 9 - IDE_IN ( active_time * system_bus_clock ( ) / 1000 + 1 , 2 , 9 ) ;
recovery_cycle = 15 - IDE_IN ( recovery_time * system_bus_clock ( ) / 1000 + 1 , 0 , 15 ) ;
} else {
active_cycle = 8 - IDE_IN ( active_time * system_bus_clock ( ) / 1000 + 1 , 1 , 8 ) ;
recovery_cycle = 18 - IDE_IN ( recovery_time * system_bus_clock ( ) / 1000 + 1 , 3 , 18 ) ;
}
return ( ( recovery_cycle < < 4 ) | 0x08 | active_cycle ) ;
}
/*
* qd6580_compute_timing
*
* idem for qd6580
*/
static u8 qd6580_compute_timing ( int active_time , int recovery_time )
{
u8 active_cycle = 17 - IDE_IN ( active_time * system_bus_clock ( ) / 1000 + 1 , 2 , 17 ) ;
u8 recovery_cycle = 15 - IDE_IN ( recovery_time * system_bus_clock ( ) / 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 , u8 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 , u8 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 , u8 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 = min_t ( u8 , 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 )
{
u8 savereg ;
u8 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 ) ;
outb ( 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 ) ;
}
/*
* qd_setup :
*
* called to setup an ata channel : adjusts attributes & links for tuning
*/
static void __init qd_setup ( ide_hwif_t * hwif , int base , int config ,
unsigned int data0 , unsigned int data1 ,
void ( * tuneproc ) ( ide_drive_t * , u8 pio ) )
{
hwif - > chipset = ide_qd65xx ;
hwif - > channel = hwif - > index ;
hwif - > select_data = base ;
hwif - > config_data = config ;
hwif - > drives [ 0 ] . drive_data = data0 ;
hwif - > drives [ 1 ] . drive_data = data1 ;
hwif - > drives [ 0 ] . io_32bit =
hwif - > drives [ 1 ] . io_32bit = 1 ;
hwif - > tuneproc = tuneproc ;
probe_hwif_init ( hwif ) ;
}
/*
* qd_unsetup :
*
* called to unsetup an ata channel : back to default values , unlinks tuning
*/
/*
static void __exit qd_unsetup ( ide_hwif_t * hwif )
{
u8 config = hwif - > config_data ;
int base = hwif - > select_data ;
void * tuneproc = ( void * ) hwif - > tuneproc ;
if ( hwif - > chipset ! = ide_qd65xx )
return ;
printk ( KERN_NOTICE " %s: back to defaults \n " , hwif - > name ) ;
hwif - > selectproc = NULL ;
hwif - > tuneproc = NULL ;
if ( tuneproc = = ( void * ) qd6500_tune_drive ) {
// will do it for both
qd_write_reg ( QD6500_DEF_DATA , QD_TIMREG ( & hwif - > drives [ 0 ] ) ) ;
} else if ( tuneproc = = ( void * ) qd6580_tune_drive ) {
if ( QD_CONTROL ( hwif ) & QD_CONTR_SEC_DISABLED ) {
qd_write_reg ( QD6580_DEF_DATA , QD_TIMREG ( & hwif - > drives [ 0 ] ) ) ;
qd_write_reg ( QD6580_DEF_DATA2 , QD_TIMREG ( & hwif - > drives [ 1 ] ) ) ;
} else {
qd_write_reg ( hwif - > channel ? QD6580_DEF_DATA2 : QD6580_DEF_DATA , QD_TIMREG ( & hwif - > drives [ 0 ] ) ) ;
}
} else {
printk ( KERN_WARNING " Unknown qd65xx tuning fonction ! \n " ) ;
printk ( KERN_WARNING " keeping settings ! \n " ) ;
}
}
*/
/*
* qd_probe :
*
* looks at the specified baseport , and if qd found , registers & initialises it
* return 1 if another qd may be probed
*/
static int __init qd_probe ( int base )
{
ide_hwif_t * hwif ;
u8 config ;
u8 unit ;
config = qd_read_reg ( QD_CONFIG_PORT ) ;
if ( ! ( ( config & QD_CONFIG_BASEPORT ) > > 1 = = ( base = = 0xb0 ) ) )
return 1 ;
unit = ! ( config & QD_CONFIG_IDE_BASEPORT ) ;
if ( ( config & 0xf0 ) = = QD_CONFIG_QD6500 ) {
if ( qd_testreg ( base ) ) return 1 ; /* bad register */
/* qd6500 found */
hwif = & ide_hwifs [ unit ] ;
printk ( KERN_NOTICE " %s: qd6500 at %#x \n " , hwif - > 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 ;
}
qd_setup ( hwif , base , config , QD6500_DEF_DATA , QD6500_DEF_DATA ,
& qd6500_tune_drive ) ;
create_proc_ide_interfaces ( ) ;
return 1 ;
}
if ( ( ( config & 0xf0 ) = = QD_CONFIG_QD6580_A ) | |
( ( config & 0xf0 ) = = QD_CONFIG_QD6580_B ) ) {
u8 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 ) {
/* secondary disabled */
hwif = & ide_hwifs [ unit ] ;
printk ( KERN_INFO " %s: qd6580: single IDE board \n " ,
hwif - > name ) ;
qd_setup ( hwif , base , config | ( control < < 8 ) ,
QD6580_DEF_DATA , QD6580_DEF_DATA2 ,
& qd6580_tune_drive ) ;
qd_write_reg ( QD_DEF_CONTR , QD_CONTROL_PORT ) ;
create_proc_ide_interfaces ( ) ;
return 1 ;
} else {
ide_hwif_t * mate ;
hwif = & ide_hwifs [ 0 ] ;
mate = & ide_hwifs [ 1 ] ;
/* secondary enabled */
printk ( KERN_INFO " %s&%s: qd6580: dual IDE board \n " ,
hwif - > name , mate - > name ) ;
qd_setup ( hwif , base , config | ( control < < 8 ) ,
QD6580_DEF_DATA , QD6580_DEF_DATA ,
& qd6580_tune_drive ) ;
qd_setup ( mate , base , config | ( control < < 8 ) ,
QD6580_DEF_DATA2 , QD6580_DEF_DATA2 ,
& qd6580_tune_drive ) ;
qd_write_reg ( QD_DEF_CONTR , QD_CONTROL_PORT ) ;
create_proc_ide_interfaces ( ) ;
return 0 ; /* no other qd65xx possible */
}
}
/* no qd65xx found */
return 1 ;
}
2007-03-03 19:48:55 +03:00
int probe_qd65xx = 0 ;
module_param_named ( probe , probe_qd65xx , bool , 0 ) ;
MODULE_PARM_DESC ( probe , " probe for QD65xx chipsets " ) ;
2005-04-17 02:20:36 +04:00
/* Can be called directly from ide.c. */
int __init qd65xx_init ( void )
{
2007-03-03 19:48:55 +03:00
if ( probe_qd65xx = = 0 )
return - ENODEV ;
2005-04-17 02:20:36 +04:00
if ( qd_probe ( 0x30 ) )
qd_probe ( 0xb0 ) ;
if ( ide_hwifs [ 0 ] . chipset ! = ide_qd65xx & &
ide_hwifs [ 1 ] . chipset ! = ide_qd65xx )
return - ENODEV ;
return 0 ;
}
# ifdef MODULE
module_init ( qd65xx_init ) ;
# endif
MODULE_AUTHOR ( " Samuel Thibault " ) ;
MODULE_DESCRIPTION ( " support of qd65xx vlb ide chipset " ) ;
MODULE_LICENSE ( " GPL " ) ;