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
d01183a5
Commit
d01183a5
authored
Jul 30, 2016
by
Mikhail Karpenko
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Read sector to predefined buffer (not SG list)
parent
ee474cb0
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
344 additions
and
2 deletions
+344
-2
ahci_elphel.c
src/drivers/ata/ahci_elphel.c
+344
-2
No files found.
src/drivers/ata/ahci_elphel.c
View file @
d01183a5
...
@@ -26,6 +26,8 @@
...
@@ -26,6 +26,8 @@
#include <linux/sysfs.h>
#include <linux/sysfs.h>
#include "ahci.h"
#include "ahci.h"
#include <elphel/elphel393-mem.h>
#define DRV_NAME "elphel-ahci"
#define DRV_NAME "elphel-ahci"
/*
/*
* FPGA bitstream control address and bit mask. These are used to check whether
* FPGA bitstream control address and bit mask. These are used to check whether
...
@@ -37,6 +39,12 @@
...
@@ -37,6 +39,12 @@
/* Property names from device tree, these are specific for the controller */
/* Property names from device tree, these are specific for the controller */
#define PROP_NAME_CLB_OFFS "clb_offs"
#define PROP_NAME_CLB_OFFS "clb_offs"
#define PROP_NAME_FB_OFFS "fb_offs"
#define PROP_NAME_FB_OFFS "fb_offs"
/** Flag indicating that IRQ should not be processed in ahci_port_interrupt */
#define IRQ_SIMPLE (1 << 0)
/** The length of a command FIS in double words */
#define CMD_FIS_LEN 5
/** This is used to get 28-bit address from 64-bit value */
#define ADDR_MASK_28_BIT ((u64)0xfffffff)
static
struct
ata_port_operations
ahci_elphel_ops
;
static
struct
ata_port_operations
ahci_elphel_ops
;
static
const
struct
ata_port_info
ahci_elphel_port_info
;
static
const
struct
ata_port_info
ahci_elphel_port_info
;
...
@@ -50,8 +58,17 @@ struct elphel_ahci_priv {
...
@@ -50,8 +58,17 @@ struct elphel_ahci_priv {
u32
clb_offs
;
u32
clb_offs
;
u32
fb_offs
;
u32
fb_offs
;
u32
base_addr
;
u32
base_addr
;
u32
flags
;
};
};
static
struct
platform_device
*
g_pdev
;
static
ssize_t
elphel_test_write
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buff
,
size_t
buff_sz
);
static
irqreturn_t
elphel_irq_handler
(
int
irq
,
void
*
dev_instance
);
static
int
elphel_write_dma
(
struct
ata_port
*
ap
,
u64
start
,
u16
count
,
struct
scatterlist
*
sg
,
unsigned
int
elem
);
static
int
elphel_read_dma
(
struct
ata_port
*
ap
,
u64
start
,
u16
count
,
u8
*
buff
,
unsigned
int
elem
);
void
prep_cfis
(
u8
*
cmd_tbl
,
u8
cmd
,
u64
start_addr
,
u16
count
);
static
ssize_t
set_load_flag
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
static
ssize_t
set_load_flag
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buff
,
size_t
buff_sz
)
const
char
*
buff
,
size_t
buff_sz
)
{
{
...
@@ -182,14 +199,13 @@ static int elphel_drv_probe(struct platform_device *pdev)
...
@@ -182,14 +199,13 @@ static int elphel_drv_probe(struct platform_device *pdev)
struct
elphel_ahci_priv
*
dpriv
;
struct
elphel_ahci_priv
*
dpriv
;
struct
device
*
dev
=
&
pdev
->
dev
;
struct
device
*
dev
=
&
pdev
->
dev
;
const
struct
of_device_id
*
match
;
const
struct
of_device_id
*
match
;
unsigned
int
reg_val
;
if
(
&
dev
->
kobj
)
{
if
(
&
dev
->
kobj
)
{
ret
=
sysfs_create_group
(
&
dev
->
kobj
,
&
dev_attr_root_group
);
ret
=
sysfs_create_group
(
&
dev
->
kobj
,
&
dev_attr_root_group
);
if
(
ret
<
0
)
if
(
ret
<
0
)
return
ret
;
return
ret
;
}
}
elphel_defer_load
(
dev
);
//
elphel_defer_load(dev);
dev_info
(
&
pdev
->
dev
,
"probing Elphel AHCI driver"
);
dev_info
(
&
pdev
->
dev
,
"probing Elphel AHCI driver"
);
...
@@ -218,6 +234,20 @@ static int elphel_drv_probe(struct platform_device *pdev)
...
@@ -218,6 +234,20 @@ static int elphel_drv_probe(struct platform_device *pdev)
ahci_platform_disable_resources
(
hpriv
);
ahci_platform_disable_resources
(
hpriv
);
return
ret
;
return
ret
;
}
}
g_pdev
=
pdev
;
/* reassign interrupt handler*/
int
rc
;
unsigned
int
irq_flags
=
IRQF_SHARED
;
int
irq
=
platform_get_irq
(
pdev
,
0
);
struct
ata_host
*
ahost
=
platform_get_drvdata
(
pdev
);
printk
(
KERN_DEBUG
">>> removing automatically assigned irq handler
\n
"
);
devm_free_irq
(
dev
,
irq
,
ahost
);
rc
=
devm_request_irq
(
dev
,
irq
,
elphel_irq_handler
,
irq_flags
,
dev_name
(
dev
),
ahost
);
if
(
rc
)
{
printk
(
KERN_DEBUG
">>> failed to request irq
\n
"
);
return
rc
;
}
printk
(
KERN_DEBUG
">>> irq handler reassigned
\n
"
);
return
0
;
return
0
;
}
}
...
@@ -231,6 +261,13 @@ static int elphel_drv_remove(struct platform_device *pdev)
...
@@ -231,6 +261,13 @@ static int elphel_drv_remove(struct platform_device *pdev)
return
0
;
return
0
;
}
}
void
dump_tf_addr
(
struct
ata_taskfile
*
tf
)
{
printk
(
KERN_DEBUG
">>> taskfile dump: lbal = 0x%x, lbam = 0x%x, lbah = 0x%x, "
"hob_lbal = 0x%x, hod_lbam = 0x%x, hob_lbah = 0x%x
\n
"
,
tf
->
lbal
,
tf
->
lbam
,
tf
->
lbah
,
tf
->
hob_lbal
,
tf
->
hob_lbam
,
tf
->
hob_lbah
);
}
static
void
elphel_qc_prep
(
struct
ata_queued_cmd
*
qc
)
static
void
elphel_qc_prep
(
struct
ata_queued_cmd
*
qc
)
{
{
struct
ata_port
*
ap
=
qc
->
ap
;
struct
ata_port
*
ap
=
qc
->
ap
;
...
@@ -256,6 +293,9 @@ static void elphel_qc_prep(struct ata_queued_cmd *qc)
...
@@ -256,6 +293,9 @@ static void elphel_qc_prep(struct ata_queued_cmd *qc)
ata_tf_to_fis
(
&
qc
->
tf
,
qc
->
dev
->
link
->
pmp
,
1
,
cmd_tbl
);
ata_tf_to_fis
(
&
qc
->
tf
,
qc
->
dev
->
link
->
pmp
,
1
,
cmd_tbl
);
dev_dbg
(
ap
->
dev
,
">>> CFIS dump, data from libahci:
\n
"
);
print_hex_dump_bytes
(
""
,
DUMP_PREFIX_OFFSET
,
cmd_tbl
,
20
);
if
(
is_atapi
)
{
if
(
is_atapi
)
{
memset
(
cmd_tbl
+
AHCI_CMD_TBL_CDB
,
0
,
32
);
memset
(
cmd_tbl
+
AHCI_CMD_TBL_CDB
,
0
,
32
);
memcpy
(
cmd_tbl
+
AHCI_CMD_TBL_CDB
,
qc
->
cdb
,
qc
->
dev
->
cdb_len
);
memcpy
(
cmd_tbl
+
AHCI_CMD_TBL_CDB
,
qc
->
cdb
,
qc
->
dev
->
cdb_len
);
...
@@ -292,8 +332,10 @@ static void elphel_qc_prep(struct ata_queued_cmd *qc)
...
@@ -292,8 +332,10 @@ static void elphel_qc_prep(struct ata_queued_cmd *qc)
}
}
static
DEVICE_ATTR
(
load_module
,
S_IWUSR
|
S_IWGRP
,
NULL
,
set_load_flag
);
static
DEVICE_ATTR
(
load_module
,
S_IWUSR
|
S_IWGRP
,
NULL
,
set_load_flag
);
static
DEVICE_ATTR
(
test_write
,
S_IWUSR
|
S_IWGRP
,
NULL
,
elphel_test_write
);
static
struct
attribute
*
root_dev_attrs
[]
=
{
static
struct
attribute
*
root_dev_attrs
[]
=
{
&
dev_attr_load_module
.
attr
,
&
dev_attr_load_module
.
attr
,
&
dev_attr_test_write
.
attr
,
NULL
NULL
};
};
static
const
struct
attribute_group
dev_attr_root_group
=
{
static
const
struct
attribute_group
dev_attr_root_group
=
{
...
@@ -341,6 +383,306 @@ static struct platform_driver ahci_elphel_driver = {
...
@@ -341,6 +383,306 @@ static struct platform_driver ahci_elphel_driver = {
};
};
module_platform_driver
(
ahci_elphel_driver
);
module_platform_driver
(
ahci_elphel_driver
);
#define TEST_BUFF_SZ 512
//#define SDA2_LBA_ADDR 124963848
#define SDA2_LBA_ADDR 1
static
ssize_t
elphel_test_write
(
struct
device
*
dev
,
struct
device_attribute
*
attr
,
const
char
*
buff
,
size_t
buff_sz
)
{
int
i
,
n_elem
;
struct
ata_host
*
host
;
struct
ata_port
*
port
;
struct
ahci_host_priv
*
hpriv
;
struct
elphel_ahci_priv
*
dpriv
;
struct
scatterlist
sg
;
struct
scatterlist
*
sg_ptr
;
u8
*
test_buff
=
pElphel_buf
->
d2h_vaddr
;
unsigned
int
lba_addr
;
if
(
sscanf
(
buff
,
"%u"
,
&
lba_addr
)
==
1
)
{
printk
(
KERN_DEBUG
">>> got LBA address: %d"
,
lba_addr
);
}
else
{
lba_addr
=
SDA2_LBA_ADDR
;
}
host
=
platform_get_drvdata
(
g_pdev
);
port
=
host
->
ports
[
0
];
hpriv
=
port
->
host
->
private_data
;
dpriv
=
hpriv
->
plat_data
;
/* read test */
// printk(KERN_DEBUG ">>> dump test buffer before reading: %d bytes, addr = 0x%p\n", TEST_BUFF_SZ, test_buff);
// dma_sync_single_for_cpu(dev, pElphel_buf->d2h_paddr, pElphel_buf->d2h_size, DMA_FROM_DEVICE);
// print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, test_buff, TEST_BUFF_SZ);
//// sg_init_one(&sg, (const void *)test_buff, TEST_BUFF_SZ);
dma_sync_single_for_device
(
dev
,
pElphel_buf
->
d2h_paddr
,
pElphel_buf
->
d2h_size
,
DMA_FROM_DEVICE
);
// printk(KERN_DEBUG ">>> buffer has been mapped for device\n");
// printk(KERN_DEBUG ">>> dump the content of SG list:\n");
// n_elem = 0;
// for_each_sg(&sg, sg_ptr, 1, n_elem) {
// dma_addr_t addr = sg_dma_address(sg_ptr);
// u32 sg_len = sg_dma_len(sg_ptr);
// printk(KERN_DEBUG ">>>\t# %d, addr = 0x%x, %d\n", n_elem, addr, sg_len);
// }
// printk(KERN_DEBUG ">>>\n");
printk
(
KERN_DEBUG
">>> trying to read data to sg list
\n
"
);
elphel_read_dma
(
port
,
lba_addr
,
1
,
test_buff
,
TEST_BUFF_SZ
);
printk
(
KERN_DEBUG
">>> command has been issued
\n
"
);
while
(
dpriv
->
flags
&
IRQ_SIMPLE
)
{
printk_once
(
KERN_DEBUG
">>> waiting for interrupt
\n
"
);
msleep
(
1
);
}
printk
(
KERN_DEBUG
">>> dump test buffer after reading: %d bytes
\n
"
,
TEST_BUFF_SZ
);
dma_sync_single_for_cpu
(
dev
,
pElphel_buf
->
d2h_paddr
,
pElphel_buf
->
d2h_size
,
DMA_FROM_DEVICE
);
print_hex_dump_bytes
(
""
,
DUMP_PREFIX_OFFSET
,
test_buff
,
TEST_BUFF_SZ
);
dma_sync_single_for_device
(
dev
,
pElphel_buf
->
d2h_paddr
,
pElphel_buf
->
d2h_size
,
DMA_FROM_DEVICE
);
printk
(
KERN_DEBUG
">>> buffer has been mapped for device
\n
"
);
/* end of read test */
// printk(KERN_DEBUG ">>> interrupt flag has been cleared\n");
// printk(KERN_DEBUG ">>> dump of SG list area\n");
// dma_sync_single_for_cpu(dev, pElphel_buf->h2d_paddr, pElphel_buf->h2d_size, DMA_TO_DEVICE);
// print_hex_dump_bytes("", DUMP_PREFIX_NONE, test_buff, TEST_BUFF_SZ);
// dma_sync_single_for_device(dev, pElphel_buf->h2d_paddr, pElphel_buf->h2d_size, DMA_TO_DEVICE);
// /* end of read test */
//
// /* write test */
// printk(KERN_DEBUG ">>> filling test buffer: %d bytes\n", TEST_BUFF_SZ);
// dma_sync_single_for_cpu(dev, pElphel_buf->h2d_paddr, pElphel_buf->h2d_size, DMA_TO_DEVICE);
// for (i = 0; i < TEST_BUFF_SZ - 1; i += 2) {
// test_buff[i] = 0xaa;
// test_buff[i + 1] = 0x55;
// }
// print_hex_dump_bytes("", DUMP_PREFIX_NONE, test_buff, TEST_BUFF_SZ);
// dma_sync_single_for_device(dev, pElphel_buf->h2d_paddr, pElphel_buf->h2d_size, DMA_TO_DEVICE);
// printk(KERN_DEBUG ">>> buffer has been mapped\n");
//
// sg_init_one(&sg, pElphel_buf->h2d_vaddr, TEST_BUFF_SZ);
//
// printk(KERN_DEBUG ">>> trying to read data to sg list\n");
// elphel_read_dma(port, SDA2_LBA_ADDR, 1, &sg, 1);
// printk(KERN_DEBUG ">>> command has been issued\n");
// /* end of write test */
//
//// while (dpriv->flags & IRQ_SIMPLE) {
//// msleep(1);
//// }
// printk(KERN_DEBUG ">>> interrupt flag has been cleared\n");
// printk(KERN_DEBUG ">>> dump of SG list area\n");
// dma_sync_single_for_cpu(dev, pElphel_buf->h2d_paddr, pElphel_buf->h2d_size, DMA_TO_DEVICE);
// print_hex_dump_bytes("", DUMP_PREFIX_NONE, test_buff, TEST_BUFF_SZ);
// dma_sync_single_for_device(dev, pElphel_buf->h2d_paddr, pElphel_buf->h2d_size, DMA_TO_DEVICE);
return
buff_sz
;
}
/** Prepare software constructed command FIS in command table area. The structure of the
* command FIS is described in Transport Layer chapter of Serial ATA revision 3.1 documentation.
*/
inline
void
prep_cfis
(
u8
*
cmd_tbl
,
///< pointer to the beginning of command table
u8
cmd
,
///< ATA command as described in ATA/ATAPI command set
u64
start_addr
,
///< LBA start address
u16
count
)
///< sector count, the number of 512 byte sectors to read or write
///< @return None
{
u8
device
,
ctrl
;
/* select the content of Device and Control registers based on command, read the description of
* a command in ATA/ATAPI command set documentation
*/
switch
(
cmd
)
{
case
ATA_CMD_WRITE_EXT
:
// not verified yet
device
=
0x00
;
ctrl
=
0x00
;
break
;
case
ATA_CMD_READ
:
device
=
0xe0
|
((
start_addr
>>
24
)
&
0x0f
);
ctrl
=
0x08
;
/* this is 28-bit command; 4 bits of the address have already been
* placed to Device register, invalidate the remaining (if any) upper
* bits of the address and leave only 24 significant bits (just in case)
*/
start_addr
&=
0xffffff
;
break
;
case
ATA_CMD_READ_EXT
:
device
=
0xe0
;
ctrl
=
0x08
;
break
;
default:
device
=
0x00
;
ctrl
=
0x00
;
}
cmd_tbl
[
0
]
=
0x27
;
// H2D register FIS
cmd_tbl
[
1
]
=
0x80
;
// set C = 1
cmd_tbl
[
2
]
=
cmd
;
// ATA READ or WRITE DMA command as described in ATA/ATAPI command set
cmd_tbl
[
3
]
=
0
;
// features(7:0)
cmd_tbl
[
4
]
=
start_addr
&
0xff
;
// LBA(7:0)
cmd_tbl
[
5
]
=
(
start_addr
>>
8
)
&
0xff
;
// LBA(15:8)
cmd_tbl
[
6
]
=
(
start_addr
>>
16
)
&
0xff
;
// LBA(23:16)
cmd_tbl
[
7
]
=
device
;
// device
cmd_tbl
[
8
]
=
(
start_addr
>>
24
)
&
0xff
;
// LBA(31:24)
cmd_tbl
[
9
]
=
(
start_addr
>>
32
)
&
0xff
;
// LBA(39:32)
cmd_tbl
[
10
]
=
(
start_addr
>>
40
)
&
0xff
;
// LBA(47:40)
cmd_tbl
[
11
]
=
0
;
// features(15:8)
cmd_tbl
[
12
]
=
count
&
0xff
;
// count(7:0)
cmd_tbl
[
13
]
=
(
count
>>
8
)
&
0xff
;
// count(15:8)
cmd_tbl
[
14
]
=
0
;
// ICC (isochronous command completion)
cmd_tbl
[
15
]
=
ctrl
;
// control
}
static
int
elphel_write_dma
(
struct
ata_port
*
ap
,
u64
start
,
u16
count
,
struct
scatterlist
*
sg
,
unsigned
int
elem
)
{
u32
opts
;
const
u32
cmd_fis_len
=
5
;
unsigned
int
n_elem
;
// void *cmd_tbl;
u8
*
cmd_tbl
;
unsigned
int
slot_num
=
0
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
elphel_ahci_priv
*
dpriv
=
hpriv
->
plat_data
;
struct
scatterlist
*
sg_ptr
;
struct
ahci_sg
*
ahci_sg
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
dpriv
->
flags
|=
IRQ_SIMPLE
;
/* prepare command FIS */
dma_sync_single_for_cpu
(
ap
->
dev
,
pp
->
cmd_tbl_dma
,
AHCI_CMD_TBL_AR_SZ
,
DMA_TO_DEVICE
);
cmd_tbl
=
pp
->
cmd_tbl
+
slot_num
*
AHCI_CMD_TBL_SZ
;
cmd_tbl
[
0
]
=
0x27
;
// H2D register FIS
cmd_tbl
[
1
]
=
0x80
;
// set C = 1
cmd_tbl
[
2
]
=
ATA_CMD_WRITE
;
// ATA WRITE DMA command as described in ATA/ATAPI command set
cmd_tbl
[
3
]
=
0
;
// features(7:0)
cmd_tbl
[
4
]
=
(
start
>>
0
)
&
0xff
;
// LBA(7:0)
cmd_tbl
[
5
]
=
(
start
>>
8
)
&
0xff
;
// LBA(15:8)
cmd_tbl
[
6
]
=
(
start
>>
16
)
&
0xff
;
// LBA(23:16)
cmd_tbl
[
7
]
=
0
;
// device
cmd_tbl
[
8
]
=
(
start
>>
24
)
&
0xff
;
// LBA(31:24)
cmd_tbl
[
9
]
=
(
start
>>
32
)
&
0xff
;
// LBA(39:32)
cmd_tbl
[
10
]
=
(
start
>>
40
)
&
0xff
;
// LBA(47:40)
cmd_tbl
[
11
]
=
0
;
// features(15:8)
cmd_tbl
[
12
]
=
(
count
>>
0
)
&
0xff
;
// count(7:0)
cmd_tbl
[
13
]
=
(
count
>>
8
)
&
0xff
;
// count(15:8)
cmd_tbl
[
14
]
=
0
;
// ICC (isochronous command completion)
cmd_tbl
[
15
]
=
0
;
// control
/* prepare physical region descriptor table */
n_elem
=
0
;
ahci_sg
=
pp
->
cmd_tbl
+
slot_num
*
AHCI_CMD_TBL_SZ
+
AHCI_CMD_TBL_HDR_SZ
;
for_each_sg
(
sg
,
sg_ptr
,
elem
,
n_elem
)
{
dma_addr_t
addr
=
sg_dma_address
(
sg_ptr
);
u32
sg_len
=
sg_dma_len
(
sg_ptr
);
ahci_sg
[
n_elem
].
addr
=
cpu_to_le32
(
addr
&
0xffffffff
);
ahci_sg
[
n_elem
].
addr_hi
=
cpu_to_le32
((
addr
>>
16
)
>>
16
);
ahci_sg
[
n_elem
].
flags_size
=
cpu_to_le32
(
sg_len
-
1
);
}
/* prepare command header */
opts
=
cmd_fis_len
|
(
n_elem
<<
16
)
|
AHCI_CMD_WRITE
|
AHCI_CMD_PREFETCH
|
AHCI_CMD_CLR_BUSY
;
ahci_fill_cmd_slot
(
pp
,
slot_num
,
opts
);
dma_sync_single_for_device
(
ap
->
dev
,
pp
->
cmd_tbl_dma
,
AHCI_CMD_TBL_AR_SZ
,
DMA_TO_DEVICE
);
/* issue command */
writel
(
1
<<
slot_num
,
port_mmio
+
PORT_CMD_ISSUE
);
return
0
;
}
static
int
elphel_read_dma
(
struct
ata_port
*
ap
,
u64
start
,
u16
count
,
u8
*
buff
,
unsigned
int
elem
)
{
u32
opts
;
const
u32
cmd_fis_len
=
5
;
unsigned
int
n_elem
;
u8
*
cmd_tbl
;
u8
cmd
;
unsigned
int
slot_num
=
0
;
struct
ahci_port_priv
*
pp
=
ap
->
private_data
;
struct
ahci_host_priv
*
hpriv
=
ap
->
host
->
private_data
;
struct
elphel_ahci_priv
*
dpriv
=
hpriv
->
plat_data
;
struct
scatterlist
*
sg_ptr
;
struct
ahci_sg
*
ahci_sg
;
void
__iomem
*
port_mmio
=
ahci_port_base
(
ap
);
dpriv
->
flags
|=
IRQ_SIMPLE
;
/* prepare command FIS */
dma_sync_single_for_cpu
(
ap
->
dev
,
pp
->
cmd_tbl_dma
,
AHCI_CMD_TBL_AR_SZ
,
DMA_TO_DEVICE
);
cmd_tbl
=
pp
->
cmd_tbl
+
slot_num
*
AHCI_CMD_TBL_SZ
;
if
(
start
&
~
ADDR_MASK_28_BIT
)
cmd
=
ATA_CMD_READ_EXT
;
else
cmd
=
ATA_CMD_READ
;
prep_cfis
(
cmd_tbl
,
cmd
,
start
,
count
);
/* prepare physical region descriptor table */
// n_elem = 0;
// ahci_sg = pp->cmd_tbl + slot_num * AHCI_CMD_TBL_SZ + AHCI_CMD_TBL_HDR_SZ;
// for_each_sg(sg, sg_ptr, elem, n_elem) {
// dma_addr_t addr = sg_dma_address(sg_ptr);
// u32 sg_len = sg_dma_len(sg_ptr);
//
// ahci_sg[n_elem].addr = cpu_to_le32(addr & 0xffffffff);
// ahci_sg[n_elem].addr_hi = cpu_to_le32((addr >> 16) >> 16);
// ahci_sg[n_elem].flags_size = cpu_to_le32(sg_len - 1);
// }
ahci_sg
=
pp
->
cmd_tbl
+
slot_num
*
AHCI_CMD_TBL_SZ
+
AHCI_CMD_TBL_HDR_SZ
;
ahci_sg
->
addr
=
cpu_to_le32
(
pElphel_buf
->
d2h_paddr
&
0xffffffff
);
ahci_sg
->
addr_hi
=
cpu_to_le32
((
pElphel_buf
->
d2h_paddr
>>
16
)
>>
16
);
ahci_sg
->
flags_size
=
cpu_to_le32
((
elem
-
1
)
|
(
1
<<
31
));
n_elem
=
1
;
/* prepare command header */
opts
=
CMD_FIS_LEN
|
(
n_elem
<<
16
)
|
AHCI_CMD_PREFETCH
|
AHCI_CMD_CLR_BUSY
;
// opts = sizeof(struct ahci_cmd_hdr) | (n_elem << 16) | AHCI_CMD_PREFETCH | AHCI_CMD_CLR_BUSY;
ahci_fill_cmd_slot
(
pp
,
slot_num
,
opts
);
printk
(
KERN_DEBUG
">>> dump command table content, first %d bytes, phys addr = 0x%x:
\n
"
,
TEST_BUFF_SZ
,
pp
->
cmd_tbl_dma
);
print_hex_dump_bytes
(
""
,
DUMP_PREFIX_OFFSET
,
pp
->
cmd_tbl
,
TEST_BUFF_SZ
);
dma_sync_single_for_device
(
ap
->
dev
,
pp
->
cmd_tbl_dma
,
AHCI_CMD_TBL_AR_SZ
,
DMA_TO_DEVICE
);
/* issue command */
writel
(
0x11
,
port_mmio
+
PORT_CMD
);
writel
(
1
<<
slot_num
,
port_mmio
+
PORT_CMD_ISSUE
);
return
0
;
}
static
irqreturn_t
elphel_irq_handler
(
int
irq
,
void
*
dev_instance
)
{
irqreturn_t
handled
;
struct
ata_host
*
host
=
dev_instance
;
struct
ahci_host_priv
*
hpriv
=
host
->
private_data
;
struct
elphel_ahci_priv
*
dpriv
=
hpriv
->
plat_data
;
u32
irq_stat
,
irq_masked
;
if
(
dpriv
->
flags
&
IRQ_SIMPLE
)
{
/* handle interrupt */
printk
(
KERN_DEBUG
">>> handling interrupt
\n
"
);
dpriv
->
flags
&=
~
IRQ_SIMPLE
;
// clear_bit(IRQ_SIMPLE, &dpriv->flags);
irq_stat
=
readl
(
hpriv
->
mmio
+
HOST_IRQ_STAT
);
if
(
!
irq_stat
)
return
IRQ_NONE
;
// irq_masked = irq_stat & hpriv->port_map;
writel
(
irq_stat
,
hpriv
->
mmio
+
HOST_IRQ_STAT
);
handled
=
IRQ_HANDLED
;
}
else
{
/* pass handling to AHCI level*/
handled
=
ahci_single_irq_intr
(
irq
,
dev_instance
);
}
return
handled
;
}
MODULE_LICENSE
(
"GPL"
);
MODULE_LICENSE
(
"GPL"
);
MODULE_AUTHOR
(
"Elphel, Inc."
);
MODULE_AUTHOR
(
"Elphel, Inc."
);
MODULE_DESCRIPTION
(
"Elphel AHCI SATA platform driver for elphel393 camera"
);
MODULE_DESCRIPTION
(
"Elphel AHCI SATA platform driver for elphel393 camera"
);
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