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