2005-04-17 02:20:36 +04:00
/*
* Copyright ( C ) 1997 - 1998 Mark Lord < mlord @ pobox . com >
* Copyright ( C ) 1998 Eddie C . Dost < ecd @ skynet . be >
* Copyright ( C ) 1999 - 2000 Andre Hedrick < andre @ linux - ide . org >
* Copyright ( C ) 2004 Grant Grundler < grundler at parisc - linux . org >
*
* Inspired by an earlier effort from David S . Miller < davem @ redhat . com >
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/interrupt.h>
# include <linux/hdreg.h>
# include <linux/pci.h>
# include <linux/delay.h>
# include <linux/ide.h>
# include <linux/init.h>
# include <asm/io.h>
# ifdef CONFIG_SUPERIO
/* SUPERIO 87560 is a PoS chip that NatSem denies exists.
* Unfortunately , it ' s built - in on all Astro - based PA - RISC workstations
* which use the integrated NS87514 cell for CD - ROM support .
* i . e we have to support for CD - ROM installs .
* See drivers / parisc / superio . c for more gory details .
*/
# include <asm/superio.h>
# define SUPERIO_IDE_MAX_RETRIES 25
/* Because of a defect in Super I/O, all reads of the PCI DMA status
* registers , IDE status register and the IDE select register need to be
* retried
*/
static u8 superio_ide_inb ( unsigned long port )
{
2008-07-23 21:55:54 +04:00
u8 tmp ;
int retries = SUPERIO_IDE_MAX_RETRIES ;
/* printk(" [ reading port 0x%x with retry ] ", port); */
2005-04-17 02:20:36 +04:00
2008-07-23 21:55:54 +04:00
do {
tmp = inb ( port ) ;
if ( tmp = = 0 )
udelay ( 50 ) ;
} while ( tmp = = 0 & & retries - - > 0 ) ;
return tmp ;
2005-04-17 02:20:36 +04:00
}
2008-07-23 21:55:52 +04:00
static u8 superio_read_status ( ide_hwif_t * hwif )
{
return superio_ide_inb ( hwif - > io_ports . status_addr ) ;
}
2008-07-23 21:55:50 +04:00
static u8 superio_read_sff_dma_status ( ide_hwif_t * hwif )
{
2008-07-23 21:55:51 +04:00
return superio_ide_inb ( hwif - > dma_base + ATA_DMA_STATUS ) ;
2008-07-23 21:55:50 +04:00
}
2008-04-29 01:44:40 +04:00
static void superio_tf_read ( ide_drive_t * drive , ide_task_t * task )
{
struct ide_io_ports * io_ports = & drive - > hwif - > io_ports ;
struct ide_taskfile * tf = & task - > tf ;
if ( task - > tf_flags & IDE_TFLAG_IN_DATA ) {
u16 data = inw ( io_ports - > data_addr ) ;
tf - > data = data & 0xff ;
tf - > hob_data = ( data > > 8 ) & 0xff ;
}
/* be sure we're looking at the low order bits */
2008-07-15 23:21:50 +04:00
outb ( ATA_DEVCTL_OBS & ~ 0x80 , io_ports - > ctl_addr ) ;
2008-04-29 01:44:40 +04:00
2008-07-23 21:55:53 +04:00
if ( task - > tf_flags & IDE_TFLAG_IN_FEATURE )
tf - > feature = inb ( io_ports - > feature_addr ) ;
2008-04-29 01:44:40 +04:00
if ( task - > tf_flags & IDE_TFLAG_IN_NSECT )
tf - > nsect = inb ( io_ports - > nsect_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_LBAL )
tf - > lbal = inb ( io_ports - > lbal_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_LBAM )
tf - > lbam = inb ( io_ports - > lbam_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_LBAH )
tf - > lbah = inb ( io_ports - > lbah_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_DEVICE )
tf - > device = superio_ide_inb ( io_ports - > device_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_LBA48 ) {
2008-07-15 23:21:50 +04:00
outb ( ATA_DEVCTL_OBS | 0x80 , io_ports - > ctl_addr ) ;
2008-04-29 01:44:40 +04:00
if ( task - > tf_flags & IDE_TFLAG_IN_HOB_FEATURE )
tf - > hob_feature = inb ( io_ports - > feature_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_HOB_NSECT )
tf - > hob_nsect = inb ( io_ports - > nsect_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_HOB_LBAL )
tf - > hob_lbal = inb ( io_ports - > lbal_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_HOB_LBAM )
tf - > hob_lbam = inb ( io_ports - > lbam_addr ) ;
if ( task - > tf_flags & IDE_TFLAG_IN_HOB_LBAH )
tf - > hob_lbah = inb ( io_ports - > lbah_addr ) ;
}
}
2005-04-17 02:20:36 +04:00
static void __devinit superio_ide_init_iops ( struct hwif_s * hwif )
{
2008-02-02 01:09:31 +03:00
struct pci_dev * pdev = to_pci_dev ( hwif - > dev ) ;
2008-07-23 21:55:54 +04:00
u32 dma_stat ;
2008-02-02 01:09:31 +03:00
u8 port = hwif - > channel , tmp ;
2005-04-17 02:20:36 +04:00
2008-07-23 21:55:54 +04:00
dma_stat = ( pci_resource_start ( pdev , 4 ) & ~ 3 ) + ( ! port ? 2 : 0xa ) ;
2005-04-17 02:20:36 +04:00
/* Clear error/interrupt, enable dma */
2008-07-23 21:55:54 +04:00
tmp = superio_ide_inb ( dma_stat ) ;
outb ( tmp | 0x66 , dma_stat ) ;
2005-04-17 02:20:36 +04:00
2008-07-23 21:55:52 +04:00
hwif - > read_status = superio_read_status ;
2008-07-23 21:55:50 +04:00
hwif - > read_sff_dma_status = superio_read_sff_dma_status ;
2008-04-29 01:44:40 +04:00
hwif - > tf_read = superio_tf_read ;
2005-04-17 02:20:36 +04:00
}
static void __devinit init_iops_ns87415 ( ide_hwif_t * hwif )
{
2008-02-02 01:09:31 +03:00
struct pci_dev * dev = to_pci_dev ( hwif - > dev ) ;
if ( PCI_SLOT ( dev - > devfn ) = = 0xE )
2005-04-17 02:20:36 +04:00
/* Built-in - assume it's under superio. */
superio_ide_init_iops ( hwif ) ;
}
# endif
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 ) ;
2008-02-02 01:09:31 +03:00
struct pci_dev * dev = to_pci_dev ( hwif - > dev ) ;
2005-04-17 02:20:36 +04:00
unsigned int bit , other , new , * old = ( unsigned int * ) hwif - > select_data ;
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 ) ;
}
2008-04-27 00:25:24 +04:00
static int ns87415_dma_end ( ide_drive_t * drive )
2005-04-17 02:20:36 +04:00
{
ide_hwif_t * hwif = HWIF ( drive ) ;
u8 dma_stat = 0 , dma_cmd = 0 ;
drive - > waiting_for_dma = 0 ;
2008-07-23 21:55:50 +04:00
dma_stat = hwif - > read_sff_dma_status ( hwif ) ;
2008-07-23 21:55:51 +04:00
/* get DMA command mode */
dma_cmd = inb ( hwif - > dma_base + ATA_DMA_CMD ) ;
2005-04-17 02:20:36 +04:00
/* stop DMA */
2008-07-23 21:55:51 +04:00
outb ( dma_cmd & ~ 1 , hwif - > dma_base + ATA_DMA_CMD ) ;
2005-04-17 02:20:36 +04:00
/* from ERRATA: clear the INTR & ERROR bits */
2008-07-23 21:55:51 +04:00
dma_cmd = inb ( hwif - > dma_base + ATA_DMA_CMD ) ;
outb ( dma_cmd | 6 , hwif - > dma_base + ATA_DMA_CMD ) ;
2005-04-17 02:20:36 +04:00
/* and free any DMA resources */
ide_destroy_dmatable ( drive ) ;
/* verify good DMA status */
return ( dma_stat & 7 ) ! = 4 ;
}
2008-04-27 00:25:24 +04:00
static int ns87415_dma_setup ( ide_drive_t * drive )
2005-04-17 02:20:36 +04:00
{
/* select DMA xfer */
ns87415_prepare_drive ( drive , 1 ) ;
if ( ! ide_dma_setup ( drive ) )
return 0 ;
/* DMA failed: select PIO xfer */
ns87415_prepare_drive ( drive , 0 ) ;
return 1 ;
}
2005-07-03 18:31:04 +04:00
static void __devinit init_hwif_ns87415 ( ide_hwif_t * hwif )
2005-04-17 02:20:36 +04:00
{
2008-02-02 01:09:31 +03:00
struct pci_dev * dev = to_pci_dev ( hwif - > dev ) ;
2005-04-17 02:20:36 +04:00
unsigned int ctrl , using_inta ;
u8 progif ;
# ifdef __sparc_v9__
int timeout ;
u8 stat ;
# 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__
/*
2008-02-02 01:09:36 +03:00
* XXX : Reset the device , if we don ' t it will not respond to
* SELECT_DRIVE ( ) properly during first ide_probe_port ( ) .
2005-04-17 02:20:36 +04:00
*/
timeout = 10000 ;
2008-04-27 17:38:32 +04:00
outb ( 12 , hwif - > io_ports . ctl_addr ) ;
2005-04-17 02:20:36 +04:00
udelay ( 10 ) ;
2008-04-27 17:38:32 +04:00
outb ( 8 , hwif - > io_ports . ctl_addr ) ;
2005-04-17 02:20:36 +04:00
do {
udelay ( 50 ) ;
2008-07-23 21:55:52 +04:00
stat = hwif - > read_status ( hwif ) ;
2005-04-17 02:20:36 +04:00
if ( stat = = 0xff )
break ;
} while ( ( stat & BUSY_STAT ) & & - - timeout ) ;
# endif
}
if ( ! using_inta )
2008-07-08 21:27:22 +04:00
hwif - > irq = __ide_default_irq ( hwif - > io_ports . data_addr ) ;
2005-04-17 02:20:36 +04:00
else if ( ! hwif - > irq & & hwif - > mate & & hwif - > mate - > irq )
hwif - > irq = hwif - > mate - > irq ; /* share IRQ with mate */
if ( ! hwif - > dma_base )
return ;
2008-07-23 21:55:51 +04:00
outb ( 0x60 , hwif - > dma_base + ATA_DMA_STATUS ) ;
2005-04-17 02:20:36 +04:00
}
2008-04-27 00:25:14 +04:00
static const struct ide_port_ops ns87415_port_ops = {
. selectproc = ns87415_selectproc ,
} ;
2008-04-27 00:25:24 +04:00
static const struct ide_dma_ops ns87415_dma_ops = {
. dma_host_set = ide_dma_host_set ,
2008-04-27 00:25:24 +04:00
. dma_setup = ns87415_dma_setup ,
2008-04-27 00:25:24 +04:00
. dma_exec_cmd = ide_dma_exec_cmd ,
. dma_start = ide_dma_start ,
2008-04-27 00:25:24 +04:00
. dma_end = ns87415_dma_end ,
2008-04-27 00:25:24 +04:00
. dma_test_irq = ide_dma_test_irq ,
. dma_lost_irq = ide_dma_lost_irq ,
. dma_timeout = ide_dma_timeout ,
2008-04-27 00:25:24 +04:00
} ;
2007-10-20 02:32:34 +04:00
static const struct ide_port_info ns87415_chipset __devinitdata = {
2005-04-17 02:20:36 +04:00
. name = " NS87415 " ,
# ifdef CONFIG_SUPERIO
. init_iops = init_iops_ns87415 ,
# endif
. init_hwif = init_hwif_ns87415 ,
2008-04-27 00:25:14 +04:00
. port_ops = & ns87415_port_ops ,
2008-04-27 00:25:24 +04:00
. dma_ops = & ns87415_dma_ops ,
2007-10-19 02:30:06 +04:00
. host_flags = IDE_HFLAG_TRUST_BIOS_FOR_DMA |
2008-04-26 19:36:35 +04:00
IDE_HFLAG_NO_ATAPI_DMA ,
2005-04-17 02:20:36 +04:00
} ;
static int __devinit ns87415_init_one ( struct pci_dev * dev , const struct pci_device_id * id )
{
return ide_setup_pci_device ( dev , & ns87415_chipset ) ;
}
2007-10-17 00:29:56 +04:00
static const struct pci_device_id ns87415_pci_tbl [ ] = {
{ PCI_VDEVICE ( NS , PCI_DEVICE_ID_NS_87415 ) , 0 } ,
2005-04-17 02:20:36 +04:00
{ 0 , } ,
} ;
MODULE_DEVICE_TABLE ( pci , ns87415_pci_tbl ) ;
static struct pci_driver driver = {
. name = " NS87415_IDE " ,
. id_table = ns87415_pci_tbl ,
. probe = ns87415_init_one ,
} ;
2007-01-27 15:46:56 +03:00
static int __init ns87415_ide_init ( void )
2005-04-17 02:20:36 +04:00
{
return ide_pci_register_driver ( & driver ) ;
}
module_init ( ns87415_ide_init ) ;
MODULE_AUTHOR ( " Mark Lord, Eddie Dost, Andre Hedrick " ) ;
MODULE_DESCRIPTION ( " PCI driver module for NS87415 IDE " ) ;
MODULE_LICENSE ( " GPL " ) ;