xref: /illumos-gate/usr/src/uts/sun4u/io/i2c/clients/max1617.c (revision 8b80e8cb6855118d46f605e91b5ed4ce83417395)
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 /*
28  * The max1617 I2C is a temp acquisition device.  As implemented on some
29  * processor modules, it contains both a local and a remote temp.  The
30  * local temp measures the ambient (room) temperature, while the remote
31  * sensor is connected to the processor die.  There are ioctl's for retrieving
32  * temperatures, and setting temperature alarm ranges.
33  */
34 
35 #include <sys/stat.h>
36 #include <sys/modctl.h>
37 #include <sys/open.h>
38 #include <sys/types.h>
39 #include <sys/kmem.h>
40 #include <sys/ddi.h>
41 #include <sys/sunddi.h>
42 #include <sys/conf.h>
43 #include <sys/file.h>
44 #include <sys/note.h>
45 
46 #include <sys/i2c/misc/i2c_svc.h>
47 #include <sys/i2c/clients/i2c_client.h>
48 #include <sys/i2c/clients/max1617.h>
49 #include <sys/i2c/clients/max1617_impl.h>
50 
51 /*
52  * cb ops (only need ioctl)
53  */
54 static int max1617_open(dev_t *, int, int, cred_t *);
55 static int max1617_close(dev_t, int, int, cred_t *);
56 static int max1617_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
57 
58 /*
59  * dev ops
60  */
61 static int max1617_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
62 		void **result);
63 static int max1617_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
64 static int max1617_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
65 
66 static struct cb_ops max1617_cbops = {
67 	max1617_open,			/* open */
68 	max1617_close,			/* close */
69 	nodev,				/* strategy */
70 	nodev,				/* print */
71 	nodev,				/* dump */
72 	nodev,				/* read */
73 	nodev,				/* write */
74 	max1617_ioctl,			/* ioctl */
75 	nodev,				/* devmap */
76 	nodev,				/* mmap */
77 	nodev,				/* segmap */
78 	nochpoll,			/* poll */
79 	ddi_prop_op,			/* cb_prop_op */
80 	NULL,				/* streamtab */
81 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
82 	CB_REV,				/* rev */
83 	nodev,				/* int (*cb_aread)() */
84 	nodev				/* int (*cb_awrite)() */
85 };
86 
87 static struct dev_ops max1617_ops = {
88 	DEVO_REV,
89 	0,
90 	max1617_info,
91 	nulldev,
92 	nulldev,
93 	max1617_attach,
94 	max1617_detach,
95 	nodev,
96 	&max1617_cbops,
97 	NULL,
98 	NULL,
99 	ddi_quiesce_not_supported,	/* devo_quiesce */
100 };
101 
102 static struct modldrv max1617_modldrv = {
103 	&mod_driverops,		/* type of module - driver */
104 	"max1617 device driver",
105 	&max1617_ops,
106 };
107 
108 static struct modlinkage max1617_modlinkage = {
109 	MODREV_1,
110 	&max1617_modldrv,
111 	0
112 };
113 
114 static int max1617_debug = 0;
115 
116 static void *max1617_soft_statep;
117 
118 int
119 _init(void)
120 {
121 	int error;
122 
123 	error = mod_install(&max1617_modlinkage);
124 	if (error == 0) {
125 		(void) ddi_soft_state_init(&max1617_soft_statep,
126 		    sizeof (struct max1617_unit), 1);
127 	}
128 
129 	return (error);
130 }
131 
132 int
133 _fini(void)
134 {
135 	int error;
136 
137 	error = mod_remove(&max1617_modlinkage);
138 	if (error == 0) {
139 		ddi_soft_state_fini(&max1617_soft_statep);
140 	}
141 
142 	return (error);
143 }
144 
145 int
146 _info(struct modinfo *modinfop)
147 {
148 	return (mod_info(&max1617_modlinkage, modinfop));
149 }
150 
151 /* ARGSUSED */
152 static int
153 max1617_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
154 {
155 	dev_t	dev;
156 	int	instance;
157 
158 	if (infocmd == DDI_INFO_DEVT2INSTANCE) {
159 		dev = (dev_t)arg;
160 		instance = MAX1617_MINOR_TO_INST(getminor(dev));
161 		*result = (void *)(uintptr_t)instance;
162 		return (DDI_SUCCESS);
163 	}
164 	return (DDI_FAILURE);
165 }
166 
167 static int
168 max1617_do_attach(dev_info_t *dip)
169 {
170 	struct max1617_unit *unitp;
171 	int instance;
172 	char minor_name[MAXNAMELEN];
173 	minor_t minor_number;
174 
175 	instance = ddi_get_instance(dip);
176 
177 	if (ddi_soft_state_zalloc(max1617_soft_statep, instance) != 0) {
178 		cmn_err(CE_WARN, "%s%d: failed to zalloc softstate",
179 		    ddi_get_name(dip), instance);
180 
181 		return (DDI_FAILURE);
182 	}
183 
184 	unitp = ddi_get_soft_state(max1617_soft_statep, instance);
185 
186 	(void) snprintf(unitp->max1617_name, sizeof (unitp->max1617_name),
187 	    "%s%d", ddi_node_name(dip), instance);
188 
189 	(void) sprintf(minor_name, "die_temp");
190 	minor_number = MAX1617_INST_TO_MINOR(instance) |
191 	    MAX1617_FCN_TO_MINOR(MAX1617_CPU_TEMP);
192 
193 	if (ddi_create_minor_node(dip, minor_name, S_IFCHR,
194 	    minor_number, MAX1617_NODE_TYPE, NULL) == DDI_FAILURE) {
195 		cmn_err(CE_WARN, "%s ddi_create_minor_node failed for minor "
196 		    " name '%s'", unitp->max1617_name, minor_name);
197 			ddi_soft_state_free(max1617_soft_statep, instance);
198 
199 		return (DDI_FAILURE);
200 	}
201 
202 	(void) sprintf(minor_name, "amb_temp");
203 	minor_number = MAX1617_INST_TO_MINOR(instance) |
204 	    MAX1617_FCN_TO_MINOR(MAX1617_AMB_TEMP);
205 
206 	if (ddi_create_minor_node(dip, minor_name, S_IFCHR,
207 	    minor_number, MAX1617_NODE_TYPE, NULL) == DDI_FAILURE) {
208 		cmn_err(CE_WARN, "%s ddi_create_minor_node failed for %s",
209 		    unitp->max1617_name, minor_name);
210 		ddi_remove_minor_node(dip, NULL);
211 		ddi_soft_state_free(max1617_soft_statep, instance);
212 
213 		return (DDI_FAILURE);
214 	}
215 
216 	if (i2c_client_register(dip, &unitp->max1617_hdl) != I2C_SUCCESS) {
217 		ddi_remove_minor_node(dip, NULL);
218 		ddi_soft_state_free(max1617_soft_statep, instance);
219 
220 		return (DDI_FAILURE);
221 	}
222 
223 	mutex_init(&unitp->max1617_mutex, NULL, MUTEX_DRIVER, NULL);
224 	cv_init(&unitp->max1617_cv, NULL, CV_DRIVER, NULL);
225 
226 	return (DDI_SUCCESS);
227 }
228 
229 static int
230 max1617_do_resume(dev_info_t *dip)
231 {
232 	int ret = DDI_SUCCESS;
233 	int instance = ddi_get_instance(dip);
234 	i2c_transfer_t *i2ctp;
235 	struct max1617_unit *unitp;
236 
237 	if ((unitp = ddi_get_soft_state(max1617_soft_statep, instance)) ==
238 	    NULL) {
239 		return (DDI_FAILURE);
240 	}
241 
242 	(void) i2c_transfer_alloc(unitp->max1617_hdl,
243 	    &i2ctp, 2, 0, I2C_SLEEP);
244 	i2ctp->i2c_version = I2C_XFER_REV;
245 	i2ctp->i2c_flags = I2C_WR;
246 
247 
248 	i2ctp->i2c_wbuf[0] = MAX1617_CONFIG_WR_REG;
249 	i2ctp->i2c_wbuf[1] = unitp->max1617_cpr_state.max1617_config;
250 
251 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
252 		ret = DDI_FAILURE;
253 		goto done;
254 	}
255 
256 	i2ctp->i2c_wbuf[0] = MAX1617_CONV_RATE_WR_REG;
257 	i2ctp->i2c_wbuf[1] = unitp->max1617_cpr_state.max1617_conv_rate;
258 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
259 		ret = DDI_FAILURE;
260 		goto done;
261 	}
262 
263 	i2ctp->i2c_wbuf[0] = MAX1617_LOCALTEMP_HIGH_WR_REG;
264 	i2ctp->i2c_wbuf[1] =  unitp->max1617_cpr_state.max1617_lcl_hlimit;
265 
266 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
267 		ret = DDI_FAILURE;
268 		goto done;
269 	}
270 
271 	i2ctp->i2c_wbuf[0] = MAX1617_REMOTETEMP_HIGH_WR_REG;
272 	i2ctp->i2c_wbuf[1] =  unitp->max1617_cpr_state.max1617_remote_hlimit;
273 
274 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
275 		ret = DDI_FAILURE;
276 		goto done;
277 	}
278 
279 	i2ctp->i2c_wbuf[0] = MAX1617_LOCALTEMP_LOW_REG;
280 	i2ctp->i2c_wbuf[1] = unitp->max1617_cpr_state.max1617_lcl_llimit;
281 
282 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
283 		ret = DDI_FAILURE;
284 		goto done;
285 	}
286 
287 	i2ctp->i2c_wbuf[0] = MAX1617_REMOTETEMP_LOW_REG;
288 	i2ctp->i2c_wbuf[1] = unitp->max1617_cpr_state.max1617_remote_llimit;
289 
290 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
291 		ret = DDI_FAILURE;
292 		goto done;
293 	}
294 
295 	done:
296 	mutex_enter(&unitp->max1617_mutex);
297 	unitp->max1617_flags = 0;
298 	cv_signal(&unitp->max1617_cv);
299 	mutex_exit(&unitp->max1617_mutex);
300 
301 	i2c_transfer_free(unitp->max1617_hdl, i2ctp);
302 	return (ret);
303 }
304 
305 static int
306 max1617_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
307 {
308 	switch (cmd) {
309 	case DDI_ATTACH:
310 
311 		return (max1617_do_attach(dip));
312 	case DDI_RESUME:
313 
314 		return (max1617_do_resume(dip));
315 	default:
316 
317 		return (DDI_FAILURE);
318 	}
319 }
320 
321 static int
322 max1617_do_detach(dev_info_t *dip)
323 {
324 	struct max1617_unit *unitp;
325 	int instance;
326 
327 	instance = ddi_get_instance(dip);
328 
329 	unitp = ddi_get_soft_state(max1617_soft_statep, instance);
330 
331 	if (unitp == NULL) {
332 		return (DDI_FAILURE);
333 	}
334 
335 	i2c_client_unregister(unitp->max1617_hdl);
336 
337 	ddi_remove_minor_node(dip, NULL);
338 
339 	mutex_destroy(&unitp->max1617_mutex);
340 	cv_destroy(&unitp->max1617_cv);
341 	ddi_soft_state_free(max1617_soft_statep, instance);
342 
343 	return (DDI_SUCCESS);
344 }
345 
346 static int
347 max1617_do_suspend(dev_info_t *dip)
348 {
349 	int ret = DDI_SUCCESS;
350 	int instance = ddi_get_instance(dip);
351 	i2c_transfer_t *i2ctp;
352 	struct max1617_unit *unitp;
353 
354 	if ((unitp = ddi_get_soft_state(max1617_soft_statep, instance)) ==
355 	    NULL) {
356 		return (DDI_FAILURE);
357 	}
358 
359 	(void) i2c_transfer_alloc(unitp->max1617_hdl,
360 	    &i2ctp, 1, 1, I2C_SLEEP);
361 
362 
363 	/*
364 	 * Block new transactions during CPR
365 	 */
366 	mutex_enter(&unitp->max1617_mutex);
367 	while (unitp->max1617_flags == MAX1617_BUSY) {
368 		cv_wait(&unitp->max1617_cv, &unitp->max1617_mutex);
369 	}
370 	unitp->max1617_flags = MAX1617_BUSY;
371 	mutex_exit(&unitp->max1617_mutex);
372 
373 	i2ctp->i2c_version = I2C_XFER_REV;
374 	i2ctp->i2c_flags = I2C_WR_RD;
375 	i2ctp->i2c_wbuf[0] = MAX1617_CONFIG_REG;
376 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
377 		ret = DDI_FAILURE;
378 		goto done;
379 	}
380 	unitp->max1617_cpr_state.max1617_config = i2ctp->i2c_rbuf[0];
381 
382 	i2ctp->i2c_wbuf[0] = MAX1617_CONV_RATE_REG;
383 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
384 		ret = DDI_FAILURE;
385 		goto done;
386 	}
387 	unitp->max1617_cpr_state.max1617_conv_rate = i2ctp->i2c_rbuf[0];
388 
389 	i2ctp->i2c_wbuf[0] = MAX1617_LOCALTEMP_HIGH_REG;
390 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
391 		ret = DDI_FAILURE;
392 		goto done;
393 	}
394 	unitp->max1617_cpr_state.max1617_lcl_hlimit = i2ctp->i2c_rbuf[0];
395 
396 	i2ctp->i2c_wbuf[0] = MAX1617_REMOTETEMP_HIGH_REG;
397 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
398 		ret = DDI_FAILURE;
399 		goto done;
400 	}
401 	unitp->max1617_cpr_state.max1617_remote_hlimit = i2ctp->i2c_rbuf[0];
402 
403 	i2ctp->i2c_wbuf[0] = MAX1617_LOCALTEMP_LOW_REG;
404 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
405 		ret = DDI_FAILURE;
406 		goto done;
407 	}
408 	unitp->max1617_cpr_state.max1617_lcl_llimit = i2ctp->i2c_rbuf[0];
409 
410 	i2ctp->i2c_wbuf[0] = MAX1617_REMOTETEMP_LOW_REG;
411 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
412 		ret = DDI_FAILURE;
413 		goto done;
414 	}
415 	unitp->max1617_cpr_state.max1617_remote_llimit = i2ctp->i2c_rbuf[0];
416 
417 	done:
418 	i2c_transfer_free(unitp->max1617_hdl, i2ctp);
419 
420 	if (ret == DDI_FAILURE) {
421 		mutex_enter(&unitp->max1617_mutex);
422 		unitp->max1617_flags = 0;
423 		cv_broadcast(&unitp->max1617_cv);
424 		mutex_exit(&unitp->max1617_mutex);
425 	}
426 	return (ret);
427 }
428 
429 static int
430 max1617_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
431 {
432 	switch (cmd) {
433 	case DDI_DETACH:
434 
435 		return (max1617_do_detach(dip));
436 	case DDI_SUSPEND:
437 
438 		return (max1617_do_suspend(dip));
439 
440 	default:
441 
442 		return (DDI_FAILURE);
443 	}
444 }
445 
446 static int
447 max1617_open(dev_t *devp, int flags, int otyp, cred_t *credp)
448 {
449 	_NOTE(ARGUNUSED(credp))
450 
451 	struct max1617_unit *unitp;
452 	int instance;
453 	int err = 0;
454 
455 	instance = MAX1617_MINOR_TO_INST(getminor(*devp));
456 
457 	if (instance < 0) {
458 
459 		return (ENXIO);
460 	}
461 
462 	unitp = (struct max1617_unit *)
463 	    ddi_get_soft_state(max1617_soft_statep, instance);
464 
465 	if (unitp == NULL) {
466 
467 		return (ENXIO);
468 	}
469 
470 	if (otyp != OTYP_CHR) {
471 
472 		return (EINVAL);
473 	}
474 
475 	mutex_enter(&unitp->max1617_mutex);
476 
477 	if (flags & FEXCL) {
478 		if (unitp->max1617_oflag != 0) {
479 			err = EBUSY;
480 		} else {
481 			unitp->max1617_oflag = FEXCL;
482 		}
483 	} else {
484 		if (unitp->max1617_oflag == FEXCL) {
485 			err = EBUSY;
486 		} else {
487 			unitp->max1617_oflag = (uint16_t)FOPEN;
488 		}
489 	}
490 
491 done:
492 	mutex_exit(&unitp->max1617_mutex);
493 
494 	return (err);
495 }
496 
497 static int
498 max1617_close(dev_t dev, int flags, int otyp, cred_t *credp)
499 {
500 	_NOTE(ARGUNUSED(flags, otyp, credp))
501 
502 	struct max1617_unit *unitp;
503 	int instance = MAX1617_MINOR_TO_INST(getminor(dev));
504 
505 	if (instance < 0) {
506 
507 		return (ENXIO);
508 	}
509 
510 	unitp = (struct max1617_unit *)
511 	    ddi_get_soft_state(max1617_soft_statep, instance);
512 
513 	if (unitp == NULL) {
514 
515 		return (ENXIO);
516 	}
517 
518 	mutex_enter(&unitp->max1617_mutex);
519 
520 	unitp->max1617_oflag = 0;
521 
522 	mutex_exit(&unitp->max1617_mutex);
523 
524 	return (DDI_SUCCESS);
525 }
526 
527 int
528 set_temp_limit(struct max1617_unit *unitp,
529 		uchar_t device_reg,
530 		caddr_t arg,
531 		int mode)
532 {
533 	int err = 0;
534 	i2c_transfer_t *i2ctp;
535 	int16_t temp;
536 
537 	(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp, 2, 0, I2C_SLEEP);
538 	i2ctp->i2c_version = I2C_XFER_REV;
539 	i2ctp->i2c_flags = I2C_WR;
540 	i2ctp->i2c_wbuf[0] = device_reg;
541 
542 	if (ddi_copyin(arg, (caddr_t)&temp, sizeof (int16_t), mode) !=
543 	    DDI_SUCCESS) {
544 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
545 
546 		return (EFAULT);
547 	}
548 
549 	i2ctp->i2c_wbuf[1] = (int8_t)temp;
550 
551 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
552 		err = EIO;
553 	}
554 
555 	i2c_transfer_free(unitp->max1617_hdl, i2ctp);
556 
557 	return (err);
558 }
559 
560 int
561 get_temp_limit(struct max1617_unit *unitp,
562 		uchar_t reg,
563 		caddr_t arg,
564 		int mode)
565 {
566 	int err = 0;
567 	i2c_transfer_t *i2ctp;
568 	int16_t temp16;
569 
570 	(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp, 1, 1, I2C_SLEEP);
571 	i2ctp->i2c_version = I2C_XFER_REV;
572 	i2ctp->i2c_flags = I2C_WR_RD;
573 	i2ctp->i2c_wbuf[0] = reg;
574 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) == I2C_SUCCESS) {
575 		/*
576 		 * This double cast is required so that the sign is preserved
577 		 * when expanding the 8 bit value to 16.
578 		 */
579 		temp16 = (int16_t)((int8_t)i2ctp->i2c_rbuf[0]);
580 		if (ddi_copyout((caddr_t)&temp16, arg, sizeof (int16_t),
581 		    mode) != DDI_SUCCESS) {
582 			err = EFAULT;
583 		}
584 	} else {
585 		err = EIO;
586 	}
587 	i2c_transfer_free(unitp->max1617_hdl, i2ctp);
588 
589 	return (err);
590 }
591 
592 static int
593 max1617_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
594 		cred_t *credp, int *rvalp)
595 {
596 	_NOTE(ARGUNUSED(credp, rvalp))
597 	struct max1617_unit *unitp;
598 	int err = 0;
599 	i2c_transfer_t *i2ctp;
600 	int fcn = MAX1617_MINOR_TO_FCN(getminor(dev));
601 	int instance = MAX1617_MINOR_TO_INST(getminor(dev));
602 	uchar_t reg;
603 
604 	unitp = (struct max1617_unit *)
605 	    ddi_get_soft_state(max1617_soft_statep, instance);
606 
607 	if (max1617_debug) {
608 		printf("max1617_ioctl: fcn=%d instance=%d\n", fcn, instance);
609 	}
610 
611 	/*
612 	 * Serialize here, in order to block transacations during CPR.
613 	 * This is not a bottle neck since i2c_transfer would serialize
614 	 * anyway.
615 	 */
616 	mutex_enter(&unitp->max1617_mutex);
617 	while (unitp->max1617_flags == MAX1617_BUSY) {
618 		if (cv_wait_sig(&unitp->max1617_cv,
619 		    &unitp->max1617_mutex) <= 0) {
620 			mutex_exit(&unitp->max1617_mutex);
621 			return (EINTR);
622 		}
623 	}
624 	unitp->max1617_flags = MAX1617_BUSY;
625 	mutex_exit(&unitp->max1617_mutex);
626 
627 	switch (cmd) {
628 
629 	/*
630 	 * I2C_GET_TEMPERATURE reads a temperature from the device and
631 	 * copies a single byte representing the celcius temp
632 	 * to user space.
633 	 */
634 	case I2C_GET_TEMPERATURE:
635 		switch (fcn) {
636 		case MAX1617_AMB_TEMP:
637 			reg = MAX1617_LOCAL_TEMP_REG;
638 			break;
639 		case MAX1617_CPU_TEMP:
640 			reg = MAX1617_REMOTE_TEMP_REG;
641 			break;
642 		default:
643 			err = EINVAL;
644 			goto done;
645 		}
646 
647 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp,
648 		    1, 1, I2C_SLEEP);
649 		i2ctp->i2c_version = I2C_XFER_REV;
650 		i2ctp->i2c_flags = I2C_WR_RD;
651 		i2ctp->i2c_wbuf[0] = reg;
652 
653 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) == I2C_SUCCESS) {
654 
655 			/*
656 			 * This double cast is needed so that the sign bit
657 			 * is preserved when casting from unsigned char to
658 			 * signed 16 bit value.
659 			 */
660 			int16_t temp = (int16_t)((int8_t)i2ctp->i2c_rbuf[0]);
661 			if (ddi_copyout((caddr_t)&temp, (caddr_t)arg,
662 			    sizeof (int16_t), mode) != DDI_SUCCESS) {
663 				err = EFAULT;
664 			}
665 		} else {
666 			err = EIO;
667 		}
668 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
669 		break;
670 
671 	case MAX1617_GET_STATUS:
672 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp,
673 		    1, 1, I2C_SLEEP);
674 		i2ctp->i2c_version = I2C_XFER_REV;
675 		i2ctp->i2c_flags = I2C_WR_RD;
676 		i2ctp->i2c_wbuf[0] = MAX1617_STATUS_REG;
677 
678 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) == I2C_SUCCESS) {
679 			if (ddi_copyout((caddr_t)i2ctp->i2c_rbuf, (caddr_t)arg,
680 			    sizeof (uint8_t), mode) != DDI_SUCCESS) {
681 				err = EFAULT;
682 			}
683 		} else {
684 			err = EIO;
685 		}
686 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
687 		break;
688 	case MAX1617_GET_CONFIG:
689 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp, 1, 1,
690 		    I2C_SLEEP);
691 		i2ctp->i2c_version = I2C_XFER_REV;
692 		i2ctp->i2c_flags = I2C_WR_RD;
693 		i2ctp->i2c_wbuf[0] = MAX1617_CONFIG_REG;
694 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) == I2C_SUCCESS) {
695 			if (ddi_copyout((caddr_t)i2ctp->i2c_rbuf, (caddr_t)arg,
696 			    sizeof (uint8_t), mode) != DDI_SUCCESS) {
697 				err = EFAULT;
698 			}
699 		} else {
700 			err = EIO;
701 		}
702 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
703 		break;
704 	case MAX1617_GET_CONV_RATE:
705 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp,
706 		    1, 1, I2C_SLEEP);
707 		i2ctp->i2c_version = I2C_XFER_REV;
708 		i2ctp->i2c_flags = I2C_WR_RD;
709 		i2ctp->i2c_wbuf[0] = MAX1617_CONV_RATE_REG;
710 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) == I2C_SUCCESS) {
711 			if (ddi_copyout((caddr_t)i2ctp->i2c_rbuf, (caddr_t)arg,
712 			    sizeof (uint8_t), mode) != DDI_SUCCESS) {
713 				err = EFAULT;
714 			}
715 		} else {
716 			err = EIO;
717 		}
718 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
719 		break;
720 
721 	case MAX1617_GET_HIGH_LIMIT:
722 		switch (fcn) {
723 		case MAX1617_AMB_TEMP:
724 			err = get_temp_limit(unitp, MAX1617_LOCALTEMP_HIGH_REG,
725 			    (caddr_t)arg, mode);
726 			break;
727 		case MAX1617_CPU_TEMP:
728 			err = get_temp_limit(unitp, MAX1617_REMOTETEMP_HIGH_REG,
729 			    (caddr_t)arg, mode);
730 			break;
731 		default:
732 			err = EINVAL;
733 			break;
734 		}
735 		break;
736 
737 	case MAX1617_GET_LOW_LIMIT:
738 
739 		switch (fcn) {
740 		case MAX1617_AMB_TEMP:
741 			err = get_temp_limit(unitp, MAX1617_LOCALTEMP_LOW_REG,
742 			    (caddr_t)arg, mode);
743 			break;
744 		case MAX1617_CPU_TEMP:
745 			err = get_temp_limit(unitp, MAX1617_REMOTETEMP_LOW_REG,
746 			    (caddr_t)arg, mode);
747 			break;
748 		default:
749 			err = EINVAL;
750 		}
751 		break;
752 
753 	case MAX1617_SET_CONV_RATE:
754 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp,
755 		    2, 0, I2C_SLEEP);
756 		i2ctp->i2c_version = I2C_XFER_REV;
757 		i2ctp->i2c_flags = I2C_WR;
758 		i2ctp->i2c_wbuf[0] = MAX1617_CONV_RATE_WR_REG;
759 		if (ddi_copyin((caddr_t)arg, (caddr_t)&i2ctp->i2c_wbuf[1],
760 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
761 			err = EFAULT;
762 			break;
763 		}
764 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
765 			err = EIO;
766 		}
767 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
768 		break;
769 
770 	case MAX1617_SET_CONFIG:
771 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp,
772 		    2, 0, I2C_SLEEP);
773 		i2ctp->i2c_version = I2C_XFER_REV;
774 		i2ctp->i2c_flags = I2C_WR;
775 		i2ctp->i2c_wbuf[0] = MAX1617_CONFIG_WR_REG;
776 		if (ddi_copyin((caddr_t)arg, (caddr_t)&i2ctp->i2c_wbuf[1],
777 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
778 			err = EFAULT;
779 			break;
780 		}
781 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
782 			err = EIO;
783 		}
784 
785 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
786 		break;
787 
788 	case MAX1617_SET_HIGH_LIMIT:
789 		switch (fcn) {
790 		case MAX1617_AMB_TEMP:
791 			err = set_temp_limit(unitp,
792 			    MAX1617_LOCALTEMP_HIGH_WR_REG, (caddr_t)arg, mode);
793 			break;
794 		case MAX1617_CPU_TEMP:
795 			err = set_temp_limit(unitp,
796 			    MAX1617_REMOTETEMP_HIGH_WR_REG, (caddr_t)arg, mode);
797 			break;
798 		default:
799 			err = EINVAL;
800 		}
801 		break;
802 
803 	case MAX1617_SET_LOW_LIMIT:
804 		switch (fcn) {
805 		case MAX1617_AMB_TEMP:
806 			err = set_temp_limit(unitp,
807 			    MAX1617_LOCALTEMP_LOW_WR_REG, (caddr_t)arg, mode);
808 			break;
809 		case MAX1617_CPU_TEMP:
810 			err = set_temp_limit(unitp,
811 			    MAX1617_REMOTETEMP_LOW_WR_REG, (caddr_t)arg, mode);
812 			break;
813 		default:
814 			err = EINVAL;
815 		}
816 		break;
817 
818 	case MAX1617_ONE_SHOT_CMD:
819 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp, 1, 0,
820 		    I2C_SLEEP);
821 		i2ctp->i2c_version = I2C_XFER_REV;
822 		i2ctp->i2c_flags = I2C_WR;
823 		i2ctp->i2c_wbuf[0] = MAX1617_ONE_SHOT_CMD_REG;
824 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
825 			err = EIO;
826 		}
827 
828 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
829 		break;
830 
831 	default:
832 		err = EINVAL;
833 	}
834 
835 	done:
836 
837 	mutex_enter(&unitp->max1617_mutex);
838 	unitp->max1617_flags = 0;
839 	cv_signal(&unitp->max1617_cv);
840 	mutex_exit(&unitp->max1617_mutex);
841 
842 	return (err);
843 }
844