Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
Menu
Open sidebar
adam.huang
Arm Trusted Firmware
Commits
e02be207
Commit
e02be207
authored
Jan 23, 2017
by
danh-arm
Committed by
GitHub
Jan 23, 2017
Browse files
Merge pull request #815 from hzhuang1/dwmmc_v3.9
drivers: add designware emmc driver
parents
7750990c
5dbdb7da
Changes
2
Hide whitespace changes
Inline
Side-by-side
drivers/synopsys/emmc/dw_mmc.c
0 → 100644
View file @
e02be207
/*
* Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include <arch.h>
#include <arch_helpers.h>
#include <assert.h>
#include <debug.h>
#include <delay_timer.h>
#include <dw_mmc.h>
#include <emmc.h>
#include <errno.h>
#include <mmio.h>
#include <string.h>
#define DWMMC_CTRL (0x00)
#define CTRL_IDMAC_EN (1 << 25)
#define CTRL_DMA_EN (1 << 5)
#define CTRL_INT_EN (1 << 4)
#define CTRL_DMA_RESET (1 << 2)
#define CTRL_FIFO_RESET (1 << 1)
#define CTRL_RESET (1 << 0)
#define CTRL_RESET_ALL (CTRL_DMA_RESET | CTRL_FIFO_RESET | \
CTRL_RESET)
#define DWMMC_PWREN (0x04)
#define DWMMC_CLKDIV (0x08)
#define DWMMC_CLKSRC (0x0c)
#define DWMMC_CLKENA (0x10)
#define DWMMC_TMOUT (0x14)
#define DWMMC_CTYPE (0x18)
#define CTYPE_8BIT (1 << 16)
#define CTYPE_4BIT (1)
#define CTYPE_1BIT (0)
#define DWMMC_BLKSIZ (0x1c)
#define DWMMC_BYTCNT (0x20)
#define DWMMC_INTMASK (0x24)
#define INT_EBE (1 << 15)
#define INT_SBE (1 << 13)
#define INT_HLE (1 << 12)
#define INT_FRUN (1 << 11)
#define INT_DRT (1 << 9)
#define INT_RTO (1 << 8)
#define INT_DCRC (1 << 7)
#define INT_RCRC (1 << 6)
#define INT_RXDR (1 << 5)
#define INT_TXDR (1 << 4)
#define INT_DTO (1 << 3)
#define INT_CMD_DONE (1 << 2)
#define INT_RE (1 << 1)
#define DWMMC_CMDARG (0x28)
#define DWMMC_CMD (0x2c)
#define CMD_START (1 << 31)
#define CMD_USE_HOLD_REG (1 << 29)
/* 0 if SDR50/100 */
#define CMD_UPDATE_CLK_ONLY (1 << 21)
#define CMD_SEND_INIT (1 << 15)
#define CMD_STOP_ABORT_CMD (1 << 14)
#define CMD_WAIT_PRVDATA_COMPLETE (1 << 13)
#define CMD_WRITE (1 << 10)
#define CMD_DATA_TRANS_EXPECT (1 << 9)
#define CMD_CHECK_RESP_CRC (1 << 8)
#define CMD_RESP_LEN (1 << 7)
#define CMD_RESP_EXPECT (1 << 6)
#define CMD(x) (x & 0x3f)
#define DWMMC_RESP0 (0x30)
#define DWMMC_RESP1 (0x34)
#define DWMMC_RESP2 (0x38)
#define DWMMC_RESP3 (0x3c)
#define DWMMC_RINTSTS (0x44)
#define DWMMC_STATUS (0x48)
#define STATUS_DATA_BUSY (1 << 9)
#define DWMMC_FIFOTH (0x4c)
#define FIFOTH_TWMARK(x) (x & 0xfff)
#define FIFOTH_RWMARK(x) ((x & 0x1ff) << 16)
#define FIFOTH_DMA_BURST_SIZE(x) ((x & 0x7) << 28)
#define DWMMC_DEBNCE (0x64)
#define DWMMC_BMOD (0x80)
#define BMOD_ENABLE (1 << 7)
#define BMOD_FB (1 << 1)
#define BMOD_SWRESET (1 << 0)
#define DWMMC_DBADDR (0x88)
#define DWMMC_IDSTS (0x8c)
#define DWMMC_IDINTEN (0x90)
#define DWMMC_CARDTHRCTL (0x100)
#define CARDTHRCTL_RD_THR(x) ((x & 0xfff) << 16)
#define CARDTHRCTL_RD_THR_EN (1 << 0)
#define IDMAC_DES0_DIC (1 << 1)
#define IDMAC_DES0_LD (1 << 2)
#define IDMAC_DES0_FS (1 << 3)
#define IDMAC_DES0_CH (1 << 4)
#define IDMAC_DES0_ER (1 << 5)
#define IDMAC_DES0_CES (1 << 30)
#define IDMAC_DES0_OWN (1 << 31)
#define IDMAC_DES1_BS1(x) ((x) & 0x1fff)
#define IDMAC_DES2_BS2(x) (((x) & 0x1fff) << 13)
#define DWMMC_DMA_MAX_BUFFER_SIZE (512 * 8)
#define DWMMC_8BIT_MODE (1 << 6)
#define TIMEOUT 100000
struct
dw_idmac_desc
{
unsigned
int
des0
;
unsigned
int
des1
;
unsigned
int
des2
;
unsigned
int
des3
;
};
static
void
dw_init
(
void
);
static
int
dw_send_cmd
(
emmc_cmd_t
*
cmd
);
static
int
dw_set_ios
(
int
clk
,
int
width
);
static
int
dw_prepare
(
int
lba
,
uintptr_t
buf
,
size_t
size
);
static
int
dw_read
(
int
lba
,
uintptr_t
buf
,
size_t
size
);
static
int
dw_write
(
int
lba
,
uintptr_t
buf
,
size_t
size
);
static
const
emmc_ops_t
dw_mmc_ops
=
{
.
init
=
dw_init
,
.
send_cmd
=
dw_send_cmd
,
.
set_ios
=
dw_set_ios
,
.
prepare
=
dw_prepare
,
.
read
=
dw_read
,
.
write
=
dw_write
,
};
static
dw_mmc_params_t
dw_params
;
static
void
dw_update_clk
(
void
)
{
unsigned
int
data
;
mmio_write_32
(
dw_params
.
reg_base
+
DWMMC_CMD
,
CMD_WAIT_PRVDATA_COMPLETE
|
CMD_UPDATE_CLK_ONLY
|
CMD_START
);
while
(
1
)
{
data
=
mmio_read_32
(
dw_params
.
reg_base
+
DWMMC_CMD
);
if
((
data
&
CMD_START
)
==
0
)
break
;
data
=
mmio_read_32
(
dw_params
.
reg_base
+
DWMMC_RINTSTS
);
assert
(
data
&
INT_HLE
);
}
}
static
void
dw_set_clk
(
int
clk
)
{
unsigned
int
data
;
int
div
;
assert
(
clk
>
0
);
for
(
div
=
1
;
div
<
256
;
div
++
)
{
if
((
dw_params
.
clk_rate
/
(
2
*
div
))
<=
clk
)
{
break
;
}
}
assert
(
div
<
256
);
/* wait until controller is idle */
do
{
data
=
mmio_read_32
(
dw_params
.
reg_base
+
DWMMC_STATUS
);
}
while
(
data
&
STATUS_DATA_BUSY
);
/* disable clock before change clock rate */
mmio_write_32
(
dw_params
.
reg_base
+
DWMMC_CLKENA
,
0
);
dw_update_clk
();
mmio_write_32
(
dw_params
.
reg_base
+
DWMMC_CLKDIV
,
div
);
dw_update_clk
();
/* enable clock */
mmio_write_32
(
dw_params
.
reg_base
+
DWMMC_CLKENA
,
1
);
mmio_write_32
(
dw_params
.
reg_base
+
DWMMC_CLKSRC
,
0
);
dw_update_clk
();
}
static
void
dw_init
(
void
)
{
unsigned
int
data
;
uintptr_t
base
;
assert
((
dw_params
.
reg_base
&
EMMC_BLOCK_MASK
)
==
0
);
base
=
dw_params
.
reg_base
;
mmio_write_32
(
base
+
DWMMC_PWREN
,
1
);
mmio_write_32
(
base
+
DWMMC_CTRL
,
CTRL_RESET_ALL
);
do
{
data
=
mmio_read_32
(
base
+
DWMMC_CTRL
);
}
while
(
data
);
/* enable DMA in CTRL */
data
=
CTRL_INT_EN
|
CTRL_DMA_EN
|
CTRL_IDMAC_EN
;
mmio_write_32
(
base
+
DWMMC_CTRL
,
data
);
mmio_write_32
(
base
+
DWMMC_RINTSTS
,
~
0
);
mmio_write_32
(
base
+
DWMMC_INTMASK
,
0
);
mmio_write_32
(
base
+
DWMMC_TMOUT
,
~
0
);
mmio_write_32
(
base
+
DWMMC_IDINTEN
,
~
0
);
mmio_write_32
(
base
+
DWMMC_BLKSIZ
,
EMMC_BLOCK_SIZE
);
mmio_write_32
(
base
+
DWMMC_BYTCNT
,
256
*
1024
);
mmio_write_32
(
base
+
DWMMC_DEBNCE
,
0x00ffffff
);
mmio_write_32
(
base
+
DWMMC_BMOD
,
BMOD_SWRESET
);
do
{
data
=
mmio_read_32
(
base
+
DWMMC_BMOD
);
}
while
(
data
&
BMOD_SWRESET
);
/* enable DMA in BMOD */
data
|=
BMOD_ENABLE
|
BMOD_FB
;
mmio_write_32
(
base
+
DWMMC_BMOD
,
data
);
udelay
(
100
);
dw_set_clk
(
EMMC_BOOT_CLK_RATE
);
udelay
(
100
);
}
static
int
dw_send_cmd
(
emmc_cmd_t
*
cmd
)
{
unsigned
int
op
,
data
,
err_mask
;
uintptr_t
base
;
int
timeout
;
assert
(
cmd
);
base
=
dw_params
.
reg_base
;
switch
(
cmd
->
cmd_idx
)
{
case
EMMC_CMD0
:
op
=
CMD_SEND_INIT
;
break
;
case
EMMC_CMD12
:
op
=
CMD_STOP_ABORT_CMD
;
break
;
case
EMMC_CMD13
:
op
=
CMD_WAIT_PRVDATA_COMPLETE
;
break
;
case
EMMC_CMD8
:
case
EMMC_CMD17
:
case
EMMC_CMD18
:
op
=
CMD_DATA_TRANS_EXPECT
|
CMD_WAIT_PRVDATA_COMPLETE
;
break
;
case
EMMC_CMD24
:
case
EMMC_CMD25
:
op
=
CMD_WRITE
|
CMD_DATA_TRANS_EXPECT
|
CMD_WAIT_PRVDATA_COMPLETE
;
break
;
default:
op
=
0
;
break
;
}
op
|=
CMD_USE_HOLD_REG
|
CMD_START
;
switch
(
cmd
->
resp_type
)
{
case
0
:
break
;
case
EMMC_RESPONSE_R2
:
op
|=
CMD_RESP_EXPECT
|
CMD_CHECK_RESP_CRC
|
CMD_RESP_LEN
;
break
;
case
EMMC_RESPONSE_R3
:
op
|=
CMD_RESP_EXPECT
;
break
;
default:
op
|=
CMD_RESP_EXPECT
|
CMD_CHECK_RESP_CRC
;
break
;
}
timeout
=
TIMEOUT
;
do
{
data
=
mmio_read_32
(
base
+
DWMMC_STATUS
);
if
(
--
timeout
<=
0
)
panic
();
}
while
(
data
&
STATUS_DATA_BUSY
);
mmio_write_32
(
base
+
DWMMC_RINTSTS
,
~
0
);
mmio_write_32
(
base
+
DWMMC_CMDARG
,
cmd
->
cmd_arg
);
mmio_write_32
(
base
+
DWMMC_CMD
,
op
|
cmd
->
cmd_idx
);
err_mask
=
INT_EBE
|
INT_HLE
|
INT_RTO
|
INT_RCRC
|
INT_RE
|
INT_DCRC
|
INT_DRT
|
INT_SBE
;
timeout
=
TIMEOUT
;
do
{
udelay
(
500
);
data
=
mmio_read_32
(
base
+
DWMMC_RINTSTS
);
if
(
data
&
err_mask
)
return
-
EIO
;
if
(
data
&
INT_DTO
)
break
;
if
(
--
timeout
==
0
)
{
ERROR
(
"%s, RINTSTS:0x%x
\n
"
,
__func__
,
data
);
panic
();
}
}
while
(
!
(
data
&
INT_CMD_DONE
));
if
(
op
&
CMD_RESP_EXPECT
)
{
cmd
->
resp_data
[
0
]
=
mmio_read_32
(
base
+
DWMMC_RESP0
);
if
(
op
&
CMD_RESP_LEN
)
{
cmd
->
resp_data
[
1
]
=
mmio_read_32
(
base
+
DWMMC_RESP1
);
cmd
->
resp_data
[
2
]
=
mmio_read_32
(
base
+
DWMMC_RESP2
);
cmd
->
resp_data
[
3
]
=
mmio_read_32
(
base
+
DWMMC_RESP3
);
}
}
return
0
;
}
static
int
dw_set_ios
(
int
clk
,
int
width
)
{
switch
(
width
)
{
case
EMMC_BUS_WIDTH_1
:
mmio_write_32
(
dw_params
.
reg_base
+
DWMMC_CTYPE
,
CTYPE_1BIT
);
break
;
case
EMMC_BUS_WIDTH_4
:
mmio_write_32
(
dw_params
.
reg_base
+
DWMMC_CTYPE
,
CTYPE_4BIT
);
break
;
case
EMMC_BUS_WIDTH_8
:
mmio_write_32
(
dw_params
.
reg_base
+
DWMMC_CTYPE
,
CTYPE_8BIT
);
break
;
default:
assert
(
0
);
}
dw_set_clk
(
clk
);
return
0
;
}
static
int
dw_prepare
(
int
lba
,
uintptr_t
buf
,
size_t
size
)
{
struct
dw_idmac_desc
*
desc
;
int
desc_cnt
,
i
,
last
;
uintptr_t
base
;
assert
(((
buf
&
EMMC_BLOCK_MASK
)
==
0
)
&&
((
size
%
EMMC_BLOCK_SIZE
)
==
0
)
&&
(
dw_params
.
desc_size
>
0
)
&&
((
dw_params
.
reg_base
&
EMMC_BLOCK_MASK
)
==
0
)
&&
((
dw_params
.
desc_base
&
EMMC_BLOCK_MASK
)
==
0
)
&&
((
dw_params
.
desc_size
&
EMMC_BLOCK_MASK
)
==
0
));
desc_cnt
=
(
size
+
DWMMC_DMA_MAX_BUFFER_SIZE
-
1
)
/
DWMMC_DMA_MAX_BUFFER_SIZE
;
assert
(
desc_cnt
*
sizeof
(
struct
dw_idmac_desc
)
<
dw_params
.
desc_size
);
base
=
dw_params
.
reg_base
;
desc
=
(
struct
dw_idmac_desc
*
)
dw_params
.
desc_base
;
mmio_write_32
(
base
+
DWMMC_BYTCNT
,
size
);
mmio_write_32
(
base
+
DWMMC_RINTSTS
,
~
0
);
for
(
i
=
0
;
i
<
desc_cnt
;
i
++
)
{
desc
[
i
].
des0
=
IDMAC_DES0_OWN
|
IDMAC_DES0_CH
|
IDMAC_DES0_DIC
;
desc
[
i
].
des1
=
IDMAC_DES1_BS1
(
DWMMC_DMA_MAX_BUFFER_SIZE
);
desc
[
i
].
des2
=
buf
+
DWMMC_DMA_MAX_BUFFER_SIZE
*
i
;
desc
[
i
].
des3
=
dw_params
.
desc_base
+
(
sizeof
(
struct
dw_idmac_desc
))
*
(
i
+
1
);
}
/* first descriptor */
desc
->
des0
|=
IDMAC_DES0_FS
;
/* last descriptor */
last
=
desc_cnt
-
1
;
(
desc
+
last
)
->
des0
|=
IDMAC_DES0_LD
;
(
desc
+
last
)
->
des0
&=
~
(
IDMAC_DES0_DIC
|
IDMAC_DES0_CH
);
(
desc
+
last
)
->
des1
=
IDMAC_DES1_BS1
(
size
-
(
last
*
DWMMC_DMA_MAX_BUFFER_SIZE
));
/* set next descriptor address as 0 */
(
desc
+
last
)
->
des3
=
0
;
mmio_write_32
(
base
+
DWMMC_DBADDR
,
dw_params
.
desc_base
);
clean_dcache_range
(
dw_params
.
desc_base
,
desc_cnt
*
DWMMC_DMA_MAX_BUFFER_SIZE
);
return
0
;
}
static
int
dw_read
(
int
lba
,
uintptr_t
buf
,
size_t
size
)
{
return
0
;
}
static
int
dw_write
(
int
lba
,
uintptr_t
buf
,
size_t
size
)
{
return
0
;
}
void
dw_mmc_init
(
dw_mmc_params_t
*
params
)
{
assert
((
params
!=
0
)
&&
((
params
->
reg_base
&
EMMC_BLOCK_MASK
)
==
0
)
&&
((
params
->
desc_base
&
EMMC_BLOCK_MASK
)
==
0
)
&&
((
params
->
desc_size
&
EMMC_BLOCK_MASK
)
==
0
)
&&
(
params
->
desc_size
>
0
)
&&
(
params
->
clk_rate
>
0
)
&&
((
params
->
bus_width
==
EMMC_BUS_WIDTH_1
)
||
(
params
->
bus_width
==
EMMC_BUS_WIDTH_4
)
||
(
params
->
bus_width
==
EMMC_BUS_WIDTH_8
)));
memcpy
(
&
dw_params
,
params
,
sizeof
(
dw_mmc_params_t
));
emmc_init
(
&
dw_mmc_ops
,
params
->
clk_rate
,
params
->
bus_width
,
params
->
flags
);
}
include/drivers/synopsys/dw_mmc.h
0 → 100644
View file @
e02be207
/*
* Copyright (c) 2016-2017, ARM Limited and Contributors. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of ARM nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific
* prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef __DW_MMC_H__
#define __DW_MMC_H__
typedef
struct
dw_mmc_params
{
uintptr_t
reg_base
;
uintptr_t
desc_base
;
size_t
desc_size
;
int
clk_rate
;
int
bus_width
;
unsigned
int
flags
;
}
dw_mmc_params_t
;
void
dw_mmc_init
(
dw_mmc_params_t
*
params
);
#endif
/* __DW_MMC_H__ */
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