2006-08-30 02:12:40 +04:00
/*
* pata_qdi . c - QDI VLB ATA controllers
* ( C ) 2006 Red Hat < alan @ redhat . com >
*
* This driver mostly exists as a proof of concept for non PCI devices under
* libata . While the QDI6580 was ' neat ' in 1993 it is no longer terribly
* useful .
*
* Tuning code written from the documentation at
* http : //www.ryston.cz/petr/vlb/qd6500.html
* http : //www.ryston.cz/petr/vlb/qd6580.html
*
* Probe code based on drivers / ide / legacy / qd65xx . c
* Rewritten from the work of Colten Edwards < pje120 @ cs . usask . ca > by
* Samuel Thibault < samuel . thibault @ fnac . net >
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/pci.h>
# include <linux/init.h>
# include <linux/blkdev.h>
# include <linux/delay.h>
# include <scsi/scsi_host.h>
# include <linux/libata.h>
# include <linux/platform_device.h>
# define DRV_NAME "pata_qdi"
2007-05-22 04:26:38 +04:00
# define DRV_VERSION "0.3.1"
2006-08-30 02:12:40 +04:00
# define NR_HOST 4 /* Two 6580s */
struct qdi_data {
unsigned long timing ;
u8 clock [ 2 ] ;
u8 last ;
int fast ;
struct platform_device * platform_dev ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
} ;
static struct ata_host * qdi_host [ NR_HOST ] ;
static struct qdi_data qdi_data [ NR_HOST ] ;
static int nr_qdi_host ;
# ifdef MODULE
static int probe_qdi = 1 ;
# else
static int probe_qdi ;
# endif
static void qdi6500_set_piomode ( struct ata_port * ap , struct ata_device * adev )
{
struct ata_timing t ;
struct qdi_data * qdi = ap - > host - > private_data ;
int active , recovery ;
u8 timing ;
/* Get the timing data in cycles */
ata_timing_compute ( adev , adev - > pio_mode , & t , 30303 , 1000 ) ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
if ( qdi - > fast ) {
active = 8 - FIT ( t . active , 1 , 8 ) ;
recovery = 18 - FIT ( t . recover , 3 , 18 ) ;
} else {
active = 9 - FIT ( t . active , 2 , 9 ) ;
recovery = 15 - FIT ( t . recover , 0 , 15 ) ;
}
timing = ( recovery < < 4 ) | active | 0x08 ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
qdi - > clock [ adev - > devno ] = timing ;
2006-08-31 08:03:49 +04:00
outb ( timing , qdi - > timing ) ;
2006-08-30 02:12:40 +04:00
}
static void qdi6580_set_piomode ( struct ata_port * ap , struct ata_device * adev )
{
struct ata_timing t ;
struct qdi_data * qdi = ap - > host - > private_data ;
int active , recovery ;
u8 timing ;
/* Get the timing data in cycles */
ata_timing_compute ( adev , adev - > pio_mode , & t , 30303 , 1000 ) ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
if ( qdi - > fast ) {
active = 8 - FIT ( t . active , 1 , 8 ) ;
recovery = 18 - FIT ( t . recover , 3 , 18 ) ;
} else {
active = 9 - FIT ( t . active , 2 , 9 ) ;
recovery = 15 - FIT ( t . recover , 0 , 15 ) ;
}
timing = ( recovery < < 4 ) | active | 0x08 ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
qdi - > clock [ adev - > devno ] = timing ;
outb ( timing , qdi - > timing ) ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
/* Clear the FIFO */
if ( adev - > class ! = ATA_DEV_ATA )
outb ( 0x5F , ( qdi - > timing & 0xFFF0 ) + 3 ) ;
}
/**
* qdi_qc_issue_prot - command issue
* @ qc : command pending
*
* Called when the libata layer is about to issue a command . We wrap
* this interface so that we can load the correct ATA timings .
*/
static unsigned int qdi_qc_issue_prot ( struct ata_queued_cmd * qc )
{
struct ata_port * ap = qc - > ap ;
struct ata_device * adev = qc - > dev ;
struct qdi_data * qdi = ap - > host - > private_data ;
if ( qdi - > clock [ adev - > devno ] ! = qdi - > last ) {
if ( adev - > pio_mode ) {
qdi - > last = qdi - > clock [ adev - > devno ] ;
outb ( qdi - > clock [ adev - > devno ] , qdi - > timing ) ;
}
}
return ata_qc_issue_prot ( qc ) ;
}
static void qdi_data_xfer ( struct ata_device * adev , unsigned char * buf , unsigned int buflen , int write_data )
{
struct ata_port * ap = adev - > ap ;
int slop = buflen & 3 ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
if ( ata_id_has_dword_io ( adev - > id ) ) {
if ( write_data )
2007-02-01 09:06:36 +03:00
iowrite32_rep ( ap - > ioaddr . data_addr , buf , buflen > > 2 ) ;
2006-08-30 02:12:40 +04:00
else
2007-02-01 09:06:36 +03:00
ioread32_rep ( ap - > ioaddr . data_addr , buf , buflen > > 2 ) ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
if ( unlikely ( slop ) ) {
u32 pad ;
if ( write_data ) {
memcpy ( & pad , buf + buflen - slop , slop ) ;
2007-02-01 09:06:36 +03:00
pad = le32_to_cpu ( pad ) ;
iowrite32 ( pad , ap - > ioaddr . data_addr ) ;
2006-08-30 02:12:40 +04:00
} else {
2007-02-01 09:06:36 +03:00
pad = ioread32 ( ap - > ioaddr . data_addr ) ;
pad = cpu_to_le32 ( pad ) ;
2006-08-30 02:12:40 +04:00
memcpy ( buf + buflen - slop , & pad , slop ) ;
}
}
} else
2007-02-01 09:06:36 +03:00
ata_data_xfer ( adev , buf , buflen , write_data ) ;
2006-08-30 02:12:40 +04:00
}
static struct scsi_host_template qdi_sht = {
. module = THIS_MODULE ,
. name = DRV_NAME ,
. ioctl = ata_scsi_ioctl ,
. queuecommand = ata_scsi_queuecmd ,
. can_queue = ATA_DEF_QUEUE ,
. this_id = ATA_SHT_THIS_ID ,
. sg_tablesize = LIBATA_MAX_PRD ,
. cmd_per_lun = ATA_SHT_CMD_PER_LUN ,
. emulated = ATA_SHT_EMULATED ,
. use_clustering = ATA_SHT_USE_CLUSTERING ,
. proc_name = DRV_NAME ,
. dma_boundary = ATA_DMA_BOUNDARY ,
. slave_configure = ata_scsi_slave_config ,
2006-11-29 05:26:47 +03:00
. slave_destroy = ata_scsi_slave_destroy ,
2006-08-30 02:12:40 +04:00
. bios_param = ata_std_bios_param ,
} ;
static struct ata_port_operations qdi6500_port_ops = {
. port_disable = ata_port_disable ,
. set_piomode = qdi6500_set_piomode ,
. tf_load = ata_tf_load ,
. tf_read = ata_tf_read ,
. check_status = ata_check_status ,
. exec_command = ata_exec_command ,
. dev_select = ata_std_dev_select ,
. freeze = ata_bmdma_freeze ,
. thaw = ata_bmdma_thaw ,
. error_handler = ata_bmdma_error_handler ,
. post_internal_cmd = ata_bmdma_post_internal_cmd ,
2007-03-27 09:43:40 +04:00
. cable_detect = ata_cable_40wire ,
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
. qc_prep = ata_qc_prep ,
. qc_issue = qdi_qc_issue_prot ,
2006-09-27 13:41:13 +04:00
2006-08-30 02:12:40 +04:00
. data_xfer = qdi_data_xfer ,
. irq_clear = ata_bmdma_irq_clear ,
2007-01-26 10:27:58 +03:00
. irq_on = ata_irq_on ,
. irq_ack = ata_irq_ack ,
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
. port_start = ata_port_start ,
2006-08-31 08:03:49 +04:00
} ;
2006-08-30 02:12:40 +04:00
static struct ata_port_operations qdi6580_port_ops = {
. port_disable = ata_port_disable ,
. set_piomode = qdi6580_set_piomode ,
. tf_load = ata_tf_load ,
. tf_read = ata_tf_read ,
. check_status = ata_check_status ,
. exec_command = ata_exec_command ,
. dev_select = ata_std_dev_select ,
. freeze = ata_bmdma_freeze ,
. thaw = ata_bmdma_thaw ,
. error_handler = ata_bmdma_error_handler ,
. post_internal_cmd = ata_bmdma_post_internal_cmd ,
2007-03-27 09:43:40 +04:00
. cable_detect = ata_cable_40wire ,
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
. qc_prep = ata_qc_prep ,
. qc_issue = qdi_qc_issue_prot ,
2006-09-27 13:41:13 +04:00
2006-08-30 02:12:40 +04:00
. data_xfer = qdi_data_xfer ,
. irq_clear = ata_bmdma_irq_clear ,
2007-01-26 10:27:58 +03:00
. irq_on = ata_irq_on ,
. irq_ack = ata_irq_ack ,
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
. port_start = ata_port_start ,
2006-08-31 08:03:49 +04:00
} ;
2006-08-30 02:12:40 +04:00
/**
* qdi_init_one - attach a qdi interface
* @ type : Type to display
* @ io : I / O port start
* @ irq : interrupt line
* @ fast : True if on a > 33 Mhz VLB
*
* Register an ISA bus IDE interface . Such interfaces are PIO and we
* assume do not support IRQ sharing .
*/
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
static __init int qdi_init_one ( unsigned long port , int type , unsigned long io , int irq , int fast )
{
struct platform_device * pdev ;
libata: convert the remaining PATA drivers to new init model
Convert pdc_adma, pata_cs5520, pata_isapnp, pata_ixp4xx_cf,
pata_legacy, pata_mpc52xx, pata_mpiix, pata_pcmcia, pata_pdc2027x,
pata_platform, pata_qdi, pata_scc and pata_winbond to new init model.
* init_one()'s now follow more consistent init order
* cs5520 now registers one host with two ports, not two hosts. If any
of the two ports are disabled, it's made dummy as other drivers do.
Tested pdc_adma and pata_legacy. Both are as broken as before. The
rest are compile tested only.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-04-17 18:44:08 +04:00
struct ata_host * host ;
struct ata_port * ap ;
2007-02-01 09:06:36 +03:00
void __iomem * io_addr , * ctl_addr ;
2006-08-30 02:12:40 +04:00
int ret ;
/*
* Fill in a probe structure first of all
*/
pdev = platform_device_register_simple ( DRV_NAME , nr_qdi_host , NULL , 0 ) ;
2006-12-16 00:08:51 +03:00
if ( IS_ERR ( pdev ) )
return PTR_ERR ( pdev ) ;
2006-08-31 08:03:49 +04:00
2007-02-01 09:06:36 +03:00
ret = - ENOMEM ;
io_addr = devm_ioport_map ( & pdev - > dev , io , 8 ) ;
ctl_addr = devm_ioport_map ( & pdev - > dev , io + 0x206 , 1 ) ;
if ( ! io_addr | | ! ctl_addr )
goto fail ;
libata: convert the remaining PATA drivers to new init model
Convert pdc_adma, pata_cs5520, pata_isapnp, pata_ixp4xx_cf,
pata_legacy, pata_mpc52xx, pata_mpiix, pata_pcmcia, pata_pdc2027x,
pata_platform, pata_qdi, pata_scc and pata_winbond to new init model.
* init_one()'s now follow more consistent init order
* cs5520 now registers one host with two ports, not two hosts. If any
of the two ports are disabled, it's made dummy as other drivers do.
Tested pdc_adma and pata_legacy. Both are as broken as before. The
rest are compile tested only.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-04-17 18:44:08 +04:00
ret = - ENOMEM ;
host = ata_host_alloc ( & pdev - > dev , 1 ) ;
if ( ! host )
goto fail ;
ap = host - > ports [ 0 ] ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
if ( type = = 6580 ) {
libata: convert the remaining PATA drivers to new init model
Convert pdc_adma, pata_cs5520, pata_isapnp, pata_ixp4xx_cf,
pata_legacy, pata_mpc52xx, pata_mpiix, pata_pcmcia, pata_pdc2027x,
pata_platform, pata_qdi, pata_scc and pata_winbond to new init model.
* init_one()'s now follow more consistent init order
* cs5520 now registers one host with two ports, not two hosts. If any
of the two ports are disabled, it's made dummy as other drivers do.
Tested pdc_adma and pata_legacy. Both are as broken as before. The
rest are compile tested only.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-04-17 18:44:08 +04:00
ap - > ops = & qdi6580_port_ops ;
ap - > pio_mask = 0x1F ;
ap - > flags | = ATA_FLAG_SLAVE_POSS ;
2006-08-30 02:12:40 +04:00
} else {
libata: convert the remaining PATA drivers to new init model
Convert pdc_adma, pata_cs5520, pata_isapnp, pata_ixp4xx_cf,
pata_legacy, pata_mpc52xx, pata_mpiix, pata_pcmcia, pata_pdc2027x,
pata_platform, pata_qdi, pata_scc and pata_winbond to new init model.
* init_one()'s now follow more consistent init order
* cs5520 now registers one host with two ports, not two hosts. If any
of the two ports are disabled, it's made dummy as other drivers do.
Tested pdc_adma and pata_legacy. Both are as broken as before. The
rest are compile tested only.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-04-17 18:44:08 +04:00
ap - > ops = & qdi6500_port_ops ;
ap - > pio_mask = 0x07 ; /* Actually PIO3 !IORDY is possible */
ap - > flags = ATA_FLAG_SLAVE_POSS | ATA_FLAG_NO_IORDY ;
2006-08-30 02:12:40 +04:00
}
2006-08-31 08:03:49 +04:00
libata: convert the remaining PATA drivers to new init model
Convert pdc_adma, pata_cs5520, pata_isapnp, pata_ixp4xx_cf,
pata_legacy, pata_mpc52xx, pata_mpiix, pata_pcmcia, pata_pdc2027x,
pata_platform, pata_qdi, pata_scc and pata_winbond to new init model.
* init_one()'s now follow more consistent init order
* cs5520 now registers one host with two ports, not two hosts. If any
of the two ports are disabled, it's made dummy as other drivers do.
Tested pdc_adma and pata_legacy. Both are as broken as before. The
rest are compile tested only.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-04-17 18:44:08 +04:00
ap - > ioaddr . cmd_addr = io_addr ;
ap - > ioaddr . altstatus_addr = ctl_addr ;
ap - > ioaddr . ctl_addr = ctl_addr ;
ata_std_ports ( & ap - > ioaddr ) ;
2006-08-30 02:12:40 +04:00
/*
* Hook in a private data structure per channel
*/
libata: convert the remaining PATA drivers to new init model
Convert pdc_adma, pata_cs5520, pata_isapnp, pata_ixp4xx_cf,
pata_legacy, pata_mpc52xx, pata_mpiix, pata_pcmcia, pata_pdc2027x,
pata_platform, pata_qdi, pata_scc and pata_winbond to new init model.
* init_one()'s now follow more consistent init order
* cs5520 now registers one host with two ports, not two hosts. If any
of the two ports are disabled, it's made dummy as other drivers do.
Tested pdc_adma and pata_legacy. Both are as broken as before. The
rest are compile tested only.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-04-17 18:44:08 +04:00
ap - > private_data = & qdi_data [ nr_qdi_host ] ;
2006-08-30 02:12:40 +04:00
qdi_data [ nr_qdi_host ] . timing = port ;
qdi_data [ nr_qdi_host ] . fast = fast ;
qdi_data [ nr_qdi_host ] . platform_dev = pdev ;
printk ( KERN_INFO DRV_NAME " : qd%d at 0x%lx. \n " , type , io ) ;
2007-02-01 09:06:36 +03:00
libata: convert the remaining PATA drivers to new init model
Convert pdc_adma, pata_cs5520, pata_isapnp, pata_ixp4xx_cf,
pata_legacy, pata_mpc52xx, pata_mpiix, pata_pcmcia, pata_pdc2027x,
pata_platform, pata_qdi, pata_scc and pata_winbond to new init model.
* init_one()'s now follow more consistent init order
* cs5520 now registers one host with two ports, not two hosts. If any
of the two ports are disabled, it's made dummy as other drivers do.
Tested pdc_adma and pata_legacy. Both are as broken as before. The
rest are compile tested only.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
2007-04-17 18:44:08 +04:00
/* activate */
ret = ata_host_activate ( host , irq , ata_interrupt , 0 , & qdi_sht ) ;
if ( ret )
2007-02-01 09:06:36 +03:00
goto fail ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
qdi_host [ nr_qdi_host + + ] = dev_get_drvdata ( & pdev - > dev ) ;
2006-08-31 08:03:49 +04:00
return 0 ;
2007-02-01 09:06:36 +03:00
fail :
platform_device_unregister ( pdev ) ;
return ret ;
2006-08-30 02:12:40 +04:00
}
/**
* qdi_init - attach qdi interfaces
*
* Attach qdi IDE interfaces by scanning the ports it may occupy .
*/
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
static __init int qdi_init ( void )
{
unsigned long flags ;
static const unsigned long qd_port [ 2 ] = { 0x30 , 0xB0 } ;
static const unsigned long ide_port [ 2 ] = { 0x170 , 0x1F0 } ;
static const int ide_irq [ 2 ] = { 14 , 15 } ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
int ct = 0 ;
int i ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
if ( probe_qdi = = 0 )
return - ENODEV ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
/*
* Check each possible QD65xx base address
*/
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
for ( i = 0 ; i < 2 ; i + + ) {
unsigned long port = qd_port [ i ] ;
u8 r , res ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
if ( request_region ( port , 2 , " pata_qdi " ) ) {
/* Check for a card */
local_irq_save ( flags ) ;
r = inb_p ( port ) ;
outb_p ( 0x19 , port ) ;
res = inb_p ( port ) ;
outb_p ( r , port ) ;
local_irq_restore ( flags ) ;
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
/* Fail */
if ( res = = 0x19 )
{
release_region ( port , 2 ) ;
continue ;
}
2006-08-31 08:03:49 +04:00
2006-08-30 02:12:40 +04:00
/* Passes the presence test */
r = inb_p ( port + 1 ) ; /* Check port agrees with port set */
if ( ( r & 2 ) > > 1 ! = i ) {
release_region ( port , 2 ) ;
continue ;
}
2006-08-31 08:03:49 +04:00
/* Check card type */
2006-08-30 02:12:40 +04:00
if ( ( r & 0xF0 ) = = 0xC0 ) {
/* QD6500: single channel */
if ( r & 8 ) {
/* Disabled ? */
release_region ( port , 2 ) ;
continue ;
}
2007-03-02 17:56:35 +03:00
if ( qdi_init_one ( port , 6500 , ide_port [ r & 0x01 ] , ide_irq [ r & 0x01 ] , r & 0x04 ) = = 0 )
ct + + ;
2006-08-30 02:12:40 +04:00
}
if ( ( ( r & 0xF0 ) = = 0xA0 ) | | ( r & 0xF0 ) = = 0x50 ) {
/* QD6580: dual channel */
if ( ! request_region ( port + 2 , 2 , " pata_qdi " ) )
{
release_region ( port , 2 ) ;
continue ;
}
res = inb ( port + 3 ) ;
if ( res & 1 ) {
/* Single channel mode */
2007-05-03 13:30:25 +04:00
if ( qdi_init_one ( port , 6580 , ide_port [ r & 0x01 ] , ide_irq [ r & 0x01 ] , r & 0x04 ) = = 0 )
2007-03-02 17:56:35 +03:00
ct + + ;
2006-08-30 02:12:40 +04:00
} else {
/* Dual channel mode */
2007-03-02 17:56:35 +03:00
if ( qdi_init_one ( port , 6580 , 0x1F0 , 14 , r & 0x04 ) = = 0 )
ct + + ;
if ( qdi_init_one ( port + 2 , 6580 , 0x170 , 15 , r & 0x04 ) = = 0 )
ct + + ;
2006-08-30 02:12:40 +04:00
}
}
}
}
if ( ct ! = 0 )
return 0 ;
return - ENODEV ;
}
static __exit void qdi_exit ( void )
{
int i ;
for ( i = 0 ; i < nr_qdi_host ; i + + ) {
2007-01-20 10:00:28 +03:00
ata_host_detach ( qdi_host [ i ] ) ;
2006-08-30 02:12:40 +04:00
/* Free the control resource. The 6580 dual channel has the resources
* claimed as a pair of 2 byte resources so we need no special cases . . .
*/
release_region ( qdi_data [ i ] . timing , 2 ) ;
platform_device_unregister ( qdi_data [ i ] . platform_dev ) ;
2006-08-31 08:03:49 +04:00
}
2006-08-30 02:12:40 +04:00
}
MODULE_AUTHOR ( " Alan Cox " ) ;
MODULE_DESCRIPTION ( " low-level driver for qdi ATA " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( DRV_VERSION ) ;
module_init ( qdi_init ) ;
module_exit ( qdi_exit ) ;
module_param ( probe_qdi , int , 0 ) ;