Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
L
linux-elphel
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Commits
Open sidebar
Elphel
linux-elphel
Commits
7180ee47
Commit
7180ee47
authored
Feb 23, 2016
by
Mikhail Karpenko
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add libahci with debug output
parent
e1485041
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
2999 additions
and
0 deletions
+2999
-0
libahci.c
src/drivers/ata/libahci.c
+2999
-0
No files found.
src/drivers/ata/libahci.c
0 → 100644
View file @
7180ee47
/*
* libahci.c - Common AHCI SATA low-level routines
*
* Maintained by: Tejun Heo <tj@kernel.org>
* 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 02139, 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>
#include <linux/gfp.h>
#include <linux/module.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"
#include "libahci_debug.h"
#include "libata.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_set_lpm
(
struct
ata_link
*
link
,
enum
ata_lpm_policy
policy
,
unsigned
hints
);
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
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_set_aggressive_devslp
(
struct
ata_port
*
ap
,
bool
sleep
);
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_pmp_retry_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_post_internal_cmd
(
struct
ata_queued_cmd
*
qc
);
static
void
ahci_dev_config
(
struct
ata_device
*
dev
);
#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
);
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
);
static
ssize_t
ahci_show_em_supported
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
char
*
buf
);
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
);
static
DEVICE_ATTR
(
em_buffer
,
S_IWUSR
|
S_IRUGO
,
ahci_read_em_buffer
,
ahci_store_em_buffer
);
static
DEVICE_ATTR
(
em_message_supported
,
S_IRUGO
,
ahci_show_em_supported
,
NULL
);
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
,
&
dev_attr_em_buffer
,
&
dev_attr_em_message_supported
,
NULL
};
EXPORT_SYMBOL_GPL
(
ahci_shost_attrs
);
struct
device_attribute
*
ahci_sdev_attrs
[]
=
{
&
dev_attr_sw_activity
,
&
dev_attr_unload_heads
,
NULL
};
EXPORT_SYMBOL_GPL
(
ahci_sdev_attrs
);
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
,
.
set_lpm
=
ahci_set_lpm
,
.
em_show
=
ahci_led_show
,
.
em_store
=
ahci_led_store
,
.
sw_activity_show
=
ahci_activity_show
,
.
sw_activity_store
=
ahci_activity_store
,
.
transmit_led_message
=
ahci_transmit_led_message
,
#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
);
struct
ata_port_operations
ahci_pmp_retry_srst_ops
=
{
.
inherits
=
&
ahci_ops
,
.
softreset
=
ahci_pmp_retry_softreset
,
};
EXPORT_SYMBOL_GPL
(
ahci_pmp_retry_srst_ops
);
static
bool
ahci_em_messages
__read_mostly
=
true
;
EXPORT_SYMBOL_GPL
(
ahci_em_messages
);
module_param
(
ahci_em_messages
,
bool
,
0444
);
/* add other LED protocol types when they become supported */
MODULE_PARM_DESC
(
ahci_em_messages
,
"AHCI Enclosure Management Message control (0 = off, 1 = on)"
);
/* device sleep idle timeout in ms */
static
int
devslp_idle_timeout
__read_mostly
=
1000
;
module_param
(
devslp_idle_timeout
,
int
,
0644
);
MODULE_PARM_DESC
(
devslp_idle_timeout
,
"device sleep idle timeout"
);
static
void
ahci_enable_ahci
(
void
__iomem
*
mmio
)
{
int
i
;
u32
tmp
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
int
len
;
/* 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
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"write HOST_AHCI_EN to HOST_CTL register"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
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
));
}
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"read EM buffer"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
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_warn
(
ap
,
"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
;
const
unsigned
char
*
msg_buf
=
buf
;
u32
em_ctl
,
msg
;
unsigned
long
flags
;
int
i
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"transmit message in EM buffer"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* 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
)
{
msg
=
msg_buf
[
i
]
|
msg_buf
[
i
+
1
]
<<
8
|
msg_buf
[
i
+
2
]
<<
16
|
msg_buf
[
i
+
3
]
<<
24
;
writel
(
msg
,
em_mmio
+
i
);
}
writel
(
em_ctl
|
EM_CTL_TM
,
mmio
+
HOST_EM_CTL
);
spin_unlock_irqrestore
(
ap
->
lock
,
flags
);
return
size
;
}
static
ssize_t
ahci_show_em_supported
(
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
;
u32
em_ctl
;
em_ctl
=
readl
(
mmio
+
HOST_EM_CTL
);
return
sprintf
(
buf
,
"%s%s%s%s
\n
"
,
em_ctl
&
EM_CTL_LED
?
"led "
:
""
,
em_ctl
&
EM_CTL_SAFTE
?
"saf-te "
:
""
,
em_ctl
&
EM_CTL_SES
?
"ses-2 "
:
""
,
em_ctl
&
EM_CTL_SGPIO
?
"sgpio "
:
""
);
}
/**
* ahci_save_initial_config - Save and fixup initial config values
* @dev: target AHCI device
* @hpriv: host private area to store config values
*
* 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.
*
* If it is not set already this function sets hpriv->start_engine to
* ahci_start_engine.
*
* LOCKING:
* None.
*/
void
ahci_save_initial_config
(
struct
device
*
dev
,
struct
ahci_host_priv
*
hpriv
)
{
void
__iomem
*
mmio
=
hpriv
->
mmio
;
u32
cap
,
cap2
,
vers
,
port_map
;
int
i
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"read and save host capabilities"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* 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_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_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_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_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_info
(
dev
,
"controller can't do SNTF, turning off CAP_SNTF
\n
"
);
cap
&=
~
HOST_CAP_SNTF
;
}
if
((
cap2
&
HOST_CAP2_SDS
)
&&
(
hpriv
->
flags
&
AHCI_HFLAG_NO_DEVSLP
))
{
dev_info
(
dev
,
"controller can't do DEVSLP, turning off
\n
"
);
cap2
&=
~
HOST_CAP2_SDS
;
cap2
&=
~
HOST_CAP2_SADM
;
}
if
(
!
(
cap
&
HOST_CAP_FBS
)
&&
(
hpriv
->
flags
&
AHCI_HFLAG_YES_FBS
))
{
dev_info
(
dev
,
"controller can do FBS, turning on CAP_FBS
\n
"
);
cap
|=
HOST_CAP_FBS
;
}
if
((
cap
&
HOST_CAP_FBS
)
&&
(
hpriv
->
flags
&
AHCI_HFLAG_NO_FBS
))
{
dev_info
(
dev
,
"controller can't do FBS, turning off CAP_FBS
\n
"
);
cap
&=
~
HOST_CAP_FBS
;
}
if
(
hpriv
->
force_port_map
&&
port_map
!=
hpriv
->
force_port_map
)
{
dev_info
(
dev
,
"forcing port_map 0x%x -> 0x%x
\n
"
,
port_map
,
hpriv
->
force_port_map
);
port_map
=
hpriv
->
force_port_map
;
}
if
(
hpriv
->
mask_port_map
)
{
dev_warn
(
dev
,
"masking port_map 0x%x -> 0x%x
\n
"
,
port_map
,
port_map
&
hpriv
->
mask_port_map
);
port_map
&=
hpriv
->
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_warn
(
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_warn
(
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
;
if
(
!
hpriv
->
start_engine
)
hpriv
->
start_engine
=
ahci_start_engine
;
}
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"restore host capabilities"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
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
);
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"read port %u SATA status and control registers"
,
link
->
ap
->
port_no
);
libahci_debug_event
(
link
->
ap
,
msg_str
,
len
);
}
if
(
offset
)
{
*
val
=
readl
(
port_mmio
+
offset
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
port %u offset: 0x%x, value: 0x%x"
,
link
->
ap
->
port_no
,
offset
,
*
val
);
libahci_debug_event
(
link
->
ap
,
msg_str
,
len
);
}
return
0
;
}
if
(
msg_str
!=
NULL
)
kfree
(
msg_str
);
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
);
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"write port %u SATA status and control registers"
,
link
->
ap
->
port_no
);
libahci_debug_event
(
link
->
ap
,
msg_str
,
len
);
}
if
(
offset
)
{
writel
(
val
,
port_mmio
+
offset
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
port %u offset: 0x%x, value: 0x%x"
,
link
->
ap
->
port_no
,
offset
,
val
);
libahci_debug_event
(
link
->
ap
,
msg_str
,
len
);
}
return
0
;
}
if
(
msg_str
!=
NULL
)
kfree
(
msg_str
);
return
-
EINVAL
;
}
void
ahci_start_engine
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"start port %u command list DMA engine"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
/* start DMA */
tmp
=
readl
(
port_mmio
+
PORT_CMD
);
tmp
|=
PORT_CMD_START
;
writel
(
tmp
,
port_mmio
+
PORT_CMD
);
readl
(
port_mmio
+
PORT_CMD
);
/* flush */
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
write to port %u register PxCMD, value: 0x%08x"
,
ap
->
port_no
,
tmp
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
}
EXPORT_SYMBOL_GPL
(
ahci_start_engine
);
int
ahci_stop_engine
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"stop port %u command list DMA engine"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
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
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
write to port %u register PxCMD, value: 0x%08x"
,
ap
->
port_no
,
tmp
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* wait for engine to stop. This could be as long as 500 msec */
tmp
=
ata_wait_register
(
ap
,
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
);
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"start port %u FIS RX reception"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* 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
);
}
EXPORT_SYMBOL_GPL
(
ahci_start_fis_rx
);
static
int
ahci_stop_fis_rx
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"stop port %u FIS RX reception"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* 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
(
ap
,
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u wake up link"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
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
int
ahci_set_lpm
(
struct
ata_link
*
link
,
enum
ata_lpm_policy
policy
,
unsigned
int
hints
)
{
struct
ata_port
*
ap
=
link
->
ap
;
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"set port %u link power management"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
policy
!=
ATA_LPM_MAX_POWER
)
{
/*
* Disable interrupts on Phy Ready. This keeps us from
* getting woken up due to spurious phy ready
* interrupts.
*/
pp
->
intr_mask
&=
~
PORT_IRQ_PHYRDY
;
writel
(
pp
->
intr_mask
,
port_mmio
+
PORT_IRQ_MASK
);
sata_link_scr_lpm
(
link
,
policy
,
false
);
}
if
(
hpriv
->
cap
&
HOST_CAP_ALPM
)
{
u32
cmd
=
readl
(
port_mmio
+
PORT_CMD
);
if
(
policy
==
ATA_LPM_MAX_POWER
||
!
(
hints
&
ATA_LPM_HIPM
))
{
cmd
&=
~
(
PORT_CMD_ASP
|
PORT_CMD_ALPE
);
cmd
|=
PORT_CMD_ICC_ACTIVE
;
writel
(
cmd
,
port_mmio
+
PORT_CMD
);
readl
(
port_mmio
+
PORT_CMD
);
/* wait 10ms to be sure we've come out of LPM state */
ata_msleep
(
ap
,
10
);
}
else
{
cmd
|=
PORT_CMD_ALPE
;
if
(
policy
==
ATA_LPM_MIN_POWER
)
cmd
|=
PORT_CMD_ASP
;
/* write out new cmd value */
writel
(
cmd
,
port_mmio
+
PORT_CMD
);
}
}
/* set aggressive device sleep */
if
((
hpriv
->
cap2
&
HOST_CAP2_SDS
)
&&
(
hpriv
->
cap2
&
HOST_CAP2_SADM
)
&&
(
link
->
device
->
flags
&
ATA_DFLAG_DEVSLP
))
{
if
(
policy
==
ATA_LPM_MIN_POWER
)
ahci_set_aggressive_devslp
(
ap
,
true
);
else
ahci_set_aggressive_devslp
(
ap
,
false
);
}
if
(
policy
==
ATA_LPM_MAX_POWER
)
{
sata_link_scr_lpm
(
link
,
policy
,
false
);
/* turn PHYRDY IRQ back on */
pp
->
intr_mask
|=
PORT_IRQ_PHYRDY
;
writel
(
pp
->
intr_mask
,
port_mmio
+
PORT_IRQ_MASK
);
}
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_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ata_link
*
link
;
struct
ahci_em_priv
*
emp
;
ssize_t
rc
;
int
i
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"start port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* enable FIS reception */
ahci_start_fis_rx
(
ap
);
/* enable DMA */
if
(
!
(
hpriv
->
flags
&
AHCI_HFLAG_DELAY_ENGINE
))
hpriv
->
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
=
ap
->
ops
->
transmit_led_message
(
ap
,
emp
->
led_state
,
4
);
/*
* If busy, give a breather but do not
* release EH ownership by using msleep()
* instead of ata_msleep(). EM Transmit
* bit is busy for the whole host and
* releasing ownership will cause other
* ports to fail the same way.
*/
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"stop port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* 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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"trying to reset host controller"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* 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
(
NULL
,
mmio
+
HOST_CTL
,
HOST_RESET
,
HOST_RESET
,
10
,
1000
);
if
(
tmp
&
HOST_RESET
)
{
dev_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_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
);
ap
->
ops
->
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"reset EM message logic"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"send port %u LED message"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* 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
;
}
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
);
/* ignore 0:4 of byte zero, fill in port info yourself */
message
[
1
]
=
((
state
&
~
EM_MSG_LED_HBA_PORT
)
|
ap
->
port_no
);
/* 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
);
}
/* 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
)
{
unsigned
int
state
;
int
pmp
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ahci_em_priv
*
emp
;
if
(
kstrtouint
(
buf
,
0
,
&
state
)
<
0
)
return
-
EINVAL
;
/* 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
ap
->
ops
->
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
));
ap
->
ops
->
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 */
ap
->
ops
->
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u init"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* 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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"init AHCI controller"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
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_info
(
dev
,
"SB600 AHCI: limiting to 255 sectors per cmd
\n
"
);
}
}
unsigned
int
ahci_dev_classify
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
struct
ata_taskfile
tf
;
u32
tmp
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
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
;
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"reading port %u signature: PxSIG = 0x%08x"
,
ap
->
port_no
,
tmp
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
return
ata_dev_classify
(
&
tf
);
}
EXPORT_SYMBOL_GPL
(
ahci_dev_classify
);
void
ahci_fill_cmd_slot
(
struct
ahci_port_priv
*
pp
,
unsigned
int
tag
,
u32
opts
)
{
dma_addr_t
cmd_tbl_dma
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
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
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
fill command slot %u: DW0 = 0x%08x, DW1 = 0x%08x, DW2 = 0x%08x, DW3 = 0x%08x"
,
tag
,
pp
->
cmd_slot
[
tag
].
opts
,
pp
->
cmd_slot
[
tag
].
status
,
pp
->
cmd_slot
[
tag
].
tbl_addr
,
pp
->
cmd_slot
[
tag
].
tbl_addr_hi
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
}
EXPORT_SYMBOL_GPL
(
ahci_fill_cmd_slot
);
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"kick port %u DMA engine"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
/* 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
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
write port %u register PxCMD, value: 0x%08x"
,
ap
->
port_no
,
tmp
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
rc
=
0
;
tmp
=
ata_wait_register
(
ap
,
port_mmio
+
PORT_CMD
,
PORT_CMD_CLO
,
PORT_CMD_CLO
,
1
,
500
);
if
(
tmp
&
PORT_CMD_CLO
)
rc
=
-
EIO
;
/* restart engine */
out_restart:
hpriv
->
start_engine
(
ap
);
kfree
(
msg_str
);
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u command issue"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* prep the command */
ata_tf_to_fis
(
tf
,
pmp
,
is_cmd
,
fis
);
ahci_fill_cmd_slot
(
pp
,
0
,
cmd_fis_len
|
flags
|
(
pmp
<<
12
));
libahci_debug_dump_region
(
ap
,
(
const
u32
*
)
fis
,
cmd_fis_len
,
"
\t
write H2D register FIS; dump: "
);
/* issue & wait */
writel
(
1
,
port_mmio
+
PORT_CMD_ISSUE
);
if
(
timeout_msec
)
{
tmp
=
ata_wait_register
(
ap
,
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
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
const
char
*
reason
=
NULL
;
unsigned
long
now
,
msecs
;
struct
ata_taskfile
tf
;
bool
fbs_disabled
=
false
;
int
rc
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u softreset"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
DPRINTK
(
"ENTER
\n
"
);
/* prepare for SRST (AHCI-1.1 10.4.1) */
rc
=
ahci_kick_engine
(
ap
);
if
(
rc
&&
rc
!=
-
EOPNOTSUPP
)
ata_link_warn
(
link
,
"failed to reset engine (errno=%d)
\n
"
,
rc
);
/*
* According to AHCI-1.2 9.3.9: if FBS is enable, software shall
* clear PxFBS.EN to '0' prior to issuing software reset to devices
* that is attached to port multiplier.
*/
if
(
!
ata_is_host_link
(
link
)
&&
pp
->
fbs_enabled
)
{
ahci_disable_fbs
(
ap
);
fbs_disabled
=
true
;
}
ata_tf_init
(
link
->
device
,
&
tf
);
/* issue the first D2H Register FIS */
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"issue the first H2D Register FIS; set ATA_SRST bit"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
msecs
=
0
;
now
=
jiffies
;
if
(
time_after
(
deadline
,
now
))
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 */
ata_msleep
(
ap
,
1
);
/* issue the second D2H Register FIS */
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"issue the second H2D Register FIS; reset ATA_SRST"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
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_info
(
link
,
"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
);
/* re-enable FBS if disabled before */
if
(
fbs_disabled
)
ahci_enable_fbs
(
ap
);
if
(
msg_str
!=
NULL
)
{
kfree
(
msg_str
);
}
DPRINTK
(
"EXIT, class=%u
\n
"
,
*
class
);
return
0
;
fail:
ata_link_err
(
link
,
"softreset failed (%s)
\n
"
,
reason
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
softreset failed, reson code: %s"
,
reason
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
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
;
int
len
;
char
*
msg_str
;
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"read port %u Task File Data: PxTFD = 0x%02x "
,
link
->
ap
->
port_no
,
status
);
libahci_debug_event
(
link
->
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
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_bad_pmp_check_ready
(
struct
ata_link
*
link
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
link
->
ap
);
u8
status
=
readl
(
port_mmio
+
PORT_TFDATA
)
&
0xFF
;
u32
irq_status
=
readl
(
port_mmio
+
PORT_IRQ_STAT
);
/*
* There is no need to check TFDATA if BAD PMP is found due to HW bug,
* which can save timeout delay.
*/
if
(
irq_status
&
PORT_IRQ_BAD_PMP
)
return
-
EIO
;
return
ata_check_ready
(
status
);
}
static
int
ahci_pmp_retry_softreset
(
struct
ata_link
*
link
,
unsigned
int
*
class
,
unsigned
long
deadline
)
{
struct
ata_port
*
ap
=
link
->
ap
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
int
pmp
=
sata_srst_pmp
(
link
);
int
rc
;
u32
irq_sts
;
DPRINTK
(
"ENTER
\n
"
);
rc
=
ahci_do_softreset
(
link
,
class
,
pmp
,
deadline
,
ahci_bad_pmp_check_ready
);
/*
* Soft reset fails with IPMS set when PMP is enabled but
* SATA HDD/ODD is connected to SATA port, do soft reset
* again to port 0.
*/
if
(
rc
==
-
EIO
)
{
irq_sts
=
readl
(
port_mmio
+
PORT_IRQ_STAT
);
if
(
irq_sts
&
PORT_IRQ_BAD_PMP
)
{
ata_link_warn
(
link
,
"applying PMP SRST workaround "
"and retrying
\n
"
);
rc
=
ahci_do_softreset
(
link
,
class
,
0
,
deadline
,
ahci_check_ready
);
}
}
return
rc
;
}
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
;
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
u8
*
d2h_fis
=
pp
->
rx_fis
+
RX_FIS_D2H_REG
;
struct
ata_taskfile
tf
;
bool
online
;
int
rc
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u hardreset"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
DPRINTK
(
"ENTER
\n
"
);
ahci_stop_engine
(
ap
);
/* clear D2H reception area to properly wait for D2H FIS */
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
clear RX FIS area"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
ata_tf_init
(
link
->
device
,
&
tf
);
tf
.
command
=
ATA_BUSY
;
ata_tf_to_fis
(
&
tf
,
0
,
0
,
d2h_fis
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
procced to sata_link_hardreset"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
rc
=
sata_link_hardreset
(
link
,
timing
,
deadline
,
&
online
,
ahci_check_ready
);
hpriv
->
start_engine
(
ap
);
if
(
online
)
{
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
link is online"
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
*
class
=
ahci_dev_classify
(
ap
);
}
if
(
msg_str
!=
NULL
)
{
kfree
(
msg_str
);
}
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u postreset actions"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
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
);
}
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
fill S/G list for port %u: %u PRD(s) written"
,
qc
->
ap
->
port_no
,
si
);
libahci_debug_event
(
qc
->
ap
,
msg_str
,
len
);
libahci_debug_dump_sg
(
qc
,
"reading data pointed by S/G list; dump: "
);
kfree
(
msg_str
);
}
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"process qc_defer ata command for port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"prepare command table information for port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/*
* 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
);
libahci_debug_dump_region
(
ap
,
(
const
u32
*
)(
cmd_tbl
+
AHCI_CMD_TBL_CDB
),
4
,
"
\t
this is ATAPI comman, dump ACMD region: "
);
}
libahci_debug_dump_region
(
ap
,
(
const
u32
*
)
cmd_tbl
,
cmd_fis_len
,
"
\t
write H2D register FIS; dump: "
);
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_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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
/* 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
))
{
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
;
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"handle port %u error"
,
link
->
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
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 10h.
*/
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
=
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_handle_port_interrupt
(
struct
ata_port
*
ap
,
void
__iomem
*
port_mmio
,
u32
status
)
{
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
qc_active
=
0
;
int
rc
;
int
len
;
char
*
msg
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u interrupt handler, PxIS: 0x%x"
,
ap
->
port_no
,
status
);
libahci_debug_event
(
ap
,
msg
,
len
);
libahci_debug_dump_irq
(
status
);
libahci_debug_irq_notify
(
ap
);
}
/* ignore BAD_PMP while resetting */
if
(
unlikely
(
resetting
))
status
&=
~
PORT_IRQ_BAD_PMP
;
/* if LPM is enabled, PHYRDY doesn't mean anything */
if
(
ap
->
link
.
lpm_policy
>
ATA_LPM_MAX_POWER
)
{
status
&=
~
PORT_IRQ_PHYRDY
;
ahci_scr_write
(
&
ap
->
link
,
SCR_ERROR
,
SERR_PHYRDY_CHG
);
if
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"resetting PORT_IRQ_PHYRDY, new PxIS: 0x%x"
,
status
);
libahci_debug_event
(
ap
,
msg
,
len
);
}
}
if
(
unlikely
(
status
&
PORT_IRQ_ERROR
))
{
if
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"processing PORT_IRQ_ERROR"
);
libahci_debug_event
(
ap
,
msg
,
len
);
}
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
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"processing PORT_IRQ_SDB_FIS, further processing will be done on sata layer"
);
libahci_debug_event
(
ap
,
msg
,
len
);
}
if
(
hpriv
->
cap
&
HOST_CAP_SNTF
)
{
if
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"host supports SNotification register, proceed to 'sata_async_notification'"
);
libahci_debug_event
(
ap
,
msg
,
len
);
}
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
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"host DOES NOT support SNotification register, snoop FIS RX area and proceed to 'sata_async_notification'"
);
libahci_debug_event
(
ap
,
msg
,
len
);
}
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
);
}
if
(
msg
!=
NULL
)
{
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"read port %u Serial ATA Active register, PxSACT: 0x%x"
,
ap
->
port_no
,
qc_active
);
libahci_debug_event
(
ap
,
msg
,
len
);
len
=
snprintf
(
msg
,
LIBAHCI_DEBUG_BUFSZ
,
"proceed to 'ata_qc_complete_multiple'"
);
libahci_debug_event
(
ap
,
msg
,
len
);
//libahci_debug_irq_notify(ap);
kfree
(
msg
);
}
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
);
}
}
static
void
ahci_port_intr
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
status
;
status
=
readl
(
port_mmio
+
PORT_IRQ_STAT
);
writel
(
status
,
port_mmio
+
PORT_IRQ_STAT
);
ahci_handle_port_interrupt
(
ap
,
port_mmio
,
status
);
}
static
irqreturn_t
ahci_port_thread_fn
(
int
irq
,
void
*
dev_instance
)
{
struct
ata_port
*
ap
=
dev_instance
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
status
;
status
=
atomic_xchg
(
&
pp
->
intr_status
,
0
);
if
(
!
status
)
return
IRQ_NONE
;
spin_lock_bh
(
ap
->
lock
);
ahci_handle_port_interrupt
(
ap
,
port_mmio
,
status
);
spin_unlock_bh
(
ap
->
lock
);
return
IRQ_HANDLED
;
}
static
irqreturn_t
ahci_multi_irqs_intr
(
int
irq
,
void
*
dev_instance
)
{
struct
ata_port
*
ap
=
dev_instance
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
u32
status
;
VPRINTK
(
"ENTER
\n
"
);
status
=
readl
(
port_mmio
+
PORT_IRQ_STAT
);
writel
(
status
,
port_mmio
+
PORT_IRQ_STAT
);
atomic_or
(
status
,
&
pp
->
intr_status
);
VPRINTK
(
"EXIT
\n
"
);
return
IRQ_WAKE_THREAD
;
}
static
irqreturn_t
ahci_single_irq_intr
(
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_warn
(
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
);
}
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u queued command issue"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
/* 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
)
{
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
write port %u register PxSACT, value: 0x%08x"
,
ap
->
port_no
,
(
1
<<
qc
->
tag
));
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
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
;
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
write port %u register PxFBS, value: 0x%08x"
,
ap
->
port_no
,
fbs
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
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
);
kfree
(
msg_str
);
return
0
;
}
EXPORT_SYMBOL_GPL
(
ahci_qc_issue
);
static
bool
ahci_qc_fill_rtf
(
struct
ata_queued_cmd
*
qc
)
{
struct
ahci_port_priv
*
pp
=
qc
->
ap
->
private_data
;
u8
*
rx_fis
=
pp
->
rx_fis
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"read port %u Received FIS and copy to Task File"
,
qc
->
ap
->
port_no
);
libahci_debug_event
(
qc
->
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
pp
->
fbs_enabled
)
rx_fis
+=
qc
->
dev
->
link
->
pmp
*
AHCI_RX_FIS_SZ
;
/*
* After a successful execution of an ATA PIO data-in command,
* the device doesn't send D2H Reg FIS to update the TF and
* the host should take TF and E_Status from the preceding PIO
* Setup FIS.
*/
if
(
qc
->
tf
.
protocol
==
ATA_PROT_PIO
&&
qc
->
dma_dir
==
DMA_FROM_DEVICE
&&
!
(
qc
->
flags
&
ATA_QCFLAG_FAILED
))
{
ata_tf_from_fis
(
rx_fis
+
RX_FIS_PIO_SETUP
,
&
qc
->
result_tf
);
qc
->
result_tf
.
command
=
(
rx_fis
+
RX_FIS_PIO_SETUP
)[
15
];
libahci_debug_dump_region
(
qc
->
ap
,
(
const
u32
*
)(
rx_fis
+
RX_FIS_PIO_SETUP
),
5
,
"
\t
read PIO Setup FIS; dump: "
);
libahci_debug_dump_sg
(
qc
,
"reading data pointed by S/G list; dump: "
);
}
else
{
ata_tf_from_fis
(
rx_fis
+
RX_FIS_D2H_REG
,
&
qc
->
result_tf
);
libahci_debug_dump_region
(
qc
->
ap
,
(
const
u32
*
)(
rx_fis
+
RX_FIS_D2H_REG
),
5
,
"
\t
read D2H Register FIS; dump: "
);
}
return
true
;
}
static
void
ahci_freeze
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"freeze port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
}
/* turn IRQ off */
writel
(
0
,
port_mmio
+
PORT_IRQ_MASK
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"
\t
write to port %u register PxIE, value: 0x0"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
}
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"thaw port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* 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
);
}
void
ahci_error_handler
(
struct
ata_port
*
ap
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u error handler"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
!
(
ap
->
pflags
&
ATA_PFLAG_FROZEN
))
{
/* restart engine */
ahci_stop_engine
(
ap
);
hpriv
->
start_engine
(
ap
);
}
sata_pmp_error_handler
(
ap
);
if
(
!
ata_dev_enabled
(
ap
->
link
.
device
))
ahci_stop_engine
(
ap
);
}
EXPORT_SYMBOL_GPL
(
ahci_error_handler
);
static
void
ahci_post_internal_cmd
(
struct
ata_queued_cmd
*
qc
)
{
struct
ata_port
*
ap
=
qc
->
ap
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"port %u post internal command"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* make DMA engine forget about the failed command */
if
(
qc
->
flags
&
ATA_QCFLAG_FAILED
)
ahci_kick_engine
(
ap
);
}
static
void
ahci_set_aggressive_devslp
(
struct
ata_port
*
ap
,
bool
sleep
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
struct
ata_device
*
dev
=
ap
->
link
.
device
;
u32
devslp
,
dm
,
dito
,
mdat
,
deto
;
int
rc
;
unsigned
int
err_mask
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"set aggressive port %u device sleep"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
devslp
=
readl
(
port_mmio
+
PORT_DEVSLP
);
if
(
!
(
devslp
&
PORT_DEVSLP_DSP
))
{
dev_info
(
ap
->
host
->
dev
,
"port does not support device sleep
\n
"
);
return
;
}
/* disable device sleep */
if
(
!
sleep
)
{
if
(
devslp
&
PORT_DEVSLP_ADSE
)
{
writel
(
devslp
&
~
PORT_DEVSLP_ADSE
,
port_mmio
+
PORT_DEVSLP
);
err_mask
=
ata_dev_set_feature
(
dev
,
SETFEATURES_SATA_DISABLE
,
SATA_DEVSLP
);
if
(
err_mask
&&
err_mask
!=
AC_ERR_DEV
)
ata_dev_warn
(
dev
,
"failed to disable DEVSLP
\n
"
);
}
return
;
}
/* device sleep was already enabled */
if
(
devslp
&
PORT_DEVSLP_ADSE
)
return
;
/* set DITO, MDAT, DETO and enable DevSlp, need to stop engine first */
rc
=
ahci_stop_engine
(
ap
);
if
(
rc
)
return
;
dm
=
(
devslp
&
PORT_DEVSLP_DM_MASK
)
>>
PORT_DEVSLP_DM_OFFSET
;
dito
=
devslp_idle_timeout
/
(
dm
+
1
);
if
(
dito
>
0x3ff
)
dito
=
0x3ff
;
/* Use the nominal value 10 ms if the read MDAT is zero,
* the nominal value of DETO is 20 ms.
*/
if
(
dev
->
devslp_timing
[
ATA_LOG_DEVSLP_VALID
]
&
ATA_LOG_DEVSLP_VALID_MASK
)
{
mdat
=
dev
->
devslp_timing
[
ATA_LOG_DEVSLP_MDAT
]
&
ATA_LOG_DEVSLP_MDAT_MASK
;
if
(
!
mdat
)
mdat
=
10
;
deto
=
dev
->
devslp_timing
[
ATA_LOG_DEVSLP_DETO
];
if
(
!
deto
)
deto
=
20
;
}
else
{
mdat
=
10
;
deto
=
20
;
}
devslp
|=
((
dito
<<
PORT_DEVSLP_DITO_OFFSET
)
|
(
mdat
<<
PORT_DEVSLP_MDAT_OFFSET
)
|
(
deto
<<
PORT_DEVSLP_DETO_OFFSET
)
|
PORT_DEVSLP_ADSE
);
writel
(
devslp
,
port_mmio
+
PORT_DEVSLP
);
hpriv
->
start_engine
(
ap
);
/* enable device sleep feature for the drive */
err_mask
=
ata_dev_set_feature
(
dev
,
SETFEATURES_SATA_ENABLE
,
SATA_DEVSLP
);
if
(
err_mask
&&
err_mask
!=
AC_ERR_DEV
)
ata_dev_warn
(
dev
,
"failed to enable DEVSLP
\n
"
);
}
static
void
ahci_enable_fbs
(
struct
ata_port
*
ap
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
fbs
;
int
rc
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"enable port %u FIS-based switching"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
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_info
(
ap
->
host
->
dev
,
"FBS is enabled
\n
"
);
pp
->
fbs_enabled
=
true
;
pp
->
fbs_last_dev
=
-
1
;
/* initialization */
}
else
dev_err
(
ap
->
host
->
dev
,
"Failed to enable FBS
\n
"
);
hpriv
->
start_engine
(
ap
);
}
static
void
ahci_disable_fbs
(
struct
ata_port
*
ap
)
{
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
u32
fbs
;
int
rc
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"disable port %u FIS-based switching"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
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_err
(
ap
->
host
->
dev
,
"Failed to disable FBS
\n
"
);
else
{
dev_info
(
ap
->
host
->
dev
,
"FBS is disabled
\n
"
);
pp
->
fbs_enabled
=
false
;
}
hpriv
->
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
;
/*
* We must not change the port interrupt mask register if the
* port is marked frozen, the value in pp->intr_mask will be
* restored later when the port is thawed.
*
* Note that during initialization, the port is marked as
* frozen since the irq handler is not yet registered.
*/
if
(
!
(
ap
->
pflags
&
ATA_PFLAG_FROZEN
))
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
;
/* see comment above in ahci_pmp_attach() */
if
(
!
(
ap
->
pflags
&
ATA_PFLAG_FROZEN
))
writel
(
pp
->
intr_mask
,
port_mmio
+
PORT_IRQ_MASK
);
}
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
;
}
EXPORT_SYMBOL_GPL
(
ahci_port_resume
);
#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_err
(
ap
,
"%s (%d)
\n
"
,
emsg
,
rc
);
ata_port_freeze
(
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"starting port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
pp
=
devm_kzalloc
(
dev
,
sizeof
(
*
pp
),
GFP_KERNEL
);
if
(
!
pp
)
return
-
ENOMEM
;
if
(
ap
->
host
->
n_ports
>
1
)
{
pp
->
irq_desc
=
devm_kzalloc
(
dev
,
8
,
GFP_KERNEL
);
if
(
!
pp
->
irq_desc
)
{
devm_kfree
(
dev
,
pp
);
return
-
ENOMEM
;
}
snprintf
(
pp
->
irq_desc
,
8
,
"%s%d"
,
dev_driver_string
(
dev
),
ap
->
port_no
);
}
/* 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
;
else
if
(
hpriv
->
flags
&
AHCI_HFLAG_YES_FBS
)
{
dev_info
(
dev
,
"port %d can do FBS, forcing FBSCP
\n
"
,
ap
->
port_no
);
pp
->
fbs_supported
=
true
;
}
else
dev_warn
(
dev
,
"port %d is not capable of FBS
\n
"
,
ap
->
port_no
);
}
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
;
/*
* Switch to per-port locking in case each port has its own MSI vector.
*/
if
((
hpriv
->
flags
&
AHCI_HFLAG_MULTI_MSI
))
{
spin_lock_init
(
&
pp
->
lock
);
ap
->
lock
=
&
pp
->
lock
;
}
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
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"stopping port %u"
,
ap
->
port_no
);
libahci_debug_event
(
ap
,
msg_str
,
len
);
kfree
(
msg_str
);
}
/* de-initialize port */
rc
=
ahci_deinit_port
(
ap
,
&
emsg
);
if
(
rc
)
ata_port_warn
(
ap
,
"%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%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_DESO
?
"deso "
:
""
,
cap2
&
HOST_CAP2_SADM
?
"sadm "
:
""
,
cap2
&
HOST_CAP2_SDS
?
"sds "
:
""
,
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
);
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"store EM registers"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
!
ahci_em_messages
||
!
(
hpriv
->
cap
&
HOST_CAP_EMS
))
return
;
messages
=
(
em_ctl
&
EM_CTRL_MSG_TYPE
)
>>
16
;
if
(
messages
)
{
/* store em_loc */
hpriv
->
em_loc
=
((
em_loc
>>
16
)
*
4
);
hpriv
->
em_buf_sz
=
((
em_loc
&
0xff
)
*
4
);
hpriv
->
em_msg_type
=
messages
;
pi
->
flags
|=
ATA_FLAG_EM
;
if
(
!
(
em_ctl
&
EM_CTL_ALHD
))
pi
->
flags
|=
ATA_FLAG_SW_ACTIVITY
;
}
}
EXPORT_SYMBOL_GPL
(
ahci_set_em_messages
);
static
int
ahci_host_activate_multi_irqs
(
struct
ata_host
*
host
,
int
irq
,
struct
scsi_host_template
*
sht
)
{
int
i
,
rc
;
rc
=
ata_host_start
(
host
);
if
(
rc
)
return
rc
;
for
(
i
=
0
;
i
<
host
->
n_ports
;
i
++
)
{
struct
ahci_port_priv
*
pp
=
host
->
ports
[
i
]
->
private_data
;
/* Do not receive interrupts sent by dummy ports */
if
(
!
pp
)
{
disable_irq
(
irq
+
i
);
continue
;
}
rc
=
devm_request_threaded_irq
(
host
->
dev
,
irq
+
i
,
ahci_multi_irqs_intr
,
ahci_port_thread_fn
,
IRQF_SHARED
,
pp
->
irq_desc
,
host
->
ports
[
i
]);
if
(
rc
)
goto
out_free_irqs
;
}
for
(
i
=
0
;
i
<
host
->
n_ports
;
i
++
)
ata_port_desc
(
host
->
ports
[
i
],
"irq %d"
,
irq
+
i
);
rc
=
ata_host_register
(
host
,
sht
);
if
(
rc
)
goto
out_free_all_irqs
;
return
0
;
out_free_all_irqs:
i
=
host
->
n_ports
;
out_free_irqs:
for
(
i
--
;
i
>=
0
;
i
--
)
devm_free_irq
(
host
->
dev
,
irq
+
i
,
host
->
ports
[
i
]);
return
rc
;
}
/**
* ahci_host_activate - start AHCI host, request IRQs and register it
* @host: target ATA host
* @irq: base IRQ number to request
* @sht: scsi_host_template to use when registering the host
*
* Similar to ata_host_activate, but requests IRQs according to AHCI-1.1
* when multiple MSIs were allocated. That is one MSI per port, starting
* from @irq.
*
* LOCKING:
* Inherited from calling layer (may sleep).
*
* RETURNS:
* 0 on success, -errno otherwise.
*/
int
ahci_host_activate
(
struct
ata_host
*
host
,
int
irq
,
struct
scsi_host_template
*
sht
)
{
struct
ahci_host_priv
*
hpriv
=
host
->
private_data
;
int
rc
;
int
len
;
char
*
msg_str
=
kzalloc
(
LIBAHCI_DEBUG_BUFSZ
,
GFP_KERNEL
);
if
(
msg_str
!=
NULL
)
{
len
=
snprintf
(
msg_str
,
LIBAHCI_DEBUG_BUFSZ
,
"activate AHCI host"
);
libahci_debug_event
(
NULL
,
msg_str
,
len
);
kfree
(
msg_str
);
}
if
(
hpriv
->
flags
&
AHCI_HFLAG_MULTI_MSI
)
rc
=
ahci_host_activate_multi_irqs
(
host
,
irq
,
sht
);
else
rc
=
ata_host_activate
(
host
,
irq
,
ahci_single_irq_intr
,
IRQF_SHARED
,
sht
);
return
rc
;
}
EXPORT_SYMBOL_GPL
(
ahci_host_activate
);
MODULE_AUTHOR
(
"Jeff Garzik"
);
MODULE_DESCRIPTION
(
"Common AHCI SATA low-level routines"
);
MODULE_LICENSE
(
"GPL"
);
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment