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
_init(void)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
_fini(void)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
_info(struct modinfo * modinfop)146 _info(struct modinfo *modinfop)
147 {
148 	return (mod_info(&max1617_modlinkage, modinfop));
149 }
150 
151 /* ARGSUSED */
152 static int
max1617_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)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
max1617_do_attach(dev_info_t * dip)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, 0) == 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, 0) == 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
max1617_do_resume(dev_info_t * dip)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
max1617_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)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
max1617_do_detach(dev_info_t * dip)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
max1617_do_suspend(dev_info_t * dip)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
max1617_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)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
max1617_open(dev_t * devp,int flags,int otyp,cred_t * credp)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
max1617_close(dev_t dev,int flags,int otyp,cred_t * credp)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
set_temp_limit(struct max1617_unit * unitp,uchar_t device_reg,caddr_t arg,int mode)528 set_temp_limit(struct max1617_unit *unitp, uchar_t device_reg, caddr_t arg,
529     int mode)
530 {
531 	int err = 0;
532 	i2c_transfer_t *i2ctp;
533 	int16_t temp;
534 
535 	(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp, 2, 0, I2C_SLEEP);
536 	i2ctp->i2c_version = I2C_XFER_REV;
537 	i2ctp->i2c_flags = I2C_WR;
538 	i2ctp->i2c_wbuf[0] = device_reg;
539 
540 	if (ddi_copyin(arg, (caddr_t)&temp, sizeof (int16_t), mode) !=
541 	    DDI_SUCCESS) {
542 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
543 
544 		return (EFAULT);
545 	}
546 
547 	i2ctp->i2c_wbuf[1] = (int8_t)temp;
548 
549 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
550 		err = EIO;
551 	}
552 
553 	i2c_transfer_free(unitp->max1617_hdl, i2ctp);
554 
555 	return (err);
556 }
557 
558 int
get_temp_limit(struct max1617_unit * unitp,uchar_t reg,caddr_t arg,int mode)559 get_temp_limit(struct max1617_unit *unitp, uchar_t reg, caddr_t arg, int mode)
560 {
561 	int err = 0;
562 	i2c_transfer_t *i2ctp;
563 	int16_t temp16;
564 
565 	(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp, 1, 1, I2C_SLEEP);
566 	i2ctp->i2c_version = I2C_XFER_REV;
567 	i2ctp->i2c_flags = I2C_WR_RD;
568 	i2ctp->i2c_wbuf[0] = reg;
569 	if (i2c_transfer(unitp->max1617_hdl, i2ctp) == I2C_SUCCESS) {
570 		/*
571 		 * This double cast is required so that the sign is preserved
572 		 * when expanding the 8 bit value to 16.
573 		 */
574 		temp16 = (int16_t)((int8_t)i2ctp->i2c_rbuf[0]);
575 		if (ddi_copyout((caddr_t)&temp16, arg, sizeof (int16_t),
576 		    mode) != DDI_SUCCESS) {
577 			err = EFAULT;
578 		}
579 	} else {
580 		err = EIO;
581 	}
582 	i2c_transfer_free(unitp->max1617_hdl, i2ctp);
583 
584 	return (err);
585 }
586 
587 static int
max1617_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)588 max1617_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
589     cred_t *credp, int *rvalp)
590 {
591 	_NOTE(ARGUNUSED(credp, rvalp))
592 	struct max1617_unit *unitp;
593 	int err = 0;
594 	i2c_transfer_t *i2ctp;
595 	int fcn = MAX1617_MINOR_TO_FCN(getminor(dev));
596 	int instance = MAX1617_MINOR_TO_INST(getminor(dev));
597 	uchar_t reg;
598 
599 	unitp = (struct max1617_unit *)
600 	    ddi_get_soft_state(max1617_soft_statep, instance);
601 
602 	if (max1617_debug) {
603 		printf("max1617_ioctl: fcn=%d instance=%d\n", fcn, instance);
604 	}
605 
606 	/*
607 	 * Serialize here, in order to block transacations during CPR.
608 	 * This is not a bottle neck since i2c_transfer would serialize
609 	 * anyway.
610 	 */
611 	mutex_enter(&unitp->max1617_mutex);
612 	while (unitp->max1617_flags == MAX1617_BUSY) {
613 		if (cv_wait_sig(&unitp->max1617_cv,
614 		    &unitp->max1617_mutex) <= 0) {
615 			mutex_exit(&unitp->max1617_mutex);
616 			return (EINTR);
617 		}
618 	}
619 	unitp->max1617_flags = MAX1617_BUSY;
620 	mutex_exit(&unitp->max1617_mutex);
621 
622 	switch (cmd) {
623 
624 	/*
625 	 * I2C_GET_TEMPERATURE reads a temperature from the device and
626 	 * copies a single byte representing the celcius temp
627 	 * to user space.
628 	 */
629 	case I2C_GET_TEMPERATURE:
630 		switch (fcn) {
631 		case MAX1617_AMB_TEMP:
632 			reg = MAX1617_LOCAL_TEMP_REG;
633 			break;
634 		case MAX1617_CPU_TEMP:
635 			reg = MAX1617_REMOTE_TEMP_REG;
636 			break;
637 		default:
638 			err = EINVAL;
639 			goto done;
640 		}
641 
642 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp,
643 		    1, 1, I2C_SLEEP);
644 		i2ctp->i2c_version = I2C_XFER_REV;
645 		i2ctp->i2c_flags = I2C_WR_RD;
646 		i2ctp->i2c_wbuf[0] = reg;
647 
648 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) == I2C_SUCCESS) {
649 
650 			/*
651 			 * This double cast is needed so that the sign bit
652 			 * is preserved when casting from unsigned char to
653 			 * signed 16 bit value.
654 			 */
655 			int16_t temp = (int16_t)((int8_t)i2ctp->i2c_rbuf[0]);
656 			if (ddi_copyout((caddr_t)&temp, (caddr_t)arg,
657 			    sizeof (int16_t), mode) != DDI_SUCCESS) {
658 				err = EFAULT;
659 			}
660 		} else {
661 			err = EIO;
662 		}
663 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
664 		break;
665 
666 	case MAX1617_GET_STATUS:
667 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp,
668 		    1, 1, I2C_SLEEP);
669 		i2ctp->i2c_version = I2C_XFER_REV;
670 		i2ctp->i2c_flags = I2C_WR_RD;
671 		i2ctp->i2c_wbuf[0] = MAX1617_STATUS_REG;
672 
673 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) == I2C_SUCCESS) {
674 			if (ddi_copyout((caddr_t)i2ctp->i2c_rbuf, (caddr_t)arg,
675 			    sizeof (uint8_t), mode) != DDI_SUCCESS) {
676 				err = EFAULT;
677 			}
678 		} else {
679 			err = EIO;
680 		}
681 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
682 		break;
683 	case MAX1617_GET_CONFIG:
684 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp, 1, 1,
685 		    I2C_SLEEP);
686 		i2ctp->i2c_version = I2C_XFER_REV;
687 		i2ctp->i2c_flags = I2C_WR_RD;
688 		i2ctp->i2c_wbuf[0] = MAX1617_CONFIG_REG;
689 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) == I2C_SUCCESS) {
690 			if (ddi_copyout((caddr_t)i2ctp->i2c_rbuf, (caddr_t)arg,
691 			    sizeof (uint8_t), mode) != DDI_SUCCESS) {
692 				err = EFAULT;
693 			}
694 		} else {
695 			err = EIO;
696 		}
697 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
698 		break;
699 	case MAX1617_GET_CONV_RATE:
700 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp,
701 		    1, 1, I2C_SLEEP);
702 		i2ctp->i2c_version = I2C_XFER_REV;
703 		i2ctp->i2c_flags = I2C_WR_RD;
704 		i2ctp->i2c_wbuf[0] = MAX1617_CONV_RATE_REG;
705 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) == I2C_SUCCESS) {
706 			if (ddi_copyout((caddr_t)i2ctp->i2c_rbuf, (caddr_t)arg,
707 			    sizeof (uint8_t), mode) != DDI_SUCCESS) {
708 				err = EFAULT;
709 			}
710 		} else {
711 			err = EIO;
712 		}
713 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
714 		break;
715 
716 	case MAX1617_GET_HIGH_LIMIT:
717 		switch (fcn) {
718 		case MAX1617_AMB_TEMP:
719 			err = get_temp_limit(unitp, MAX1617_LOCALTEMP_HIGH_REG,
720 			    (caddr_t)arg, mode);
721 			break;
722 		case MAX1617_CPU_TEMP:
723 			err = get_temp_limit(unitp, MAX1617_REMOTETEMP_HIGH_REG,
724 			    (caddr_t)arg, mode);
725 			break;
726 		default:
727 			err = EINVAL;
728 			break;
729 		}
730 		break;
731 
732 	case MAX1617_GET_LOW_LIMIT:
733 
734 		switch (fcn) {
735 		case MAX1617_AMB_TEMP:
736 			err = get_temp_limit(unitp, MAX1617_LOCALTEMP_LOW_REG,
737 			    (caddr_t)arg, mode);
738 			break;
739 		case MAX1617_CPU_TEMP:
740 			err = get_temp_limit(unitp, MAX1617_REMOTETEMP_LOW_REG,
741 			    (caddr_t)arg, mode);
742 			break;
743 		default:
744 			err = EINVAL;
745 		}
746 		break;
747 
748 	case MAX1617_SET_CONV_RATE:
749 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp,
750 		    2, 0, I2C_SLEEP);
751 		i2ctp->i2c_version = I2C_XFER_REV;
752 		i2ctp->i2c_flags = I2C_WR;
753 		i2ctp->i2c_wbuf[0] = MAX1617_CONV_RATE_WR_REG;
754 		if (ddi_copyin((caddr_t)arg, (caddr_t)&i2ctp->i2c_wbuf[1],
755 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
756 			err = EFAULT;
757 			break;
758 		}
759 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
760 			err = EIO;
761 		}
762 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
763 		break;
764 
765 	case MAX1617_SET_CONFIG:
766 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp,
767 		    2, 0, I2C_SLEEP);
768 		i2ctp->i2c_version = I2C_XFER_REV;
769 		i2ctp->i2c_flags = I2C_WR;
770 		i2ctp->i2c_wbuf[0] = MAX1617_CONFIG_WR_REG;
771 		if (ddi_copyin((caddr_t)arg, (caddr_t)&i2ctp->i2c_wbuf[1],
772 		    sizeof (uint8_t), mode) != DDI_SUCCESS) {
773 			err = EFAULT;
774 			break;
775 		}
776 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
777 			err = EIO;
778 		}
779 
780 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
781 		break;
782 
783 	case MAX1617_SET_HIGH_LIMIT:
784 		switch (fcn) {
785 		case MAX1617_AMB_TEMP:
786 			err = set_temp_limit(unitp,
787 			    MAX1617_LOCALTEMP_HIGH_WR_REG, (caddr_t)arg, mode);
788 			break;
789 		case MAX1617_CPU_TEMP:
790 			err = set_temp_limit(unitp,
791 			    MAX1617_REMOTETEMP_HIGH_WR_REG, (caddr_t)arg, mode);
792 			break;
793 		default:
794 			err = EINVAL;
795 		}
796 		break;
797 
798 	case MAX1617_SET_LOW_LIMIT:
799 		switch (fcn) {
800 		case MAX1617_AMB_TEMP:
801 			err = set_temp_limit(unitp,
802 			    MAX1617_LOCALTEMP_LOW_WR_REG, (caddr_t)arg, mode);
803 			break;
804 		case MAX1617_CPU_TEMP:
805 			err = set_temp_limit(unitp,
806 			    MAX1617_REMOTETEMP_LOW_WR_REG, (caddr_t)arg, mode);
807 			break;
808 		default:
809 			err = EINVAL;
810 		}
811 		break;
812 
813 	case MAX1617_ONE_SHOT_CMD:
814 		(void) i2c_transfer_alloc(unitp->max1617_hdl, &i2ctp, 1, 0,
815 		    I2C_SLEEP);
816 		i2ctp->i2c_version = I2C_XFER_REV;
817 		i2ctp->i2c_flags = I2C_WR;
818 		i2ctp->i2c_wbuf[0] = MAX1617_ONE_SHOT_CMD_REG;
819 		if (i2c_transfer(unitp->max1617_hdl, i2ctp) != I2C_SUCCESS) {
820 			err = EIO;
821 		}
822 
823 		i2c_transfer_free(unitp->max1617_hdl, i2ctp);
824 		break;
825 
826 	default:
827 		err = EINVAL;
828 	}
829 
830 	done:
831 
832 	mutex_enter(&unitp->max1617_mutex);
833 	unitp->max1617_flags = 0;
834 	cv_signal(&unitp->max1617_cv);
835 	mutex_exit(&unitp->max1617_mutex);
836 
837 	return (err);
838 }
839