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