xref: /illumos-gate/usr/src/uts/common/io/sdcard/impl/sda_mem.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
23  * Copyright 2011 Nexenta Systems, Inc.  All rights reserved.
24  */
25 
26 /*
27  * Memory target support for SDcard.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/note.h>
32 #include <sys/conf.h>
33 #include <sys/blkdev.h>
34 #include <sys/ddi.h>
35 #include <sys/sunddi.h>
36 #include <sys/sdcard/sda.h>
37 #include <sys/sdcard/sda_impl.h>
38 
39 static int sda_mem_errno(sda_err_t);
40 static int sda_mem_rw(sda_slot_t *, bd_xfer_t *, uint8_t, uint16_t);
41 static void sda_mem_done(sda_cmd_t *);
42 static void sda_mem_getstring(uint32_t *, char *, int, int);
43 
44 /*
45  * To minimize complexity and reduce layering, we implement almost the
46  * entire memory card driver (sdcard) here.  The memory card still
47  * needs to be a separate driver though, due to the requirement to
48  * have both SCSI HBA bus ops and SD bus ops.
49  */
50 
51 /*
52  * Everything beyond this is private.
53  */
54 
55 int
56 sda_mem_errno(sda_err_t errno)
57 {
58 	/* the hot path */
59 	if (errno == SDA_EOK) {
60 		return (0);
61 	}
62 
63 	switch (errno) {
64 	case SDA_ENOMEM:
65 		return (ENOMEM);
66 	case SDA_ETIME:
67 		return (ETIMEDOUT);
68 	case SDA_EWPROTECT:
69 		return (EROFS);
70 	case SDA_ESUSPENDED:
71 	case SDA_ENODEV:
72 		return (ENODEV);
73 	case SDA_EFAULT:
74 	case SDA_ECRC7:
75 	case SDA_EPROTO:
76 	case SDA_ERESET:
77 	case SDA_EIO:
78 	case SDA_ERESID:
79 	default:
80 		return (EIO);
81 	}
82 }
83 
84 void
85 sda_mem_done(sda_cmd_t *cmdp)
86 {
87 	bd_xfer_t	*xfer = sda_cmd_data(cmdp);
88 	int		errno = sda_cmd_errno(cmdp);
89 
90 	bd_xfer_done(xfer, sda_mem_errno(errno));
91 	sda_cmd_free(cmdp);
92 }
93 
94 int
95 sda_mem_rw(sda_slot_t *slot, bd_xfer_t *xfer, uint8_t cmd, uint16_t flags)
96 {
97 	sda_cmd_t	*cmdp;
98 	uint64_t	nblks;
99 	uint64_t	blkno;
100 	uint16_t	rblen;
101 
102 	blkno = xfer->x_blkno;
103 	nblks = xfer->x_nblks;
104 
105 	ASSERT(nblks != 0);
106 
107 	if ((blkno + nblks) > slot->s_nblks) {
108 		return (EINVAL);
109 	}
110 
111 	cmdp = sda_cmd_alloc(slot, cmd, blkno << slot->s_bshift,
112 	    R1, xfer, KM_NOSLEEP);
113 	if (cmdp == NULL) {
114 		return (ENOMEM);
115 	}
116 
117 	if (slot->s_hostp->h_dma != NULL) {
118 		cmdp->sc_dmah = xfer->x_dmah;
119 		cmdp->sc_ndmac = xfer->x_ndmac;
120 		cmdp->sc_dmac = xfer->x_dmac;
121 		cmdp->sc_kvaddr = 0;
122 	} else {
123 		cmdp->sc_ndmac = 0;
124 		cmdp->sc_kvaddr = xfer->x_kaddr;
125 	}
126 
127 	rblen = slot->s_blksz;
128 
129 	/* other fields are set by sda_cmd_alloc */
130 	cmdp->sc_blksz = rblen;
131 	cmdp->sc_nblks = (uint16_t)nblks;
132 	cmdp->sc_flags = flags;
133 
134 	sda_cmd_submit(slot, cmdp, sda_mem_done);
135 	return (0);
136 }
137 
138 int
139 sda_mem_bd_read(void *arg, bd_xfer_t *xfer)
140 {
141 	sda_slot_t	*slot = arg;
142 	uint8_t		cmd;
143 	uint16_t	flags;
144 
145 	if (xfer->x_flags & BD_XFER_POLL) {
146 		return (EIO);
147 	}
148 	if (xfer->x_nblks > 1) {
149 		cmd = CMD_READ_MULTI;
150 		flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_READ |
151 		    SDA_CMDF_AUTO_CMD12;
152 	} else {
153 		cmd = CMD_READ_SINGLE;
154 		flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_READ;
155 	}
156 
157 	return (sda_mem_rw(slot, xfer, cmd, flags));
158 }
159 
160 int
161 sda_mem_bd_write(void *arg, bd_xfer_t *xfer)
162 {
163 	sda_slot_t	*slot = arg;
164 	uint8_t		cmd;
165 	uint16_t	flags;
166 
167 	if (xfer->x_flags & BD_XFER_POLL) {
168 		return (EIO);
169 	}
170 	if ((slot->s_flags & SLOTF_WRITABLE) == 0) {
171 		return (EROFS);
172 	}
173 	if (xfer->x_nblks > 1) {
174 		cmd = CMD_WRITE_MULTI;
175 		flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_WRITE |
176 		    SDA_CMDF_AUTO_CMD12;
177 	} else {
178 		cmd = CMD_WRITE_SINGLE;
179 		flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_WRITE;
180 	}
181 
182 	return (sda_mem_rw(slot, xfer, cmd, flags));
183 }
184 
185 void
186 sda_mem_bd_driveinfo(void *arg, bd_drive_t *drive)
187 {
188 	sda_slot_t	*slot = arg;
189 
190 	drive->d_qsize = 4;	/* we queue up internally, 4 is enough */
191 	drive->d_maxxfer = 65536;
192 	drive->d_removable = B_TRUE;
193 	drive->d_hotpluggable = B_FALSE;
194 	drive->d_target = slot->s_slot_num;
195 }
196 
197 int
198 sda_mem_bd_mediainfo(void *arg, bd_media_t *media)
199 {
200 	sda_slot_t	*slot = arg;
201 
202 	sda_slot_enter(slot);
203 	if (!slot->s_ready) {
204 		sda_slot_exit(slot);
205 		return (ENXIO);
206 	}
207 	media->m_nblks = slot->s_nblks;
208 	media->m_blksize = slot->s_blksz;
209 	media->m_readonly = slot->s_flags & SLOTF_WRITABLE ? B_FALSE : B_TRUE;
210 	sda_slot_exit(slot);
211 	return (0);
212 }
213 
214 uint32_t
215 sda_mem_getbits(uint32_t *resp, int hibit, int len)
216 {
217 	uint32_t	val = 0;
218 	uint32_t	bit;
219 
220 	for (bit = hibit; len--; bit--) {
221 		val <<= 1;
222 		val |= ((resp[bit / 32]) >> (bit % 32)) & 1;
223 	}
224 	return (val);
225 }
226 
227 void
228 sda_mem_getstring(uint32_t *resp, char *s, int hibit, int len)
229 {
230 	while (len--) {
231 		*s++ = sda_mem_getbits(resp, hibit, 8);
232 		hibit -= 8;
233 	}
234 	*s = 0;
235 }
236 
237 uint32_t
238 sda_mem_maxclk(sda_slot_t *slot)
239 {
240 	static const uint32_t	mult[16] = {
241 		0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
242 	};
243 
244 	static const uint32_t	units[8] = {
245 		10000, 100000, 1000000, 10000000, 0, 0, 0, 0,
246 	};
247 	uint8_t			ts;
248 
249 	ts = sda_mem_getbits(slot->s_rcsd, 103, 8);
250 
251 	return ((units[ts & 0x7]) * (mult[(ts >> 3) & 0xf]));
252 }
253 
254 int
255 sda_mem_parse_cid_csd(sda_slot_t *slot)
256 {
257 	uint32_t	*rcid;
258 	uint32_t	*rcsd;
259 	int		csdver;
260 	uint16_t	rblen;
261 	uint16_t	bshift;
262 	uint32_t	cmult;
263 	uint32_t	csize;
264 
265 	rcid = slot->s_rcid;
266 	rcsd = slot->s_rcsd;
267 
268 	csdver = sda_mem_getbits(rcsd, 127, 2);
269 
270 	if (slot->s_flags & SLOTF_SDMEM) {
271 		switch (csdver) {
272 		case 0:
273 			csize = sda_mem_getbits(rcsd, 73, 12);
274 			rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
275 			cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
276 			bshift = 9;
277 			break;
278 		case 1:
279 			rblen = 512;
280 			csize = sda_mem_getbits(rcsd, 69, 22);
281 			cmult = 1024;
282 			bshift = 0;
283 			break;
284 		default:
285 			sda_slot_err(slot, "Unknown SD CSD version (%d)",
286 			    csdver);
287 			return (DDI_FAILURE);
288 		}
289 
290 		slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
291 		sda_mem_getstring(rcid, slot->s_oem, 119, 2);
292 		sda_mem_getstring(rcid, slot->s_prod, 103, 5);
293 		slot->s_majver = sda_mem_getbits(rcid, 63, 4);
294 		slot->s_minver = sda_mem_getbits(rcid, 59, 4);
295 		slot->s_serial =  sda_mem_getbits(rcid, 55, 32);
296 		slot->s_year = sda_mem_getbits(rcid, 19, 8) + 2000;
297 		slot->s_month = sda_mem_getbits(rcid, 11, 4);
298 
299 	} else if (slot->s_flags & SLOTF_MMC) {
300 		if ((csdver < 1) || (csdver > 2)) {
301 			sda_slot_err(slot, "Unknown MMC CSD version (%d)",
302 			    csdver);
303 			return (DDI_FAILURE);
304 		}
305 
306 		switch (sda_mem_getbits(rcsd, 125, 4)) {
307 		case 0:	/* MMC 1.0 - 1.2 */
308 		case 1:	/* MMC 1.4 */
309 			slot->s_mfg = sda_mem_getbits(rcid, 127, 24);
310 			slot->s_oem[0] = 0;
311 			sda_mem_getstring(rcid, slot->s_prod, 103, 7);
312 			slot->s_majver = sda_mem_getbits(rcid, 47, 4);
313 			slot->s_minver = sda_mem_getbits(rcid, 43, 4);
314 			slot->s_serial =  sda_mem_getbits(rcid, 39, 24);
315 			break;
316 
317 		case 2:	/* MMC 2.0 - 2.2 */
318 		case 3:	/* MMC 3.1 - 3.3 */
319 		case 4:	/* MMC 4.x */
320 			slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
321 			sda_mem_getstring(rcid, slot->s_oem, 119, 2);
322 			sda_mem_getstring(rcid, slot->s_prod, 103, 6);
323 			slot->s_majver = sda_mem_getbits(rcid, 55, 4);
324 			slot->s_minver = sda_mem_getbits(rcid, 51, 4);
325 			slot->s_serial =  sda_mem_getbits(rcid, 47, 32);
326 			break;
327 
328 		default:
329 			/* this error isn't fatal to us */
330 			sda_slot_err(slot, "Unknown MMCA version (%d)",
331 			    sda_mem_getbits(rcsd, 125, 4));
332 			break;
333 		}
334 
335 		slot->s_year = sda_mem_getbits(rcid, 11, 4) + 1997;
336 		slot->s_month = sda_mem_getbits(rcid, 15, 4);
337 
338 		csize = sda_mem_getbits(rcsd, 73, 12);
339 		rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
340 		cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
341 		bshift = 9;
342 
343 	} else {
344 
345 		sda_slot_err(slot, "Card type unknown");
346 		return (DDI_FAILURE);
347 	}
348 
349 	/*
350 	 * These fields are common to all known MMC/SDcard memory cards.
351 	 *
352 	 * The spec requires that block size 512 be supported.
353 	 * The media may have a different native size, but 512
354 	 * byte blocks will always work.  This is true for SDcard,
355 	 * and apparently for MMC as well.
356 	 */
357 	rblen = max(rblen, 512);	/* paranoia */
358 	slot->s_nblks = (csize + 1) * cmult * (rblen / 512);
359 	slot->s_bshift = bshift;
360 	slot->s_blksz = 512;
361 
362 	slot->s_r2w = (1 << sda_mem_getbits(rcsd, 28, 3));
363 	slot->s_ccc = sda_mem_getbits(rcsd, 95, 12);
364 	slot->s_perm_wp = sda_mem_getbits(rcsd, 13, 1);
365 	slot->s_temp_wp = sda_mem_getbits(rcsd, 12, 1);
366 	slot->s_dsr = sda_mem_getbits(rcsd, 76, 1);
367 
368 	if (((slot->s_ccc & (1 << 4)) == 0) ||
369 	    (slot->s_perm_wp != 0) || (slot->s_temp_wp != 0)) {
370 		slot->s_flags &= ~SLOTF_WRITABLE;
371 	}
372 
373 	return (DDI_SUCCESS);
374 }
375