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