xref: /illumos-gate/usr/src/uts/sun4u/io/i2c/clients/lm75.c (revision f47a9c508408507a404eaf38dd597e6ac41f92e6)
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, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <sys/stat.h>		/* ddi_create_minor_node S_IFCHR */
30 #include <sys/modctl.h>		/* for modldrv */
31 #include <sys/open.h>		/* for open params.	 */
32 #include <sys/types.h>
33 #include <sys/kmem.h>
34 #include <sys/sunddi.h>
35 #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
36 #include <sys/ddi.h>
37 #include <sys/file.h>
38 #include <sys/note.h>
39 #include <sys/i2c/clients/lm75.h>
40 #include <sys/i2c/clients/lm75_impl.h>
41 
42 static void *lm75soft_statep;
43 
44 static int lm75_do_attach(dev_info_t *);
45 static int lm75_do_detach(dev_info_t *);
46 static int lm75_do_resume(void);
47 static int lm75_do_suspend(void);
48 static int lm75_get16(intptr_t, int, struct lm75_unit *, int);
49 static int lm75_set16(intptr_t, int, struct lm75_unit *, int);
50 
51 /*
52  * cb ops (only need ioctl)
53  */
54 static int lm75_open(dev_t *, int, int, cred_t *);
55 static int lm75_close(dev_t, int, int, cred_t *);
56 static int lm75_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
57 
58 static struct cb_ops lm75_cbops = {
59 	lm75_open,			/* open  */
60 	lm75_close,			/* close */
61 	nodev,				/* strategy */
62 	nodev,				/* print */
63 	nodev,				/* dump */
64 	nodev,				/* read */
65 	nodev,				/* write */
66 	lm75_ioctl,			/* ioctl */
67 	nodev,				/* devmap */
68 	nodev,				/* mmap */
69 	nodev,				/* segmap */
70 	nochpoll,			/* poll */
71 	ddi_prop_op,			/* cb_prop_op */
72 	NULL,				/* streamtab */
73 	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
74 	CB_REV,				/* rev */
75 	nodev,				/* int (*cb_aread)() */
76 	nodev				/* int (*cb_awrite)() */
77 };
78 
79 /*
80  * dev ops
81  */
82 static int lm75_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
83 static int lm75_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
84 
85 static struct dev_ops lm75_ops = {
86 	DEVO_REV,
87 	0,
88 	ddi_getinfo_1to1,
89 	nulldev,
90 	nulldev,
91 	lm75_attach,
92 	lm75_detach,
93 	nodev,
94 	&lm75_cbops,
95 	NULL
96 };
97 
98 extern struct mod_ops mod_driverops;
99 
100 static struct modldrv lm75_modldrv = {
101 	&mod_driverops,			/* type of module - driver */
102 	"LM75 i2c device driver: v%I%",
103 	&lm75_ops
104 };
105 
106 static struct modlinkage lm75_modlinkage = {
107 	MODREV_1,
108 	&lm75_modldrv,
109 	0
110 };
111 
112 
113 int
114 _init(void)
115 {
116 	int error;
117 
118 	error = mod_install(&lm75_modlinkage);
119 
120 	if (!error)
121 		(void) ddi_soft_state_init(&lm75soft_statep,
122 			sizeof (struct lm75_unit), 1);
123 	return (error);
124 }
125 
126 int
127 _fini(void)
128 {
129 	int error;
130 
131 	error = mod_remove(&lm75_modlinkage);
132 	if (!error)
133 		ddi_soft_state_fini(&lm75soft_statep);
134 
135 	return (error);
136 }
137 
138 int
139 _info(struct modinfo *modinfop)
140 {
141 	return (mod_info(&lm75_modlinkage, modinfop));
142 }
143 
144 static int
145 lm75_open(dev_t *devp, int flags, int otyp, cred_t *credp)
146 {
147 	_NOTE(ARGUNUSED(credp))
148 
149 	struct lm75_unit *unitp;
150 	int instance;
151 	int error = 0;
152 
153 	instance = getminor(*devp);
154 
155 	if (instance < 0) {
156 		return (ENXIO);
157 	}
158 
159 	unitp = (struct lm75_unit *)
160 		ddi_get_soft_state(lm75soft_statep, instance);
161 
162 	if (unitp == NULL) {
163 		return (ENXIO);
164 	}
165 
166 	if (otyp != OTYP_CHR) {
167 		return (EINVAL);
168 	}
169 
170 	mutex_enter(&unitp->lm75_mutex);
171 
172 	if (flags & FEXCL) {
173 		if (unitp->lm75_oflag != 0) {
174 			error = EBUSY;
175 		} else {
176 			unitp->lm75_oflag = FEXCL;
177 		}
178 	} else {
179 		if (unitp->lm75_oflag == FEXCL) {
180 			error = EBUSY;
181 		} else {
182 			unitp->lm75_oflag = FOPEN;
183 		}
184 	}
185 
186 	mutex_exit(&unitp->lm75_mutex);
187 
188 	return (error);
189 }
190 
191 static int
192 lm75_close(dev_t dev, int flags, int otyp, cred_t *credp)
193 {
194 	_NOTE(ARGUNUSED(flags, otyp, credp))
195 
196 	struct lm75_unit *unitp;
197 	int instance;
198 
199 	instance = getminor(dev);
200 
201 	if (instance < 0) {
202 		return (ENXIO);
203 	}
204 
205 	unitp = (struct lm75_unit *)
206 		ddi_get_soft_state(lm75soft_statep, instance);
207 
208 	if (unitp == NULL) {
209 		return (ENXIO);
210 	}
211 
212 	mutex_enter(&unitp->lm75_mutex);
213 
214 	unitp->lm75_oflag = 0;
215 
216 	mutex_exit(&unitp->lm75_mutex);
217 	return (DDI_SUCCESS);
218 }
219 
220 static int
221 lm75_get16(intptr_t arg, int reg, struct lm75_unit *unitp, int mode) {
222 	i2c_transfer_t		*i2c_tran_pointer;
223 	int err = DDI_SUCCESS;
224 	int16_t			temp16;
225 	int8_t			holder;
226 
227 	(void) i2c_transfer_alloc(unitp->lm75_hdl, &i2c_tran_pointer,
228 				1, 2, I2C_SLEEP);
229 	if (i2c_tran_pointer == NULL) {
230 		D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_TEMPERATURE "
231 				"i2c_tran_pointer not allocated\n",
232 				unitp->lm75_name));
233 		return (ENOMEM);
234 	}
235 
236 	i2c_tran_pointer->i2c_flags = I2C_WR_RD;
237 	i2c_tran_pointer->i2c_wbuf[0] = (uchar_t)reg;
238 
239 	err = i2c_transfer(unitp->lm75_hdl, i2c_tran_pointer);
240 	if (err) {
241 		D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_TEMPERATURE "
242 				"i2c_transfer routine\n",
243 				unitp->lm75_name));
244 		i2c_transfer_free(unitp->lm75_hdl, i2c_tran_pointer);
245 		return (err);
246 	}
247 
248 	D1CMN_ERR((CE_NOTE, "%s: rbuf[0] =  %x rbuf[1] = %x\n",
249 		unitp->lm75_name, i2c_tran_pointer->i2c_rbuf[0],
250 		i2c_tran_pointer->i2c_rbuf[0]));
251 	temp16 = i2c_tran_pointer->i2c_rbuf[0];
252 	temp16 = (temp16 << 1);
253 	temp16 = (temp16 | ((i2c_tran_pointer->i2c_rbuf[1] & 0x80) >> 7));
254 
255 
256 	if (temp16 & LM75_COMP_MASK) {
257 		holder = (temp16 & LM75_COMP_MASK_UPPER);
258 		holder = -holder;
259 		holder = holder/2;
260 		temp16 = 0 - holder;
261 	} else {
262 		temp16 = temp16 / 2;
263 	}
264 	if (ddi_copyout((caddr_t)&temp16, (caddr_t)arg,
265 			sizeof (int16_t), mode) != DDI_SUCCESS) {
266 		D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_TEMPERATURE "
267 				"ddi_copyout routine\n",
268 				unitp->lm75_name));
269 		err = EFAULT;
270 	}
271 	i2c_transfer_free(unitp->lm75_hdl, i2c_tran_pointer);
272 	return (err);
273 }
274 
275 static int
276 lm75_set16(intptr_t arg, int reg, struct lm75_unit *unitp, int mode) {
277 	i2c_transfer_t		*i2c_tran_pointer;
278 	int err = DDI_SUCCESS;
279 	int16_t			temp16;
280 
281 	if (ddi_copyin((caddr_t)arg, (caddr_t)&temp16,
282 			sizeof (int16_t), mode) != DDI_SUCCESS) {
283 		D2CMN_ERR((CE_WARN, "%s: Failed in LM74_SET_HYST "
284 				"ddi_copyin routine\n",
285 				unitp->lm75_name));
286 		return (EFAULT);
287 	}
288 
289 	(void) i2c_transfer_alloc(unitp->lm75_hdl, &i2c_tran_pointer,
290 				3, 0, I2C_SLEEP);
291 	if (i2c_tran_pointer == NULL) {
292 		D2CMN_ERR((CE_WARN, "%s: Failed in LM75_SET_HYST "
293 				"i2c_tran_pointer not allocated\n",
294 				unitp->lm75_name));
295 		return (ENOMEM);
296 	}
297 	i2c_tran_pointer->i2c_flags = I2C_WR;
298 	i2c_tran_pointer->i2c_wbuf[0] = (uchar_t)reg;
299 	i2c_tran_pointer->i2c_wbuf[1] = (temp16 >> 1);
300 	i2c_tran_pointer->i2c_wbuf[2] =	((temp16 & 0xFE) << 7);
301 
302 	err = i2c_transfer(unitp->lm75_hdl, i2c_tran_pointer);
303 	i2c_transfer_free(unitp->lm75_hdl, i2c_tran_pointer);
304 	return (err);
305 }
306 
307 static int
308 lm75_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp,
309 		int *rvalp)
310 {
311 	_NOTE(ARGUNUSED(credp, rvalp))
312 
313 	struct lm75_unit	*unitp;
314 	int			instance;
315 	int			err = 0;
316 	i2c_transfer_t		*i2c_tran_pointer;
317 	uchar_t			passin_byte;
318 
319 	if (arg == NULL) {
320 		D2CMN_ERR((CE_WARN, "LM75: ioctl: arg passed in to ioctl "
321 				"= NULL\n"));
322 		err = EINVAL;
323 		return (err);
324 	}
325 	instance = getminor(dev);
326 	unitp = (struct lm75_unit *)
327 		ddi_get_soft_state(lm75soft_statep, instance);
328 
329 	if (unitp == NULL) {
330 		cmn_err(CE_WARN, "LM75: ioctl: unitp = NULL\n");
331 		err = ENOMEM;
332 		return (err);
333 	}
334 
335 	mutex_enter(&unitp->lm75_mutex);
336 
337 	switch (cmd) {
338 	case I2C_GET_TEMPERATURE:
339 		err = lm75_get16(arg, LM75_TEMPERATURE_REG, unitp, mode);
340 		break;
341 
342 	case LM75_GET_HYST:
343 		err = lm75_get16(arg, LM75_HYST_REG, unitp, mode);
344 		break;
345 
346 	case LM75_SET_HYST:
347 		err = lm75_set16(arg, LM75_HYST_REG, unitp, mode);
348 		break;
349 
350 	case LM75_GET_OVERTEMP_SHUTDOWN:
351 		err = lm75_get16(arg, LM75_OVERTEMP_REG, unitp, mode);
352 		break;
353 
354 	case LM75_SET_OVERTEMP_SHUTDOWN:
355 		err = lm75_set16(arg, LM75_OVERTEMP_REG, unitp, mode);
356 		break;
357 
358 	case LM75_GET_CONFIG:
359 		(void) i2c_transfer_alloc(unitp->lm75_hdl, &i2c_tran_pointer,
360 					1, 1, I2C_SLEEP);
361 		if (i2c_tran_pointer == NULL) {
362 			D2CMN_ERR((CE_WARN, "%s: Failed in LM75_GET_CONFIG "
363 					"i2c_tran_pointer not allocated\n",
364 					unitp->lm75_name));
365 			err = ENOMEM;
366 			break;
367 		}
368 		i2c_tran_pointer->i2c_flags = I2C_WR_RD;
369 		i2c_tran_pointer->i2c_wbuf[0] = LM75_CONFIGURATION_REG;
370 
371 		err = i2c_transfer(unitp->lm75_hdl, i2c_tran_pointer);
372 		if (err) {
373 			D2CMN_ERR((CE_WARN, "%s: Failed in LM75_GET_CONFIG "
374 					"i2c_transfer routine\n",
375 					unitp->lm75_name));
376 			i2c_transfer_free(unitp->lm75_hdl, i2c_tran_pointer);
377 			break;
378 		}
379 		if (ddi_copyout((caddr_t)i2c_tran_pointer->i2c_rbuf,
380 				(caddr_t)arg,
381 				sizeof (uint8_t), mode) != DDI_SUCCESS) {
382 			D2CMN_ERR((CE_WARN, "%s: Failed in LM75_GET_CONFIG "
383 					"ddi_copyout routine\n",
384 					unitp->lm75_name));
385 			err = EFAULT;
386 		}
387 		i2c_transfer_free(unitp->lm75_hdl, i2c_tran_pointer);
388 		break;
389 
390 	case LM75_SET_CONFIG:
391 		if (ddi_copyin((caddr_t)arg, (caddr_t)&passin_byte,
392 				sizeof (uint8_t), mode) != DDI_SUCCESS) {
393 			D2CMN_ERR((CE_WARN, "%s: Failed in LM75_SET_CONFIG "
394 					"ddi_copyin routine\n",
395 					unitp->lm75_name));
396 			err = EFAULT;
397 			break;
398 		}
399 		(void) i2c_transfer_alloc(unitp->lm75_hdl, &i2c_tran_pointer,
400 					2, 0, I2C_SLEEP);
401 		if (i2c_tran_pointer == NULL) {
402 			D2CMN_ERR((CE_WARN, "%s: Failed in LM75_SET_CONFIG "
403 					"i2c_tran_pointer not allocated\n",
404 					unitp->lm75_name));
405 			err = ENOMEM;
406 			break;
407 		}
408 		i2c_tran_pointer->i2c_flags = I2C_WR;
409 		i2c_tran_pointer->i2c_wbuf[0] = LM75_CONFIGURATION_REG;
410 		i2c_tran_pointer->i2c_wbuf[1] = passin_byte;
411 
412 		err = i2c_transfer(unitp->lm75_hdl, i2c_tran_pointer);
413 		i2c_transfer_free(unitp->lm75_hdl, i2c_tran_pointer);
414 		break;
415 
416 	default:
417 		D2CMN_ERR((CE_WARN, "%s: Invalid IOCTL cmd %x\n",
418 				unitp->lm75_name, cmd));
419 		err = EINVAL;
420 	}
421 
422 	mutex_exit(&unitp->lm75_mutex);
423 	return (err);
424 }
425 
426 static int
427 lm75_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
428 {
429 	switch (cmd) {
430 	case DDI_ATTACH:
431 		return (lm75_do_attach(dip));
432 	case DDI_RESUME:
433 		return (lm75_do_resume());
434 	default:
435 		return (DDI_FAILURE);
436 	}
437 }
438 
439 static int
440 lm75_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
441 {
442 	switch (cmd) {
443 	case DDI_DETACH:
444 		return (lm75_do_detach(dip));
445 	case DDI_SUSPEND:
446 		return (lm75_do_suspend());
447 	default:
448 		return (DDI_FAILURE);
449 	}
450 }
451 
452 static int
453 lm75_do_attach(dev_info_t *dip)
454 {
455 	struct lm75_unit *unitp;
456 	int instance;
457 
458 	instance = ddi_get_instance(dip);
459 
460 	if (ddi_soft_state_zalloc(lm75soft_statep, instance) != 0) {
461 		cmn_err(CE_WARN, "%s%d: failed to zalloc softstate\n",
462 				ddi_get_name(dip), instance);
463 		return (DDI_FAILURE);
464 	}
465 
466 	unitp = ddi_get_soft_state(lm75soft_statep, instance);
467 
468 	if (unitp == NULL) {
469 		cmn_err(CE_WARN, "LM75: ioctl: unitp = NULL\n");
470 		return (ENOMEM);
471 	}
472 
473 	(void) snprintf(unitp->lm75_name, sizeof (unitp->lm75_name),
474 			"%s%d", ddi_node_name(dip), instance);
475 
476 	if (ddi_create_minor_node(dip, "lm75", S_IFCHR, instance,
477 			"ddi_i2c:temperature_sensor", NULL) == DDI_FAILURE) {
478 		cmn_err(CE_WARN, "%s ddi_create_minor_node failed for "
479 			"%s\n", unitp->lm75_name, "lm75");
480 		ddi_soft_state_free(lm75soft_statep, instance);
481 
482 		return (DDI_FAILURE);
483 	}
484 
485 	if (i2c_client_register(dip, &unitp->lm75_hdl) != I2C_SUCCESS) {
486 		ddi_remove_minor_node(dip, NULL);
487 		ddi_soft_state_free(lm75soft_statep, instance);
488 
489 		return (DDI_FAILURE);
490 	}
491 
492 	mutex_init(&unitp->lm75_mutex, NULL, MUTEX_DRIVER, NULL);
493 
494 	return (DDI_SUCCESS);
495 }
496 
497 static int
498 lm75_do_resume(void)
499 {
500 	int ret = DDI_SUCCESS;
501 
502 	return (ret);
503 }
504 
505 static int
506 lm75_do_suspend()
507 {
508 	int ret = DDI_SUCCESS;
509 
510 	return (ret);
511 }
512 
513 static int
514 lm75_do_detach(dev_info_t *dip)
515 {
516 	struct lm75_unit *unitp;
517 	int instance;
518 
519 	instance = ddi_get_instance(dip);
520 
521 	unitp = ddi_get_soft_state(lm75soft_statep, instance);
522 
523 	if (unitp == NULL) {
524 		cmn_err(CE_WARN, "LM75: ioctl: unitp = NULL\n");
525 		return (ENOMEM);
526 	}
527 
528 	i2c_client_unregister(unitp->lm75_hdl);
529 
530 	ddi_remove_minor_node(dip, NULL);
531 
532 	mutex_destroy(&unitp->lm75_mutex);
533 	ddi_soft_state_free(lm75soft_statep, instance);
534 
535 	return (DDI_SUCCESS);
536 }
537