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
sda_mem_errno(sda_err_t errno)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
sda_mem_done(sda_cmd_t * cmdp)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
sda_mem_rw(sda_slot_t * slot,bd_xfer_t * xfer,uint8_t cmd,uint16_t flags)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
sda_mem_bd_read(void * arg,bd_xfer_t * xfer)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
sda_mem_bd_write(void * arg,bd_xfer_t * xfer)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
sda_mem_bd_driveinfo(void * arg,bd_drive_t * drive)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
sda_mem_bd_mediainfo(void * arg,bd_media_t * media)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
sda_mem_getbits(uint32_t * resp,int hibit,int len)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
sda_mem_getstring(uint32_t * resp,char * s,int hibit,int len)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
sda_mem_maxclk(sda_slot_t * slot)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
sda_mem_parse_cid_csd(sda_slot_t * slot)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