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
53b84bad
Commit
53b84bad
authored
May 17, 2016
by
Vinod Koul
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'topic/sun6i' into for-linus
parents
82770a2f
a90e173f
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
188 additions
and
66 deletions
+188
-66
drivers/dma/sun6i-dma.c
drivers/dma/sun6i-dma.c
+188
-66
No files found.
drivers/dma/sun6i-dma.c
View file @
53b84bad
...
...
@@ -146,6 +146,8 @@ struct sun6i_vchan {
struct
dma_slave_config
cfg
;
struct
sun6i_pchan
*
phy
;
u8
port
;
u8
irq_type
;
bool
cyclic
;
};
struct
sun6i_dma_dev
{
...
...
@@ -254,6 +256,30 @@ static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width)
return
addr_width
>>
1
;
}
static
size_t
sun6i_get_chan_size
(
struct
sun6i_pchan
*
pchan
)
{
struct
sun6i_desc
*
txd
=
pchan
->
desc
;
struct
sun6i_dma_lli
*
lli
;
size_t
bytes
;
dma_addr_t
pos
;
pos
=
readl
(
pchan
->
base
+
DMA_CHAN_LLI_ADDR
);
bytes
=
readl
(
pchan
->
base
+
DMA_CHAN_CUR_CNT
);
if
(
pos
==
LLI_LAST_ITEM
)
return
bytes
;
for
(
lli
=
txd
->
v_lli
;
lli
;
lli
=
lli
->
v_lli_next
)
{
if
(
lli
->
p_lli_next
==
pos
)
{
for
(
lli
=
lli
->
v_lli_next
;
lli
;
lli
=
lli
->
v_lli_next
)
bytes
+=
lli
->
len
;
break
;
}
}
return
bytes
;
}
static
void
*
sun6i_dma_lli_add
(
struct
sun6i_dma_lli
*
prev
,
struct
sun6i_dma_lli
*
next
,
dma_addr_t
next_phy
,
...
...
@@ -276,45 +302,6 @@ static void *sun6i_dma_lli_add(struct sun6i_dma_lli *prev,
return
next
;
}
static
inline
int
sun6i_dma_cfg_lli
(
struct
sun6i_dma_lli
*
lli
,
dma_addr_t
src
,
dma_addr_t
dst
,
u32
len
,
struct
dma_slave_config
*
config
)
{
u8
src_width
,
dst_width
,
src_burst
,
dst_burst
;
if
(
!
config
)
return
-
EINVAL
;
src_burst
=
convert_burst
(
config
->
src_maxburst
);
if
(
src_burst
)
return
src_burst
;
dst_burst
=
convert_burst
(
config
->
dst_maxburst
);
if
(
dst_burst
)
return
dst_burst
;
src_width
=
convert_buswidth
(
config
->
src_addr_width
);
if
(
src_width
)
return
src_width
;
dst_width
=
convert_buswidth
(
config
->
dst_addr_width
);
if
(
dst_width
)
return
dst_width
;
lli
->
cfg
=
DMA_CHAN_CFG_SRC_BURST
(
src_burst
)
|
DMA_CHAN_CFG_SRC_WIDTH
(
src_width
)
|
DMA_CHAN_CFG_DST_BURST
(
dst_burst
)
|
DMA_CHAN_CFG_DST_WIDTH
(
dst_width
);
lli
->
src
=
src
;
lli
->
dst
=
dst
;
lli
->
len
=
len
;
lli
->
para
=
NORMAL_WAIT
;
return
0
;
}
static
inline
void
sun6i_dma_dump_lli
(
struct
sun6i_vchan
*
vchan
,
struct
sun6i_dma_lli
*
lli
)
{
...
...
@@ -381,9 +368,13 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
irq_reg
=
pchan
->
idx
/
DMA_IRQ_CHAN_NR
;
irq_offset
=
pchan
->
idx
%
DMA_IRQ_CHAN_NR
;
irq_val
=
readl
(
sdev
->
base
+
DMA_IRQ_EN
(
irq_offset
));
irq_val
|=
DMA_IRQ_QUEUE
<<
(
irq_offset
*
DMA_IRQ_CHAN_WIDTH
);
writel
(
irq_val
,
sdev
->
base
+
DMA_IRQ_EN
(
irq_offset
));
vchan
->
irq_type
=
vchan
->
cyclic
?
DMA_IRQ_PKG
:
DMA_IRQ_QUEUE
;
irq_val
=
readl
(
sdev
->
base
+
DMA_IRQ_EN
(
irq_reg
));
irq_val
&=
~
((
DMA_IRQ_HALF
|
DMA_IRQ_PKG
|
DMA_IRQ_QUEUE
)
<<
(
irq_offset
*
DMA_IRQ_CHAN_WIDTH
));
irq_val
|=
vchan
->
irq_type
<<
(
irq_offset
*
DMA_IRQ_CHAN_WIDTH
);
writel
(
irq_val
,
sdev
->
base
+
DMA_IRQ_EN
(
irq_reg
));
writel
(
pchan
->
desc
->
p_lli
,
pchan
->
base
+
DMA_CHAN_LLI_ADDR
);
writel
(
DMA_CHAN_ENABLE_START
,
pchan
->
base
+
DMA_CHAN_ENABLE
);
...
...
@@ -479,11 +470,12 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
writel
(
status
,
sdev
->
base
+
DMA_IRQ_STAT
(
i
));
for
(
j
=
0
;
(
j
<
DMA_IRQ_CHAN_NR
)
&&
status
;
j
++
)
{
if
(
status
&
DMA_IRQ_QUEUE
)
{
pchan
=
sdev
->
pchans
+
j
;
vchan
=
pchan
->
vchan
;
if
(
vchan
)
{
pchan
=
sdev
->
pchans
+
j
;
vchan
=
pchan
->
vchan
;
if
(
vchan
&&
(
status
&
vchan
->
irq_type
))
{
if
(
vchan
->
cyclic
)
{
vchan_cyclic_callback
(
&
pchan
->
desc
->
vd
);
}
else
{
spin_lock
(
&
vchan
->
vc
.
lock
);
vchan_cookie_complete
(
&
pchan
->
desc
->
vd
);
pchan
->
done
=
pchan
->
desc
;
...
...
@@ -502,6 +494,55 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
return
ret
;
}
static
int
set_config
(
struct
sun6i_dma_dev
*
sdev
,
struct
dma_slave_config
*
sconfig
,
enum
dma_transfer_direction
direction
,
u32
*
p_cfg
)
{
s8
src_width
,
dst_width
,
src_burst
,
dst_burst
;
switch
(
direction
)
{
case
DMA_MEM_TO_DEV
:
src_burst
=
convert_burst
(
sconfig
->
src_maxburst
?
sconfig
->
src_maxburst
:
8
);
src_width
=
convert_buswidth
(
sconfig
->
src_addr_width
!=
DMA_SLAVE_BUSWIDTH_UNDEFINED
?
sconfig
->
src_addr_width
:
DMA_SLAVE_BUSWIDTH_4_BYTES
);
dst_burst
=
convert_burst
(
sconfig
->
dst_maxburst
);
dst_width
=
convert_buswidth
(
sconfig
->
dst_addr_width
);
break
;
case
DMA_DEV_TO_MEM
:
src_burst
=
convert_burst
(
sconfig
->
src_maxburst
);
src_width
=
convert_buswidth
(
sconfig
->
src_addr_width
);
dst_burst
=
convert_burst
(
sconfig
->
dst_maxburst
?
sconfig
->
dst_maxburst
:
8
);
dst_width
=
convert_buswidth
(
sconfig
->
dst_addr_width
!=
DMA_SLAVE_BUSWIDTH_UNDEFINED
?
sconfig
->
dst_addr_width
:
DMA_SLAVE_BUSWIDTH_4_BYTES
);
break
;
default:
return
-
EINVAL
;
}
if
(
src_burst
<
0
)
return
src_burst
;
if
(
src_width
<
0
)
return
src_width
;
if
(
dst_burst
<
0
)
return
dst_burst
;
if
(
dst_width
<
0
)
return
dst_width
;
*
p_cfg
=
DMA_CHAN_CFG_SRC_BURST
(
src_burst
)
|
DMA_CHAN_CFG_SRC_WIDTH
(
src_width
)
|
DMA_CHAN_CFG_DST_BURST
(
dst_burst
)
|
DMA_CHAN_CFG_DST_WIDTH
(
dst_width
);
return
0
;
}
static
struct
dma_async_tx_descriptor
*
sun6i_dma_prep_dma_memcpy
(
struct
dma_chan
*
chan
,
dma_addr_t
dest
,
dma_addr_t
src
,
size_t
len
,
unsigned
long
flags
)
...
...
@@ -569,13 +610,15 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
struct
sun6i_desc
*
txd
;
struct
scatterlist
*
sg
;
dma_addr_t
p_lli
;
u32
lli_cfg
;
int
i
,
ret
;
if
(
!
sgl
)
return
NULL
;
if
(
!
is_slave_direction
(
dir
))
{
dev_err
(
chan2dev
(
chan
),
"Invalid DMA direction
\n
"
);
ret
=
set_config
(
sdev
,
sconfig
,
dir
,
&
lli_cfg
);
if
(
ret
)
{
dev_err
(
chan2dev
(
chan
),
"Invalid DMA configuration
\n
"
);
return
NULL
;
}
...
...
@@ -588,14 +631,14 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
if
(
!
v_lli
)
goto
err_lli_free
;
if
(
dir
==
DMA_MEM_TO_DEV
)
{
ret
=
sun6i_dma_cfg_lli
(
v_lli
,
sg_dma_address
(
sg
),
sconfig
->
dst_addr
,
sg_dma_len
(
sg
),
sconfig
);
if
(
ret
)
goto
err_cur_lli_free
;
v_lli
->
len
=
sg_dma_len
(
sg
);
v_lli
->
para
=
NORMAL_WAIT
;
v_lli
->
cfg
|=
DMA_CHAN_CFG_DST_IO_MODE
|
if
(
dir
==
DMA_MEM_TO_DEV
)
{
v_lli
->
src
=
sg_dma_address
(
sg
);
v_lli
->
dst
=
sconfig
->
dst_addr
;
v_lli
->
cfg
=
lli_cfg
|
DMA_CHAN_CFG_DST_IO_MODE
|
DMA_CHAN_CFG_SRC_LINEAR_MODE
|
DMA_CHAN_CFG_SRC_DRQ
(
DRQ_SDRAM
)
|
DMA_CHAN_CFG_DST_DRQ
(
vchan
->
port
);
...
...
@@ -607,13 +650,10 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
sg_dma_len
(
sg
),
flags
);
}
else
{
ret
=
sun6i_dma_cfg_lli
(
v_lli
,
sconfig
->
src_addr
,
sg_dma_address
(
sg
),
sg_dma_len
(
sg
),
sconfig
);
if
(
ret
)
goto
err_cur_lli_free
;
v_lli
->
cfg
|=
DMA_CHAN_CFG_DST_LINEAR_MODE
|
v_lli
->
src
=
sconfig
->
src_addr
;
v_lli
->
dst
=
sg_dma_address
(
sg
);
v_lli
->
cfg
=
lli_cfg
|
DMA_CHAN_CFG_DST_LINEAR_MODE
|
DMA_CHAN_CFG_SRC_IO_MODE
|
DMA_CHAN_CFG_DST_DRQ
(
DRQ_SDRAM
)
|
DMA_CHAN_CFG_SRC_DRQ
(
vchan
->
port
);
...
...
@@ -634,8 +674,78 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
return
vchan_tx_prep
(
&
vchan
->
vc
,
&
txd
->
vd
,
flags
);
err_cur_lli_free:
dma_pool_free
(
sdev
->
pool
,
v_lli
,
p_lli
);
err_lli_free:
for
(
prev
=
txd
->
v_lli
;
prev
;
prev
=
prev
->
v_lli_next
)
dma_pool_free
(
sdev
->
pool
,
prev
,
virt_to_phys
(
prev
));
kfree
(
txd
);
return
NULL
;
}
static
struct
dma_async_tx_descriptor
*
sun6i_dma_prep_dma_cyclic
(
struct
dma_chan
*
chan
,
dma_addr_t
buf_addr
,
size_t
buf_len
,
size_t
period_len
,
enum
dma_transfer_direction
dir
,
unsigned
long
flags
)
{
struct
sun6i_dma_dev
*
sdev
=
to_sun6i_dma_dev
(
chan
->
device
);
struct
sun6i_vchan
*
vchan
=
to_sun6i_vchan
(
chan
);
struct
dma_slave_config
*
sconfig
=
&
vchan
->
cfg
;
struct
sun6i_dma_lli
*
v_lli
,
*
prev
=
NULL
;
struct
sun6i_desc
*
txd
;
dma_addr_t
p_lli
;
u32
lli_cfg
;
unsigned
int
i
,
periods
=
buf_len
/
period_len
;
int
ret
;
ret
=
set_config
(
sdev
,
sconfig
,
dir
,
&
lli_cfg
);
if
(
ret
)
{
dev_err
(
chan2dev
(
chan
),
"Invalid DMA configuration
\n
"
);
return
NULL
;
}
txd
=
kzalloc
(
sizeof
(
*
txd
),
GFP_NOWAIT
);
if
(
!
txd
)
return
NULL
;
for
(
i
=
0
;
i
<
periods
;
i
++
)
{
v_lli
=
dma_pool_alloc
(
sdev
->
pool
,
GFP_NOWAIT
,
&
p_lli
);
if
(
!
v_lli
)
{
dev_err
(
sdev
->
slave
.
dev
,
"Failed to alloc lli memory
\n
"
);
goto
err_lli_free
;
}
v_lli
->
len
=
period_len
;
v_lli
->
para
=
NORMAL_WAIT
;
if
(
dir
==
DMA_MEM_TO_DEV
)
{
v_lli
->
src
=
buf_addr
+
period_len
*
i
;
v_lli
->
dst
=
sconfig
->
dst_addr
;
v_lli
->
cfg
=
lli_cfg
|
DMA_CHAN_CFG_DST_IO_MODE
|
DMA_CHAN_CFG_SRC_LINEAR_MODE
|
DMA_CHAN_CFG_SRC_DRQ
(
DRQ_SDRAM
)
|
DMA_CHAN_CFG_DST_DRQ
(
vchan
->
port
);
}
else
{
v_lli
->
src
=
sconfig
->
src_addr
;
v_lli
->
dst
=
buf_addr
+
period_len
*
i
;
v_lli
->
cfg
=
lli_cfg
|
DMA_CHAN_CFG_DST_LINEAR_MODE
|
DMA_CHAN_CFG_SRC_IO_MODE
|
DMA_CHAN_CFG_DST_DRQ
(
DRQ_SDRAM
)
|
DMA_CHAN_CFG_SRC_DRQ
(
vchan
->
port
);
}
prev
=
sun6i_dma_lli_add
(
prev
,
v_lli
,
p_lli
,
txd
);
}
prev
->
p_lli_next
=
txd
->
p_lli
;
/* cyclic list */
vchan
->
cyclic
=
true
;
return
vchan_tx_prep
(
&
vchan
->
vc
,
&
txd
->
vd
,
flags
);
err_lli_free:
for
(
prev
=
txd
->
v_lli
;
prev
;
prev
=
prev
->
v_lli_next
)
dma_pool_free
(
sdev
->
pool
,
prev
,
virt_to_phys
(
prev
));
...
...
@@ -712,6 +822,16 @@ static int sun6i_dma_terminate_all(struct dma_chan *chan)
spin_lock_irqsave
(
&
vchan
->
vc
.
lock
,
flags
);
if
(
vchan
->
cyclic
)
{
vchan
->
cyclic
=
false
;
if
(
pchan
&&
pchan
->
desc
)
{
struct
virt_dma_desc
*
vd
=
&
pchan
->
desc
->
vd
;
struct
virt_dma_chan
*
vc
=
&
vchan
->
vc
;
list_add_tail
(
&
vd
->
node
,
&
vc
->
desc_completed
);
}
}
vchan_get_all_descriptors
(
&
vchan
->
vc
,
&
head
);
if
(
pchan
)
{
...
...
@@ -759,7 +879,7 @@ static enum dma_status sun6i_dma_tx_status(struct dma_chan *chan,
}
else
if
(
!
pchan
||
!
pchan
->
desc
)
{
bytes
=
0
;
}
else
{
bytes
=
readl
(
pchan
->
base
+
DMA_CHAN_CUR_CNT
);
bytes
=
sun6i_get_chan_size
(
pchan
);
}
spin_unlock_irqrestore
(
&
vchan
->
vc
.
lock
,
flags
);
...
...
@@ -963,6 +1083,7 @@ static int sun6i_dma_probe(struct platform_device *pdev)
dma_cap_set
(
DMA_PRIVATE
,
sdc
->
slave
.
cap_mask
);
dma_cap_set
(
DMA_MEMCPY
,
sdc
->
slave
.
cap_mask
);
dma_cap_set
(
DMA_SLAVE
,
sdc
->
slave
.
cap_mask
);
dma_cap_set
(
DMA_CYCLIC
,
sdc
->
slave
.
cap_mask
);
INIT_LIST_HEAD
(
&
sdc
->
slave
.
channels
);
sdc
->
slave
.
device_free_chan_resources
=
sun6i_dma_free_chan_resources
;
...
...
@@ -970,6 +1091,7 @@ static int sun6i_dma_probe(struct platform_device *pdev)
sdc
->
slave
.
device_issue_pending
=
sun6i_dma_issue_pending
;
sdc
->
slave
.
device_prep_slave_sg
=
sun6i_dma_prep_slave_sg
;
sdc
->
slave
.
device_prep_dma_memcpy
=
sun6i_dma_prep_dma_memcpy
;
sdc
->
slave
.
device_prep_dma_cyclic
=
sun6i_dma_prep_dma_cyclic
;
sdc
->
slave
.
copy_align
=
DMAENGINE_ALIGN_4_BYTES
;
sdc
->
slave
.
device_config
=
sun6i_dma_config
;
sdc
->
slave
.
device_pause
=
sun6i_dma_pause
;
...
...
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