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/pci.h>
# include <linux/delay.h>
# include <linux/ide.h>
# include <linux/init.h>
# include <asm/io.h>
2008-07-25 00:53:32 +04:00
# define DRV_NAME "ns87415"
2005-04-17 02:20:36 +04:00
# 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 ) ;
}
2009-01-06 19:21:02 +03:00
static u8 superio_dma_sff_read_status ( ide_hwif_t * hwif )
2008-07-23 21:55:50 +04:00
{
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
}
2009-04-08 16:13:03 +04:00
static void superio_tf_read ( ide_drive_t * drive , struct ide_taskfile * tf ,
u8 valid )
2008-04-29 01:44:40 +04:00
{
struct ide_io_ports * io_ports = & drive - > hwif - > io_ports ;
2009-04-08 16:13:01 +04:00
if ( valid & IDE_VALID_ERROR )
2009-03-31 22:15:30 +04:00
tf - > error = inb ( io_ports - > feature_addr ) ;
2009-04-08 16:13:01 +04:00
if ( valid & IDE_VALID_NSECT )
2008-04-29 01:44:40 +04:00
tf - > nsect = inb ( io_ports - > nsect_addr ) ;
2009-04-08 16:13:01 +04:00
if ( valid & IDE_VALID_LBAL )
2008-04-29 01:44:40 +04:00
tf - > lbal = inb ( io_ports - > lbal_addr ) ;
2009-04-08 16:13:01 +04:00
if ( valid & IDE_VALID_LBAM )
2008-04-29 01:44:40 +04:00
tf - > lbam = inb ( io_ports - > lbam_addr ) ;
2009-04-08 16:13:01 +04:00
if ( valid & IDE_VALID_LBAH )
2008-04-29 01:44:40 +04:00
tf - > lbah = inb ( io_ports - > lbah_addr ) ;
2009-04-08 16:13:01 +04:00
if ( valid & IDE_VALID_DEVICE )
2008-04-29 01:44:40 +04:00
tf - > device = superio_ide_inb ( io_ports - > device_addr ) ;
}
2009-03-31 22:15:32 +04:00
static void ns87415_dev_select ( ide_drive_t * drive ) ;
2008-07-23 21:55:56 +04:00
static const struct ide_tp_ops superio_tp_ops = {
. exec_command = ide_exec_command ,
. read_status = superio_read_status ,
. read_altstatus = ide_read_altstatus ,
2009-03-31 22:15:30 +04:00
. write_devctl = ide_write_devctl ,
2008-07-23 21:55:56 +04:00
2009-03-31 22:15:32 +04:00
. dev_select = ns87415_dev_select ,
2008-07-23 21:55:56 +04:00
. tf_load = ide_tf_load ,
. tf_read = superio_tf_read ,
. input_data = ide_input_data ,
. output_data = ide_output_data ,
} ;
static void __devinit superio_init_iops ( struct hwif_s * hwif )
2005-04-17 02:20:36 +04:00
{
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
}
2009-01-06 19:21:02 +03:00
# else
# define superio_dma_sff_read_status ide_dma_sff_read_status
2005-04-17 02:20:36 +04:00
# endif
static unsigned int ns87415_count = 0 , ns87415_control [ MAX_HWIFS ] = { 0 } ;
/*
2008-10-13 23:39:36 +04:00
* This routine either enables / disables ( according to IDE_DFLAG_PRESENT )
2009-01-06 19:20:52 +03:00
* the IRQ associated with the port ,
2005-04-17 02:20:36 +04:00
* 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 )
{
2009-01-06 19:20:52 +03:00
ide_hwif_t * hwif = drive - > hwif ;
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 ) ;
2008-10-13 23:39:36 +04:00
if ( drive - > dev_flags & IDE_DFLAG_PRESENT )
new & = ~ bit ;
else
new | = bit ;
2005-04-17 02:20:36 +04:00
/* Select PIO or DMA, DMA may only be selected for one drive/channel. */
2008-10-13 23:39:40 +04:00
bit = 1 < < ( 20 + ( drive - > dn & 1 ) + ( hwif - > channel < < 1 ) ) ;
other = 1 < < ( 20 + ( 1 - ( drive - > dn & 1 ) ) + ( hwif - > channel < < 1 ) ) ;
2005-04-17 02:20:36 +04:00
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 ) ;
}
2009-03-31 22:15:32 +04:00
static void ns87415_dev_select ( ide_drive_t * drive )
2005-04-17 02:20:36 +04:00
{
2008-10-13 23:39:36 +04:00
ns87415_prepare_drive ( drive ,
! ! ( drive - > dev_flags & IDE_DFLAG_USING_DMA ) ) ;
2009-03-31 22:15:32 +04:00
outb ( drive - > select | ATA_DEVICE_OBS , drive - > hwif - > io_ports . device_addr ) ;
2005-04-17 02:20:36 +04:00
}
2009-03-31 22:15:20 +04:00
static void ns87415_dma_start ( ide_drive_t * drive )
{
ns87415_prepare_drive ( drive , 1 ) ;
ide_dma_start ( drive ) ;
}
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
{
2009-01-06 19:20:52 +03:00
ide_hwif_t * hwif = drive - > hwif ;
2005-04-17 02:20:36 +04:00
u8 dma_stat = 0 , dma_cmd = 0 ;
2009-01-06 19:21:02 +03:00
dma_stat = hwif - > dma_ops - > dma_sff_read_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
ns87415_prepare_drive ( drive , 0 ) ;
2009-03-31 22:15:20 +04:00
/* verify good DMA status */
return ( dma_stat & 7 ) ! = 4 ;
2005-04-17 02:20:36 +04:00
}
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 . .
*
2009-03-31 22:15:32 +04:00
* IRQs get unmasked in dev_select ( ) when drive is first used .
2005-04-17 02:20:36 +04:00
*/
( 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
2009-03-31 22:15:33 +04:00
* dev_select ( ) 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:56 +04:00
stat = hwif - > tp_ops - > read_status ( hwif ) ;
2008-10-11 00:39:21 +04:00
if ( stat = = 0xff )
break ;
} while ( ( stat & ATA_BUSY ) & & - - timeout ) ;
2005-04-17 02:20:36 +04:00
# endif
}
if ( ! using_inta )
2009-03-25 01:22:48 +03:00
hwif - > irq = pci_get_legacy_ide_irq ( dev , hwif - > channel ) ;
2005-04-17 02:20:36 +04:00
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
}
2009-03-31 22:15:32 +04:00
static const struct ide_tp_ops ns87415_tp_ops = {
. exec_command = ide_exec_command ,
. read_status = ide_read_status ,
. read_altstatus = ide_read_altstatus ,
. write_devctl = ide_write_devctl ,
. dev_select = ns87415_dev_select ,
. tf_load = ide_tf_load ,
. tf_read = ide_tf_read ,
. input_data = ide_input_data ,
. output_data = ide_output_data ,
2008-04-27 00:25:14 +04:00
} ;
2008-04-27 00:25:24 +04:00
static const struct ide_dma_ops ns87415_dma_ops = {
. dma_host_set = ide_dma_host_set ,
2009-03-31 22:15:20 +04:00
. dma_setup = ide_dma_setup ,
. dma_start = ns87415_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 ,
2009-03-27 14:46:47 +03:00
. dma_timer_expiry = ide_dma_sff_timer_expiry ,
2009-01-06 19:21:02 +03:00
. dma_sff_read_status = superio_dma_sff_read_status ,
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 = {
2008-07-25 00:53:32 +04:00
. name = DRV_NAME ,
2005-04-17 02:20:36 +04:00
. init_hwif = init_hwif_ns87415 ,
2009-03-31 22:15:32 +04:00
. tp_ops = & ns87415_tp_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 )
{
2008-07-23 21:55:56 +04:00
struct ide_port_info d = ns87415_chipset ;
# ifdef CONFIG_SUPERIO
if ( PCI_SLOT ( dev - > devfn ) = = 0xE ) {
/* Built-in - assume it's under superio. */
d . init_iops = superio_init_iops ;
d . tp_ops = & superio_tp_ops ;
}
# endif
2008-07-25 00:53:14 +04:00
return ide_pci_init_one ( dev , & d , NULL ) ;
2005-04-17 02:20:36 +04:00
}
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 ) ;
2008-10-13 23:39:41 +04:00
static struct pci_driver ns87415_pci_driver = {
2005-04-17 02:20:36 +04:00
. name = " NS87415_IDE " ,
. id_table = ns87415_pci_tbl ,
. probe = ns87415_init_one ,
2008-07-25 00:53:23 +04:00
. remove = ide_pci_remove ,
2008-10-11 00:39:32 +04:00
. suspend = ide_pci_suspend ,
. resume = ide_pci_resume ,
2005-04-17 02:20:36 +04:00
} ;
2007-01-27 15:46:56 +03:00
static int __init ns87415_ide_init ( void )
2005-04-17 02:20:36 +04:00
{
2008-10-13 23:39:41 +04:00
return ide_pci_register_driver ( & ns87415_pci_driver ) ;
2005-04-17 02:20:36 +04:00
}
2008-07-25 00:53:23 +04:00
static void __exit ns87415_ide_exit ( void )
{
2008-10-13 23:39:41 +04:00
pci_unregister_driver ( & ns87415_pci_driver ) ;
2008-07-25 00:53:23 +04:00
}
2005-04-17 02:20:36 +04:00
module_init ( ns87415_ide_init ) ;
2008-07-25 00:53:23 +04:00
module_exit ( ns87415_ide_exit ) ;
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Mark Lord, Eddie Dost, Andre Hedrick " ) ;
MODULE_DESCRIPTION ( " PCI driver module for NS87415 IDE " ) ;
MODULE_LICENSE ( " GPL " ) ;