2010-03-28 08:22:14 +04:00
/*
* libahci . c - Common AHCI SATA low - level routines
*
* Maintained by : Jeff Garzik < jgarzik @ pobox . com >
* Please ALWAYS copy linux - ide @ vger . kernel . org
* on emails .
*
* Copyright 2004 - 2005 Red Hat , Inc .
*
*
* 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 . *
*
* AHCI hardware documentation :
* http : //www.intel.com/technology/serialata/pdf/rev1_0.pdf
* http : //www.intel.com/technology/serialata/pdf/rev1_1.pdf
*
*/
# include <linux/kernel.h>
2010-03-29 21:52:43 +04:00
# include <linux/gfp.h>
2010-03-28 08:22:14 +04:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/blkdev.h>
# include <linux/delay.h>
# include <linux/interrupt.h>
# include <linux/dma-mapping.h>
# include <linux/device.h>
# include <scsi/scsi_host.h>
# include <scsi/scsi_cmnd.h>
# include <linux/libata.h>
# include "ahci.h"
static int ahci_skip_host_reset ;
int ahci_ignore_sss ;
EXPORT_SYMBOL_GPL ( ahci_ignore_sss ) ;
module_param_named ( skip_host_reset , ahci_skip_host_reset , int , 0444 ) ;
MODULE_PARM_DESC ( skip_host_reset , " skip global host reset (0=don't skip, 1=skip) " ) ;
module_param_named ( ignore_sss , ahci_ignore_sss , int , 0444 ) ;
MODULE_PARM_DESC ( ignore_sss , " Ignore staggered spinup flag (0=don't ignore, 1=ignore) " ) ;
static int ahci_enable_alpm ( struct ata_port * ap ,
enum link_pm policy ) ;
static void ahci_disable_alpm ( struct ata_port * ap ) ;
static ssize_t ahci_led_show ( struct ata_port * ap , char * buf ) ;
static ssize_t ahci_led_store ( struct ata_port * ap , const char * buf ,
size_t size ) ;
static ssize_t ahci_transmit_led_message ( struct ata_port * ap , u32 state ,
ssize_t size ) ;
static int ahci_scr_read ( struct ata_link * link , unsigned int sc_reg , u32 * val ) ;
static int ahci_scr_write ( struct ata_link * link , unsigned int sc_reg , u32 val ) ;
static unsigned int ahci_qc_issue ( struct ata_queued_cmd * qc ) ;
static bool ahci_qc_fill_rtf ( struct ata_queued_cmd * qc ) ;
static int ahci_port_start ( struct ata_port * ap ) ;
static void ahci_port_stop ( struct ata_port * ap ) ;
static void ahci_qc_prep ( struct ata_queued_cmd * qc ) ;
static int ahci_pmp_qc_defer ( struct ata_queued_cmd * qc ) ;
static void ahci_freeze ( struct ata_port * ap ) ;
static void ahci_thaw ( struct ata_port * ap ) ;
static void ahci_enable_fbs ( struct ata_port * ap ) ;
static void ahci_disable_fbs ( struct ata_port * ap ) ;
static void ahci_pmp_attach ( struct ata_port * ap ) ;
static void ahci_pmp_detach ( struct ata_port * ap ) ;
static int ahci_softreset ( struct ata_link * link , unsigned int * class ,
unsigned long deadline ) ;
static int ahci_hardreset ( struct ata_link * link , unsigned int * class ,
unsigned long deadline ) ;
static void ahci_postreset ( struct ata_link * link , unsigned int * class ) ;
static void ahci_error_handler ( struct ata_port * ap ) ;
static void ahci_post_internal_cmd ( struct ata_queued_cmd * qc ) ;
static int ahci_port_resume ( struct ata_port * ap ) ;
static void ahci_dev_config ( struct ata_device * dev ) ;
static void ahci_fill_cmd_slot ( struct ahci_port_priv * pp , unsigned int tag ,
u32 opts ) ;
# ifdef CONFIG_PM
static int ahci_port_suspend ( struct ata_port * ap , pm_message_t mesg ) ;
# endif
static ssize_t ahci_activity_show ( struct ata_device * dev , char * buf ) ;
static ssize_t ahci_activity_store ( struct ata_device * dev ,
enum sw_activity val ) ;
static void ahci_init_sw_activity ( struct ata_link * link ) ;
static ssize_t ahci_show_host_caps ( struct device * dev ,
struct device_attribute * attr , char * buf ) ;
static ssize_t ahci_show_host_cap2 ( struct device * dev ,
struct device_attribute * attr , char * buf ) ;
static ssize_t ahci_show_host_version ( struct device * dev ,
struct device_attribute * attr , char * buf ) ;
static ssize_t ahci_show_port_cmd ( struct device * dev ,
struct device_attribute * attr , char * buf ) ;
2010-04-23 13:28:38 +04:00
static ssize_t ahci_read_em_buffer ( struct device * dev ,
struct device_attribute * attr , char * buf ) ;
static ssize_t ahci_store_em_buffer ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t size ) ;
2010-03-28 08:22:14 +04:00
static DEVICE_ATTR ( ahci_host_caps , S_IRUGO , ahci_show_host_caps , NULL ) ;
static DEVICE_ATTR ( ahci_host_cap2 , S_IRUGO , ahci_show_host_cap2 , NULL ) ;
static DEVICE_ATTR ( ahci_host_version , S_IRUGO , ahci_show_host_version , NULL ) ;
static DEVICE_ATTR ( ahci_port_cmd , S_IRUGO , ahci_show_port_cmd , NULL ) ;
2010-04-23 13:28:38 +04:00
static DEVICE_ATTR ( em_buffer , S_IWUSR | S_IRUGO ,
ahci_read_em_buffer , ahci_store_em_buffer ) ;
2010-03-28 08:22:14 +04:00
static struct device_attribute * ahci_shost_attrs [ ] = {
& dev_attr_link_power_management_policy ,
& dev_attr_em_message_type ,
& dev_attr_em_message ,
& dev_attr_ahci_host_caps ,
& dev_attr_ahci_host_cap2 ,
& dev_attr_ahci_host_version ,
& dev_attr_ahci_port_cmd ,
2010-04-23 13:28:38 +04:00
& dev_attr_em_buffer ,
2010-03-28 08:22:14 +04:00
NULL
} ;
static struct device_attribute * ahci_sdev_attrs [ ] = {
& dev_attr_sw_activity ,
& dev_attr_unload_heads ,
NULL
} ;
struct scsi_host_template ahci_sht = {
ATA_NCQ_SHT ( " ahci " ) ,
. can_queue = AHCI_MAX_CMDS - 1 ,
. sg_tablesize = AHCI_MAX_SG ,
. dma_boundary = AHCI_DMA_BOUNDARY ,
. shost_attrs = ahci_shost_attrs ,
. sdev_attrs = ahci_sdev_attrs ,
} ;
EXPORT_SYMBOL_GPL ( ahci_sht ) ;
struct ata_port_operations ahci_ops = {
. inherits = & sata_pmp_port_ops ,
. qc_defer = ahci_pmp_qc_defer ,
. qc_prep = ahci_qc_prep ,
. qc_issue = ahci_qc_issue ,
. qc_fill_rtf = ahci_qc_fill_rtf ,
. freeze = ahci_freeze ,
. thaw = ahci_thaw ,
. softreset = ahci_softreset ,
. hardreset = ahci_hardreset ,
. postreset = ahci_postreset ,
. pmp_softreset = ahci_softreset ,
. error_handler = ahci_error_handler ,
. post_internal_cmd = ahci_post_internal_cmd ,
. dev_config = ahci_dev_config ,
. scr_read = ahci_scr_read ,
. scr_write = ahci_scr_write ,
. pmp_attach = ahci_pmp_attach ,
. pmp_detach = ahci_pmp_detach ,
. enable_pm = ahci_enable_alpm ,
. disable_pm = ahci_disable_alpm ,
. em_show = ahci_led_show ,
. em_store = ahci_led_store ,
. sw_activity_show = ahci_activity_show ,
. sw_activity_store = ahci_activity_store ,
# ifdef CONFIG_PM
. port_suspend = ahci_port_suspend ,
. port_resume = ahci_port_resume ,
# endif
. port_start = ahci_port_start ,
. port_stop = ahci_port_stop ,
} ;
EXPORT_SYMBOL_GPL ( ahci_ops ) ;
int ahci_em_messages = 1 ;
EXPORT_SYMBOL_GPL ( ahci_em_messages ) ;
module_param ( ahci_em_messages , int , 0444 ) ;
/* add other LED protocol types when they become supported */
MODULE_PARM_DESC ( ahci_em_messages ,
2010-04-23 13:27:19 +04:00
" AHCI Enclosure Management Message control (0 = off, 1 = on) " ) ;
2010-03-28 08:22:14 +04:00
static void ahci_enable_ahci ( void __iomem * mmio )
{
int i ;
u32 tmp ;
/* turn on AHCI_EN */
tmp = readl ( mmio + HOST_CTL ) ;
if ( tmp & HOST_AHCI_EN )
return ;
/* Some controllers need AHCI_EN to be written multiple times.
* Try a few times before giving up .
*/
for ( i = 0 ; i < 5 ; i + + ) {
tmp | = HOST_AHCI_EN ;
writel ( tmp , mmio + HOST_CTL ) ;
tmp = readl ( mmio + HOST_CTL ) ; /* flush && sanity check */
if ( tmp & HOST_AHCI_EN )
return ;
msleep ( 10 ) ;
}
WARN_ON ( 1 ) ;
}
static ssize_t ahci_show_host_caps ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ata_port * ap = ata_shost_to_port ( shost ) ;
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
return sprintf ( buf , " %x \n " , hpriv - > cap ) ;
}
static ssize_t ahci_show_host_cap2 ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ata_port * ap = ata_shost_to_port ( shost ) ;
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
return sprintf ( buf , " %x \n " , hpriv - > cap2 ) ;
}
static ssize_t ahci_show_host_version ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ata_port * ap = ata_shost_to_port ( shost ) ;
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
void __iomem * mmio = hpriv - > mmio ;
return sprintf ( buf , " %x \n " , readl ( mmio + HOST_VERSION ) ) ;
}
static ssize_t ahci_show_port_cmd ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ata_port * ap = ata_shost_to_port ( shost ) ;
void __iomem * port_mmio = ahci_port_base ( ap ) ;
return sprintf ( buf , " %x \n " , readl ( port_mmio + PORT_CMD ) ) ;
}
2010-04-23 13:28:38 +04:00
static ssize_t ahci_read_em_buffer ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ata_port * ap = ata_shost_to_port ( shost ) ;
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
void __iomem * mmio = hpriv - > mmio ;
void __iomem * em_mmio = mmio + hpriv - > em_loc ;
u32 em_ctl , msg ;
unsigned long flags ;
size_t count ;
int i ;
spin_lock_irqsave ( ap - > lock , flags ) ;
em_ctl = readl ( mmio + HOST_EM_CTL ) ;
if ( ! ( ap - > flags & ATA_FLAG_EM ) | | em_ctl & EM_CTL_XMT | |
! ( hpriv - > em_msg_type & EM_MSG_TYPE_SGPIO ) ) {
spin_unlock_irqrestore ( ap - > lock , flags ) ;
return - EINVAL ;
}
if ( ! ( em_ctl & EM_CTL_MR ) ) {
spin_unlock_irqrestore ( ap - > lock , flags ) ;
return - EAGAIN ;
}
if ( ! ( em_ctl & EM_CTL_SMB ) )
em_mmio + = hpriv - > em_buf_sz ;
count = hpriv - > em_buf_sz ;
/* the count should not be larger than PAGE_SIZE */
if ( count > PAGE_SIZE ) {
if ( printk_ratelimit ( ) )
ata_port_printk ( ap , KERN_WARNING ,
" EM read buffer size too large: "
" buffer size %u, page size %lu \n " ,
hpriv - > em_buf_sz , PAGE_SIZE ) ;
count = PAGE_SIZE ;
}
for ( i = 0 ; i < count ; i + = 4 ) {
msg = readl ( em_mmio + i ) ;
buf [ i ] = msg & 0xff ;
buf [ i + 1 ] = ( msg > > 8 ) & 0xff ;
buf [ i + 2 ] = ( msg > > 16 ) & 0xff ;
buf [ i + 3 ] = ( msg > > 24 ) & 0xff ;
}
spin_unlock_irqrestore ( ap - > lock , flags ) ;
return i ;
}
static ssize_t ahci_store_em_buffer ( struct device * dev ,
struct device_attribute * attr ,
const char * buf , size_t size )
{
struct Scsi_Host * shost = class_to_shost ( dev ) ;
struct ata_port * ap = ata_shost_to_port ( shost ) ;
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
void __iomem * mmio = hpriv - > mmio ;
void __iomem * em_mmio = mmio + hpriv - > em_loc ;
2010-06-24 07:34:23 +04:00
const unsigned char * msg_buf = buf ;
2010-04-23 13:28:38 +04:00
u32 em_ctl , msg ;
unsigned long flags ;
int i ;
/* check size validity */
if ( ! ( ap - > flags & ATA_FLAG_EM ) | |
! ( hpriv - > em_msg_type & EM_MSG_TYPE_SGPIO ) | |
size % 4 | | size > hpriv - > em_buf_sz )
return - EINVAL ;
spin_lock_irqsave ( ap - > lock , flags ) ;
em_ctl = readl ( mmio + HOST_EM_CTL ) ;
if ( em_ctl & EM_CTL_TM ) {
spin_unlock_irqrestore ( ap - > lock , flags ) ;
return - EBUSY ;
}
for ( i = 0 ; i < size ; i + = 4 ) {
2010-06-24 07:34:23 +04:00
msg = msg_buf [ i ] | msg_buf [ i + 1 ] < < 8 |
msg_buf [ i + 2 ] < < 16 | msg_buf [ i + 3 ] < < 24 ;
2010-04-23 13:28:38 +04:00
writel ( msg , em_mmio + i ) ;
}
writel ( em_ctl | EM_CTL_TM , mmio + HOST_EM_CTL ) ;
spin_unlock_irqrestore ( ap - > lock , flags ) ;
return size ;
}
2010-03-28 08:22:14 +04:00
/**
* ahci_save_initial_config - Save and fixup initial config values
* @ dev : target AHCI device
* @ hpriv : host private area to store config values
* @ force_port_map : force port map to a specified value
* @ mask_port_map : mask out particular bits from port map
*
* Some registers containing configuration info might be setup by
* BIOS and might be cleared on reset . This function saves the
* initial values of those registers into @ hpriv such that they
* can be restored after controller reset .
*
* If inconsistent , config values are fixed up by this function .
*
* LOCKING :
* None .
*/
void ahci_save_initial_config ( struct device * dev ,
struct ahci_host_priv * hpriv ,
unsigned int force_port_map ,
unsigned int mask_port_map )
{
void __iomem * mmio = hpriv - > mmio ;
u32 cap , cap2 , vers , port_map ;
int i ;
/* make sure AHCI mode is enabled before accessing CAP */
ahci_enable_ahci ( mmio ) ;
/* Values prefixed with saved_ are written back to host after
* reset . Values without are used for driver operation .
*/
hpriv - > saved_cap = cap = readl ( mmio + HOST_CAP ) ;
hpriv - > saved_port_map = port_map = readl ( mmio + HOST_PORTS_IMPL ) ;
/* CAP2 register is only defined for AHCI 1.2 and later */
vers = readl ( mmio + HOST_VERSION ) ;
if ( ( vers > > 16 ) > 1 | |
( ( vers > > 16 ) = = 1 & & ( vers & 0xFFFF ) > = 0x200 ) )
hpriv - > saved_cap2 = cap2 = readl ( mmio + HOST_CAP2 ) ;
else
hpriv - > saved_cap2 = cap2 = 0 ;
/* some chips have errata preventing 64bit use */
if ( ( cap & HOST_CAP_64 ) & & ( hpriv - > flags & AHCI_HFLAG_32BIT_ONLY ) ) {
dev_printk ( KERN_INFO , dev ,
" controller can't do 64bit DMA, forcing 32bit \n " ) ;
cap & = ~ HOST_CAP_64 ;
}
if ( ( cap & HOST_CAP_NCQ ) & & ( hpriv - > flags & AHCI_HFLAG_NO_NCQ ) ) {
dev_printk ( KERN_INFO , dev ,
" controller can't do NCQ, turning off CAP_NCQ \n " ) ;
cap & = ~ HOST_CAP_NCQ ;
}
if ( ! ( cap & HOST_CAP_NCQ ) & & ( hpriv - > flags & AHCI_HFLAG_YES_NCQ ) ) {
dev_printk ( KERN_INFO , dev ,
" controller can do NCQ, turning on CAP_NCQ \n " ) ;
cap | = HOST_CAP_NCQ ;
}
if ( ( cap & HOST_CAP_PMP ) & & ( hpriv - > flags & AHCI_HFLAG_NO_PMP ) ) {
dev_printk ( KERN_INFO , dev ,
" controller can't do PMP, turning off CAP_PMP \n " ) ;
cap & = ~ HOST_CAP_PMP ;
}
if ( ( cap & HOST_CAP_SNTF ) & & ( hpriv - > flags & AHCI_HFLAG_NO_SNTF ) ) {
dev_printk ( KERN_INFO , dev ,
" controller can't do SNTF, turning off CAP_SNTF \n " ) ;
cap & = ~ HOST_CAP_SNTF ;
}
2010-07-24 18:53:48 +04:00
if ( ! ( cap & HOST_CAP_FBS ) & & ( hpriv - > flags & AHCI_HFLAG_YES_FBS ) ) {
dev_printk ( KERN_INFO , dev ,
" controller can do FBS, turning on CAP_FBS \n " ) ;
cap | = HOST_CAP_FBS ;
}
2010-03-28 08:22:14 +04:00
if ( force_port_map & & port_map ! = force_port_map ) {
dev_printk ( KERN_INFO , dev , " forcing port_map 0x%x -> 0x%x \n " ,
port_map , force_port_map ) ;
port_map = force_port_map ;
}
if ( mask_port_map ) {
dev_printk ( KERN_ERR , dev , " masking port_map 0x%x -> 0x%x \n " ,
port_map ,
port_map & mask_port_map ) ;
port_map & = mask_port_map ;
}
/* cross check port_map and cap.n_ports */
if ( port_map ) {
int map_ports = 0 ;
for ( i = 0 ; i < AHCI_MAX_PORTS ; i + + )
if ( port_map & ( 1 < < i ) )
map_ports + + ;
/* If PI has more ports than n_ports, whine, clear
* port_map and let it be generated from n_ports .
*/
if ( map_ports > ahci_nr_ports ( cap ) ) {
dev_printk ( KERN_WARNING , dev ,
" implemented port map (0x%x) contains more "
" ports than nr_ports (%u), using nr_ports \n " ,
port_map , ahci_nr_ports ( cap ) ) ;
port_map = 0 ;
}
}
/* fabricate port_map from cap.nr_ports */
if ( ! port_map ) {
port_map = ( 1 < < ahci_nr_ports ( cap ) ) - 1 ;
dev_printk ( KERN_WARNING , dev ,
" forcing PORTS_IMPL to 0x%x \n " , port_map ) ;
/* write the fixed up value to the PI register */
hpriv - > saved_port_map = port_map ;
}
/* record values to use during operation */
hpriv - > cap = cap ;
hpriv - > cap2 = cap2 ;
hpriv - > port_map = port_map ;
}
EXPORT_SYMBOL_GPL ( ahci_save_initial_config ) ;
/**
* ahci_restore_initial_config - Restore initial config
* @ host : target ATA host
*
* Restore initial config stored by ahci_save_initial_config ( ) .
*
* LOCKING :
* None .
*/
static void ahci_restore_initial_config ( struct ata_host * host )
{
struct ahci_host_priv * hpriv = host - > private_data ;
void __iomem * mmio = hpriv - > mmio ;
writel ( hpriv - > saved_cap , mmio + HOST_CAP ) ;
if ( hpriv - > saved_cap2 )
writel ( hpriv - > saved_cap2 , mmio + HOST_CAP2 ) ;
writel ( hpriv - > saved_port_map , mmio + HOST_PORTS_IMPL ) ;
( void ) readl ( mmio + HOST_PORTS_IMPL ) ; /* flush */
}
static unsigned ahci_scr_offset ( struct ata_port * ap , unsigned int sc_reg )
{
static const int offset [ ] = {
[ SCR_STATUS ] = PORT_SCR_STAT ,
[ SCR_CONTROL ] = PORT_SCR_CTL ,
[ SCR_ERROR ] = PORT_SCR_ERR ,
[ SCR_ACTIVE ] = PORT_SCR_ACT ,
[ SCR_NOTIFICATION ] = PORT_SCR_NTF ,
} ;
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
if ( sc_reg < ARRAY_SIZE ( offset ) & &
( sc_reg ! = SCR_NOTIFICATION | | ( hpriv - > cap & HOST_CAP_SNTF ) ) )
return offset [ sc_reg ] ;
return 0 ;
}
static int ahci_scr_read ( struct ata_link * link , unsigned int sc_reg , u32 * val )
{
void __iomem * port_mmio = ahci_port_base ( link - > ap ) ;
int offset = ahci_scr_offset ( link - > ap , sc_reg ) ;
if ( offset ) {
* val = readl ( port_mmio + offset ) ;
return 0 ;
}
return - EINVAL ;
}
static int ahci_scr_write ( struct ata_link * link , unsigned int sc_reg , u32 val )
{
void __iomem * port_mmio = ahci_port_base ( link - > ap ) ;
int offset = ahci_scr_offset ( link - > ap , sc_reg ) ;
if ( offset ) {
writel ( val , port_mmio + offset ) ;
return 0 ;
}
return - EINVAL ;
}
void ahci_start_engine ( struct ata_port * ap )
{
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 tmp ;
/* start DMA */
tmp = readl ( port_mmio + PORT_CMD ) ;
tmp | = PORT_CMD_START ;
writel ( tmp , port_mmio + PORT_CMD ) ;
readl ( port_mmio + PORT_CMD ) ; /* flush */
}
EXPORT_SYMBOL_GPL ( ahci_start_engine ) ;
int ahci_stop_engine ( struct ata_port * ap )
{
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 tmp ;
tmp = readl ( port_mmio + PORT_CMD ) ;
/* check if the HBA is idle */
if ( ( tmp & ( PORT_CMD_START | PORT_CMD_LIST_ON ) ) = = 0 )
return 0 ;
/* setting HBA to idle */
tmp & = ~ PORT_CMD_START ;
writel ( tmp , port_mmio + PORT_CMD ) ;
/* wait for engine to stop. This could be as long as 500 msec */
tmp = ata_wait_register ( port_mmio + PORT_CMD ,
PORT_CMD_LIST_ON , PORT_CMD_LIST_ON , 1 , 500 ) ;
if ( tmp & PORT_CMD_LIST_ON )
return - EIO ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( ahci_stop_engine ) ;
static void ahci_start_fis_rx ( struct ata_port * ap )
{
void __iomem * port_mmio = ahci_port_base ( ap ) ;
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
struct ahci_port_priv * pp = ap - > private_data ;
u32 tmp ;
/* set FIS registers */
if ( hpriv - > cap & HOST_CAP_64 )
writel ( ( pp - > cmd_slot_dma > > 16 ) > > 16 ,
port_mmio + PORT_LST_ADDR_HI ) ;
writel ( pp - > cmd_slot_dma & 0xffffffff , port_mmio + PORT_LST_ADDR ) ;
if ( hpriv - > cap & HOST_CAP_64 )
writel ( ( pp - > rx_fis_dma > > 16 ) > > 16 ,
port_mmio + PORT_FIS_ADDR_HI ) ;
writel ( pp - > rx_fis_dma & 0xffffffff , port_mmio + PORT_FIS_ADDR ) ;
/* enable FIS reception */
tmp = readl ( port_mmio + PORT_CMD ) ;
tmp | = PORT_CMD_FIS_RX ;
writel ( tmp , port_mmio + PORT_CMD ) ;
/* flush */
readl ( port_mmio + PORT_CMD ) ;
}
static int ahci_stop_fis_rx ( struct ata_port * ap )
{
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 tmp ;
/* disable FIS reception */
tmp = readl ( port_mmio + PORT_CMD ) ;
tmp & = ~ PORT_CMD_FIS_RX ;
writel ( tmp , port_mmio + PORT_CMD ) ;
/* wait for completion, spec says 500ms, give it 1000 */
tmp = ata_wait_register ( port_mmio + PORT_CMD , PORT_CMD_FIS_ON ,
PORT_CMD_FIS_ON , 10 , 1000 ) ;
if ( tmp & PORT_CMD_FIS_ON )
return - EBUSY ;
return 0 ;
}
static void ahci_power_up ( struct ata_port * ap )
{
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 cmd ;
cmd = readl ( port_mmio + PORT_CMD ) & ~ PORT_CMD_ICC_MASK ;
/* spin up device */
if ( hpriv - > cap & HOST_CAP_SSS ) {
cmd | = PORT_CMD_SPIN_UP ;
writel ( cmd , port_mmio + PORT_CMD ) ;
}
/* wake up link */
writel ( cmd | PORT_CMD_ICC_ACTIVE , port_mmio + PORT_CMD ) ;
}
static void ahci_disable_alpm ( struct ata_port * ap )
{
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 cmd ;
struct ahci_port_priv * pp = ap - > private_data ;
/* IPM bits should be disabled by libata-core */
/* get the existing command bits */
cmd = readl ( port_mmio + PORT_CMD ) ;
/* disable ALPM and ASP */
cmd & = ~ PORT_CMD_ASP ;
cmd & = ~ PORT_CMD_ALPE ;
/* force the interface back to active */
cmd | = PORT_CMD_ICC_ACTIVE ;
/* write out new cmd value */
writel ( cmd , port_mmio + PORT_CMD ) ;
cmd = readl ( port_mmio + PORT_CMD ) ;
/* wait 10ms to be sure we've come out of any low power state */
msleep ( 10 ) ;
/* clear out any PhyRdy stuff from interrupt status */
writel ( PORT_IRQ_PHYRDY , port_mmio + PORT_IRQ_STAT ) ;
/* go ahead and clean out PhyRdy Change from Serror too */
ahci_scr_write ( & ap - > link , SCR_ERROR , ( ( 1 < < 16 ) | ( 1 < < 18 ) ) ) ;
/*
* Clear flag to indicate that we should ignore all PhyRdy
* state changes
*/
hpriv - > flags & = ~ AHCI_HFLAG_NO_HOTPLUG ;
/*
* Enable interrupts on Phy Ready .
*/
pp - > intr_mask | = PORT_IRQ_PHYRDY ;
writel ( pp - > intr_mask , port_mmio + PORT_IRQ_MASK ) ;
/*
* don ' t change the link pm policy - we can be called
* just to turn of link pm temporarily
*/
}
static int ahci_enable_alpm ( struct ata_port * ap ,
enum link_pm policy )
{
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 cmd ;
struct ahci_port_priv * pp = ap - > private_data ;
u32 asp ;
/* Make sure the host is capable of link power management */
if ( ! ( hpriv - > cap & HOST_CAP_ALPM ) )
return - EINVAL ;
switch ( policy ) {
case MAX_PERFORMANCE :
case NOT_AVAILABLE :
/*
* if we came here with NOT_AVAILABLE ,
* it just means this is the first time we
* have tried to enable - default to max performance ,
* and let the user go to lower power modes on request .
*/
ahci_disable_alpm ( ap ) ;
return 0 ;
case MIN_POWER :
/* configure HBA to enter SLUMBER */
asp = PORT_CMD_ASP ;
break ;
case MEDIUM_POWER :
/* configure HBA to enter PARTIAL */
asp = 0 ;
break ;
default :
return - EINVAL ;
}
/*
* Disable interrupts on Phy Ready . This keeps us from
* getting woken up due to spurious phy ready interrupts
* TBD - Hot plug should be done via polling now , is
* that even supported ?
*/
pp - > intr_mask & = ~ PORT_IRQ_PHYRDY ;
writel ( pp - > intr_mask , port_mmio + PORT_IRQ_MASK ) ;
/*
* Set a flag to indicate that we should ignore all PhyRdy
* state changes since these can happen now whenever we
* change link state
*/
hpriv - > flags | = AHCI_HFLAG_NO_HOTPLUG ;
/* get the existing command bits */
cmd = readl ( port_mmio + PORT_CMD ) ;
/*
* Set ASP based on Policy
*/
cmd | = asp ;
/*
* Setting this bit will instruct the HBA to aggressively
* enter a lower power link state when it ' s appropriate and
* based on the value set above for ASP
*/
cmd | = PORT_CMD_ALPE ;
/* write out new cmd value */
writel ( cmd , port_mmio + PORT_CMD ) ;
cmd = readl ( port_mmio + PORT_CMD ) ;
/* IPM bits should be set by libata-core */
return 0 ;
}
# ifdef CONFIG_PM
static void ahci_power_down ( struct ata_port * ap )
{
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 cmd , scontrol ;
if ( ! ( hpriv - > cap & HOST_CAP_SSS ) )
return ;
/* put device into listen mode, first set PxSCTL.DET to 0 */
scontrol = readl ( port_mmio + PORT_SCR_CTL ) ;
scontrol & = ~ 0xf ;
writel ( scontrol , port_mmio + PORT_SCR_CTL ) ;
/* then set PxCMD.SUD to 0 */
cmd = readl ( port_mmio + PORT_CMD ) & ~ PORT_CMD_ICC_MASK ;
cmd & = ~ PORT_CMD_SPIN_UP ;
writel ( cmd , port_mmio + PORT_CMD ) ;
}
# endif
static void ahci_start_port ( struct ata_port * ap )
{
struct ahci_port_priv * pp = ap - > private_data ;
struct ata_link * link ;
struct ahci_em_priv * emp ;
ssize_t rc ;
int i ;
/* enable FIS reception */
ahci_start_fis_rx ( ap ) ;
/* enable DMA */
ahci_start_engine ( ap ) ;
/* turn on LEDs */
if ( ap - > flags & ATA_FLAG_EM ) {
ata_for_each_link ( link , ap , EDGE ) {
emp = & pp - > em_priv [ link - > pmp ] ;
/* EM Transmit bit maybe busy during init */
for ( i = 0 ; i < EM_MAX_RETRY ; i + + ) {
rc = ahci_transmit_led_message ( ap ,
emp - > led_state ,
4 ) ;
if ( rc = = - EBUSY )
msleep ( 1 ) ;
else
break ;
}
}
}
if ( ap - > flags & ATA_FLAG_SW_ACTIVITY )
ata_for_each_link ( link , ap , EDGE )
ahci_init_sw_activity ( link ) ;
}
static int ahci_deinit_port ( struct ata_port * ap , const char * * emsg )
{
int rc ;
/* disable DMA */
rc = ahci_stop_engine ( ap ) ;
if ( rc ) {
* emsg = " failed to stop engine " ;
return rc ;
}
/* disable FIS reception */
rc = ahci_stop_fis_rx ( ap ) ;
if ( rc ) {
* emsg = " failed stop FIS RX " ;
return rc ;
}
return 0 ;
}
int ahci_reset_controller ( struct ata_host * host )
{
struct ahci_host_priv * hpriv = host - > private_data ;
void __iomem * mmio = hpriv - > mmio ;
u32 tmp ;
/* we must be in AHCI mode, before using anything
* AHCI - specific , such as HOST_RESET .
*/
ahci_enable_ahci ( mmio ) ;
/* global controller reset */
if ( ! ahci_skip_host_reset ) {
tmp = readl ( mmio + HOST_CTL ) ;
if ( ( tmp & HOST_RESET ) = = 0 ) {
writel ( tmp | HOST_RESET , mmio + HOST_CTL ) ;
readl ( mmio + HOST_CTL ) ; /* flush */
}
/*
* to perform host reset , OS should set HOST_RESET
* and poll until this bit is read to be " 0 " .
* reset must complete within 1 second , or
* the hardware should be considered fried .
*/
tmp = ata_wait_register ( mmio + HOST_CTL , HOST_RESET ,
HOST_RESET , 10 , 1000 ) ;
if ( tmp & HOST_RESET ) {
dev_printk ( KERN_ERR , host - > dev ,
" controller reset failed (0x%x) \n " , tmp ) ;
return - EIO ;
}
/* turn on AHCI mode */
ahci_enable_ahci ( mmio ) ;
/* Some registers might be cleared on reset. Restore
* initial values .
*/
ahci_restore_initial_config ( host ) ;
} else
dev_printk ( KERN_INFO , host - > dev ,
" skipping global host reset \n " ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( ahci_reset_controller ) ;
static void ahci_sw_activity ( struct ata_link * link )
{
struct ata_port * ap = link - > ap ;
struct ahci_port_priv * pp = ap - > private_data ;
struct ahci_em_priv * emp = & pp - > em_priv [ link - > pmp ] ;
if ( ! ( link - > flags & ATA_LFLAG_SW_ACTIVITY ) )
return ;
emp - > activity + + ;
if ( ! timer_pending ( & emp - > timer ) )
mod_timer ( & emp - > timer , jiffies + msecs_to_jiffies ( 10 ) ) ;
}
static void ahci_sw_activity_blink ( unsigned long arg )
{
struct ata_link * link = ( struct ata_link * ) arg ;
struct ata_port * ap = link - > ap ;
struct ahci_port_priv * pp = ap - > private_data ;
struct ahci_em_priv * emp = & pp - > em_priv [ link - > pmp ] ;
unsigned long led_message = emp - > led_state ;
u32 activity_led_state ;
unsigned long flags ;
led_message & = EM_MSG_LED_VALUE ;
led_message | = ap - > port_no | ( link - > pmp < < 8 ) ;
/* check to see if we've had activity. If so,
* toggle state of LED and reset timer . If not ,
* turn LED to desired idle state .
*/
spin_lock_irqsave ( ap - > lock , flags ) ;
if ( emp - > saved_activity ! = emp - > activity ) {
emp - > saved_activity = emp - > activity ;
/* get the current LED state */
activity_led_state = led_message & EM_MSG_LED_VALUE_ON ;
if ( activity_led_state )
activity_led_state = 0 ;
else
activity_led_state = 1 ;
/* clear old state */
led_message & = ~ EM_MSG_LED_VALUE_ACTIVITY ;
/* toggle state */
led_message | = ( activity_led_state < < 16 ) ;
mod_timer ( & emp - > timer , jiffies + msecs_to_jiffies ( 100 ) ) ;
} else {
/* switch to idle */
led_message & = ~ EM_MSG_LED_VALUE_ACTIVITY ;
if ( emp - > blink_policy = = BLINK_OFF )
led_message | = ( 1 < < 16 ) ;
}
spin_unlock_irqrestore ( ap - > lock , flags ) ;
ahci_transmit_led_message ( ap , led_message , 4 ) ;
}
static void ahci_init_sw_activity ( struct ata_link * link )
{
struct ata_port * ap = link - > ap ;
struct ahci_port_priv * pp = ap - > private_data ;
struct ahci_em_priv * emp = & pp - > em_priv [ link - > pmp ] ;
/* init activity stats, setup timer */
emp - > saved_activity = emp - > activity = 0 ;
setup_timer ( & emp - > timer , ahci_sw_activity_blink , ( unsigned long ) link ) ;
/* check our blink policy and set flag for link if it's enabled */
if ( emp - > blink_policy )
link - > flags | = ATA_LFLAG_SW_ACTIVITY ;
}
int ahci_reset_em ( struct ata_host * host )
{
struct ahci_host_priv * hpriv = host - > private_data ;
void __iomem * mmio = hpriv - > mmio ;
u32 em_ctl ;
em_ctl = readl ( mmio + HOST_EM_CTL ) ;
if ( ( em_ctl & EM_CTL_TM ) | | ( em_ctl & EM_CTL_RST ) )
return - EINVAL ;
writel ( em_ctl | EM_CTL_RST , mmio + HOST_EM_CTL ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( ahci_reset_em ) ;
static ssize_t ahci_transmit_led_message ( struct ata_port * ap , u32 state ,
ssize_t size )
{
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
struct ahci_port_priv * pp = ap - > private_data ;
void __iomem * mmio = hpriv - > mmio ;
u32 em_ctl ;
u32 message [ ] = { 0 , 0 } ;
unsigned long flags ;
int pmp ;
struct ahci_em_priv * emp ;
/* get the slot number from the message */
pmp = ( state & EM_MSG_LED_PMP_SLOT ) > > 8 ;
if ( pmp < EM_MAX_SLOTS )
emp = & pp - > em_priv [ pmp ] ;
else
return - EINVAL ;
spin_lock_irqsave ( ap - > lock , flags ) ;
/*
* if we are still busy transmitting a previous message ,
* do not allow
*/
em_ctl = readl ( mmio + HOST_EM_CTL ) ;
if ( em_ctl & EM_CTL_TM ) {
spin_unlock_irqrestore ( ap - > lock , flags ) ;
return - EBUSY ;
}
2010-04-23 13:27:19 +04:00
if ( hpriv - > em_msg_type & EM_MSG_TYPE_LED ) {
/*
* create message header - this is all zero except for
* the message size , which is 4 bytes .
*/
message [ 0 ] | = ( 4 < < 8 ) ;
2010-03-28 08:22:14 +04:00
2010-04-23 13:27:19 +04:00
/* ignore 0:4 of byte zero, fill in port info yourself */
message [ 1 ] = ( ( state & ~ EM_MSG_LED_HBA_PORT ) | ap - > port_no ) ;
2010-03-28 08:22:14 +04:00
2010-04-23 13:27:19 +04:00
/* write message to EM_LOC */
writel ( message [ 0 ] , mmio + hpriv - > em_loc ) ;
writel ( message [ 1 ] , mmio + hpriv - > em_loc + 4 ) ;
/*
* tell hardware to transmit the message
*/
writel ( em_ctl | EM_CTL_TM , mmio + HOST_EM_CTL ) ;
}
2010-03-28 08:22:14 +04:00
/* save off new led state for port/slot */
emp - > led_state = state ;
spin_unlock_irqrestore ( ap - > lock , flags ) ;
return size ;
}
static ssize_t ahci_led_show ( struct ata_port * ap , char * buf )
{
struct ahci_port_priv * pp = ap - > private_data ;
struct ata_link * link ;
struct ahci_em_priv * emp ;
int rc = 0 ;
ata_for_each_link ( link , ap , EDGE ) {
emp = & pp - > em_priv [ link - > pmp ] ;
rc + = sprintf ( buf , " %lx \n " , emp - > led_state ) ;
}
return rc ;
}
static ssize_t ahci_led_store ( struct ata_port * ap , const char * buf ,
size_t size )
{
int state ;
int pmp ;
struct ahci_port_priv * pp = ap - > private_data ;
struct ahci_em_priv * emp ;
state = simple_strtoul ( buf , NULL , 0 ) ;
/* get the slot number from the message */
pmp = ( state & EM_MSG_LED_PMP_SLOT ) > > 8 ;
if ( pmp < EM_MAX_SLOTS )
emp = & pp - > em_priv [ pmp ] ;
else
return - EINVAL ;
/* mask off the activity bits if we are in sw_activity
* mode , user should turn off sw_activity before setting
* activity led through em_message
*/
if ( emp - > blink_policy )
state & = ~ EM_MSG_LED_VALUE_ACTIVITY ;
return ahci_transmit_led_message ( ap , state , size ) ;
}
static ssize_t ahci_activity_store ( struct ata_device * dev , enum sw_activity val )
{
struct ata_link * link = dev - > link ;
struct ata_port * ap = link - > ap ;
struct ahci_port_priv * pp = ap - > private_data ;
struct ahci_em_priv * emp = & pp - > em_priv [ link - > pmp ] ;
u32 port_led_state = emp - > led_state ;
/* save the desired Activity LED behavior */
if ( val = = OFF ) {
/* clear LFLAG */
link - > flags & = ~ ( ATA_LFLAG_SW_ACTIVITY ) ;
/* set the LED to OFF */
port_led_state & = EM_MSG_LED_VALUE_OFF ;
port_led_state | = ( ap - > port_no | ( link - > pmp < < 8 ) ) ;
ahci_transmit_led_message ( ap , port_led_state , 4 ) ;
} else {
link - > flags | = ATA_LFLAG_SW_ACTIVITY ;
if ( val = = BLINK_OFF ) {
/* set LED to ON for idle */
port_led_state & = EM_MSG_LED_VALUE_OFF ;
port_led_state | = ( ap - > port_no | ( link - > pmp < < 8 ) ) ;
port_led_state | = EM_MSG_LED_VALUE_ON ; /* check this */
ahci_transmit_led_message ( ap , port_led_state , 4 ) ;
}
}
emp - > blink_policy = val ;
return 0 ;
}
static ssize_t ahci_activity_show ( struct ata_device * dev , char * buf )
{
struct ata_link * link = dev - > link ;
struct ata_port * ap = link - > ap ;
struct ahci_port_priv * pp = ap - > private_data ;
struct ahci_em_priv * emp = & pp - > em_priv [ link - > pmp ] ;
/* display the saved value of activity behavior for this
* disk .
*/
return sprintf ( buf , " %d \n " , emp - > blink_policy ) ;
}
static void ahci_port_init ( struct device * dev , struct ata_port * ap ,
int port_no , void __iomem * mmio ,
void __iomem * port_mmio )
{
const char * emsg = NULL ;
int rc ;
u32 tmp ;
/* make sure port is not active */
rc = ahci_deinit_port ( ap , & emsg ) ;
if ( rc )
dev_warn ( dev , " %s (%d) \n " , emsg , rc ) ;
/* clear SError */
tmp = readl ( port_mmio + PORT_SCR_ERR ) ;
VPRINTK ( " PORT_SCR_ERR 0x%x \n " , tmp ) ;
writel ( tmp , port_mmio + PORT_SCR_ERR ) ;
/* clear port IRQ */
tmp = readl ( port_mmio + PORT_IRQ_STAT ) ;
VPRINTK ( " PORT_IRQ_STAT 0x%x \n " , tmp ) ;
if ( tmp )
writel ( tmp , port_mmio + PORT_IRQ_STAT ) ;
writel ( 1 < < port_no , mmio + HOST_IRQ_STAT ) ;
}
void ahci_init_controller ( struct ata_host * host )
{
struct ahci_host_priv * hpriv = host - > private_data ;
void __iomem * mmio = hpriv - > mmio ;
int i ;
void __iomem * port_mmio ;
u32 tmp ;
for ( i = 0 ; i < host - > n_ports ; i + + ) {
struct ata_port * ap = host - > ports [ i ] ;
port_mmio = ahci_port_base ( ap ) ;
if ( ata_port_is_dummy ( ap ) )
continue ;
ahci_port_init ( host - > dev , ap , i , mmio , port_mmio ) ;
}
tmp = readl ( mmio + HOST_CTL ) ;
VPRINTK ( " HOST_CTL 0x%x \n " , tmp ) ;
writel ( tmp | HOST_IRQ_EN , mmio + HOST_CTL ) ;
tmp = readl ( mmio + HOST_CTL ) ;
VPRINTK ( " HOST_CTL 0x%x \n " , tmp ) ;
}
EXPORT_SYMBOL_GPL ( ahci_init_controller ) ;
static void ahci_dev_config ( struct ata_device * dev )
{
struct ahci_host_priv * hpriv = dev - > link - > ap - > host - > private_data ;
if ( hpriv - > flags & AHCI_HFLAG_SECT255 ) {
dev - > max_sectors = 255 ;
ata_dev_printk ( dev , KERN_INFO ,
" SB600 AHCI: limiting to 255 sectors per cmd \n " ) ;
}
}
static unsigned int ahci_dev_classify ( struct ata_port * ap )
{
void __iomem * port_mmio = ahci_port_base ( ap ) ;
struct ata_taskfile tf ;
u32 tmp ;
tmp = readl ( port_mmio + PORT_SIG ) ;
tf . lbah = ( tmp > > 24 ) & 0xff ;
tf . lbam = ( tmp > > 16 ) & 0xff ;
tf . lbal = ( tmp > > 8 ) & 0xff ;
tf . nsect = ( tmp ) & 0xff ;
return ata_dev_classify ( & tf ) ;
}
static void ahci_fill_cmd_slot ( struct ahci_port_priv * pp , unsigned int tag ,
u32 opts )
{
dma_addr_t cmd_tbl_dma ;
cmd_tbl_dma = pp - > cmd_tbl_dma + tag * AHCI_CMD_TBL_SZ ;
pp - > cmd_slot [ tag ] . opts = cpu_to_le32 ( opts ) ;
pp - > cmd_slot [ tag ] . status = 0 ;
pp - > cmd_slot [ tag ] . tbl_addr = cpu_to_le32 ( cmd_tbl_dma & 0xffffffff ) ;
pp - > cmd_slot [ tag ] . tbl_addr_hi = cpu_to_le32 ( ( cmd_tbl_dma > > 16 ) > > 16 ) ;
}
int ahci_kick_engine ( struct ata_port * ap )
{
void __iomem * port_mmio = ahci_port_base ( ap ) ;
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
u8 status = readl ( port_mmio + PORT_TFDATA ) & 0xFF ;
u32 tmp ;
int busy , rc ;
/* stop engine */
rc = ahci_stop_engine ( ap ) ;
if ( rc )
goto out_restart ;
/* need to do CLO?
* always do CLO if PMP is attached ( AHCI - 1.3 9.2 )
*/
busy = status & ( ATA_BUSY | ATA_DRQ ) ;
if ( ! busy & & ! sata_pmp_attached ( ap ) ) {
rc = 0 ;
goto out_restart ;
}
if ( ! ( hpriv - > cap & HOST_CAP_CLO ) ) {
rc = - EOPNOTSUPP ;
goto out_restart ;
}
/* perform CLO */
tmp = readl ( port_mmio + PORT_CMD ) ;
tmp | = PORT_CMD_CLO ;
writel ( tmp , port_mmio + PORT_CMD ) ;
rc = 0 ;
tmp = ata_wait_register ( port_mmio + PORT_CMD ,
PORT_CMD_CLO , PORT_CMD_CLO , 1 , 500 ) ;
if ( tmp & PORT_CMD_CLO )
rc = - EIO ;
/* restart engine */
out_restart :
ahci_start_engine ( ap ) ;
return rc ;
}
EXPORT_SYMBOL_GPL ( ahci_kick_engine ) ;
static int ahci_exec_polled_cmd ( struct ata_port * ap , int pmp ,
struct ata_taskfile * tf , int is_cmd , u16 flags ,
unsigned long timeout_msec )
{
const u32 cmd_fis_len = 5 ; /* five dwords */
struct ahci_port_priv * pp = ap - > private_data ;
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u8 * fis = pp - > cmd_tbl ;
u32 tmp ;
/* prep the command */
ata_tf_to_fis ( tf , pmp , is_cmd , fis ) ;
ahci_fill_cmd_slot ( pp , 0 , cmd_fis_len | flags | ( pmp < < 12 ) ) ;
/* issue & wait */
writel ( 1 , port_mmio + PORT_CMD_ISSUE ) ;
if ( timeout_msec ) {
tmp = ata_wait_register ( port_mmio + PORT_CMD_ISSUE , 0x1 , 0x1 ,
1 , timeout_msec ) ;
if ( tmp & 0x1 ) {
ahci_kick_engine ( ap ) ;
return - EBUSY ;
}
} else
readl ( port_mmio + PORT_CMD_ISSUE ) ; /* flush */
return 0 ;
}
int ahci_do_softreset ( struct ata_link * link , unsigned int * class ,
int pmp , unsigned long deadline ,
int ( * check_ready ) ( struct ata_link * link ) )
{
struct ata_port * ap = link - > ap ;
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
const char * reason = NULL ;
unsigned long now , msecs ;
struct ata_taskfile tf ;
int rc ;
DPRINTK ( " ENTER \n " ) ;
/* prepare for SRST (AHCI-1.1 10.4.1) */
rc = ahci_kick_engine ( ap ) ;
if ( rc & & rc ! = - EOPNOTSUPP )
ata_link_printk ( link , KERN_WARNING ,
" failed to reset engine (errno=%d) \n " , rc ) ;
ata_tf_init ( link - > device , & tf ) ;
/* issue the first D2H Register FIS */
msecs = 0 ;
now = jiffies ;
if ( time_after ( now , deadline ) )
msecs = jiffies_to_msecs ( deadline - now ) ;
tf . ctl | = ATA_SRST ;
if ( ahci_exec_polled_cmd ( ap , pmp , & tf , 0 ,
AHCI_CMD_RESET | AHCI_CMD_CLR_BUSY , msecs ) ) {
rc = - EIO ;
reason = " 1st FIS failed " ;
goto fail ;
}
/* spec says at least 5us, but be generous and sleep for 1ms */
msleep ( 1 ) ;
/* issue the second D2H Register FIS */
tf . ctl & = ~ ATA_SRST ;
ahci_exec_polled_cmd ( ap , pmp , & tf , 0 , 0 , 0 ) ;
/* wait for link to become ready */
rc = ata_wait_after_reset ( link , deadline , check_ready ) ;
if ( rc = = - EBUSY & & hpriv - > flags & AHCI_HFLAG_SRST_TOUT_IS_OFFLINE ) {
/*
* Workaround for cases where link online status can ' t
* be trusted . Treat device readiness timeout as link
* offline .
*/
ata_link_printk ( link , KERN_INFO ,
" device not ready, treating as offline \n " ) ;
* class = ATA_DEV_NONE ;
} else if ( rc ) {
/* link occupied, -ENODEV too is an error */
reason = " device not ready " ;
goto fail ;
} else
* class = ahci_dev_classify ( ap ) ;
DPRINTK ( " EXIT, class=%u \n " , * class ) ;
return 0 ;
fail :
ata_link_printk ( link , KERN_ERR , " softreset failed (%s) \n " , reason ) ;
return rc ;
}
int ahci_check_ready ( struct ata_link * link )
{
void __iomem * port_mmio = ahci_port_base ( link - > ap ) ;
u8 status = readl ( port_mmio + PORT_TFDATA ) & 0xFF ;
return ata_check_ready ( status ) ;
}
EXPORT_SYMBOL_GPL ( ahci_check_ready ) ;
static int ahci_softreset ( struct ata_link * link , unsigned int * class ,
unsigned long deadline )
{
int pmp = sata_srst_pmp ( link ) ;
DPRINTK ( " ENTER \n " ) ;
return ahci_do_softreset ( link , class , pmp , deadline , ahci_check_ready ) ;
}
EXPORT_SYMBOL_GPL ( ahci_do_softreset ) ;
static int ahci_hardreset ( struct ata_link * link , unsigned int * class ,
unsigned long deadline )
{
const unsigned long * timing = sata_ehc_deb_timing ( & link - > eh_context ) ;
struct ata_port * ap = link - > ap ;
struct ahci_port_priv * pp = ap - > private_data ;
u8 * d2h_fis = pp - > rx_fis + RX_FIS_D2H_REG ;
struct ata_taskfile tf ;
bool online ;
int rc ;
DPRINTK ( " ENTER \n " ) ;
ahci_stop_engine ( ap ) ;
/* clear D2H reception area to properly wait for D2H FIS */
ata_tf_init ( link - > device , & tf ) ;
tf . command = 0x80 ;
ata_tf_to_fis ( & tf , 0 , 0 , d2h_fis ) ;
rc = sata_link_hardreset ( link , timing , deadline , & online ,
ahci_check_ready ) ;
ahci_start_engine ( ap ) ;
if ( online )
* class = ahci_dev_classify ( ap ) ;
DPRINTK ( " EXIT, rc=%d, class=%u \n " , rc , * class ) ;
return rc ;
}
static void ahci_postreset ( struct ata_link * link , unsigned int * class )
{
struct ata_port * ap = link - > ap ;
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 new_tmp , tmp ;
ata_std_postreset ( link , class ) ;
/* Make sure port's ATAPI bit is set appropriately */
new_tmp = tmp = readl ( port_mmio + PORT_CMD ) ;
if ( * class = = ATA_DEV_ATAPI )
new_tmp | = PORT_CMD_ATAPI ;
else
new_tmp & = ~ PORT_CMD_ATAPI ;
if ( new_tmp ! = tmp ) {
writel ( new_tmp , port_mmio + PORT_CMD ) ;
readl ( port_mmio + PORT_CMD ) ; /* flush */
}
}
static unsigned int ahci_fill_sg ( struct ata_queued_cmd * qc , void * cmd_tbl )
{
struct scatterlist * sg ;
struct ahci_sg * ahci_sg = cmd_tbl + AHCI_CMD_TBL_HDR_SZ ;
unsigned int si ;
VPRINTK ( " ENTER \n " ) ;
/*
* Next , the S / G list .
*/
for_each_sg ( qc - > sg , sg , qc - > n_elem , si ) {
dma_addr_t addr = sg_dma_address ( sg ) ;
u32 sg_len = sg_dma_len ( sg ) ;
ahci_sg [ si ] . addr = cpu_to_le32 ( addr & 0xffffffff ) ;
ahci_sg [ si ] . addr_hi = cpu_to_le32 ( ( addr > > 16 ) > > 16 ) ;
ahci_sg [ si ] . flags_size = cpu_to_le32 ( sg_len - 1 ) ;
}
return si ;
}
static int ahci_pmp_qc_defer ( struct ata_queued_cmd * qc )
{
struct ata_port * ap = qc - > ap ;
struct ahci_port_priv * pp = ap - > private_data ;
if ( ! sata_pmp_attached ( ap ) | | pp - > fbs_enabled )
return ata_std_qc_defer ( qc ) ;
else
return sata_pmp_qc_defer_cmd_switch ( qc ) ;
}
static void ahci_qc_prep ( struct ata_queued_cmd * qc )
{
struct ata_port * ap = qc - > ap ;
struct ahci_port_priv * pp = ap - > private_data ;
int is_atapi = ata_is_atapi ( qc - > tf . protocol ) ;
void * cmd_tbl ;
u32 opts ;
const u32 cmd_fis_len = 5 ; /* five dwords */
unsigned int n_elem ;
/*
* Fill in command table information . First , the header ,
* a SATA Register - Host to Device command FIS .
*/
cmd_tbl = pp - > cmd_tbl + qc - > tag * AHCI_CMD_TBL_SZ ;
ata_tf_to_fis ( & qc - > tf , qc - > dev - > link - > pmp , 1 , cmd_tbl ) ;
if ( is_atapi ) {
memset ( cmd_tbl + AHCI_CMD_TBL_CDB , 0 , 32 ) ;
memcpy ( cmd_tbl + AHCI_CMD_TBL_CDB , qc - > cdb , qc - > dev - > cdb_len ) ;
}
n_elem = 0 ;
if ( qc - > flags & ATA_QCFLAG_DMAMAP )
n_elem = ahci_fill_sg ( qc , cmd_tbl ) ;
/*
* Fill in command slot information .
*/
opts = cmd_fis_len | n_elem < < 16 | ( qc - > dev - > link - > pmp < < 12 ) ;
if ( qc - > tf . flags & ATA_TFLAG_WRITE )
opts | = AHCI_CMD_WRITE ;
if ( is_atapi )
opts | = AHCI_CMD_ATAPI | AHCI_CMD_PREFETCH ;
ahci_fill_cmd_slot ( pp , qc - > tag , opts ) ;
}
static void ahci_fbs_dec_intr ( struct ata_port * ap )
{
struct ahci_port_priv * pp = ap - > private_data ;
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 fbs = readl ( port_mmio + PORT_FBS ) ;
int retries = 3 ;
DPRINTK ( " ENTER \n " ) ;
BUG_ON ( ! pp - > fbs_enabled ) ;
/* time to wait for DEC is not specified by AHCI spec,
* add a retry loop for safety .
*/
writel ( fbs | PORT_FBS_DEC , port_mmio + PORT_FBS ) ;
fbs = readl ( port_mmio + PORT_FBS ) ;
while ( ( fbs & PORT_FBS_DEC ) & & retries - - ) {
udelay ( 1 ) ;
fbs = readl ( port_mmio + PORT_FBS ) ;
}
if ( fbs & PORT_FBS_DEC )
dev_printk ( KERN_ERR , ap - > host - > dev ,
" failed to clear device error \n " ) ;
}
static void ahci_error_intr ( struct ata_port * ap , u32 irq_stat )
{
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
struct ahci_port_priv * pp = ap - > private_data ;
struct ata_eh_info * host_ehi = & ap - > link . eh_info ;
struct ata_link * link = NULL ;
struct ata_queued_cmd * active_qc ;
struct ata_eh_info * active_ehi ;
bool fbs_need_dec = false ;
u32 serror ;
/* determine active link with error */
if ( pp - > fbs_enabled ) {
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 fbs = readl ( port_mmio + PORT_FBS ) ;
int pmp = fbs > > PORT_FBS_DWE_OFFSET ;
if ( ( fbs & PORT_FBS_SDE ) & & ( pmp < ap - > nr_pmp_links ) & &
ata_link_online ( & ap - > pmp_link [ pmp ] ) ) {
link = & ap - > pmp_link [ pmp ] ;
fbs_need_dec = true ;
}
} else
ata_for_each_link ( link , ap , EDGE )
if ( ata_link_active ( link ) )
break ;
if ( ! link )
link = & ap - > link ;
active_qc = ata_qc_from_tag ( ap , link - > active_tag ) ;
active_ehi = & link - > eh_info ;
/* record irq stat */
ata_ehi_clear_desc ( host_ehi ) ;
ata_ehi_push_desc ( host_ehi , " irq_stat 0x%08x " , irq_stat ) ;
/* AHCI needs SError cleared; otherwise, it might lock up */
ahci_scr_read ( & ap - > link , SCR_ERROR , & serror ) ;
ahci_scr_write ( & ap - > link , SCR_ERROR , serror ) ;
host_ehi - > serror | = serror ;
/* some controllers set IRQ_IF_ERR on device errors, ignore it */
if ( hpriv - > flags & AHCI_HFLAG_IGN_IRQ_IF_ERR )
irq_stat & = ~ PORT_IRQ_IF_ERR ;
if ( irq_stat & PORT_IRQ_TF_ERR ) {
/* If qc is active, charge it; otherwise, the active
* link . There ' s no active qc on NCQ errors . It will
* be determined by EH by reading log page 10 h .
*/
if ( active_qc )
active_qc - > err_mask | = AC_ERR_DEV ;
else
active_ehi - > err_mask | = AC_ERR_DEV ;
if ( hpriv - > flags & AHCI_HFLAG_IGN_SERR_INTERNAL )
host_ehi - > serror & = ~ SERR_INTERNAL ;
}
if ( irq_stat & PORT_IRQ_UNK_FIS ) {
u32 * unk = ( u32 * ) ( pp - > rx_fis + RX_FIS_UNK ) ;
active_ehi - > err_mask | = AC_ERR_HSM ;
active_ehi - > action | = ATA_EH_RESET ;
ata_ehi_push_desc ( active_ehi ,
" unknown FIS %08x %08x %08x %08x " ,
unk [ 0 ] , unk [ 1 ] , unk [ 2 ] , unk [ 3 ] ) ;
}
if ( sata_pmp_attached ( ap ) & & ( irq_stat & PORT_IRQ_BAD_PMP ) ) {
active_ehi - > err_mask | = AC_ERR_HSM ;
active_ehi - > action | = ATA_EH_RESET ;
ata_ehi_push_desc ( active_ehi , " incorrect PMP " ) ;
}
if ( irq_stat & ( PORT_IRQ_HBUS_ERR | PORT_IRQ_HBUS_DATA_ERR ) ) {
host_ehi - > err_mask | = AC_ERR_HOST_BUS ;
host_ehi - > action | = ATA_EH_RESET ;
ata_ehi_push_desc ( host_ehi , " host bus error " ) ;
}
if ( irq_stat & PORT_IRQ_IF_ERR ) {
if ( fbs_need_dec )
active_ehi - > err_mask | = AC_ERR_DEV ;
else {
host_ehi - > err_mask | = AC_ERR_ATA_BUS ;
host_ehi - > action | = ATA_EH_RESET ;
}
ata_ehi_push_desc ( host_ehi , " interface fatal error " ) ;
}
if ( irq_stat & ( PORT_IRQ_CONNECT | PORT_IRQ_PHYRDY ) ) {
ata_ehi_hotplugged ( host_ehi ) ;
ata_ehi_push_desc ( host_ehi , " %s " ,
irq_stat & PORT_IRQ_CONNECT ?
" connection status changed " : " PHY RDY changed " ) ;
}
/* okay, let's hand over to EH */
if ( irq_stat & PORT_IRQ_FREEZE )
ata_port_freeze ( ap ) ;
else if ( fbs_need_dec ) {
ata_link_abort ( link ) ;
ahci_fbs_dec_intr ( ap ) ;
} else
ata_port_abort ( ap ) ;
}
static void ahci_port_intr ( struct ata_port * ap )
{
void __iomem * port_mmio = ahci_port_base ( ap ) ;
struct ata_eh_info * ehi = & ap - > link . eh_info ;
struct ahci_port_priv * pp = ap - > private_data ;
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
int resetting = ! ! ( ap - > pflags & ATA_PFLAG_RESETTING ) ;
u32 status , qc_active = 0 ;
int rc ;
status = readl ( port_mmio + PORT_IRQ_STAT ) ;
writel ( status , port_mmio + PORT_IRQ_STAT ) ;
/* ignore BAD_PMP while resetting */
if ( unlikely ( resetting ) )
status & = ~ PORT_IRQ_BAD_PMP ;
/* If we are getting PhyRdy, this is
* just a power state change , we should
* clear out this , plus the PhyRdy / Comm
* Wake bits from Serror
*/
if ( ( hpriv - > flags & AHCI_HFLAG_NO_HOTPLUG ) & &
( status & PORT_IRQ_PHYRDY ) ) {
status & = ~ PORT_IRQ_PHYRDY ;
ahci_scr_write ( & ap - > link , SCR_ERROR , ( ( 1 < < 16 ) | ( 1 < < 18 ) ) ) ;
}
if ( unlikely ( status & PORT_IRQ_ERROR ) ) {
ahci_error_intr ( ap , status ) ;
return ;
}
if ( status & PORT_IRQ_SDB_FIS ) {
/* If SNotification is available, leave notification
* handling to sata_async_notification ( ) . If not ,
* emulate it by snooping SDB FIS RX area .
*
* Snooping FIS RX area is probably cheaper than
* poking SNotification but some constrollers which
* implement SNotification , ICH9 for example , don ' t
* store AN SDB FIS into receive area .
*/
if ( hpriv - > cap & HOST_CAP_SNTF )
sata_async_notification ( ap ) ;
else {
/* If the 'N' bit in word 0 of the FIS is set,
* we just received asynchronous notification .
* Tell libata about it .
*
* Lack of SNotification should not appear in
* ahci 1.2 , so the workaround is unnecessary
* when FBS is enabled .
*/
if ( pp - > fbs_enabled )
WARN_ON_ONCE ( 1 ) ;
else {
const __le32 * f = pp - > rx_fis + RX_FIS_SDB ;
u32 f0 = le32_to_cpu ( f [ 0 ] ) ;
if ( f0 & ( 1 < < 15 ) )
sata_async_notification ( ap ) ;
}
}
}
/* pp->active_link is not reliable once FBS is enabled, both
* PORT_SCR_ACT and PORT_CMD_ISSUE should be checked because
* NCQ and non - NCQ commands may be in flight at the same time .
*/
if ( pp - > fbs_enabled ) {
if ( ap - > qc_active ) {
qc_active = readl ( port_mmio + PORT_SCR_ACT ) ;
qc_active | = readl ( port_mmio + PORT_CMD_ISSUE ) ;
}
} else {
/* pp->active_link is valid iff any command is in flight */
if ( ap - > qc_active & & pp - > active_link - > sactive )
qc_active = readl ( port_mmio + PORT_SCR_ACT ) ;
else
qc_active = readl ( port_mmio + PORT_CMD_ISSUE ) ;
}
rc = ata_qc_complete_multiple ( ap , qc_active ) ;
/* while resetting, invalid completions are expected */
if ( unlikely ( rc < 0 & & ! resetting ) ) {
ehi - > err_mask | = AC_ERR_HSM ;
ehi - > action | = ATA_EH_RESET ;
ata_port_freeze ( ap ) ;
}
}
irqreturn_t ahci_interrupt ( int irq , void * dev_instance )
{
struct ata_host * host = dev_instance ;
struct ahci_host_priv * hpriv ;
unsigned int i , handled = 0 ;
void __iomem * mmio ;
u32 irq_stat , irq_masked ;
VPRINTK ( " ENTER \n " ) ;
hpriv = host - > private_data ;
mmio = hpriv - > mmio ;
/* sigh. 0xffffffff is a valid return from h/w */
irq_stat = readl ( mmio + HOST_IRQ_STAT ) ;
if ( ! irq_stat )
return IRQ_NONE ;
irq_masked = irq_stat & hpriv - > port_map ;
spin_lock ( & host - > lock ) ;
for ( i = 0 ; i < host - > n_ports ; i + + ) {
struct ata_port * ap ;
if ( ! ( irq_masked & ( 1 < < i ) ) )
continue ;
ap = host - > ports [ i ] ;
if ( ap ) {
ahci_port_intr ( ap ) ;
VPRINTK ( " port %u \n " , i ) ;
} else {
VPRINTK ( " port %u (no irq) \n " , i ) ;
if ( ata_ratelimit ( ) )
dev_printk ( KERN_WARNING , host - > dev ,
" interrupt on disabled port %u \n " , i ) ;
}
handled = 1 ;
}
/* HOST_IRQ_STAT behaves as level triggered latch meaning that
* it should be cleared after all the port events are cleared ;
* otherwise , it will raise a spurious interrupt after each
* valid one . Please read section 10.6 .2 of ahci 1.1 for more
* information .
*
* Also , use the unmasked value to clear interrupt as spurious
* pending event on a dummy port might cause screaming IRQ .
*/
writel ( irq_stat , mmio + HOST_IRQ_STAT ) ;
spin_unlock ( & host - > lock ) ;
VPRINTK ( " EXIT \n " ) ;
return IRQ_RETVAL ( handled ) ;
}
EXPORT_SYMBOL_GPL ( ahci_interrupt ) ;
static unsigned int ahci_qc_issue ( struct ata_queued_cmd * qc )
{
struct ata_port * ap = qc - > ap ;
void __iomem * port_mmio = ahci_port_base ( ap ) ;
struct ahci_port_priv * pp = ap - > private_data ;
/* Keep track of the currently active link. It will be used
* in completion path to determine whether NCQ phase is in
* progress .
*/
pp - > active_link = qc - > dev - > link ;
if ( qc - > tf . protocol = = ATA_PROT_NCQ )
writel ( 1 < < qc - > tag , port_mmio + PORT_SCR_ACT ) ;
if ( pp - > fbs_enabled & & pp - > fbs_last_dev ! = qc - > dev - > link - > pmp ) {
u32 fbs = readl ( port_mmio + PORT_FBS ) ;
fbs & = ~ ( PORT_FBS_DEV_MASK | PORT_FBS_DEC ) ;
fbs | = qc - > dev - > link - > pmp < < PORT_FBS_DEV_OFFSET ;
writel ( fbs , port_mmio + PORT_FBS ) ;
pp - > fbs_last_dev = qc - > dev - > link - > pmp ;
}
writel ( 1 < < qc - > tag , port_mmio + PORT_CMD_ISSUE ) ;
ahci_sw_activity ( qc - > dev - > link ) ;
return 0 ;
}
static bool ahci_qc_fill_rtf ( struct ata_queued_cmd * qc )
{
struct ahci_port_priv * pp = qc - > ap - > private_data ;
u8 * d2h_fis = pp - > rx_fis + RX_FIS_D2H_REG ;
if ( pp - > fbs_enabled )
d2h_fis + = qc - > dev - > link - > pmp * AHCI_RX_FIS_SZ ;
ata_tf_from_fis ( d2h_fis , & qc - > result_tf ) ;
return true ;
}
static void ahci_freeze ( struct ata_port * ap )
{
void __iomem * port_mmio = ahci_port_base ( ap ) ;
/* turn IRQ off */
writel ( 0 , port_mmio + PORT_IRQ_MASK ) ;
}
static void ahci_thaw ( struct ata_port * ap )
{
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
void __iomem * mmio = hpriv - > mmio ;
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 tmp ;
struct ahci_port_priv * pp = ap - > private_data ;
/* clear IRQ */
tmp = readl ( port_mmio + PORT_IRQ_STAT ) ;
writel ( tmp , port_mmio + PORT_IRQ_STAT ) ;
writel ( 1 < < ap - > port_no , mmio + HOST_IRQ_STAT ) ;
/* turn IRQ back on */
writel ( pp - > intr_mask , port_mmio + PORT_IRQ_MASK ) ;
}
static void ahci_error_handler ( struct ata_port * ap )
{
if ( ! ( ap - > pflags & ATA_PFLAG_FROZEN ) ) {
/* restart engine */
ahci_stop_engine ( ap ) ;
ahci_start_engine ( ap ) ;
}
sata_pmp_error_handler ( ap ) ;
2010-06-07 17:15:08 +04:00
if ( ! ata_dev_enabled ( ap - > link . device ) )
ahci_stop_engine ( ap ) ;
2010-03-28 08:22:14 +04:00
}
static void ahci_post_internal_cmd ( struct ata_queued_cmd * qc )
{
struct ata_port * ap = qc - > ap ;
/* make DMA engine forget about the failed command */
if ( qc - > flags & ATA_QCFLAG_FAILED )
ahci_kick_engine ( ap ) ;
}
static void ahci_enable_fbs ( struct ata_port * ap )
{
struct ahci_port_priv * pp = ap - > private_data ;
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 fbs ;
int rc ;
if ( ! pp - > fbs_supported )
return ;
fbs = readl ( port_mmio + PORT_FBS ) ;
if ( fbs & PORT_FBS_EN ) {
pp - > fbs_enabled = true ;
pp - > fbs_last_dev = - 1 ; /* initialization */
return ;
}
rc = ahci_stop_engine ( ap ) ;
if ( rc )
return ;
writel ( fbs | PORT_FBS_EN , port_mmio + PORT_FBS ) ;
fbs = readl ( port_mmio + PORT_FBS ) ;
if ( fbs & PORT_FBS_EN ) {
dev_printk ( KERN_INFO , ap - > host - > dev , " FBS is enabled. \n " ) ;
pp - > fbs_enabled = true ;
pp - > fbs_last_dev = - 1 ; /* initialization */
} else
dev_printk ( KERN_ERR , ap - > host - > dev , " Failed to enable FBS \n " ) ;
ahci_start_engine ( ap ) ;
}
static void ahci_disable_fbs ( struct ata_port * ap )
{
struct ahci_port_priv * pp = ap - > private_data ;
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 fbs ;
int rc ;
if ( ! pp - > fbs_supported )
return ;
fbs = readl ( port_mmio + PORT_FBS ) ;
if ( ( fbs & PORT_FBS_EN ) = = 0 ) {
pp - > fbs_enabled = false ;
return ;
}
rc = ahci_stop_engine ( ap ) ;
if ( rc )
return ;
writel ( fbs & ~ PORT_FBS_EN , port_mmio + PORT_FBS ) ;
fbs = readl ( port_mmio + PORT_FBS ) ;
if ( fbs & PORT_FBS_EN )
dev_printk ( KERN_ERR , ap - > host - > dev , " Failed to disable FBS \n " ) ;
else {
dev_printk ( KERN_INFO , ap - > host - > dev , " FBS is disabled. \n " ) ;
pp - > fbs_enabled = false ;
}
ahci_start_engine ( ap ) ;
}
static void ahci_pmp_attach ( struct ata_port * ap )
{
void __iomem * port_mmio = ahci_port_base ( ap ) ;
struct ahci_port_priv * pp = ap - > private_data ;
u32 cmd ;
cmd = readl ( port_mmio + PORT_CMD ) ;
cmd | = PORT_CMD_PMP ;
writel ( cmd , port_mmio + PORT_CMD ) ;
ahci_enable_fbs ( ap ) ;
pp - > intr_mask | = PORT_IRQ_BAD_PMP ;
writel ( pp - > intr_mask , port_mmio + PORT_IRQ_MASK ) ;
}
static void ahci_pmp_detach ( struct ata_port * ap )
{
void __iomem * port_mmio = ahci_port_base ( ap ) ;
struct ahci_port_priv * pp = ap - > private_data ;
u32 cmd ;
ahci_disable_fbs ( ap ) ;
cmd = readl ( port_mmio + PORT_CMD ) ;
cmd & = ~ PORT_CMD_PMP ;
writel ( cmd , port_mmio + PORT_CMD ) ;
pp - > intr_mask & = ~ PORT_IRQ_BAD_PMP ;
writel ( pp - > intr_mask , port_mmio + PORT_IRQ_MASK ) ;
}
static int ahci_port_resume ( struct ata_port * ap )
{
ahci_power_up ( ap ) ;
ahci_start_port ( ap ) ;
if ( sata_pmp_attached ( ap ) )
ahci_pmp_attach ( ap ) ;
else
ahci_pmp_detach ( ap ) ;
return 0 ;
}
# ifdef CONFIG_PM
static int ahci_port_suspend ( struct ata_port * ap , pm_message_t mesg )
{
const char * emsg = NULL ;
int rc ;
rc = ahci_deinit_port ( ap , & emsg ) ;
if ( rc = = 0 )
ahci_power_down ( ap ) ;
else {
ata_port_printk ( ap , KERN_ERR , " %s (%d) \n " , emsg , rc ) ;
ahci_start_port ( ap ) ;
}
return rc ;
}
# endif
static int ahci_port_start ( struct ata_port * ap )
{
struct ahci_host_priv * hpriv = ap - > host - > private_data ;
struct device * dev = ap - > host - > dev ;
struct ahci_port_priv * pp ;
void * mem ;
dma_addr_t mem_dma ;
size_t dma_sz , rx_fis_sz ;
pp = devm_kzalloc ( dev , sizeof ( * pp ) , GFP_KERNEL ) ;
if ( ! pp )
return - ENOMEM ;
/* check FBS capability */
if ( ( hpriv - > cap & HOST_CAP_FBS ) & & sata_pmp_supported ( ap ) ) {
void __iomem * port_mmio = ahci_port_base ( ap ) ;
u32 cmd = readl ( port_mmio + PORT_CMD ) ;
if ( cmd & PORT_CMD_FBSCP )
pp - > fbs_supported = true ;
2010-07-24 18:53:48 +04:00
else if ( hpriv - > flags & AHCI_HFLAG_YES_FBS ) {
dev_printk ( KERN_INFO , dev ,
" port %d can do FBS, forcing FBSCP \n " ,
ap - > port_no ) ;
pp - > fbs_supported = true ;
} else
2010-03-28 08:22:14 +04:00
dev_printk ( KERN_WARNING , dev ,
2010-07-24 18:53:48 +04:00
" port %d is not capable of FBS \n " ,
ap - > port_no ) ;
2010-03-28 08:22:14 +04:00
}
if ( pp - > fbs_supported ) {
dma_sz = AHCI_PORT_PRIV_FBS_DMA_SZ ;
rx_fis_sz = AHCI_RX_FIS_SZ * 16 ;
} else {
dma_sz = AHCI_PORT_PRIV_DMA_SZ ;
rx_fis_sz = AHCI_RX_FIS_SZ ;
}
mem = dmam_alloc_coherent ( dev , dma_sz , & mem_dma , GFP_KERNEL ) ;
if ( ! mem )
return - ENOMEM ;
memset ( mem , 0 , dma_sz ) ;
/*
* First item in chunk of DMA memory : 32 - slot command table ,
* 32 bytes each in size
*/
pp - > cmd_slot = mem ;
pp - > cmd_slot_dma = mem_dma ;
mem + = AHCI_CMD_SLOT_SZ ;
mem_dma + = AHCI_CMD_SLOT_SZ ;
/*
* Second item : Received - FIS area
*/
pp - > rx_fis = mem ;
pp - > rx_fis_dma = mem_dma ;
mem + = rx_fis_sz ;
mem_dma + = rx_fis_sz ;
/*
* Third item : data area for storing a single command
* and its scatter - gather table
*/
pp - > cmd_tbl = mem ;
pp - > cmd_tbl_dma = mem_dma ;
/*
* Save off initial list of interrupts to be enabled .
* This could be changed later
*/
pp - > intr_mask = DEF_PORT_IRQ ;
ap - > private_data = pp ;
/* engage engines, captain */
return ahci_port_resume ( ap ) ;
}
static void ahci_port_stop ( struct ata_port * ap )
{
const char * emsg = NULL ;
int rc ;
/* de-initialize port */
rc = ahci_deinit_port ( ap , & emsg ) ;
if ( rc )
ata_port_printk ( ap , KERN_WARNING , " %s (%d) \n " , emsg , rc ) ;
}
void ahci_print_info ( struct ata_host * host , const char * scc_s )
{
struct ahci_host_priv * hpriv = host - > private_data ;
void __iomem * mmio = hpriv - > mmio ;
u32 vers , cap , cap2 , impl , speed ;
const char * speed_s ;
vers = readl ( mmio + HOST_VERSION ) ;
cap = hpriv - > cap ;
cap2 = hpriv - > cap2 ;
impl = hpriv - > port_map ;
speed = ( cap > > 20 ) & 0xf ;
if ( speed = = 1 )
speed_s = " 1.5 " ;
else if ( speed = = 2 )
speed_s = " 3 " ;
else if ( speed = = 3 )
speed_s = " 6 " ;
else
speed_s = " ? " ;
dev_info ( host - > dev ,
" AHCI %02x%02x.%02x%02x "
" %u slots %u ports %s Gbps 0x%x impl %s mode \n "
,
( vers > > 24 ) & 0xff ,
( vers > > 16 ) & 0xff ,
( vers > > 8 ) & 0xff ,
vers & 0xff ,
( ( cap > > 8 ) & 0x1f ) + 1 ,
( cap & 0x1f ) + 1 ,
speed_s ,
impl ,
scc_s ) ;
dev_info ( host - > dev ,
" flags: "
" %s%s%s%s%s%s%s "
" %s%s%s%s%s%s%s "
" %s%s%s%s%s%s \n "
,
cap & HOST_CAP_64 ? " 64bit " : " " ,
cap & HOST_CAP_NCQ ? " ncq " : " " ,
cap & HOST_CAP_SNTF ? " sntf " : " " ,
cap & HOST_CAP_MPS ? " ilck " : " " ,
cap & HOST_CAP_SSS ? " stag " : " " ,
cap & HOST_CAP_ALPM ? " pm " : " " ,
cap & HOST_CAP_LED ? " led " : " " ,
cap & HOST_CAP_CLO ? " clo " : " " ,
cap & HOST_CAP_ONLY ? " only " : " " ,
cap & HOST_CAP_PMP ? " pmp " : " " ,
cap & HOST_CAP_FBS ? " fbs " : " " ,
cap & HOST_CAP_PIO_MULTI ? " pio " : " " ,
cap & HOST_CAP_SSC ? " slum " : " " ,
cap & HOST_CAP_PART ? " part " : " " ,
cap & HOST_CAP_CCC ? " ccc " : " " ,
cap & HOST_CAP_EMS ? " ems " : " " ,
cap & HOST_CAP_SXS ? " sxs " : " " ,
cap2 & HOST_CAP2_APST ? " apst " : " " ,
cap2 & HOST_CAP2_NVMHCI ? " nvmp " : " " ,
cap2 & HOST_CAP2_BOH ? " boh " : " "
) ;
}
EXPORT_SYMBOL_GPL ( ahci_print_info ) ;
void ahci_set_em_messages ( struct ahci_host_priv * hpriv ,
struct ata_port_info * pi )
{
u8 messages ;
void __iomem * mmio = hpriv - > mmio ;
u32 em_loc = readl ( mmio + HOST_EM_LOC ) ;
u32 em_ctl = readl ( mmio + HOST_EM_CTL ) ;
if ( ! ahci_em_messages | | ! ( hpriv - > cap & HOST_CAP_EMS ) )
return ;
messages = ( em_ctl & EM_CTRL_MSG_TYPE ) > > 16 ;
2010-04-23 13:27:19 +04:00
if ( messages ) {
2010-03-28 08:22:14 +04:00
/* store em_loc */
hpriv - > em_loc = ( ( em_loc > > 16 ) * 4 ) ;
2010-04-23 13:28:38 +04:00
hpriv - > em_buf_sz = ( ( em_loc & 0xff ) * 4 ) ;
2010-04-23 13:27:19 +04:00
hpriv - > em_msg_type = messages ;
2010-03-28 08:22:14 +04:00
pi - > flags | = ATA_FLAG_EM ;
if ( ! ( em_ctl & EM_CTL_ALHD ) )
pi - > flags | = ATA_FLAG_SW_ACTIVITY ;
}
}
EXPORT_SYMBOL_GPL ( ahci_set_em_messages ) ;
MODULE_AUTHOR ( " Jeff Garzik " ) ;
MODULE_DESCRIPTION ( " Common AHCI SATA low-level routines " ) ;
MODULE_LICENSE ( " GPL " ) ;