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