@ -1,5 +1,5 @@
/*
* Copyright ( C ) 2016 Socionext Inc .
* Copyright ( C ) 2017 Socionext Inc .
* Author : Masahiro Yamada < yamada . masahiro @ socionext . com >
*
* SPDX - License - Identifier : GPL - 2.0 +
@ -7,87 +7,218 @@
# include <common.h>
# include <spl.h>
# include <linux/bitops.h>
# include <linux/compat.h>
# include <linux/io.h>
# include <asm/processor.h>
# include "../soc-info.h"
struct uniphier_romfunc_table {
void * mmc_send_cmd ;
void * mmc_card_blockaddr ;
void * mmc_switch_part ;
void * mmc_load_image ;
} ;
# define MMC_CMD_SWITCH 6
# define MMC_CMD_SELECT_CARD 7
# define MMC_CMD_SEND_CSD 9
# define MMC_CMD_READ_MULTIPLE_BLOCK 18
static const struct uniphier_romfunc_table uniphier_ld11_romfunc_table = {
. mmc_send_cmd = ( void * ) 0x20d8 ,
. mmc_card_blockaddr = ( void * ) 0x1b68 ,
. mmc_switch_part = ( void * ) 0x1c38 ,
. mmc_load_image = ( void * ) 0x2e48 ,
} ;
# define EXT_CSD_PART_CONF 179 /* R/W */
# define MMC_RSP_PRESENT BIT(0)
# define MMC_RSP_136 BIT(1) /* 136 bit response */
# define MMC_RSP_CRC BIT(2) /* expect valid crc */
# define MMC_RSP_BUSY BIT(3) /* card may send busy */
# define MMC_RSP_OPCODE BIT(4) /* response contains opcode */
# define MMC_RSP_NONE (0)
# define MMC_RSP_R1 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
# define MMC_RSP_R1b (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE | \
MMC_RSP_BUSY )
# define MMC_RSP_R2 (MMC_RSP_PRESENT | MMC_RSP_136 | MMC_RSP_CRC)
# define MMC_RSP_R3 (MMC_RSP_PRESENT)
# define MMC_RSP_R4 (MMC_RSP_PRESENT)
# define MMC_RSP_R5 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
# define MMC_RSP_R6 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
# define MMC_RSP_R7 (MMC_RSP_PRESENT | MMC_RSP_CRC | MMC_RSP_OPCODE)
# define SDHCI_DMA_ADDRESS 0x00
# define SDHCI_BLOCK_SIZE 0x04
# define SDHCI_MAKE_BLKSZ(dma, blksz) ((((dma) & 0x7) << 12) | ((blksz) & 0xFFF))
# define SDHCI_BLOCK_COUNT 0x06
# define SDHCI_ARGUMENT 0x08
# define SDHCI_TRANSFER_MODE 0x0C
# define SDHCI_TRNS_DMA BIT(0)
# define SDHCI_TRNS_BLK_CNT_EN BIT(1)
# define SDHCI_TRNS_ACMD12 BIT(2)
# define SDHCI_TRNS_READ BIT(4)
# define SDHCI_TRNS_MULTI BIT(5)
# define SDHCI_COMMAND 0x0E
# define SDHCI_CMD_RESP_MASK 0x03
# define SDHCI_CMD_CRC 0x08
# define SDHCI_CMD_INDEX 0x10
# define SDHCI_CMD_DATA 0x20
# define SDHCI_CMD_ABORTCMD 0xC0
# define SDHCI_CMD_RESP_NONE 0x00
# define SDHCI_CMD_RESP_LONG 0x01
# define SDHCI_CMD_RESP_SHORT 0x02
# define SDHCI_CMD_RESP_SHORT_BUSY 0x03
# define SDHCI_MAKE_CMD(c, f) ((((c) & 0xff) << 8) | ((f) & 0xff))
# define SDHCI_RESPONSE 0x10
# define SDHCI_HOST_CONTROL 0x28
# define SDHCI_CTRL_DMA_MASK 0x18
# define SDHCI_CTRL_SDMA 0x00
# define SDHCI_BLOCK_GAP_CONTROL 0x2A
# define SDHCI_SOFTWARE_RESET 0x2F
# define SDHCI_RESET_CMD 0x02
# define SDHCI_RESET_DATA 0x04
# define SDHCI_INT_STATUS 0x30
# define SDHCI_INT_RESPONSE BIT(0)
# define SDHCI_INT_DATA_END BIT(1)
# define SDHCI_INT_ERROR BIT(15)
# define SDHCI_SIGNAL_ENABLE 0x38
/* RCA assigned by Boot ROM */
# define UNIPHIER_EMMC_RCA 0x1000
static const struct uniphier_romfunc_table uniphier_ld20_romfunc_table = {
. mmc_send_cmd = ( void * ) 0x2130 ,
. mmc_card_blockaddr = ( void * ) 0x1ba0 ,
. mmc_switch_part = ( void * ) 0x1c70 ,
. mmc_load_image = ( void * ) 0x2ef0 ,
struct uniphier_mmc_cmd {
unsigned int cmdidx ;
unsigned int resp_type ;
unsigned int cmdarg ;
unsigned int is_data ;
} ;
int uniphier_rom_get_mmc_funcptr ( int ( * * send_cmd ) ( u32 , u32 ) ,
int ( * * card_blockaddr ) ( u32 ) ,
int ( * * switch_part ) ( int ) ,
int ( * * load_image ) ( u32 , uintptr_t , u32 ) )
static int uniphier_emmc_send_cmd ( void __iomem * host_base ,
struct uniphier_mmc_cmd * cmd )
{
const struct uniphier_romfunc_table * table ;
switch ( uniphier_get_soc_id ( ) ) {
case UNIPHIER_LD11_ID :
table = & uniphier_ld11_romfunc_table ;
break ;
case UNIPHIER_LD20_ID :
table = & uniphier_ld20_romfunc_table ;
break ;
default :
printf ( " unsupported SoC \n " ) ;
return - EINVAL ;
}
u32 mode = 0 ;
u32 mask = SDHCI_INT_RESPONSE ;
u32 stat , flags ;
writel ( U32_MAX , host_base + SDHCI_INT_STATUS ) ;
writel ( 0 , host_base + SDHCI_SIGNAL_ENABLE ) ;
writel ( cmd - > cmdarg , host_base + SDHCI_ARGUMENT ) ;
if ( cmd - > is_data )
mode = SDHCI_TRNS_DMA | SDHCI_TRNS_BLK_CNT_EN |
SDHCI_TRNS_ACMD12 | SDHCI_TRNS_READ |
SDHCI_TRNS_MULTI ;
writew ( mode , host_base + SDHCI_TRANSFER_MODE ) ;
if ( ! ( cmd - > resp_type & MMC_RSP_PRESENT ) )
flags = SDHCI_CMD_RESP_NONE ;
else if ( cmd - > resp_type & MMC_RSP_136 )
flags = SDHCI_CMD_RESP_LONG ;
else if ( cmd - > resp_type & MMC_RSP_BUSY )
flags = SDHCI_CMD_RESP_SHORT_BUSY ;
else
flags = SDHCI_CMD_RESP_SHORT ;
if ( cmd - > resp_type & MMC_RSP_CRC )
flags | = SDHCI_CMD_CRC ;
if ( cmd - > resp_type & MMC_RSP_OPCODE )
flags | = SDHCI_CMD_INDEX ;
if ( cmd - > is_data )
flags | = SDHCI_CMD_DATA ;
* send_cmd = table - > mmc_send_cmd ;
* card_blockaddr = table - > mmc_card_blockaddr ;
* switch_part = table - > mmc_switch_part ;
* load_image = table - > mmc_load_image ;
if ( cmd - > resp_type & MMC_RSP_BUSY | | cmd - > is_data )
mask | = SDHCI_INT_DATA_END ;
writew ( SDHCI_MAKE_CMD ( cmd - > cmdidx , flags ) , host_base + SDHCI_COMMAND ) ;
do {
stat = readl ( host_base + SDHCI_INT_STATUS ) ;
if ( stat & SDHCI_INT_ERROR )
return - EIO ;
} while ( ( stat & mask ) ! = mask ) ;
return 0 ;
}
static int spl_board_load_image ( struct spl_image_info * spl_image ,
struct spl_boot_device * bootdev )
static int uniphier_emmc_switch_part ( void __iomem * host_base , int part_num )
{
int ( * send_cmd ) ( u32 cmd , u32 arg ) ;
int ( * card_blockaddr ) ( u32 rca ) ;
int ( * switch_part ) ( int part ) ;
int ( * load_image ) ( u32 dev_addr , uintptr_t load_addr , u32 block_cnt ) ;
u32 dev_addr = CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR ;
const u32 rca = 0x1000 ; /* RCA assigned by Boot ROM */
struct uniphier_mmc_cmd cmd = { } ;
cmd . cmdidx = MMC_CMD_SWITCH ;
cmd . resp_type = MMC_RSP_R1b ;
cmd . cmdarg = ( EXT_CSD_PART_CONF < < 16 ) | ( part_num < < 8 ) | ( 3 < < 24 ) ;
return uniphier_emmc_send_cmd ( host_base , & cmd ) ;
}
static int uniphier_emmc_is_over_2gb ( void __iomem * host_base )
{
struct uniphier_mmc_cmd cmd = { } ;
u32 csd40 , csd72 ; /* CSD[71:40], CSD[103:72] */
int ret ;
ret = uniphier_rom_get_mmc_funcptr ( & send_cmd , & card_blockaddr ,
& switch_part , & load_image ) ;
cmd . cmdidx = MMC_CMD_SEND_CSD ;
cmd . resp_type = MMC_RSP_R2 ;
cmd . cmdarg = UNIPHIER_EMMC_RCA < < 16 ;
ret = uniphier_emmc_send_cmd ( host_base , & cmd ) ;
if ( ret )
return ret ;
csd40 = readl ( host_base + SDHCI_RESPONSE + 4 ) ;
csd72 = readl ( host_base + SDHCI_RESPONSE + 8 ) ;
return ! ( ~ csd40 & 0xffc00380 ) & & ! ( ~ csd72 & 0x3 ) ;
}
static int uniphier_emmc_load_image ( void __iomem * host_base , u32 dev_addr ,
unsigned long load_addr , u32 block_cnt )
{
struct uniphier_mmc_cmd cmd = { } ;
u8 tmp ;
WARN_ON ( load_addr > > 32 ) ;
writel ( load_addr , host_base + SDHCI_DMA_ADDRESS ) ;
writew ( SDHCI_MAKE_BLKSZ ( 7 , 512 ) , host_base + SDHCI_BLOCK_SIZE ) ;
writew ( block_cnt , host_base + SDHCI_BLOCK_COUNT ) ;
tmp = readb ( host_base + SDHCI_HOST_CONTROL ) ;
tmp & = ~ SDHCI_CTRL_DMA_MASK ;
tmp | = SDHCI_CTRL_SDMA ;
writeb ( tmp , host_base + SDHCI_HOST_CONTROL ) ;
tmp = readb ( host_base + SDHCI_BLOCK_GAP_CONTROL ) ;
tmp & = ~ 1 ; /* clear Stop At Block Gap Request */
writeb ( tmp , host_base + SDHCI_BLOCK_GAP_CONTROL ) ;
cmd . cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK ;
cmd . resp_type = MMC_RSP_R1 ;
cmd . cmdarg = dev_addr ;
cmd . is_data = 1 ;
return uniphier_emmc_send_cmd ( host_base , & cmd ) ;
}
static int spl_board_load_image ( struct spl_image_info * spl_image ,
struct spl_boot_device * bootdev )
{
u32 dev_addr = CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR ;
void __iomem * host_base = ( void __iomem * ) 0x5a000200 ;
struct uniphier_mmc_cmd cmd = { } ;
int ret ;
/*
* deselect card before SEND_CSD command .
* Do not check the return code . It fails , but it is OK .
*/
( * send_cmd ) ( 0x071a0000 , 0 ) ; /* CMD7 (arg=0) */
cmd . cmdidx = MMC_CMD_SELECT_CARD ;
cmd . resp_type = MMC_RSP_R1 ;
uniphier_emmc_send_cmd ( host_base , & cmd ) ; /* CMD7 (arg=0) */
/* reset CMD Line */
writeb ( 0x6 , 0x5a00022f ) ;
while ( readb ( 0x5a00022f ) )
writeb ( SDHCI_RESET_CMD | SDHCI_RESET_DATA ,
host_base + SDHCI_SOFTWARE_RESET ) ;
while ( readb ( host_base + SDHCI_SOFTWARE_RESET ) )
cpu_relax ( ) ;
ret = ( * card_blockaddr ) ( rca ) ;
ret = uniphier_emmc_is_over_2gb ( host_base ) ;
if ( ret < 0 )
return ret ;
if ( ret ) {
debug ( " card is block addressing \n " ) ;
} else {
@ -95,15 +226,20 @@ static int spl_board_load_image(struct spl_image_info *spl_image,
dev_addr * = 512 ;
}
ret = ( * send_cmd ) ( 0x071a0000 , rca < < 16 ) ; /* CMD7: select card again */
cmd . cmdarg = UNIPHIER_EMMC_RCA < < 16 ;
/* select card again */
ret = uniphier_emmc_send_cmd ( host_base , & cmd ) ;
if ( ret )
printf ( " failed to select card \n " ) ;
ret = ( * switch_part ) ( 1 ) ; /* Switch to Boot Partition 1 */
/* Switch to Boot Partition 1 */
ret = uniphier_emmc_switch_part ( host_base , 1 ) ;
if ( ret )
printf ( " failed to switch partition \n " ) ;
ret = ( * load_image ) ( dev_addr , CONFIG_SYS_TEXT_BASE , 1 ) ;
ret = uniphier_emmc_load_image ( host_base , dev_addr ,
CONFIG_SYS_TEXT_BASE , 1 ) ;
if ( ret ) {
printf ( " failed to load image \n " ) ;
return ret ;
@ -113,8 +249,9 @@ static int spl_board_load_image(struct spl_image_info *spl_image,
if ( ret )
return ret ;
ret = ( * load_image ) ( dev_addr , spl_image - > load_addr ,
spl_image - > size / 512 ) ;
ret = uniphier_emmc_load_image ( host_base , dev_addr ,
spl_image - > load_addr ,
spl_image - > size / 512 ) ;
if ( ret ) {
printf ( " failed to load image \n " ) ;
return ret ;