Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
linux
Commits
e2e875d9
Commit
e2e875d9
authored
Oct 19, 2004
by
Jeff Garzik
Browse files
Options
Browse Files
Download
Plain Diff
Merge pobox.com:/spare/repo/libata-dev/ahci
into pobox.com:/spare/repo/libata-2.6
parents
b72cb401
164446d7
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
1032 additions
and
0 deletions
+1032
-0
drivers/scsi/Kconfig
drivers/scsi/Kconfig
+8
-0
drivers/scsi/Makefile
drivers/scsi/Makefile
+1
-0
drivers/scsi/ahci.c
drivers/scsi/ahci.c
+1023
-0
No files found.
drivers/scsi/Kconfig
View file @
e2e875d9
...
...
@@ -406,6 +406,14 @@ config SCSI_SATA
If unsure, say N.
config SCSI_SATA_AHCI
tristate "AHCI SATA support"
depends on SCSI_SATA && PCI && EXPERIMENTAL
help
This option enables support for AHCI Serial ATA.
If unsure, say N.
config SCSI_SATA_SVW
tristate "ServerWorks Frodo / Apple K2 SATA support (EXPERIMENTAL)"
depends on SCSI_SATA && PCI && EXPERIMENTAL
...
...
drivers/scsi/Makefile
View file @
e2e875d9
...
...
@@ -121,6 +121,7 @@ obj-$(CONFIG_SCSI_LASI700) += 53c700.o lasi700.o
obj-$(CONFIG_SCSI_NSP32)
+=
nsp32.o
obj-$(CONFIG_SCSI_IPR)
+=
ipr.o
obj-$(CONFIG_SCSI_IBMVSCSI)
+=
ibmvscsi/
obj-$(CONFIG_SCSI_SATA_AHCI)
+=
libata.o ahci.o
obj-$(CONFIG_SCSI_SATA_SVW)
+=
libata.o sata_svw.o
obj-$(CONFIG_SCSI_ATA_PIIX)
+=
libata.o ata_piix.o
obj-$(CONFIG_SCSI_SATA_PROMISE)
+=
libata.o sata_promise.o
...
...
drivers/scsi/ahci.c
0 → 100644
View file @
e2e875d9
/*
* ahci.c - AHCI SATA support
*
* Copyright 2004 Red Hat, Inc.
*
* The contents of this file are subject to the Open
* Software License version 1.1 that can be found at
* http://www.opensource.org/licenses/osl-1.1.txt and is included herein
* by reference.
*
* Alternatively, the contents of this file may be used under the terms
* of the GNU General Public License version 2 (the "GPL") as distributed
* in the kernel source COPYING file, in which case the provisions of
* the GPL are applicable instead of the above. If you wish to allow
* the use of your version of this file only under the terms of the
* GPL and not to allow others to use your version of this file under
* the OSL, indicate your decision by deleting the provisions above and
* replace them with the notice and other provisions required by the GPL.
* If you do not delete the provisions above, a recipient may use your
* version of this file under either the OSL or the GPL.
*
* Version 1.0 of the AHCI specification:
* http://www.intel.com/technology/serialata/pdf/rev1_0.pdf
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include "scsi.h"
#include <scsi/scsi_host.h>
#include <linux/libata.h>
#include <asm/io.h>
#define DRV_NAME "ahci"
#define DRV_VERSION "0.11"
enum
{
AHCI_PCI_BAR
=
5
,
AHCI_MAX_SG
=
168
,
/* hardware max is 64K */
AHCI_DMA_BOUNDARY
=
0xffffffff
,
AHCI_USE_CLUSTERING
=
0
,
AHCI_CMD_SLOT_SZ
=
32
*
32
,
AHCI_RX_FIS_SZ
=
256
,
AHCI_CMD_TBL_HDR
=
0x80
,
AHCI_CMD_TBL_SZ
=
AHCI_CMD_TBL_HDR
+
(
AHCI_MAX_SG
*
16
),
AHCI_PORT_PRIV_DMA_SZ
=
AHCI_CMD_SLOT_SZ
+
AHCI_CMD_TBL_SZ
+
AHCI_RX_FIS_SZ
,
AHCI_IRQ_ON_SG
=
(
1
<<
31
),
AHCI_CMD_ATAPI
=
(
1
<<
5
),
AHCI_CMD_WRITE
=
(
1
<<
6
),
RX_FIS_D2H_REG
=
0x40
,
/* offset of D2H Register FIS data */
board_ahci
=
0
,
/* global controller registers */
HOST_CAP
=
0x00
,
/* host capabilities */
HOST_CTL
=
0x04
,
/* global host control */
HOST_IRQ_STAT
=
0x08
,
/* interrupt status */
HOST_PORTS_IMPL
=
0x0c
,
/* bitmap of implemented ports */
HOST_VERSION
=
0x10
,
/* AHCI spec. version compliancy */
/* HOST_CTL bits */
HOST_RESET
=
(
1
<<
0
),
/* reset controller; self-clear */
HOST_IRQ_EN
=
(
1
<<
1
),
/* global IRQ enable */
HOST_AHCI_EN
=
(
1
<<
31
),
/* AHCI enabled */
/* HOST_CAP bits */
HOST_CAP_64
=
(
1
<<
31
),
/* PCI DAC (64-bit DMA) support */
/* registers for each SATA port */
PORT_LST_ADDR
=
0x00
,
/* command list DMA addr */
PORT_LST_ADDR_HI
=
0x04
,
/* command list DMA addr hi */
PORT_FIS_ADDR
=
0x08
,
/* FIS rx buf addr */
PORT_FIS_ADDR_HI
=
0x0c
,
/* FIS rx buf addr hi */
PORT_IRQ_STAT
=
0x10
,
/* interrupt status */
PORT_IRQ_MASK
=
0x14
,
/* interrupt enable/disable mask */
PORT_CMD
=
0x18
,
/* port command */
PORT_TFDATA
=
0x20
,
/* taskfile data */
PORT_SIG
=
0x24
,
/* device TF signature */
PORT_CMD_ISSUE
=
0x38
,
/* command issue */
PORT_SCR
=
0x28
,
/* SATA phy register block */
PORT_SCR_STAT
=
0x28
,
/* SATA phy register: SStatus */
PORT_SCR_CTL
=
0x2c
,
/* SATA phy register: SControl */
PORT_SCR_ERR
=
0x30
,
/* SATA phy register: SError */
/* PORT_IRQ_{STAT,MASK} bits */
PORT_IRQ_COLD_PRES
=
(
1
<<
31
),
/* cold presence detect */
PORT_IRQ_TF_ERR
=
(
1
<<
30
),
/* task file error */
PORT_IRQ_HBUS_ERR
=
(
1
<<
29
),
/* host bus fatal error */
PORT_IRQ_HBUS_DATA_ERR
=
(
1
<<
28
),
/* host bus data error */
PORT_IRQ_IF_ERR
=
(
1
<<
27
),
/* interface fatal error */
PORT_IRQ_IF_NONFATAL
=
(
1
<<
26
),
/* interface non-fatal error */
PORT_IRQ_OVERFLOW
=
(
1
<<
24
),
/* xfer exhausted available S/G */
PORT_IRQ_BAD_PMP
=
(
1
<<
23
),
/* incorrect port multiplier */
PORT_IRQ_PHYRDY
=
(
1
<<
22
),
/* PhyRdy changed */
PORT_IRQ_DEV_ILCK
=
(
1
<<
7
),
/* device interlock */
PORT_IRQ_CONNECT
=
(
1
<<
6
),
/* port connect change status */
PORT_IRQ_SG_DONE
=
(
1
<<
5
),
/* descriptor processed */
PORT_IRQ_UNK_FIS
=
(
1
<<
4
),
/* unknown FIS rx'd */
PORT_IRQ_SDB_FIS
=
(
1
<<
3
),
/* Set Device Bits FIS rx'd */
PORT_IRQ_DMAS_FIS
=
(
1
<<
2
),
/* DMA Setup FIS rx'd */
PORT_IRQ_PIOS_FIS
=
(
1
<<
1
),
/* PIO Setup FIS rx'd */
PORT_IRQ_D2H_REG_FIS
=
(
1
<<
0
),
/* D2H Register FIS rx'd */
PORT_IRQ_FATAL
=
PORT_IRQ_TF_ERR
|
PORT_IRQ_HBUS_ERR
|
PORT_IRQ_HBUS_DATA_ERR
|
PORT_IRQ_IF_ERR
,
DEF_PORT_IRQ
=
PORT_IRQ_FATAL
|
PORT_IRQ_PHYRDY
|
PORT_IRQ_D2H_REG_FIS
,
/* PORT_CMD bits */
PORT_CMD_LIST_ON
=
(
1
<<
15
),
/* cmd list DMA engine running */
PORT_CMD_FIS_ON
=
(
1
<<
14
),
/* FIS DMA engine running */
PORT_CMD_FIS_RX
=
(
1
<<
4
),
/* Enable FIS receive DMA engine */
PORT_CMD_POWER_ON
=
(
1
<<
2
),
/* Power up device */
PORT_CMD_SPIN_UP
=
(
1
<<
1
),
/* Spin up device */
PORT_CMD_START
=
(
1
<<
0
),
/* Enable port DMA engine */
PORT_CMD_ICC_ACTIVE
=
(
0x1
<<
28
),
/* Put i/f in active state */
PORT_CMD_ICC_PARTIAL
=
(
0x2
<<
28
),
/* Put i/f in partial state */
PORT_CMD_ICC_SLUMBER
=
(
0x6
<<
28
),
/* Put i/f in slumber state */
};
struct
ahci_cmd_hdr
{
u32
opts
;
u32
status
;
u32
tbl_addr
;
u32
tbl_addr_hi
;
u32
reserved
[
4
];
};
struct
ahci_sg
{
u32
addr
;
u32
addr_hi
;
u32
reserved
;
u32
flags_size
;
};
struct
ahci_host_priv
{
unsigned
long
flags
;
u32
cap
;
/* cache of HOST_CAP register */
u32
port_map
;
/* cache of HOST_PORTS_IMPL reg */
};
struct
ahci_port_priv
{
struct
ahci_cmd_hdr
*
cmd_slot
;
dma_addr_t
cmd_slot_dma
;
void
*
cmd_tbl
;
dma_addr_t
cmd_tbl_dma
;
struct
ahci_sg
*
cmd_tbl_sg
;
void
*
rx_fis
;
dma_addr_t
rx_fis_dma
;
};
static
u32
ahci_scr_read
(
struct
ata_port
*
ap
,
unsigned
int
sc_reg
);
static
void
ahci_scr_write
(
struct
ata_port
*
ap
,
unsigned
int
sc_reg
,
u32
val
);
static
int
ahci_init_one
(
struct
pci_dev
*
pdev
,
const
struct
pci_device_id
*
ent
);
static
int
ahci_qc_issue
(
struct
ata_queued_cmd
*
qc
);
static
irqreturn_t
ahci_interrupt
(
int
irq
,
void
*
dev_instance
,
struct
pt_regs
*
regs
);
static
void
ahci_phy_reset
(
struct
ata_port
*
ap
);
static
void
ahci_irq_clear
(
struct
ata_port
*
ap
);
static
void
ahci_eng_timeout
(
struct
ata_port
*
ap
);
static
int
ahci_port_start
(
struct
ata_port
*
ap
);
static
void
ahci_port_stop
(
struct
ata_port
*
ap
);
static
void
ahci_host_stop
(
struct
ata_host_set
*
host_set
);
static
void
ahci_qc_prep
(
struct
ata_queued_cmd
*
qc
);
static
u8
ahci_check_status
(
struct
ata_port
*
ap
);
static
inline
int
ahci_host_intr
(
struct
ata_port
*
ap
,
struct
ata_queued_cmd
*
qc
);
static
Scsi_Host_Template
ahci_sht
=
{
.
module
=
THIS_MODULE
,
.
name
=
DRV_NAME
,
.
queuecommand
=
ata_scsi_queuecmd
,
.
eh_strategy_handler
=
ata_scsi_error
,
.
can_queue
=
ATA_DEF_QUEUE
,
.
this_id
=
ATA_SHT_THIS_ID
,
.
sg_tablesize
=
AHCI_MAX_SG
,
.
max_sectors
=
ATA_MAX_SECTORS
,
.
cmd_per_lun
=
ATA_SHT_CMD_PER_LUN
,
.
emulated
=
ATA_SHT_EMULATED
,
.
use_clustering
=
AHCI_USE_CLUSTERING
,
.
proc_name
=
DRV_NAME
,
.
dma_boundary
=
AHCI_DMA_BOUNDARY
,
.
slave_configure
=
ata_scsi_slave_config
,
.
bios_param
=
ata_std_bios_param
,
};
static
struct
ata_port_operations
ahci_ops
=
{
.
port_disable
=
ata_port_disable
,
.
check_status
=
ahci_check_status
,
.
dev_select
=
ata_noop_dev_select
,
.
phy_reset
=
ahci_phy_reset
,
.
qc_prep
=
ahci_qc_prep
,
.
qc_issue
=
ahci_qc_issue
,
.
eng_timeout
=
ahci_eng_timeout
,
.
irq_handler
=
ahci_interrupt
,
.
irq_clear
=
ahci_irq_clear
,
.
scr_read
=
ahci_scr_read
,
.
scr_write
=
ahci_scr_write
,
.
port_start
=
ahci_port_start
,
.
port_stop
=
ahci_port_stop
,
.
host_stop
=
ahci_host_stop
,
};
static
struct
ata_port_info
ahci_port_info
[]
=
{
/* board_ahci */
{
.
sht
=
&
ahci_sht
,
.
host_flags
=
ATA_FLAG_SATA
|
ATA_FLAG_NO_LEGACY
|
ATA_FLAG_SATA_RESET
|
ATA_FLAG_MMIO
,
.
pio_mask
=
0x03
,
/* pio3-4 */
.
udma_mask
=
0x7f
,
/* udma0-6 ; FIXME */
.
port_ops
=
&
ahci_ops
,
},
};
static
struct
pci_device_id
ahci_pci_tbl
[]
=
{
{
PCI_VENDOR_ID_INTEL
,
0x2652
,
PCI_ANY_ID
,
PCI_ANY_ID
,
0
,
0
,
board_ahci
},
{
PCI_VENDOR_ID_INTEL
,
0x2653
,
PCI_ANY_ID
,
PCI_ANY_ID
,
0
,
0
,
board_ahci
},
{
}
/* terminate list */
};
static
struct
pci_driver
ahci_pci_driver
=
{
.
name
=
DRV_NAME
,
.
id_table
=
ahci_pci_tbl
,
.
probe
=
ahci_init_one
,
.
remove
=
ata_pci_remove_one
,
};
static
inline
unsigned
long
ahci_port_base_ul
(
unsigned
long
base
,
unsigned
int
port
)
{
return
base
+
0x100
+
(
port
*
0x80
);
}
static
inline
void
*
ahci_port_base
(
void
*
base
,
unsigned
int
port
)
{
return
(
void
*
)
ahci_port_base_ul
((
unsigned
long
)
base
,
port
);
}
static
void
ahci_host_stop
(
struct
ata_host_set
*
host_set
)
{
struct
ahci_host_priv
*
hpriv
=
host_set
->
private_data
;
kfree
(
hpriv
);
}
static
int
ahci_port_start
(
struct
ata_port
*
ap
)
{
struct
pci_dev
*
pdev
=
ap
->
host_set
->
pdev
;
struct
ahci_host_priv
*
hpriv
=
ap
->
host_set
->
private_data
;
struct
ahci_port_priv
*
pp
;
int
rc
;
void
*
mem
,
*
mmio
=
ap
->
host_set
->
mmio_base
;
void
*
port_mmio
=
ahci_port_base
(
mmio
,
ap
->
port_no
);
dma_addr_t
mem_dma
;
rc
=
ata_port_start
(
ap
);
if
(
rc
)
return
rc
;
pp
=
kmalloc
(
sizeof
(
*
pp
),
GFP_KERNEL
);
if
(
!
pp
)
{
rc
=
-
ENOMEM
;
goto
err_out
;
}
memset
(
pp
,
0
,
sizeof
(
*
pp
));
mem
=
pci_alloc_consistent
(
pdev
,
AHCI_PORT_PRIV_DMA_SZ
,
&
mem_dma
);
if
(
!
mem
)
{
rc
=
-
ENOMEM
;
goto
err_out_kfree
;
}
memset
(
mem
,
0
,
AHCI_PORT_PRIV_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
+=
AHCI_RX_FIS_SZ
;
mem_dma
+=
AHCI_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
;
pp
->
cmd_tbl_sg
=
mem
+
AHCI_CMD_TBL_HDR
;
ap
->
private_data
=
pp
;
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
);
readl
(
port_mmio
+
PORT_LST_ADDR
);
/* flush */
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_LST_ADDR
);
readl
(
port_mmio
+
PORT_LST_ADDR
);
/* flush */
writel
(
PORT_CMD_ICC_ACTIVE
|
PORT_CMD_FIS_RX
|
PORT_CMD_POWER_ON
|
PORT_CMD_SPIN_UP
|
PORT_CMD_START
,
port_mmio
+
PORT_CMD
);
readl
(
port_mmio
+
PORT_CMD
);
/* flush */
return
0
;
err_out_kfree:
kfree
(
pp
);
err_out:
ata_port_stop
(
ap
);
return
rc
;
}
static
void
ahci_port_stop
(
struct
ata_port
*
ap
)
{
struct
pci_dev
*
pdev
=
ap
->
host_set
->
pdev
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
void
*
mmio
=
ap
->
host_set
->
mmio_base
;
void
*
port_mmio
=
ahci_port_base
(
mmio
,
ap
->
port_no
);
u32
tmp
;
tmp
=
readl
(
port_mmio
+
PORT_CMD
);
tmp
&=
~
(
PORT_CMD_START
|
PORT_CMD_FIS_RX
);
writel
(
tmp
,
port_mmio
+
PORT_CMD
);
readl
(
port_mmio
+
PORT_CMD
);
/* flush */
/* spec says 500 msecs for each PORT_CMD_{START,FIS_RX} bit, so
* this is slightly incorrect.
*/
set_current_state
(
TASK_UNINTERRUPTIBLE
);
schedule_timeout
((
HZ
/
2
)
+
1
);
ap
->
private_data
=
NULL
;
pci_free_consistent
(
pdev
,
AHCI_PORT_PRIV_DMA_SZ
,
pp
->
cmd_slot
,
pp
->
cmd_slot_dma
);
kfree
(
pp
);
ata_port_stop
(
ap
);
}
static
u32
ahci_scr_read
(
struct
ata_port
*
ap
,
unsigned
int
sc_reg_in
)
{
unsigned
int
sc_reg
;
switch
(
sc_reg_in
)
{
case
SCR_STATUS
:
sc_reg
=
0
;
break
;
case
SCR_CONTROL
:
sc_reg
=
1
;
break
;
case
SCR_ERROR
:
sc_reg
=
2
;
break
;
case
SCR_ACTIVE
:
sc_reg
=
3
;
break
;
default:
return
0xffffffffU
;
}
return
readl
((
void
*
)
ap
->
ioaddr
.
scr_addr
+
(
sc_reg
*
4
));
}
static
void
ahci_scr_write
(
struct
ata_port
*
ap
,
unsigned
int
sc_reg_in
,
u32
val
)
{
unsigned
int
sc_reg
;
switch
(
sc_reg_in
)
{
case
SCR_STATUS
:
sc_reg
=
0
;
break
;
case
SCR_CONTROL
:
sc_reg
=
1
;
break
;
case
SCR_ERROR
:
sc_reg
=
2
;
break
;
case
SCR_ACTIVE
:
sc_reg
=
3
;
break
;
default:
return
;
}
writel
(
val
,
(
void
*
)
ap
->
ioaddr
.
scr_addr
+
(
sc_reg
*
4
));
}
static
void
ahci_phy_reset
(
struct
ata_port
*
ap
)
{
void
__iomem
*
port_mmio
=
(
void
__iomem
*
)
ap
->
ioaddr
.
cmd_addr
;
struct
ata_taskfile
tf
;
struct
ata_device
*
dev
=
&
ap
->
device
[
0
];
u32
tmp
;
__sata_phy_reset
(
ap
);
if
(
ap
->
flags
&
ATA_FLAG_PORT_DISABLED
)
return
;
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
;
dev
->
class
=
ata_dev_classify
(
&
tf
);
if
(
!
ata_dev_present
(
dev
))
ata_port_disable
(
ap
);
}
static
u8
ahci_check_status
(
struct
ata_port
*
ap
)
{
void
*
mmio
=
(
void
*
)
ap
->
ioaddr
.
cmd_addr
;
return
readl
(
mmio
+
PORT_TFDATA
)
&
0xFF
;
}
static
void
ahci_fill_sg
(
struct
ata_queued_cmd
*
qc
)
{
struct
ahci_port_priv
*
pp
=
qc
->
ap
->
private_data
;
unsigned
int
i
;
VPRINTK
(
"ENTER
\n
"
);
/*
* Next, the S/G list.
*/
for
(
i
=
0
;
i
<
qc
->
n_elem
;
i
++
)
{
u32
sg_len
;
dma_addr_t
addr
;
addr
=
sg_dma_address
(
&
qc
->
sg
[
i
]);
sg_len
=
sg_dma_len
(
&
qc
->
sg
[
i
]);
pp
->
cmd_tbl_sg
[
i
].
addr
=
cpu_to_le32
(
addr
&
0xffffffff
);
pp
->
cmd_tbl_sg
[
i
].
addr_hi
=
cpu_to_le32
((
addr
>>
16
)
>>
16
);
pp
->
cmd_tbl_sg
[
i
].
flags_size
=
cpu_to_le32
(
sg_len
-
1
);
}
}
static
void
ahci_qc_prep
(
struct
ata_queued_cmd
*
qc
)
{
struct
ahci_port_priv
*
pp
=
qc
->
ap
->
private_data
;
u32
opts
;
const
u32
cmd_fis_len
=
5
;
/* five dwords */
/*
* Fill in command slot information (currently only one slot,
* slot 0, is currently since we don't do queueing)
*/
opts
=
(
qc
->
n_elem
<<
16
)
|
cmd_fis_len
;
if
(
qc
->
tf
.
flags
&
ATA_TFLAG_WRITE
)
opts
|=
AHCI_CMD_WRITE
;
switch
(
qc
->
tf
.
protocol
)
{
case
ATA_PROT_ATAPI
:
case
ATA_PROT_ATAPI_NODATA
:
case
ATA_PROT_ATAPI_DMA
:
opts
|=
AHCI_CMD_ATAPI
;
break
;
default:
/* do nothing */
break
;
}
pp
->
cmd_slot
[
0
].
opts
=
cpu_to_le32
(
opts
);
pp
->
cmd_slot
[
0
].
status
=
0
;
pp
->
cmd_slot
[
0
].
tbl_addr
=
cpu_to_le32
(
pp
->
cmd_tbl_dma
&
0xffffffff
);
pp
->
cmd_slot
[
0
].
tbl_addr_hi
=
cpu_to_le32
((
pp
->
cmd_tbl_dma
>>
16
)
>>
16
);
/*
* Fill in command table information. First, the header,
* a SATA Register - Host to Device command FIS.
*/
ata_tf_to_fis
(
&
qc
->
tf
,
pp
->
cmd_tbl
,
0
);
if
(
!
(
qc
->
flags
&
ATA_QCFLAG_DMAMAP
))
return
;
ahci_fill_sg
(
qc
);
}
static
inline
void
ahci_dma_complete
(
struct
ata_port
*
ap
,
struct
ata_queued_cmd
*
qc
,
int
have_err
)
{
/* get drive status; clear intr; complete txn */
ata_qc_complete
(
ata_qc_from_tag
(
ap
,
ap
->
active_tag
),
have_err
?
ATA_ERR
:
0
);
}
static
void
ahci_intr_error
(
struct
ata_port
*
ap
,
u32
irq_stat
)
{
void
*
mmio
=
ap
->
host_set
->
mmio_base
;
void
*
port_mmio
=
ahci_port_base
(
mmio
,
ap
->
port_no
);
u32
tmp
;
int
work
;
/* stop DMA */
tmp
=
readl
(
port_mmio
+
PORT_CMD
);
tmp
&=
PORT_CMD_START
|
PORT_CMD_FIS_RX
;
writel
(
tmp
,
port_mmio
+
PORT_CMD
);
/* wait for engine to stop. TODO: this could be
* as long as 500 msec
*/
work
=
1000
;
while
(
work
--
>
0
)
{
tmp
=
readl
(
port_mmio
+
PORT_CMD
);
if
((
tmp
&
PORT_CMD_LIST_ON
)
==
0
)
break
;
udelay
(
10
);
}
/* clear SATA phy error, if any */
tmp
=
readl
(
port_mmio
+
PORT_SCR_ERR
);
writel
(
tmp
,
port_mmio
+
PORT_SCR_ERR
);
/* if DRQ/BSY is set, device needs to be reset.
* if so, issue COMRESET
*/
tmp
=
readl
(
port_mmio
+
PORT_TFDATA
);
if
(
tmp
&
(
ATA_BUSY
|
ATA_DRQ
))
{
writel
(
0x301
,
port_mmio
+
PORT_SCR_CTL
);
readl
(
port_mmio
+
PORT_SCR_CTL
);
/* flush */
udelay
(
10
);
writel
(
0x300
,
port_mmio
+
PORT_SCR_CTL
);
readl
(
port_mmio
+
PORT_SCR_CTL
);
/* flush */
}
/* re-start DMA */
tmp
=
readl
(
port_mmio
+
PORT_CMD
);
tmp
|=
PORT_CMD_START
|
PORT_CMD_FIS_RX
;
writel
(
tmp
,
port_mmio
+
PORT_CMD
);
readl
(
port_mmio
+
PORT_CMD
);
/* flush */
printk
(
KERN_WARNING
"ata%u: error occurred, port reset
\n
"
,
ap
->
port_no
);
}
static
void
ahci_eng_timeout
(
struct
ata_port
*
ap
)
{
void
*
mmio
=
ap
->
host_set
->
mmio_base
;
void
*
port_mmio
=
ahci_port_base
(
mmio
,
ap
->
port_no
);
struct
ata_queued_cmd
*
qc
;
DPRINTK
(
"ENTER
\n
"
);
ahci_intr_error
(
ap
,
readl
(
port_mmio
+
PORT_IRQ_STAT
));
qc
=
ata_qc_from_tag
(
ap
,
ap
->
active_tag
);
if
(
!
qc
)
{
printk
(
KERN_ERR
"ata%u: BUG: timeout without command
\n
"
,
ap
->
id
);
}
else
{
/* hack alert! We cannot use the supplied completion
* function from inside the ->eh_strategy_handler() thread.
* libata is the only user of ->eh_strategy_handler() in
* any kernel, so the default scsi_done() assumes it is
* not being called from the SCSI EH.
*/
qc
->
scsidone
=
scsi_finish_command
;
ata_qc_complete
(
qc
,
ATA_ERR
);
}
}
static
inline
int
ahci_host_intr
(
struct
ata_port
*
ap
,
struct
ata_queued_cmd
*
qc
)
{
void
*
mmio
=
ap
->
host_set
->
mmio_base
;
void
*
port_mmio
=
ahci_port_base
(
mmio
,
ap
->
port_no
);
u32
status
,
serr
,
ci
;
serr
=
readl
(
port_mmio
+
PORT_SCR_ERR
);
writel
(
serr
,
port_mmio
+
PORT_SCR_ERR
);
status
=
readl
(
port_mmio
+
PORT_IRQ_STAT
);
writel
(
status
,
port_mmio
+
PORT_IRQ_STAT
);
ci
=
readl
(
port_mmio
+
PORT_CMD_ISSUE
);
if
(
likely
((
ci
&
0x1
)
==
0
))
{
if
(
qc
)
{
ata_qc_complete
(
qc
,
0
);
qc
=
NULL
;
}
}
if
(
status
&
PORT_IRQ_FATAL
)
{
ahci_intr_error
(
ap
,
status
);
if
(
qc
)
ata_qc_complete
(
qc
,
ATA_ERR
);
}
return
1
;
}
static
void
ahci_irq_clear
(
struct
ata_port
*
ap
)
{
/* TODO */
}
static
irqreturn_t
ahci_interrupt
(
int
irq
,
void
*
dev_instance
,
struct
pt_regs
*
regs
)
{
struct
ata_host_set
*
host_set
=
dev_instance
;
struct
ahci_host_priv
*
hpriv
;
unsigned
int
i
,
handled
=
0
;
void
*
mmio
;
u32
irq_stat
,
irq_ack
=
0
;
VPRINTK
(
"ENTER
\n
"
);
hpriv
=
host_set
->
private_data
;
mmio
=
host_set
->
mmio_base
;
/* sigh. 0xffffffff is a valid return from h/w */
irq_stat
=
readl
(
mmio
+
HOST_IRQ_STAT
);
irq_stat
&=
hpriv
->
port_map
;
if
(
!
irq_stat
)
return
IRQ_NONE
;
spin_lock
(
&
host_set
->
lock
);
for
(
i
=
0
;
i
<
host_set
->
n_ports
;
i
++
)
{
struct
ata_port
*
ap
;
u32
tmp
;
VPRINTK
(
"port %u
\n
"
,
i
);
ap
=
host_set
->
ports
[
i
];
tmp
=
irq_stat
&
(
1
<<
i
);
if
(
tmp
&&
ap
)
{
struct
ata_queued_cmd
*
qc
;
qc
=
ata_qc_from_tag
(
ap
,
ap
->
active_tag
);
if
(
ahci_host_intr
(
ap
,
qc
))
irq_ack
|=
(
1
<<
i
);
}
}
if
(
irq_ack
)
{
writel
(
irq_ack
,
mmio
+
HOST_IRQ_STAT
);
handled
=
1
;
}
spin_unlock
(
&
host_set
->
lock
);
VPRINTK
(
"EXIT
\n
"
);
return
IRQ_RETVAL
(
handled
);
}
static
int
ahci_qc_issue
(
struct
ata_queued_cmd
*
qc
)
{
struct
ata_port
*
ap
=
qc
->
ap
;
void
*
mmio
=
(
void
*
)
ap
->
ioaddr
.
cmd_addr
;
writel
(
1
,
mmio
+
PORT_CMD_ISSUE
);
readl
(
mmio
+
PORT_CMD_ISSUE
);
/* flush */
return
0
;
}
static
void
ahci_setup_port
(
struct
ata_ioports
*
port
,
unsigned
long
base
,
unsigned
int
port_idx
)
{
VPRINTK
(
"ENTER, base==0x%lx, port_idx %u
\n
"
,
base
,
port_idx
);
base
=
ahci_port_base_ul
(
base
,
port_idx
);
VPRINTK
(
"base now==0x%lx
\n
"
,
base
);
port
->
cmd_addr
=
base
;
port
->
scr_addr
=
base
+
PORT_SCR
;
VPRINTK
(
"EXIT
\n
"
);
}
static
int
ahci_host_init
(
struct
ata_probe_ent
*
probe_ent
)
{
struct
ahci_host_priv
*
hpriv
=
probe_ent
->
private_data
;
struct
pci_dev
*
pdev
=
probe_ent
->
pdev
;
void
__iomem
*
mmio
=
probe_ent
->
mmio_base
;
u32
tmp
,
cap_save
;
u16
tmp16
;
unsigned
int
i
,
j
,
using_dac
;
int
rc
;
void
__iomem
*
port_mmio
;
cap_save
=
readl
(
mmio
+
HOST_CAP
);
cap_save
&=
(
(
1
<<
28
)
|
(
1
<<
17
)
);
cap_save
|=
(
1
<<
27
);
/* global controller reset */
tmp
=
readl
(
mmio
+
HOST_CTL
);
if
((
tmp
&
HOST_RESET
)
==
0
)
{
writel
(
tmp
|
HOST_RESET
,
mmio
+
HOST_CTL
);
readl
(
mmio
+
HOST_CTL
);
/* flush */
}
/* reset must complete within 1 second, or
* the hardware should be considered fried.
*/
set_current_state
(
TASK_UNINTERRUPTIBLE
);
schedule_timeout
(
HZ
+
1
);
tmp
=
readl
(
mmio
+
HOST_CTL
);
if
(
tmp
&
HOST_RESET
)
{
printk
(
KERN_ERR
DRV_NAME
"(%s): controller reset failed (0x%x)
\n
"
,
pci_name
(
pdev
),
tmp
);
return
-
EIO
;
}
writel
(
HOST_AHCI_EN
,
mmio
+
HOST_CTL
);
(
void
)
readl
(
mmio
+
HOST_CTL
);
/* flush */
writel
(
cap_save
,
mmio
+
HOST_CAP
);
writel
(
0xf
,
mmio
+
HOST_PORTS_IMPL
);
(
void
)
readl
(
mmio
+
HOST_PORTS_IMPL
);
/* flush */
pci_read_config_word
(
pdev
,
0x92
,
&
tmp16
);
tmp16
|=
0xf
;
pci_write_config_word
(
pdev
,
0x92
,
tmp16
);
hpriv
->
cap
=
readl
(
mmio
+
HOST_CAP
);
hpriv
->
port_map
=
readl
(
mmio
+
HOST_PORTS_IMPL
);
probe_ent
->
n_ports
=
(
hpriv
->
cap
&
0x1f
)
+
1
;
VPRINTK
(
"cap 0x%x port_map 0x%x n_ports %d
\n
"
,
hpriv
->
cap
,
hpriv
->
port_map
,
probe_ent
->
n_ports
);
using_dac
=
hpriv
->
cap
&
HOST_CAP_64
;
if
(
using_dac
&&
!
pci_set_dma_mask
(
pdev
,
0xffffffffffffffffULL
))
{
rc
=
pci_set_consistent_dma_mask
(
pdev
,
0xffffffffffffffffULL
);
if
(
rc
)
{
rc
=
pci_set_consistent_dma_mask
(
pdev
,
0xffffffffULL
);
if
(
rc
)
{
printk
(
KERN_ERR
DRV_NAME
"(%s): 64-bit DMA enable failed
\n
"
,
pci_name
(
pdev
));
return
rc
;
}
}
hpriv
->
flags
|=
HOST_CAP_64
;
}
else
{
rc
=
pci_set_dma_mask
(
pdev
,
0xffffffffULL
);
if
(
rc
)
{
printk
(
KERN_ERR
DRV_NAME
"(%s): 32-bit DMA enable failed
\n
"
,
pci_name
(
pdev
));
return
rc
;
}
rc
=
pci_set_consistent_dma_mask
(
pdev
,
0xffffffffULL
);
if
(
rc
)
{
printk
(
KERN_ERR
DRV_NAME
"(%s): 32-bit consistent DMA enable failed
\n
"
,
pci_name
(
pdev
));
return
rc
;
}
}
for
(
i
=
0
;
i
<
probe_ent
->
n_ports
;
i
++
)
{
if
(
!
(
hpriv
->
port_map
&
(
1
<<
i
)))
continue
;
port_mmio
=
ahci_port_base
(
mmio
,
i
);
VPRINTK
(
"mmio %p port_mmio %p
\n
"
,
mmio
,
port_mmio
);
ahci_setup_port
(
&
probe_ent
->
port
[
i
],
(
unsigned
long
)
mmio
,
i
);
/* make sure port is not active */
tmp
=
readl
(
port_mmio
+
PORT_CMD
);
VPRINTK
(
"PORT_CMD 0x%x
\n
"
,
tmp
);
if
(
tmp
&
(
PORT_CMD_LIST_ON
|
PORT_CMD_FIS_ON
|
PORT_CMD_FIS_RX
|
PORT_CMD_START
))
{
tmp
&=
~
(
PORT_CMD_LIST_ON
|
PORT_CMD_FIS_ON
|
PORT_CMD_FIS_RX
|
PORT_CMD_START
);
writel
(
tmp
,
port_mmio
+
PORT_CMD
);
readl
(
port_mmio
+
PORT_CMD
);
/* flush */
/* spec says 500 msecs for each bit, so
* this is slightly incorrect.
*/
set_current_state
(
TASK_UNINTERRUPTIBLE
);
schedule_timeout
((
HZ
/
2
)
+
1
);
}
writel
(
PORT_CMD_SPIN_UP
,
port_mmio
+
PORT_CMD
);
j
=
0
;
while
(
j
<
100
)
{
msleep
(
10
);
tmp
=
readl
(
port_mmio
+
PORT_SCR_STAT
);
if
((
tmp
&
0xf
)
==
0x3
)
break
;
j
++
;
}
tmp
=
readl
(
port_mmio
+
PORT_SCR_ERR
);
VPRINTK
(
"PORT_SCR_ERR 0x%x
\n
"
,
tmp
);
writel
(
tmp
,
port_mmio
+
PORT_SCR_ERR
);
/* ack any pending irq events for this port */
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
<<
i
,
mmio
+
HOST_IRQ_STAT
);
/* set irq mask (enables interrupts) */
writel
(
DEF_PORT_IRQ
,
port_mmio
+
PORT_IRQ_MASK
);
}
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
);
pci_set_master
(
pdev
);
return
0
;
}
/* move to PCI layer, integrate w/ MSI stuff */
static
void
pci_enable_intx
(
struct
pci_dev
*
pdev
)
{
u16
pci_command
;
pci_read_config_word
(
pdev
,
PCI_COMMAND
,
&
pci_command
);
if
(
pci_command
&
PCI_COMMAND_INTX_DISABLE
)
{
pci_command
&=
~
PCI_COMMAND_INTX_DISABLE
;
pci_write_config_word
(
pdev
,
PCI_COMMAND
,
pci_command
);
}
}
static
void
ahci_print_info
(
struct
ata_probe_ent
*
probe_ent
)
{
struct
ahci_host_priv
*
hpriv
=
probe_ent
->
private_data
;
struct
pci_dev
*
pdev
=
probe_ent
->
pdev
;
void
*
mmio
=
probe_ent
->
mmio_base
;
u32
vers
,
cap
,
impl
,
speed
;
const
char
*
speed_s
;
vers
=
readl
(
mmio
+
HOST_VERSION
);
cap
=
hpriv
->
cap
;
impl
=
hpriv
->
port_map
;
speed
=
(
cap
>>
20
)
&
0xf
;
if
(
speed
==
1
)
speed_s
=
"1.5"
;
else
if
(
speed
==
2
)
speed_s
=
"3"
;
else
speed_s
=
"?"
;
printk
(
KERN_INFO
DRV_NAME
"(%s) AHCI %02x%02x.%02x%02x "
"%u slots %u ports %s Gbps 0x%x impl
\n
"
,
pci_name
(
pdev
),
(
vers
>>
24
)
&
0xff
,
(
vers
>>
16
)
&
0xff
,
(
vers
>>
8
)
&
0xff
,
vers
&
0xff
,
((
cap
>>
8
)
&
0x1f
)
+
1
,
(
cap
&
0x1f
)
+
1
,
speed_s
,
impl
);
printk
(
KERN_INFO
DRV_NAME
"(%s) flags: "
"%s%s%s%s%s%s"
"%s%s%s%s%s%s%s
\n
"
,
pci_name
(
pdev
),
cap
&
(
1
<<
31
)
?
"64bit "
:
""
,
cap
&
(
1
<<
30
)
?
"ncq "
:
""
,
cap
&
(
1
<<
28
)
?
"ilck "
:
""
,
cap
&
(
1
<<
27
)
?
"stag "
:
""
,
cap
&
(
1
<<
26
)
?
"pm "
:
""
,
cap
&
(
1
<<
25
)
?
"led "
:
""
,
cap
&
(
1
<<
24
)
?
"clo "
:
""
,
cap
&
(
1
<<
19
)
?
"nz "
:
""
,
cap
&
(
1
<<
18
)
?
"only "
:
""
,
cap
&
(
1
<<
17
)
?
"pmp "
:
""
,
cap
&
(
1
<<
15
)
?
"pio "
:
""
,
cap
&
(
1
<<
14
)
?
"slum "
:
""
,
cap
&
(
1
<<
13
)
?
"part "
:
""
);
}
static
int
ahci_init_one
(
struct
pci_dev
*
pdev
,
const
struct
pci_device_id
*
ent
)
{
static
int
printed_version
;
struct
ata_probe_ent
*
probe_ent
=
NULL
;
struct
ahci_host_priv
*
hpriv
;
unsigned
long
base
;
void
*
mmio_base
;
unsigned
int
board_idx
=
(
unsigned
int
)
ent
->
driver_data
;
int
rc
;
VPRINTK
(
"ENTER
\n
"
);
if
(
!
printed_version
++
)
printk
(
KERN_DEBUG
DRV_NAME
" version "
DRV_VERSION
"
\n
"
);
/*
* If this driver happens to only be useful on Apple's K2, then
* we should check that here as it has a normal Serverworks ID
*/
rc
=
pci_enable_device
(
pdev
);
if
(
rc
)
return
rc
;
rc
=
pci_request_regions
(
pdev
,
DRV_NAME
);
if
(
rc
)
goto
err_out
;
pci_enable_intx
(
pdev
);
probe_ent
=
kmalloc
(
sizeof
(
*
probe_ent
),
GFP_KERNEL
);
if
(
probe_ent
==
NULL
)
{
rc
=
-
ENOMEM
;
goto
err_out_regions
;
}
memset
(
probe_ent
,
0
,
sizeof
(
*
probe_ent
));
probe_ent
->
pdev
=
pdev
;
INIT_LIST_HEAD
(
&
probe_ent
->
node
);
mmio_base
=
ioremap
(
pci_resource_start
(
pdev
,
AHCI_PCI_BAR
),
pci_resource_len
(
pdev
,
AHCI_PCI_BAR
));
if
(
mmio_base
==
NULL
)
{
rc
=
-
ENOMEM
;
goto
err_out_free_ent
;
}
base
=
(
unsigned
long
)
mmio_base
;
hpriv
=
kmalloc
(
sizeof
(
*
hpriv
),
GFP_KERNEL
);
if
(
!
hpriv
)
{
rc
=
-
ENOMEM
;
goto
err_out_iounmap
;
}
memset
(
hpriv
,
0
,
sizeof
(
*
hpriv
));
probe_ent
->
sht
=
ahci_port_info
[
board_idx
].
sht
;
probe_ent
->
host_flags
=
ahci_port_info
[
board_idx
].
host_flags
;
probe_ent
->
pio_mask
=
ahci_port_info
[
board_idx
].
pio_mask
;
probe_ent
->
udma_mask
=
ahci_port_info
[
board_idx
].
udma_mask
;
probe_ent
->
port_ops
=
ahci_port_info
[
board_idx
].
port_ops
;
probe_ent
->
irq
=
pdev
->
irq
;
probe_ent
->
irq_flags
=
SA_SHIRQ
;
probe_ent
->
mmio_base
=
mmio_base
;
probe_ent
->
private_data
=
hpriv
;
/* initialize adapter */
rc
=
ahci_host_init
(
probe_ent
);
if
(
rc
)
goto
err_out_hpriv
;
ahci_print_info
(
probe_ent
);
/* FIXME: check ata_device_add return value */
ata_device_add
(
probe_ent
);
kfree
(
probe_ent
);
return
0
;
err_out_hpriv:
kfree
(
hpriv
);
err_out_iounmap:
iounmap
(
mmio_base
);
err_out_free_ent:
kfree
(
probe_ent
);
err_out_regions:
pci_release_regions
(
pdev
);
err_out:
pci_disable_device
(
pdev
);
return
rc
;
}
static
int
__init
ahci_init
(
void
)
{
return
pci_module_init
(
&
ahci_pci_driver
);
}
static
void
__exit
ahci_exit
(
void
)
{
pci_unregister_driver
(
&
ahci_pci_driver
);
}
MODULE_AUTHOR
(
"Jeff Garzik"
);
MODULE_DESCRIPTION
(
"AHCI SATA low-level driver"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_DEVICE_TABLE
(
pci
,
ahci_pci_tbl
);
module_init
(
ahci_init
);
module_exit
(
ahci_exit
);
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