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