138774Snsouch/*-
293023Snsouch * Copyright (c) 1998, 2001 Nicolas Souchu
338774Snsouch * All rights reserved.
438774Snsouch *
538774Snsouch * Redistribution and use in source and binary forms, with or without
638774Snsouch * modification, are permitted provided that the following conditions
738774Snsouch * are met:
838774Snsouch * 1. Redistributions of source code must retain the above copyright
938774Snsouch *    notice, this list of conditions and the following disclaimer.
1038774Snsouch * 2. Redistributions in binary form must reproduce the above copyright
1138774Snsouch *    notice, this list of conditions and the following disclaimer in the
1238774Snsouch *    documentation and/or other materials provided with the distribution.
1338774Snsouch *
1438774Snsouch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1538774Snsouch * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1638774Snsouch * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1738774Snsouch * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1838774Snsouch * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1938774Snsouch * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2038774Snsouch * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2138774Snsouch * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2238774Snsouch * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2338774Snsouch * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2438774Snsouch * SUCH DAMAGE.
2538774Snsouch *
2650477Speter * $FreeBSD$
2738774Snsouch *
2838774Snsouch */
2938774Snsouch#include <sys/param.h>
30181304Sjhb#include <sys/bus.h>
31181304Sjhb#include <sys/conf.h>
32181304Sjhb#include <sys/fcntl.h>
33181304Sjhb#include <sys/lock.h>
3438774Snsouch#include <sys/kernel.h>
35129291Sjoerg#include <sys/malloc.h>
3638774Snsouch#include <sys/module.h>
37181304Sjhb#include <sys/sx.h>
38181304Sjhb#include <sys/systm.h>
3938774Snsouch#include <sys/uio.h>
4038774Snsouch
4138774Snsouch#include <dev/iicbus/iiconf.h>
4238774Snsouch#include <dev/iicbus/iicbus.h>
43103588Speter#include <dev/iicbus/iic.h>
4442442Snsouch
4538774Snsouch#include "iicbus_if.h"
4638774Snsouch
4738774Snsouch#define BUFSIZE 1024
4838774Snsouch
4938774Snsouchstruct iic_softc {
5038774Snsouch
51181304Sjhb	device_t sc_dev;
5293023Snsouch	u_char sc_addr;			/* 7 bit address on iicbus */
5338774Snsouch	int sc_count;			/* >0 if device opened */
5438774Snsouch
5538774Snsouch	char sc_buffer[BUFSIZE];	/* output buffer */
5638774Snsouch	char sc_inbuf[BUFSIZE];		/* input buffer */
5793023Snsouch
58130585Sphk	struct cdev *sc_devnode;
59181304Sjhb	struct sx sc_lock;
6038774Snsouch};
6138774Snsouch
62181304Sjhb#define	IIC_LOCK(sc)			sx_xlock(&(sc)->sc_lock)
63181304Sjhb#define	IIC_UNLOCK(sc)			sx_xunlock(&(sc)->sc_lock)
6438774Snsouch
6538774Snsouchstatic int iic_probe(device_t);
6638774Snsouchstatic int iic_attach(device_t);
6793023Snsouchstatic int iic_detach(device_t);
6893023Snsouchstatic void iic_identify(driver_t *driver, device_t parent);
6938774Snsouch
7038774Snsouchstatic devclass_t iic_devclass;
7138774Snsouch
7238774Snsouchstatic device_method_t iic_methods[] = {
7338774Snsouch	/* device interface */
7493023Snsouch	DEVMETHOD(device_identify,	iic_identify),
7538774Snsouch	DEVMETHOD(device_probe,		iic_probe),
7638774Snsouch	DEVMETHOD(device_attach,	iic_attach),
7793023Snsouch	DEVMETHOD(device_detach,	iic_detach),
7838774Snsouch
7938774Snsouch	/* iicbus interface */
8038774Snsouch	DEVMETHOD(iicbus_intr,		iicbus_generic_intr),
8138774Snsouch
8238774Snsouch	{ 0, 0 }
8338774Snsouch};
8438774Snsouch
8538774Snsouchstatic driver_t iic_driver = {
8638774Snsouch	"iic",
8738774Snsouch	iic_methods,
8838774Snsouch	sizeof(struct iic_softc),
8938774Snsouch};
9038774Snsouch
9138774Snsouchstatic	d_open_t	iicopen;
9238774Snsouchstatic	d_close_t	iicclose;
9338774Snsouchstatic	d_write_t	iicwrite;
9438774Snsouchstatic	d_read_t	iicread;
9538774Snsouchstatic	d_ioctl_t	iicioctl;
9638774Snsouch
9747625Sphkstatic struct cdevsw iic_cdevsw = {
98126080Sphk	.d_version =	D_VERSION,
99181304Sjhb	.d_flags =	D_TRACKCLOSE,
100111815Sphk	.d_open =	iicopen,
101111815Sphk	.d_close =	iicclose,
102111815Sphk	.d_read =	iicread,
103111815Sphk	.d_write =	iicwrite,
104111815Sphk	.d_ioctl =	iicioctl,
105111815Sphk	.d_name =	"iic",
10647625Sphk};
10738774Snsouch
10893023Snsouchstatic void
10993023Snsouchiic_identify(driver_t *driver, device_t parent)
11093023Snsouch{
111181304Sjhb
112181304Sjhb	if (device_find_child(parent, "iic", -1) == NULL)
113187321Snwhitehorn		BUS_ADD_CHILD(parent, 0, "iic", -1);
11493023Snsouch}
11593023Snsouch
11638774Snsouchstatic int
11738774Snsouchiic_probe(device_t dev)
11838774Snsouch{
119187321Snwhitehorn	if (iicbus_get_addr(dev) > 0)
120187321Snwhitehorn		return (ENXIO);
121187321Snwhitehorn
12293023Snsouch	device_set_desc(dev, "I2C generic I/O");
123187321Snwhitehorn
124187321Snwhitehorn	return (0);
12538774Snsouch}
12638774Snsouch
12738774Snsouchstatic int
12838774Snsouchiic_attach(device_t dev)
12938774Snsouch{
13093023Snsouch	struct iic_softc *sc = (struct iic_softc *)device_get_softc(dev);
13193023Snsouch
132181304Sjhb	sc->sc_dev = dev;
133181304Sjhb	sx_init(&sc->sc_lock, "iic");
13493023Snsouch	sc->sc_devnode = make_dev(&iic_cdevsw, device_get_unit(dev),
13553329Speter			UID_ROOT, GID_WHEEL,
13653329Speter			0600, "iic%d", device_get_unit(dev));
137181304Sjhb	if (sc->sc_devnode == NULL) {
138181304Sjhb		device_printf(dev, "failed to create character device\n");
139181304Sjhb		sx_destroy(&sc->sc_lock);
140181304Sjhb		return (ENXIO);
141181304Sjhb	}
142181304Sjhb	sc->sc_devnode->si_drv1 = sc;
143181304Sjhb
14438774Snsouch	return (0);
14538774Snsouch}
14638774Snsouch
14738774Snsouchstatic int
14893023Snsouchiic_detach(device_t dev)
14993023Snsouch{
15093023Snsouch	struct iic_softc *sc = (struct iic_softc *)device_get_softc(dev);
15193023Snsouch
15293023Snsouch	if (sc->sc_devnode)
15393023Snsouch		destroy_dev(sc->sc_devnode);
154181304Sjhb	sx_destroy(&sc->sc_lock);
15593023Snsouch
15693023Snsouch	return (0);
15793023Snsouch}
15893023Snsouch
15993023Snsouchstatic int
160157523Simpiicopen(struct cdev *dev, int flags, int fmt, struct thread *td)
16138774Snsouch{
162181304Sjhb	struct iic_softc *sc = dev->si_drv1;
16338774Snsouch
164181304Sjhb	IIC_LOCK(sc);
165181304Sjhb	if (sc->sc_count > 0) {
166181304Sjhb		IIC_UNLOCK(sc);
16738774Snsouch		return (EBUSY);
168181304Sjhb	}
16938774Snsouch
17038774Snsouch	sc->sc_count++;
171181304Sjhb	IIC_UNLOCK(sc);
17238774Snsouch
17338774Snsouch	return (0);
17438774Snsouch}
17538774Snsouch
17638774Snsouchstatic int
177130585Sphkiicclose(struct cdev *dev, int flags, int fmt, struct thread *td)
17838774Snsouch{
179181304Sjhb	struct iic_softc *sc = dev->si_drv1;
18038774Snsouch
181181304Sjhb	IIC_LOCK(sc);
182181304Sjhb	if (!sc->sc_count) {
183181304Sjhb		/* XXX: I don't think this can happen. */
184181304Sjhb		IIC_UNLOCK(sc);
18538774Snsouch		return (EINVAL);
186181304Sjhb	}
18738774Snsouch
18838774Snsouch	sc->sc_count--;
18938774Snsouch
19038774Snsouch	if (sc->sc_count < 0)
19187599Sobrien		panic("%s: iic_count < 0!", __func__);
192181304Sjhb	IIC_UNLOCK(sc);
19338774Snsouch
19438774Snsouch	return (0);
19538774Snsouch}
19638774Snsouch
19738774Snsouchstatic int
198130585Sphkiicwrite(struct cdev *dev, struct uio * uio, int ioflag)
19938774Snsouch{
200181304Sjhb	struct iic_softc *sc = dev->si_drv1;
201181304Sjhb	device_t iicdev = sc->sc_dev;
20238774Snsouch	int sent, error, count;
20338774Snsouch
204181304Sjhb	IIC_LOCK(sc);
205181304Sjhb	if (!sc->sc_addr) {
206181304Sjhb		IIC_UNLOCK(sc);
20738774Snsouch		return (EINVAL);
208181304Sjhb	}
20938774Snsouch
210181304Sjhb	if (sc->sc_count == 0) {
211181304Sjhb		/* XXX: I don't think this can happen. */
212181304Sjhb		IIC_UNLOCK(sc);
21338774Snsouch		return (EINVAL);
214181304Sjhb	}
21538774Snsouch
216181304Sjhb	error = iicbus_request_bus(device_get_parent(iicdev), iicdev,
217181304Sjhb	    IIC_DONTWAIT);
218181304Sjhb	if (error) {
219181304Sjhb		IIC_UNLOCK(sc);
22043976Snsouch		return (error);
221181304Sjhb	}
22243976Snsouch
22338774Snsouch	count = min(uio->uio_resid, BUFSIZE);
224242947Skevlo	error = uiomove(sc->sc_buffer, count, uio);
225242947Skevlo	if (error) {
226242947Skevlo		IIC_UNLOCK(sc);
227242947Skevlo		return (error);
228242947Skevlo	}
22938774Snsouch
23038774Snsouch	error = iicbus_block_write(device_get_parent(iicdev), sc->sc_addr,
23138774Snsouch					sc->sc_buffer, count, &sent);
23238774Snsouch
23343976Snsouch	iicbus_release_bus(device_get_parent(iicdev), iicdev);
234181304Sjhb	IIC_UNLOCK(sc);
23543976Snsouch
236181304Sjhb	return (error);
23738774Snsouch}
23838774Snsouch
23938774Snsouchstatic int
240130585Sphkiicread(struct cdev *dev, struct uio * uio, int ioflag)
24138774Snsouch{
242181304Sjhb	struct iic_softc *sc = dev->si_drv1;
243181304Sjhb	device_t iicdev = sc->sc_dev;
24438774Snsouch	int len, error = 0;
24538774Snsouch	int bufsize;
24638774Snsouch
247181304Sjhb	IIC_LOCK(sc);
248181304Sjhb	if (!sc->sc_addr) {
249181304Sjhb		IIC_UNLOCK(sc);
25038774Snsouch		return (EINVAL);
251181304Sjhb	}
25238774Snsouch
253181304Sjhb	if (sc->sc_count == 0) {
254181304Sjhb		/* XXX: I don't think this can happen. */
255181304Sjhb		IIC_UNLOCK(sc);
25638774Snsouch		return (EINVAL);
257181304Sjhb	}
25838774Snsouch
259181304Sjhb	error = iicbus_request_bus(device_get_parent(iicdev), iicdev,
260181304Sjhb	    IIC_DONTWAIT);
261181304Sjhb	if (error) {
262181304Sjhb		IIC_UNLOCK(sc);
26343976Snsouch		return (error);
264181304Sjhb	}
26543976Snsouch
26638774Snsouch	/* max amount of data to read */
26738774Snsouch	len = min(uio->uio_resid, BUFSIZE);
26838774Snsouch
269181304Sjhb	error = iicbus_block_read(device_get_parent(iicdev), sc->sc_addr,
270181304Sjhb	    sc->sc_inbuf, len, &bufsize);
271181304Sjhb	if (error) {
272181304Sjhb		IIC_UNLOCK(sc);
27338774Snsouch		return (error);
274181304Sjhb	}
27538774Snsouch
27638774Snsouch	if (bufsize > uio->uio_resid)
27787599Sobrien		panic("%s: too much data read!", __func__);
27838774Snsouch
27943976Snsouch	iicbus_release_bus(device_get_parent(iicdev), iicdev);
28043976Snsouch
281181304Sjhb	error = uiomove(sc->sc_inbuf, bufsize, uio);
282181304Sjhb	IIC_UNLOCK(sc);
283181304Sjhb	return (error);
28438774Snsouch}
28538774Snsouch
28638774Snsouchstatic int
287130585Sphkiicioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
28838774Snsouch{
289181304Sjhb	struct iic_softc *sc = dev->si_drv1;
290181304Sjhb	device_t iicdev = sc->sc_dev;
29138774Snsouch	device_t parent = device_get_parent(iicdev);
29242442Snsouch	struct iiccmd *s = (struct iiccmd *)data;
293160372Simp	struct iic_rdwr_data *d = (struct iic_rdwr_data *)data;
294160372Simp	struct iic_msg *m;
295160372Simp	int error, count, i;
296129291Sjoerg	char *buf = NULL;
297160372Simp	void **usrbufs = NULL;
29838774Snsouch
299167856Simp	if ((error = iicbus_request_bus(parent, iicdev,
300167856Simp	    (flags & O_NONBLOCK) ? IIC_DONTWAIT : (IIC_WAIT | IIC_INTR))))
30143976Snsouch		return (error);
30243976Snsouch
30338774Snsouch	switch (cmd) {
30438774Snsouch	case I2CSTART:
305181304Sjhb		IIC_LOCK(sc);
30642442Snsouch		error = iicbus_start(parent, s->slave, 0);
30793023Snsouch
30893023Snsouch		/*
30993023Snsouch		 * Implicitly set the chip addr to the slave addr passed as
31093023Snsouch		 * parameter. Consequently, start/stop shall be called before
31193023Snsouch		 * the read or the write of a block.
31293023Snsouch		 */
31393023Snsouch		if (!error)
31493023Snsouch			sc->sc_addr = s->slave;
315181304Sjhb		IIC_UNLOCK(sc);
31693023Snsouch
31738774Snsouch		break;
31838774Snsouch
31938774Snsouch	case I2CSTOP:
32038774Snsouch		error = iicbus_stop(parent);
32138774Snsouch		break;
32238774Snsouch
32338774Snsouch	case I2CRSTCARD:
324157482Simp		error = iicbus_reset(parent, IIC_UNKNOWN, 0, NULL);
325270241Sloos		/*
326270241Sloos		 * Ignore IIC_ENOADDR as it only means we have a master-only
327270241Sloos		 * controller.
328270241Sloos		 */
329270241Sloos		if (error == IIC_ENOADDR)
330270241Sloos			error = 0;
33138774Snsouch		break;
33238774Snsouch
33342442Snsouch	case I2CWRITE:
334129291Sjoerg		if (s->count <= 0) {
335129291Sjoerg			error = EINVAL;
336129291Sjoerg			break;
337129291Sjoerg		}
338129291Sjoerg		buf = malloc((unsigned long)s->count, M_TEMP, M_WAITOK);
339129291Sjoerg		error = copyin(s->buf, buf, s->count);
340129291Sjoerg		if (error)
341129291Sjoerg			break;
342129291Sjoerg		error = iicbus_write(parent, buf, s->count, &count, 10);
34342442Snsouch		break;
34442442Snsouch
34542442Snsouch	case I2CREAD:
346129291Sjoerg		if (s->count <= 0) {
347129291Sjoerg			error = EINVAL;
348129291Sjoerg			break;
349129291Sjoerg		}
350129291Sjoerg		buf = malloc((unsigned long)s->count, M_TEMP, M_WAITOK);
351129291Sjoerg		error = iicbus_read(parent, buf, s->count, &count, s->last, 10);
352129291Sjoerg		if (error)
353129291Sjoerg			break;
354129291Sjoerg		error = copyout(buf, s->buf, s->count);
35542442Snsouch		break;
35642442Snsouch
357160372Simp	case I2CRDWR:
358160372Simp		buf = malloc(sizeof(*d->msgs) * d->nmsgs, M_TEMP, M_WAITOK);
359160372Simp		error = copyin(d->msgs, buf, sizeof(*d->msgs) * d->nmsgs);
360226442Sbrueffer		if (error)
361160372Simp			break;
362164501Simp		/* Alloc kernel buffers for userland data, copyin write data */
363226442Sbrueffer		usrbufs = malloc(sizeof(void *) * d->nmsgs, M_TEMP, M_ZERO | M_WAITOK);
364160372Simp		for (i = 0; i < d->nmsgs; i++) {
365160372Simp			m = &((struct iic_msg *)buf)[i];
366160372Simp			usrbufs[i] = m->buf;
367160372Simp			m->buf = malloc(m->len, M_TEMP, M_WAITOK);
368160372Simp			if (!(m->flags & IIC_M_RD))
369160372Simp				copyin(usrbufs[i], m->buf, m->len);
370160372Simp		}
371167856Simp		error = iicbus_transfer(iicdev, (struct iic_msg *)buf, d->nmsgs);
372160372Simp		/* Copyout all read segments, free up kernel buffers */
373160372Simp		for (i = 0; i < d->nmsgs; i++) {
374160372Simp			m = &((struct iic_msg *)buf)[i];
375164501Simp			if (m->flags & IIC_M_RD)
376160372Simp				copyout(m->buf, usrbufs[i], m->len);
377160372Simp			free(m->buf, M_TEMP);
378160372Simp		}
379160372Simp		free(usrbufs, M_TEMP);
380160372Simp		break;
381187709Sraj
382187709Sraj	case I2CRPTSTART:
383187709Sraj		error = iicbus_repeated_start(parent, s->slave, 0);
384187709Sraj		break;
385187709Sraj
38638774Snsouch	default:
387129291Sjoerg		error = ENOTTY;
38838774Snsouch	}
38938774Snsouch
390167856Simp	iicbus_release_bus(parent, iicdev);
39143976Snsouch
392129291Sjoerg	if (buf != NULL)
393129291Sjoerg		free(buf, M_TEMP);
39438774Snsouch	return (error);
39538774Snsouch}
39638774Snsouch
39753005SpeterDRIVER_MODULE(iic, iicbus, iic_driver, iic_devclass, 0, 0);
39893023SnsouchMODULE_DEPEND(iic, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
39993023SnsouchMODULE_VERSION(iic, 1);
400