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 2008 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Memory target support for SDcard.
28  */
29 
30 #include <sys/types.h>
31 #include <sys/types.h>
32 #include <sys/conf.h>
33 #include <sys/scsi/adapters/blk2scsa.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_attach(dev_info_t *, ddi_attach_cmd_t);
40 static int sda_mem_detach(dev_info_t *, ddi_detach_cmd_t);
41 static int sda_mem_quiesce(dev_info_t *);
42 static b2s_err_t sda_mem_b2s_errno(sda_err_t);
43 static boolean_t sda_mem_b2s_request(void *, b2s_request_t *);
44 static boolean_t sda_mem_b2s_rw(sda_slot_t *, b2s_request_t *);
45 static void sda_mem_b2s_done(sda_cmd_t *);
46 static void sda_mem_getstring(uint32_t *, char *, int, int);
47 static int sda_mem_parse_cid_csd(sda_slot_t *, dev_info_t *);
48 static int sda_mem_cmd(sda_slot_t *, uint8_t, uint32_t, uint8_t, uint32_t *);
49 
50 
51 /*
52  * To minimize complexity and reduce layering, we implement almost the
53  * entire memory card driver (sdcard) here.  The memory card still
54  * needs to be a separate driver though, due to the requirement to
55  * have both SCSI HBA bus ops and SD bus ops.
56  */
57 
58 /*
59  * SCSA layer supplies a cb_ops, but we don't want it, because we
60  * don't want to expose a SCSI attachment point.  (Our parent handles
61  * the attachment point, the SCSI one would be confusing.)  We have to
62  * supply a stubbed out one, to prevent SCSA from trying to create minor
63  * nodes on our behalf.
64  *
65  * Perhaps at some future point we might want to expose a separate set
66  * of ioctls for these nodes, but for now we rely on our parent to do
67  * all that work.
68  */
69 static struct cb_ops sda_mem_ops = {
70 	nodev,			/* cb_open */
71 	nodev,			/* cb_close */
72 	nodev,			/* cb_strategy */
73 	nodev,			/* cb_print */
74 	nodev,			/* cb_dump */
75 	nodev,			/* cb_read */
76 	nodev,			/* cb_write */
77 	nodev,			/* cb_ioctl */
78 	nodev,			/* cb_devmap */
79 	nodev,			/* cb_mmap */
80 	nodev,			/* cb_segmap */
81 	nochpoll,		/* cb_chpoll */
82 	ddi_prop_op,		/* cb_prop_op */
83 	NULL,			/* cb_stream */
84 	D_MP			/* cb_flag */
85 };
86 
87 /*
88  * Here are the public functions.
89  */
90 void
91 sda_mem_init(struct modlinkage *modlp)
92 {
93 	struct dev_ops *devo;
94 
95 	devo = ((struct modldrv *)(modlp->ml_linkage[0]))->drv_dev_ops;
96 	devo->devo_attach = sda_mem_attach;
97 	devo->devo_detach = sda_mem_detach;
98 	devo->devo_quiesce = sda_mem_quiesce;
99 
100 	devo->devo_cb_ops = &sda_mem_ops;
101 
102 	/* it turns out that this can't ever really fail */
103 	(void) b2s_mod_init(modlp);
104 }
105 
106 void
107 sda_mem_fini(struct modlinkage *modlp)
108 {
109 	b2s_mod_fini(modlp);
110 }
111 
112 /*
113  * Everything beyond this is private.
114  */
115 
116 int
117 sda_mem_cmd(sda_slot_t *slot, uint8_t cmd, uint32_t arg, uint8_t rtype,
118     uint32_t *resp)
119 {
120 	sda_cmd_t	*cmdp;
121 	int		errno;
122 
123 	cmdp = sda_cmd_alloc(slot, cmd, arg, rtype, NULL, KM_SLEEP);
124 	if (cmdp == NULL) {
125 		return (ENOMEM);
126 	}
127 	errno = sda_cmd_exec(slot, cmdp, resp);
128 	sda_cmd_free(cmdp);
129 
130 	return (errno);
131 }
132 
133 boolean_t
134 sda_mem_b2s_rw(sda_slot_t *slot, b2s_request_t *reqp)
135 {
136 	sda_cmd_t	*cmdp;
137 	uint64_t	nblks;
138 	uint64_t	blkno;
139 	uint16_t	rblen;
140 	int		rv;
141 	uint8_t		index;
142 	uint16_t	flags;
143 
144 	blkno = reqp->br_lba;
145 	nblks = reqp->br_nblks;
146 
147 	switch (reqp->br_cmd) {
148 	case B2S_CMD_READ:
149 		if (nblks > 1) {
150 			index = CMD_READ_MULTI;
151 			flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_READ |
152 			    SDA_CMDF_AUTO_CMD12;
153 		} else {
154 			index = CMD_READ_SINGLE;
155 			flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_READ;
156 		}
157 		break;
158 	case B2S_CMD_WRITE:
159 		if (nblks > 1) {
160 			index = CMD_WRITE_MULTI;
161 			flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_WRITE |
162 			    SDA_CMDF_AUTO_CMD12;
163 		} else {
164 			index = CMD_WRITE_SINGLE;
165 			flags = SDA_CMDF_DAT | SDA_CMDF_MEM | SDA_CMDF_WRITE;
166 		}
167 		break;
168 	default:
169 		ASSERT(0);
170 		break;
171 	}
172 
173 	cmdp = sda_cmd_alloc(slot, index, blkno << slot->s_bshift,
174 	    R1, reqp, KM_NOSLEEP);
175 	if (cmdp == NULL) {
176 		b2s_request_done(reqp, B2S_ENOMEM, 0);
177 		return (B_TRUE);
178 	}
179 
180 	if (slot->s_hostp->h_dma != NULL) {
181 		b2s_request_dma(reqp, &cmdp->sc_ndmac, &cmdp->sc_dmacs);
182 		cmdp->sc_kvaddr = 0;
183 	}
184 	if ((slot->s_caps & SLOT_CAP_NOPIO) == 0) {
185 		size_t	maplen;
186 		b2s_request_mapin(reqp, &cmdp->sc_kvaddr, &maplen);
187 		cmdp->sc_ndmac = 0;
188 	}
189 
190 	if (nblks == 0) {
191 		/*
192 		 * This is not strictly a failure, but no work to do.
193 		 * We have to do it late here because we don't want to
194 		 * by pass the above media readiness checks.
195 		 */
196 		rv = B2S_EOK;
197 		goto failed;
198 	}
199 	if (nblks > 0xffff) {
200 		rv = B2S_EINVAL;
201 		goto failed;
202 	}
203 
204 	rblen = slot->s_blksz;
205 
206 	if ((blkno + nblks) > slot->s_nblks) {
207 		rv = B2S_EBLKADDR;
208 		goto failed;
209 	}
210 
211 	cmdp->sc_rtype = R1;
212 	cmdp->sc_blksz = rblen;
213 	cmdp->sc_nblks = (uint16_t)nblks;
214 	cmdp->sc_index = index;
215 	cmdp->sc_flags = flags;
216 
217 	sda_cmd_submit(slot, cmdp, sda_mem_b2s_done);
218 	return (B_TRUE);
219 
220 failed:
221 	sda_cmd_free(cmdp);
222 	b2s_request_done(reqp, rv, 0);
223 	return (B_TRUE);
224 }
225 
226 boolean_t
227 sda_mem_b2s_format(sda_slot_t *slot, b2s_request_t *reqp)
228 {
229 	sda_cmd_t	*cmdp;
230 	int		rv;
231 
232 
233 	rv = sda_mem_cmd(slot, CMD_ERASE_START, 0, R1, NULL);
234 	if (rv != 0) {
235 		b2s_request_done(reqp, sda_mem_b2s_errno(rv), 0);
236 		return (B_TRUE);
237 	}
238 	rv = sda_mem_cmd(slot, CMD_ERASE_END, slot->s_nblks - 1, R1, NULL);
239 	if (rv != 0) {
240 		b2s_request_done(reqp, sda_mem_b2s_errno(rv), 0);
241 		return (B_TRUE);
242 	}
243 
244 	cmdp = sda_cmd_alloc(slot, CMD_ERASE, 0, R1b, reqp, KM_NOSLEEP);
245 	if (cmdp == NULL) {
246 		b2s_request_done(reqp, B2S_ENOMEM, 0);
247 		return (B_TRUE);
248 	}
249 	cmdp->sc_flags = SDA_CMDF_DAT | SDA_CMDF_MEM;
250 
251 	sda_cmd_submit(slot, cmdp, sda_mem_b2s_done);
252 	return (B_TRUE);
253 }
254 
255 b2s_err_t
256 sda_mem_b2s_errno(sda_err_t errno)
257 {
258 	/* the hot path */
259 	if (errno == SDA_EOK) {
260 		return (B2S_EOK);
261 	}
262 
263 	switch (errno) {
264 	case SDA_ENOMEM:
265 		return (B2S_ENOMEM);
266 	case SDA_ETIME:
267 		return (B2S_ETIMEDOUT);
268 	case SDA_EWPROTECT:
269 		return (B2S_EWPROTECT);
270 	case SDA_ESUSPENDED:
271 	case SDA_ENODEV:
272 		return (B2S_ENOMEDIA);
273 	case SDA_EFAULT:
274 	case SDA_ECRC7:
275 	case SDA_EPROTO:
276 		return (B2S_EHARDWARE);
277 	case SDA_ERESET:
278 		return (B2S_ERESET);
279 	case SDA_EIO:
280 	case SDA_ERESID:
281 	default:
282 		return (B2S_EIO);
283 	}
284 }
285 
286 void
287 sda_mem_b2s_done(sda_cmd_t *cmdp)
288 {
289 	b2s_request_t	*reqp = sda_cmd_data(cmdp);
290 	int		errno = sda_cmd_errno(cmdp);
291 
292 	b2s_request_done(reqp, sda_mem_b2s_errno(errno), cmdp->sc_resid);
293 	sda_cmd_free(cmdp);
294 }
295 
296 boolean_t
297 sda_mem_b2s_request(void *arg, b2s_request_t *reqp)
298 {
299 	sda_slot_t	*slot = arg;
300 	int		rv;
301 
302 	switch (reqp->br_cmd) {
303 	case B2S_CMD_WRITE:
304 		if ((slot->s_flags & SLOTF_WRITABLE) == 0) {
305 			rv = B2S_EWPROTECT;
306 		} else {
307 			return (sda_mem_b2s_rw(slot, reqp));
308 		}
309 		break;
310 
311 	case B2S_CMD_READ:
312 		return (sda_mem_b2s_rw(slot, reqp));
313 
314 	case B2S_CMD_INQUIRY:
315 		reqp->br_inquiry.inq_vendor = "OSOL";
316 		reqp->br_inquiry.inq_product =
317 		    slot->s_flags & SLOTF_MMC ? "MultiMediaCard" :
318 		    slot->s_flags & SLOTF_SDHC ? "SDHC Memory Card" :
319 		    "SD Memory Card";
320 		reqp->br_inquiry.inq_revision = "";
321 		reqp->br_inquiry.inq_serial = "";
322 		rv = B2S_EOK;
323 		break;
324 
325 	case B2S_CMD_GETMEDIA:
326 		if (!slot->s_ready) {
327 			rv = B2S_ENODEV;
328 		} else {
329 			reqp->br_media.media_blksz = slot->s_blksz;
330 			reqp->br_media.media_nblks = slot->s_nblks;
331 			/* detect read-only cards */
332 			if (slot->s_flags & SLOTF_WRITABLE) {
333 				reqp->br_media.media_flags = 0;
334 			} else {
335 				reqp->br_media.media_flags =
336 				    B2S_MEDIA_FLAG_READ_ONLY;
337 			}
338 			rv = B2S_EOK;
339 		}
340 		break;
341 
342 	case B2S_CMD_FORMAT:
343 		return (sda_mem_b2s_format(slot, reqp));
344 
345 	case B2S_CMD_ABORT:
346 		sda_slot_mem_reset(slot, SDA_EABORT);
347 		rv = B2S_EOK;
348 		break;
349 
350 	case B2S_CMD_RESET:
351 		sda_slot_mem_reset(slot, SDA_ERESET);
352 		rv = B2S_EOK;
353 		break;
354 
355 	case B2S_CMD_START:
356 	case B2S_CMD_STOP:
357 	case B2S_CMD_SYNC:
358 		rv = B2S_EOK;
359 		break;
360 
361 	case B2S_CMD_LOCK:
362 	case B2S_CMD_UNLOCK:
363 	default:
364 		rv = B2S_ENOTSUP;
365 		break;
366 	}
367 
368 	b2s_request_done(reqp, rv, 0);
369 	return (B_TRUE);
370 }
371 
372 int
373 sda_mem_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
374 {
375 	sda_slot_t		*slot;
376 	b2s_nexus_t		*nexus;
377 	b2s_nexus_info_t	nexinfo;
378 	b2s_leaf_info_t		leafinfo;
379 
380 	switch (cmd) {
381 	case DDI_ATTACH:
382 		if ((slot = ddi_get_parent_data(dip)) == NULL) {
383 			return (DDI_FAILURE);
384 		}
385 
386 		if (sda_mem_parse_cid_csd(slot, dip) != DDI_SUCCESS) {
387 			return (DDI_FAILURE);
388 		}
389 
390 		nexinfo.nexus_version = B2S_VERSION_0;
391 		nexinfo.nexus_private = slot;
392 		nexinfo.nexus_dip = dip;
393 		nexinfo.nexus_dma_attr = slot->s_hostp->h_dma;
394 		nexinfo.nexus_request = sda_mem_b2s_request;
395 
396 		nexus = b2s_alloc_nexus(&nexinfo);
397 		if (nexus == NULL) {
398 			return (DDI_FAILURE);
399 		}
400 
401 		leafinfo.leaf_target = 0;
402 		leafinfo.leaf_lun = 0;
403 		leafinfo.leaf_flags =
404 		    B2S_LEAF_REMOVABLE | B2S_LEAF_HOTPLUGGABLE;
405 		leafinfo.leaf_unique_id = slot->s_uuid;
406 
407 		slot->s_leaf = b2s_attach_leaf(nexus, &leafinfo);
408 		if (slot->s_leaf == NULL) {
409 			b2s_free_nexus(nexus);
410 			return (DDI_FAILURE);
411 		}
412 
413 		slot->s_nexus = nexus;
414 		if (b2s_attach_nexus(nexus) != DDI_SUCCESS) {
415 			slot->s_nexus = NULL;
416 			b2s_free_nexus(nexus);
417 			return (DDI_FAILURE);
418 		}
419 		slot->s_nexus = nexus;
420 
421 		return (DDI_SUCCESS);
422 
423 
424 	case DDI_RESUME:
425 		return (DDI_SUCCESS);
426 
427 	default:
428 		return (DDI_FAILURE);
429 	}
430 }
431 
432 int
433 sda_mem_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
434 {
435 	sda_slot_t	*slot;
436 	b2s_nexus_t	*nexus;
437 
438 	switch (cmd) {
439 	case DDI_DETACH:
440 		if ((slot = ddi_get_parent_data(dip)) == NULL) {
441 			return (DDI_FAILURE);
442 		}
443 		if ((nexus = slot->s_nexus) == NULL) {
444 			/* nothing to do */
445 			return (DDI_SUCCESS);
446 		}
447 		if (b2s_detach_nexus(nexus) != DDI_SUCCESS) {
448 			return (DDI_FAILURE);
449 		}
450 		slot->s_nexus = NULL;
451 		b2s_free_nexus(nexus);
452 		return (DDI_SUCCESS);
453 
454 	case DDI_SUSPEND:
455 		return (DDI_SUCCESS);
456 
457 	default:
458 		return (DDI_FAILURE);
459 	}
460 }
461 
462 int
463 sda_mem_quiesce(dev_info_t *dip)
464 {
465 	/* no work to do */
466 	return (DDI_SUCCESS);
467 }
468 
469 uint32_t
470 sda_mem_getbits(uint32_t *resp, int hibit, int len)
471 {
472 	uint32_t	val = 0;
473 	uint32_t	bit;
474 
475 	for (bit = hibit; len--; bit--) {
476 		val <<= 1;
477 		val |= ((resp[bit / 32]) >> (bit % 32)) & 1;
478 	}
479 	return (val);
480 }
481 
482 void
483 sda_mem_getstring(uint32_t *resp, char *s, int hibit, int len)
484 {
485 	while (len--) {
486 		*s++ = sda_mem_getbits(resp, hibit, 8);
487 		hibit -= 8;
488 	}
489 	*s = 0;
490 }
491 
492 uint32_t
493 sda_mem_maxclk(sda_slot_t *slot)
494 {
495 	static const uint32_t	mult[16] = {
496 		0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
497 	};
498 
499 	static const uint32_t	units[8] = {
500 		10000, 100000, 1000000, 10000000, 0, 0, 0, 0,
501 	};
502 	uint8_t			ts;
503 
504 	ts = sda_mem_getbits(slot->s_rcsd, 103, 8);
505 
506 	return ((units[ts & 0x7]) * (mult[(ts >> 3) & 0xf]));
507 }
508 
509 int
510 sda_mem_parse_cid_csd(sda_slot_t *slot, dev_info_t *dip)
511 {
512 	uint32_t	*rcid;
513 	uint32_t	*rcsd;
514 	int		csdver;
515 	uint16_t	rblen;
516 	uint16_t	bshift;
517 	uint32_t	cmult;
518 	uint32_t	csize;
519 	char		date[16];
520 	char		*dtype;
521 
522 	rcid = slot->s_rcid;
523 	rcsd = slot->s_rcsd;
524 
525 	csdver = sda_mem_getbits(rcsd, 127, 2);
526 
527 	if (slot->s_flags & SLOTF_SDMEM) {
528 		switch (csdver) {
529 		case 0:
530 			csize = sda_mem_getbits(rcsd, 73, 12);
531 			/* see comment above */
532 			rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
533 			cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
534 			bshift = 9;
535 			break;
536 		case 1:
537 			rblen = 512;
538 			csize = sda_mem_getbits(rcsd, 69, 22);
539 			cmult = 1024;
540 			bshift = 0;
541 			break;
542 		default:
543 			sda_slot_err(slot, "Unknown SD CSD version (%d)",
544 			    csdver);
545 			return (DDI_FAILURE);
546 		}
547 
548 		dtype = slot->s_flags & SLOTF_SDHC ? "sdhc" : "sdcard";
549 		slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
550 		sda_mem_getstring(rcid, slot->s_oem, 119, 2);
551 		sda_mem_getstring(rcid, slot->s_prod, 103, 5);
552 		slot->s_majver = sda_mem_getbits(rcid, 63, 4);
553 		slot->s_minver = sda_mem_getbits(rcid, 59, 4);
554 		slot->s_serial =  sda_mem_getbits(rcid, 55, 32);
555 		slot->s_year = sda_mem_getbits(rcid, 19, 8) + 2000;
556 		slot->s_month = sda_mem_getbits(rcid, 11, 4);
557 
558 	} else if (slot->s_flags & SLOTF_MMC) {
559 		if ((csdver < 1) || (csdver > 2)) {
560 			sda_slot_err(slot, "Unknown MMC CSD version (%d)",
561 			    csdver);
562 			return (DDI_FAILURE);
563 		}
564 
565 		dtype = "mmc";
566 
567 		switch (sda_mem_getbits(rcsd, 125, 4)) {
568 		case 0:	/* MMC 1.0 - 1.2 */
569 		case 1:	/* MMC 1.4 */
570 			slot->s_mfg = sda_mem_getbits(rcid, 127, 24);
571 			slot->s_oem[0] = 0;
572 			sda_mem_getstring(rcid, slot->s_prod, 103, 7);
573 			slot->s_majver = sda_mem_getbits(rcid, 47, 4);
574 			slot->s_minver = sda_mem_getbits(rcid, 43, 4);
575 			slot->s_serial =  sda_mem_getbits(rcid, 39, 24);
576 			break;
577 
578 		case 2:	/* MMC 2.0 - 2.2 */
579 		case 3:	/* MMC 3.1 - 3.3 */
580 		case 4:	/* MMC 4.x */
581 			slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
582 			sda_mem_getstring(rcid, slot->s_oem, 119, 2);
583 			sda_mem_getstring(rcid, slot->s_prod, 103, 6);
584 			slot->s_majver = sda_mem_getbits(rcid, 55, 4);
585 			slot->s_minver = sda_mem_getbits(rcid, 51, 4);
586 			slot->s_serial =  sda_mem_getbits(rcid, 47, 32);
587 			break;
588 
589 		default:
590 			/* this error isn't fatal to us */
591 			sda_slot_err(slot, "Unknown MMCA version (%d)",
592 			    sda_mem_getbits(rcsd, 125, 4));
593 			break;
594 		}
595 
596 		slot->s_year = sda_mem_getbits(rcid, 11, 4) + 1997;
597 		slot->s_month = sda_mem_getbits(rcid, 15, 4);
598 
599 		csize = sda_mem_getbits(rcsd, 73, 12);
600 		rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
601 		cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
602 		bshift = 9;
603 
604 	} else {
605 
606 		sda_slot_err(slot, "Card type unknown");
607 		return (DDI_FAILURE);
608 	}
609 
610 	/*
611 	 * These fields are common to all known MMC/SDcard memory cards.
612 	 *
613 	 * The spec requires that block size 512 be supported.
614 	 * The media may have a different native size, but 512
615 	 * byte blocks will always work.  This is true for SDcard,
616 	 * and apparently for MMC as well.
617 	 */
618 	rblen = max(rblen, 512);	/* paranoia */
619 	slot->s_nblks = (csize + 1) * cmult * (rblen / 512);
620 	slot->s_bshift = bshift;
621 	slot->s_blksz = 512;
622 
623 	slot->s_r2w = (1 << sda_mem_getbits(rcsd, 28, 3));
624 	slot->s_ccc = sda_mem_getbits(rcsd, 95, 12);
625 	slot->s_perm_wp = sda_mem_getbits(rcsd, 13, 1);
626 	slot->s_temp_wp = sda_mem_getbits(rcsd, 12, 1);
627 	slot->s_dsr = sda_mem_getbits(rcsd, 76, 1);
628 
629 	if (((slot->s_ccc & (1 << 4)) == 0) ||
630 	    (slot->s_perm_wp != 0) || (slot->s_temp_wp != 0)) {
631 		slot->s_flags &= ~SLOTF_WRITABLE;
632 	}
633 	(void) snprintf(date, sizeof (date), "%02d-%04d",
634 	    slot->s_month, slot->s_year);
635 
636 #define	prop_set_int(name, val)		\
637 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, name, val)
638 #define	prop_set_str(name, val)		\
639 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, name, val)
640 #define	prop_set_bool(name, val)	\
641 	if (val) (void) ddi_prop_create(DDI_DEV_T_NONE, dip, 0, name, NULL, 0)
642 
643 	prop_set_str("device-type", dtype);
644 	prop_set_int("mfg-id", slot->s_mfg);
645 	prop_set_str("product-id", slot->s_prod);
646 	prop_set_str("oem-id", slot->s_oem);
647 	prop_set_str("mfg-date", date);
648 
649 	prop_set_int("block-size", slot->s_blksz);
650 	prop_set_int("num-blocks", slot->s_nblks);
651 	prop_set_int("max-freq", slot->s_maxclk);
652 	prop_set_bool("dsr-implemented", slot->s_dsr);
653 	prop_set_int("ccc", slot->s_ccc);
654 	prop_set_bool("perm-wp", slot->s_perm_wp);
655 	prop_set_bool("temp-wp", slot->s_temp_wp);
656 
657 #undef	prop_set_int
658 #undef	prop_set_str
659 #undef	prop_set_bool
660 
661 	return (DDI_SUCCESS);
662 }
663