xref: /illumos-gate/usr/src/uts/sun4u/io/i2c/clients/ltc1427.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
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 (c) 2000-2001 by Sun Microsystems, Inc.
24  * All rights reserved.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 
30 #include <sys/stat.h>		/* ddi_create_minor_node S_IFCHR */
31 #include <sys/modctl.h>		/* for modldrv */
32 #include <sys/open.h>		/* for open params.	 */
33 #include <sys/types.h>
34 #include <sys/kmem.h>
35 #include <sys/sunddi.h>
36 #include <sys/conf.h>		/* req. by dev_ops flags MTSAFE etc. */
37 #include <sys/ddi.h>
38 #include <sys/file.h>
39 #include <sys/note.h>
40 
41 #include <sys/i2c/clients/ltc1427_impl.h>
42 
43 static void *ltc1427soft_statep;
44 
45 
46 static int ltc1427_do_attach(dev_info_t *);
47 static int ltc1427_do_detach(dev_info_t *);
48 static int ltc1427_do_resume(void);
49 static int ltc1427_do_suspend(void);
50 
51 /*
52  * cb ops (only need ioctl)
53  */
54 static int ltc1427_open(dev_t *, int, int, cred_t *);
55 static int ltc1427_close(dev_t, int, int, cred_t *);
56 static int ltc1427_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
57 
58 static struct cb_ops ltc1427_cbops = {
59 	ltc1427_open,			/* open  */
60 	ltc1427_close,			/* close */
61 	nodev,				/* strategy */
62 	nodev,				/* print */
63 	nodev,				/* dump */
64 	nodev,				/* read */
65 	nodev,				/* write */
66 	ltc1427_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 ltc1427_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
83 static int ltc1427_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
84 
85 static struct dev_ops ltc1427_ops = {
86 	DEVO_REV,
87 	0,
88 	ddi_getinfo_1to1,
89 	nulldev,
90 	nulldev,
91 	ltc1427_attach,
92 	ltc1427_detach,
93 	nodev,
94 	&ltc1427_cbops,
95 	NULL
96 };
97 
98 extern struct mod_ops mod_driverops;
99 
100 static struct modldrv ltc1427_modldrv = {
101 	&mod_driverops,			/* type of module - driver */
102 	"LTC1427 i2c device driver: v%I%",
103 	&ltc1427_ops
104 };
105 
106 static struct modlinkage ltc1427_modlinkage = {
107 	MODREV_1,
108 	&ltc1427_modldrv,
109 	0
110 };
111 
112 
113 int
114 _init(void)
115 {
116 	int error;
117 
118 	error = mod_install(&ltc1427_modlinkage);
119 
120 	if (!error)
121 		(void) ddi_soft_state_init(&ltc1427soft_statep,
122 			sizeof (struct ltc1427_unit), 1);
123 	return (error);
124 }
125 
126 int
127 _fini(void)
128 {
129 	int error;
130 
131 	error = mod_remove(&ltc1427_modlinkage);
132 	if (!error)
133 		ddi_soft_state_fini(&ltc1427soft_statep);
134 
135 	return (error);
136 }
137 
138 int
139 _info(struct modinfo *modinfop)
140 {
141 	return (mod_info(&ltc1427_modlinkage, modinfop));
142 }
143 
144 static int
145 ltc1427_open(dev_t *devp, int flags, int otyp, cred_t *credp)
146 {
147 	_NOTE(ARGUNUSED(credp))
148 
149 	struct ltc1427_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 ltc1427_unit *)
160 		ddi_get_soft_state(ltc1427soft_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->ltc1427_mutex);
171 
172 	if (flags & FEXCL) {
173 		if (unitp->ltc1427_oflag != 0) {
174 			error = EBUSY;
175 		} else {
176 			unitp->ltc1427_oflag = FEXCL;
177 		}
178 	} else {
179 		if (unitp->ltc1427_oflag == FEXCL) {
180 			error = EBUSY;
181 		} else {
182 			unitp->ltc1427_oflag = FOPEN;
183 		}
184 	}
185 
186 	mutex_exit(&unitp->ltc1427_mutex);
187 
188 	return (error);
189 }
190 
191 static int
192 ltc1427_close(dev_t dev, int flags, int otyp, cred_t *credp)
193 {
194 	_NOTE(ARGUNUSED(flags, otyp, credp))
195 
196 	struct ltc1427_unit *unitp;
197 	int instance;
198 
199 	instance = getminor(dev);
200 
201 	if (instance < 0) {
202 		return (ENXIO);
203 	}
204 
205 	unitp = (struct ltc1427_unit *)
206 		ddi_get_soft_state(ltc1427soft_statep, instance);
207 
208 	if (unitp == NULL) {
209 		return (ENXIO);
210 	}
211 
212 	mutex_enter(&unitp->ltc1427_mutex);
213 
214 	unitp->ltc1427_oflag = 0;
215 
216 	mutex_exit(&unitp->ltc1427_mutex);
217 	return (DDI_SUCCESS);
218 }
219 
220 static int
221 ltc1427_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
222 		cred_t *credp, int *rvalp)
223 {
224 	_NOTE(ARGUNUSED(credp, rvalp))
225 
226 	struct ltc1427_unit 	*unitp;
227 	int 		instance;
228 	int 			err = 0;
229 	i2c_transfer_t		*i2c_tran_pointer;
230 	int32_t			fan_speed;
231 
232 	if (arg == NULL) {
233 		D2CMN_ERR((CE_WARN, "LTC1427: ioctl: arg passed in to ioctl "
234 				"= NULL\n"));
235 		err = EINVAL;
236 		return (err);
237 	}
238 	instance = getminor(dev);
239 	unitp = (struct ltc1427_unit *)
240 		ddi_get_soft_state(ltc1427soft_statep, instance);
241 
242 	mutex_enter(&unitp->ltc1427_mutex);
243 
244 	switch (cmd) {
245 	case I2C_GET_OUTPUT:
246 		D1CMN_ERR((CE_NOTE, "current_set_flag = %d\n",
247 			unitp->current_set_flag));
248 		if (unitp->current_set_flag == 0) {
249 			err = EIO;
250 			break;
251 		} else {
252 			if (ddi_copyout((caddr_t)&unitp->current_value,
253 				(caddr_t)arg, sizeof (int32_t),
254 				mode) != DDI_SUCCESS) {
255 				D2CMN_ERR((CE_WARN,
256 				"%s: Failed in I2C_GET_OUTPUT "
257 				"ddi_copyout routine\n",
258 					unitp->ltc1427_name));
259 				err = EFAULT;
260 				break;
261 			}
262 		}
263 		break;
264 
265 	case I2C_SET_OUTPUT:
266 		if (ddi_copyin((caddr_t)arg, (caddr_t)&fan_speed,
267 			sizeof (int32_t), mode) != DDI_SUCCESS) {
268 			D2CMN_ERR((CE_WARN,
269 				"%s: Failed in I2C_SET_OUTPUT "
270 				"ioctl before switch\n",
271 					unitp->ltc1427_name));
272 			err = EFAULT;
273 			break;
274 		}
275 
276 		(void) i2c_transfer_alloc(unitp->ltc1427_hdl,
277 		    &i2c_tran_pointer, 2, 0, I2C_SLEEP);
278 		if (i2c_tran_pointer == NULL) {
279 			D2CMN_ERR((CE_WARN,
280 				"%s: Failed in I2C_SET_OUTPUT "
281 				"i2c_transfer_pointer not allocated\n",
282 					unitp->ltc1427_name));
283 			err = ENOMEM;
284 			break;
285 		}
286 		i2c_tran_pointer->i2c_flags = I2C_WR;
287 		i2c_tran_pointer->i2c_wbuf[0] =
288 		    (uchar_t)((fan_speed >> 8) & 0x03);
289 		i2c_tran_pointer->i2c_wbuf[1] =
290 		    (uchar_t)((fan_speed) & 0x000000ff);
291 
292 		err = i2c_transfer(unitp->ltc1427_hdl, i2c_tran_pointer);
293 		if (!err) {
294 			unitp->current_value = fan_speed;
295 			unitp->current_set_flag = 1;
296 		}
297 		i2c_transfer_free(unitp->ltc1427_hdl, i2c_tran_pointer);
298 		break;
299 
300 	default:
301 		D2CMN_ERR((CE_WARN, "%s: Invalid IOCTL cmd: %x\n",
302 			unitp->ltc1427_name, cmd));
303 		err = EINVAL;
304 	}
305 
306 	mutex_exit(&unitp->ltc1427_mutex);
307 	return (err);
308 }
309 
310 static int
311 ltc1427_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
312 {
313 	switch (cmd) {
314 	case DDI_ATTACH:
315 		return (ltc1427_do_attach(dip));
316 	case DDI_RESUME:
317 		return (ltc1427_do_resume());
318 	default:
319 		return (DDI_FAILURE);
320 	}
321 }
322 
323 static int
324 ltc1427_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
325 {
326 	switch (cmd) {
327 	case DDI_DETACH:
328 		return (ltc1427_do_detach(dip));
329 	case DDI_SUSPEND:
330 		return (ltc1427_do_suspend());
331 	default:
332 		return (DDI_FAILURE);
333 	}
334 }
335 
336 static int
337 ltc1427_do_attach(dev_info_t *dip)
338 {
339 	struct ltc1427_unit *unitp;
340 	int instance;
341 
342 	instance = ddi_get_instance(dip);
343 
344 	if (ddi_soft_state_zalloc(ltc1427soft_statep, instance) != 0) {
345 		cmn_err(CE_WARN, "%s%d: failed to zalloc softstate\n",
346 			ddi_get_name(dip), instance);
347 		return (DDI_FAILURE);
348 	}
349 
350 	unitp = ddi_get_soft_state(ltc1427soft_statep, instance);
351 
352 	if (unitp == NULL) {
353 		cmn_err(CE_WARN, "%s%d: unitp not filled\n",
354 			ddi_get_name(dip), instance);
355 		return (ENOMEM);
356 	}
357 
358 	(void) snprintf(unitp->ltc1427_name, sizeof (unitp->ltc1427_name),
359 			"%s%d", ddi_node_name(dip), instance);
360 
361 	if (ddi_create_minor_node(dip, "ltc1427", S_IFCHR, instance,
362 			"ddi_i2c:adio",	NULL) == DDI_FAILURE) {
363 		cmn_err(CE_WARN, "%s ddi_create_minor_node failed for "
364 			"%s\n", unitp->ltc1427_name, "ltc1427");
365 		ddi_soft_state_free(ltc1427soft_statep, instance);
366 
367 		return (DDI_FAILURE);
368 	}
369 
370 	if (i2c_client_register(dip, &unitp->ltc1427_hdl) != I2C_SUCCESS) {
371 		ddi_remove_minor_node(dip, NULL);
372 		ddi_soft_state_free(ltc1427soft_statep, instance);
373 
374 		return (DDI_FAILURE);
375 	}
376 
377 	mutex_init(&unitp->ltc1427_mutex, NULL, MUTEX_DRIVER, NULL);
378 
379 	return (DDI_SUCCESS);
380 }
381 
382 static int
383 ltc1427_do_resume()
384 {
385 	int ret = DDI_SUCCESS;
386 
387 	return (ret);
388 }
389 
390 static int
391 ltc1427_do_suspend()
392 {
393 	int ret = DDI_SUCCESS;
394 
395 	return (ret);
396 }
397 
398 static int
399 ltc1427_do_detach(dev_info_t *dip)
400 {
401 	struct ltc1427_unit *unitp;
402 	int instance;
403 
404 	instance = ddi_get_instance(dip);
405 
406 	unitp = ddi_get_soft_state(ltc1427soft_statep, instance);
407 
408 	if (unitp == NULL) {
409 		cmn_err(CE_WARN, "%s%d: unitp not filled\n",
410 			ddi_get_name(dip), instance);
411 		return (ENOMEM);
412 	}
413 
414 	i2c_client_unregister(unitp->ltc1427_hdl);
415 
416 	ddi_remove_minor_node(dip, NULL);
417 
418 	mutex_destroy(&unitp->ltc1427_mutex);
419 
420 	ddi_soft_state_free(ltc1427soft_statep, instance);
421 
422 	return (DDI_SUCCESS);
423 }
424