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