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 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #include <sys/stat.h> /* ddi_create_minor_node S_IFCHR */
27 #include <sys/modctl.h> /* for modldrv */
28 #include <sys/open.h> /* for open params. */
29 #include <sys/types.h>
30 #include <sys/kmem.h>
31 #include <sys/sunddi.h>
32 #include <sys/conf.h> /* req. by dev_ops flags MTSAFE etc. */
33 #include <sys/ddi.h>
34 #include <sys/file.h>
35 #include <sys/note.h>
36
37 #include <sys/i2c/clients/pcf8591.h>
38 #include <sys/i2c/clients/pcf8591_impl.h>
39
40 static void *pcf8591soft_statep;
41
42 static uint8_t ipmode = PCF8591_4SINGLE;
43 static int32_t current_value = 0;
44 static int current_set_flag = 0;
45
46 static int pcf8591_do_attach(dev_info_t *);
47 static int pcf8591_do_detach(dev_info_t *);
48 static int pcf8591_do_resume(void);
49 static int pcf8591_do_suspend(void);
50
51 /*
52 * cb ops (only need ioctl)
53 */
54 static int pcf8591_open(dev_t *, int, int, cred_t *);
55 static int pcf8591_close(dev_t, int, int, cred_t *);
56 static int pcf8591_ioctl(dev_t, int, intptr_t, int, cred_t *, int *);
57
58 static struct cb_ops pcf8591_cbops = {
59 pcf8591_open, /* open */
60 pcf8591_close, /* close */
61 nodev, /* strategy */
62 nodev, /* print */
63 nodev, /* dump */
64 nodev, /* read */
65 nodev, /* write */
66 pcf8591_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 pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
83 void **result);
84 static int pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
85 static int pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
86
87 static struct dev_ops pcf8591_ops = {
88 DEVO_REV,
89 0,
90 pcf8591_info,
91 nulldev,
92 nulldev,
93 pcf8591_attach,
94 pcf8591_detach,
95 nodev,
96 &pcf8591_cbops,
97 NULL,
98 NULL,
99 ddi_quiesce_not_needed, /* quiesce */
100 };
101
102 extern struct mod_ops mod_driverops;
103
104 static struct modldrv pcf8591_modldrv = {
105 &mod_driverops, /* type of module - driver */
106 "PCF8591 i2c device driver",
107 &pcf8591_ops
108 };
109
110 static struct modlinkage pcf8591_modlinkage = {
111 MODREV_1,
112 &pcf8591_modldrv,
113 0
114 };
115
116
117 int
_init(void)118 _init(void)
119 {
120 int error;
121
122 error = mod_install(&pcf8591_modlinkage);
123 if (!error)
124 (void) ddi_soft_state_init(&pcf8591soft_statep,
125 sizeof (struct pcf8591_unit), 1);
126 return (error);
127 }
128
129 int
_fini(void)130 _fini(void)
131 {
132 int error;
133
134 error = mod_remove(&pcf8591_modlinkage);
135 if (!error)
136 ddi_soft_state_fini(&pcf8591soft_statep);
137
138 return (error);
139 }
140
141 int
_info(struct modinfo * modinfop)142 _info(struct modinfo *modinfop)
143 {
144 return (mod_info(&pcf8591_modlinkage, modinfop));
145 }
146
147 static int
pcf8591_open(dev_t * devp,int flags,int otyp,cred_t * credp)148 pcf8591_open(dev_t *devp, int flags, int otyp, cred_t *credp)
149 {
150 _NOTE(ARGUNUSED(credp))
151
152 struct pcf8591_unit *unitp;
153 int instance;
154 int error = 0;
155
156 instance = MINOR_TO_INST(getminor(*devp));
157
158 if (instance < 0) {
159 return (ENXIO);
160 }
161
162 unitp = (struct pcf8591_unit *)
163 ddi_get_soft_state(pcf8591soft_statep, instance);
164
165 if (unitp == NULL) {
166 return (ENXIO);
167 }
168
169 if (otyp != OTYP_CHR) {
170 return (EINVAL);
171 }
172
173 mutex_enter(&unitp->pcf8591_mutex);
174
175 if (flags & FEXCL) {
176 if (unitp->pcf8591_oflag != 0) {
177 error = EBUSY;
178 } else {
179 unitp->pcf8591_oflag = FEXCL;
180 }
181 } else {
182 if (unitp->pcf8591_oflag == FEXCL) {
183 error = EBUSY;
184 } else {
185 unitp->pcf8591_oflag = FOPEN;
186 }
187 }
188
189 mutex_exit(&unitp->pcf8591_mutex);
190
191 return (error);
192 }
193
194 static int
pcf8591_close(dev_t dev,int flags,int otyp,cred_t * credp)195 pcf8591_close(dev_t dev, int flags, int otyp, cred_t *credp)
196 {
197 _NOTE(ARGUNUSED(flags, otyp, credp))
198
199 struct pcf8591_unit *unitp;
200 int instance;
201
202 instance = MINOR_TO_INST(getminor(dev));
203
204 if (instance < 0) {
205 return (ENXIO);
206 }
207
208 unitp = (struct pcf8591_unit *)
209 ddi_get_soft_state(pcf8591soft_statep, instance);
210
211 if (unitp == NULL) {
212 return (ENXIO);
213 }
214
215 mutex_enter(&unitp->pcf8591_mutex);
216
217 unitp->pcf8591_oflag = 0;
218
219 mutex_exit(&unitp->pcf8591_mutex);
220 return (DDI_SUCCESS);
221 }
222
223 static int
pcf8591_ioctl(dev_t dev,int cmd,intptr_t arg,int mode,cred_t * credp,int * rvalp)224 pcf8591_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
225 cred_t *credp, int *rvalp)
226 {
227 _NOTE(ARGUNUSED(credp, rvalp))
228
229 struct pcf8591_unit *unitp;
230 int err = 0;
231 i2c_transfer_t *i2c_tran_pointer;
232 int port = MINOR_TO_PORT(getminor(dev));
233 int instance = MINOR_TO_INST(getminor(dev));
234 int autoincr = 0;
235 uchar_t control, reg;
236 int32_t value;
237 uint8_t uvalue;
238
239 if (arg == (intptr_t)NULL) {
240 D2CMN_ERR((CE_WARN, "PCF8591: ioctl: arg passed in to ioctl "
241 "= NULL\n"));
242 err = EINVAL;
243 return (err);
244 }
245 unitp = (struct pcf8591_unit *)
246 ddi_get_soft_state(pcf8591soft_statep, instance);
247
248 if (unitp == NULL) {
249 cmn_err(CE_WARN, "PCF8591: ioctl: unitp not filled\n");
250 return (ENOMEM);
251 }
252
253 mutex_enter(&unitp->pcf8591_mutex);
254
255 D1CMN_ERR((CE_NOTE, "%s: ioctl: port = %d instance = %d\n",
256 unitp->pcf8591_name, port, instance));
257
258 switch (cmd) {
259 case I2C_GET_INPUT:
260 (void) i2c_transfer_alloc(unitp->pcf8591_hdl, &i2c_tran_pointer,
261 1, 2, I2C_SLEEP);
262 if (i2c_tran_pointer == NULL) {
263 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_INPUT "
264 "i2c_tran_pointer not allocated\n",
265 unitp->pcf8591_name));
266 err = ENOMEM;
267 break;
268 }
269 reg = (uchar_t)port;
270 if ((reg == 0x02) && (ipmode == PCF8591_2DIFF)) {
271 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_INPUT "
272 "cannot use port 2 when ipmode is "
273 "0x03\n", unitp->pcf8591_name));
274 err = EIO;
275 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer);
276 break;
277 }
278
279 if ((reg == 0x03) && (ipmode != PCF8591_4SINGLE)) {
280 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_INPUT "
281 "cannot use port 3 when ipmode is not "
282 "0x00\n", unitp->pcf8591_name));
283 err = EIO;
284 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer);
285 break;
286 }
287 control = ((0 << PCF8591_ANALOG_OUTPUT_SHIFT) |
288 (ipmode << PCF8591_ANALOG_INPUT_SHIFT) |
289 (autoincr << PCF8591_AUTOINCR_SHIFT) | reg);
290
291 i2c_tran_pointer->i2c_flags = I2C_WR_RD;
292 i2c_tran_pointer->i2c_wbuf[0] = control;
293
294 err = i2c_transfer(unitp->pcf8591_hdl, i2c_tran_pointer);
295 if (err) {
296 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_GET_INPUT"
297 " i2c_transfer routine\n",
298 unitp->pcf8591_name));
299 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer);
300 break;
301 }
302 i2c_tran_pointer->i2c_rbuf[0] = i2c_tran_pointer->i2c_rbuf[1];
303 value = i2c_tran_pointer->i2c_rbuf[0];
304 D1CMN_ERR((CE_NOTE, "%s: Back from transfer result is %x\n",
305 unitp->pcf8591_name, value));
306 if (ddi_copyout((caddr_t)&value,
307 (caddr_t)arg,
308 sizeof (int32_t), mode) != DDI_SUCCESS) {
309 D2CMN_ERR((CE_WARN, "%s: Failed I2C_GET_INPUT"
310 " ddi_copyout routine\n",
311 unitp->pcf8591_name));
312 err = EFAULT;
313 }
314 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer);
315 break;
316
317 case I2C_SET_OUTPUT:
318 reg = (uchar_t)port;
319 if (ipmode != PCF8591_4SINGLE) {
320 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_SET_OUTPUT "
321 "cannot set output when ipmode is not "
322 "0x00\n", unitp->pcf8591_name));
323 err = EIO;
324 break;
325 }
326
327 (void) i2c_transfer_alloc(unitp->pcf8591_hdl, &i2c_tran_pointer,
328 2, 0, I2C_SLEEP);
329 if (i2c_tran_pointer == NULL) {
330 D2CMN_ERR((CE_WARN, "%s: Failed in "
331 "I2C_SET_OUTPUT "
332 "i2c_tran_pointer not allocated\n",
333 unitp->pcf8591_name));
334 err = ENOMEM;
335 break;
336 }
337 if (ddi_copyin((caddr_t)arg, (caddr_t)&value,
338 sizeof (int32_t), mode) != DDI_SUCCESS) {
339 D2CMN_ERR((CE_WARN, "%s: Failed in I2C_SET_OUTPUT"
340 " ddi_copyout routine\n",
341 unitp->pcf8591_name));
342 err = EFAULT;
343 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer);
344 break;
345 }
346 control = ((1 << PCF8591_ANALOG_OUTPUT_SHIFT) |
347 (0 << PCF8591_ANALOG_INPUT_SHIFT) |
348 (autoincr << PCF8591_AUTOINCR_SHIFT) | reg);
349
350 i2c_tran_pointer->i2c_flags = I2C_WR;
351 i2c_tran_pointer->i2c_wbuf[0] = control;
352 i2c_tran_pointer->i2c_wbuf[1] = (uchar_t)value;
353
354 err = i2c_transfer(unitp->pcf8591_hdl, i2c_tran_pointer);
355 if (!err) {
356 current_value = value;
357 current_set_flag = 1;
358 }
359 i2c_transfer_free(unitp->pcf8591_hdl, i2c_tran_pointer);
360 break;
361
362 case I2C_GET_OUTPUT:
363 if (current_set_flag == 0) {
364 err = EIO;
365 break;
366 } else {
367 if (ddi_copyout((caddr_t)(uintptr_t)current_value,
368 (caddr_t)arg, sizeof (int32_t), mode)
369 != DDI_SUCCESS) {
370 D2CMN_ERR((CE_WARN, "%s: Failed in "
371 "I2C_GET_OUTPUT ddi_copyout routine\n",
372 unitp->pcf8591_name));
373 err = EFAULT;
374 break;
375 }
376 }
377 break;
378
379 case PCF8591_SET_IPMODE:
380 if (ddi_copyin((caddr_t)arg, (caddr_t)&uvalue,
381 sizeof (uint_t), mode) != DDI_SUCCESS) {
382 D2CMN_ERR((CE_WARN, "%s: Failed in PCF8591_SET_IPMODE"
383 " ddi_copyout routine\n",
384 unitp->pcf8591_name));
385 err = EFAULT;
386 break;
387 }
388
389 if (uvalue > 0x03) {
390 D2CMN_ERR((CE_WARN, "%s: Failed in PCF8591_SET_IPMODE"
391 " value is not a valid mode\n",
392 unitp->pcf8591_name));
393 err = EIO;
394 break;
395 }
396
397 ipmode = uvalue;
398 break;
399
400 default:
401 D2CMN_ERR((CE_WARN, "%s: Invalid IOCTL cmd: %x\n",
402 unitp->pcf8591_name, cmd));
403 err = EINVAL;
404 }
405
406 mutex_exit(&unitp->pcf8591_mutex);
407 return (err);
408
409 }
410
411 /* ARGSUSED */
412 static int
pcf8591_info(dev_info_t * dip,ddi_info_cmd_t infocmd,void * arg,void ** result)413 pcf8591_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
414 {
415 dev_t dev;
416 int instance;
417
418 if (infocmd == DDI_INFO_DEVT2INSTANCE) {
419 dev = (dev_t)arg;
420 instance = MINOR_TO_INST(getminor(dev));
421 *result = (void *)(uintptr_t)instance;
422 return (DDI_SUCCESS);
423 }
424 return (DDI_FAILURE);
425 }
426
427 static int
pcf8591_attach(dev_info_t * dip,ddi_attach_cmd_t cmd)428 pcf8591_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
429 {
430 switch (cmd) {
431 case DDI_ATTACH:
432 return (pcf8591_do_attach(dip));
433 case DDI_RESUME:
434 return (pcf8591_do_resume());
435 default:
436 return (DDI_FAILURE);
437 }
438 }
439
440 static int
pcf8591_detach(dev_info_t * dip,ddi_detach_cmd_t cmd)441 pcf8591_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
442 {
443 switch (cmd) {
444 case DDI_DETACH:
445 return (pcf8591_do_detach(dip));
446 case DDI_SUSPEND:
447 return (pcf8591_do_suspend());
448 default:
449 return (DDI_FAILURE);
450 }
451 }
452
453 static int
pcf8591_do_attach(dev_info_t * dip)454 pcf8591_do_attach(dev_info_t *dip)
455 {
456 struct pcf8591_unit *unitp;
457 int instance;
458 char name[MAXNAMELEN];
459 minor_t minor_number;
460 int i;
461
462 instance = ddi_get_instance(dip);
463
464 if (ddi_soft_state_zalloc(pcf8591soft_statep, instance) != 0) {
465 cmn_err(CE_WARN, "%s%d: failed to zalloc softstate\n",
466 ddi_get_name(dip), instance);
467 return (DDI_FAILURE);
468 }
469
470 unitp = ddi_get_soft_state(pcf8591soft_statep, instance);
471
472 if (unitp == NULL) {
473 cmn_err(CE_WARN, "%s%d: unitp not filled\n",
474 ddi_get_name(dip), instance);
475 return (ENOMEM);
476 }
477
478 (void) snprintf(unitp->pcf8591_name, sizeof (unitp->pcf8591_name),
479 "%s%d", ddi_node_name(dip), instance);
480
481 for (i = 0; i < 4; i++) {
482 (void) sprintf(name, "port_%d", i);
483
484 minor_number = INST_TO_MINOR(instance) |
485 PORT_TO_MINOR(I2C_PORT(i));
486
487 if (ddi_create_minor_node(dip, name, S_IFCHR, minor_number,
488 "ddi_i2c:adio", 0) == DDI_FAILURE) {
489 cmn_err(CE_WARN, "%s ddi_create_minor_node failed for "
490 "%s\n", unitp->pcf8591_name, name);
491 ddi_soft_state_free(pcf8591soft_statep, instance);
492
493 return (DDI_FAILURE);
494 }
495 }
496
497 if (i2c_client_register(dip, &unitp->pcf8591_hdl) != I2C_SUCCESS) {
498 ddi_remove_minor_node(dip, NULL);
499 ddi_soft_state_free(pcf8591soft_statep, instance);
500
501 return (DDI_FAILURE);
502 }
503
504 mutex_init(&unitp->pcf8591_mutex, NULL, MUTEX_DRIVER, NULL);
505
506 return (DDI_SUCCESS);
507 }
508
509 static int
pcf8591_do_resume()510 pcf8591_do_resume()
511 {
512 int ret = DDI_SUCCESS;
513
514 return (ret);
515 }
516
517 static int
pcf8591_do_suspend()518 pcf8591_do_suspend()
519 {
520 int ret = DDI_SUCCESS;
521
522 return (ret);
523 }
524
525 static int
pcf8591_do_detach(dev_info_t * dip)526 pcf8591_do_detach(dev_info_t *dip)
527 {
528 struct pcf8591_unit *unitp;
529 int instance;
530
531 instance = ddi_get_instance(dip);
532
533 unitp = ddi_get_soft_state(pcf8591soft_statep, instance);
534
535 if (unitp == NULL) {
536 cmn_err(CE_WARN, "%s%d: unitp not filled\n",
537 ddi_get_name(dip), instance);
538 return (ENOMEM);
539 }
540
541 i2c_client_unregister(unitp->pcf8591_hdl);
542
543 ddi_remove_minor_node(dip, NULL);
544
545 mutex_destroy(&unitp->pcf8591_mutex);
546
547 ddi_soft_state_free(pcf8591soft_statep, instance);
548
549 return (DDI_SUCCESS);
550 }
551