Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
kernel_linux
提交
98cd085e
K
kernel_linux
项目概览
OpenHarmony
/
kernel_linux
上一次同步 4 年多
通知
15
Star
8
Fork
2
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
kernel_linux
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
You need to sign in or sign up before continuing.
提交
98cd085e
编写于
7月 04, 2017
作者:
V
Vinod Koul
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'topic/pl08x' into for-linus
上级
70ef9af7
fcc78541
变更
9
隐藏空白更改
内联
并排
Showing
9 changed file
with
905 addition
and
276 deletion
+905
-276
Documentation/devicetree/bindings/dma/arm-pl08x.txt
Documentation/devicetree/bindings/dma/arm-pl08x.txt
+7
-2
arch/arm/mach-lpc32xx/phy3250.c
arch/arm/mach-lpc32xx/phy3250.c
+3
-0
arch/arm/mach-s3c64xx/pl080.c
arch/arm/mach-s3c64xx/pl080.c
+8
-20
arch/arm/mach-spear/spear3xx.c
arch/arm/mach-spear/spear3xx.c
+4
-10
arch/arm/mach-spear/spear6xx.c
arch/arm/mach-spear/spear6xx.c
+4
-10
drivers/dma/Kconfig
drivers/dma/Kconfig
+4
-2
drivers/dma/amba-pl08x.c
drivers/dma/amba-pl08x.c
+755
-215
include/linux/amba/pl080.h
include/linux/amba/pl080.h
+94
-13
include/linux/amba/pl08x.h
include/linux/amba/pl08x.h
+26
-4
未找到文件。
Documentation/devicetree/bindings/dma/arm-pl08x.txt
浏览文件 @
98cd085e
...
...
@@ -3,6 +3,11 @@
Required properties:
- compatible: "arm,pl080", "arm,primecell";
"arm,pl081", "arm,primecell";
"faraday,ftdmac020", "arm,primecell"
- arm,primecell-periphid: on the FTDMAC020 the primecell ID is not hard-coded
in the hardware and must be specified here as <0x0003b080>. This number
follows the PrimeCell standard numbering using the JEP106 vendor code 0x38
for Faraday Technology.
- reg: Address range of the PL08x registers
- interrupt: The PL08x interrupt number
- clocks: The clock running the IP core clock
...
...
@@ -20,8 +25,8 @@ Optional properties:
- dma-requests: contains the total number of DMA requests supported by the DMAC
- memcpy-burst-size: the size of the bursts for memcpy: 1, 4, 8, 16, 32
64, 128 or 256 bytes are legal values
- memcpy-bus-width: the bus width used for memcpy: 8, 16 or 32 are legal
values
- memcpy-bus-width: the bus width used for memcpy
in bits
: 8, 16 or 32 are legal
values
, the Faraday FTDMAC020 can also accept 64 bits
Clients
Required properties:
...
...
arch/arm/mach-lpc32xx/phy3250.c
浏览文件 @
98cd085e
...
...
@@ -137,6 +137,9 @@ static void pl08x_put_signal(const struct pl08x_channel_data *cd, int ch)
}
static
struct
pl08x_platform_data
pl08x_pd
=
{
/* Some reasonable memcpy defaults */
.
memcpy_burst_size
=
PL08X_BURST_SZ_256
,
.
memcpy_bus_width
=
PL08X_BUS_WIDTH_32_BITS
,
.
slave_channels
=
&
pl08x_slave_channels
[
0
],
.
num_slave_channels
=
ARRAY_SIZE
(
pl08x_slave_channels
),
.
get_xfer_signal
=
pl08x_get_signal
,
...
...
arch/arm/mach-s3c64xx/pl080.c
浏览文件 @
98cd085e
...
...
@@ -137,16 +137,10 @@ static const struct dma_slave_map s3c64xx_dma0_slave_map[] = {
};
struct
pl08x_platform_data
s3c64xx_dma0_plat_data
=
{
.
memcpy_channel
=
{
.
bus_id
=
"memcpy"
,
.
cctl_memcpy
=
(
PL080_BSIZE_4
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_4
<<
PL080_CONTROL_DB_SIZE_SHIFT
|
PL080_WIDTH_32BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
|
PL080_WIDTH_32BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
|
PL080_CONTROL_PROT_BUFF
|
PL080_CONTROL_PROT_CACHE
|
PL080_CONTROL_PROT_SYS
),
},
.
memcpy_burst_size
=
PL08X_BURST_SZ_4
,
.
memcpy_bus_width
=
PL08X_BUS_WIDTH_32_BITS
,
.
memcpy_prot_buff
=
true
,
.
memcpy_prot_cache
=
true
,
.
lli_buses
=
PL08X_AHB1
,
.
mem_buses
=
PL08X_AHB1
,
.
get_xfer_signal
=
pl08x_get_xfer_signal
,
...
...
@@ -238,16 +232,10 @@ static const struct dma_slave_map s3c64xx_dma1_slave_map[] = {
};
struct
pl08x_platform_data
s3c64xx_dma1_plat_data
=
{
.
memcpy_channel
=
{
.
bus_id
=
"memcpy"
,
.
cctl_memcpy
=
(
PL080_BSIZE_4
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_4
<<
PL080_CONTROL_DB_SIZE_SHIFT
|
PL080_WIDTH_32BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
|
PL080_WIDTH_32BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
|
PL080_CONTROL_PROT_BUFF
|
PL080_CONTROL_PROT_CACHE
|
PL080_CONTROL_PROT_SYS
),
},
.
memcpy_burst_size
=
PL08X_BURST_SZ_4
,
.
memcpy_bus_width
=
PL08X_BUS_WIDTH_32_BITS
,
.
memcpy_prot_buff
=
true
,
.
memcpy_prot_cache
=
true
,
.
lli_buses
=
PL08X_AHB1
,
.
mem_buses
=
PL08X_AHB1
,
.
get_xfer_signal
=
pl08x_get_xfer_signal
,
...
...
arch/arm/mach-spear/spear3xx.c
浏览文件 @
98cd085e
...
...
@@ -44,16 +44,10 @@ struct pl022_ssp_controller pl022_plat_data = {
/* dmac device registration */
struct
pl08x_platform_data
pl080_plat_data
=
{
.
memcpy_channel
=
{
.
bus_id
=
"memcpy"
,
.
cctl_memcpy
=
(
PL080_BSIZE_16
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
\
PL080_BSIZE_16
<<
PL080_CONTROL_DB_SIZE_SHIFT
|
\
PL080_WIDTH_32BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
|
\
PL080_WIDTH_32BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
|
\
PL080_CONTROL_PROT_BUFF
|
PL080_CONTROL_PROT_CACHE
|
\
PL080_CONTROL_PROT_SYS
),
},
.
memcpy_burst_size
=
PL08X_BURST_SZ_16
,
.
memcpy_bus_width
=
PL08X_BUS_WIDTH_32_BITS
,
.
memcpy_prot_buff
=
true
,
.
memcpy_prot_cache
=
true
,
.
lli_buses
=
PL08X_AHB1
,
.
mem_buses
=
PL08X_AHB1
,
.
get_xfer_signal
=
pl080_get_signal
,
...
...
arch/arm/mach-spear/spear6xx.c
浏览文件 @
98cd085e
...
...
@@ -322,16 +322,10 @@ static struct pl08x_channel_data spear600_dma_info[] = {
};
static
struct
pl08x_platform_data
spear6xx_pl080_plat_data
=
{
.
memcpy_channel
=
{
.
bus_id
=
"memcpy"
,
.
cctl_memcpy
=
(
PL080_BSIZE_16
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
\
PL080_BSIZE_16
<<
PL080_CONTROL_DB_SIZE_SHIFT
|
\
PL080_WIDTH_32BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
|
\
PL080_WIDTH_32BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
|
\
PL080_CONTROL_PROT_BUFF
|
PL080_CONTROL_PROT_CACHE
|
\
PL080_CONTROL_PROT_SYS
),
},
.
memcpy_burst_size
=
PL08X_BURST_SZ_16
,
.
memcpy_bus_width
=
PL08X_BUS_WIDTH_32_BITS
,
.
memcpy_prot_buff
=
true
,
.
memcpy_prot_cache
=
true
,
.
lli_buses
=
PL08X_AHB1
,
.
mem_buses
=
PL08X_AHB1
,
.
get_xfer_signal
=
pl080_get_signal
,
...
...
drivers/dma/Kconfig
浏览文件 @
98cd085e
...
...
@@ -62,8 +62,10 @@ config AMBA_PL08X
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
help
Platform has a PL08x DMAC device
which can provide DMA engine support
Say yes if your platform has a PL08x DMAC device which can
provide DMA engine support. This includes the original ARM
PL080 and PL081, Samsungs PL080 derivative and Faraday
Technology's FTDMAC020 PL080 derivative.
config AMCC_PPC440SPE_ADMA
tristate "AMCC PPC440SPe ADMA support"
...
...
drivers/dma/amba-pl08x.c
浏览文件 @
98cd085e
/*
* Copyright (c) 2006 ARM Ltd.
* Copyright (c) 2010 ST-Ericsson SA
* Copyirght (c) 2017 Linaro Ltd.
*
* Author: Peter Pearse <peter.pearse@arm.com>
* Author: Linus Walleij <linus.walleij@
stericsson.com
>
* Author: Linus Walleij <linus.walleij@
linaro.org
>
*
* 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
...
...
@@ -110,11 +111,12 @@ struct pl08x_driver_data;
* @channels: the number of channels available in this variant
* @signals: the number of request signals available from the hardware
* @dualmaster: whether this version supports dual AHB masters or not.
* @nomadik: whether the channels have Nomadik security extension bits
* that need to be checked for permission before use and some registers are
* missing
* @pl080s: whether this version is a PL080S, which has separate register and
* LLI word for transfer size.
* @nomadik: whether this variant is a ST Microelectronics Nomadik, where the
* channels have Nomadik security extension bits that need to be checked
* for permission before use and some registers are missing
* @pl080s: whether this variant is a Samsung PL080S, which has separate
* register and LLI word for transfer size.
* @ftdmac020: whether this variant is a Faraday Technology FTDMAC020
* @max_transfer_size: the maximum single element transfer size for this
* PL08x variant.
*/
...
...
@@ -125,6 +127,7 @@ struct vendor_data {
bool
dualmaster
;
bool
nomadik
;
bool
pl080s
;
bool
ftdmac020
;
u32
max_transfer_size
;
};
...
...
@@ -148,19 +151,34 @@ struct pl08x_bus_data {
* @id: physical index to this channel
* @base: memory base address for this physical channel
* @reg_config: configuration address for this physical channel
* @reg_control: control address for this physical channel
* @reg_src: transfer source address register
* @reg_dst: transfer destination address register
* @reg_lli: transfer LLI address register
* @reg_busy: if the variant has a special per-channel busy register,
* this contains a pointer to it
* @lock: a lock to use when altering an instance of this struct
* @serving: the virtual channel currently being served by this physical
* channel
* @locked: channel unavailable for the system, e.g. dedicated to secure
* world
* @ftdmac020: channel is on a FTDMAC020
* @pl080s: channel is on a PL08s
*/
struct
pl08x_phy_chan
{
unsigned
int
id
;
void
__iomem
*
base
;
void
__iomem
*
reg_config
;
void
__iomem
*
reg_control
;
void
__iomem
*
reg_src
;
void
__iomem
*
reg_dst
;
void
__iomem
*
reg_lli
;
void
__iomem
*
reg_busy
;
spinlock_t
lock
;
struct
pl08x_dma_chan
*
serving
;
bool
locked
;
bool
ftdmac020
;
bool
pl080s
;
};
/**
...
...
@@ -253,8 +271,9 @@ struct pl08x_dma_chan {
/**
* struct pl08x_driver_data - the local state holder for the PL08x
* @slave: slave engine for this instance
* @slave:
optional
slave engine for this instance
* @memcpy: memcpy engine for this instance
* @has_slave: the PL08x has a slave engine (routed signals)
* @base: virtual memory base (remapped) for the PL08x
* @adev: the corresponding AMBA (PrimeCell) bus entry
* @vd: vendor data for this PL08x variant
...
...
@@ -269,6 +288,7 @@ struct pl08x_dma_chan {
struct
pl08x_driver_data
{
struct
dma_device
slave
;
struct
dma_device
memcpy
;
bool
has_slave
;
void
__iomem
*
base
;
struct
amba_device
*
adev
;
const
struct
vendor_data
*
vd
;
...
...
@@ -360,10 +380,24 @@ static int pl08x_phy_channel_busy(struct pl08x_phy_chan *ch)
{
unsigned
int
val
;
/* If we have a special busy register, take a shortcut */
if
(
ch
->
reg_busy
)
{
val
=
readl
(
ch
->
reg_busy
);
return
!!
(
val
&
BIT
(
ch
->
id
));
}
val
=
readl
(
ch
->
reg_config
);
return
val
&
PL080_CONFIG_ACTIVE
;
}
/*
* pl08x_write_lli() - Write an LLI into the DMA controller.
*
* The PL08x derivatives support linked lists, but the first item of the
* list containing the source, destination, control word and next LLI is
* ignored. Instead the driver has to write those values directly into the
* SRC, DST, LLI and control registers. On FTDMAC020 also the SIZE
* register need to be set up for the first transfer.
*/
static
void
pl08x_write_lli
(
struct
pl08x_driver_data
*
pl08x
,
struct
pl08x_phy_chan
*
phychan
,
const
u32
*
lli
,
u32
ccfg
)
{
...
...
@@ -381,11 +415,112 @@ static void pl08x_write_lli(struct pl08x_driver_data *pl08x,
phychan
->
id
,
lli
[
PL080_LLI_SRC
],
lli
[
PL080_LLI_DST
],
lli
[
PL080_LLI_LLI
],
lli
[
PL080_LLI_CCTL
],
ccfg
);
writel_relaxed
(
lli
[
PL080_LLI_SRC
],
phychan
->
base
+
PL080_CH_SRC_ADDR
);
writel_relaxed
(
lli
[
PL080_LLI_DST
],
phychan
->
base
+
PL080_CH_DST_ADDR
);
writel_relaxed
(
lli
[
PL080_LLI_LLI
],
phychan
->
base
+
PL080_CH_LLI
);
writel_relaxed
(
lli
[
PL080_LLI_CCTL
],
phychan
->
base
+
PL080_CH_CONTROL
);
writel_relaxed
(
lli
[
PL080_LLI_SRC
],
phychan
->
reg_src
);
writel_relaxed
(
lli
[
PL080_LLI_DST
],
phychan
->
reg_dst
);
writel_relaxed
(
lli
[
PL080_LLI_LLI
],
phychan
->
reg_lli
);
/*
* The FTMAC020 has a different layout in the CCTL word of the LLI
* and the CCTL register which is split in CSR and SIZE registers.
* Convert the LLI item CCTL into the proper values to write into
* the CSR and SIZE registers.
*/
if
(
phychan
->
ftdmac020
)
{
u32
llictl
=
lli
[
PL080_LLI_CCTL
];
u32
val
=
0
;
/* Write the transfer size (12 bits) to the size register */
writel_relaxed
(
llictl
&
FTDMAC020_LLI_TRANSFER_SIZE_MASK
,
phychan
->
base
+
FTDMAC020_CH_SIZE
);
/*
* Then write the control bits 28..16 to the control register
* by shuffleing the bits around to where they are in the
* main register. The mapping is as follows:
* Bit 28: TC_MSK - mask on all except last LLI
* Bit 27..25: SRC_WIDTH
* Bit 24..22: DST_WIDTH
* Bit 21..20: SRCAD_CTRL
* Bit 19..17: DSTAD_CTRL
* Bit 17: SRC_SEL
* Bit 16: DST_SEL
*/
if
(
llictl
&
FTDMAC020_LLI_TC_MSK
)
val
|=
FTDMAC020_CH_CSR_TC_MSK
;
val
|=
((
llictl
&
FTDMAC020_LLI_SRC_WIDTH_MSK
)
>>
(
FTDMAC020_LLI_SRC_WIDTH_SHIFT
-
FTDMAC020_CH_CSR_SRC_WIDTH_SHIFT
));
val
|=
((
llictl
&
FTDMAC020_LLI_DST_WIDTH_MSK
)
>>
(
FTDMAC020_LLI_DST_WIDTH_SHIFT
-
FTDMAC020_CH_CSR_DST_WIDTH_SHIFT
));
val
|=
((
llictl
&
FTDMAC020_LLI_SRCAD_CTL_MSK
)
>>
(
FTDMAC020_LLI_SRCAD_CTL_SHIFT
-
FTDMAC020_CH_CSR_SRCAD_CTL_SHIFT
));
val
|=
((
llictl
&
FTDMAC020_LLI_DSTAD_CTL_MSK
)
>>
(
FTDMAC020_LLI_DSTAD_CTL_SHIFT
-
FTDMAC020_CH_CSR_DSTAD_CTL_SHIFT
));
if
(
llictl
&
FTDMAC020_LLI_SRC_SEL
)
val
|=
FTDMAC020_CH_CSR_SRC_SEL
;
if
(
llictl
&
FTDMAC020_LLI_DST_SEL
)
val
|=
FTDMAC020_CH_CSR_DST_SEL
;
/*
* Set up the bits that exist in the CSR but are not
* part the LLI, i.e. only gets written to the control
* register right here.
*
* FIXME: do not just handle memcpy, also handle slave DMA.
*/
switch
(
pl08x
->
pd
->
memcpy_burst_size
)
{
default:
case
PL08X_BURST_SZ_1
:
val
|=
PL080_BSIZE_1
<<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_4
:
val
|=
PL080_BSIZE_4
<<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_8
:
val
|=
PL080_BSIZE_8
<<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_16
:
val
|=
PL080_BSIZE_16
<<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_32
:
val
|=
PL080_BSIZE_32
<<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_64
:
val
|=
PL080_BSIZE_64
<<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_128
:
val
|=
PL080_BSIZE_128
<<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_256
:
val
|=
PL080_BSIZE_256
<<
FTDMAC020_CH_CSR_SRC_SIZE_SHIFT
;
break
;
}
/* Protection flags */
if
(
pl08x
->
pd
->
memcpy_prot_buff
)
val
|=
FTDMAC020_CH_CSR_PROT2
;
if
(
pl08x
->
pd
->
memcpy_prot_cache
)
val
|=
FTDMAC020_CH_CSR_PROT3
;
/* We are the kernel, so we are in privileged mode */
val
|=
FTDMAC020_CH_CSR_PROT1
;
writel_relaxed
(
val
,
phychan
->
reg_control
);
}
else
{
/* Bits are just identical */
writel_relaxed
(
lli
[
PL080_LLI_CCTL
],
phychan
->
reg_control
);
}
/* Second control word on the PL080s */
if
(
pl08x
->
vd
->
pl080s
)
writel_relaxed
(
lli
[
PL080S_LLI_CCTL2
],
phychan
->
base
+
PL080S_CH_CONTROL2
);
...
...
@@ -423,11 +558,25 @@ static void pl08x_start_next_txd(struct pl08x_dma_chan *plchan)
cpu_relax
();
/* Do not access config register until channel shows as inactive */
val
=
readl
(
phychan
->
reg_config
);
while
((
val
&
PL080_CONFIG_ACTIVE
)
||
(
val
&
PL080_CONFIG_ENABLE
))
if
(
phychan
->
ftdmac020
)
{
val
=
readl
(
phychan
->
reg_config
);
while
(
val
&
FTDMAC020_CH_CFG_BUSY
)
val
=
readl
(
phychan
->
reg_config
);
val
=
readl
(
phychan
->
reg_control
);
while
(
val
&
FTDMAC020_CH_CSR_EN
)
val
=
readl
(
phychan
->
reg_control
);
writel
(
val
|
FTDMAC020_CH_CSR_EN
,
phychan
->
reg_control
);
}
else
{
val
=
readl
(
phychan
->
reg_config
);
while
((
val
&
PL080_CONFIG_ACTIVE
)
||
(
val
&
PL080_CONFIG_ENABLE
))
val
=
readl
(
phychan
->
reg_config
);
writel
(
val
|
PL080_CONFIG_ENABLE
,
phychan
->
reg_config
);
writel
(
val
|
PL080_CONFIG_ENABLE
,
phychan
->
reg_config
);
}
}
/*
...
...
@@ -445,6 +594,14 @@ static void pl08x_pause_phy_chan(struct pl08x_phy_chan *ch)
u32
val
;
int
timeout
;
if
(
ch
->
ftdmac020
)
{
/* Use the enable bit on the FTDMAC020 */
val
=
readl
(
ch
->
reg_control
);
val
&=
~
FTDMAC020_CH_CSR_EN
;
writel
(
val
,
ch
->
reg_control
);
return
;
}
/* Set the HALT bit and wait for the FIFO to drain */
val
=
readl
(
ch
->
reg_config
);
val
|=
PL080_CONFIG_HALT
;
...
...
@@ -464,6 +621,14 @@ static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch)
{
u32
val
;
/* Use the enable bit on the FTDMAC020 */
if
(
ch
->
ftdmac020
)
{
val
=
readl
(
ch
->
reg_control
);
val
|=
FTDMAC020_CH_CSR_EN
;
writel
(
val
,
ch
->
reg_control
);
return
;
}
/* Clear the HALT bit */
val
=
readl
(
ch
->
reg_config
);
val
&=
~
PL080_CONFIG_HALT
;
...
...
@@ -479,25 +644,68 @@ static void pl08x_resume_phy_chan(struct pl08x_phy_chan *ch)
static
void
pl08x_terminate_phy_chan
(
struct
pl08x_driver_data
*
pl08x
,
struct
pl08x_phy_chan
*
ch
)
{
u32
val
=
readl
(
ch
->
reg_config
)
;
u32
val
;
/* The layout for the FTDMAC020 is different */
if
(
ch
->
ftdmac020
)
{
/* Disable all interrupts */
val
=
readl
(
ch
->
reg_config
);
val
|=
(
FTDMAC020_CH_CFG_INT_ABT_MASK
|
FTDMAC020_CH_CFG_INT_ERR_MASK
|
FTDMAC020_CH_CFG_INT_TC_MASK
);
writel
(
val
,
ch
->
reg_config
);
/* Abort and disable channel */
val
=
readl
(
ch
->
reg_control
);
val
&=
~
FTDMAC020_CH_CSR_EN
;
val
|=
FTDMAC020_CH_CSR_ABT
;
writel
(
val
,
ch
->
reg_control
);
/* Clear ABT and ERR interrupt flags */
writel
(
BIT
(
ch
->
id
)
|
BIT
(
ch
->
id
+
16
),
pl08x
->
base
+
PL080_ERR_CLEAR
);
writel
(
BIT
(
ch
->
id
),
pl08x
->
base
+
PL080_TC_CLEAR
);
return
;
}
val
=
readl
(
ch
->
reg_config
);
val
&=
~
(
PL080_CONFIG_ENABLE
|
PL080_CONFIG_ERR_IRQ_MASK
|
PL080_CONFIG_TC_IRQ_MASK
);
writel
(
val
,
ch
->
reg_config
);
writel
(
BIT
(
ch
->
id
),
pl08x
->
base
+
PL080_ERR_CLEAR
);
writel
(
BIT
(
ch
->
id
),
pl08x
->
base
+
PL080_TC_CLEAR
);
}
static
inline
u32
get_bytes_in_cctl
(
u32
cctl
)
static
u32
get_bytes_in_phy_channel
(
struct
pl08x_phy_chan
*
ch
)
{
/* The source width defines the number of bytes */
u32
bytes
=
cctl
&
PL080_CONTROL_TRANSFER_SIZE_MASK
;
u32
val
;
u32
bytes
;
if
(
ch
->
ftdmac020
)
{
bytes
=
readl
(
ch
->
base
+
FTDMAC020_CH_SIZE
);
cctl
&=
PL080_CONTROL_SWIDTH_MASK
;
val
=
readl
(
ch
->
reg_control
);
val
&=
FTDMAC020_CH_CSR_SRC_WIDTH_MSK
;
val
>>=
FTDMAC020_CH_CSR_SRC_WIDTH_SHIFT
;
}
else
if
(
ch
->
pl080s
)
{
val
=
readl
(
ch
->
base
+
PL080S_CH_CONTROL2
);
bytes
=
val
&
PL080S_CONTROL_TRANSFER_SIZE_MASK
;
switch
(
cctl
>>
PL080_CONTROL_SWIDTH_SHIFT
)
{
val
=
readl
(
ch
->
reg_control
);
val
&=
PL080_CONTROL_SWIDTH_MASK
;
val
>>=
PL080_CONTROL_SWIDTH_SHIFT
;
}
else
{
/* Plain PL08x */
val
=
readl
(
ch
->
reg_control
);
bytes
=
val
&
PL080_CONTROL_TRANSFER_SIZE_MASK
;
val
&=
PL080_CONTROL_SWIDTH_MASK
;
val
>>=
PL080_CONTROL_SWIDTH_SHIFT
;
}
switch
(
val
)
{
case
PL080_WIDTH_8BIT
:
break
;
case
PL080_WIDTH_16BIT
:
...
...
@@ -510,14 +718,35 @@ static inline u32 get_bytes_in_cctl(u32 cctl)
return
bytes
;
}
static
inline
u32
get_bytes_in_cctl_pl080s
(
u32
cctl
,
u32
cctl1
)
static
u32
get_bytes_in_lli
(
struct
pl08x_phy_chan
*
ch
,
const
u32
*
llis_va
)
{
/* The source width defines the number of bytes */
u32
bytes
=
cctl1
&
PL080S_CONTROL_TRANSFER_SIZE_MASK
;
u32
val
;
u32
bytes
;
if
(
ch
->
ftdmac020
)
{
val
=
llis_va
[
PL080_LLI_CCTL
];
bytes
=
val
&
FTDMAC020_LLI_TRANSFER_SIZE_MASK
;
val
=
llis_va
[
PL080_LLI_CCTL
];
val
&=
FTDMAC020_LLI_SRC_WIDTH_MSK
;
val
>>=
FTDMAC020_LLI_SRC_WIDTH_SHIFT
;
}
else
if
(
ch
->
pl080s
)
{
val
=
llis_va
[
PL080S_LLI_CCTL2
];
bytes
=
val
&
PL080S_CONTROL_TRANSFER_SIZE_MASK
;
val
=
llis_va
[
PL080_LLI_CCTL
];
val
&=
PL080_CONTROL_SWIDTH_MASK
;
val
>>=
PL080_CONTROL_SWIDTH_SHIFT
;
}
else
{
/* Plain PL08x */
val
=
llis_va
[
PL080_LLI_CCTL
];
bytes
=
val
&
PL080_CONTROL_TRANSFER_SIZE_MASK
;
cctl
&=
PL080_CONTROL_SWIDTH_MASK
;
val
&=
PL080_CONTROL_SWIDTH_MASK
;
val
>>=
PL080_CONTROL_SWIDTH_SHIFT
;
}
switch
(
cctl
>>
PL080_CONTROL_SWIDTH_SHIFT
)
{
switch
(
val
)
{
case
PL080_WIDTH_8BIT
:
break
;
case
PL080_WIDTH_16BIT
:
...
...
@@ -552,15 +781,10 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
* Follow the LLIs to get the number of remaining
* bytes in the currently active transaction.
*/
clli
=
readl
(
ch
->
base
+
PL080_CH_LLI
)
&
~
PL080_LLI_LM_AHB2
;
clli
=
readl
(
ch
->
reg_lli
)
&
~
PL080_LLI_LM_AHB2
;
/* First get the remaining bytes in the active transfer */
if
(
pl08x
->
vd
->
pl080s
)
bytes
=
get_bytes_in_cctl_pl080s
(
readl
(
ch
->
base
+
PL080_CH_CONTROL
),
readl
(
ch
->
base
+
PL080S_CH_CONTROL2
));
else
bytes
=
get_bytes_in_cctl
(
readl
(
ch
->
base
+
PL080_CH_CONTROL
));
bytes
=
get_bytes_in_phy_channel
(
ch
);
if
(
!
clli
)
return
bytes
;
...
...
@@ -581,12 +805,7 @@ static u32 pl08x_getbytes_chan(struct pl08x_dma_chan *plchan)
llis_va_limit
=
llis_va
+
llis_max_words
;
for
(;
llis_va
<
llis_va_limit
;
llis_va
+=
pl08x
->
lli_words
)
{
if
(
pl08x
->
vd
->
pl080s
)
bytes
+=
get_bytes_in_cctl_pl080s
(
llis_va
[
PL080_LLI_CCTL
],
llis_va
[
PL080S_LLI_CCTL2
]);
else
bytes
+=
get_bytes_in_cctl
(
llis_va
[
PL080_LLI_CCTL
]);
bytes
+=
get_bytes_in_lli
(
ch
,
llis_va
);
/*
* A LLI pointer going backward terminates the LLI list
...
...
@@ -705,7 +924,7 @@ static void pl08x_phy_free(struct pl08x_dma_chan *plchan)
break
;
}
if
(
!
next
)
{
if
(
!
next
&&
pl08x
->
has_slave
)
{
list_for_each_entry
(
p
,
&
pl08x
->
slave
.
channels
,
vc
.
chan
.
device_node
)
if
(
p
->
state
==
PL08X_CHAN_WAITING
)
{
next
=
p
;
...
...
@@ -746,9 +965,30 @@ static void pl08x_phy_free(struct pl08x_dma_chan *plchan)
* LLI handling
*/
static
inline
unsigned
int
pl08x_get_bytes_for_cctl
(
unsigned
int
coded
)
static
inline
unsigned
int
pl08x_get_bytes_for_lli
(
struct
pl08x_driver_data
*
pl08x
,
u32
cctl
,
bool
source
)
{
switch
(
coded
)
{
u32
val
;
if
(
pl08x
->
vd
->
ftdmac020
)
{
if
(
source
)
val
=
(
cctl
&
FTDMAC020_LLI_SRC_WIDTH_MSK
)
>>
FTDMAC020_LLI_SRC_WIDTH_SHIFT
;
else
val
=
(
cctl
&
FTDMAC020_LLI_DST_WIDTH_MSK
)
>>
FTDMAC020_LLI_DST_WIDTH_SHIFT
;
}
else
{
if
(
source
)
val
=
(
cctl
&
PL080_CONTROL_SWIDTH_MASK
)
>>
PL080_CONTROL_SWIDTH_SHIFT
;
else
val
=
(
cctl
&
PL080_CONTROL_DWIDTH_MASK
)
>>
PL080_CONTROL_DWIDTH_SHIFT
;
}
switch
(
val
)
{
case
PL080_WIDTH_8BIT
:
return
1
;
case
PL080_WIDTH_16BIT
:
...
...
@@ -762,49 +1002,106 @@ static inline unsigned int pl08x_get_bytes_for_cctl(unsigned int coded)
return
0
;
}
static
inline
u32
pl08x_cctl_bits
(
u32
cctl
,
u8
srcwidth
,
u8
dstwidth
,
size_t
tsize
)
static
inline
u32
pl08x_lli_control_bits
(
struct
pl08x_driver_data
*
pl08x
,
u32
cctl
,
u8
srcwidth
,
u8
dstwidth
,
size_t
tsize
)
{
u32
retbits
=
cctl
;
/* Remove all src, dst and transfer size bits */
retbits
&=
~
PL080_CONTROL_DWIDTH_MASK
;
retbits
&=
~
PL080_CONTROL_SWIDTH_MASK
;
retbits
&=
~
PL080_CONTROL_TRANSFER_SIZE_MASK
;
/*
* Remove all src, dst and transfer size bits, then set the
* width and size according to the parameters. The bit offsets
* are different in the FTDMAC020 so we need to accound for this.
*/
if
(
pl08x
->
vd
->
ftdmac020
)
{
retbits
&=
~
FTDMAC020_LLI_DST_WIDTH_MSK
;
retbits
&=
~
FTDMAC020_LLI_SRC_WIDTH_MSK
;
retbits
&=
~
FTDMAC020_LLI_TRANSFER_SIZE_MASK
;
switch
(
srcwidth
)
{
case
1
:
retbits
|=
PL080_WIDTH_8BIT
<<
FTDMAC020_LLI_SRC_WIDTH_SHIFT
;
break
;
case
2
:
retbits
|=
PL080_WIDTH_16BIT
<<
FTDMAC020_LLI_SRC_WIDTH_SHIFT
;
break
;
case
4
:
retbits
|=
PL080_WIDTH_32BIT
<<
FTDMAC020_LLI_SRC_WIDTH_SHIFT
;
break
;
default:
BUG
();
break
;
}
/* Then set the bits according to the parameters */
switch
(
srcwidth
)
{
case
1
:
retbits
|=
PL080_WIDTH_8BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
;
break
;
case
2
:
retbits
|=
PL080_WIDTH_16BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
;
break
;
case
4
:
retbits
|=
PL080_WIDTH_32BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
;
break
;
default:
BUG
();
break
;
}
switch
(
dstwidth
)
{
case
1
:
retbits
|=
PL080_WIDTH_8BIT
<<
FTDMAC020_LLI_DST_WIDTH_SHIFT
;
break
;
case
2
:
retbits
|=
PL080_WIDTH_16BIT
<<
FTDMAC020_LLI_DST_WIDTH_SHIFT
;
break
;
case
4
:
retbits
|=
PL080_WIDTH_32BIT
<<
FTDMAC020_LLI_DST_WIDTH_SHIFT
;
break
;
default:
BUG
();
break
;
}
switch
(
dstwidth
)
{
case
1
:
retbits
|=
PL080_WIDTH_8BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
;
break
;
case
2
:
retbits
|=
PL080_WIDTH_16BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
;
break
;
case
4
:
retbits
|=
PL080_WIDTH_32BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
;
break
;
default:
BUG
();
break
;
tsize
&=
FTDMAC020_LLI_TRANSFER_SIZE_MASK
;
retbits
|=
tsize
<<
FTDMAC020_LLI_TRANSFER_SIZE_SHIFT
;
}
else
{
retbits
&=
~
PL080_CONTROL_DWIDTH_MASK
;
retbits
&=
~
PL080_CONTROL_SWIDTH_MASK
;
retbits
&=
~
PL080_CONTROL_TRANSFER_SIZE_MASK
;
switch
(
srcwidth
)
{
case
1
:
retbits
|=
PL080_WIDTH_8BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
;
break
;
case
2
:
retbits
|=
PL080_WIDTH_16BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
;
break
;
case
4
:
retbits
|=
PL080_WIDTH_32BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
;
break
;
default:
BUG
();
break
;
}
switch
(
dstwidth
)
{
case
1
:
retbits
|=
PL080_WIDTH_8BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
;
break
;
case
2
:
retbits
|=
PL080_WIDTH_16BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
;
break
;
case
4
:
retbits
|=
PL080_WIDTH_32BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
;
break
;
default:
BUG
();
break
;
}
tsize
&=
PL080_CONTROL_TRANSFER_SIZE_MASK
;
retbits
|=
tsize
<<
PL080_CONTROL_TRANSFER_SIZE_SHIFT
;
}
tsize
&=
PL080_CONTROL_TRANSFER_SIZE_MASK
;
retbits
|=
tsize
<<
PL080_CONTROL_TRANSFER_SIZE_SHIFT
;
return
retbits
;
}
...
...
@@ -825,13 +1122,35 @@ struct pl08x_lli_build_data {
* - prefers the destination bus if both available
* - prefers bus with fixed address (i.e. peripheral)
*/
static
void
pl08x_choose_master_bus
(
struct
pl08x_lli_build_data
*
bd
,
struct
pl08x_bus_data
**
mbus
,
struct
pl08x_bus_data
**
sbus
,
u32
cctl
)
static
void
pl08x_choose_master_bus
(
struct
pl08x_driver_data
*
pl08x
,
struct
pl08x_lli_build_data
*
bd
,
struct
pl08x_bus_data
**
mbus
,
struct
pl08x_bus_data
**
sbus
,
u32
cctl
)
{
if
(
!
(
cctl
&
PL080_CONTROL_DST_INCR
))
{
bool
dst_incr
;
bool
src_incr
;
/*
* The FTDMAC020 only supports memory-to-memory transfer, so
* source and destination always increase.
*/
if
(
pl08x
->
vd
->
ftdmac020
)
{
dst_incr
=
true
;
src_incr
=
true
;
}
else
{
dst_incr
=
!!
(
cctl
&
PL080_CONTROL_DST_INCR
);
src_incr
=
!!
(
cctl
&
PL080_CONTROL_SRC_INCR
);
}
/*
* If either bus is not advancing, i.e. it is a peripheral, that
* one becomes master
*/
if
(
!
dst_incr
)
{
*
mbus
=
&
bd
->
dstbus
;
*
sbus
=
&
bd
->
srcbus
;
}
else
if
(
!
(
cctl
&
PL080_CONTROL_SRC_INCR
)
)
{
}
else
if
(
!
src_incr
)
{
*
mbus
=
&
bd
->
srcbus
;
*
sbus
=
&
bd
->
dstbus
;
}
else
{
...
...
@@ -869,10 +1188,16 @@ static void pl08x_fill_lli_for_desc(struct pl08x_driver_data *pl08x,
if
(
pl08x
->
vd
->
pl080s
)
llis_va
[
PL080S_LLI_CCTL2
]
=
cctl2
;
if
(
cctl
&
PL080_CONTROL_SRC_INCR
)
if
(
pl08x
->
vd
->
ftdmac020
)
{
/* FIXME: only memcpy so far so both increase */
bd
->
srcbus
.
addr
+=
len
;
if
(
cctl
&
PL080_CONTROL_DST_INCR
)
bd
->
dstbus
.
addr
+=
len
;
}
else
{
if
(
cctl
&
PL080_CONTROL_SRC_INCR
)
bd
->
srcbus
.
addr
+=
len
;
if
(
cctl
&
PL080_CONTROL_DST_INCR
)
bd
->
dstbus
.
addr
+=
len
;
}
BUG_ON
(
bd
->
remainder
<
len
);
...
...
@@ -883,12 +1208,12 @@ static inline void prep_byte_width_lli(struct pl08x_driver_data *pl08x,
struct
pl08x_lli_build_data
*
bd
,
u32
*
cctl
,
u32
len
,
int
num_llis
,
size_t
*
total_bytes
)
{
*
cctl
=
pl08x_
cctl_bits
(
*
cctl
,
1
,
1
,
len
);
*
cctl
=
pl08x_
lli_control_bits
(
pl08x
,
*
cctl
,
1
,
1
,
len
);
pl08x_fill_lli_for_desc
(
pl08x
,
bd
,
num_llis
,
len
,
*
cctl
,
len
);
(
*
total_bytes
)
+=
len
;
}
#if
def VERBOSE_DEBUG
#if
1
static
void
pl08x_dump_lli
(
struct
pl08x_driver_data
*
pl08x
,
const
u32
*
llis_va
,
int
num_llis
)
{
...
...
@@ -953,14 +1278,10 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
cctl
=
txd
->
cctl
;
/* Find maximum width of the source bus */
bd
.
srcbus
.
maxwidth
=
pl08x_get_bytes_for_cctl
((
cctl
&
PL080_CONTROL_SWIDTH_MASK
)
>>
PL080_CONTROL_SWIDTH_SHIFT
);
bd
.
srcbus
.
maxwidth
=
pl08x_get_bytes_for_lli
(
pl08x
,
cctl
,
true
);
/* Find maximum width of the destination bus */
bd
.
dstbus
.
maxwidth
=
pl08x_get_bytes_for_cctl
((
cctl
&
PL080_CONTROL_DWIDTH_MASK
)
>>
PL080_CONTROL_DWIDTH_SHIFT
);
bd
.
dstbus
.
maxwidth
=
pl08x_get_bytes_for_lli
(
pl08x
,
cctl
,
false
);
list_for_each_entry
(
dsg
,
&
txd
->
dsg_list
,
node
)
{
total_bytes
=
0
;
...
...
@@ -972,7 +1293,7 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
bd
.
srcbus
.
buswidth
=
bd
.
srcbus
.
maxwidth
;
bd
.
dstbus
.
buswidth
=
bd
.
dstbus
.
maxwidth
;
pl08x_choose_master_bus
(
&
bd
,
&
mbus
,
&
sbus
,
cctl
);
pl08x_choose_master_bus
(
pl08x
,
&
bd
,
&
mbus
,
&
sbus
,
cctl
);
dev_vdbg
(
&
pl08x
->
adev
->
dev
,
"src=0x%08llx%s/%u dst=0x%08llx%s/%u len=%zu
\n
"
,
...
...
@@ -1009,8 +1330,14 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
* supported. Thus, we can't have scattered addresses.
*/
if
(
!
bd
.
remainder
)
{
u32
fc
=
(
txd
->
ccfg
&
PL080_CONFIG_FLOW_CONTROL_MASK
)
>>
PL080_CONFIG_FLOW_CONTROL_SHIFT
;
u32
fc
;
/* FTDMAC020 only does memory-to-memory */
if
(
pl08x
->
vd
->
ftdmac020
)
fc
=
PL080_FLOW_MEM2MEM
;
else
fc
=
(
txd
->
ccfg
&
PL080_CONFIG_FLOW_CONTROL_MASK
)
>>
PL080_CONFIG_FLOW_CONTROL_SHIFT
;
if
(
!
((
fc
>=
PL080_FLOW_SRC2DST_DST
)
&&
(
fc
<=
PL080_FLOW_SRC2DST_SRC
)))
{
dev_err
(
&
pl08x
->
adev
->
dev
,
"%s sg len can't be zero"
,
...
...
@@ -1027,8 +1354,9 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
return
0
;
}
cctl
=
pl08x_cctl_bits
(
cctl
,
bd
.
srcbus
.
buswidth
,
bd
.
dstbus
.
buswidth
,
0
);
cctl
=
pl08x_lli_control_bits
(
pl08x
,
cctl
,
bd
.
srcbus
.
buswidth
,
bd
.
dstbus
.
buswidth
,
0
);
pl08x_fill_lli_for_desc
(
pl08x
,
&
bd
,
num_llis
++
,
0
,
cctl
,
0
);
break
;
...
...
@@ -1107,8 +1435,9 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
"size 0x%08zx (remainder 0x%08zx)
\n
"
,
__func__
,
lli_len
,
bd
.
remainder
);
cctl
=
pl08x_cctl_bits
(
cctl
,
bd
.
srcbus
.
buswidth
,
bd
.
dstbus
.
buswidth
,
tsize
);
cctl
=
pl08x_lli_control_bits
(
pl08x
,
cctl
,
bd
.
srcbus
.
buswidth
,
bd
.
dstbus
.
buswidth
,
tsize
);
pl08x_fill_lli_for_desc
(
pl08x
,
&
bd
,
num_llis
++
,
lli_len
,
cctl
,
tsize
);
total_bytes
+=
lli_len
;
...
...
@@ -1151,7 +1480,10 @@ static int pl08x_fill_llis_for_desc(struct pl08x_driver_data *pl08x,
/* The final LLI terminates the LLI. */
last_lli
[
PL080_LLI_LLI
]
=
0
;
/* The final LLI element shall also fire an interrupt. */
last_lli
[
PL080_LLI_CCTL
]
|=
PL080_CONTROL_TC_IRQ_EN
;
if
(
pl08x
->
vd
->
ftdmac020
)
last_lli
[
PL080_LLI_CCTL
]
&=
~
FTDMAC020_LLI_TC_MSK
;
else
last_lli
[
PL080_LLI_CCTL
]
|=
PL080_CONTROL_TC_IRQ_EN
;
}
pl08x_dump_lli
(
pl08x
,
llis_va
,
num_llis
);
...
...
@@ -1317,14 +1649,25 @@ static const struct burst_table burst_sizes[] = {
* will be routed to each port. We try to have source and destination
* on separate ports, but always respect the allowable settings.
*/
static
u32
pl08x_select_bus
(
u8
src
,
u8
dst
)
static
u32
pl08x_select_bus
(
bool
ftdmac020
,
u8
src
,
u8
dst
)
{
u32
cctl
=
0
;
u32
dst_ahb2
;
u32
src_ahb2
;
/* The FTDMAC020 use different bits to indicate src/dst bus */
if
(
ftdmac020
)
{
dst_ahb2
=
FTDMAC020_LLI_DST_SEL
;
src_ahb2
=
FTDMAC020_LLI_SRC_SEL
;
}
else
{
dst_ahb2
=
PL080_CONTROL_DST_AHB2
;
src_ahb2
=
PL080_CONTROL_SRC_AHB2
;
}
if
(
!
(
dst
&
PL08X_AHB1
)
||
((
dst
&
PL08X_AHB2
)
&&
(
src
&
PL08X_AHB1
)))
cctl
|=
PL080_CONTROL_DST_AHB
2
;
cctl
|=
dst_ahb
2
;
if
(
!
(
src
&
PL08X_AHB1
)
||
((
src
&
PL08X_AHB2
)
&&
!
(
dst
&
PL08X_AHB2
)))
cctl
|=
PL080_CONTROL_SRC_AHB
2
;
cctl
|=
src_ahb
2
;
return
cctl
;
}
...
...
@@ -1412,14 +1755,134 @@ static struct pl08x_txd *pl08x_get_txd(struct pl08x_dma_chan *plchan)
{
struct
pl08x_txd
*
txd
=
kzalloc
(
sizeof
(
*
txd
),
GFP_NOWAIT
);
if
(
txd
)
{
if
(
txd
)
INIT_LIST_HEAD
(
&
txd
->
dsg_list
);
return
txd
;
}
/* Always enable error and terminal interrupts */
txd
->
ccfg
=
PL080_CONFIG_ERR_IRQ_MASK
|
PL080_CONFIG_TC_IRQ_MASK
;
static
u32
pl08x_memcpy_cctl
(
struct
pl08x_driver_data
*
pl08x
)
{
u32
cctl
=
0
;
/* Conjure cctl */
switch
(
pl08x
->
pd
->
memcpy_burst_size
)
{
default:
dev_err
(
&
pl08x
->
adev
->
dev
,
"illegal burst size for memcpy, set to 1
\n
"
);
/* Fall through */
case
PL08X_BURST_SZ_1
:
cctl
|=
PL080_BSIZE_1
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_1
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_4
:
cctl
|=
PL080_BSIZE_4
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_4
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_8
:
cctl
|=
PL080_BSIZE_8
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_8
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_16
:
cctl
|=
PL080_BSIZE_16
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_16
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_32
:
cctl
|=
PL080_BSIZE_32
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_32
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_64
:
cctl
|=
PL080_BSIZE_64
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_64
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_128
:
cctl
|=
PL080_BSIZE_128
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_128
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
break
;
case
PL08X_BURST_SZ_256
:
cctl
|=
PL080_BSIZE_256
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_256
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
break
;
}
return
txd
;
switch
(
pl08x
->
pd
->
memcpy_bus_width
)
{
default:
dev_err
(
&
pl08x
->
adev
->
dev
,
"illegal bus width for memcpy, set to 8 bits
\n
"
);
/* Fall through */
case
PL08X_BUS_WIDTH_8_BITS
:
cctl
|=
PL080_WIDTH_8BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
|
PL080_WIDTH_8BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
;
break
;
case
PL08X_BUS_WIDTH_16_BITS
:
cctl
|=
PL080_WIDTH_16BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
|
PL080_WIDTH_16BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
;
break
;
case
PL08X_BUS_WIDTH_32_BITS
:
cctl
|=
PL080_WIDTH_32BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
|
PL080_WIDTH_32BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
;
break
;
}
/* Protection flags */
if
(
pl08x
->
pd
->
memcpy_prot_buff
)
cctl
|=
PL080_CONTROL_PROT_BUFF
;
if
(
pl08x
->
pd
->
memcpy_prot_cache
)
cctl
|=
PL080_CONTROL_PROT_CACHE
;
/* We are the kernel, so we are in privileged mode */
cctl
|=
PL080_CONTROL_PROT_SYS
;
/* Both to be incremented or the code will break */
cctl
|=
PL080_CONTROL_SRC_INCR
|
PL080_CONTROL_DST_INCR
;
if
(
pl08x
->
vd
->
dualmaster
)
cctl
|=
pl08x_select_bus
(
false
,
pl08x
->
mem_buses
,
pl08x
->
mem_buses
);
return
cctl
;
}
static
u32
pl08x_ftdmac020_memcpy_cctl
(
struct
pl08x_driver_data
*
pl08x
)
{
u32
cctl
=
0
;
/* Conjure cctl */
switch
(
pl08x
->
pd
->
memcpy_bus_width
)
{
default:
dev_err
(
&
pl08x
->
adev
->
dev
,
"illegal bus width for memcpy, set to 8 bits
\n
"
);
/* Fall through */
case
PL08X_BUS_WIDTH_8_BITS
:
cctl
|=
PL080_WIDTH_8BIT
<<
FTDMAC020_LLI_SRC_WIDTH_SHIFT
|
PL080_WIDTH_8BIT
<<
FTDMAC020_LLI_DST_WIDTH_SHIFT
;
break
;
case
PL08X_BUS_WIDTH_16_BITS
:
cctl
|=
PL080_WIDTH_16BIT
<<
FTDMAC020_LLI_SRC_WIDTH_SHIFT
|
PL080_WIDTH_16BIT
<<
FTDMAC020_LLI_DST_WIDTH_SHIFT
;
break
;
case
PL08X_BUS_WIDTH_32_BITS
:
cctl
|=
PL080_WIDTH_32BIT
<<
FTDMAC020_LLI_SRC_WIDTH_SHIFT
|
PL080_WIDTH_32BIT
<<
FTDMAC020_LLI_DST_WIDTH_SHIFT
;
break
;
}
/*
* By default mask the TC IRQ on all LLIs, it will be unmasked on
* the last LLI item by other code.
*/
cctl
|=
FTDMAC020_LLI_TC_MSK
;
/*
* Both to be incremented so leave bits FTDMAC020_LLI_SRCAD_CTL
* and FTDMAC020_LLI_DSTAD_CTL as zero
*/
if
(
pl08x
->
vd
->
dualmaster
)
cctl
|=
pl08x_select_bus
(
true
,
pl08x
->
mem_buses
,
pl08x
->
mem_buses
);
return
cctl
;
}
/*
...
...
@@ -1452,18 +1915,16 @@ static struct dma_async_tx_descriptor *pl08x_prep_dma_memcpy(
dsg
->
src_addr
=
src
;
dsg
->
dst_addr
=
dest
;
dsg
->
len
=
len
;
/* Set platform data for m2m */
txd
->
ccfg
|=
PL080_FLOW_MEM2MEM
<<
PL080_CONFIG_FLOW_CONTROL_SHIFT
;
txd
->
cctl
=
pl08x
->
pd
->
memcpy_channel
.
cctl_memcpy
&
~
(
PL080_CONTROL_DST_AHB2
|
PL080_CONTROL_SRC_AHB2
);
/* Both to be incremented or the code will break */
txd
->
cctl
|=
PL080_CONTROL_SRC_INCR
|
PL080_CONTROL_DST_INCR
;
if
(
pl08x
->
vd
->
dualmaster
)
txd
->
cctl
|=
pl08x_select_bus
(
pl08x
->
mem_buses
,
pl08x
->
mem_buses
);
if
(
pl08x
->
vd
->
ftdmac020
)
{
/* Writing CCFG zero ENABLES all interrupts */
txd
->
ccfg
=
0
;
txd
->
cctl
=
pl08x_ftdmac020_memcpy_cctl
(
pl08x
);
}
else
{
txd
->
ccfg
=
PL080_CONFIG_ERR_IRQ_MASK
|
PL080_CONFIG_TC_IRQ_MASK
|
PL080_FLOW_MEM2MEM
<<
PL080_CONFIG_FLOW_CONTROL_SHIFT
;
txd
->
cctl
=
pl08x_memcpy_cctl
(
pl08x
);
}
ret
=
pl08x_fill_llis_for_desc
(
plchan
->
host
,
txd
);
if
(
!
ret
)
{
...
...
@@ -1527,7 +1988,7 @@ static struct pl08x_txd *pl08x_init_txd(
return
NULL
;
}
txd
->
cctl
=
cctl
|
pl08x_select_bus
(
src_buses
,
dst_buses
);
txd
->
cctl
=
cctl
|
pl08x_select_bus
(
false
,
src_buses
,
dst_buses
);
if
(
plchan
->
cfg
.
device_fc
)
tmp
=
(
direction
==
DMA_MEM_TO_DEV
)
?
PL080_FLOW_MEM2PER_PER
:
...
...
@@ -1536,7 +1997,9 @@ static struct pl08x_txd *pl08x_init_txd(
tmp
=
(
direction
==
DMA_MEM_TO_DEV
)
?
PL080_FLOW_MEM2PER
:
PL080_FLOW_PER2MEM
;
txd
->
ccfg
|=
tmp
<<
PL080_CONFIG_FLOW_CONTROL_SHIFT
;
txd
->
ccfg
=
PL080_CONFIG_ERR_IRQ_MASK
|
PL080_CONFIG_TC_IRQ_MASK
|
tmp
<<
PL080_CONFIG_FLOW_CONTROL_SHIFT
;
ret
=
pl08x_request_mux
(
plchan
);
if
(
ret
<
0
)
{
...
...
@@ -1813,6 +2276,11 @@ static void pl08x_ensure_on(struct pl08x_driver_data *pl08x)
/* The Nomadik variant does not have the config register */
if
(
pl08x
->
vd
->
nomadik
)
return
;
/* The FTDMAC020 variant does this in another register */
if
(
pl08x
->
vd
->
ftdmac020
)
{
writel
(
PL080_CONFIG_ENABLE
,
pl08x
->
base
+
FTDMAC020_CSR
);
return
;
}
writel
(
PL080_CONFIG_ENABLE
,
pl08x
->
base
+
PL080_CONFIG
);
}
...
...
@@ -1925,9 +2393,16 @@ static int pl08x_dma_init_virtual_channels(struct pl08x_driver_data *pl08x,
chan
->
signal
=
i
;
pl08x_dma_slave_init
(
chan
);
}
else
{
chan
->
cd
=
&
pl08x
->
pd
->
memcpy_channel
;
chan
->
cd
=
kzalloc
(
sizeof
(
*
chan
->
cd
),
GFP_KERNEL
);
if
(
!
chan
->
cd
)
{
kfree
(
chan
);
return
-
ENOMEM
;
}
chan
->
cd
->
bus_id
=
"memcpy"
;
chan
->
cd
->
periph_buses
=
pl08x
->
pd
->
mem_buses
;
chan
->
name
=
kasprintf
(
GFP_KERNEL
,
"memcpy%d"
,
i
);
if
(
!
chan
->
name
)
{
kfree
(
chan
->
cd
);
kfree
(
chan
);
return
-
ENOMEM
;
}
...
...
@@ -2009,12 +2484,15 @@ static int pl08x_debugfs_show(struct seq_file *s, void *data)
pl08x_state_str
(
chan
->
state
));
}
seq_printf
(
s
,
"
\n
PL08x virtual slave channels:
\n
"
);
seq_printf
(
s
,
"CHANNEL:
\t
STATE:
\n
"
);
seq_printf
(
s
,
"--------
\t
------
\n
"
);
list_for_each_entry
(
chan
,
&
pl08x
->
slave
.
channels
,
vc
.
chan
.
device_node
)
{
seq_printf
(
s
,
"%s
\t\t
%s
\n
"
,
chan
->
name
,
pl08x_state_str
(
chan
->
state
));
if
(
pl08x
->
has_slave
)
{
seq_printf
(
s
,
"
\n
PL08x virtual slave channels:
\n
"
);
seq_printf
(
s
,
"CHANNEL:
\t
STATE:
\n
"
);
seq_printf
(
s
,
"--------
\t
------
\n
"
);
list_for_each_entry
(
chan
,
&
pl08x
->
slave
.
channels
,
vc
.
chan
.
device_node
)
{
seq_printf
(
s
,
"%s
\t\t
%s
\n
"
,
chan
->
name
,
pl08x_state_str
(
chan
->
state
));
}
}
return
0
;
...
...
@@ -2052,6 +2530,10 @@ static struct dma_chan *pl08x_find_chan_id(struct pl08x_driver_data *pl08x,
{
struct
pl08x_dma_chan
*
chan
;
/* Trying to get a slave channel from something with no slave support */
if
(
!
pl08x
->
has_slave
)
return
NULL
;
list_for_each_entry
(
chan
,
&
pl08x
->
slave
.
channels
,
vc
.
chan
.
device_node
)
{
if
(
chan
->
signal
==
id
)
return
&
chan
->
vc
.
chan
;
...
...
@@ -2099,7 +2581,6 @@ static int pl08x_of_probe(struct amba_device *adev,
{
struct
pl08x_platform_data
*
pd
;
struct
pl08x_channel_data
*
chanp
=
NULL
;
u32
cctl_memcpy
=
0
;
u32
val
;
int
ret
;
int
i
;
...
...
@@ -2139,36 +2620,28 @@ static int pl08x_of_probe(struct amba_device *adev,
dev_err
(
&
adev
->
dev
,
"illegal burst size for memcpy, set to 1
\n
"
);
/* Fall through */
case
1
:
cctl_memcpy
|=
PL080_BSIZE_1
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_1
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
pd
->
memcpy_burst_size
=
PL08X_BURST_SZ_1
;
break
;
case
4
:
cctl_memcpy
|=
PL080_BSIZE_4
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_4
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
pd
->
memcpy_burst_size
=
PL08X_BURST_SZ_4
;
break
;
case
8
:
cctl_memcpy
|=
PL080_BSIZE_8
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_8
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
pd
->
memcpy_burst_size
=
PL08X_BURST_SZ_8
;
break
;
case
16
:
cctl_memcpy
|=
PL080_BSIZE_16
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_16
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
pd
->
memcpy_burst_size
=
PL08X_BURST_SZ_16
;
break
;
case
32
:
cctl_memcpy
|=
PL080_BSIZE_32
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_32
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
pd
->
memcpy_burst_size
=
PL08X_BURST_SZ_32
;
break
;
case
64
:
cctl_memcpy
|=
PL080_BSIZE_64
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_64
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
pd
->
memcpy_burst_size
=
PL08X_BURST_SZ_64
;
break
;
case
128
:
cctl_memcpy
|=
PL080_BSIZE_128
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_128
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
pd
->
memcpy_burst_size
=
PL08X_BURST_SZ_128
;
break
;
case
256
:
cctl_memcpy
|=
PL080_BSIZE_256
<<
PL080_CONTROL_SB_SIZE_SHIFT
|
PL080_BSIZE_256
<<
PL080_CONTROL_DB_SIZE_SHIFT
;
pd
->
memcpy_burst_size
=
PL08X_BURST_SZ_256
;
break
;
}
...
...
@@ -2182,48 +2655,40 @@ static int pl08x_of_probe(struct amba_device *adev,
dev_err
(
&
adev
->
dev
,
"illegal bus width for memcpy, set to 8 bits
\n
"
);
/* Fall through */
case
8
:
cctl_memcpy
|=
PL080_WIDTH_8BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
|
PL080_WIDTH_8BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
;
pd
->
memcpy_bus_width
=
PL08X_BUS_WIDTH_8_BITS
;
break
;
case
16
:
cctl_memcpy
|=
PL080_WIDTH_16BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
|
PL080_WIDTH_16BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
;
pd
->
memcpy_bus_width
=
PL08X_BUS_WIDTH_16_BITS
;
break
;
case
32
:
cctl_memcpy
|=
PL080_WIDTH_32BIT
<<
PL080_CONTROL_SWIDTH_SHIFT
|
PL080_WIDTH_32BIT
<<
PL080_CONTROL_DWIDTH_SHIFT
;
pd
->
memcpy_bus_width
=
PL08X_BUS_WIDTH_32_BITS
;
break
;
}
/* This is currently the only thing making sense */
cctl_memcpy
|=
PL080_CONTROL_PROT_SYS
;
/* Set up memcpy channel */
pd
->
memcpy_channel
.
bus_id
=
"memcpy"
;
pd
->
memcpy_channel
.
cctl_memcpy
=
cctl_memcpy
;
/* Use the buses that can access memory, obviously */
pd
->
memcpy_channel
.
periph_buses
=
pd
->
mem_buses
;
/*
* Allocate channel data for all possible slave channels (one
* for each possible signal), channels will then be allocated
* for a device and have it's AHB interfaces set up at
* translation time.
*/
chanp
=
devm_kcalloc
(
&
adev
->
dev
,
pl08x
->
vd
->
signals
,
sizeof
(
struct
pl08x_channel_data
),
GFP_KERNEL
);
if
(
!
chanp
)
return
-
ENOMEM
;
if
(
pl08x
->
vd
->
signals
)
{
chanp
=
devm_kcalloc
(
&
adev
->
dev
,
pl08x
->
vd
->
signals
,
sizeof
(
struct
pl08x_channel_data
),
GFP_KERNEL
);
if
(
!
chanp
)
return
-
ENOMEM
;
pd
->
slave_channels
=
chanp
;
for
(
i
=
0
;
i
<
pl08x
->
vd
->
signals
;
i
++
)
{
/* chanp->periph_buses will be assigned at translation */
chanp
->
bus_id
=
kasprintf
(
GFP_KERNEL
,
"slave%d"
,
i
);
chanp
++
;
pd
->
slave_channels
=
chanp
;
for
(
i
=
0
;
i
<
pl08x
->
vd
->
signals
;
i
++
)
{
/*
* chanp->periph_buses will be assigned at translation
*/
chanp
->
bus_id
=
kasprintf
(
GFP_KERNEL
,
"slave%d"
,
i
);
chanp
++
;
}
pd
->
num_slave_channels
=
pl08x
->
vd
->
signals
;
}
pd
->
num_slave_channels
=
pl08x
->
vd
->
signals
;
pl08x
->
pd
=
pd
;
...
...
@@ -2242,7 +2707,7 @@ static inline int pl08x_of_probe(struct amba_device *adev,
static
int
pl08x_probe
(
struct
amba_device
*
adev
,
const
struct
amba_id
*
id
)
{
struct
pl08x_driver_data
*
pl08x
;
const
struct
vendor_data
*
vd
=
id
->
data
;
struct
vendor_data
*
vd
=
id
->
data
;
struct
device_node
*
np
=
adev
->
dev
.
of_node
;
u32
tsfr_size
;
int
ret
=
0
;
...
...
@@ -2268,6 +2733,34 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
pl08x
->
adev
=
adev
;
pl08x
->
vd
=
vd
;
pl08x
->
base
=
ioremap
(
adev
->
res
.
start
,
resource_size
(
&
adev
->
res
));
if
(
!
pl08x
->
base
)
{
ret
=
-
ENOMEM
;
goto
out_no_ioremap
;
}
if
(
vd
->
ftdmac020
)
{
u32
val
;
val
=
readl
(
pl08x
->
base
+
FTDMAC020_REVISION
);
dev_info
(
&
pl08x
->
adev
->
dev
,
"FTDMAC020 %d.%d rel %d
\n
"
,
(
val
>>
16
)
&
0xff
,
(
val
>>
8
)
&
0xff
,
val
&
0xff
);
val
=
readl
(
pl08x
->
base
+
FTDMAC020_FEATURE
);
dev_info
(
&
pl08x
->
adev
->
dev
,
"FTDMAC020 %d channels, "
"%s built-in bridge, %s, %s linked lists
\n
"
,
(
val
>>
12
)
&
0x0f
,
(
val
&
BIT
(
10
))
?
"no"
:
"has"
,
(
val
&
BIT
(
9
))
?
"AHB0 and AHB1"
:
"AHB0"
,
(
val
&
BIT
(
8
))
?
"supports"
:
"does not support"
);
/* Vendor data from feature register */
if
(
!
(
val
&
BIT
(
8
)))
dev_warn
(
&
pl08x
->
adev
->
dev
,
"linked lists not supported, required
\n
"
);
vd
->
channels
=
(
val
>>
12
)
&
0x0f
;
vd
->
dualmaster
=
!!
(
val
&
BIT
(
9
));
}
/* Initialize memcpy engine */
dma_cap_set
(
DMA_MEMCPY
,
pl08x
->
memcpy
.
cap_mask
);
pl08x
->
memcpy
.
dev
=
&
adev
->
dev
;
...
...
@@ -2284,25 +2777,38 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
pl08x
->
memcpy
.
dst_addr_widths
=
PL80X_DMA_BUSWIDTHS
;
pl08x
->
memcpy
.
directions
=
BIT
(
DMA_MEM_TO_MEM
);
pl08x
->
memcpy
.
residue_granularity
=
DMA_RESIDUE_GRANULARITY_SEGMENT
;
if
(
vd
->
ftdmac020
)
pl08x
->
memcpy
.
copy_align
=
DMAENGINE_ALIGN_4_BYTES
;
/* Initialize slave engine */
dma_cap_set
(
DMA_SLAVE
,
pl08x
->
slave
.
cap_mask
);
dma_cap_set
(
DMA_CYCLIC
,
pl08x
->
slave
.
cap_mask
);
pl08x
->
slave
.
dev
=
&
adev
->
dev
;
pl08x
->
slave
.
device_free_chan_resources
=
pl08x_free_chan_resources
;
pl08x
->
slave
.
device_prep_dma_interrupt
=
pl08x_prep_dma_interrupt
;
pl08x
->
slave
.
device_tx_status
=
pl08x_dma_tx_status
;
pl08x
->
slave
.
device_issue_pending
=
pl08x_issue_pending
;
pl08x
->
slave
.
device_prep_slave_sg
=
pl08x_prep_slave_sg
;
pl08x
->
slave
.
device_prep_dma_cyclic
=
pl08x_prep_dma_cyclic
;
pl08x
->
slave
.
device_config
=
pl08x_config
;
pl08x
->
slave
.
device_pause
=
pl08x_pause
;
pl08x
->
slave
.
device_resume
=
pl08x_resume
;
pl08x
->
slave
.
device_terminate_all
=
pl08x_terminate_all
;
pl08x
->
slave
.
src_addr_widths
=
PL80X_DMA_BUSWIDTHS
;
pl08x
->
slave
.
dst_addr_widths
=
PL80X_DMA_BUSWIDTHS
;
pl08x
->
slave
.
directions
=
BIT
(
DMA_DEV_TO_MEM
)
|
BIT
(
DMA_MEM_TO_DEV
);
pl08x
->
slave
.
residue_granularity
=
DMA_RESIDUE_GRANULARITY_SEGMENT
;
/*
* Initialize slave engine, if the block has no signals, that means
* we have no slave support.
*/
if
(
vd
->
signals
)
{
pl08x
->
has_slave
=
true
;
dma_cap_set
(
DMA_SLAVE
,
pl08x
->
slave
.
cap_mask
);
dma_cap_set
(
DMA_CYCLIC
,
pl08x
->
slave
.
cap_mask
);
pl08x
->
slave
.
dev
=
&
adev
->
dev
;
pl08x
->
slave
.
device_free_chan_resources
=
pl08x_free_chan_resources
;
pl08x
->
slave
.
device_prep_dma_interrupt
=
pl08x_prep_dma_interrupt
;
pl08x
->
slave
.
device_tx_status
=
pl08x_dma_tx_status
;
pl08x
->
slave
.
device_issue_pending
=
pl08x_issue_pending
;
pl08x
->
slave
.
device_prep_slave_sg
=
pl08x_prep_slave_sg
;
pl08x
->
slave
.
device_prep_dma_cyclic
=
pl08x_prep_dma_cyclic
;
pl08x
->
slave
.
device_config
=
pl08x_config
;
pl08x
->
slave
.
device_pause
=
pl08x_pause
;
pl08x
->
slave
.
device_resume
=
pl08x_resume
;
pl08x
->
slave
.
device_terminate_all
=
pl08x_terminate_all
;
pl08x
->
slave
.
src_addr_widths
=
PL80X_DMA_BUSWIDTHS
;
pl08x
->
slave
.
dst_addr_widths
=
PL80X_DMA_BUSWIDTHS
;
pl08x
->
slave
.
directions
=
BIT
(
DMA_DEV_TO_MEM
)
|
BIT
(
DMA_MEM_TO_DEV
);
pl08x
->
slave
.
residue_granularity
=
DMA_RESIDUE_GRANULARITY_SEGMENT
;
}
/* Get the platform data */
pl08x
->
pd
=
dev_get_platdata
(
&
adev
->
dev
);
...
...
@@ -2344,19 +2850,18 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
goto
out_no_lli_pool
;
}
pl08x
->
base
=
ioremap
(
adev
->
res
.
start
,
resource_size
(
&
adev
->
res
));
if
(
!
pl08x
->
base
)
{
ret
=
-
ENOMEM
;
goto
out_no_ioremap
;
}
/* Turn on the PL08x */
pl08x_ensure_on
(
pl08x
);
/* Attach the interrupt handler */
writel
(
0x000000FF
,
pl08x
->
base
+
PL080_ERR_CLEAR
);
/* Clear any pending interrupts */
if
(
vd
->
ftdmac020
)
/* This variant has error IRQs in bits 16-19 */
writel
(
0x0000FFFF
,
pl08x
->
base
+
PL080_ERR_CLEAR
);
else
writel
(
0x000000FF
,
pl08x
->
base
+
PL080_ERR_CLEAR
);
writel
(
0x000000FF
,
pl08x
->
base
+
PL080_TC_CLEAR
);
/* Attach the interrupt handler */
ret
=
request_irq
(
adev
->
irq
[
0
],
pl08x_irq
,
0
,
DRIVER_NAME
,
pl08x
);
if
(
ret
)
{
dev_err
(
&
adev
->
dev
,
"%s failed to request interrupt %d
\n
"
,
...
...
@@ -2377,7 +2882,25 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
ch
->
id
=
i
;
ch
->
base
=
pl08x
->
base
+
PL080_Cx_BASE
(
i
);
ch
->
reg_config
=
ch
->
base
+
vd
->
config_offset
;
if
(
vd
->
ftdmac020
)
{
/* FTDMA020 has a special channel busy register */
ch
->
reg_busy
=
ch
->
base
+
FTDMAC020_CH_BUSY
;
ch
->
reg_config
=
ch
->
base
+
FTDMAC020_CH_CFG
;
ch
->
reg_control
=
ch
->
base
+
FTDMAC020_CH_CSR
;
ch
->
reg_src
=
ch
->
base
+
FTDMAC020_CH_SRC_ADDR
;
ch
->
reg_dst
=
ch
->
base
+
FTDMAC020_CH_DST_ADDR
;
ch
->
reg_lli
=
ch
->
base
+
FTDMAC020_CH_LLP
;
ch
->
ftdmac020
=
true
;
}
else
{
ch
->
reg_config
=
ch
->
base
+
vd
->
config_offset
;
ch
->
reg_control
=
ch
->
base
+
PL080_CH_CONTROL
;
ch
->
reg_src
=
ch
->
base
+
PL080_CH_SRC_ADDR
;
ch
->
reg_dst
=
ch
->
base
+
PL080_CH_DST_ADDR
;
ch
->
reg_lli
=
ch
->
base
+
PL080_CH_LLI
;
}
if
(
vd
->
pl080s
)
ch
->
pl080s
=
true
;
spin_lock_init
(
&
ch
->
lock
);
/*
...
...
@@ -2410,13 +2933,15 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
}
/* Register slave channels */
ret
=
pl08x_dma_init_virtual_channels
(
pl08x
,
&
pl08x
->
slave
,
pl08x
->
pd
->
num_slave_channels
,
true
);
if
(
ret
<
0
)
{
dev_warn
(
&
pl08x
->
adev
->
dev
,
"%s failed to enumerate slave channels - %d
\n
"
,
__func__
,
ret
);
goto
out_no_slave
;
if
(
pl08x
->
has_slave
)
{
ret
=
pl08x_dma_init_virtual_channels
(
pl08x
,
&
pl08x
->
slave
,
pl08x
->
pd
->
num_slave_channels
,
true
);
if
(
ret
<
0
)
{
dev_warn
(
&
pl08x
->
adev
->
dev
,
"%s failed to enumerate slave channels - %d
\n
"
,
__func__
,
ret
);
goto
out_no_slave
;
}
}
ret
=
dma_async_device_register
(
&
pl08x
->
memcpy
);
...
...
@@ -2427,12 +2952,14 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
goto
out_no_memcpy_reg
;
}
ret
=
dma_async_device_register
(
&
pl08x
->
slave
);
if
(
ret
)
{
dev_warn
(
&
pl08x
->
adev
->
dev
,
if
(
pl08x
->
has_slave
)
{
ret
=
dma_async_device_register
(
&
pl08x
->
slave
);
if
(
ret
)
{
dev_warn
(
&
pl08x
->
adev
->
dev
,
"%s failed to register slave as an async device - %d
\n
"
,
__func__
,
ret
);
goto
out_no_slave_reg
;
goto
out_no_slave_reg
;
}
}
amba_set_drvdata
(
adev
,
pl08x
);
...
...
@@ -2446,7 +2973,8 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
out_no_slave_reg:
dma_async_device_unregister
(
&
pl08x
->
memcpy
);
out_no_memcpy_reg:
pl08x_free_virtual_channels
(
&
pl08x
->
slave
);
if
(
pl08x
->
has_slave
)
pl08x_free_virtual_channels
(
&
pl08x
->
slave
);
out_no_slave:
pl08x_free_virtual_channels
(
&
pl08x
->
memcpy
);
out_no_memcpy:
...
...
@@ -2454,11 +2982,11 @@ static int pl08x_probe(struct amba_device *adev, const struct amba_id *id)
out_no_phychans:
free_irq
(
adev
->
irq
[
0
],
pl08x
);
out_no_irq:
iounmap
(
pl08x
->
base
);
out_no_ioremap:
dma_pool_destroy
(
pl08x
->
pool
);
out_no_lli_pool:
out_no_platdata:
iounmap
(
pl08x
->
base
);
out_no_ioremap:
kfree
(
pl08x
);
out_no_pl08x:
amba_release_regions
(
adev
);
...
...
@@ -2499,6 +3027,12 @@ static struct vendor_data vendor_pl081 = {
.
max_transfer_size
=
PL080_CONTROL_TRANSFER_SIZE_MASK
,
};
static
struct
vendor_data
vendor_ftdmac020
=
{
.
config_offset
=
PL080_CH_CONFIG
,
.
ftdmac020
=
true
,
.
max_transfer_size
=
PL080_CONTROL_TRANSFER_SIZE_MASK
,
};
static
struct
amba_id
pl08x_ids
[]
=
{
/* Samsung PL080S variant */
{
...
...
@@ -2524,6 +3058,12 @@ static struct amba_id pl08x_ids[] = {
.
mask
=
0x00ffffff
,
.
data
=
&
vendor_nomadik
,
},
/* Faraday Technology FTDMAC020 */
{
.
id
=
0x0003b080
,
.
mask
=
0x000fffff
,
.
data
=
&
vendor_ftdmac020
,
},
{
0
,
0
},
};
...
...
include/linux/amba/pl080.h
浏览文件 @
98cd085e
...
...
@@ -44,7 +44,14 @@
#define PL080_SYNC (0x34)
/* Per channel configuration registers */
/* The Faraday Technology FTDMAC020 variant registers */
#define FTDMAC020_CH_BUSY (0x20)
/* Identical to PL080_CONFIG */
#define FTDMAC020_CSR (0x24)
/* Identical to PL080_SYNC */
#define FTDMAC020_SYNC (0x2C)
#define FTDMAC020_REVISION (0x30)
#define FTDMAC020_FEATURE (0x34)
/* Per channel configuration registers */
#define PL080_Cx_BASE(x) ((0x100 + (x * 0x20)))
...
...
@@ -55,13 +62,20 @@
#define PL080_CH_CONFIG (0x10)
#define PL080S_CH_CONTROL2 (0x10)
#define PL080S_CH_CONFIG (0x14)
#define PL080_LLI_ADDR_MASK (0x3fffffff << 2)
/* The Faraday FTDMAC020 derivative shuffles the registers around */
#define FTDMAC020_CH_CSR (0x00)
#define FTDMAC020_CH_CFG (0x04)
#define FTDMAC020_CH_SRC_ADDR (0x08)
#define FTDMAC020_CH_DST_ADDR (0x0C)
#define FTDMAC020_CH_LLP (0x10)
#define FTDMAC020_CH_SIZE (0x14)
#define PL080_LLI_ADDR_MASK GENMASK(31, 2)
#define PL080_LLI_ADDR_SHIFT (2)
#define PL080_LLI_LM_AHB2 BIT(0)
#define PL080_CONTROL_TC_IRQ_EN BIT(31)
#define PL080_CONTROL_PROT_MASK
(0x7 <<
28)
#define PL080_CONTROL_PROT_MASK
GENMASK(30,
28)
#define PL080_CONTROL_PROT_SHIFT (28)
#define PL080_CONTROL_PROT_CACHE BIT(30)
#define PL080_CONTROL_PROT_BUFF BIT(29)
...
...
@@ -70,16 +84,16 @@
#define PL080_CONTROL_SRC_INCR BIT(26)
#define PL080_CONTROL_DST_AHB2 BIT(25)
#define PL080_CONTROL_SRC_AHB2 BIT(24)
#define PL080_CONTROL_DWIDTH_MASK
(0x7 <<
21)
#define PL080_CONTROL_DWIDTH_MASK
GENMASK(23,
21)
#define PL080_CONTROL_DWIDTH_SHIFT (21)
#define PL080_CONTROL_SWIDTH_MASK
(0x7 <<
18)
#define PL080_CONTROL_SWIDTH_MASK
GENMASK(20,
18)
#define PL080_CONTROL_SWIDTH_SHIFT (18)
#define PL080_CONTROL_DB_SIZE_MASK
(0x7 <<
15)
#define PL080_CONTROL_DB_SIZE_MASK
GENMASK(17,
15)
#define PL080_CONTROL_DB_SIZE_SHIFT (15)
#define PL080_CONTROL_SB_SIZE_MASK
(0x7 <<
12)
#define PL080_CONTROL_SB_SIZE_MASK
GENMASK(14,
12)
#define PL080_CONTROL_SB_SIZE_SHIFT (12)
#define PL080_CONTROL_TRANSFER_SIZE_MASK
(0xfff <<
0)
#define PL080S_CONTROL_TRANSFER_SIZE_MASK
(0x1ffffff <<
0)
#define PL080_CONTROL_TRANSFER_SIZE_MASK
GENMASK(11,
0)
#define PL080S_CONTROL_TRANSFER_SIZE_MASK
GENMASK(24,
0)
#define PL080_CONTROL_TRANSFER_SIZE_SHIFT (0)
#define PL080_BSIZE_1 (0x0)
...
...
@@ -102,11 +116,11 @@
#define PL080_CONFIG_LOCK BIT(16)
#define PL080_CONFIG_TC_IRQ_MASK BIT(15)
#define PL080_CONFIG_ERR_IRQ_MASK BIT(14)
#define PL080_CONFIG_FLOW_CONTROL_MASK
(0x7 <<
11)
#define PL080_CONFIG_FLOW_CONTROL_MASK
GENMASK(13,
11)
#define PL080_CONFIG_FLOW_CONTROL_SHIFT (11)
#define PL080_CONFIG_DST_SEL_MASK
(0xf <<
6)
#define PL080_CONFIG_DST_SEL_MASK
GENMASK(9,
6)
#define PL080_CONFIG_DST_SEL_SHIFT (6)
#define PL080_CONFIG_SRC_SEL_MASK
(0xf <<
1)
#define PL080_CONFIG_SRC_SEL_MASK
GENMASK(4,
1)
#define PL080_CONFIG_SRC_SEL_SHIFT (1)
#define PL080_CONFIG_ENABLE BIT(0)
...
...
@@ -119,6 +133,73 @@
#define PL080_FLOW_PER2MEM_PER (0x6)
#define PL080_FLOW_SRC2DST_SRC (0x7)
#define FTDMAC020_CH_CSR_TC_MSK BIT(31)
/* Later versions have a threshold in bits 24..26, */
#define FTDMAC020_CH_CSR_FIFOTH_MSK GENMASK(26, 24)
#define FTDMAC020_CH_CSR_FIFOTH_SHIFT (24)
#define FTDMAC020_CH_CSR_CHPR1_MSK GENMASK(23, 22)
#define FTDMAC020_CH_CSR_PROT3 BIT(21)
#define FTDMAC020_CH_CSR_PROT2 BIT(20)
#define FTDMAC020_CH_CSR_PROT1 BIT(19)
#define FTDMAC020_CH_CSR_SRC_SIZE_MSK GENMASK(18, 16)
#define FTDMAC020_CH_CSR_SRC_SIZE_SHIFT (16)
#define FTDMAC020_CH_CSR_ABT BIT(15)
#define FTDMAC020_CH_CSR_SRC_WIDTH_MSK GENMASK(13, 11)
#define FTDMAC020_CH_CSR_SRC_WIDTH_SHIFT (11)
#define FTDMAC020_CH_CSR_DST_WIDTH_MSK GENMASK(10, 8)
#define FTDMAC020_CH_CSR_DST_WIDTH_SHIFT (8)
#define FTDMAC020_CH_CSR_MODE BIT(7)
/* 00 = increase, 01 = decrease, 10 = fix */
#define FTDMAC020_CH_CSR_SRCAD_CTL_MSK GENMASK(6, 5)
#define FTDMAC020_CH_CSR_SRCAD_CTL_SHIFT (5)
#define FTDMAC020_CH_CSR_DSTAD_CTL_MSK GENMASK(4, 3)
#define FTDMAC020_CH_CSR_DSTAD_CTL_SHIFT (3)
#define FTDMAC020_CH_CSR_SRC_SEL BIT(2)
#define FTDMAC020_CH_CSR_DST_SEL BIT(1)
#define FTDMAC020_CH_CSR_EN BIT(0)
/* FIFO threshold setting */
#define FTDMAC020_CH_CSR_FIFOTH_1 (0x0)
#define FTDMAC020_CH_CSR_FIFOTH_2 (0x1)
#define FTDMAC020_CH_CSR_FIFOTH_4 (0x2)
#define FTDMAC020_CH_CSR_FIFOTH_8 (0x3)
#define FTDMAC020_CH_CSR_FIFOTH_16 (0x4)
/* The FTDMAC020 supports 64bit wide transfers */
#define FTDMAC020_WIDTH_64BIT (0x3)
/* Address can be increased, decreased or fixed */
#define FTDMAC020_CH_CSR_SRCAD_CTL_INC (0x0)
#define FTDMAC020_CH_CSR_SRCAD_CTL_DEC (0x1)
#define FTDMAC020_CH_CSR_SRCAD_CTL_FIXED (0x2)
#define FTDMAC020_CH_CFG_LLP_CNT_MASK GENMASK(19, 16)
#define FTDMAC020_CH_CFG_LLP_CNT_SHIFT (16)
#define FTDMAC020_CH_CFG_BUSY BIT(8)
#define FTDMAC020_CH_CFG_INT_ABT_MASK BIT(2)
#define FTDMAC020_CH_CFG_INT_ERR_MASK BIT(1)
#define FTDMAC020_CH_CFG_INT_TC_MASK BIT(0)
/* Inside the LLIs, the applicable CSR fields are mapped differently */
#define FTDMAC020_LLI_TC_MSK BIT(28)
#define FTDMAC020_LLI_SRC_WIDTH_MSK GENMASK(27, 25)
#define FTDMAC020_LLI_SRC_WIDTH_SHIFT (25)
#define FTDMAC020_LLI_DST_WIDTH_MSK GENMASK(24, 22)
#define FTDMAC020_LLI_DST_WIDTH_SHIFT (22)
#define FTDMAC020_LLI_SRCAD_CTL_MSK GENMASK(21, 20)
#define FTDMAC020_LLI_SRCAD_CTL_SHIFT (20)
#define FTDMAC020_LLI_DSTAD_CTL_MSK GENMASK(19, 18)
#define FTDMAC020_LLI_DSTAD_CTL_SHIFT (18)
#define FTDMAC020_LLI_SRC_SEL BIT(17)
#define FTDMAC020_LLI_DST_SEL BIT(16)
#define FTDMAC020_LLI_TRANSFER_SIZE_MASK GENMASK(11, 0)
#define FTDMAC020_LLI_TRANSFER_SIZE_SHIFT (0)
#define FTDMAC020_CFG_LLP_CNT_MASK GENMASK(19, 16)
#define FTDMAC020_CFG_LLP_CNT_SHIFT (16)
#define FTDMAC020_CFG_BUSY BIT(8)
#define FTDMAC020_CFG_INT_ABT_MSK BIT(2)
#define FTDMAC020_CFG_INT_ERR_MSK BIT(1)
#define FTDMAC020_CFG_INT_TC_MSK BIT(0)
/* DMA linked list chain structure */
struct
pl080_lli
{
...
...
include/linux/amba/pl08x.h
浏览文件 @
98cd085e
...
...
@@ -47,8 +47,6 @@ enum {
* devices with static assignments
* @muxval: a number usually used to poke into some mux regiser to
* mux in the signal to this channel
* @cctl_memcpy: options for the channel control register for memcpy
* *** not used for slave channels ***
* @addr: source/target address in physical memory for this DMA channel,
* can be the address of a FIFO register for burst requests for example.
* This can be left undefined if the PrimeCell API is used for configuring
...
...
@@ -63,12 +61,28 @@ struct pl08x_channel_data {
int
min_signal
;
int
max_signal
;
u32
muxval
;
u32
cctl_memcpy
;
dma_addr_t
addr
;
bool
single
;
u8
periph_buses
;
};
enum
pl08x_burst_size
{
PL08X_BURST_SZ_1
,
PL08X_BURST_SZ_4
,
PL08X_BURST_SZ_8
,
PL08X_BURST_SZ_16
,
PL08X_BURST_SZ_32
,
PL08X_BURST_SZ_64
,
PL08X_BURST_SZ_128
,
PL08X_BURST_SZ_256
,
};
enum
pl08x_bus_width
{
PL08X_BUS_WIDTH_8_BITS
,
PL08X_BUS_WIDTH_16_BITS
,
PL08X_BUS_WIDTH_32_BITS
,
};
/**
* struct pl08x_platform_data - the platform configuration for the PL08x
* PrimeCells.
...
...
@@ -76,6 +90,11 @@ struct pl08x_channel_data {
* platform, all inclusive, including multiplexed channels. The available
* physical channels will be multiplexed around these signals as they are
* requested, just enumerate all possible channels.
* @num_slave_channels: number of elements in the slave channel array
* @memcpy_burst_size: the appropriate burst size for memcpy operations
* @memcpy_bus_width: memory bus width
* @memcpy_prot_buff: whether memcpy DMA is bufferable
* @memcpy_prot_cache: whether memcpy DMA is cacheable
* @get_xfer_signal: request a physical signal to be used for a DMA transfer
* immediately: if there is some multiplexing or similar blocking the use
* of the channel the transfer can be denied by returning less than zero,
...
...
@@ -90,7 +109,10 @@ struct pl08x_channel_data {
struct
pl08x_platform_data
{
struct
pl08x_channel_data
*
slave_channels
;
unsigned
int
num_slave_channels
;
struct
pl08x_channel_data
memcpy_channel
;
enum
pl08x_burst_size
memcpy_burst_size
;
enum
pl08x_bus_width
memcpy_bus_width
;
bool
memcpy_prot_buff
;
bool
memcpy_prot_cache
;
int
(
*
get_xfer_signal
)(
const
struct
pl08x_channel_data
*
);
void
(
*
put_xfer_signal
)(
const
struct
pl08x_channel_data
*
,
int
);
u8
lli_buses
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录