2006-02-09 13:15:27 +03:00
/*
2007-07-16 19:23:03 +04:00
* libata - sff . c - helper library for PCI IDE BMDMA
2006-02-09 13:15:27 +03:00
*
* Maintained by : Jeff Garzik < jgarzik @ pobox . com >
* Please ALWAYS copy linux - ide @ vger . kernel . org
* on emails .
*
* Copyright 2003 - 2006 Red Hat , Inc . All rights reserved .
* Copyright 2003 - 2006 Jeff Garzik
*
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 , or ( at your option )
* any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; see the file COPYING . If not , write to
* the Free Software Foundation , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*
* libata documentation is available via ' make { ps | pdf } docs ' ,
* as Documentation / DocBook / libata . *
*
* Hardware documentation available from http : //www.t13.org/ and
* http : //www.sata-io.org/
*
*/
# include <linux/kernel.h>
# include <linux/pci.h>
# include <linux/libata.h>
# include "libata.h"
2006-10-09 06:10:26 +04:00
/**
* ata_irq_on - Enable interrupts on a port .
* @ ap : Port on which interrupts are enabled .
*
* Enable interrupts on a legacy IDE device using MMIO or PIO ,
* wait for idle , clear any pending interrupts .
*
* LOCKING :
* Inherited from caller .
*/
u8 ata_irq_on ( struct ata_port * ap )
{
struct ata_ioports * ioaddr = & ap - > ioaddr ;
u8 tmp ;
ap - > ctl & = ~ ATA_NIEN ;
ap - > last_ctl = ap - > ctl ;
2008-03-06 07:12:54 +03:00
if ( ioaddr - > ctl_addr )
iowrite8 ( ap - > ctl , ioaddr - > ctl_addr ) ;
2006-10-09 06:10:26 +04:00
tmp = ata_wait_idle ( ap ) ;
ap - > ops - > irq_clear ( ap ) ;
return tmp ;
}
2006-02-09 13:15:27 +03:00
/**
2007-02-01 09:06:36 +03:00
* ata_tf_load - send taskfile registers to host controller
2006-02-09 13:15:27 +03:00
* @ ap : Port to which output is sent
* @ tf : ATA taskfile register set
*
* Outputs ATA taskfile to standard ATA host controller .
*
* LOCKING :
* Inherited from caller .
*/
2007-02-01 09:06:36 +03:00
void ata_tf_load ( struct ata_port * ap , const struct ata_taskfile * tf )
2006-02-09 13:15:27 +03:00
{
struct ata_ioports * ioaddr = & ap - > ioaddr ;
unsigned int is_addr = tf - > flags & ATA_TFLAG_ISADDR ;
if ( tf - > ctl ! = ap - > last_ctl ) {
2008-03-06 07:12:54 +03:00
if ( ioaddr - > ctl_addr )
iowrite8 ( tf - > ctl , ioaddr - > ctl_addr ) ;
2006-02-09 13:15:27 +03:00
ap - > last_ctl = tf - > ctl ;
ata_wait_idle ( ap ) ;
}
if ( is_addr & & ( tf - > flags & ATA_TFLAG_LBA48 ) ) {
2008-03-06 07:12:54 +03:00
WARN_ON ( ! ioaddr - > ctl_addr ) ;
2007-02-01 09:06:36 +03:00
iowrite8 ( tf - > hob_feature , ioaddr - > feature_addr ) ;
iowrite8 ( tf - > hob_nsect , ioaddr - > nsect_addr ) ;
iowrite8 ( tf - > hob_lbal , ioaddr - > lbal_addr ) ;
iowrite8 ( tf - > hob_lbam , ioaddr - > lbam_addr ) ;
iowrite8 ( tf - > hob_lbah , ioaddr - > lbah_addr ) ;
2006-02-09 13:15:27 +03:00
VPRINTK ( " hob: feat 0x%X nsect 0x%X, lba 0x%X 0x%X 0x%X \n " ,
tf - > hob_feature ,
tf - > hob_nsect ,
tf - > hob_lbal ,
tf - > hob_lbam ,
tf - > hob_lbah ) ;
}
if ( is_addr ) {
2007-02-01 09:06:36 +03:00
iowrite8 ( tf - > feature , ioaddr - > feature_addr ) ;
iowrite8 ( tf - > nsect , ioaddr - > nsect_addr ) ;
iowrite8 ( tf - > lbal , ioaddr - > lbal_addr ) ;
iowrite8 ( tf - > lbam , ioaddr - > lbam_addr ) ;
iowrite8 ( tf - > lbah , ioaddr - > lbah_addr ) ;
2006-02-09 13:15:27 +03:00
VPRINTK ( " feat 0x%X nsect 0x%X lba 0x%X 0x%X 0x%X \n " ,
tf - > feature ,
tf - > nsect ,
tf - > lbal ,
tf - > lbam ,
tf - > lbah ) ;
}
if ( tf - > flags & ATA_TFLAG_DEVICE ) {
2007-02-01 09:06:36 +03:00
iowrite8 ( tf - > device , ioaddr - > device_addr ) ;
2006-02-09 13:15:27 +03:00
VPRINTK ( " device 0x%X \n " , tf - > device ) ;
}
ata_wait_idle ( ap ) ;
}
/**
2007-02-01 09:06:36 +03:00
* ata_exec_command - issue ATA command to host controller
2006-02-09 13:15:27 +03:00
* @ ap : port to which command is being issued
* @ tf : ATA taskfile register set
*
2007-02-01 09:06:36 +03:00
* Issues ATA command , with proper synchronization with interrupt
* handler / other threads .
2006-03-22 18:47:34 +03:00
*
2006-02-09 13:15:27 +03:00
* LOCKING :
2006-08-24 11:19:22 +04:00
* spin_lock_irqsave ( host lock )
2006-02-09 13:15:27 +03:00
*/
2007-02-01 09:06:36 +03:00
void ata_exec_command ( struct ata_port * ap , const struct ata_taskfile * tf )
2006-02-09 13:15:27 +03:00
{
2007-02-20 19:06:51 +03:00
DPRINTK ( " ata%u: cmd 0x%X \n " , ap - > print_id , tf - > command ) ;
2006-02-09 13:15:27 +03:00
2007-02-01 09:06:36 +03:00
iowrite8 ( tf - > command , ap - > ioaddr . command_addr ) ;
2006-02-09 13:15:27 +03:00
ata_pause ( ap ) ;
}
/**
2007-02-01 09:06:36 +03:00
* ata_tf_read - input device ' s ATA taskfile shadow registers
2006-02-09 13:15:27 +03:00
* @ ap : Port from which input is read
* @ tf : ATA taskfile register set for storing input
*
* Reads ATA taskfile registers for currently - selected device
2007-11-19 17:34:56 +03:00
* into @ tf . Assumes the device has a fully SFF compliant task file
* layout and behaviour . If you device does not ( eg has a different
* status method ) then you will need to provide a replacement tf_read
2006-02-09 13:15:27 +03:00
*
* LOCKING :
* Inherited from caller .
*/
2007-02-01 09:06:36 +03:00
void ata_tf_read ( struct ata_port * ap , struct ata_taskfile * tf )
2006-02-09 13:15:27 +03:00
{
struct ata_ioports * ioaddr = & ap - > ioaddr ;
2007-11-19 17:34:56 +03:00
tf - > command = ata_check_status ( ap ) ;
2007-02-01 09:06:36 +03:00
tf - > feature = ioread8 ( ioaddr - > error_addr ) ;
tf - > nsect = ioread8 ( ioaddr - > nsect_addr ) ;
tf - > lbal = ioread8 ( ioaddr - > lbal_addr ) ;
tf - > lbam = ioread8 ( ioaddr - > lbam_addr ) ;
tf - > lbah = ioread8 ( ioaddr - > lbah_addr ) ;
tf - > device = ioread8 ( ioaddr - > device_addr ) ;
2006-02-09 13:15:27 +03:00
if ( tf - > flags & ATA_TFLAG_LBA48 ) {
2008-03-06 07:12:54 +03:00
if ( likely ( ioaddr - > ctl_addr ) ) {
iowrite8 ( tf - > ctl | ATA_HOB , ioaddr - > ctl_addr ) ;
tf - > hob_feature = ioread8 ( ioaddr - > error_addr ) ;
tf - > hob_nsect = ioread8 ( ioaddr - > nsect_addr ) ;
tf - > hob_lbal = ioread8 ( ioaddr - > lbal_addr ) ;
tf - > hob_lbam = ioread8 ( ioaddr - > lbam_addr ) ;
tf - > hob_lbah = ioread8 ( ioaddr - > lbah_addr ) ;
iowrite8 ( tf - > ctl , ioaddr - > ctl_addr ) ;
ap - > last_ctl = tf - > ctl ;
} else
WARN_ON ( 1 ) ;
2006-02-09 13:15:27 +03:00
}
}
/**
* ata_check_status - Read device status reg & clear interrupt
* @ ap : port where the device is
*
* Reads ATA taskfile status register for currently - selected device
* and return its value . This also clears pending interrupts
* from this device
*
* LOCKING :
* Inherited from caller .
*/
u8 ata_check_status ( struct ata_port * ap )
{
2007-02-01 09:06:36 +03:00
return ioread8 ( ap - > ioaddr . status_addr ) ;
2006-02-09 13:15:27 +03:00
}
/**
* ata_altstatus - Read device alternate status reg
* @ ap : port where the device is
*
* Reads ATA taskfile alternate status register for
* currently - selected device and return its value .
*
* Note : may NOT be used as the check_altstatus ( ) entry in
* ata_port_operations .
*
* LOCKING :
* Inherited from caller .
*/
u8 ata_altstatus ( struct ata_port * ap )
{
if ( ap - > ops - > check_altstatus )
return ap - > ops - > check_altstatus ( ap ) ;
2007-02-01 09:06:36 +03:00
return ioread8 ( ap - > ioaddr . altstatus_addr ) ;
2006-02-09 13:15:27 +03:00
}
2006-03-23 08:32:03 +03:00
/**
2007-02-01 09:06:36 +03:00
* ata_bmdma_setup - Set up PCI IDE BMDMA transaction
2006-03-23 08:32:03 +03:00
* @ qc : Info associated with this ATA transaction .
*
* LOCKING :
2006-08-24 11:19:22 +04:00
* spin_lock_irqsave ( host lock )
2006-03-23 08:32:03 +03:00
*/
2007-02-01 09:06:36 +03:00
void ata_bmdma_setup ( struct ata_queued_cmd * qc )
2006-03-23 08:32:03 +03:00
{
struct ata_port * ap = qc - > ap ;
unsigned int rw = ( qc - > tf . flags & ATA_TFLAG_WRITE ) ;
u8 dmactl ;
/* load PRD table addr. */
mb ( ) ; /* make sure PRD table writes are visible to controller */
2007-02-01 09:06:36 +03:00
iowrite32 ( ap - > prd_dma , ap - > ioaddr . bmdma_addr + ATA_DMA_TABLE_OFS ) ;
2006-03-23 08:32:03 +03:00
/* specify data direction, triple-check start bit is clear */
2007-02-01 09:06:36 +03:00
dmactl = ioread8 ( ap - > ioaddr . bmdma_addr + ATA_DMA_CMD ) ;
2006-03-23 08:32:03 +03:00
dmactl & = ~ ( ATA_DMA_WR | ATA_DMA_START ) ;
if ( ! rw )
dmactl | = ATA_DMA_WR ;
2007-02-01 09:06:36 +03:00
iowrite8 ( dmactl , ap - > ioaddr . bmdma_addr + ATA_DMA_CMD ) ;
2006-03-23 08:32:03 +03:00
/* issue r/w command */
ap - > ops - > exec_command ( ap , & qc - > tf ) ;
}
/**
2007-02-01 09:06:36 +03:00
* ata_bmdma_start - Start a PCI IDE BMDMA transaction
2006-03-23 08:32:03 +03:00
* @ qc : Info associated with this ATA transaction .
*
* LOCKING :
2006-08-24 11:19:22 +04:00
* spin_lock_irqsave ( host lock )
2006-03-23 08:32:03 +03:00
*/
2007-10-19 14:42:56 +04:00
void ata_bmdma_start ( struct ata_queued_cmd * qc )
2006-03-23 08:32:03 +03:00
{
struct ata_port * ap = qc - > ap ;
u8 dmactl ;
/* start host DMA transaction */
2007-02-01 09:06:36 +03:00
dmactl = ioread8 ( ap - > ioaddr . bmdma_addr + ATA_DMA_CMD ) ;
iowrite8 ( dmactl | ATA_DMA_START , ap - > ioaddr . bmdma_addr + ATA_DMA_CMD ) ;
2006-03-23 08:32:03 +03:00
2007-09-20 18:03:07 +04:00
/* Strictly, one may wish to issue an ioread8() here, to
2006-03-23 08:32:03 +03:00
* flush the mmio write . However , control also passes
* to the hardware at this point , and it will interrupt
* us when we are to resume control . So , in effect ,
* we don ' t care when the mmio write flushes .
* Further , a read of the DMA status register _immediately_
* following the write may not be what certain flaky hardware
* is expected , so I think it is best to not add a readb ( )
* without first all the MMIO ATA cards / mobos .
* Or maybe I ' m just being paranoid .
2007-09-20 18:03:07 +04:00
*
* FIXME : The posting of this write means I / O starts are
* unneccessarily delayed for MMIO
2006-03-23 08:32:03 +03:00
*/
}
/**
* ata_bmdma_irq_clear - Clear PCI IDE BMDMA interrupt .
* @ ap : Port associated with this ATA transaction .
*
* Clear interrupt and error flags in DMA status register .
*
* May be used as the irq_clear ( ) entry in ata_port_operations .
*
* LOCKING :
2006-08-24 11:19:22 +04:00
* spin_lock_irqsave ( host lock )
2006-03-23 08:32:03 +03:00
*/
void ata_bmdma_irq_clear ( struct ata_port * ap )
{
2007-02-01 09:06:36 +03:00
void __iomem * mmio = ap - > ioaddr . bmdma_addr ;
if ( ! mmio )
2006-03-23 08:32:03 +03:00
return ;
2007-02-01 09:06:36 +03:00
iowrite8 ( ioread8 ( mmio + ATA_DMA_STATUS ) , mmio + ATA_DMA_STATUS ) ;
2006-03-23 08:32:03 +03:00
}
/**
* ata_bmdma_status - Read PCI IDE BMDMA status
* @ ap : Port associated with this ATA transaction .
*
* Read and return BMDMA status register .
*
* May be used as the bmdma_status ( ) entry in ata_port_operations .
*
* LOCKING :
2006-08-24 11:19:22 +04:00
* spin_lock_irqsave ( host lock )
2006-03-23 08:32:03 +03:00
*/
u8 ata_bmdma_status ( struct ata_port * ap )
{
2007-02-01 09:06:36 +03:00
return ioread8 ( ap - > ioaddr . bmdma_addr + ATA_DMA_STATUS ) ;
2006-03-23 08:32:03 +03:00
}
/**
* ata_bmdma_stop - Stop PCI IDE BMDMA transfer
* @ qc : Command we are ending DMA for
*
* Clears the ATA_DMA_START flag in the dma control register
*
* May be used as the bmdma_stop ( ) entry in ata_port_operations .
*
* LOCKING :
2006-08-24 11:19:22 +04:00
* spin_lock_irqsave ( host lock )
2006-03-23 08:32:03 +03:00
*/
void ata_bmdma_stop ( struct ata_queued_cmd * qc )
{
struct ata_port * ap = qc - > ap ;
2007-02-01 09:06:36 +03:00
void __iomem * mmio = ap - > ioaddr . bmdma_addr ;
/* clear start/stop bit */
iowrite8 ( ioread8 ( mmio + ATA_DMA_CMD ) & ~ ATA_DMA_START ,
mmio + ATA_DMA_CMD ) ;
2006-03-23 08:32:03 +03:00
/* one-PIO-cycle guaranteed wait, per spec, for HDMA1:0 transition */
ata_altstatus ( ap ) ; /* dummy read */
}
2006-05-15 15:58:24 +04:00
/**
* ata_bmdma_freeze - Freeze BMDMA controller port
* @ ap : port to freeze
*
* Freeze BMDMA controller port .
*
* LOCKING :
* Inherited from caller .
*/
void ata_bmdma_freeze ( struct ata_port * ap )
{
struct ata_ioports * ioaddr = & ap - > ioaddr ;
ap - > ctl | = ATA_NIEN ;
ap - > last_ctl = ap - > ctl ;
2008-03-06 07:12:54 +03:00
if ( ioaddr - > ctl_addr )
iowrite8 ( ap - > ctl , ioaddr - > ctl_addr ) ;
2006-11-17 06:24:22 +03:00
/* Under certain circumstances, some controllers raise IRQ on
* ATA_NIEN manipulation . Also , many controllers fail to mask
* previously pending IRQ on ATA_NIEN assertion . Clear it .
*/
ata_chk_status ( ap ) ;
ap - > ops - > irq_clear ( ap ) ;
2006-05-15 15:58:24 +04:00
}
/**
* ata_bmdma_thaw - Thaw BMDMA controller port
* @ ap : port to thaw
*
* Thaw BMDMA controller port .
*
* LOCKING :
* Inherited from caller .
*/
void ata_bmdma_thaw ( struct ata_port * ap )
{
/* clear & re-enable interrupts */
ata_chk_status ( ap ) ;
ap - > ops - > irq_clear ( ap ) ;
2007-01-26 10:27:32 +03:00
ap - > ops - > irq_on ( ap ) ;
2006-05-15 15:58:24 +04:00
}
/**
* ata_bmdma_drive_eh - Perform EH with given methods for BMDMA controller
* @ ap : port to handle error for
2006-05-31 13:27:48 +04:00
* @ prereset : prereset method ( can be NULL )
2006-05-15 15:58:24 +04:00
* @ softreset : softreset method ( can be NULL )
* @ hardreset : hardreset method ( can be NULL )
* @ postreset : postreset method ( can be NULL )
*
* Handle error for ATA BMDMA controller . It can handle both
* PATA and SATA controllers . Many controllers should be able to
* use this EH as - is or with some added handling before and
* after .
*
* This function is intended to be used for constructing
* - > error_handler callback by low level drivers .
*
* LOCKING :
* Kernel thread context ( may sleep )
*/
2006-05-31 13:27:48 +04:00
void ata_bmdma_drive_eh ( struct ata_port * ap , ata_prereset_fn_t prereset ,
ata_reset_fn_t softreset , ata_reset_fn_t hardreset ,
ata_postreset_fn_t postreset )
2006-05-15 15:58:24 +04:00
{
struct ata_queued_cmd * qc ;
unsigned long flags ;
int thaw = 0 ;
2007-08-06 13:36:22 +04:00
qc = __ata_qc_from_tag ( ap , ap - > link . active_tag ) ;
2006-05-15 15:58:24 +04:00
if ( qc & & ! ( qc - > flags & ATA_QCFLAG_FAILED ) )
qc = NULL ;
/* reset PIO HSM and stop DMA engine */
2006-06-23 07:46:10 +04:00
spin_lock_irqsave ( ap - > lock , flags ) ;
2006-05-15 15:58:24 +04:00
ap - > hsm_task_state = HSM_ST_IDLE ;
if ( qc & & ( qc - > tf . protocol = = ATA_PROT_DMA | |
2007-12-19 00:34:43 +03:00
qc - > tf . protocol = = ATAPI_PROT_DMA ) ) {
2006-05-15 15:58:24 +04:00
u8 host_stat ;
2006-10-28 06:08:41 +04:00
host_stat = ap - > ops - > bmdma_status ( ap ) ;
2006-05-15 15:58:24 +04:00
/* BMDMA controllers indicate host bus error by
* setting DMA_ERR bit and timing out . As it wasn ' t
* really a timeout event , adjust error mask and
* cancel frozen state .
*/
2007-01-24 14:42:38 +03:00
if ( qc - > err_mask = = AC_ERR_TIMEOUT & & ( host_stat & ATA_DMA_ERR ) ) {
2006-05-15 15:58:24 +04:00
qc - > err_mask = AC_ERR_HOST_BUS ;
thaw = 1 ;
}
ap - > ops - > bmdma_stop ( qc ) ;
}
ata_altstatus ( ap ) ;
ata_chk_status ( ap ) ;
ap - > ops - > irq_clear ( ap ) ;
2006-06-23 07:46:10 +04:00
spin_unlock_irqrestore ( ap - > lock , flags ) ;
2006-05-15 15:58:24 +04:00
if ( thaw )
ata_eh_thaw_port ( ap ) ;
/* PIO and DMA engines have been stopped, perform recovery */
2006-05-31 13:27:48 +04:00
ata_do_eh ( ap , prereset , softreset , hardreset , postreset ) ;
2006-05-15 15:58:24 +04:00
}
/**
* ata_bmdma_error_handler - Stock error handler for BMDMA controller
* @ ap : port to handle error for
*
* Stock error handler for BMDMA controller .
*
* LOCKING :
* Kernel thread context ( may sleep )
*/
void ata_bmdma_error_handler ( struct ata_port * ap )
{
2008-03-06 07:12:54 +03:00
ata_reset_fn_t softreset = NULL , hardreset = NULL ;
2006-05-15 15:58:24 +04:00
2008-03-06 07:12:54 +03:00
if ( ap - > ioaddr . ctl_addr )
softreset = ata_std_softreset ;
2007-08-06 13:36:23 +04:00
if ( sata_scr_valid ( & ap - > link ) )
2006-05-15 15:58:24 +04:00
hardreset = sata_std_hardreset ;
2008-03-06 07:12:54 +03:00
ata_bmdma_drive_eh ( ap , ata_std_prereset , softreset , hardreset ,
2006-05-31 13:27:48 +04:00
ata_std_postreset ) ;
2006-05-15 15:58:24 +04:00
}
/**
* ata_bmdma_post_internal_cmd - Stock post_internal_cmd for
* BMDMA controller
* @ qc : internal command to clean up
*
* LOCKING :
* Kernel thread context ( may sleep )
*/
void ata_bmdma_post_internal_cmd ( struct ata_queued_cmd * qc )
{
2007-01-25 18:09:05 +03:00
if ( qc - > ap - > ioaddr . bmdma_addr )
ata_bmdma_stop ( qc ) ;
2006-05-15 15:58:24 +04:00
}
2007-06-07 19:19:15 +04:00
/**
* ata_sff_port_start - Set port up for dma .
* @ ap : Port to initialize
*
* Called just after data structures for each port are
* initialized . Allocates space for PRD table if the device
* is DMA capable SFF .
*
* May be used as the port_start ( ) entry in ata_port_operations .
*
* LOCKING :
* Inherited from caller .
*/
int ata_sff_port_start ( struct ata_port * ap )
{
if ( ap - > ioaddr . bmdma_addr )
return ata_port_start ( ap ) ;
return 0 ;
}
2006-02-09 13:15:27 +03:00
# ifdef CONFIG_PCI
2007-01-08 15:10:05 +03:00
static int ata_resources_present ( struct pci_dev * pdev , int port )
{
int i ;
2007-02-26 13:51:33 +03:00
2007-01-08 15:10:05 +03:00
/* Check the PCI resources for this channel are enabled */
port = port * 2 ;
for ( i = 0 ; i < 2 ; i + + ) {
if ( pci_resource_start ( pdev , port + i ) = = 0 | |
2007-03-09 13:43:35 +03:00
pci_resource_len ( pdev , port + i ) = = 0 )
return 0 ;
2007-01-08 15:10:05 +03:00
}
return 1 ;
}
2007-02-26 13:51:33 +03:00
2007-04-17 18:44:07 +04:00
/**
* ata_pci_init_bmdma - acquire PCI BMDMA resources and init ATA host
* @ host : target ATA host
*
* Acquire PCI BMDMA resources and initialize @ host accordingly .
*
* LOCKING :
* Inherited from calling layer ( may sleep ) .
*
* RETURNS :
* 0 on success , - errno otherwise .
*/
2007-05-04 14:43:58 +04:00
int ata_pci_init_bmdma ( struct ata_host * host )
2006-02-09 13:15:27 +03:00
{
2007-04-17 18:44:07 +04:00
struct device * gdev = host - > dev ;
struct pci_dev * pdev = to_pci_dev ( gdev ) ;
int i , rc ;
2007-02-01 09:06:36 +03:00
2007-07-26 21:41:30 +04:00
/* No BAR4 allocation: No DMA */
if ( pci_resource_start ( pdev , 4 ) = = 0 )
return 0 ;
2007-04-17 18:44:07 +04:00
/* TODO: If we get no DMA mask we should fall back to PIO */
rc = pci_set_dma_mask ( pdev , ATA_DMA_MASK ) ;
if ( rc )
return rc ;
rc = pci_set_consistent_dma_mask ( pdev , ATA_DMA_MASK ) ;
if ( rc )
return rc ;
/* request and iomap DMA region */
2008-01-04 12:42:21 +03:00
rc = pcim_iomap_regions ( pdev , 1 < < 4 , dev_driver_string ( gdev ) ) ;
2007-04-17 18:44:07 +04:00
if ( rc ) {
dev_printk ( KERN_ERR , gdev , " failed to request/iomap BAR4 \n " ) ;
return - ENOMEM ;
2007-02-01 09:06:36 +03:00
}
2007-04-17 18:44:07 +04:00
host - > iomap = pcim_iomap_table ( pdev ) ;
2007-02-01 09:06:36 +03:00
2007-05-04 14:43:58 +04:00
for ( i = 0 ; i < 2 ; i + + ) {
2007-04-17 18:44:07 +04:00
struct ata_port * ap = host - > ports [ i ] ;
void __iomem * bmdma = host - > iomap [ 4 ] + 8 * i ;
if ( ata_port_is_dummy ( ap ) )
continue ;
2007-04-17 18:44:07 +04:00
ap - > ioaddr . bmdma_addr = bmdma ;
2007-04-17 18:44:07 +04:00
if ( ( ! ( ap - > flags & ATA_FLAG_IGN_SIMPLEX ) ) & &
( ioread8 ( bmdma + 2 ) & 0x80 ) )
host - > flags | = ATA_HOST_SIMPLEX ;
2007-08-18 08:14:55 +04:00
ata_port_desc ( ap , " bmdma 0x%llx " ,
( unsigned long long ) pci_resource_start ( pdev , 4 ) + 8 * i ) ;
2007-02-01 09:06:36 +03:00
}
2007-04-17 18:44:07 +04:00
return 0 ;
}
2006-08-10 11:59:10 +04:00
2007-04-17 18:44:07 +04:00
/**
2007-07-04 13:02:07 +04:00
* ata_pci_init_sff_host - acquire native PCI ATA resources and init host
2007-04-17 18:44:07 +04:00
* @ host : target ATA host
*
2007-05-04 14:43:58 +04:00
* Acquire native PCI ATA resources for @ host and initialize the
* first two ports of @ host accordingly . Ports marked dummy are
* skipped and allocation failure makes the port dummy .
2007-04-17 18:44:07 +04:00
*
2007-07-04 13:02:07 +04:00
* Note that native PCI resources are valid even for legacy hosts
* as we fix up pdev resources array early in boot , so this
* function can be used for both native and legacy SFF hosts .
*
2007-04-17 18:44:07 +04:00
* LOCKING :
* Inherited from calling layer ( may sleep ) .
*
* RETURNS :
2007-05-04 14:43:58 +04:00
* 0 if at least one port is initialized , - ENODEV if no port is
* available .
2007-04-17 18:44:07 +04:00
*/
2007-07-04 13:02:07 +04:00
int ata_pci_init_sff_host ( struct ata_host * host )
2007-04-17 18:44:07 +04:00
{
struct device * gdev = host - > dev ;
struct pci_dev * pdev = to_pci_dev ( gdev ) ;
2007-05-04 14:43:58 +04:00
unsigned int mask = 0 ;
2007-04-17 18:44:07 +04:00
int i , rc ;
/* request, iomap BARs and init port addresses accordingly */
for ( i = 0 ; i < 2 ; i + + ) {
struct ata_port * ap = host - > ports [ i ] ;
int base = i * 2 ;
void __iomem * const * iomap ;
2007-05-04 14:43:58 +04:00
if ( ata_port_is_dummy ( ap ) )
continue ;
/* Discard disabled ports. Some controllers show
* their unused channels this way . Disabled ports are
* made dummy .
*/
if ( ! ata_resources_present ( pdev , i ) ) {
ap - > ops = & ata_dummy_port_ops ;
2007-04-17 18:44:07 +04:00
continue ;
2007-05-04 14:43:58 +04:00
}
2007-04-17 18:44:07 +04:00
2008-01-04 12:42:21 +03:00
rc = pcim_iomap_regions ( pdev , 0x3 < < base ,
dev_driver_string ( gdev ) ) ;
2007-04-17 18:44:07 +04:00
if ( rc ) {
2007-05-04 14:43:58 +04:00
dev_printk ( KERN_WARNING , gdev ,
" failed to request/iomap BARs for port %d "
" (errno=%d) \n " , i , rc ) ;
2007-04-17 18:44:07 +04:00
if ( rc = = - EBUSY )
pcim_pin_device ( pdev ) ;
2007-05-04 14:43:58 +04:00
ap - > ops = & ata_dummy_port_ops ;
continue ;
2007-04-17 18:44:07 +04:00
}
host - > iomap = iomap = pcim_iomap_table ( pdev ) ;
ap - > ioaddr . cmd_addr = iomap [ base ] ;
ap - > ioaddr . altstatus_addr =
ap - > ioaddr . ctl_addr = ( void __iomem * )
( ( unsigned long ) iomap [ base + 1 ] | ATA_PCI_CTL_OFS ) ;
ata_std_ports ( & ap - > ioaddr ) ;
2007-05-04 14:43:58 +04:00
2007-08-18 08:14:55 +04:00
ata_port_desc ( ap , " cmd 0x%llx ctl 0x%llx " ,
( unsigned long long ) pci_resource_start ( pdev , base ) ,
( unsigned long long ) pci_resource_start ( pdev , base + 1 ) ) ;
2007-05-04 14:43:58 +04:00
mask | = 1 < < i ;
}
if ( ! mask ) {
dev_printk ( KERN_ERR , gdev , " no available native port \n " ) ;
return - ENODEV ;
2007-04-17 18:44:07 +04:00
}
return 0 ;
}
2007-04-17 18:44:07 +04:00
/**
2007-07-04 13:02:07 +04:00
* ata_pci_prepare_sff_host - helper to prepare native PCI ATA host
2007-04-17 18:44:07 +04:00
* @ pdev : target PCI device
2007-05-04 14:43:58 +04:00
* @ ppi : array of port_info , must be enough for two ports
2007-04-17 18:44:07 +04:00
* @ r_host : out argument for the initialized ATA host
*
* Helper to allocate ATA host for @ pdev , acquire all native PCI
* resources and initialize it accordingly in one go .
*
* LOCKING :
* Inherited from calling layer ( may sleep ) .
*
* RETURNS :
* 0 on success , - errno otherwise .
*/
2007-07-04 13:02:07 +04:00
int ata_pci_prepare_sff_host ( struct pci_dev * pdev ,
const struct ata_port_info * const * ppi ,
struct ata_host * * r_host )
2007-04-17 18:44:07 +04:00
{
struct ata_host * host ;
int rc ;
if ( ! devres_open_group ( & pdev - > dev , NULL , GFP_KERNEL ) )
return - ENOMEM ;
host = ata_host_alloc_pinfo ( & pdev - > dev , ppi , 2 ) ;
if ( ! host ) {
dev_printk ( KERN_ERR , & pdev - > dev ,
" failed to allocate ATA host \n " ) ;
rc = - ENOMEM ;
goto err_out ;
}
2007-07-04 13:02:07 +04:00
rc = ata_pci_init_sff_host ( host ) ;
2007-04-17 18:44:07 +04:00
if ( rc )
goto err_out ;
/* init DMA related stuff */
rc = ata_pci_init_bmdma ( host ) ;
if ( rc )
goto err_bmdma ;
devres_remove_group ( & pdev - > dev , NULL ) ;
* r_host = host ;
return 0 ;
err_bmdma :
/* This is necessary because PCI and iomap resources are
* merged and releasing the top group won ' t release the
* acquired resources if some of those have been acquired
* before entering this function .
*/
pcim_iounmap_regions ( pdev , 0xf ) ;
err_out :
devres_release_group ( & pdev - > dev , NULL ) ;
return rc ;
}
2008-01-18 12:36:28 +03:00
/**
* ata_pci_activate_sff_host - start SFF host , request IRQ and register it
* @ host : target SFF ATA host
* @ irq_handler : irq_handler used when requesting IRQ ( s )
* @ sht : scsi_host_template to use when registering the host
*
* This is the counterpart of ata_host_activate ( ) for SFF ATA
* hosts . This separate helper is necessary because SFF hosts
* use two separate interrupts in legacy mode .
*
* LOCKING :
* Inherited from calling layer ( may sleep ) .
*
* RETURNS :
* 0 on success , - errno otherwise .
*/
int ata_pci_activate_sff_host ( struct ata_host * host ,
irq_handler_t irq_handler ,
struct scsi_host_template * sht )
{
struct device * dev = host - > dev ;
struct pci_dev * pdev = to_pci_dev ( dev ) ;
const char * drv_name = dev_driver_string ( host - > dev ) ;
int legacy_mode = 0 , rc ;
rc = ata_host_start ( host ) ;
if ( rc )
return rc ;
if ( ( pdev - > class > > 8 ) = = PCI_CLASS_STORAGE_IDE ) {
u8 tmp8 , mask ;
/* TODO: What if one channel is in native mode ... */
pci_read_config_byte ( pdev , PCI_CLASS_PROG , & tmp8 ) ;
mask = ( 1 < < 2 ) | ( 1 < < 0 ) ;
if ( ( tmp8 & mask ) ! = mask )
legacy_mode = 1 ;
# if defined(CONFIG_NO_ATA_LEGACY)
/* Some platforms with PCI limits cannot address compat
port space . In that case we punt if their firmware has
left a device in compatibility mode */
if ( legacy_mode ) {
printk ( KERN_ERR " ata: Compatibility mode ATA is not supported on this platform, skipping. \n " ) ;
return - EOPNOTSUPP ;
}
# endif
}
if ( ! devres_open_group ( dev , NULL , GFP_KERNEL ) )
return - ENOMEM ;
if ( ! legacy_mode & & pdev - > irq ) {
rc = devm_request_irq ( dev , pdev - > irq , irq_handler ,
IRQF_SHARED , drv_name , host ) ;
if ( rc )
goto out ;
ata_port_desc ( host - > ports [ 0 ] , " irq %d " , pdev - > irq ) ;
ata_port_desc ( host - > ports [ 1 ] , " irq %d " , pdev - > irq ) ;
} else if ( legacy_mode ) {
if ( ! ata_port_is_dummy ( host - > ports [ 0 ] ) ) {
rc = devm_request_irq ( dev , ATA_PRIMARY_IRQ ( pdev ) ,
irq_handler , IRQF_SHARED ,
drv_name , host ) ;
if ( rc )
goto out ;
ata_port_desc ( host - > ports [ 0 ] , " irq %d " ,
ATA_PRIMARY_IRQ ( pdev ) ) ;
}
if ( ! ata_port_is_dummy ( host - > ports [ 1 ] ) ) {
rc = devm_request_irq ( dev , ATA_SECONDARY_IRQ ( pdev ) ,
irq_handler , IRQF_SHARED ,
drv_name , host ) ;
if ( rc )
goto out ;
ata_port_desc ( host - > ports [ 1 ] , " irq %d " ,
ATA_SECONDARY_IRQ ( pdev ) ) ;
}
}
rc = ata_host_register ( host , sht ) ;
out :
if ( rc = = 0 )
devres_remove_group ( dev , NULL ) ;
else
devres_release_group ( dev , NULL ) ;
return rc ;
}
2006-02-09 13:15:27 +03:00
/**
* ata_pci_init_one - Initialize / register PCI IDE host controller
* @ pdev : Controller to be initialized
2007-05-04 14:43:58 +04:00
* @ ppi : array of port_info , must be enough for two ports
2006-02-09 13:15:27 +03:00
*
* This is a helper function which can be called from a driver ' s
* xxx_init_one ( ) probe function if the hardware uses traditional
* IDE taskfile registers .
*
* This function calls pci_enable_device ( ) , reserves its register
* regions , sets the dma mask , enables bus master mode , and calls
* ata_device_add ( )
*
2006-08-10 11:59:10 +04:00
* ASSUMPTION :
* Nobody makes a single channel controller that appears solely as
* the secondary legacy port on PCI .
*
2006-02-09 13:15:27 +03:00
* LOCKING :
* Inherited from PCI layer ( may sleep ) .
*
* RETURNS :
* Zero on success , negative on errno - based value on error .
*/
2007-05-04 14:43:58 +04:00
int ata_pci_init_one ( struct pci_dev * pdev ,
const struct ata_port_info * const * ppi )
2006-02-09 13:15:27 +03:00
{
2007-01-20 10:00:28 +03:00
struct device * dev = & pdev - > dev ;
2007-05-04 14:43:58 +04:00
const struct ata_port_info * pi = NULL ;
2007-04-17 18:44:07 +04:00
struct ata_host * host = NULL ;
2007-05-04 14:43:58 +04:00
int i , rc ;
2006-02-09 13:15:27 +03:00
DPRINTK ( " ENTER \n " ) ;
2007-05-04 14:43:58 +04:00
/* look up the first valid port_info */
for ( i = 0 ; i < 2 & & ppi [ i ] ; i + + ) {
if ( ppi [ i ] - > port_ops ! = & ata_dummy_port_ops ) {
pi = ppi [ i ] ;
break ;
}
}
2007-01-20 10:00:28 +03:00
2007-05-04 14:43:58 +04:00
if ( ! pi ) {
dev_printk ( KERN_ERR , & pdev - > dev ,
" no valid port_info specified \n " ) ;
return - EINVAL ;
}
2006-09-28 11:40:11 +04:00
2007-05-04 14:43:58 +04:00
if ( ! devres_open_group ( dev , NULL , GFP_KERNEL ) )
return - ENOMEM ;
2006-02-09 13:15:27 +03:00
2007-01-20 10:00:28 +03:00
rc = pcim_enable_device ( pdev ) ;
2006-02-09 13:15:27 +03:00
if ( rc )
2008-01-18 12:36:28 +03:00
goto out ;
2006-02-09 13:15:27 +03:00
2008-01-18 12:36:28 +03:00
/* prepare and activate SFF host */
2007-07-04 13:02:07 +04:00
rc = ata_pci_prepare_sff_host ( pdev , ppi , & host ) ;
if ( rc )
2008-01-18 12:36:28 +03:00
goto out ;
2007-04-17 18:44:07 +04:00
pci_set_master ( pdev ) ;
2008-01-18 12:36:28 +03:00
rc = ata_pci_activate_sff_host ( host , pi - > port_ops - > irq_handler ,
pi - > sht ) ;
out :
if ( rc = = 0 )
devres_remove_group ( & pdev - > dev , NULL ) ;
else
devres_release_group ( & pdev - > dev , NULL ) ;
2007-04-17 18:44:07 +04:00
2006-02-09 13:15:27 +03:00
return rc ;
}
2006-03-21 18:59:57 +03:00
/**
* ata_pci_clear_simplex - attempt to kick device out of simplex
* @ pdev : PCI device
*
* Some PCI ATA devices report simplex mode but in fact can be told to
2007-10-20 01:10:43 +04:00
* enter non simplex mode . This implements the necessary logic to
2006-03-21 18:59:57 +03:00
* perform the task on such devices . Calling it on other devices will
* have - undefined - behaviour .
*/
int ata_pci_clear_simplex ( struct pci_dev * pdev )
{
unsigned long bmdma = pci_resource_start ( pdev , 4 ) ;
u8 simplex ;
if ( bmdma = = 0 )
return - ENOENT ;
simplex = inb ( bmdma + 0x02 ) ;
outb ( simplex & 0x60 , bmdma + 0x02 ) ;
simplex = inb ( bmdma + 0x02 ) ;
if ( simplex & 0x80 )
return - EOPNOTSUPP ;
return 0 ;
}
2007-03-09 17:34:07 +03:00
unsigned long ata_pci_default_filter ( struct ata_device * adev , unsigned long xfer_mask )
2006-03-21 18:59:57 +03:00
{
/* Filter out DMA modes if the device has been configured by
the BIOS as PIO only */
2006-03-24 17:56:57 +03:00
2007-10-18 14:07:05 +04:00
if ( adev - > link - > ap - > ioaddr . bmdma_addr = = NULL )
2006-03-21 18:59:57 +03:00
xfer_mask & = ~ ( ATA_MASK_MWDMA | ATA_MASK_UDMA ) ;
return xfer_mask ;
}
2006-02-09 13:15:27 +03:00
# endif /* CONFIG_PCI */