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 2009 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/note.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 	} else {
184 		cmdp->sc_ndmac = 0;
185 	}
186 	if ((slot->s_caps & SLOT_CAP_NOPIO) == 0) {
187 		size_t	maplen;
188 		b2s_request_mapin(reqp, &cmdp->sc_kvaddr, &maplen);
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 		leafinfo.leaf_eui = 0;
408 
409 		slot->s_leaf = b2s_attach_leaf(nexus, &leafinfo);
410 		if (slot->s_leaf == NULL) {
411 			b2s_free_nexus(nexus);
412 			return (DDI_FAILURE);
413 		}
414 
415 		slot->s_nexus = nexus;
416 		if (b2s_attach_nexus(nexus) != DDI_SUCCESS) {
417 			slot->s_nexus = NULL;
418 			b2s_free_nexus(nexus);
419 			return (DDI_FAILURE);
420 		}
421 		slot->s_nexus = nexus;
422 
423 		return (DDI_SUCCESS);
424 
425 
426 	case DDI_RESUME:
427 		return (DDI_SUCCESS);
428 
429 	default:
430 		return (DDI_FAILURE);
431 	}
432 }
433 
434 int
435 sda_mem_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
436 {
437 	sda_slot_t	*slot;
438 	b2s_nexus_t	*nexus;
439 
440 	switch (cmd) {
441 	case DDI_DETACH:
442 		if ((slot = ddi_get_parent_data(dip)) == NULL) {
443 			return (DDI_FAILURE);
444 		}
445 		if ((nexus = slot->s_nexus) == NULL) {
446 			/* nothing to do */
447 			return (DDI_SUCCESS);
448 		}
449 		if (b2s_detach_nexus(nexus) != DDI_SUCCESS) {
450 			return (DDI_FAILURE);
451 		}
452 		slot->s_nexus = NULL;
453 		b2s_free_nexus(nexus);
454 		return (DDI_SUCCESS);
455 
456 	case DDI_SUSPEND:
457 		return (DDI_SUCCESS);
458 
459 	default:
460 		return (DDI_FAILURE);
461 	}
462 }
463 
464 int
465 sda_mem_quiesce(dev_info_t *dip)
466 {
467 	_NOTE(ARGUNUSED(dip));
468 	/* no work to do */
469 	return (DDI_SUCCESS);
470 }
471 
472 uint32_t
473 sda_mem_getbits(uint32_t *resp, int hibit, int len)
474 {
475 	uint32_t	val = 0;
476 	uint32_t	bit;
477 
478 	for (bit = hibit; len--; bit--) {
479 		val <<= 1;
480 		val |= ((resp[bit / 32]) >> (bit % 32)) & 1;
481 	}
482 	return (val);
483 }
484 
485 void
486 sda_mem_getstring(uint32_t *resp, char *s, int hibit, int len)
487 {
488 	while (len--) {
489 		*s++ = sda_mem_getbits(resp, hibit, 8);
490 		hibit -= 8;
491 	}
492 	*s = 0;
493 }
494 
495 uint32_t
496 sda_mem_maxclk(sda_slot_t *slot)
497 {
498 	static const uint32_t	mult[16] = {
499 		0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80
500 	};
501 
502 	static const uint32_t	units[8] = {
503 		10000, 100000, 1000000, 10000000, 0, 0, 0, 0,
504 	};
505 	uint8_t			ts;
506 
507 	ts = sda_mem_getbits(slot->s_rcsd, 103, 8);
508 
509 	return ((units[ts & 0x7]) * (mult[(ts >> 3) & 0xf]));
510 }
511 
512 int
513 sda_mem_parse_cid_csd(sda_slot_t *slot, dev_info_t *dip)
514 {
515 	uint32_t	*rcid;
516 	uint32_t	*rcsd;
517 	int		csdver;
518 	uint16_t	rblen;
519 	uint16_t	bshift;
520 	uint32_t	cmult;
521 	uint32_t	csize;
522 	char		date[16];
523 	char		*dtype;
524 
525 	rcid = slot->s_rcid;
526 	rcsd = slot->s_rcsd;
527 
528 	csdver = sda_mem_getbits(rcsd, 127, 2);
529 
530 	if (slot->s_flags & SLOTF_SDMEM) {
531 		switch (csdver) {
532 		case 0:
533 			csize = sda_mem_getbits(rcsd, 73, 12);
534 			/* see comment above */
535 			rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
536 			cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
537 			bshift = 9;
538 			break;
539 		case 1:
540 			rblen = 512;
541 			csize = sda_mem_getbits(rcsd, 69, 22);
542 			cmult = 1024;
543 			bshift = 0;
544 			break;
545 		default:
546 			sda_slot_err(slot, "Unknown SD CSD version (%d)",
547 			    csdver);
548 			return (DDI_FAILURE);
549 		}
550 
551 		dtype = slot->s_flags & SLOTF_SDHC ? "sdhc" : "sdcard";
552 		slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
553 		sda_mem_getstring(rcid, slot->s_oem, 119, 2);
554 		sda_mem_getstring(rcid, slot->s_prod, 103, 5);
555 		slot->s_majver = sda_mem_getbits(rcid, 63, 4);
556 		slot->s_minver = sda_mem_getbits(rcid, 59, 4);
557 		slot->s_serial =  sda_mem_getbits(rcid, 55, 32);
558 		slot->s_year = sda_mem_getbits(rcid, 19, 8) + 2000;
559 		slot->s_month = sda_mem_getbits(rcid, 11, 4);
560 
561 	} else if (slot->s_flags & SLOTF_MMC) {
562 		if ((csdver < 1) || (csdver > 2)) {
563 			sda_slot_err(slot, "Unknown MMC CSD version (%d)",
564 			    csdver);
565 			return (DDI_FAILURE);
566 		}
567 
568 		dtype = "mmc";
569 
570 		switch (sda_mem_getbits(rcsd, 125, 4)) {
571 		case 0:	/* MMC 1.0 - 1.2 */
572 		case 1:	/* MMC 1.4 */
573 			slot->s_mfg = sda_mem_getbits(rcid, 127, 24);
574 			slot->s_oem[0] = 0;
575 			sda_mem_getstring(rcid, slot->s_prod, 103, 7);
576 			slot->s_majver = sda_mem_getbits(rcid, 47, 4);
577 			slot->s_minver = sda_mem_getbits(rcid, 43, 4);
578 			slot->s_serial =  sda_mem_getbits(rcid, 39, 24);
579 			break;
580 
581 		case 2:	/* MMC 2.0 - 2.2 */
582 		case 3:	/* MMC 3.1 - 3.3 */
583 		case 4:	/* MMC 4.x */
584 			slot->s_mfg = sda_mem_getbits(rcid, 127, 8);
585 			sda_mem_getstring(rcid, slot->s_oem, 119, 2);
586 			sda_mem_getstring(rcid, slot->s_prod, 103, 6);
587 			slot->s_majver = sda_mem_getbits(rcid, 55, 4);
588 			slot->s_minver = sda_mem_getbits(rcid, 51, 4);
589 			slot->s_serial =  sda_mem_getbits(rcid, 47, 32);
590 			break;
591 
592 		default:
593 			/* this error isn't fatal to us */
594 			sda_slot_err(slot, "Unknown MMCA version (%d)",
595 			    sda_mem_getbits(rcsd, 125, 4));
596 			break;
597 		}
598 
599 		slot->s_year = sda_mem_getbits(rcid, 11, 4) + 1997;
600 		slot->s_month = sda_mem_getbits(rcid, 15, 4);
601 
602 		csize = sda_mem_getbits(rcsd, 73, 12);
603 		rblen = (1 << sda_mem_getbits(rcsd, 83, 4));
604 		cmult = (4 << sda_mem_getbits(rcsd, 49, 3));
605 		bshift = 9;
606 
607 	} else {
608 
609 		sda_slot_err(slot, "Card type unknown");
610 		return (DDI_FAILURE);
611 	}
612 
613 	/*
614 	 * These fields are common to all known MMC/SDcard memory cards.
615 	 *
616 	 * The spec requires that block size 512 be supported.
617 	 * The media may have a different native size, but 512
618 	 * byte blocks will always work.  This is true for SDcard,
619 	 * and apparently for MMC as well.
620 	 */
621 	rblen = max(rblen, 512);	/* paranoia */
622 	slot->s_nblks = (csize + 1) * cmult * (rblen / 512);
623 	slot->s_bshift = bshift;
624 	slot->s_blksz = 512;
625 
626 	slot->s_r2w = (1 << sda_mem_getbits(rcsd, 28, 3));
627 	slot->s_ccc = sda_mem_getbits(rcsd, 95, 12);
628 	slot->s_perm_wp = sda_mem_getbits(rcsd, 13, 1);
629 	slot->s_temp_wp = sda_mem_getbits(rcsd, 12, 1);
630 	slot->s_dsr = sda_mem_getbits(rcsd, 76, 1);
631 
632 	if (((slot->s_ccc & (1 << 4)) == 0) ||
633 	    (slot->s_perm_wp != 0) || (slot->s_temp_wp != 0)) {
634 		slot->s_flags &= ~SLOTF_WRITABLE;
635 	}
636 	(void) snprintf(date, sizeof (date), "%02d-%04d",
637 	    slot->s_month, slot->s_year);
638 
639 #define	prop_set_int(name, val)		\
640 	(void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, name, val)
641 #define	prop_set_str(name, val)		\
642 	(void) ddi_prop_update_string(DDI_DEV_T_NONE, dip, name, val)
643 #define	prop_set_bool(name, val)	\
644 	if (val) (void) ddi_prop_create(DDI_DEV_T_NONE, dip, 0, name, NULL, 0)
645 
646 	prop_set_str("device-type", dtype);
647 	prop_set_int("mfg-id", slot->s_mfg);
648 	prop_set_str("product-id", slot->s_prod);
649 	prop_set_str("oem-id", slot->s_oem);
650 	prop_set_str("mfg-date", date);
651 
652 	prop_set_int("block-size", slot->s_blksz);
653 	prop_set_int("num-blocks", slot->s_nblks);
654 	prop_set_int("max-freq", slot->s_maxclk);
655 	prop_set_bool("dsr-implemented", slot->s_dsr);
656 	prop_set_int("ccc", slot->s_ccc);
657 	prop_set_bool("perm-wp", slot->s_perm_wp);
658 	prop_set_bool("temp-wp", slot->s_temp_wp);
659 
660 #undef	prop_set_int
661 #undef	prop_set_str
662 #undef	prop_set_bool
663 
664 	return (DDI_SUCCESS);
665 }
666