lm75.c revision 7656:2621e50fdf4a
1239462Sdim/*
2218885Sdim * CDDL HEADER START
3218885Sdim *
4218885Sdim * The contents of this file are subject to the terms of the
5218885Sdim * Common Development and Distribution License (the "License").
6218885Sdim * You may not use this file except in compliance with the License.
7218885Sdim *
8218885Sdim * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9249423Sdim * or http://www.opensolaris.org/os/licensing.
10249423Sdim * See the License for the specific language governing permissions
11249423Sdim * and limitations under the License.
12249423Sdim *
13249423Sdim * When distributing Covered Code, include this CDDL HEADER in each
14249423Sdim * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15249423Sdim * If applicable, add the following below this CDDL HEADER, with the
16249423Sdim * fields enclosed by brackets "[]" replaced with your own identifying
17249423Sdim * information: Portions Copyright [yyyy] [name of copyright owner]
18249423Sdim *
19249423Sdim * CDDL HEADER END
20249423Sdim */
21249423Sdim/*
22249423Sdim * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23218885Sdim * Use is subject to license terms.
24218885Sdim */
25249423Sdim
26249423Sdim
27218885Sdim#include <sys/stat.h>		/* ddi_create_minor_node S_IFCHR */
28263508Sdim#include <sys/modctl.h>		/* for modldrv */
29263508Sdim#include <sys/open.h>		/* for open params.	 */
30249423Sdim#include <sys/types.h>
31263508Sdim#include <sys/kmem.h>
32263508Sdim#include <sys/sunddi.h>
33249423Sdim#include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
34218885Sdim#include <sys/ddi.h>
35218885Sdim#include <sys/file.h>
36218885Sdim#include <sys/note.h>
37263508Sdim#include <sys/i2c/clients/lm75.h>
38263508Sdim#include <sys/i2c/clients/lm75_impl.h>
39218885Sdim
40218885Sdimstatic void *lm75soft_statep;
41249423Sdim
42218885Sdimstatic int lm75_do_attach(dev_info_t *);
43249423Sdimstatic int lm75_do_detach(dev_info_t *);
44249423Sdimstatic int lm75_do_resume(void);
45249423Sdimstatic int lm75_do_suspend(void);
46249423Sdimstatic int lm75_get16(intptr_t, int, struct lm75_unit *, int);
47249423Sdimstatic int lm75_set16(intptr_t, int, struct lm75_unit *, int);
48249423Sdim
49249423Sdim/*
50249423Sdim * cb ops (only need ioctl)
51249423Sdim */
52249423Sdimstatic int lm75_open(dev_t *, int, int, cred_t *);
53249423Sdimstatic int lm75_close(dev_t, int, int, cred_t *);
54249423Sdimstatic int lm75_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
55218885Sdim
56249423Sdimstatic struct cb_ops lm75_cbops = {
57249423Sdim	lm75_open,			/* open  */
58249423Sdim	lm75_close,			/* close */
59263508Sdim	nodev,				/* strategy */
60263508Sdim	nodev,				/* print */
61249423Sdim	nodev,				/* dump */
62249423Sdim	nodev,				/* read */
63249423Sdim	nodev,				/* write */
64249423Sdim	lm75_ioctl,			/* ioctl */
65263508Sdim	nodev,				/* devmap */
66249423Sdim	nodev,				/* mmap */
67249423Sdim	nodev,				/* segmap */
68249423Sdim	nochpoll,			/* poll */
69218885Sdim	ddi_prop_op,			/* cb_prop_op */
70249423Sdim	NULL,				/* streamtab */
71249423Sdim	D_NEW | D_MP | D_HOTPLUG,	/* Driver compatibility flag */
72218885Sdim	CB_REV,				/* rev */
73249423Sdim	nodev,				/* int (*cb_aread)() */
74249423Sdim	nodev				/* int (*cb_awrite)() */
75249423Sdim};
76249423Sdim
77249423Sdim/*
78218885Sdim * dev ops
79249423Sdim */
80249423Sdimstatic int lm75_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
81249423Sdimstatic int lm75_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
82249423Sdim
83249423Sdimstatic struct dev_ops lm75_ops = {
84218885Sdim	DEVO_REV,
85249423Sdim	0,
86249423Sdim	ddi_getinfo_1to1,
87249423Sdim	nulldev,
88249423Sdim	nulldev,
89249423Sdim	lm75_attach,
90218885Sdim	lm75_detach,
91249423Sdim	nodev,
92249423Sdim	&lm75_cbops,
93218885Sdim	NULL,
94249423Sdim	NULL,
95249423Sdim	ddi_quiesce_not_needed,		/* quiesce */
96218885Sdim};
97249423Sdim
98218885Sdimextern struct mod_ops mod_driverops;
99249423Sdim
100218885Sdimstatic struct modldrv lm75_modldrv = {
101249423Sdim	&mod_driverops,			/* type of module - driver */
102249423Sdim	"LM75 i2c device driver: v1.8",
103249423Sdim	&lm75_ops
104249423Sdim};
105249423Sdim
106249423Sdimstatic struct modlinkage lm75_modlinkage = {
107239462Sdim	MODREV_1,
108249423Sdim	&lm75_modldrv,
109249423Sdim	0
110249423Sdim};
111218885Sdim
112249423Sdim
113249423Sdimint
114249423Sdim_init(void)
115249423Sdim{
116249423Sdim	int error;
117218885Sdim
118249423Sdim	error = mod_install(&lm75_modlinkage);
119249423Sdim
120218885Sdim	if (!error)
121249423Sdim		(void) ddi_soft_state_init(&lm75soft_statep,
122249423Sdim		    sizeof (struct lm75_unit), 1);
123249423Sdim	return (error);
124249423Sdim}
125218885Sdim
126249423Sdimint
127218885Sdim_fini(void)
128249423Sdim{
129249423Sdim	int error;
130249423Sdim
131218885Sdim	error = mod_remove(&lm75_modlinkage);
132249423Sdim	if (!error)
133249423Sdim		ddi_soft_state_fini(&lm75soft_statep);
134218885Sdim
135249423Sdim	return (error);
136234982Sdim}
137249423Sdim
138249423Sdimint
139249423Sdim_info(struct modinfo *modinfop)
140239462Sdim{
141249423Sdim	return (mod_info(&lm75_modlinkage, modinfop));
142249423Sdim}
143249423Sdim
144249423Sdimstatic int
145249423Sdimlm75_open(dev_t *devp, int flags, int otyp, cred_t *credp)
146249423Sdim{
147249423Sdim	_NOTE(ARGUNUSED(credp))
148249423Sdim
149249423Sdim	struct lm75_unit *unitp;
150249423Sdim	int instance;
151249423Sdim	int error = 0;
152249423Sdim
153249423Sdim	instance = getminor(*devp);
154249423Sdim
155249423Sdim	if (instance < 0) {
156249423Sdim		return (ENXIO);
157249423Sdim	}
158249423Sdim
159249423Sdim	unitp = (struct lm75_unit *)
160249423Sdim	    ddi_get_soft_state(lm75soft_statep, instance);
161249423Sdim
162249423Sdim	if (unitp == NULL) {
163249423Sdim		return (ENXIO);
164249423Sdim	}
165249423Sdim
166249423Sdim	if (otyp != OTYP_CHR) {
167249423Sdim		return (EINVAL);
168249423Sdim	}
169249423Sdim
170263508Sdim	mutex_enter(&unitp->lm75_mutex);
171263508Sdim
172263508Sdim	if (flags & FEXCL) {
173263508Sdim		if (unitp->lm75_oflag != 0) {
174263508Sdim			error = EBUSY;
175263508Sdim		} else {
176263508Sdim			unitp->lm75_oflag = FEXCL;
177263508Sdim		}
178263508Sdim	} else {
179263508Sdim		if (unitp->lm75_oflag == FEXCL) {
180263508Sdim			error = EBUSY;
181263508Sdim		} else {
182249423Sdim			unitp->lm75_oflag = FOPEN;
183249423Sdim		}
184249423Sdim	}
185249423Sdim
186249423Sdim	mutex_exit(&unitp->lm75_mutex);
187249423Sdim
188249423Sdim	return (error);
189249423Sdim}
190249423Sdim
191249423Sdimstatic int
192249423Sdimlm75_close(dev_t dev, int flags, int otyp, cred_t *credp)
193249423Sdim{
194249423Sdim	_NOTE(ARGUNUSED(flags, otyp, credp))
195249423Sdim
196249423Sdim	struct lm75_unit *unitp;
197249423Sdim	int instance;
198249423Sdim
199249423Sdim	instance = getminor(dev);
200249423Sdim
201249423Sdim	if (instance < 0) {
202249423Sdim		return (ENXIO);
203249423Sdim	}
204249423Sdim
205249423Sdim	unitp = (struct lm75_unit *)
206249423Sdim	    ddi_get_soft_state(lm75soft_statep, instance);
207249423Sdim
208249423Sdim	if (unitp == NULL) {
209249423Sdim		return (ENXIO);
210249423Sdim	}
211249423Sdim
212249423Sdim	mutex_enter(&unitp->lm75_mutex);
213249423Sdim
214249423Sdim	unitp->lm75_oflag = 0;
215249423Sdim
216249423Sdim	mutex_exit(&unitp->lm75_mutex);
217249423Sdim	return (DDI_SUCCESS);
218249423Sdim}
219249423Sdim
220249423Sdimstatic int
221249423Sdimlm75_get16(intptr_t arg, int reg, struct lm75_unit *unitp, int mode) {
222249423Sdim	i2c_transfer_t		*i2c_tran_pointer;
223249423Sdim	int err = DDI_SUCCESS;
224249423Sdim	int16_t			temp16;
225249423Sdim	int8_t			holder;
226249423Sdim
227249423Sdim	(void) i2c_transfer_alloc(unitp->lm75_hdl, &i2c_tran_pointer,
228249423Sdim				1, 2, I2C_SLEEP);
229249423Sdim	if (i2c_tran_pointer == NULL) {
230263508Sdim		D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_TEMPERATURE "
231263508Sdim				"i2c_tran_pointer not allocated\n",
232263508Sdim				unitp->lm75_name));
233263508Sdim		return (ENOMEM);
234263508Sdim	}
235263508Sdim
236249423Sdim	i2c_tran_pointer->i2c_flags = I2C_WR_RD;
237249423Sdim	i2c_tran_pointer->i2c_wbuf[0] = (uchar_t)reg;
238249423Sdim
239249423Sdim	err = i2c_transfer(unitp->lm75_hdl, i2c_tran_pointer);
240249423Sdim	if (err) {
241249423Sdim		D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_TEMPERATURE "
242249423Sdim				"i2c_transfer routine\n",
243249423Sdim				unitp->lm75_name));
244249423Sdim		i2c_transfer_free(unitp->lm75_hdl, i2c_tran_pointer);
245249423Sdim		return (err);
246249423Sdim	}
247249423Sdim
248249423Sdim	D1CMN_ERR((CE_NOTE, "%s: rbuf[0] =  %x rbuf[1] = %x\n",
249249423Sdim		unitp->lm75_name, i2c_tran_pointer->i2c_rbuf[0],
250249423Sdim		i2c_tran_pointer->i2c_rbuf[0]));
251249423Sdim	temp16 = i2c_tran_pointer->i2c_rbuf[0];
252249423Sdim	temp16 = (temp16 << 1);
253249423Sdim	temp16 = (temp16 | ((i2c_tran_pointer->i2c_rbuf[1] & 0x80) >> 7));
254249423Sdim
255249423Sdim
256249423Sdim	if (temp16 & LM75_COMP_MASK) {
257249423Sdim		holder = (temp16 & LM75_COMP_MASK_UPPER);
258249423Sdim		holder = -holder;
259249423Sdim		holder = holder/2;
260249423Sdim		temp16 = 0 - holder;
261249423Sdim	} else {
262249423Sdim		temp16 = temp16 / 2;
263218885Sdim	}
264218885Sdim	if (ddi_copyout((caddr_t)&temp16, (caddr_t)arg,
265218885Sdim			sizeof (int16_t), mode) != DDI_SUCCESS) {
266218885Sdim		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
275static int
276lm75_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
307static int
308lm75_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
426static int
427lm75_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
439static int
440lm75_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
452static int
453lm75_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
497static int
498lm75_do_resume(void)
499{
500	int ret = DDI_SUCCESS;
501
502	return (ret);
503}
504
505static int
506lm75_do_suspend()
507{
508	int ret = DDI_SUCCESS;
509
510	return (ret);
511}
512
513static int
514lm75_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