1 /*
2 * himci_boot.c
3 *
4 * This file is only for emmc boot.
5 *
6 * Copyright (c) 2020 HiSilicon (Shanghai) Technologies CO., LIMITED.
7 *
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by the
10 * Free Software Foundation; either version 2 of the License, or (at your
11 * option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 */
22
23 #include <config.h>
24 #include <asm/arch/platform.h>
25 #include <compiler.h>
26
27 #ifndef CONFIG_ASIC
28 #define DELAY_US 10
29 #else
30 #define DELAY_US 1000
31 #endif
32
33 #define MMC_BLK_SZ 512
34
35 /* mmc.h */
36 #define MMC_CMD_GO_IDLE_STATE 0
37 #define MMC_CMD_SEND_OP_COND 1
38 #define MMC_CMD_ALL_SEND_CID 2
39 #define MMC_CMD_SET_RELATIVE_ADDR 3
40 #define MMC_CMD_SET_DSR 4
41 #define MMC_CMD_SWITCH 6
42 #define MMC_CMD_SELECT_CARD 7
43 #define MMC_CMD_SEND_EXT_CSD 8
44 #define MMC_CMD_SEND_CSD 9
45 #define MMC_CMD_SEND_CID 10
46 #define MMC_CMD_STOP_TRANSMISSION 12
47 #define MMC_CMD_SEND_STATUS 13
48 #define MMC_CMD_SET_BLOCKLEN 16
49 #define MMC_CMD_READ_SINGLE_BLOCK 17
50 #define MMC_CMD_READ_MULTIPLE_BLOCK 18
51 #define MMC_CMD_WRITE_SINGLE_BLOCK 24
52 #define MMC_CMD_WRITE_MULTIPLE_BLOCK 25
53 #define MMC_CMD_APP_CMD 55
54 #define SD_CMD_SEND_IF_COND 8
55 #define SD_CMD_APP_SEND_OP_COND 41
56 #define SD_CMD_APP_SET_BUS_WIDTH 6
57
58 #define OCR_BUSY 0x80000000
59 #define OCR_HCS 0x40000000
60
61 #define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */
62 #define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */
63
64 #define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target byte to value */
65
66 #define EXT_CSD_BUS_WIDTH 183 /* R/W */
67 #define EXT_CSD_HS_TIMING 185 /* R/W */
68
69 #define EXT_CSD_BUS_WIDTH_1 0 /* Card is in 1 bit mode */
70 #define EXT_CSD_BUS_WIDTH_4 1 /* Card is in 4 bit mode */
71 #define EXT_CSD_BUS_WIDTH_8 2 /* Card is in 8 bit mode */
72
delay(unsigned int cnt)73 static inline void delay(unsigned int cnt)
74 {
75 while (cnt--)
76 __asm__ __volatile__("nop");
77 }
78
79 #ifdef CONFIG_SDHCI
80 #define SDHCI_BUFFER 0x20
81 #define SDHCI_INT_STATUS 0x30
82 #define SDHCI_INT_DATA_AVAIL 0x00000020
83 #define SDHCI_INT_ERROR_MASK 0xFFFF8000
84
85 #define debug_printf(fmt, args...) ;
86
sdhci_readl(unsigned addr)87 static inline unsigned int sdhci_readl(unsigned addr)
88 {
89 uintptr_t reg_addr = EMMC_BASE_REG + addr;
90 return *((volatile unsigned *) (reg_addr));
91 }
92
sdhci_writel(unsigned val,unsigned addr)93 static inline void sdhci_writel(unsigned val, unsigned addr)
94 {
95 uintptr_t reg_addr = EMMC_BASE_REG + addr;
96 (*(volatile unsigned *) (reg_addr)) = (val);
97 }
98
sdhci_read_block_pio(void * data_addr,unsigned int block)99 static void sdhci_read_block_pio(void *data_addr, unsigned int block)
100 {
101 const unsigned int offset = sizeof(unsigned int);
102 unsigned int size;
103 unsigned char *buf;
104
105 size = MMC_BLK_SZ;
106 buf = (unsigned char *)data_addr + MMC_BLK_SZ * block;
107 while (size) {
108 *(unsigned int *)buf = sdhci_readl(SDHCI_BUFFER);
109 buf += offset;
110 size -= offset;
111 }
112 }
113
sdhci_check_int_status(unsigned int mask,unsigned int timeout)114 int sdhci_check_int_status(unsigned int mask, unsigned int timeout)
115 {
116 unsigned int reg;
117
118 for (;;) {
119 reg = sdhci_readl(SDHCI_INT_STATUS);
120 if (reg & mask)
121 break;
122 if (!(--timeout)) {
123 debug_printf("wait int status time out, reg = 0x%x, mask = 0x%x\n",
124 reg, mask);
125 return -1;
126 }
127 if (reg & SDHCI_INT_ERROR_MASK) {
128 debug_printf("int err: reg = 0x%x\n", reg);
129 return -1;
130 }
131
132 delay(1000 * DELAY_US); /* ms converted to us multiplied by 1000 */
133 }
134
135 return 0;
136 }
137
copy_step1_to_ddr(unsigned int * dst,unsigned int * src,unsigned int size)138 static void copy_step1_to_ddr(unsigned int *dst, unsigned int *src, unsigned int size)
139 {
140 const unsigned int cycle = size / sizeof(unsigned int);
141 unsigned int i;
142
143 for (i = 0; i < cycle; i++)
144 *dst++ = *src++;
145 }
146
147 #define CP_STEP1_SIZE 0x8000
sdhci_read_boot_data(void * data_addr,unsigned int data_size)148 int sdhci_read_boot_data(void *data_addr, unsigned int data_size)
149 {
150 const unsigned int timeout = 2000; /* 2s timeout: 2000000 * 1us */
151 unsigned int blocks = 0;
152 unsigned int read_block;
153 int ret;
154
155 if (data_size <= CP_STEP1_SIZE) {
156 copy_step1_to_ddr((void *)data_addr, (void *)RAM_START_ADRS, data_size);
157 return 0;
158 } else {
159 copy_step1_to_ddr((void *)data_addr, (void *)RAM_START_ADRS, CP_STEP1_SIZE);
160 data_addr += CP_STEP1_SIZE;
161 data_size -= CP_STEP1_SIZE;
162 }
163
164 if (data_size % MMC_BLK_SZ) {
165 debug_printf("sdhci_read_boot_data error\n");
166 debug_printf("data_size:%d not round by block size\n", data_size);
167 read_block = data_size / MMC_BLK_SZ + 1;
168 } else {
169 read_block = data_size / MMC_BLK_SZ;
170 }
171
172 while (1) {
173 ret = sdhci_check_int_status(SDHCI_INT_DATA_AVAIL, timeout);
174 if (ret) {
175 debug_printf("wait data available int time out\n");
176 return -1;
177 }
178
179 sdhci_writel(SDHCI_INT_DATA_AVAIL, SDHCI_INT_STATUS);
180 sdhci_read_block_pio(data_addr, blocks);
181
182 blocks++;
183 if (blocks == read_block)
184 break;
185 }
186
187 return 0;
188 }
emmc_boot_read(void * ptr,unsigned int size)189 void emmc_boot_read(void *ptr, unsigned int size)
190 {
191 sdhci_read_boot_data(ptr, size);
192 }
193 #endif
194