xref: /rk3399_ARM-atf/drivers/mmc/mmc.c (revision ad71d45e7ca3256651b7735e105b18cccff416ce)
1*ad71d45eSYann Gautier /*
2*ad71d45eSYann Gautier  * Copyright (c) 2018, ARM Limited and Contributors. All rights reserved.
3*ad71d45eSYann Gautier  *
4*ad71d45eSYann Gautier  * SPDX-License-Identifier: BSD-3-Clause
5*ad71d45eSYann Gautier  */
6*ad71d45eSYann Gautier 
7*ad71d45eSYann Gautier /* Define a simple and generic interface to access eMMC and SD-card devices. */
8*ad71d45eSYann Gautier 
9*ad71d45eSYann Gautier #include <arch_helpers.h>
10*ad71d45eSYann Gautier #include <assert.h>
11*ad71d45eSYann Gautier #include <debug.h>
12*ad71d45eSYann Gautier #include <errno.h>
13*ad71d45eSYann Gautier #include <mmc.h>
14*ad71d45eSYann Gautier #include <stdbool.h>
15*ad71d45eSYann Gautier #include <string.h>
16*ad71d45eSYann Gautier #include <utils.h>
17*ad71d45eSYann Gautier 
18*ad71d45eSYann Gautier #define MMC_DEFAULT_MAX_RETRIES		5
19*ad71d45eSYann Gautier #define SEND_OP_COND_MAX_RETRIES	100
20*ad71d45eSYann Gautier 
21*ad71d45eSYann Gautier #define MULT_BY_512K_SHIFT		19
22*ad71d45eSYann Gautier 
23*ad71d45eSYann Gautier static const struct mmc_ops *ops;
24*ad71d45eSYann Gautier static unsigned int mmc_ocr_value;
25*ad71d45eSYann Gautier static struct mmc_csd_emmc mmc_csd;
26*ad71d45eSYann Gautier static unsigned char mmc_ext_csd[512] __aligned(4);
27*ad71d45eSYann Gautier static unsigned int mmc_flags;
28*ad71d45eSYann Gautier static struct mmc_device_info *mmc_dev_info;
29*ad71d45eSYann Gautier static unsigned int rca;
30*ad71d45eSYann Gautier 
31*ad71d45eSYann Gautier static const unsigned char tran_speed_base[16] = {
32*ad71d45eSYann Gautier 	0, 10, 12, 13, 15, 20, 26, 30, 35, 40, 45, 52, 55, 60, 70, 80
33*ad71d45eSYann Gautier };
34*ad71d45eSYann Gautier 
35*ad71d45eSYann Gautier static const unsigned char sd_tran_speed_base[16] = {
36*ad71d45eSYann Gautier 	0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
37*ad71d45eSYann Gautier };
38*ad71d45eSYann Gautier 
39*ad71d45eSYann Gautier static bool is_cmd23_enabled(void)
40*ad71d45eSYann Gautier {
41*ad71d45eSYann Gautier 	return ((mmc_flags & MMC_FLAG_CMD23) != 0U);
42*ad71d45eSYann Gautier }
43*ad71d45eSYann Gautier 
44*ad71d45eSYann Gautier static int mmc_send_cmd(unsigned int idx, unsigned int arg,
45*ad71d45eSYann Gautier 			unsigned int r_type, unsigned int *r_data)
46*ad71d45eSYann Gautier {
47*ad71d45eSYann Gautier 	struct mmc_cmd cmd;
48*ad71d45eSYann Gautier 	int ret;
49*ad71d45eSYann Gautier 
50*ad71d45eSYann Gautier 	zeromem(&cmd, sizeof(struct mmc_cmd));
51*ad71d45eSYann Gautier 
52*ad71d45eSYann Gautier 	cmd.cmd_idx = idx;
53*ad71d45eSYann Gautier 	cmd.cmd_arg = arg;
54*ad71d45eSYann Gautier 	cmd.resp_type = r_type;
55*ad71d45eSYann Gautier 
56*ad71d45eSYann Gautier 	ret = ops->send_cmd(&cmd);
57*ad71d45eSYann Gautier 
58*ad71d45eSYann Gautier 	if ((ret == 0) && (r_data != NULL)) {
59*ad71d45eSYann Gautier 		int i;
60*ad71d45eSYann Gautier 
61*ad71d45eSYann Gautier 		for (i = 0; i < 4; i++) {
62*ad71d45eSYann Gautier 			*r_data = cmd.resp_data[i];
63*ad71d45eSYann Gautier 			r_data++;
64*ad71d45eSYann Gautier 		}
65*ad71d45eSYann Gautier 	}
66*ad71d45eSYann Gautier 
67*ad71d45eSYann Gautier 	if (ret != 0) {
68*ad71d45eSYann Gautier 		VERBOSE("Send command %u error: %d\n", idx, ret);
69*ad71d45eSYann Gautier 	}
70*ad71d45eSYann Gautier 
71*ad71d45eSYann Gautier 	return ret;
72*ad71d45eSYann Gautier }
73*ad71d45eSYann Gautier 
74*ad71d45eSYann Gautier static int mmc_device_state(void)
75*ad71d45eSYann Gautier {
76*ad71d45eSYann Gautier 	int retries = MMC_DEFAULT_MAX_RETRIES;
77*ad71d45eSYann Gautier 	unsigned int resp_data[4];
78*ad71d45eSYann Gautier 
79*ad71d45eSYann Gautier 	do {
80*ad71d45eSYann Gautier 		int ret;
81*ad71d45eSYann Gautier 
82*ad71d45eSYann Gautier 		if (retries == 0) {
83*ad71d45eSYann Gautier 			ERROR("CMD13 failed after %d retries\n",
84*ad71d45eSYann Gautier 			      MMC_DEFAULT_MAX_RETRIES);
85*ad71d45eSYann Gautier 			return -EIO;
86*ad71d45eSYann Gautier 		}
87*ad71d45eSYann Gautier 
88*ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(13), rca << RCA_SHIFT_OFFSET,
89*ad71d45eSYann Gautier 				   MMC_RESPONSE_R(1), &resp_data[0]);
90*ad71d45eSYann Gautier 		if (ret != 0) {
91*ad71d45eSYann Gautier 			return ret;
92*ad71d45eSYann Gautier 		}
93*ad71d45eSYann Gautier 
94*ad71d45eSYann Gautier 		if ((resp_data[0] & STATUS_SWITCH_ERROR) != 0U) {
95*ad71d45eSYann Gautier 			return -EIO;
96*ad71d45eSYann Gautier 		}
97*ad71d45eSYann Gautier 
98*ad71d45eSYann Gautier 		retries--;
99*ad71d45eSYann Gautier 	} while ((resp_data[0] & STATUS_READY_FOR_DATA) == 0U);
100*ad71d45eSYann Gautier 
101*ad71d45eSYann Gautier 	return MMC_GET_STATE(resp_data[0]);
102*ad71d45eSYann Gautier }
103*ad71d45eSYann Gautier 
104*ad71d45eSYann Gautier static int mmc_set_ext_csd(unsigned int ext_cmd, unsigned int value)
105*ad71d45eSYann Gautier {
106*ad71d45eSYann Gautier 	int ret;
107*ad71d45eSYann Gautier 
108*ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(6),
109*ad71d45eSYann Gautier 			   EXTCSD_WRITE_BYTES | EXTCSD_CMD(ext_cmd) |
110*ad71d45eSYann Gautier 			   EXTCSD_VALUE(value) | EXTCSD_CMD_SET_NORMAL,
111*ad71d45eSYann Gautier 			   0, NULL);
112*ad71d45eSYann Gautier 	if (ret != 0) {
113*ad71d45eSYann Gautier 		return ret;
114*ad71d45eSYann Gautier 	}
115*ad71d45eSYann Gautier 
116*ad71d45eSYann Gautier 	do {
117*ad71d45eSYann Gautier 		ret = mmc_device_state();
118*ad71d45eSYann Gautier 		if (ret < 0) {
119*ad71d45eSYann Gautier 			return ret;
120*ad71d45eSYann Gautier 		}
121*ad71d45eSYann Gautier 	} while (ret == MMC_STATE_PRG);
122*ad71d45eSYann Gautier 
123*ad71d45eSYann Gautier 	return 0;
124*ad71d45eSYann Gautier }
125*ad71d45eSYann Gautier 
126*ad71d45eSYann Gautier static int mmc_sd_switch(unsigned int bus_width)
127*ad71d45eSYann Gautier {
128*ad71d45eSYann Gautier 	int ret;
129*ad71d45eSYann Gautier 	int retries = MMC_DEFAULT_MAX_RETRIES;
130*ad71d45eSYann Gautier 	unsigned int scr[2] = { 0 };
131*ad71d45eSYann Gautier 	unsigned int bus_width_arg = 0;
132*ad71d45eSYann Gautier 
133*ad71d45eSYann Gautier 	ret = ops->prepare(0, (uintptr_t)&scr, sizeof(scr));
134*ad71d45eSYann Gautier 	if (ret != 0) {
135*ad71d45eSYann Gautier 		return ret;
136*ad71d45eSYann Gautier 	}
137*ad71d45eSYann Gautier 
138*ad71d45eSYann Gautier 	/* CMD55: Application Specific Command */
139*ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
140*ad71d45eSYann Gautier 			   MMC_RESPONSE_R(1), NULL);
141*ad71d45eSYann Gautier 	if (ret != 0) {
142*ad71d45eSYann Gautier 		return ret;
143*ad71d45eSYann Gautier 	}
144*ad71d45eSYann Gautier 
145*ad71d45eSYann Gautier 	/* ACMD51: SEND_SCR */
146*ad71d45eSYann Gautier 	do {
147*ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_ACMD(51), 0, MMC_RESPONSE_R(1), NULL);
148*ad71d45eSYann Gautier 		if ((ret != 0) && (retries == 0)) {
149*ad71d45eSYann Gautier 			ERROR("ACMD51 failed after %d retries (ret=%d)\n",
150*ad71d45eSYann Gautier 			      MMC_DEFAULT_MAX_RETRIES, ret);
151*ad71d45eSYann Gautier 			return ret;
152*ad71d45eSYann Gautier 		}
153*ad71d45eSYann Gautier 
154*ad71d45eSYann Gautier 		retries--;
155*ad71d45eSYann Gautier 	} while (ret != 0);
156*ad71d45eSYann Gautier 
157*ad71d45eSYann Gautier 	ret = ops->read(0, (uintptr_t)&scr, sizeof(scr));
158*ad71d45eSYann Gautier 	if (ret != 0) {
159*ad71d45eSYann Gautier 		return ret;
160*ad71d45eSYann Gautier 	}
161*ad71d45eSYann Gautier 
162*ad71d45eSYann Gautier 	if (((scr[0] & SD_SCR_BUS_WIDTH_4) != 0U) &&
163*ad71d45eSYann Gautier 	    (bus_width == MMC_BUS_WIDTH_4)) {
164*ad71d45eSYann Gautier 		bus_width_arg = 2;
165*ad71d45eSYann Gautier 	}
166*ad71d45eSYann Gautier 
167*ad71d45eSYann Gautier 	/* CMD55: Application Specific Command */
168*ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(55), rca << RCA_SHIFT_OFFSET,
169*ad71d45eSYann Gautier 			   MMC_RESPONSE_R(1), NULL);
170*ad71d45eSYann Gautier 	if (ret != 0) {
171*ad71d45eSYann Gautier 		return ret;
172*ad71d45eSYann Gautier 	}
173*ad71d45eSYann Gautier 
174*ad71d45eSYann Gautier 	/* ACMD6: SET_BUS_WIDTH */
175*ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_ACMD(6), bus_width_arg, MMC_RESPONSE_R(1), NULL);
176*ad71d45eSYann Gautier 	if (ret != 0) {
177*ad71d45eSYann Gautier 		return ret;
178*ad71d45eSYann Gautier 	}
179*ad71d45eSYann Gautier 
180*ad71d45eSYann Gautier 	do {
181*ad71d45eSYann Gautier 		ret = mmc_device_state();
182*ad71d45eSYann Gautier 		if (ret < 0) {
183*ad71d45eSYann Gautier 			return ret;
184*ad71d45eSYann Gautier 		}
185*ad71d45eSYann Gautier 	} while (ret == MMC_STATE_PRG);
186*ad71d45eSYann Gautier 
187*ad71d45eSYann Gautier 	return 0;
188*ad71d45eSYann Gautier }
189*ad71d45eSYann Gautier 
190*ad71d45eSYann Gautier static int mmc_set_ios(unsigned int clk, unsigned int bus_width)
191*ad71d45eSYann Gautier {
192*ad71d45eSYann Gautier 	int ret;
193*ad71d45eSYann Gautier 	unsigned int width = bus_width;
194*ad71d45eSYann Gautier 
195*ad71d45eSYann Gautier 	if (mmc_dev_info->mmc_dev_type != MMC_IS_EMMC) {
196*ad71d45eSYann Gautier 		if (width == MMC_BUS_WIDTH_8) {
197*ad71d45eSYann Gautier 			WARN("Wrong bus config for SD-card, force to 4\n");
198*ad71d45eSYann Gautier 			width = MMC_BUS_WIDTH_4;
199*ad71d45eSYann Gautier 		}
200*ad71d45eSYann Gautier 		ret = mmc_sd_switch(width);
201*ad71d45eSYann Gautier 		if (ret != 0) {
202*ad71d45eSYann Gautier 			return ret;
203*ad71d45eSYann Gautier 		}
204*ad71d45eSYann Gautier 	} else if (mmc_csd.spec_vers == 4U) {
205*ad71d45eSYann Gautier 		ret = mmc_set_ext_csd(CMD_EXTCSD_BUS_WIDTH,
206*ad71d45eSYann Gautier 				      (unsigned int)width);
207*ad71d45eSYann Gautier 		if (ret != 0) {
208*ad71d45eSYann Gautier 			return ret;
209*ad71d45eSYann Gautier 		}
210*ad71d45eSYann Gautier 	} else {
211*ad71d45eSYann Gautier 		VERBOSE("Wrong MMC type or spec version\n");
212*ad71d45eSYann Gautier 	}
213*ad71d45eSYann Gautier 
214*ad71d45eSYann Gautier 	return ops->set_ios(clk, width);
215*ad71d45eSYann Gautier }
216*ad71d45eSYann Gautier 
217*ad71d45eSYann Gautier static int mmc_fill_device_info(void)
218*ad71d45eSYann Gautier {
219*ad71d45eSYann Gautier 	unsigned long long c_size;
220*ad71d45eSYann Gautier 	unsigned int speed_idx;
221*ad71d45eSYann Gautier 	unsigned int nb_blocks;
222*ad71d45eSYann Gautier 	unsigned int freq_unit;
223*ad71d45eSYann Gautier 	int ret;
224*ad71d45eSYann Gautier 	struct mmc_csd_sd_v2 *csd_sd_v2;
225*ad71d45eSYann Gautier 
226*ad71d45eSYann Gautier 	switch (mmc_dev_info->mmc_dev_type) {
227*ad71d45eSYann Gautier 	case MMC_IS_EMMC:
228*ad71d45eSYann Gautier 		mmc_dev_info->block_size = MMC_BLOCK_SIZE;
229*ad71d45eSYann Gautier 
230*ad71d45eSYann Gautier 		ret = ops->prepare(0, (uintptr_t)&mmc_ext_csd,
231*ad71d45eSYann Gautier 				   sizeof(mmc_ext_csd));
232*ad71d45eSYann Gautier 		if (ret != 0) {
233*ad71d45eSYann Gautier 			return ret;
234*ad71d45eSYann Gautier 		}
235*ad71d45eSYann Gautier 
236*ad71d45eSYann Gautier 		/* MMC CMD8: SEND_EXT_CSD */
237*ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(8), 0, MMC_RESPONSE_R(1), NULL);
238*ad71d45eSYann Gautier 		if (ret != 0) {
239*ad71d45eSYann Gautier 			return ret;
240*ad71d45eSYann Gautier 		}
241*ad71d45eSYann Gautier 
242*ad71d45eSYann Gautier 		ret = ops->read(0, (uintptr_t)&mmc_ext_csd,
243*ad71d45eSYann Gautier 				sizeof(mmc_ext_csd));
244*ad71d45eSYann Gautier 		if (ret != 0) {
245*ad71d45eSYann Gautier 			return ret;
246*ad71d45eSYann Gautier 		}
247*ad71d45eSYann Gautier 
248*ad71d45eSYann Gautier 		nb_blocks = (mmc_ext_csd[CMD_EXTCSD_SEC_CNT] << 0) |
249*ad71d45eSYann Gautier 			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 1] << 8) |
250*ad71d45eSYann Gautier 			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 2] << 16) |
251*ad71d45eSYann Gautier 			    (mmc_ext_csd[CMD_EXTCSD_SEC_CNT + 3] << 24);
252*ad71d45eSYann Gautier 
253*ad71d45eSYann Gautier 		mmc_dev_info->device_size = (unsigned long long)nb_blocks *
254*ad71d45eSYann Gautier 			mmc_dev_info->block_size;
255*ad71d45eSYann Gautier 
256*ad71d45eSYann Gautier 		break;
257*ad71d45eSYann Gautier 
258*ad71d45eSYann Gautier 	case MMC_IS_SD:
259*ad71d45eSYann Gautier 		/*
260*ad71d45eSYann Gautier 		 * Use the same mmc_csd struct, as required fields here
261*ad71d45eSYann Gautier 		 * (READ_BL_LEN, C_SIZE, CSIZE_MULT) are common with eMMC.
262*ad71d45eSYann Gautier 		 */
263*ad71d45eSYann Gautier 		mmc_dev_info->block_size = BIT_32(mmc_csd.read_bl_len);
264*ad71d45eSYann Gautier 
265*ad71d45eSYann Gautier 		c_size = ((unsigned long long)mmc_csd.c_size_high << 2U) |
266*ad71d45eSYann Gautier 			 (unsigned long long)mmc_csd.c_size_low;
267*ad71d45eSYann Gautier 		assert(c_size != 0xFFFU);
268*ad71d45eSYann Gautier 
269*ad71d45eSYann Gautier 		mmc_dev_info->device_size = (c_size + 1U) *
270*ad71d45eSYann Gautier 					    BIT_64(mmc_csd.c_size_mult + 2U) *
271*ad71d45eSYann Gautier 					    mmc_dev_info->block_size;
272*ad71d45eSYann Gautier 
273*ad71d45eSYann Gautier 		break;
274*ad71d45eSYann Gautier 
275*ad71d45eSYann Gautier 	case MMC_IS_SD_HC:
276*ad71d45eSYann Gautier 		assert(mmc_csd.csd_structure == 1U);
277*ad71d45eSYann Gautier 
278*ad71d45eSYann Gautier 		mmc_dev_info->block_size = MMC_BLOCK_SIZE;
279*ad71d45eSYann Gautier 
280*ad71d45eSYann Gautier 		/* Need to use mmc_csd_sd_v2 struct */
281*ad71d45eSYann Gautier 		csd_sd_v2 = (struct mmc_csd_sd_v2 *)&mmc_csd;
282*ad71d45eSYann Gautier 		c_size = ((unsigned long long)csd_sd_v2->c_size_high << 16) |
283*ad71d45eSYann Gautier 			 (unsigned long long)csd_sd_v2->c_size_low;
284*ad71d45eSYann Gautier 
285*ad71d45eSYann Gautier 		mmc_dev_info->device_size = (c_size + 1U) << MULT_BY_512K_SHIFT;
286*ad71d45eSYann Gautier 
287*ad71d45eSYann Gautier 		break;
288*ad71d45eSYann Gautier 
289*ad71d45eSYann Gautier 	default:
290*ad71d45eSYann Gautier 		ret = -EINVAL;
291*ad71d45eSYann Gautier 		break;
292*ad71d45eSYann Gautier 	}
293*ad71d45eSYann Gautier 
294*ad71d45eSYann Gautier 	if (ret != 0) {
295*ad71d45eSYann Gautier 		return ret;
296*ad71d45eSYann Gautier 	}
297*ad71d45eSYann Gautier 
298*ad71d45eSYann Gautier 	speed_idx = (mmc_csd.tran_speed & CSD_TRAN_SPEED_MULT_MASK) >>
299*ad71d45eSYann Gautier 			 CSD_TRAN_SPEED_MULT_SHIFT;
300*ad71d45eSYann Gautier 
301*ad71d45eSYann Gautier 	assert(speed_idx > 0U);
302*ad71d45eSYann Gautier 
303*ad71d45eSYann Gautier 	if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
304*ad71d45eSYann Gautier 		mmc_dev_info->max_bus_freq = tran_speed_base[speed_idx];
305*ad71d45eSYann Gautier 	} else {
306*ad71d45eSYann Gautier 		mmc_dev_info->max_bus_freq = sd_tran_speed_base[speed_idx];
307*ad71d45eSYann Gautier 	}
308*ad71d45eSYann Gautier 
309*ad71d45eSYann Gautier 	freq_unit = mmc_csd.tran_speed & CSD_TRAN_SPEED_UNIT_MASK;
310*ad71d45eSYann Gautier 	while (freq_unit != 0U) {
311*ad71d45eSYann Gautier 		mmc_dev_info->max_bus_freq *= 10U;
312*ad71d45eSYann Gautier 		--freq_unit;
313*ad71d45eSYann Gautier 	}
314*ad71d45eSYann Gautier 
315*ad71d45eSYann Gautier 	mmc_dev_info->max_bus_freq *= 10000U;
316*ad71d45eSYann Gautier 
317*ad71d45eSYann Gautier 	return 0;
318*ad71d45eSYann Gautier }
319*ad71d45eSYann Gautier 
320*ad71d45eSYann Gautier static int sd_send_op_cond(void)
321*ad71d45eSYann Gautier {
322*ad71d45eSYann Gautier 	int retries = SEND_OP_COND_MAX_RETRIES;
323*ad71d45eSYann Gautier 	unsigned int resp_data[4];
324*ad71d45eSYann Gautier 
325*ad71d45eSYann Gautier 	do {
326*ad71d45eSYann Gautier 		int ret;
327*ad71d45eSYann Gautier 
328*ad71d45eSYann Gautier 		if (retries == 0) {
329*ad71d45eSYann Gautier 			ERROR("ACMD41 failed after %d retries\n",
330*ad71d45eSYann Gautier 			      SEND_OP_COND_MAX_RETRIES);
331*ad71d45eSYann Gautier 			return -EIO;
332*ad71d45eSYann Gautier 		}
333*ad71d45eSYann Gautier 
334*ad71d45eSYann Gautier 		/* CMD55: Application Specific Command */
335*ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(55), 0, MMC_RESPONSE_R(1), NULL);
336*ad71d45eSYann Gautier 		if (ret != 0) {
337*ad71d45eSYann Gautier 			return ret;
338*ad71d45eSYann Gautier 		}
339*ad71d45eSYann Gautier 
340*ad71d45eSYann Gautier 		/* ACMD41: SD_SEND_OP_COND */
341*ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_ACMD(41), OCR_HCS, MMC_RESPONSE_R(3),
342*ad71d45eSYann Gautier 				   &resp_data[0]);
343*ad71d45eSYann Gautier 		if (ret != 0) {
344*ad71d45eSYann Gautier 			return ret;
345*ad71d45eSYann Gautier 		}
346*ad71d45eSYann Gautier 
347*ad71d45eSYann Gautier 		retries--;
348*ad71d45eSYann Gautier 	} while ((resp_data[0] & OCR_POWERUP) == 0U);
349*ad71d45eSYann Gautier 
350*ad71d45eSYann Gautier 	mmc_ocr_value = resp_data[0];
351*ad71d45eSYann Gautier 
352*ad71d45eSYann Gautier 	if ((mmc_ocr_value & OCR_HCS) != 0U) {
353*ad71d45eSYann Gautier 		mmc_dev_info->mmc_dev_type = MMC_IS_SD_HC;
354*ad71d45eSYann Gautier 	} else {
355*ad71d45eSYann Gautier 		mmc_dev_info->mmc_dev_type = MMC_IS_SD;
356*ad71d45eSYann Gautier 	}
357*ad71d45eSYann Gautier 
358*ad71d45eSYann Gautier 	return 0;
359*ad71d45eSYann Gautier }
360*ad71d45eSYann Gautier 
361*ad71d45eSYann Gautier static int mmc_send_op_cond(void)
362*ad71d45eSYann Gautier {
363*ad71d45eSYann Gautier 	int ret;
364*ad71d45eSYann Gautier 	int retries = SEND_OP_COND_MAX_RETRIES;
365*ad71d45eSYann Gautier 	unsigned int resp_data[4];
366*ad71d45eSYann Gautier 
367*ad71d45eSYann Gautier 	/* CMD0: reset to IDLE */
368*ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(0), 0, 0, NULL);
369*ad71d45eSYann Gautier 	if (ret != 0) {
370*ad71d45eSYann Gautier 		return ret;
371*ad71d45eSYann Gautier 	}
372*ad71d45eSYann Gautier 
373*ad71d45eSYann Gautier 	do {
374*ad71d45eSYann Gautier 		if (retries == 0) {
375*ad71d45eSYann Gautier 			ERROR("CMD1 failed after %d retries\n",
376*ad71d45eSYann Gautier 			      SEND_OP_COND_MAX_RETRIES);
377*ad71d45eSYann Gautier 			return -EIO;
378*ad71d45eSYann Gautier 		}
379*ad71d45eSYann Gautier 
380*ad71d45eSYann Gautier 		/* CMD1: get OCR register (SEND_OP_COND) */
381*ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(1), OCR_SECTOR_MODE |
382*ad71d45eSYann Gautier 				   OCR_VDD_MIN_2V7 | OCR_VDD_MIN_1V7,
383*ad71d45eSYann Gautier 				   MMC_RESPONSE_R(3), &resp_data[0]);
384*ad71d45eSYann Gautier 		if (ret != 0) {
385*ad71d45eSYann Gautier 			return ret;
386*ad71d45eSYann Gautier 		}
387*ad71d45eSYann Gautier 
388*ad71d45eSYann Gautier 		retries--;
389*ad71d45eSYann Gautier 	} while ((resp_data[0] & OCR_POWERUP) == 0U);
390*ad71d45eSYann Gautier 
391*ad71d45eSYann Gautier 	mmc_ocr_value = resp_data[0];
392*ad71d45eSYann Gautier 
393*ad71d45eSYann Gautier 	return 0;
394*ad71d45eSYann Gautier }
395*ad71d45eSYann Gautier 
396*ad71d45eSYann Gautier static int mmc_enumerate(unsigned int clk, unsigned int bus_width)
397*ad71d45eSYann Gautier {
398*ad71d45eSYann Gautier 	int ret;
399*ad71d45eSYann Gautier 	unsigned int resp_data[4];
400*ad71d45eSYann Gautier 
401*ad71d45eSYann Gautier 	ops->init();
402*ad71d45eSYann Gautier 
403*ad71d45eSYann Gautier 	/* CMD0: reset to IDLE */
404*ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(0), 0, 0, NULL);
405*ad71d45eSYann Gautier 	if (ret != 0) {
406*ad71d45eSYann Gautier 		return ret;
407*ad71d45eSYann Gautier 	}
408*ad71d45eSYann Gautier 
409*ad71d45eSYann Gautier 	/* CMD8: Send Interface Condition Command */
410*ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(8), VHS_2_7_3_6_V | CMD8_CHECK_PATTERN,
411*ad71d45eSYann Gautier 			   MMC_RESPONSE_R(7), &resp_data[0]);
412*ad71d45eSYann Gautier 
413*ad71d45eSYann Gautier 	if ((ret == 0) && ((resp_data[0] & 0xffU) == CMD8_CHECK_PATTERN)) {
414*ad71d45eSYann Gautier 		ret = sd_send_op_cond();
415*ad71d45eSYann Gautier 	} else {
416*ad71d45eSYann Gautier 		ret = mmc_send_op_cond();
417*ad71d45eSYann Gautier 	}
418*ad71d45eSYann Gautier 	if (ret != 0) {
419*ad71d45eSYann Gautier 		return ret;
420*ad71d45eSYann Gautier 	}
421*ad71d45eSYann Gautier 
422*ad71d45eSYann Gautier 	/* CMD2: Card Identification */
423*ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(2), 0, MMC_RESPONSE_R(2), NULL);
424*ad71d45eSYann Gautier 	if (ret != 0) {
425*ad71d45eSYann Gautier 		return ret;
426*ad71d45eSYann Gautier 	}
427*ad71d45eSYann Gautier 
428*ad71d45eSYann Gautier 	/* CMD3: Set Relative Address */
429*ad71d45eSYann Gautier 	if (mmc_dev_info->mmc_dev_type == MMC_IS_EMMC) {
430*ad71d45eSYann Gautier 		rca = MMC_FIX_RCA;
431*ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(3), rca << RCA_SHIFT_OFFSET,
432*ad71d45eSYann Gautier 				   MMC_RESPONSE_R(1), NULL);
433*ad71d45eSYann Gautier 		if (ret != 0) {
434*ad71d45eSYann Gautier 			return ret;
435*ad71d45eSYann Gautier 		}
436*ad71d45eSYann Gautier 	} else {
437*ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(3), 0,
438*ad71d45eSYann Gautier 				   MMC_RESPONSE_R(6), &resp_data[0]);
439*ad71d45eSYann Gautier 		if (ret != 0) {
440*ad71d45eSYann Gautier 			return ret;
441*ad71d45eSYann Gautier 		}
442*ad71d45eSYann Gautier 
443*ad71d45eSYann Gautier 		rca = (resp_data[0] & 0xFFFF0000U) >> 16;
444*ad71d45eSYann Gautier 	}
445*ad71d45eSYann Gautier 
446*ad71d45eSYann Gautier 	/* CMD9: CSD Register */
447*ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(9), rca << RCA_SHIFT_OFFSET,
448*ad71d45eSYann Gautier 			   MMC_RESPONSE_R(2), &resp_data[0]);
449*ad71d45eSYann Gautier 	if (ret != 0) {
450*ad71d45eSYann Gautier 		return ret;
451*ad71d45eSYann Gautier 	}
452*ad71d45eSYann Gautier 
453*ad71d45eSYann Gautier 	memcpy(&mmc_csd, &resp_data, sizeof(resp_data));
454*ad71d45eSYann Gautier 
455*ad71d45eSYann Gautier 	/* CMD7: Select Card */
456*ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(7), rca << RCA_SHIFT_OFFSET,
457*ad71d45eSYann Gautier 			   MMC_RESPONSE_R(1), NULL);
458*ad71d45eSYann Gautier 	if (ret != 0) {
459*ad71d45eSYann Gautier 		return ret;
460*ad71d45eSYann Gautier 	}
461*ad71d45eSYann Gautier 
462*ad71d45eSYann Gautier 	do {
463*ad71d45eSYann Gautier 		ret = mmc_device_state();
464*ad71d45eSYann Gautier 		if (ret < 0) {
465*ad71d45eSYann Gautier 			return ret;
466*ad71d45eSYann Gautier 		}
467*ad71d45eSYann Gautier 	} while (ret != MMC_STATE_TRAN);
468*ad71d45eSYann Gautier 
469*ad71d45eSYann Gautier 	ret = mmc_fill_device_info();
470*ad71d45eSYann Gautier 	if (ret != 0) {
471*ad71d45eSYann Gautier 		return ret;
472*ad71d45eSYann Gautier 	}
473*ad71d45eSYann Gautier 
474*ad71d45eSYann Gautier 	return mmc_set_ios(clk, bus_width);
475*ad71d45eSYann Gautier }
476*ad71d45eSYann Gautier 
477*ad71d45eSYann Gautier size_t mmc_read_blocks(unsigned int lba, uintptr_t buf, size_t size)
478*ad71d45eSYann Gautier {
479*ad71d45eSYann Gautier 	int ret;
480*ad71d45eSYann Gautier 	unsigned int cmd_idx, cmd_arg;
481*ad71d45eSYann Gautier 
482*ad71d45eSYann Gautier 	assert((ops != NULL) &&
483*ad71d45eSYann Gautier 	       (ops->read != NULL) &&
484*ad71d45eSYann Gautier 	       (size != 0U) &&
485*ad71d45eSYann Gautier 	       ((size & MMC_BLOCK_MASK) == 0U));
486*ad71d45eSYann Gautier 
487*ad71d45eSYann Gautier 	ret = ops->prepare(lba, buf, size);
488*ad71d45eSYann Gautier 	if (ret != 0) {
489*ad71d45eSYann Gautier 		return 0;
490*ad71d45eSYann Gautier 	}
491*ad71d45eSYann Gautier 
492*ad71d45eSYann Gautier 	if (is_cmd23_enabled()) {
493*ad71d45eSYann Gautier 		/* Set block count */
494*ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
495*ad71d45eSYann Gautier 				   MMC_RESPONSE_R(1), NULL);
496*ad71d45eSYann Gautier 		if (ret != 0) {
497*ad71d45eSYann Gautier 			return 0;
498*ad71d45eSYann Gautier 		}
499*ad71d45eSYann Gautier 
500*ad71d45eSYann Gautier 		cmd_idx = MMC_CMD(18);
501*ad71d45eSYann Gautier 	} else {
502*ad71d45eSYann Gautier 		if (size > MMC_BLOCK_SIZE) {
503*ad71d45eSYann Gautier 			cmd_idx = MMC_CMD(18);
504*ad71d45eSYann Gautier 		} else {
505*ad71d45eSYann Gautier 			cmd_idx = MMC_CMD(17);
506*ad71d45eSYann Gautier 		}
507*ad71d45eSYann Gautier 	}
508*ad71d45eSYann Gautier 
509*ad71d45eSYann Gautier 	if (((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) &&
510*ad71d45eSYann Gautier 	    (mmc_dev_info->mmc_dev_type != MMC_IS_SD_HC)) {
511*ad71d45eSYann Gautier 		cmd_arg = lba * MMC_BLOCK_SIZE;
512*ad71d45eSYann Gautier 	} else {
513*ad71d45eSYann Gautier 		cmd_arg = lba;
514*ad71d45eSYann Gautier 	}
515*ad71d45eSYann Gautier 
516*ad71d45eSYann Gautier 	ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R(1), NULL);
517*ad71d45eSYann Gautier 	if (ret != 0) {
518*ad71d45eSYann Gautier 		return 0;
519*ad71d45eSYann Gautier 	}
520*ad71d45eSYann Gautier 
521*ad71d45eSYann Gautier 	ret = ops->read(lba, buf, size);
522*ad71d45eSYann Gautier 	if (ret != 0) {
523*ad71d45eSYann Gautier 		return 0;
524*ad71d45eSYann Gautier 	}
525*ad71d45eSYann Gautier 
526*ad71d45eSYann Gautier 	/* Wait buffer empty */
527*ad71d45eSYann Gautier 	do {
528*ad71d45eSYann Gautier 		ret = mmc_device_state();
529*ad71d45eSYann Gautier 		if (ret < 0) {
530*ad71d45eSYann Gautier 			return 0;
531*ad71d45eSYann Gautier 		}
532*ad71d45eSYann Gautier 	} while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_DATA));
533*ad71d45eSYann Gautier 
534*ad71d45eSYann Gautier 	if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
535*ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(12), 0, 0, NULL);
536*ad71d45eSYann Gautier 		if (ret != 0) {
537*ad71d45eSYann Gautier 			return 0;
538*ad71d45eSYann Gautier 		}
539*ad71d45eSYann Gautier 	}
540*ad71d45eSYann Gautier 
541*ad71d45eSYann Gautier 	return size;
542*ad71d45eSYann Gautier }
543*ad71d45eSYann Gautier 
544*ad71d45eSYann Gautier size_t mmc_write_blocks(unsigned int lba, const uintptr_t buf, size_t size)
545*ad71d45eSYann Gautier {
546*ad71d45eSYann Gautier 	int ret;
547*ad71d45eSYann Gautier 	unsigned int cmd_idx, cmd_arg;
548*ad71d45eSYann Gautier 
549*ad71d45eSYann Gautier 	assert((ops != NULL) &&
550*ad71d45eSYann Gautier 	       (ops->write != NULL) &&
551*ad71d45eSYann Gautier 	       (size != 0U) &&
552*ad71d45eSYann Gautier 	       ((buf & MMC_BLOCK_MASK) == 0U) &&
553*ad71d45eSYann Gautier 	       ((size & MMC_BLOCK_MASK) == 0U));
554*ad71d45eSYann Gautier 
555*ad71d45eSYann Gautier 	ret = ops->prepare(lba, buf, size);
556*ad71d45eSYann Gautier 	if (ret != 0) {
557*ad71d45eSYann Gautier 		return 0;
558*ad71d45eSYann Gautier 	}
559*ad71d45eSYann Gautier 
560*ad71d45eSYann Gautier 	if (is_cmd23_enabled()) {
561*ad71d45eSYann Gautier 		/* Set block count */
562*ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(23), size / MMC_BLOCK_SIZE,
563*ad71d45eSYann Gautier 				   MMC_RESPONSE_R(1), NULL);
564*ad71d45eSYann Gautier 		if (ret != 0) {
565*ad71d45eSYann Gautier 			return 0;
566*ad71d45eSYann Gautier 		}
567*ad71d45eSYann Gautier 
568*ad71d45eSYann Gautier 		cmd_idx = MMC_CMD(25);
569*ad71d45eSYann Gautier 	} else {
570*ad71d45eSYann Gautier 		if (size > MMC_BLOCK_SIZE) {
571*ad71d45eSYann Gautier 			cmd_idx = MMC_CMD(25);
572*ad71d45eSYann Gautier 		} else {
573*ad71d45eSYann Gautier 			cmd_idx = MMC_CMD(24);
574*ad71d45eSYann Gautier 		}
575*ad71d45eSYann Gautier 	}
576*ad71d45eSYann Gautier 
577*ad71d45eSYann Gautier 	if ((mmc_ocr_value & OCR_ACCESS_MODE_MASK) == OCR_BYTE_MODE) {
578*ad71d45eSYann Gautier 		cmd_arg = lba * MMC_BLOCK_SIZE;
579*ad71d45eSYann Gautier 	} else {
580*ad71d45eSYann Gautier 		cmd_arg = lba;
581*ad71d45eSYann Gautier 	}
582*ad71d45eSYann Gautier 
583*ad71d45eSYann Gautier 	ret = mmc_send_cmd(cmd_idx, cmd_arg, MMC_RESPONSE_R(1), NULL);
584*ad71d45eSYann Gautier 	if (ret != 0) {
585*ad71d45eSYann Gautier 		return 0;
586*ad71d45eSYann Gautier 	}
587*ad71d45eSYann Gautier 
588*ad71d45eSYann Gautier 	ret = ops->write(lba, buf, size);
589*ad71d45eSYann Gautier 	if (ret != 0) {
590*ad71d45eSYann Gautier 		return 0;
591*ad71d45eSYann Gautier 	}
592*ad71d45eSYann Gautier 
593*ad71d45eSYann Gautier 	/* Wait buffer empty */
594*ad71d45eSYann Gautier 	do {
595*ad71d45eSYann Gautier 		ret = mmc_device_state();
596*ad71d45eSYann Gautier 		if (ret < 0) {
597*ad71d45eSYann Gautier 			return 0;
598*ad71d45eSYann Gautier 		}
599*ad71d45eSYann Gautier 	} while ((ret != MMC_STATE_TRAN) && (ret != MMC_STATE_RCV));
600*ad71d45eSYann Gautier 
601*ad71d45eSYann Gautier 	if (!is_cmd23_enabled() && (size > MMC_BLOCK_SIZE)) {
602*ad71d45eSYann Gautier 		ret = mmc_send_cmd(MMC_CMD(12), 0, 0, NULL);
603*ad71d45eSYann Gautier 		if (ret != 0) {
604*ad71d45eSYann Gautier 			return 0;
605*ad71d45eSYann Gautier 		}
606*ad71d45eSYann Gautier 	}
607*ad71d45eSYann Gautier 
608*ad71d45eSYann Gautier 	return size;
609*ad71d45eSYann Gautier }
610*ad71d45eSYann Gautier 
611*ad71d45eSYann Gautier size_t mmc_erase_blocks(unsigned int lba, size_t size)
612*ad71d45eSYann Gautier {
613*ad71d45eSYann Gautier 	int ret;
614*ad71d45eSYann Gautier 
615*ad71d45eSYann Gautier 	assert(ops != NULL);
616*ad71d45eSYann Gautier 	assert((size != 0U) && ((size & MMC_BLOCK_MASK) == 0U));
617*ad71d45eSYann Gautier 
618*ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(35), lba, MMC_RESPONSE_R(1), NULL);
619*ad71d45eSYann Gautier 	if (ret != 0) {
620*ad71d45eSYann Gautier 		return 0;
621*ad71d45eSYann Gautier 	}
622*ad71d45eSYann Gautier 
623*ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(36), lba + (size / MMC_BLOCK_SIZE) - 1U,
624*ad71d45eSYann Gautier 			   MMC_RESPONSE_R(1), NULL);
625*ad71d45eSYann Gautier 	if (ret != 0) {
626*ad71d45eSYann Gautier 		return 0;
627*ad71d45eSYann Gautier 	}
628*ad71d45eSYann Gautier 
629*ad71d45eSYann Gautier 	ret = mmc_send_cmd(MMC_CMD(38), lba, MMC_RESPONSE_R(0x1B), NULL);
630*ad71d45eSYann Gautier 	if (ret != 0) {
631*ad71d45eSYann Gautier 		return 0;
632*ad71d45eSYann Gautier 	}
633*ad71d45eSYann Gautier 
634*ad71d45eSYann Gautier 	do {
635*ad71d45eSYann Gautier 		ret = mmc_device_state();
636*ad71d45eSYann Gautier 		if (ret < 0) {
637*ad71d45eSYann Gautier 			return 0;
638*ad71d45eSYann Gautier 		}
639*ad71d45eSYann Gautier 	} while (ret != MMC_STATE_TRAN);
640*ad71d45eSYann Gautier 
641*ad71d45eSYann Gautier 	return size;
642*ad71d45eSYann Gautier }
643*ad71d45eSYann Gautier 
644*ad71d45eSYann Gautier static inline void mmc_rpmb_enable(void)
645*ad71d45eSYann Gautier {
646*ad71d45eSYann Gautier 	mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
647*ad71d45eSYann Gautier 			PART_CFG_BOOT_PARTITION1_ENABLE |
648*ad71d45eSYann Gautier 			PART_CFG_PARTITION1_ACCESS);
649*ad71d45eSYann Gautier }
650*ad71d45eSYann Gautier 
651*ad71d45eSYann Gautier static inline void mmc_rpmb_disable(void)
652*ad71d45eSYann Gautier {
653*ad71d45eSYann Gautier 	mmc_set_ext_csd(CMD_EXTCSD_PARTITION_CONFIG,
654*ad71d45eSYann Gautier 			PART_CFG_BOOT_PARTITION1_ENABLE);
655*ad71d45eSYann Gautier }
656*ad71d45eSYann Gautier 
657*ad71d45eSYann Gautier size_t mmc_rpmb_read_blocks(unsigned int lba, uintptr_t buf, size_t size)
658*ad71d45eSYann Gautier {
659*ad71d45eSYann Gautier 	size_t size_read;
660*ad71d45eSYann Gautier 
661*ad71d45eSYann Gautier 	mmc_rpmb_enable();
662*ad71d45eSYann Gautier 	size_read = mmc_read_blocks(lba, buf, size);
663*ad71d45eSYann Gautier 	mmc_rpmb_disable();
664*ad71d45eSYann Gautier 
665*ad71d45eSYann Gautier 	return size_read;
666*ad71d45eSYann Gautier }
667*ad71d45eSYann Gautier 
668*ad71d45eSYann Gautier size_t mmc_rpmb_write_blocks(unsigned int lba, const uintptr_t buf, size_t size)
669*ad71d45eSYann Gautier {
670*ad71d45eSYann Gautier 	size_t size_written;
671*ad71d45eSYann Gautier 
672*ad71d45eSYann Gautier 	mmc_rpmb_enable();
673*ad71d45eSYann Gautier 	size_written = mmc_write_blocks(lba, buf, size);
674*ad71d45eSYann Gautier 	mmc_rpmb_disable();
675*ad71d45eSYann Gautier 
676*ad71d45eSYann Gautier 	return size_written;
677*ad71d45eSYann Gautier }
678*ad71d45eSYann Gautier 
679*ad71d45eSYann Gautier size_t mmc_rpmb_erase_blocks(unsigned int lba, size_t size)
680*ad71d45eSYann Gautier {
681*ad71d45eSYann Gautier 	size_t size_erased;
682*ad71d45eSYann Gautier 
683*ad71d45eSYann Gautier 	mmc_rpmb_enable();
684*ad71d45eSYann Gautier 	size_erased = mmc_erase_blocks(lba, size);
685*ad71d45eSYann Gautier 	mmc_rpmb_disable();
686*ad71d45eSYann Gautier 
687*ad71d45eSYann Gautier 	return size_erased;
688*ad71d45eSYann Gautier }
689*ad71d45eSYann Gautier 
690*ad71d45eSYann Gautier int mmc_init(const struct mmc_ops *ops_ptr, unsigned int clk,
691*ad71d45eSYann Gautier 	     unsigned int width, unsigned int flags,
692*ad71d45eSYann Gautier 	     struct mmc_device_info *device_info)
693*ad71d45eSYann Gautier {
694*ad71d45eSYann Gautier 	assert((ops_ptr != NULL) &&
695*ad71d45eSYann Gautier 	       (ops_ptr->init != NULL) &&
696*ad71d45eSYann Gautier 	       (ops_ptr->send_cmd != NULL) &&
697*ad71d45eSYann Gautier 	       (ops_ptr->set_ios != NULL) &&
698*ad71d45eSYann Gautier 	       (ops_ptr->prepare != NULL) &&
699*ad71d45eSYann Gautier 	       (ops_ptr->read != NULL) &&
700*ad71d45eSYann Gautier 	       (ops_ptr->write != NULL) &&
701*ad71d45eSYann Gautier 	       (device_info != NULL) &&
702*ad71d45eSYann Gautier 	       (clk != 0) &&
703*ad71d45eSYann Gautier 	       ((width == MMC_BUS_WIDTH_1) ||
704*ad71d45eSYann Gautier 		(width == MMC_BUS_WIDTH_4) ||
705*ad71d45eSYann Gautier 		(width == MMC_BUS_WIDTH_8) ||
706*ad71d45eSYann Gautier 		(width == MMC_BUS_WIDTH_DDR_4) ||
707*ad71d45eSYann Gautier 		(width == MMC_BUS_WIDTH_DDR_8)));
708*ad71d45eSYann Gautier 
709*ad71d45eSYann Gautier 	ops = ops_ptr;
710*ad71d45eSYann Gautier 	mmc_flags = flags;
711*ad71d45eSYann Gautier 	mmc_dev_info = device_info;
712*ad71d45eSYann Gautier 
713*ad71d45eSYann Gautier 	return mmc_enumerate(clk, width);
714*ad71d45eSYann Gautier }
715