iic.c revision 46743
1/*-
2 * Copyright (c) 1998 Nicolas Souchu
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 *	$Id: iic.c,v 1.8 1999/05/07 10:09:46 phk Exp $
27 *
28 */
29#include <sys/param.h>
30#include <sys/kernel.h>
31#include <sys/systm.h>
32#include <sys/module.h>
33#include <sys/bus.h>
34#include <sys/conf.h>
35#include <sys/buf.h>
36#include <sys/uio.h>
37#include <sys/malloc.h>
38#include <sys/fcntl.h>
39
40#include <machine/clock.h>
41
42#include <dev/iicbus/iiconf.h>
43#include <dev/iicbus/iicbus.h>
44
45#include <machine/iic.h>
46
47#include "iicbus_if.h"
48
49#define BUFSIZE 1024
50
51struct iic_softc {
52
53	u_char sc_addr;			/* address on iicbus */
54	int sc_count;			/* >0 if device opened */
55
56	char sc_buffer[BUFSIZE];	/* output buffer */
57	char sc_inbuf[BUFSIZE];		/* input buffer */
58};
59
60#define IIC_SOFTC(unit) \
61	((struct iic_softc *)devclass_get_softc(iic_devclass, (unit)))
62
63#define IIC_DEVICE(unit) \
64	(devclass_get_device(iic_devclass, (unit)))
65
66static int iic_probe(device_t);
67static int iic_attach(device_t);
68
69static devclass_t iic_devclass;
70
71static device_method_t iic_methods[] = {
72	/* device interface */
73	DEVMETHOD(device_probe,		iic_probe),
74	DEVMETHOD(device_attach,	iic_attach),
75
76	/* iicbus interface */
77	DEVMETHOD(iicbus_intr,		iicbus_generic_intr),
78
79	{ 0, 0 }
80};
81
82static driver_t iic_driver = {
83	"iic",
84	iic_methods,
85	sizeof(struct iic_softc),
86};
87
88static	d_open_t	iicopen;
89static	d_close_t	iicclose;
90static	d_write_t	iicwrite;
91static	d_read_t	iicread;
92static	d_ioctl_t	iicioctl;
93
94#define CDEV_MAJOR 105
95static struct cdevsw iic_cdevsw =
96	{ iicopen,	iicclose,	iicread,	iicwrite,	/*105*/
97	  iicioctl,	nullstop,	nullreset,	nodevtotty,	/*iic*/
98	  seltrue,	nommap,		nostrat,	"iic",	NULL,	-1 };
99
100/*
101 * iicprobe()
102 */
103static int
104iic_probe(device_t dev)
105{
106	struct iic_softc *sc = (struct iic_softc *)device_get_softc(dev);
107
108	sc->sc_addr = iicbus_get_addr(dev);
109
110	/* XXX detect chip with start/stop conditions */
111
112	return (0);
113}
114
115/*
116 * iicattach()
117 */
118static int
119iic_attach(device_t dev)
120{
121	return (0);
122}
123
124static int
125iicopen (dev_t dev, int flags, int fmt, struct proc *p)
126{
127	struct iic_softc *sc = IIC_SOFTC(minor(dev));
128
129	if (!sc)
130		return (EINVAL);
131
132	if (sc->sc_count > 0)
133		return (EBUSY);
134
135	sc->sc_count++;
136
137	return (0);
138}
139
140static int
141iicclose(dev_t dev, int flags, int fmt, struct proc *p)
142{
143	struct iic_softc *sc = IIC_SOFTC(minor(dev));
144
145	if (!sc)
146		return (EINVAL);
147
148	if (!sc->sc_count)
149		return (EINVAL);
150
151	sc->sc_count--;
152
153	if (sc->sc_count < 0)
154		panic("%s: iic_count < 0!", __FUNCTION__);
155
156	return (0);
157}
158
159static int
160iicwrite(dev_t dev, struct uio * uio, int ioflag)
161{
162	device_t iicdev = IIC_DEVICE(minor(dev));
163	struct iic_softc *sc = IIC_SOFTC(minor(dev));
164	int sent, error, count;
165
166	if (!sc || !iicdev)
167		return (EINVAL);
168
169	if (sc->sc_count == 0)
170		return (EINVAL);
171
172	if ((error = iicbus_request_bus(device_get_parent(iicdev), iicdev, IIC_DONTWAIT)))
173		return (error);
174
175	count = min(uio->uio_resid, BUFSIZE);
176	uiomove(sc->sc_buffer, count, uio);
177
178	error = iicbus_block_write(device_get_parent(iicdev), sc->sc_addr,
179					sc->sc_buffer, count, &sent);
180
181	iicbus_release_bus(device_get_parent(iicdev), iicdev);
182
183	return(error);
184}
185
186static int
187iicread(dev_t dev, struct uio * uio, int ioflag)
188{
189	device_t iicdev = IIC_DEVICE(minor(dev));
190	struct iic_softc *sc = IIC_SOFTC(minor(dev));
191	int len, error = 0;
192	int bufsize;
193
194	if (!sc || !iicdev)
195		return (EINVAL);
196
197	if (sc->sc_count == 0)
198		return (EINVAL);
199
200	if ((error = iicbus_request_bus(device_get_parent(iicdev), iicdev, IIC_DONTWAIT)))
201		return (error);
202
203	/* max amount of data to read */
204	len = min(uio->uio_resid, BUFSIZE);
205
206	if ((error = iicbus_block_read(device_get_parent(iicdev), sc->sc_addr,
207					sc->sc_inbuf, len, &bufsize)))
208		return (error);
209
210	if (bufsize > uio->uio_resid)
211		panic("%s: too much data read!", __FUNCTION__);
212
213	iicbus_release_bus(device_get_parent(iicdev), iicdev);
214
215	return (uiomove(sc->sc_inbuf, bufsize, uio));
216}
217
218static int
219iicioctl(dev_t dev, u_long cmd, caddr_t data, int flags, struct proc *p)
220{
221	device_t iicdev = IIC_DEVICE(minor(dev));
222	struct iic_softc *sc = IIC_SOFTC(minor(dev));
223	device_t parent = device_get_parent(iicdev);
224	struct iiccmd *s = (struct iiccmd *)data;
225	int error, count;
226
227	if (!sc)
228		return (EINVAL);
229
230	if ((error = iicbus_request_bus(device_get_parent(iicdev), iicdev,
231			(flags & O_NONBLOCK) ? IIC_DONTWAIT :
232						(IIC_WAIT | IIC_INTR))))
233		return (error);
234
235	switch (cmd) {
236	case I2CSTART:
237		error = iicbus_start(parent, s->slave, 0);
238		break;
239
240	case I2CSTOP:
241		error = iicbus_stop(parent);
242		break;
243
244	case I2CRSTCARD:
245		error = iicbus_reset(parent, 0, 0, NULL);
246		break;
247
248	case I2CWRITE:
249		error = iicbus_write(parent, s->buf, s->count, &count, 0);
250		break;
251
252	case I2CREAD:
253		error = iicbus_read(parent, s->buf, s->count, &count, s->last, 0);
254		break;
255
256	default:
257		error = ENODEV;
258	}
259
260	iicbus_release_bus(device_get_parent(iicdev), iicdev);
261
262	return (error);
263}
264
265static int iic_devsw_installed = 0;
266
267static void
268iic_drvinit(void *unused)
269{
270        dev_t dev;
271
272        if( ! iic_devsw_installed ) {
273                dev = makedev(CDEV_MAJOR,0);
274                cdevsw_add(&dev,&iic_cdevsw,NULL);
275                iic_devsw_installed = 1;
276        }
277}
278
279DEV_DRIVER_MODULE(iic, iicbus, iic_driver, iic_devclass, CDEV_MAJOR,
280			NODEV, iic_cdevsw, 0, 0);
281
282SYSINIT(iicdev,SI_SUB_DRIVERS,SI_ORDER_MIDDLE+CDEV_MAJOR,iic_drvinit,NULL)
283