xref: /freebsd/usr.bin/sdiotool/cam_sdio.c (revision 1d386b48a555f61cb7325543adbbb5c3f3407a66)
1 /*-
2  * Copyright (c) 2017 Ilya Bakulin
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 #include "cam_sdio.h"
29 
30 /* Use CMD52 to read or write a single byte */
31 int
sdio_rw_direct(struct cam_device * dev,uint8_t func_number,uint32_t addr,uint8_t is_write,uint8_t * data,uint8_t * resp)32 sdio_rw_direct(struct cam_device *dev,
33 	       uint8_t func_number,
34 	       uint32_t addr,
35 	       uint8_t is_write,
36 	       uint8_t *data, uint8_t *resp) {
37 	union ccb *ccb;
38 	uint32_t flags;
39 	uint32_t arg;
40 	int retval = 0;
41 
42 	ccb = cam_getccb(dev);
43 	if (ccb == NULL) {
44 		warnx("%s: error allocating CCB", __func__);
45 		return (-1);
46 	}
47 	bzero(&(&ccb->ccb_h)[1],
48 	      sizeof(union ccb) - sizeof(struct ccb_hdr));
49 
50 	flags = MMC_RSP_R5 | MMC_CMD_AC;
51 	arg = SD_IO_RW_FUNC(func_number) | SD_IO_RW_ADR(addr);
52 	if (is_write)
53 		arg |= SD_IO_RW_WR | SD_IO_RW_RAW | SD_IO_RW_DAT(*data);
54 
55 	cam_fill_mmcio(&ccb->mmcio,
56 		       /*retries*/ 0,
57 		       /*cbfcnp*/ NULL,
58 		       /*flags*/ CAM_DIR_NONE,
59 		       /*mmc_opcode*/ SD_IO_RW_DIRECT,
60 		       /*mmc_arg*/ arg,
61 		       /*mmc_flags*/ flags,
62 		       /*mmc_data*/ 0,
63 		       /*timeout*/ 5000);
64 
65 	if (((retval = cam_send_ccb(dev, ccb)) < 0)
66 	    || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
67 		const char warnstr[] = "error sending command";
68 
69 		if (retval < 0)
70 			warn(warnstr);
71 		else
72 			warnx(warnstr);
73 		return (-1);
74 	}
75 
76 	*resp = ccb->mmcio.cmd.resp[0] & 0xFF;
77 	cam_freeccb(ccb);
78 	return (retval);
79 }
80 
81 /*
82  * CMD53 -- IO_RW_EXTENDED
83  * Use to read or write memory blocks
84  *
85  * is_increment=1: FIFO mode
86  * blk_count > 0: block mode
87  */
88 int
sdio_rw_extended(struct cam_device * dev,uint8_t func_number,uint32_t addr,uint8_t is_write,caddr_t data,size_t datalen,uint8_t is_increment,uint16_t blk_count)89 sdio_rw_extended(struct cam_device *dev,
90 		 uint8_t func_number,
91 		 uint32_t addr,
92 		 uint8_t is_write,
93 		 caddr_t data, size_t datalen,
94 		 uint8_t is_increment,
95 		 uint16_t blk_count) {
96 	union ccb *ccb;
97 	uint32_t flags;
98 	uint32_t arg;
99 	uint32_t cam_flags;
100 	uint8_t resp;
101 	struct mmc_data mmcd;
102 	int retval = 0;
103 
104 	if (blk_count != 0) {
105 		warnx("%s: block mode is not supported yet", __func__);
106 		return (-1);
107 	}
108 
109 	ccb = cam_getccb(dev);
110 	if (ccb == NULL) {
111 		warnx("%s: error allocating CCB", __func__);
112 		return (-1);
113 	}
114 	bzero(&(&ccb->ccb_h)[1],
115 	      sizeof(union ccb) - sizeof(struct ccb_hdr));
116 
117 	flags = MMC_RSP_R5 | MMC_CMD_ADTC;
118 	arg = SD_IO_RW_FUNC(func_number) | SD_IO_RW_ADR(addr) |
119 		SD_IOE_RW_LEN(datalen);
120 
121 	if (is_increment)
122 		arg |= SD_IO_RW_INCR;
123 
124 	mmcd.data = data;
125 	mmcd.len = datalen;
126 	mmcd.xfer_len = 0; /* not used by MMCCAM */
127 	mmcd.mrq = NULL; /* not used by MMCCAM */
128 
129 	if (is_write) {
130 		arg |= SD_IO_RW_WR;
131 		cam_flags = CAM_DIR_OUT;
132 		mmcd.flags = MMC_DATA_WRITE;
133 	} else {
134 		cam_flags = CAM_DIR_IN;
135 		mmcd.flags = MMC_DATA_READ;
136 	}
137 	cam_fill_mmcio(&ccb->mmcio,
138 		       /*retries*/ 0,
139 		       /*cbfcnp*/ NULL,
140 		       /*flags*/ cam_flags,
141 		       /*mmc_opcode*/ SD_IO_RW_EXTENDED,
142 		       /*mmc_arg*/ arg,
143 		       /*mmc_flags*/ flags,
144 		       /*mmc_data*/ &mmcd,
145 		       /*timeout*/ 5000);
146 
147 	if (((retval = cam_send_ccb(dev, ccb)) < 0)
148 	    || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
149 		const char warnstr[] = "error sending command";
150 
151 		if (retval < 0)
152 			warn(warnstr);
153 		else
154 			warnx(warnstr);
155 		return (-1);
156 	}
157 
158 	resp = ccb->mmcio.cmd.resp[0] & 0xFF;
159 	if (resp != 0)
160 		warn("Response from CMD53 is not 0?!");
161 	cam_freeccb(ccb);
162 	return (retval);
163 }
164 
165 
166 int
sdio_read_bool_for_func(struct cam_device * dev,uint32_t addr,uint8_t func_number,uint8_t * is_enab)167 sdio_read_bool_for_func(struct cam_device *dev, uint32_t addr, uint8_t func_number, uint8_t *is_enab) {
168 	uint8_t resp;
169 	int ret;
170 
171 	ret = sdio_rw_direct(dev, 0, addr, 0, NULL, &resp);
172 	if (ret < 0)
173 		return ret;
174 
175 	*is_enab = (resp & (1 << func_number)) > 0 ? 1 : 0;
176 
177 	return (0);
178 }
179 
180 int
sdio_set_bool_for_func(struct cam_device * dev,uint32_t addr,uint8_t func_number,int enable)181 sdio_set_bool_for_func(struct cam_device *dev, uint32_t addr, uint8_t func_number, int enable) {
182 	uint8_t resp;
183 	int ret;
184 	uint8_t is_enabled;
185 
186 	ret = sdio_rw_direct(dev, 0, addr, 0, NULL, &resp);
187 	if (ret != 0)
188 		return ret;
189 
190 	is_enabled = resp & (1 << func_number);
191 	if ((is_enabled !=0 && enable == 1) || (is_enabled == 0 && enable == 0))
192 		return 0;
193 
194 	if (enable)
195 		resp |= 1 << func_number;
196 	else
197 		resp &= ~ (1 << func_number);
198 
199 	ret = sdio_rw_direct(dev, 0, addr, 1, &resp, &resp);
200 
201 	return ret;
202 }
203 
204 /* Conventional I/O functions */
205 uint8_t
sdio_read_1(struct cam_device * dev,uint8_t func_number,uint32_t addr,int * ret)206 sdio_read_1(struct cam_device *dev, uint8_t func_number, uint32_t addr, int *ret) {
207 	uint8_t val;
208 	*ret = sdio_rw_direct(dev, func_number, addr, 0, NULL, &val);
209 	return val;
210 }
211 
212 int
sdio_write_1(struct cam_device * dev,uint8_t func_number,uint32_t addr,uint8_t val)213 sdio_write_1(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint8_t val) {
214 	uint8_t _val;
215 	return sdio_rw_direct(dev, func_number, addr, 0, &val, &_val);
216 }
217 
218 uint16_t
sdio_read_2(struct cam_device * dev,uint8_t func_number,uint32_t addr,int * ret)219 sdio_read_2(struct cam_device *dev, uint8_t func_number, uint32_t addr, int *ret) {
220 	uint16_t val;
221 	*ret = sdio_rw_extended(dev, func_number, addr,
222 				/* is_write */ 0,
223 				/* data */ (caddr_t) &val,
224 				/* datalen */ sizeof(val),
225 				/* is_increment */ 1,
226 				/* blk_count */ 0
227 		);
228 	return val;
229 }
230 
231 
232 int
sdio_write_2(struct cam_device * dev,uint8_t func_number,uint32_t addr,uint16_t val)233 sdio_write_2(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint16_t val) {
234 	return sdio_rw_extended(dev, func_number, addr,
235 				/* is_write */ 1,
236 				/* data */ (caddr_t) &val,
237 				/* datalen */ sizeof(val),
238 				/* is_increment */ 1,
239 				/* blk_count */ 0
240 		);
241 }
242 
243 uint32_t
sdio_read_4(struct cam_device * dev,uint8_t func_number,uint32_t addr,int * ret)244 sdio_read_4(struct cam_device *dev, uint8_t func_number, uint32_t addr, int *ret) {
245 	uint32_t val;
246 	*ret = sdio_rw_extended(dev, func_number, addr,
247 				/* is_write */ 0,
248 				/* data */ (caddr_t) &val,
249 				/* datalen */ sizeof(val),
250 				/* is_increment */ 1,
251 				/* blk_count */ 0
252 		);
253 	return val;
254 }
255 
256 
257 int
sdio_write_4(struct cam_device * dev,uint8_t func_number,uint32_t addr,uint32_t val)258 sdio_write_4(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint32_t val) {
259 	return sdio_rw_extended(dev, func_number, addr,
260 				/* is_write */ 1,
261 				/* data */ (caddr_t) &val,
262 				/* datalen */ sizeof(val),
263 				/* is_increment */ 1,
264 				/* blk_count */ 0
265 		);
266 }
267 
268 /* Higher-level wrappers for certain management operations */
269 int
sdio_is_func_ready(struct cam_device * dev,uint8_t func_number,uint8_t * is_enab)270 sdio_is_func_ready(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab) {
271 	return sdio_read_bool_for_func(dev, SD_IO_CCCR_FN_READY, func_number, is_enab);
272 }
273 
274 int
sdio_is_func_enabled(struct cam_device * dev,uint8_t func_number,uint8_t * is_enab)275 sdio_is_func_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab) {
276 	return sdio_read_bool_for_func(dev, SD_IO_CCCR_FN_ENABLE, func_number, is_enab);
277 }
278 
279 int
sdio_func_enable(struct cam_device * dev,uint8_t func_number,int enable)280 sdio_func_enable(struct cam_device *dev, uint8_t func_number, int enable) {
281 	return sdio_set_bool_for_func(dev, SD_IO_CCCR_FN_ENABLE, func_number, enable);
282 }
283 
284 int
sdio_is_func_intr_enabled(struct cam_device * dev,uint8_t func_number,uint8_t * is_enab)285 sdio_is_func_intr_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab) {
286 	return sdio_read_bool_for_func(dev, SD_IO_CCCR_INT_ENABLE, func_number, is_enab);
287 }
288 
289 int
sdio_func_intr_enable(struct cam_device * dev,uint8_t func_number,int enable)290 sdio_func_intr_enable(struct cam_device *dev, uint8_t func_number, int enable) {
291 	return sdio_set_bool_for_func(dev, SD_IO_CCCR_INT_ENABLE, func_number, enable);
292 }
293 
294 int
sdio_card_set_bus_width(struct cam_device * dev,enum mmc_bus_width bw)295 sdio_card_set_bus_width(struct cam_device *dev, enum mmc_bus_width bw) {
296 	int ret;
297 	uint8_t ctl_val;
298 	ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_BUS_WIDTH, 0, NULL, &ctl_val);
299 	if (ret < 0) {
300 		warn("Error getting CCCR_BUS_WIDTH value");
301 		return ret;
302 	}
303 	ctl_val &= ~0x3;
304 	switch (bw) {
305 	case bus_width_1:
306 		/* Already set to 1-bit */
307 		break;
308 	case bus_width_4:
309 		ctl_val |= CCCR_BUS_WIDTH_4;
310 		break;
311 	case bus_width_8:
312 		warn("Cannot do 8-bit on SDIO yet");
313 		return -1;
314 		break;
315 	}
316 	ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_BUS_WIDTH, 1, &ctl_val, &ctl_val);
317 	if (ret < 0) {
318 		warn("Error setting CCCR_BUS_WIDTH value");
319 		return ret;
320 	}
321 	return ret;
322 }
323 
324 int
sdio_func_read_cis(struct cam_device * dev,uint8_t func_number,uint32_t cis_addr,struct cis_info * info)325 sdio_func_read_cis(struct cam_device *dev, uint8_t func_number,
326 		   uint32_t cis_addr, struct cis_info *info) {
327 	uint8_t tuple_id, tuple_len, tuple_count;
328 	uint32_t addr;
329 
330 	char *cis1_info[4];
331 	int start, i, ch, count, ret;
332 	char cis1_info_buf[256];
333 
334 	tuple_count = 0; /* Use to prevent infinite loop in case of parse errors */
335 	memset(cis1_info_buf, 0, 256);
336 	do {
337 		addr = cis_addr;
338 		tuple_id = sdio_read_1(dev, 0, addr++, &ret);
339 		if (tuple_id == SD_IO_CISTPL_END)
340 			break;
341 		if (tuple_id == 0) {
342 			cis_addr++;
343 			continue;
344 		}
345 		tuple_len = sdio_read_1(dev, 0, addr++, &ret);
346 		if (tuple_len == 0 && tuple_id != 0x00) {
347 			warn("Parse error: 0-length tuple %02X\n", tuple_id);
348 			return -1;
349 		}
350 
351 		switch (tuple_id) {
352 		case SD_IO_CISTPL_VERS_1:
353 			addr += 2;
354 			for (count = 0, start = 0, i = 0;
355 			     (count < 4) && ((i + 4) < 256); i++) {
356 				ch = sdio_read_1(dev, 0, addr + i, &ret);
357 				printf("count=%d, start=%d, i=%d, Got %c (0x%02x)\n", count, start, i, ch, ch);
358 				if (ch == 0xff)
359 					break;
360 				cis1_info_buf[i] = ch;
361 				if (ch == 0) {
362 					cis1_info[count] =
363 						cis1_info_buf + start;
364 					start = i + 1;
365 					count++;
366 				}
367 			}
368 			printf("Card info:");
369 			for (i=0; i<4; i++)
370 				if (cis1_info[i])
371 					printf(" %s", cis1_info[i]);
372 			printf("\n");
373 			break;
374 		case SD_IO_CISTPL_MANFID:
375 			info->man_id =  sdio_read_1(dev, 0, addr++, &ret);
376 			info->man_id |= sdio_read_1(dev, 0, addr++, &ret) << 8;
377 
378 			info->prod_id =  sdio_read_1(dev, 0, addr++, &ret);
379 			info->prod_id |= sdio_read_1(dev, 0, addr++, &ret) << 8;
380 			break;
381 		case SD_IO_CISTPL_FUNCID:
382 			/* not sure if we need to parse it? */
383 			break;
384 		case SD_IO_CISTPL_FUNCE:
385 			if (tuple_len < 4) {
386 				printf("FUNCE is too short: %d\n", tuple_len);
387 				break;
388 			}
389 			if (func_number == 0) {
390 				/* skip extended_data */
391 				addr++;
392 				info->max_block_size  = sdio_read_1(dev, 0, addr++, &ret);
393 				info->max_block_size |= sdio_read_1(dev, 0, addr++, &ret) << 8;
394 			} else {
395 				info->max_block_size  = sdio_read_1(dev, 0, addr + 0xC, &ret);
396 				info->max_block_size |= sdio_read_1(dev, 0, addr + 0xD, &ret) << 8;
397 			}
398 			break;
399 		default:
400 			warnx("Skipping tuple ID %02X len %02X\n", tuple_id, tuple_len);
401 		}
402 		cis_addr += tuple_len + 2;
403 		tuple_count++;
404 	} while (tuple_count < 20);
405 
406 	return 0;
407 }
408 
409 uint32_t
sdio_get_common_cis_addr(struct cam_device * dev)410 sdio_get_common_cis_addr(struct cam_device *dev) {
411 	uint32_t addr;
412 	int ret;
413 
414 	addr =  sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR, &ret);
415 	addr |= sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR + 1, &ret) << 8;
416 	addr |= sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR + 2, &ret) << 16;
417 
418 	if (addr < SD_IO_CIS_START || addr > SD_IO_CIS_START + SD_IO_CIS_SIZE) {
419 		warn("Bad CIS address: %04X\n", addr);
420 		addr = 0;
421 	}
422 
423 	return addr;
424 }
425 
sdio_card_reset(struct cam_device * dev)426 void sdio_card_reset(struct cam_device *dev) {
427 	int ret;
428 	uint8_t ctl_val;
429 	ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_CTL, 0, NULL, &ctl_val);
430 	if (ret < 0)
431 		errx(1, "Error getting CCCR_CTL value");
432 	ctl_val |= CCCR_CTL_RES;
433 	ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_CTL, 1, &ctl_val, &ctl_val);
434 	if (ret < 0)
435 		errx(1, "Error setting CCCR_CTL value");
436 }
437