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