1*54fd6939SJiyong Park /*
2*54fd6939SJiyong Park * Copyright (c) 2019, Linaro Limited
3*54fd6939SJiyong Park * Copyright (c) 2019, Ying-Chun Liu (PaulLiu) <[email protected]>
4*54fd6939SJiyong Park *
5*54fd6939SJiyong Park * SPDX-License-Identifier: BSD-3-Clause
6*54fd6939SJiyong Park */
7*54fd6939SJiyong Park
8*54fd6939SJiyong Park #include <arch.h>
9*54fd6939SJiyong Park #include <arch_helpers.h>
10*54fd6939SJiyong Park #include <assert.h>
11*54fd6939SJiyong Park #include <common/debug.h>
12*54fd6939SJiyong Park #include <lib/mmio.h>
13*54fd6939SJiyong Park #include <drivers/delay_timer.h>
14*54fd6939SJiyong Park #include <drivers/rpi3/sdhost/rpi3_sdhost.h>
15*54fd6939SJiyong Park #include <drivers/mmc.h>
16*54fd6939SJiyong Park #include <drivers/rpi3/gpio/rpi3_gpio.h>
17*54fd6939SJiyong Park #include <errno.h>
18*54fd6939SJiyong Park #include <string.h>
19*54fd6939SJiyong Park
20*54fd6939SJiyong Park static void rpi3_sdhost_initialize(void);
21*54fd6939SJiyong Park static int rpi3_sdhost_send_cmd(struct mmc_cmd *cmd);
22*54fd6939SJiyong Park static int rpi3_sdhost_set_ios(unsigned int clk, unsigned int width);
23*54fd6939SJiyong Park static int rpi3_sdhost_prepare(int lba, uintptr_t buf, size_t size);
24*54fd6939SJiyong Park static int rpi3_sdhost_read(int lba, uintptr_t buf, size_t size);
25*54fd6939SJiyong Park static int rpi3_sdhost_write(int lba, uintptr_t buf, size_t size);
26*54fd6939SJiyong Park
27*54fd6939SJiyong Park static const struct mmc_ops rpi3_sdhost_ops = {
28*54fd6939SJiyong Park .init = rpi3_sdhost_initialize,
29*54fd6939SJiyong Park .send_cmd = rpi3_sdhost_send_cmd,
30*54fd6939SJiyong Park .set_ios = rpi3_sdhost_set_ios,
31*54fd6939SJiyong Park .prepare = rpi3_sdhost_prepare,
32*54fd6939SJiyong Park .read = rpi3_sdhost_read,
33*54fd6939SJiyong Park .write = rpi3_sdhost_write,
34*54fd6939SJiyong Park };
35*54fd6939SJiyong Park
36*54fd6939SJiyong Park static struct rpi3_sdhost_params rpi3_sdhost_params;
37*54fd6939SJiyong Park
38*54fd6939SJiyong Park /**
39*54fd6939SJiyong Park * Wait for command being processed.
40*54fd6939SJiyong Park *
41*54fd6939SJiyong Park * This function waits the command being processed. It compares
42*54fd6939SJiyong Park * the ENABLE flag of the HC_COMMAND register. When ENABLE flag disappeared
43*54fd6939SJiyong Park * it means the command is processed by the SDHOST.
44*54fd6939SJiyong Park * The timeout is currently 1000*100 us = 100 ms.
45*54fd6939SJiyong Park *
46*54fd6939SJiyong Park * @return 0: command finished. 1: command timed out.
47*54fd6939SJiyong Park */
rpi3_sdhost_waitcommand(void)48*54fd6939SJiyong Park static int rpi3_sdhost_waitcommand(void)
49*54fd6939SJiyong Park {
50*54fd6939SJiyong Park uintptr_t reg_base = rpi3_sdhost_params.reg_base;
51*54fd6939SJiyong Park
52*54fd6939SJiyong Park volatile int timeout = 1000;
53*54fd6939SJiyong Park
54*54fd6939SJiyong Park while ((mmio_read_32(reg_base + HC_COMMAND) & HC_CMD_ENABLE)
55*54fd6939SJiyong Park && (--timeout > 0)) {
56*54fd6939SJiyong Park udelay(100);
57*54fd6939SJiyong Park }
58*54fd6939SJiyong Park
59*54fd6939SJiyong Park return ((timeout > 0) ? 0 : (-(ETIMEDOUT)));
60*54fd6939SJiyong Park }
61*54fd6939SJiyong Park
62*54fd6939SJiyong Park /**
63*54fd6939SJiyong Park * Send the command and argument to the SDHOST
64*54fd6939SJiyong Park *
65*54fd6939SJiyong Park * This function will wait for the previous command finished. And then
66*54fd6939SJiyong Park * clear any error status of previous command. And then
67*54fd6939SJiyong Park * send out the command and args. The command will be turned on the ENABLE
68*54fd6939SJiyong Park * flag before sending out.
69*54fd6939SJiyong Park */
send_command_raw(unsigned int cmd,unsigned int arg)70*54fd6939SJiyong Park static void send_command_raw(unsigned int cmd, unsigned int arg)
71*54fd6939SJiyong Park {
72*54fd6939SJiyong Park unsigned int status;
73*54fd6939SJiyong Park uintptr_t reg_base = rpi3_sdhost_params.reg_base;
74*54fd6939SJiyong Park
75*54fd6939SJiyong Park /* wait for previous command finish */
76*54fd6939SJiyong Park rpi3_sdhost_waitcommand();
77*54fd6939SJiyong Park
78*54fd6939SJiyong Park /* clean error status */
79*54fd6939SJiyong Park status = mmio_read_32(reg_base + HC_HOSTSTATUS);
80*54fd6939SJiyong Park if (status & HC_HSTST_MASK_ERROR_ALL)
81*54fd6939SJiyong Park mmio_write_32(reg_base + HC_HOSTSTATUS, status);
82*54fd6939SJiyong Park
83*54fd6939SJiyong Park /* recording the command */
84*54fd6939SJiyong Park rpi3_sdhost_params.current_cmd = cmd & HC_CMD_COMMAND_MASK;
85*54fd6939SJiyong Park
86*54fd6939SJiyong Park /* send the argument and command */
87*54fd6939SJiyong Park mmio_write_32(reg_base + HC_ARGUMENT, arg);
88*54fd6939SJiyong Park mmio_write_32(reg_base + HC_COMMAND, cmd | HC_CMD_ENABLE);
89*54fd6939SJiyong Park }
90*54fd6939SJiyong Park
91*54fd6939SJiyong Park /**
92*54fd6939SJiyong Park * Send the command and argument to the SDHOST, decorated with control
93*54fd6939SJiyong Park * flags.
94*54fd6939SJiyong Park *
95*54fd6939SJiyong Park * This function will use send_command_raw to send the commands to SDHOST.
96*54fd6939SJiyong Park * But before sending it will decorate the command with control flags specific
97*54fd6939SJiyong Park * to SDHOST.
98*54fd6939SJiyong Park */
send_command_decorated(unsigned int cmd,unsigned int arg)99*54fd6939SJiyong Park static void send_command_decorated(unsigned int cmd, unsigned int arg)
100*54fd6939SJiyong Park {
101*54fd6939SJiyong Park unsigned int cmd_flags = 0;
102*54fd6939SJiyong Park
103*54fd6939SJiyong Park switch (cmd & HC_CMD_COMMAND_MASK) {
104*54fd6939SJiyong Park case MMC_CMD(0):
105*54fd6939SJiyong Park cmd_flags |= HC_CMD_RESPONSE_NONE;
106*54fd6939SJiyong Park break;
107*54fd6939SJiyong Park case MMC_ACMD(51):
108*54fd6939SJiyong Park cmd_flags |= HC_CMD_READ;
109*54fd6939SJiyong Park break;
110*54fd6939SJiyong Park case MMC_CMD(8):
111*54fd6939SJiyong Park case MMC_CMD(11):
112*54fd6939SJiyong Park case MMC_CMD(17):
113*54fd6939SJiyong Park case MMC_CMD(18):
114*54fd6939SJiyong Park cmd_flags |= HC_CMD_READ;
115*54fd6939SJiyong Park break;
116*54fd6939SJiyong Park case MMC_CMD(20):
117*54fd6939SJiyong Park case MMC_CMD(24):
118*54fd6939SJiyong Park case MMC_CMD(25):
119*54fd6939SJiyong Park cmd_flags |= HC_CMD_WRITE;
120*54fd6939SJiyong Park break;
121*54fd6939SJiyong Park case MMC_CMD(12):
122*54fd6939SJiyong Park cmd_flags |= HC_CMD_BUSY;
123*54fd6939SJiyong Park break;
124*54fd6939SJiyong Park default:
125*54fd6939SJiyong Park break;
126*54fd6939SJiyong Park }
127*54fd6939SJiyong Park send_command_raw(cmd | cmd_flags, arg);
128*54fd6939SJiyong Park }
129*54fd6939SJiyong Park
130*54fd6939SJiyong Park /**
131*54fd6939SJiyong Park * drains the FIFO on DATA port
132*54fd6939SJiyong Park *
133*54fd6939SJiyong Park * This function drains any data left in the DATA port.
134*54fd6939SJiyong Park */
rpi3_drain_fifo(void)135*54fd6939SJiyong Park static void rpi3_drain_fifo(void)
136*54fd6939SJiyong Park {
137*54fd6939SJiyong Park uintptr_t reg_base = rpi3_sdhost_params.reg_base;
138*54fd6939SJiyong Park volatile int timeout = 100000;
139*54fd6939SJiyong Park
140*54fd6939SJiyong Park rpi3_sdhost_waitcommand();
141*54fd6939SJiyong Park
142*54fd6939SJiyong Park while (mmio_read_32(reg_base + HC_HOSTSTATUS) & HC_HSTST_HAVEDATA) {
143*54fd6939SJiyong Park mmio_read_32(reg_base + HC_DATAPORT);
144*54fd6939SJiyong Park udelay(100);
145*54fd6939SJiyong Park }
146*54fd6939SJiyong Park
147*54fd6939SJiyong Park while (1) {
148*54fd6939SJiyong Park uint32_t edm, fsm;
149*54fd6939SJiyong Park
150*54fd6939SJiyong Park edm = mmio_read_32(reg_base + HC_DEBUG);
151*54fd6939SJiyong Park fsm = edm & HC_DBG_FSM_MASK;
152*54fd6939SJiyong Park
153*54fd6939SJiyong Park if ((fsm == HC_DBG_FSM_IDENTMODE) ||
154*54fd6939SJiyong Park (fsm == HC_DBG_FSM_DATAMODE))
155*54fd6939SJiyong Park break;
156*54fd6939SJiyong Park
157*54fd6939SJiyong Park if ((fsm == HC_DBG_FSM_READWAIT) ||
158*54fd6939SJiyong Park (fsm == HC_DBG_FSM_WRITESTART1) ||
159*54fd6939SJiyong Park (fsm == HC_DBG_FSM_READDATA)) {
160*54fd6939SJiyong Park mmio_write_32(reg_base + HC_DEBUG,
161*54fd6939SJiyong Park edm | HC_DBG_FORCE_DATA_MODE);
162*54fd6939SJiyong Park break;
163*54fd6939SJiyong Park }
164*54fd6939SJiyong Park
165*54fd6939SJiyong Park if (--timeout <= 0) {
166*54fd6939SJiyong Park ERROR("rpi3_sdhost: %s cannot recover stat\n",
167*54fd6939SJiyong Park __func__);
168*54fd6939SJiyong Park return;
169*54fd6939SJiyong Park }
170*54fd6939SJiyong Park }
171*54fd6939SJiyong Park }
172*54fd6939SJiyong Park
173*54fd6939SJiyong Park /**
174*54fd6939SJiyong Park * Dump SDHOST registers
175*54fd6939SJiyong Park */
rpi3_sdhost_print_regs(void)176*54fd6939SJiyong Park static void rpi3_sdhost_print_regs(void)
177*54fd6939SJiyong Park {
178*54fd6939SJiyong Park uintptr_t reg_base = rpi3_sdhost_params.reg_base;
179*54fd6939SJiyong Park
180*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_COMMAND: 0x%08x\n",
181*54fd6939SJiyong Park mmio_read_32(reg_base + HC_COMMAND));
182*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_ARGUMENT: 0x%08x\n",
183*54fd6939SJiyong Park mmio_read_32(reg_base + HC_ARGUMENT));
184*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_TIMEOUTCOUNTER: 0x%08x\n",
185*54fd6939SJiyong Park mmio_read_32(reg_base + HC_TIMEOUTCOUNTER));
186*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_CLOCKDIVISOR: 0x%08x\n",
187*54fd6939SJiyong Park mmio_read_32(reg_base + HC_CLOCKDIVISOR));
188*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_RESPONSE_0: 0x%08x\n",
189*54fd6939SJiyong Park mmio_read_32(reg_base + HC_RESPONSE_0));
190*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_RESPONSE_1: 0x%08x\n",
191*54fd6939SJiyong Park mmio_read_32(reg_base + HC_RESPONSE_1));
192*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_RESPONSE_2: 0x%08x\n",
193*54fd6939SJiyong Park mmio_read_32(reg_base + HC_RESPONSE_2));
194*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_RESPONSE_3: 0x%08x\n",
195*54fd6939SJiyong Park mmio_read_32(reg_base + HC_RESPONSE_3));
196*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_HOSTSTATUS: 0x%08x\n",
197*54fd6939SJiyong Park mmio_read_32(reg_base + HC_HOSTSTATUS));
198*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_POWER: 0x%08x\n",
199*54fd6939SJiyong Park mmio_read_32(reg_base + HC_POWER));
200*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_DEBUG: 0x%08x\n",
201*54fd6939SJiyong Park mmio_read_32(reg_base + HC_DEBUG));
202*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_HOSTCONFIG: 0x%08x\n",
203*54fd6939SJiyong Park mmio_read_32(reg_base + HC_HOSTCONFIG));
204*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_BLOCKSIZE: 0x%08x\n",
205*54fd6939SJiyong Park mmio_read_32(reg_base + HC_BLOCKSIZE));
206*54fd6939SJiyong Park INFO("rpi3_sdhost: HC_BLOCKCOUNT: 0x%08x\n",
207*54fd6939SJiyong Park mmio_read_32(reg_base + HC_BLOCKCOUNT));
208*54fd6939SJiyong Park }
209*54fd6939SJiyong Park
210*54fd6939SJiyong Park /**
211*54fd6939SJiyong Park * Reset SDHOST
212*54fd6939SJiyong Park */
rpi3_sdhost_reset(void)213*54fd6939SJiyong Park static void rpi3_sdhost_reset(void)
214*54fd6939SJiyong Park {
215*54fd6939SJiyong Park uintptr_t reg_base = rpi3_sdhost_params.reg_base;
216*54fd6939SJiyong Park unsigned int dbg;
217*54fd6939SJiyong Park uint32_t tmp1;
218*54fd6939SJiyong Park
219*54fd6939SJiyong Park mmio_write_32(reg_base + HC_POWER, 0);
220*54fd6939SJiyong Park mmio_write_32(reg_base + HC_COMMAND, 0);
221*54fd6939SJiyong Park mmio_write_32(reg_base + HC_ARGUMENT, 0);
222*54fd6939SJiyong Park
223*54fd6939SJiyong Park mmio_write_32(reg_base + HC_TIMEOUTCOUNTER, HC_TIMEOUT_DEFAULT);
224*54fd6939SJiyong Park mmio_write_32(reg_base + HC_CLOCKDIVISOR, 0);
225*54fd6939SJiyong Park mmio_write_32(reg_base + HC_HOSTSTATUS, HC_HSTST_RESET);
226*54fd6939SJiyong Park mmio_write_32(reg_base + HC_HOSTCONFIG, 0);
227*54fd6939SJiyong Park mmio_write_32(reg_base + HC_BLOCKSIZE, 0);
228*54fd6939SJiyong Park mmio_write_32(reg_base + HC_BLOCKCOUNT, 0);
229*54fd6939SJiyong Park
230*54fd6939SJiyong Park dbg = mmio_read_32(reg_base + HC_DEBUG);
231*54fd6939SJiyong Park dbg &= ~((HC_DBG_FIFO_THRESH_MASK << HC_DBG_FIFO_THRESH_READ_SHIFT) |
232*54fd6939SJiyong Park (HC_DBG_FIFO_THRESH_MASK << HC_DBG_FIFO_THRESH_WRITE_SHIFT));
233*54fd6939SJiyong Park dbg |= (HC_FIFO_THRESH_READ << HC_DBG_FIFO_THRESH_READ_SHIFT) |
234*54fd6939SJiyong Park (HC_FIFO_THRESH_WRITE << HC_DBG_FIFO_THRESH_WRITE_SHIFT);
235*54fd6939SJiyong Park mmio_write_32(reg_base + HC_DEBUG, dbg);
236*54fd6939SJiyong Park mdelay(250);
237*54fd6939SJiyong Park mmio_write_32(reg_base + HC_POWER, 1);
238*54fd6939SJiyong Park mdelay(250);
239*54fd6939SJiyong Park rpi3_sdhost_params.clk_rate = 0;
240*54fd6939SJiyong Park
241*54fd6939SJiyong Park mmio_write_32(reg_base + HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_MAXVAL);
242*54fd6939SJiyong Park tmp1 = mmio_read_32(reg_base + HC_HOSTCONFIG);
243*54fd6939SJiyong Park mmio_write_32(reg_base + HC_HOSTCONFIG, tmp1 | HC_HSTCF_INT_BUSY);
244*54fd6939SJiyong Park }
245*54fd6939SJiyong Park
rpi3_sdhost_initialize(void)246*54fd6939SJiyong Park static void rpi3_sdhost_initialize(void)
247*54fd6939SJiyong Park {
248*54fd6939SJiyong Park uintptr_t reg_base = rpi3_sdhost_params.reg_base;
249*54fd6939SJiyong Park
250*54fd6939SJiyong Park assert((rpi3_sdhost_params.reg_base & MMC_BLOCK_MASK) == 0);
251*54fd6939SJiyong Park
252*54fd6939SJiyong Park rpi3_sdhost_reset();
253*54fd6939SJiyong Park
254*54fd6939SJiyong Park mmio_write_32(reg_base + HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_PREFERVAL);
255*54fd6939SJiyong Park udelay(300);
256*54fd6939SJiyong Park }
257*54fd6939SJiyong Park
rpi3_sdhost_send_cmd(struct mmc_cmd * cmd)258*54fd6939SJiyong Park static int rpi3_sdhost_send_cmd(struct mmc_cmd *cmd)
259*54fd6939SJiyong Park {
260*54fd6939SJiyong Park uintptr_t reg_base = rpi3_sdhost_params.reg_base;
261*54fd6939SJiyong Park int err = 0;
262*54fd6939SJiyong Park uint32_t cmd_idx;
263*54fd6939SJiyong Park uint32_t cmd_arg;
264*54fd6939SJiyong Park uint32_t cmd_flags = 0;
265*54fd6939SJiyong Park uint32_t intmask;
266*54fd6939SJiyong Park
267*54fd6939SJiyong Park /* Wait for the command done */
268*54fd6939SJiyong Park err = rpi3_sdhost_waitcommand();
269*54fd6939SJiyong Park if (err != 0) {
270*54fd6939SJiyong Park WARN("previous command not done yet\n");
271*54fd6939SJiyong Park return err;
272*54fd6939SJiyong Park }
273*54fd6939SJiyong Park
274*54fd6939SJiyong Park cmd_idx = cmd->cmd_idx & HC_CMD_COMMAND_MASK;
275*54fd6939SJiyong Park
276*54fd6939SJiyong Park cmd_arg = cmd->cmd_arg;
277*54fd6939SJiyong Park if (cmd_idx == MMC_ACMD(51)) {
278*54fd6939SJiyong Park /* if previous cmd send to SDHOST is not MMC_CMD(55).
279*54fd6939SJiyong Park * It means this MMC_ACMD(51) is a resend.
280*54fd6939SJiyong Park * And we must also resend MMC_CMD(55) in this case
281*54fd6939SJiyong Park */
282*54fd6939SJiyong Park if (rpi3_sdhost_params.current_cmd != MMC_CMD(55)) {
283*54fd6939SJiyong Park send_command_decorated(
284*54fd6939SJiyong Park MMC_CMD(55),
285*54fd6939SJiyong Park rpi3_sdhost_params.sdcard_rca <<
286*54fd6939SJiyong Park RCA_SHIFT_OFFSET);
287*54fd6939SJiyong Park rpi3_sdhost_params.mmc_app_cmd = 1;
288*54fd6939SJiyong Park rpi3_sdhost_waitcommand();
289*54fd6939SJiyong Park
290*54fd6939SJiyong Park /* Also we need to call prepare to clean the buffer */
291*54fd6939SJiyong Park rpi3_sdhost_prepare(0, (uintptr_t)NULL, 8);
292*54fd6939SJiyong Park }
293*54fd6939SJiyong Park }
294*54fd6939SJiyong Park
295*54fd6939SJiyong Park /* We ignore MMC_CMD(12) sending from the TF-A's MMC driver
296*54fd6939SJiyong Park * because we send MMC_CMD(12) by ourselves.
297*54fd6939SJiyong Park */
298*54fd6939SJiyong Park if (cmd_idx == MMC_CMD(12))
299*54fd6939SJiyong Park return 0;
300*54fd6939SJiyong Park
301*54fd6939SJiyong Park if ((cmd->resp_type & MMC_RSP_136) &&
302*54fd6939SJiyong Park (cmd->resp_type & MMC_RSP_BUSY)) {
303*54fd6939SJiyong Park ERROR("rpi3_sdhost: unsupported response type!\n");
304*54fd6939SJiyong Park return -(EOPNOTSUPP);
305*54fd6939SJiyong Park }
306*54fd6939SJiyong Park
307*54fd6939SJiyong Park if (cmd->resp_type & MMC_RSP_48 && cmd->resp_type != MMC_RESPONSE_R2) {
308*54fd6939SJiyong Park /* 48-bit command
309*54fd6939SJiyong Park * We don't need to set any flags here because it is default.
310*54fd6939SJiyong Park */
311*54fd6939SJiyong Park } else if (cmd->resp_type & MMC_RSP_136) {
312*54fd6939SJiyong Park /* 136-bit command */
313*54fd6939SJiyong Park cmd_flags |= HC_CMD_RESPONSE_LONG;
314*54fd6939SJiyong Park } else {
315*54fd6939SJiyong Park /* no respond command */
316*54fd6939SJiyong Park cmd_flags |= HC_CMD_RESPONSE_NONE;
317*54fd6939SJiyong Park }
318*54fd6939SJiyong Park
319*54fd6939SJiyong Park rpi3_sdhost_params.cmdbusy = 0;
320*54fd6939SJiyong Park if (cmd->resp_type & MMC_RSP_BUSY) {
321*54fd6939SJiyong Park cmd_flags |= HC_CMD_BUSY;
322*54fd6939SJiyong Park rpi3_sdhost_params.cmdbusy = 1;
323*54fd6939SJiyong Park }
324*54fd6939SJiyong Park
325*54fd6939SJiyong Park if (rpi3_sdhost_params.mmc_app_cmd) {
326*54fd6939SJiyong Park switch (cmd_idx) {
327*54fd6939SJiyong Park case MMC_ACMD(41):
328*54fd6939SJiyong Park if (cmd_arg == OCR_HCS)
329*54fd6939SJiyong Park cmd_arg |= OCR_3_3_3_4;
330*54fd6939SJiyong Park break;
331*54fd6939SJiyong Park default:
332*54fd6939SJiyong Park break;
333*54fd6939SJiyong Park }
334*54fd6939SJiyong Park rpi3_sdhost_params.mmc_app_cmd = 0;
335*54fd6939SJiyong Park }
336*54fd6939SJiyong Park
337*54fd6939SJiyong Park if (cmd_idx == MMC_CMD(55))
338*54fd6939SJiyong Park rpi3_sdhost_params.mmc_app_cmd = 1;
339*54fd6939SJiyong Park
340*54fd6939SJiyong Park send_command_decorated(cmd_idx | cmd_flags, cmd_arg);
341*54fd6939SJiyong Park
342*54fd6939SJiyong Park intmask = mmio_read_32(reg_base + HC_HOSTSTATUS);
343*54fd6939SJiyong Park if (rpi3_sdhost_params.cmdbusy && (intmask & HC_HSTST_INT_BUSY)) {
344*54fd6939SJiyong Park mmio_write_32(reg_base + HC_HOSTSTATUS, HC_HSTST_INT_BUSY);
345*54fd6939SJiyong Park rpi3_sdhost_params.cmdbusy = 0;
346*54fd6939SJiyong Park }
347*54fd6939SJiyong Park
348*54fd6939SJiyong Park if (!(cmd_flags & HC_CMD_RESPONSE_NONE)) {
349*54fd6939SJiyong Park err = rpi3_sdhost_waitcommand();
350*54fd6939SJiyong Park if (err != 0)
351*54fd6939SJiyong Park ERROR("rpi3_sdhost: cmd cannot be finished\n");
352*54fd6939SJiyong Park }
353*54fd6939SJiyong Park
354*54fd6939SJiyong Park cmd->resp_data[0] = mmio_read_32(reg_base + HC_RESPONSE_0);
355*54fd6939SJiyong Park cmd->resp_data[1] = mmio_read_32(reg_base + HC_RESPONSE_1);
356*54fd6939SJiyong Park cmd->resp_data[2] = mmio_read_32(reg_base + HC_RESPONSE_2);
357*54fd6939SJiyong Park cmd->resp_data[3] = mmio_read_32(reg_base + HC_RESPONSE_3);
358*54fd6939SJiyong Park
359*54fd6939SJiyong Park if (mmio_read_32(reg_base + HC_COMMAND) & HC_CMD_FAILED) {
360*54fd6939SJiyong Park uint32_t sdhsts = mmio_read_32(reg_base + HC_HOSTSTATUS);
361*54fd6939SJiyong Park
362*54fd6939SJiyong Park mmio_write_32(reg_base + HC_HOSTSTATUS,
363*54fd6939SJiyong Park HC_HSTST_MASK_ERROR_ALL);
364*54fd6939SJiyong Park
365*54fd6939SJiyong Park /*
366*54fd6939SJiyong Park * If the command SEND_OP_COND returns with CRC7 error,
367*54fd6939SJiyong Park * it can be considered as having completed successfully.
368*54fd6939SJiyong Park */
369*54fd6939SJiyong Park if (!(sdhsts & HC_HSTST_ERROR_CRC7)
370*54fd6939SJiyong Park || (cmd_idx != MMC_CMD(1))) {
371*54fd6939SJiyong Park if (sdhsts & HC_HSTST_TIMEOUT_CMD) {
372*54fd6939SJiyong Park ERROR("rpi3_sdhost: timeout status 0x%x\n",
373*54fd6939SJiyong Park sdhsts);
374*54fd6939SJiyong Park err = -(ETIMEDOUT);
375*54fd6939SJiyong Park } else {
376*54fd6939SJiyong Park ERROR("rpi3_sdhost: unknown err, cmd = 0x%x\n",
377*54fd6939SJiyong Park mmio_read_32(reg_base + HC_COMMAND));
378*54fd6939SJiyong Park ERROR("rpi3_sdhost status: 0x%x\n", sdhsts);
379*54fd6939SJiyong Park err = -(EILSEQ);
380*54fd6939SJiyong Park }
381*54fd6939SJiyong Park }
382*54fd6939SJiyong Park }
383*54fd6939SJiyong Park
384*54fd6939SJiyong Park if ((!err) && (cmd_idx == MMC_CMD(3))) {
385*54fd6939SJiyong Park /* we keep the RCA in case to send MMC_CMD(55) ourselves */
386*54fd6939SJiyong Park rpi3_sdhost_params.sdcard_rca = (cmd->resp_data[0]
387*54fd6939SJiyong Park & 0xFFFF0000U) >> 16;
388*54fd6939SJiyong Park }
389*54fd6939SJiyong Park
390*54fd6939SJiyong Park return err;
391*54fd6939SJiyong Park }
392*54fd6939SJiyong Park
rpi3_sdhost_set_clock(unsigned int clk)393*54fd6939SJiyong Park static int rpi3_sdhost_set_clock(unsigned int clk)
394*54fd6939SJiyong Park {
395*54fd6939SJiyong Park uintptr_t reg_base = rpi3_sdhost_params.reg_base;
396*54fd6939SJiyong Park uint32_t max_clk = 250000000;
397*54fd6939SJiyong Park uint32_t div;
398*54fd6939SJiyong Park
399*54fd6939SJiyong Park if (clk < 100000) {
400*54fd6939SJiyong Park mmio_write_32(reg_base + HC_CLOCKDIVISOR,
401*54fd6939SJiyong Park HC_CLOCKDIVISOR_MAXVAL);
402*54fd6939SJiyong Park return 0;
403*54fd6939SJiyong Park }
404*54fd6939SJiyong Park
405*54fd6939SJiyong Park div = max_clk / clk;
406*54fd6939SJiyong Park if (div < 2)
407*54fd6939SJiyong Park div = 2;
408*54fd6939SJiyong Park
409*54fd6939SJiyong Park if ((max_clk / div) > clk)
410*54fd6939SJiyong Park div++;
411*54fd6939SJiyong Park
412*54fd6939SJiyong Park div -= 2;
413*54fd6939SJiyong Park if (div > HC_CLOCKDIVISOR_MAXVAL)
414*54fd6939SJiyong Park div = HC_CLOCKDIVISOR_MAXVAL;
415*54fd6939SJiyong Park
416*54fd6939SJiyong Park rpi3_sdhost_params.clk_rate = max_clk / (div + 2);
417*54fd6939SJiyong Park rpi3_sdhost_params.ns_per_fifo_word = (1000000000 /
418*54fd6939SJiyong Park rpi3_sdhost_params.clk_rate)
419*54fd6939SJiyong Park * 8;
420*54fd6939SJiyong Park
421*54fd6939SJiyong Park mmio_write_32(reg_base + HC_CLOCKDIVISOR, div);
422*54fd6939SJiyong Park return 0;
423*54fd6939SJiyong Park }
424*54fd6939SJiyong Park
rpi3_sdhost_set_ios(unsigned int clk,unsigned int width)425*54fd6939SJiyong Park static int rpi3_sdhost_set_ios(unsigned int clk, unsigned int width)
426*54fd6939SJiyong Park {
427*54fd6939SJiyong Park uintptr_t reg_base = rpi3_sdhost_params.reg_base;
428*54fd6939SJiyong Park uint32_t tmp1;
429*54fd6939SJiyong Park
430*54fd6939SJiyong Park rpi3_sdhost_set_clock(clk);
431*54fd6939SJiyong Park VERBOSE("rpi3_sdhost: Changing clock to %dHz for data mode\n", clk);
432*54fd6939SJiyong Park
433*54fd6939SJiyong Park if (width != MMC_BUS_WIDTH_4 && width != MMC_BUS_WIDTH_1) {
434*54fd6939SJiyong Park ERROR("rpi3_sdhost: width %d not supported\n", width);
435*54fd6939SJiyong Park return -(EOPNOTSUPP);
436*54fd6939SJiyong Park }
437*54fd6939SJiyong Park rpi3_sdhost_params.bus_width = width;
438*54fd6939SJiyong Park
439*54fd6939SJiyong Park tmp1 = mmio_read_32(reg_base + HC_HOSTCONFIG);
440*54fd6939SJiyong Park tmp1 &= ~(HC_HSTCF_EXTBUS_4BIT);
441*54fd6939SJiyong Park if (rpi3_sdhost_params.bus_width == MMC_BUS_WIDTH_4)
442*54fd6939SJiyong Park tmp1 |= HC_HSTCF_EXTBUS_4BIT;
443*54fd6939SJiyong Park
444*54fd6939SJiyong Park mmio_write_32(reg_base + HC_HOSTCONFIG, tmp1);
445*54fd6939SJiyong Park tmp1 = mmio_read_32(reg_base + HC_HOSTCONFIG);
446*54fd6939SJiyong Park mmio_write_32(reg_base + HC_HOSTCONFIG, tmp1 |
447*54fd6939SJiyong Park HC_HSTCF_SLOW_CARD | HC_HSTCF_INTBUS_WIDE);
448*54fd6939SJiyong Park
449*54fd6939SJiyong Park return 0;
450*54fd6939SJiyong Park }
451*54fd6939SJiyong Park
rpi3_sdhost_prepare(int lba,uintptr_t buf,size_t size)452*54fd6939SJiyong Park static int rpi3_sdhost_prepare(int lba, uintptr_t buf, size_t size)
453*54fd6939SJiyong Park {
454*54fd6939SJiyong Park uintptr_t reg_base = rpi3_sdhost_params.reg_base;
455*54fd6939SJiyong Park size_t blocks;
456*54fd6939SJiyong Park size_t blocksize;
457*54fd6939SJiyong Park
458*54fd6939SJiyong Park if (size < 512) {
459*54fd6939SJiyong Park blocksize = size;
460*54fd6939SJiyong Park blocks = 1;
461*54fd6939SJiyong Park } else {
462*54fd6939SJiyong Park blocksize = 512;
463*54fd6939SJiyong Park blocks = size / blocksize;
464*54fd6939SJiyong Park if (size % blocksize != 0)
465*54fd6939SJiyong Park blocks++;
466*54fd6939SJiyong Park }
467*54fd6939SJiyong Park
468*54fd6939SJiyong Park rpi3_drain_fifo();
469*54fd6939SJiyong Park
470*54fd6939SJiyong Park mmio_write_32(reg_base + HC_BLOCKSIZE, blocksize);
471*54fd6939SJiyong Park mmio_write_32(reg_base + HC_BLOCKCOUNT, blocks);
472*54fd6939SJiyong Park udelay(100);
473*54fd6939SJiyong Park return 0;
474*54fd6939SJiyong Park }
475*54fd6939SJiyong Park
rpi3_sdhost_read(int lba,uintptr_t buf,size_t size)476*54fd6939SJiyong Park static int rpi3_sdhost_read(int lba, uintptr_t buf, size_t size)
477*54fd6939SJiyong Park {
478*54fd6939SJiyong Park int err = 0;
479*54fd6939SJiyong Park uint32_t *buf1 = ((uint32_t *) buf);
480*54fd6939SJiyong Park uintptr_t reg_base = rpi3_sdhost_params.reg_base;
481*54fd6939SJiyong Park int timeout = 100000;
482*54fd6939SJiyong Park int remaining_words = 0;
483*54fd6939SJiyong Park
484*54fd6939SJiyong Park for (int i = 0; i < size / 4; i++) {
485*54fd6939SJiyong Park volatile int t = timeout;
486*54fd6939SJiyong Park uint32_t hsts_err;
487*54fd6939SJiyong Park
488*54fd6939SJiyong Park while ((mmio_read_32(reg_base + HC_HOSTSTATUS)
489*54fd6939SJiyong Park & HC_HSTST_HAVEDATA) == 0) {
490*54fd6939SJiyong Park if (t == 0) {
491*54fd6939SJiyong Park ERROR("rpi3_sdhost: fifo timeout after %dus\n",
492*54fd6939SJiyong Park timeout);
493*54fd6939SJiyong Park err = -(ETIMEDOUT);
494*54fd6939SJiyong Park break;
495*54fd6939SJiyong Park }
496*54fd6939SJiyong Park t--;
497*54fd6939SJiyong Park udelay(10);
498*54fd6939SJiyong Park }
499*54fd6939SJiyong Park if (t == 0)
500*54fd6939SJiyong Park break;
501*54fd6939SJiyong Park
502*54fd6939SJiyong Park uint32_t data = mmio_read_32(reg_base + HC_DATAPORT);
503*54fd6939SJiyong Park
504*54fd6939SJiyong Park hsts_err = mmio_read_32(reg_base + HC_HOSTSTATUS)
505*54fd6939SJiyong Park & HC_HSTST_MASK_ERROR_ALL;
506*54fd6939SJiyong Park if (hsts_err) {
507*54fd6939SJiyong Park ERROR("rpi3_sdhost: transfer FIFO word %d: 0x%x\n",
508*54fd6939SJiyong Park i,
509*54fd6939SJiyong Park mmio_read_32(reg_base + HC_HOSTSTATUS));
510*54fd6939SJiyong Park rpi3_sdhost_print_regs();
511*54fd6939SJiyong Park
512*54fd6939SJiyong Park err = -(EILSEQ);
513*54fd6939SJiyong Park
514*54fd6939SJiyong Park /* clean the error status */
515*54fd6939SJiyong Park mmio_write_32(reg_base + HC_HOSTSTATUS, hsts_err);
516*54fd6939SJiyong Park }
517*54fd6939SJiyong Park
518*54fd6939SJiyong Park if (buf1)
519*54fd6939SJiyong Park buf1[i] = data;
520*54fd6939SJiyong Park
521*54fd6939SJiyong Park /* speeding up if the remaining words are still a lot */
522*54fd6939SJiyong Park remaining_words = (mmio_read_32(reg_base + HC_DEBUG) >> 4)
523*54fd6939SJiyong Park & HC_DBG_FIFO_THRESH_MASK;
524*54fd6939SJiyong Park if (remaining_words >= 7)
525*54fd6939SJiyong Park continue;
526*54fd6939SJiyong Park
527*54fd6939SJiyong Park /* delay. slowing down the read process */
528*54fd6939SJiyong Park udelay(100);
529*54fd6939SJiyong Park }
530*54fd6939SJiyong Park
531*54fd6939SJiyong Park /* We decide to stop by ourselves.
532*54fd6939SJiyong Park * It is because MMC_CMD(18) -> MMC_CMD(13) -> MMC_CMD(12)
533*54fd6939SJiyong Park * doesn't work for RPi3 SDHost.
534*54fd6939SJiyong Park */
535*54fd6939SJiyong Park if (rpi3_sdhost_params.current_cmd == MMC_CMD(18))
536*54fd6939SJiyong Park send_command_decorated(MMC_CMD(12), 0);
537*54fd6939SJiyong Park
538*54fd6939SJiyong Park return err;
539*54fd6939SJiyong Park }
540*54fd6939SJiyong Park
rpi3_sdhost_write(int lba,uintptr_t buf,size_t size)541*54fd6939SJiyong Park static int rpi3_sdhost_write(int lba, uintptr_t buf, size_t size)
542*54fd6939SJiyong Park {
543*54fd6939SJiyong Park uint32_t *buf1 = ((uint32_t *) buf);
544*54fd6939SJiyong Park uintptr_t reg_base = rpi3_sdhost_params.reg_base;
545*54fd6939SJiyong Park int err = 0;
546*54fd6939SJiyong Park int remaining_words = 0;
547*54fd6939SJiyong Park
548*54fd6939SJiyong Park for (int i = 0; i < size / 4; i++) {
549*54fd6939SJiyong Park uint32_t hsts_err;
550*54fd6939SJiyong Park uint32_t data = buf1[i];
551*54fd6939SJiyong Park uint32_t dbg;
552*54fd6939SJiyong Park uint32_t fsm_state;
553*54fd6939SJiyong Park
554*54fd6939SJiyong Park mmio_write_32(reg_base + HC_DATAPORT, data);
555*54fd6939SJiyong Park
556*54fd6939SJiyong Park dbg = mmio_read_32(reg_base + HC_DEBUG);
557*54fd6939SJiyong Park fsm_state = dbg & HC_DBG_FSM_MASK;
558*54fd6939SJiyong Park if (fsm_state != HC_DBG_FSM_WRITEDATA
559*54fd6939SJiyong Park && fsm_state != HC_DBG_FSM_WRITESTART1
560*54fd6939SJiyong Park && fsm_state != HC_DBG_FSM_WRITESTART2
561*54fd6939SJiyong Park && fsm_state != HC_DBG_FSM_WRITECRC
562*54fd6939SJiyong Park && fsm_state != HC_DBG_FSM_WRITEWAIT1
563*54fd6939SJiyong Park && fsm_state != HC_DBG_FSM_WRITEWAIT2) {
564*54fd6939SJiyong Park hsts_err = mmio_read_32(reg_base + HC_HOSTSTATUS)
565*54fd6939SJiyong Park & HC_HSTST_MASK_ERROR_ALL;
566*54fd6939SJiyong Park if (hsts_err)
567*54fd6939SJiyong Park err = -(EILSEQ);
568*54fd6939SJiyong Park }
569*54fd6939SJiyong Park
570*54fd6939SJiyong Park /* speeding up if the remaining words are not many */
571*54fd6939SJiyong Park remaining_words = (mmio_read_32(reg_base + HC_DEBUG) >> 4)
572*54fd6939SJiyong Park & HC_DBG_FIFO_THRESH_MASK;
573*54fd6939SJiyong Park if (remaining_words <= 4)
574*54fd6939SJiyong Park continue;
575*54fd6939SJiyong Park
576*54fd6939SJiyong Park udelay(100);
577*54fd6939SJiyong Park }
578*54fd6939SJiyong Park
579*54fd6939SJiyong Park /* We decide to stop by ourselves.
580*54fd6939SJiyong Park * It is because MMC_CMD(25) -> MMC_CMD(13) -> MMC_CMD(12)
581*54fd6939SJiyong Park * doesn't work for RPi3 SDHost.
582*54fd6939SJiyong Park */
583*54fd6939SJiyong Park if (rpi3_sdhost_params.current_cmd == MMC_CMD(25))
584*54fd6939SJiyong Park send_command_decorated(MMC_CMD(12), 0);
585*54fd6939SJiyong Park
586*54fd6939SJiyong Park return err;
587*54fd6939SJiyong Park }
588*54fd6939SJiyong Park
rpi3_sdhost_init(struct rpi3_sdhost_params * params,struct mmc_device_info * mmc_dev_info)589*54fd6939SJiyong Park void rpi3_sdhost_init(struct rpi3_sdhost_params *params,
590*54fd6939SJiyong Park struct mmc_device_info *mmc_dev_info)
591*54fd6939SJiyong Park {
592*54fd6939SJiyong Park assert((params != 0) &&
593*54fd6939SJiyong Park ((params->reg_base & MMC_BLOCK_MASK) == 0));
594*54fd6939SJiyong Park
595*54fd6939SJiyong Park memcpy(&rpi3_sdhost_params, params, sizeof(struct rpi3_sdhost_params));
596*54fd6939SJiyong Park
597*54fd6939SJiyong Park /* backup GPIO 48 to 53 configurations */
598*54fd6939SJiyong Park for (int i = 48; i <= 53; i++) {
599*54fd6939SJiyong Park rpi3_sdhost_params.gpio48_pinselect[i - 48]
600*54fd6939SJiyong Park = rpi3_gpio_get_select(i);
601*54fd6939SJiyong Park VERBOSE("rpi3_sdhost: Original GPIO state %d: %d\n",
602*54fd6939SJiyong Park i,
603*54fd6939SJiyong Park rpi3_sdhost_params.gpio48_pinselect[i - 48]);
604*54fd6939SJiyong Park }
605*54fd6939SJiyong Park
606*54fd6939SJiyong Park /* setting pull resistors for 48 to 53.
607*54fd6939SJiyong Park * It is debatable to set SD_CLK to UP or NONE. We massively
608*54fd6939SJiyong Park * tested different brands of SD Cards and found NONE works
609*54fd6939SJiyong Park * most stable.
610*54fd6939SJiyong Park *
611*54fd6939SJiyong Park * GPIO 48 (SD_CLK) to GPIO_PULL_NONE
612*54fd6939SJiyong Park * GPIO 49 (SD_CMD) to GPIO_PULL_UP
613*54fd6939SJiyong Park * GPIO 50 (SD_D0) to GPIO_PULL_UP
614*54fd6939SJiyong Park * GPIO 51 (SD_D1) to GPIO_PULL_UP
615*54fd6939SJiyong Park * GPIO 52 (SD_D2) to GPIO_PULL_UP
616*54fd6939SJiyong Park * GPIO 53 (SD_D3) to GPIO_PULL_UP
617*54fd6939SJiyong Park */
618*54fd6939SJiyong Park gpio_set_pull(48, GPIO_PULL_NONE);
619*54fd6939SJiyong Park for (int i = 49; i <= 53; i++)
620*54fd6939SJiyong Park gpio_set_pull(i, GPIO_PULL_UP);
621*54fd6939SJiyong Park
622*54fd6939SJiyong Park /* Set pin 48-53 to alt-0. It means route SDHOST to card slot */
623*54fd6939SJiyong Park for (int i = 48; i <= 53; i++)
624*54fd6939SJiyong Park rpi3_gpio_set_select(i, RPI3_GPIO_FUNC_ALT0);
625*54fd6939SJiyong Park
626*54fd6939SJiyong Park mmc_init(&rpi3_sdhost_ops, params->clk_rate, params->bus_width,
627*54fd6939SJiyong Park params->flags, mmc_dev_info);
628*54fd6939SJiyong Park }
629*54fd6939SJiyong Park
rpi3_sdhost_stop(void)630*54fd6939SJiyong Park void rpi3_sdhost_stop(void)
631*54fd6939SJiyong Park {
632*54fd6939SJiyong Park uintptr_t reg_base = rpi3_sdhost_params.reg_base;
633*54fd6939SJiyong Park
634*54fd6939SJiyong Park VERBOSE("rpi3_sdhost: Shutting down: drain FIFO out\n");
635*54fd6939SJiyong Park rpi3_drain_fifo();
636*54fd6939SJiyong Park
637*54fd6939SJiyong Park VERBOSE("rpi3_sdhost: Shutting down: slowing down the clock\n");
638*54fd6939SJiyong Park mmio_write_32(reg_base+HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_SLOWVAL);
639*54fd6939SJiyong Park udelay(500);
640*54fd6939SJiyong Park
641*54fd6939SJiyong Park VERBOSE("rpi3_sdhost: Shutting down: put SDHost into idle state\n");
642*54fd6939SJiyong Park send_command_decorated(MMC_CMD(0), 0);
643*54fd6939SJiyong Park udelay(500);
644*54fd6939SJiyong Park
645*54fd6939SJiyong Park mmio_write_32(reg_base + HC_COMMAND, 0);
646*54fd6939SJiyong Park mmio_write_32(reg_base + HC_ARGUMENT, 0);
647*54fd6939SJiyong Park mmio_write_32(reg_base + HC_TIMEOUTCOUNTER, HC_TIMEOUT_IDLE);
648*54fd6939SJiyong Park mmio_write_32(reg_base + HC_CLOCKDIVISOR, HC_CLOCKDIVISOR_STOPVAL);
649*54fd6939SJiyong Park
650*54fd6939SJiyong Park udelay(100);
651*54fd6939SJiyong Park
652*54fd6939SJiyong Park mmio_write_32(reg_base + HC_POWER, 0);
653*54fd6939SJiyong Park mmio_write_32(reg_base + HC_HOSTCONFIG, 0);
654*54fd6939SJiyong Park mmio_write_32(reg_base + HC_BLOCKSIZE, 0x400);
655*54fd6939SJiyong Park mmio_write_32(reg_base + HC_BLOCKCOUNT, 0);
656*54fd6939SJiyong Park mmio_write_32(reg_base + HC_HOSTSTATUS, 0x7f8);
657*54fd6939SJiyong Park
658*54fd6939SJiyong Park mmio_write_32(reg_base + HC_COMMAND, 0);
659*54fd6939SJiyong Park mmio_write_32(reg_base + HC_ARGUMENT, 0);
660*54fd6939SJiyong Park
661*54fd6939SJiyong Park udelay(100);
662*54fd6939SJiyong Park
663*54fd6939SJiyong Park /* Restore the pinmux to original state */
664*54fd6939SJiyong Park for (int i = 48; i <= 53; i++) {
665*54fd6939SJiyong Park rpi3_gpio_set_select(i,
666*54fd6939SJiyong Park rpi3_sdhost_params.gpio48_pinselect[i-48]);
667*54fd6939SJiyong Park }
668*54fd6939SJiyong Park
669*54fd6939SJiyong Park /* Reset the pull resistors before entering BL33.
670*54fd6939SJiyong Park * GPIO 48 (SD_CLK) to GPIO_PULL_UP
671*54fd6939SJiyong Park * GPIO 49 (SD_CMD) to GPIO_PULL_UP
672*54fd6939SJiyong Park * GPIO 50 (SD_D0) to GPIO_PULL_UP
673*54fd6939SJiyong Park * GPIO 51 (SD_D1) to GPIO_PULL_UP
674*54fd6939SJiyong Park * GPIO 52 (SD_D2) to GPIO_PULL_UP
675*54fd6939SJiyong Park * GPIO 53 (SD_D3) to GPIO_PULL_UP
676*54fd6939SJiyong Park */
677*54fd6939SJiyong Park for (int i = 48; i <= 53; i++)
678*54fd6939SJiyong Park gpio_set_pull(i, GPIO_PULL_UP);
679*54fd6939SJiyong Park }
680