xref: /freebsd/usr.bin/sdiotool/cam_sdio.c (revision 1d386b48)
1029c02a3SIlya Bakulin /*-
2029c02a3SIlya Bakulin  * Copyright (c) 2017 Ilya Bakulin
3029c02a3SIlya Bakulin  * All rights reserved.
4029c02a3SIlya Bakulin  *
5029c02a3SIlya Bakulin  * Redistribution and use in source and binary forms, with or without
6029c02a3SIlya Bakulin  * modification, are permitted provided that the following conditions
7029c02a3SIlya Bakulin  * are met:
8029c02a3SIlya Bakulin  * 1. Redistributions of source code must retain the above copyright
9029c02a3SIlya Bakulin  *    notice, this list of conditions and the following disclaimer.
10029c02a3SIlya Bakulin  * 2. Redistributions in binary form must reproduce the above copyright
11029c02a3SIlya Bakulin  *    notice, this list of conditions and the following disclaimer in the
12029c02a3SIlya Bakulin  *    documentation and/or other materials provided with the distribution.
13029c02a3SIlya Bakulin  *
14029c02a3SIlya Bakulin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15029c02a3SIlya Bakulin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16029c02a3SIlya Bakulin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17029c02a3SIlya Bakulin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18029c02a3SIlya Bakulin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19029c02a3SIlya Bakulin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20029c02a3SIlya Bakulin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21029c02a3SIlya Bakulin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22029c02a3SIlya Bakulin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23029c02a3SIlya Bakulin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24029c02a3SIlya Bakulin  * SUCH DAMAGE.
25029c02a3SIlya Bakulin  */
26029c02a3SIlya Bakulin 
27029c02a3SIlya Bakulin #include <sys/cdefs.h>
28029c02a3SIlya Bakulin #include "cam_sdio.h"
29029c02a3SIlya Bakulin 
30029c02a3SIlya Bakulin /* Use CMD52 to read or write a single byte */
31029c02a3SIlya Bakulin 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)32029c02a3SIlya Bakulin sdio_rw_direct(struct cam_device *dev,
33029c02a3SIlya Bakulin 	       uint8_t func_number,
34029c02a3SIlya Bakulin 	       uint32_t addr,
35029c02a3SIlya Bakulin 	       uint8_t is_write,
36029c02a3SIlya Bakulin 	       uint8_t *data, uint8_t *resp) {
37029c02a3SIlya Bakulin 	union ccb *ccb;
38029c02a3SIlya Bakulin 	uint32_t flags;
39029c02a3SIlya Bakulin 	uint32_t arg;
40029c02a3SIlya Bakulin 	int retval = 0;
41029c02a3SIlya Bakulin 
42029c02a3SIlya Bakulin 	ccb = cam_getccb(dev);
43029c02a3SIlya Bakulin 	if (ccb == NULL) {
44029c02a3SIlya Bakulin 		warnx("%s: error allocating CCB", __func__);
45029c02a3SIlya Bakulin 		return (-1);
46029c02a3SIlya Bakulin 	}
47029c02a3SIlya Bakulin 	bzero(&(&ccb->ccb_h)[1],
48029c02a3SIlya Bakulin 	      sizeof(union ccb) - sizeof(struct ccb_hdr));
49029c02a3SIlya Bakulin 
50029c02a3SIlya Bakulin 	flags = MMC_RSP_R5 | MMC_CMD_AC;
51029c02a3SIlya Bakulin 	arg = SD_IO_RW_FUNC(func_number) | SD_IO_RW_ADR(addr);
52029c02a3SIlya Bakulin 	if (is_write)
53029c02a3SIlya Bakulin 		arg |= SD_IO_RW_WR | SD_IO_RW_RAW | SD_IO_RW_DAT(*data);
54029c02a3SIlya Bakulin 
55029c02a3SIlya Bakulin 	cam_fill_mmcio(&ccb->mmcio,
56029c02a3SIlya Bakulin 		       /*retries*/ 0,
57029c02a3SIlya Bakulin 		       /*cbfcnp*/ NULL,
58029c02a3SIlya Bakulin 		       /*flags*/ CAM_DIR_NONE,
59029c02a3SIlya Bakulin 		       /*mmc_opcode*/ SD_IO_RW_DIRECT,
60029c02a3SIlya Bakulin 		       /*mmc_arg*/ arg,
61029c02a3SIlya Bakulin 		       /*mmc_flags*/ flags,
62029c02a3SIlya Bakulin 		       /*mmc_data*/ 0,
63029c02a3SIlya Bakulin 		       /*timeout*/ 5000);
64029c02a3SIlya Bakulin 
65029c02a3SIlya Bakulin 	if (((retval = cam_send_ccb(dev, ccb)) < 0)
66029c02a3SIlya Bakulin 	    || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
67029c02a3SIlya Bakulin 		const char warnstr[] = "error sending command";
68029c02a3SIlya Bakulin 
69029c02a3SIlya Bakulin 		if (retval < 0)
70029c02a3SIlya Bakulin 			warn(warnstr);
71029c02a3SIlya Bakulin 		else
72029c02a3SIlya Bakulin 			warnx(warnstr);
73029c02a3SIlya Bakulin 		return (-1);
74029c02a3SIlya Bakulin 	}
75029c02a3SIlya Bakulin 
76029c02a3SIlya Bakulin 	*resp = ccb->mmcio.cmd.resp[0] & 0xFF;
77029c02a3SIlya Bakulin 	cam_freeccb(ccb);
78029c02a3SIlya Bakulin 	return (retval);
79029c02a3SIlya Bakulin }
80029c02a3SIlya Bakulin 
81029c02a3SIlya Bakulin /*
82029c02a3SIlya Bakulin  * CMD53 -- IO_RW_EXTENDED
83029c02a3SIlya Bakulin  * Use to read or write memory blocks
84029c02a3SIlya Bakulin  *
85029c02a3SIlya Bakulin  * is_increment=1: FIFO mode
86029c02a3SIlya Bakulin  * blk_count > 0: block mode
87029c02a3SIlya Bakulin  */
88029c02a3SIlya Bakulin 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)89029c02a3SIlya Bakulin sdio_rw_extended(struct cam_device *dev,
90029c02a3SIlya Bakulin 		 uint8_t func_number,
91029c02a3SIlya Bakulin 		 uint32_t addr,
92029c02a3SIlya Bakulin 		 uint8_t is_write,
93029c02a3SIlya Bakulin 		 caddr_t data, size_t datalen,
94029c02a3SIlya Bakulin 		 uint8_t is_increment,
95029c02a3SIlya Bakulin 		 uint16_t blk_count) {
96029c02a3SIlya Bakulin 	union ccb *ccb;
97029c02a3SIlya Bakulin 	uint32_t flags;
98029c02a3SIlya Bakulin 	uint32_t arg;
99029c02a3SIlya Bakulin 	uint32_t cam_flags;
100029c02a3SIlya Bakulin 	uint8_t resp;
101029c02a3SIlya Bakulin 	struct mmc_data mmcd;
102029c02a3SIlya Bakulin 	int retval = 0;
103029c02a3SIlya Bakulin 
104029c02a3SIlya Bakulin 	if (blk_count != 0) {
105029c02a3SIlya Bakulin 		warnx("%s: block mode is not supported yet", __func__);
106029c02a3SIlya Bakulin 		return (-1);
107029c02a3SIlya Bakulin 	}
108029c02a3SIlya Bakulin 
109029c02a3SIlya Bakulin 	ccb = cam_getccb(dev);
110029c02a3SIlya Bakulin 	if (ccb == NULL) {
111029c02a3SIlya Bakulin 		warnx("%s: error allocating CCB", __func__);
112029c02a3SIlya Bakulin 		return (-1);
113029c02a3SIlya Bakulin 	}
114029c02a3SIlya Bakulin 	bzero(&(&ccb->ccb_h)[1],
115029c02a3SIlya Bakulin 	      sizeof(union ccb) - sizeof(struct ccb_hdr));
116029c02a3SIlya Bakulin 
117029c02a3SIlya Bakulin 	flags = MMC_RSP_R5 | MMC_CMD_ADTC;
118029c02a3SIlya Bakulin 	arg = SD_IO_RW_FUNC(func_number) | SD_IO_RW_ADR(addr) |
119029c02a3SIlya Bakulin 		SD_IOE_RW_LEN(datalen);
120029c02a3SIlya Bakulin 
121029c02a3SIlya Bakulin 	if (is_increment)
122029c02a3SIlya Bakulin 		arg |= SD_IO_RW_INCR;
123029c02a3SIlya Bakulin 
124029c02a3SIlya Bakulin 	mmcd.data = data;
125029c02a3SIlya Bakulin 	mmcd.len = datalen;
126029c02a3SIlya Bakulin 	mmcd.xfer_len = 0; /* not used by MMCCAM */
127029c02a3SIlya Bakulin 	mmcd.mrq = NULL; /* not used by MMCCAM */
128029c02a3SIlya Bakulin 
129029c02a3SIlya Bakulin 	if (is_write) {
130029c02a3SIlya Bakulin 		arg |= SD_IO_RW_WR;
131029c02a3SIlya Bakulin 		cam_flags = CAM_DIR_OUT;
132029c02a3SIlya Bakulin 		mmcd.flags = MMC_DATA_WRITE;
133029c02a3SIlya Bakulin 	} else {
134029c02a3SIlya Bakulin 		cam_flags = CAM_DIR_IN;
135029c02a3SIlya Bakulin 		mmcd.flags = MMC_DATA_READ;
136029c02a3SIlya Bakulin 	}
137029c02a3SIlya Bakulin 	cam_fill_mmcio(&ccb->mmcio,
138029c02a3SIlya Bakulin 		       /*retries*/ 0,
139029c02a3SIlya Bakulin 		       /*cbfcnp*/ NULL,
140029c02a3SIlya Bakulin 		       /*flags*/ cam_flags,
141029c02a3SIlya Bakulin 		       /*mmc_opcode*/ SD_IO_RW_EXTENDED,
142029c02a3SIlya Bakulin 		       /*mmc_arg*/ arg,
143029c02a3SIlya Bakulin 		       /*mmc_flags*/ flags,
144029c02a3SIlya Bakulin 		       /*mmc_data*/ &mmcd,
145029c02a3SIlya Bakulin 		       /*timeout*/ 5000);
146029c02a3SIlya Bakulin 
147029c02a3SIlya Bakulin 	if (((retval = cam_send_ccb(dev, ccb)) < 0)
148029c02a3SIlya Bakulin 	    || ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP)) {
149029c02a3SIlya Bakulin 		const char warnstr[] = "error sending command";
150029c02a3SIlya Bakulin 
151029c02a3SIlya Bakulin 		if (retval < 0)
152029c02a3SIlya Bakulin 			warn(warnstr);
153029c02a3SIlya Bakulin 		else
154029c02a3SIlya Bakulin 			warnx(warnstr);
155029c02a3SIlya Bakulin 		return (-1);
156029c02a3SIlya Bakulin 	}
157029c02a3SIlya Bakulin 
158029c02a3SIlya Bakulin 	resp = ccb->mmcio.cmd.resp[0] & 0xFF;
159029c02a3SIlya Bakulin 	if (resp != 0)
160029c02a3SIlya Bakulin 		warn("Response from CMD53 is not 0?!");
161029c02a3SIlya Bakulin 	cam_freeccb(ccb);
162029c02a3SIlya Bakulin 	return (retval);
163029c02a3SIlya Bakulin }
164029c02a3SIlya Bakulin 
165029c02a3SIlya Bakulin 
166029c02a3SIlya Bakulin int
sdio_read_bool_for_func(struct cam_device * dev,uint32_t addr,uint8_t func_number,uint8_t * is_enab)167029c02a3SIlya Bakulin sdio_read_bool_for_func(struct cam_device *dev, uint32_t addr, uint8_t func_number, uint8_t *is_enab) {
168029c02a3SIlya Bakulin 	uint8_t resp;
169029c02a3SIlya Bakulin 	int ret;
170029c02a3SIlya Bakulin 
171029c02a3SIlya Bakulin 	ret = sdio_rw_direct(dev, 0, addr, 0, NULL, &resp);
172029c02a3SIlya Bakulin 	if (ret < 0)
173029c02a3SIlya Bakulin 		return ret;
174029c02a3SIlya Bakulin 
175029c02a3SIlya Bakulin 	*is_enab = (resp & (1 << func_number)) > 0 ? 1 : 0;
176029c02a3SIlya Bakulin 
177029c02a3SIlya Bakulin 	return (0);
178029c02a3SIlya Bakulin }
179029c02a3SIlya Bakulin 
180029c02a3SIlya Bakulin int
sdio_set_bool_for_func(struct cam_device * dev,uint32_t addr,uint8_t func_number,int enable)181029c02a3SIlya Bakulin sdio_set_bool_for_func(struct cam_device *dev, uint32_t addr, uint8_t func_number, int enable) {
182029c02a3SIlya Bakulin 	uint8_t resp;
183029c02a3SIlya Bakulin 	int ret;
184029c02a3SIlya Bakulin 	uint8_t is_enabled;
185029c02a3SIlya Bakulin 
186029c02a3SIlya Bakulin 	ret = sdio_rw_direct(dev, 0, addr, 0, NULL, &resp);
187029c02a3SIlya Bakulin 	if (ret != 0)
188029c02a3SIlya Bakulin 		return ret;
189029c02a3SIlya Bakulin 
190029c02a3SIlya Bakulin 	is_enabled = resp & (1 << func_number);
191029c02a3SIlya Bakulin 	if ((is_enabled !=0 && enable == 1) || (is_enabled == 0 && enable == 0))
192029c02a3SIlya Bakulin 		return 0;
193029c02a3SIlya Bakulin 
194029c02a3SIlya Bakulin 	if (enable)
195029c02a3SIlya Bakulin 		resp |= 1 << func_number;
196029c02a3SIlya Bakulin 	else
197029c02a3SIlya Bakulin 		resp &= ~ (1 << func_number);
198029c02a3SIlya Bakulin 
199029c02a3SIlya Bakulin 	ret = sdio_rw_direct(dev, 0, addr, 1, &resp, &resp);
200029c02a3SIlya Bakulin 
201029c02a3SIlya Bakulin 	return ret;
202029c02a3SIlya Bakulin }
203029c02a3SIlya Bakulin 
204029c02a3SIlya Bakulin /* Conventional I/O functions */
205029c02a3SIlya Bakulin uint8_t
sdio_read_1(struct cam_device * dev,uint8_t func_number,uint32_t addr,int * ret)206029c02a3SIlya Bakulin sdio_read_1(struct cam_device *dev, uint8_t func_number, uint32_t addr, int *ret) {
207029c02a3SIlya Bakulin 	uint8_t val;
208029c02a3SIlya Bakulin 	*ret = sdio_rw_direct(dev, func_number, addr, 0, NULL, &val);
209029c02a3SIlya Bakulin 	return val;
210029c02a3SIlya Bakulin }
211029c02a3SIlya Bakulin 
212029c02a3SIlya Bakulin int
sdio_write_1(struct cam_device * dev,uint8_t func_number,uint32_t addr,uint8_t val)213029c02a3SIlya Bakulin sdio_write_1(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint8_t val) {
214029c02a3SIlya Bakulin 	uint8_t _val;
215029c02a3SIlya Bakulin 	return sdio_rw_direct(dev, func_number, addr, 0, &val, &_val);
216029c02a3SIlya Bakulin }
217029c02a3SIlya Bakulin 
218029c02a3SIlya Bakulin uint16_t
sdio_read_2(struct cam_device * dev,uint8_t func_number,uint32_t addr,int * ret)219029c02a3SIlya Bakulin sdio_read_2(struct cam_device *dev, uint8_t func_number, uint32_t addr, int *ret) {
220029c02a3SIlya Bakulin 	uint16_t val;
221029c02a3SIlya Bakulin 	*ret = sdio_rw_extended(dev, func_number, addr,
222029c02a3SIlya Bakulin 				/* is_write */ 0,
223029c02a3SIlya Bakulin 				/* data */ (caddr_t) &val,
224029c02a3SIlya Bakulin 				/* datalen */ sizeof(val),
225029c02a3SIlya Bakulin 				/* is_increment */ 1,
226029c02a3SIlya Bakulin 				/* blk_count */ 0
227029c02a3SIlya Bakulin 		);
228029c02a3SIlya Bakulin 	return val;
229029c02a3SIlya Bakulin }
230029c02a3SIlya Bakulin 
231029c02a3SIlya Bakulin 
232029c02a3SIlya Bakulin int
sdio_write_2(struct cam_device * dev,uint8_t func_number,uint32_t addr,uint16_t val)233029c02a3SIlya Bakulin sdio_write_2(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint16_t val) {
234029c02a3SIlya Bakulin 	return sdio_rw_extended(dev, func_number, addr,
235029c02a3SIlya Bakulin 				/* is_write */ 1,
236029c02a3SIlya Bakulin 				/* data */ (caddr_t) &val,
237029c02a3SIlya Bakulin 				/* datalen */ sizeof(val),
238029c02a3SIlya Bakulin 				/* is_increment */ 1,
239029c02a3SIlya Bakulin 				/* blk_count */ 0
240029c02a3SIlya Bakulin 		);
241029c02a3SIlya Bakulin }
242029c02a3SIlya Bakulin 
243029c02a3SIlya Bakulin uint32_t
sdio_read_4(struct cam_device * dev,uint8_t func_number,uint32_t addr,int * ret)244029c02a3SIlya Bakulin sdio_read_4(struct cam_device *dev, uint8_t func_number, uint32_t addr, int *ret) {
245029c02a3SIlya Bakulin 	uint32_t val;
246029c02a3SIlya Bakulin 	*ret = sdio_rw_extended(dev, func_number, addr,
247029c02a3SIlya Bakulin 				/* is_write */ 0,
248029c02a3SIlya Bakulin 				/* data */ (caddr_t) &val,
249029c02a3SIlya Bakulin 				/* datalen */ sizeof(val),
250029c02a3SIlya Bakulin 				/* is_increment */ 1,
251029c02a3SIlya Bakulin 				/* blk_count */ 0
252029c02a3SIlya Bakulin 		);
253029c02a3SIlya Bakulin 	return val;
254029c02a3SIlya Bakulin }
255029c02a3SIlya Bakulin 
256029c02a3SIlya Bakulin 
257029c02a3SIlya Bakulin int
sdio_write_4(struct cam_device * dev,uint8_t func_number,uint32_t addr,uint32_t val)258029c02a3SIlya Bakulin sdio_write_4(struct cam_device *dev, uint8_t func_number, uint32_t addr, uint32_t val) {
259029c02a3SIlya Bakulin 	return sdio_rw_extended(dev, func_number, addr,
260029c02a3SIlya Bakulin 				/* is_write */ 1,
261029c02a3SIlya Bakulin 				/* data */ (caddr_t) &val,
262029c02a3SIlya Bakulin 				/* datalen */ sizeof(val),
263029c02a3SIlya Bakulin 				/* is_increment */ 1,
264029c02a3SIlya Bakulin 				/* blk_count */ 0
265029c02a3SIlya Bakulin 		);
266029c02a3SIlya Bakulin }
267029c02a3SIlya Bakulin 
268029c02a3SIlya Bakulin /* Higher-level wrappers for certain management operations */
269029c02a3SIlya Bakulin int
sdio_is_func_ready(struct cam_device * dev,uint8_t func_number,uint8_t * is_enab)270029c02a3SIlya Bakulin sdio_is_func_ready(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab) {
271029c02a3SIlya Bakulin 	return sdio_read_bool_for_func(dev, SD_IO_CCCR_FN_READY, func_number, is_enab);
272029c02a3SIlya Bakulin }
273029c02a3SIlya Bakulin 
274029c02a3SIlya Bakulin int
sdio_is_func_enabled(struct cam_device * dev,uint8_t func_number,uint8_t * is_enab)275029c02a3SIlya Bakulin sdio_is_func_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab) {
276029c02a3SIlya Bakulin 	return sdio_read_bool_for_func(dev, SD_IO_CCCR_FN_ENABLE, func_number, is_enab);
277029c02a3SIlya Bakulin }
278029c02a3SIlya Bakulin 
279029c02a3SIlya Bakulin int
sdio_func_enable(struct cam_device * dev,uint8_t func_number,int enable)280029c02a3SIlya Bakulin sdio_func_enable(struct cam_device *dev, uint8_t func_number, int enable) {
281029c02a3SIlya Bakulin 	return sdio_set_bool_for_func(dev, SD_IO_CCCR_FN_ENABLE, func_number, enable);
282029c02a3SIlya Bakulin }
283029c02a3SIlya Bakulin 
284029c02a3SIlya Bakulin int
sdio_is_func_intr_enabled(struct cam_device * dev,uint8_t func_number,uint8_t * is_enab)285029c02a3SIlya Bakulin sdio_is_func_intr_enabled(struct cam_device *dev, uint8_t func_number, uint8_t *is_enab) {
286029c02a3SIlya Bakulin 	return sdio_read_bool_for_func(dev, SD_IO_CCCR_INT_ENABLE, func_number, is_enab);
287029c02a3SIlya Bakulin }
288029c02a3SIlya Bakulin 
289029c02a3SIlya Bakulin int
sdio_func_intr_enable(struct cam_device * dev,uint8_t func_number,int enable)290029c02a3SIlya Bakulin sdio_func_intr_enable(struct cam_device *dev, uint8_t func_number, int enable) {
291029c02a3SIlya Bakulin 	return sdio_set_bool_for_func(dev, SD_IO_CCCR_INT_ENABLE, func_number, enable);
292029c02a3SIlya Bakulin }
293029c02a3SIlya Bakulin 
294029c02a3SIlya Bakulin int
sdio_card_set_bus_width(struct cam_device * dev,enum mmc_bus_width bw)295029c02a3SIlya Bakulin sdio_card_set_bus_width(struct cam_device *dev, enum mmc_bus_width bw) {
296029c02a3SIlya Bakulin 	int ret;
297029c02a3SIlya Bakulin 	uint8_t ctl_val;
298029c02a3SIlya Bakulin 	ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_BUS_WIDTH, 0, NULL, &ctl_val);
299029c02a3SIlya Bakulin 	if (ret < 0) {
300029c02a3SIlya Bakulin 		warn("Error getting CCCR_BUS_WIDTH value");
301029c02a3SIlya Bakulin 		return ret;
302029c02a3SIlya Bakulin 	}
303029c02a3SIlya Bakulin 	ctl_val &= ~0x3;
304029c02a3SIlya Bakulin 	switch (bw) {
305029c02a3SIlya Bakulin 	case bus_width_1:
306029c02a3SIlya Bakulin 		/* Already set to 1-bit */
307029c02a3SIlya Bakulin 		break;
308029c02a3SIlya Bakulin 	case bus_width_4:
309029c02a3SIlya Bakulin 		ctl_val |= CCCR_BUS_WIDTH_4;
310029c02a3SIlya Bakulin 		break;
311029c02a3SIlya Bakulin 	case bus_width_8:
312029c02a3SIlya Bakulin 		warn("Cannot do 8-bit on SDIO yet");
313029c02a3SIlya Bakulin 		return -1;
314029c02a3SIlya Bakulin 		break;
315029c02a3SIlya Bakulin 	}
316029c02a3SIlya Bakulin 	ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_BUS_WIDTH, 1, &ctl_val, &ctl_val);
317029c02a3SIlya Bakulin 	if (ret < 0) {
318029c02a3SIlya Bakulin 		warn("Error setting CCCR_BUS_WIDTH value");
319029c02a3SIlya Bakulin 		return ret;
320029c02a3SIlya Bakulin 	}
321029c02a3SIlya Bakulin 	return ret;
322029c02a3SIlya Bakulin }
323029c02a3SIlya Bakulin 
324029c02a3SIlya Bakulin int
sdio_func_read_cis(struct cam_device * dev,uint8_t func_number,uint32_t cis_addr,struct cis_info * info)325029c02a3SIlya Bakulin sdio_func_read_cis(struct cam_device *dev, uint8_t func_number,
326029c02a3SIlya Bakulin 		   uint32_t cis_addr, struct cis_info *info) {
327029c02a3SIlya Bakulin 	uint8_t tuple_id, tuple_len, tuple_count;
328029c02a3SIlya Bakulin 	uint32_t addr;
329029c02a3SIlya Bakulin 
330029c02a3SIlya Bakulin 	char *cis1_info[4];
331029c02a3SIlya Bakulin 	int start, i, ch, count, ret;
332029c02a3SIlya Bakulin 	char cis1_info_buf[256];
333029c02a3SIlya Bakulin 
334029c02a3SIlya Bakulin 	tuple_count = 0; /* Use to prevent infinite loop in case of parse errors */
335029c02a3SIlya Bakulin 	memset(cis1_info_buf, 0, 256);
336029c02a3SIlya Bakulin 	do {
337029c02a3SIlya Bakulin 		addr = cis_addr;
338029c02a3SIlya Bakulin 		tuple_id = sdio_read_1(dev, 0, addr++, &ret);
339029c02a3SIlya Bakulin 		if (tuple_id == SD_IO_CISTPL_END)
340029c02a3SIlya Bakulin 			break;
341029c02a3SIlya Bakulin 		if (tuple_id == 0) {
342029c02a3SIlya Bakulin 			cis_addr++;
343029c02a3SIlya Bakulin 			continue;
344029c02a3SIlya Bakulin 		}
345029c02a3SIlya Bakulin 		tuple_len = sdio_read_1(dev, 0, addr++, &ret);
346029c02a3SIlya Bakulin 		if (tuple_len == 0 && tuple_id != 0x00) {
347029c02a3SIlya Bakulin 			warn("Parse error: 0-length tuple %02X\n", tuple_id);
348029c02a3SIlya Bakulin 			return -1;
349029c02a3SIlya Bakulin 		}
350029c02a3SIlya Bakulin 
351029c02a3SIlya Bakulin 		switch (tuple_id) {
352029c02a3SIlya Bakulin 		case SD_IO_CISTPL_VERS_1:
353029c02a3SIlya Bakulin 			addr += 2;
354029c02a3SIlya Bakulin 			for (count = 0, start = 0, i = 0;
355029c02a3SIlya Bakulin 			     (count < 4) && ((i + 4) < 256); i++) {
356029c02a3SIlya Bakulin 				ch = sdio_read_1(dev, 0, addr + i, &ret);
357029c02a3SIlya Bakulin 				printf("count=%d, start=%d, i=%d, Got %c (0x%02x)\n", count, start, i, ch, ch);
358029c02a3SIlya Bakulin 				if (ch == 0xff)
359029c02a3SIlya Bakulin 					break;
360029c02a3SIlya Bakulin 				cis1_info_buf[i] = ch;
361029c02a3SIlya Bakulin 				if (ch == 0) {
362029c02a3SIlya Bakulin 					cis1_info[count] =
363029c02a3SIlya Bakulin 						cis1_info_buf + start;
364029c02a3SIlya Bakulin 					start = i + 1;
365029c02a3SIlya Bakulin 					count++;
366029c02a3SIlya Bakulin 				}
367029c02a3SIlya Bakulin 			}
368029c02a3SIlya Bakulin 			printf("Card info:");
369029c02a3SIlya Bakulin 			for (i=0; i<4; i++)
370029c02a3SIlya Bakulin 				if (cis1_info[i])
371029c02a3SIlya Bakulin 					printf(" %s", cis1_info[i]);
372029c02a3SIlya Bakulin 			printf("\n");
373029c02a3SIlya Bakulin 			break;
374029c02a3SIlya Bakulin 		case SD_IO_CISTPL_MANFID:
375029c02a3SIlya Bakulin 			info->man_id =  sdio_read_1(dev, 0, addr++, &ret);
376029c02a3SIlya Bakulin 			info->man_id |= sdio_read_1(dev, 0, addr++, &ret) << 8;
377029c02a3SIlya Bakulin 
378029c02a3SIlya Bakulin 			info->prod_id =  sdio_read_1(dev, 0, addr++, &ret);
379029c02a3SIlya Bakulin 			info->prod_id |= sdio_read_1(dev, 0, addr++, &ret) << 8;
380029c02a3SIlya Bakulin 			break;
381029c02a3SIlya Bakulin 		case SD_IO_CISTPL_FUNCID:
382029c02a3SIlya Bakulin 			/* not sure if we need to parse it? */
383029c02a3SIlya Bakulin 			break;
384029c02a3SIlya Bakulin 		case SD_IO_CISTPL_FUNCE:
385029c02a3SIlya Bakulin 			if (tuple_len < 4) {
386029c02a3SIlya Bakulin 				printf("FUNCE is too short: %d\n", tuple_len);
387029c02a3SIlya Bakulin 				break;
388029c02a3SIlya Bakulin 			}
389029c02a3SIlya Bakulin 			if (func_number == 0) {
390029c02a3SIlya Bakulin 				/* skip extended_data */
391029c02a3SIlya Bakulin 				addr++;
392029c02a3SIlya Bakulin 				info->max_block_size  = sdio_read_1(dev, 0, addr++, &ret);
393029c02a3SIlya Bakulin 				info->max_block_size |= sdio_read_1(dev, 0, addr++, &ret) << 8;
394029c02a3SIlya Bakulin 			} else {
395029c02a3SIlya Bakulin 				info->max_block_size  = sdio_read_1(dev, 0, addr + 0xC, &ret);
396029c02a3SIlya Bakulin 				info->max_block_size |= sdio_read_1(dev, 0, addr + 0xD, &ret) << 8;
397029c02a3SIlya Bakulin 			}
398029c02a3SIlya Bakulin 			break;
399029c02a3SIlya Bakulin 		default:
400029c02a3SIlya Bakulin 			warnx("Skipping tuple ID %02X len %02X\n", tuple_id, tuple_len);
401029c02a3SIlya Bakulin 		}
402029c02a3SIlya Bakulin 		cis_addr += tuple_len + 2;
403029c02a3SIlya Bakulin 		tuple_count++;
404029c02a3SIlya Bakulin 	} while (tuple_count < 20);
405029c02a3SIlya Bakulin 
406029c02a3SIlya Bakulin 	return 0;
407029c02a3SIlya Bakulin }
408029c02a3SIlya Bakulin 
409029c02a3SIlya Bakulin uint32_t
sdio_get_common_cis_addr(struct cam_device * dev)410029c02a3SIlya Bakulin sdio_get_common_cis_addr(struct cam_device *dev) {
411029c02a3SIlya Bakulin 	uint32_t addr;
412029c02a3SIlya Bakulin 	int ret;
413029c02a3SIlya Bakulin 
414029c02a3SIlya Bakulin 	addr =  sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR, &ret);
415029c02a3SIlya Bakulin 	addr |= sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR + 1, &ret) << 8;
416029c02a3SIlya Bakulin 	addr |= sdio_read_1(dev, 0, SD_IO_CCCR_CISPTR + 2, &ret) << 16;
417029c02a3SIlya Bakulin 
418029c02a3SIlya Bakulin 	if (addr < SD_IO_CIS_START || addr > SD_IO_CIS_START + SD_IO_CIS_SIZE) {
419029c02a3SIlya Bakulin 		warn("Bad CIS address: %04X\n", addr);
420029c02a3SIlya Bakulin 		addr = 0;
421029c02a3SIlya Bakulin 	}
422029c02a3SIlya Bakulin 
423029c02a3SIlya Bakulin 	return addr;
424029c02a3SIlya Bakulin }
425029c02a3SIlya Bakulin 
sdio_card_reset(struct cam_device * dev)426029c02a3SIlya Bakulin void sdio_card_reset(struct cam_device *dev) {
427029c02a3SIlya Bakulin 	int ret;
428029c02a3SIlya Bakulin 	uint8_t ctl_val;
429029c02a3SIlya Bakulin 	ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_CTL, 0, NULL, &ctl_val);
430029c02a3SIlya Bakulin 	if (ret < 0)
431029c02a3SIlya Bakulin 		errx(1, "Error getting CCCR_CTL value");
432029c02a3SIlya Bakulin 	ctl_val |= CCCR_CTL_RES;
433029c02a3SIlya Bakulin 	ret = sdio_rw_direct(dev, 0, SD_IO_CCCR_CTL, 1, &ctl_val, &ctl_val);
434029c02a3SIlya Bakulin 	if (ret < 0)
435029c02a3SIlya Bakulin 		errx(1, "Error setting CCCR_CTL value");
436029c02a3SIlya Bakulin }
437