xref: /freebsd/sys/dev/smbus/smb.c (revision 68b2efbd3b74f0d45bbbf07cef5408e455eefbd1)
1d70424edSNicolas Souchu /*-
2c17d4340SNicolas Souchu  * Copyright (c) 1998, 2001 Nicolas Souchu
3d70424edSNicolas Souchu  * All rights reserved.
4d70424edSNicolas Souchu  *
5d70424edSNicolas Souchu  * Redistribution and use in source and binary forms, with or without
6d70424edSNicolas Souchu  * modification, are permitted provided that the following conditions
7d70424edSNicolas Souchu  * are met:
8d70424edSNicolas Souchu  * 1. Redistributions of source code must retain the above copyright
9d70424edSNicolas Souchu  *    notice, this list of conditions and the following disclaimer.
10d70424edSNicolas Souchu  * 2. Redistributions in binary form must reproduce the above copyright
11d70424edSNicolas Souchu  *    notice, this list of conditions and the following disclaimer in the
12d70424edSNicolas Souchu  *    documentation and/or other materials provided with the distribution.
13d70424edSNicolas Souchu  *
14d70424edSNicolas Souchu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15d70424edSNicolas Souchu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16d70424edSNicolas Souchu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17d70424edSNicolas Souchu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18d70424edSNicolas Souchu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19d70424edSNicolas Souchu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20d70424edSNicolas Souchu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21d70424edSNicolas Souchu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22d70424edSNicolas Souchu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23d70424edSNicolas Souchu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24d70424edSNicolas Souchu  * SUCH DAMAGE.
25d70424edSNicolas Souchu  *
26c3aac50fSPeter Wemm  * $FreeBSD$
27d70424edSNicolas Souchu  */
28b3dd2b7bSJoerg Wunsch 
29d70424edSNicolas Souchu #include <sys/param.h>
30d70424edSNicolas Souchu #include <sys/kernel.h>
31d70424edSNicolas Souchu #include <sys/systm.h>
32d70424edSNicolas Souchu #include <sys/module.h>
33d70424edSNicolas Souchu #include <sys/bus.h>
34d70424edSNicolas Souchu #include <sys/conf.h>
35d70424edSNicolas Souchu #include <sys/uio.h>
36ba81c311SNicolas Souchu #include <sys/fcntl.h>
37d70424edSNicolas Souchu 
38d70424edSNicolas Souchu #include <dev/smbus/smbconf.h>
39d70424edSNicolas Souchu #include <dev/smbus/smbus.h>
40531facbaSPeter Wemm #include <dev/smbus/smb.h>
41d70424edSNicolas Souchu 
42d70424edSNicolas Souchu #include "smbus_if.h"
43d70424edSNicolas Souchu 
44f43618f5SAndriy Gapon #define SMB_OLD_READB	_IOW('i', 7, struct smbcmd)
45f43618f5SAndriy Gapon #define SMB_OLD_READW	_IOW('i', 8, struct smbcmd)
46f43618f5SAndriy Gapon #define SMB_OLD_PCALL	_IOW('i', 9, struct smbcmd)
47d70424edSNicolas Souchu 
48d70424edSNicolas Souchu struct smb_softc {
49697218c9SJohn Baldwin 	device_t sc_dev;
5089c9c53dSPoul-Henning Kamp 	struct cdev *sc_devnode;
51d70424edSNicolas Souchu };
52d70424edSNicolas Souchu 
537048a99cSJohn Baldwin static void smb_identify(driver_t *driver, device_t parent);
54d70424edSNicolas Souchu static int smb_probe(device_t);
55d70424edSNicolas Souchu static int smb_attach(device_t);
56c17d4340SNicolas Souchu static int smb_detach(device_t);
57d70424edSNicolas Souchu 
58d70424edSNicolas Souchu static devclass_t smb_devclass;
59d70424edSNicolas Souchu 
60d70424edSNicolas Souchu static device_method_t smb_methods[] = {
61d70424edSNicolas Souchu 	/* device interface */
627048a99cSJohn Baldwin 	DEVMETHOD(device_identify,	smb_identify),
63d70424edSNicolas Souchu 	DEVMETHOD(device_probe,		smb_probe),
64d70424edSNicolas Souchu 	DEVMETHOD(device_attach,	smb_attach),
65c17d4340SNicolas Souchu 	DEVMETHOD(device_detach,	smb_detach),
66d70424edSNicolas Souchu 
67d70424edSNicolas Souchu 	/* smbus interface */
68d70424edSNicolas Souchu 	DEVMETHOD(smbus_intr,		smbus_generic_intr),
69d70424edSNicolas Souchu 
70d70424edSNicolas Souchu 	{ 0, 0 }
71d70424edSNicolas Souchu };
72d70424edSNicolas Souchu 
73d70424edSNicolas Souchu static driver_t smb_driver = {
74d70424edSNicolas Souchu 	"smb",
75d70424edSNicolas Souchu 	smb_methods,
76d70424edSNicolas Souchu 	sizeof(struct smb_softc),
77d70424edSNicolas Souchu };
78d70424edSNicolas Souchu 
79d70424edSNicolas Souchu static	d_ioctl_t	smbioctl;
80d70424edSNicolas Souchu 
814e2f199eSPoul-Henning Kamp static struct cdevsw smb_cdevsw = {
82dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
83697218c9SJohn Baldwin 	.d_flags =	D_TRACKCLOSE,
847ac40f5fSPoul-Henning Kamp 	.d_ioctl =	smbioctl,
857ac40f5fSPoul-Henning Kamp 	.d_name =	"smb",
864e2f199eSPoul-Henning Kamp };
87d70424edSNicolas Souchu 
887048a99cSJohn Baldwin static void
897048a99cSJohn Baldwin smb_identify(driver_t *driver, device_t parent)
907048a99cSJohn Baldwin {
917048a99cSJohn Baldwin 
927048a99cSJohn Baldwin 	if (device_find_child(parent, "smb", -1) == NULL)
937048a99cSJohn Baldwin 		BUS_ADD_CHILD(parent, 0, "smb", -1);
947048a99cSJohn Baldwin }
957048a99cSJohn Baldwin 
96d70424edSNicolas Souchu static int
97d70424edSNicolas Souchu smb_probe(device_t dev)
98d70424edSNicolas Souchu {
99202379afSMichael Gmelin 	if (smbus_get_addr(dev) != -1)
100202379afSMichael Gmelin 		return (ENXIO);
101d70424edSNicolas Souchu 
102202379afSMichael Gmelin 	device_set_desc(dev, "SMBus generic I/O");
103202379afSMichael Gmelin 	return (BUS_PROBE_NOWILDCARD);
104d70424edSNicolas Souchu }
105d70424edSNicolas Souchu 
106d70424edSNicolas Souchu static int
107d70424edSNicolas Souchu smb_attach(device_t dev)
108d70424edSNicolas Souchu {
109*68b2efbdSKonstantin Belousov 	struct smb_softc *sc;
110*68b2efbdSKonstantin Belousov 	struct make_dev_args mda;
111*68b2efbdSKonstantin Belousov 	int error;
112c17d4340SNicolas Souchu 
113*68b2efbdSKonstantin Belousov 	sc = device_get_softc(dev);
114697218c9SJohn Baldwin 	sc->sc_dev = dev;
115202379afSMichael Gmelin 
116*68b2efbdSKonstantin Belousov 	make_dev_args_init(&mda);
117*68b2efbdSKonstantin Belousov 	mda.mda_devsw = &smb_cdevsw;
118*68b2efbdSKonstantin Belousov 	mda.mda_unit = device_get_unit(dev);
119*68b2efbdSKonstantin Belousov 	mda.mda_uid = UID_ROOT;
120*68b2efbdSKonstantin Belousov 	mda.mda_gid = GID_WHEEL;
121*68b2efbdSKonstantin Belousov 	mda.mda_mode = 0600;
122*68b2efbdSKonstantin Belousov 	mda.mda_si_drv1 = sc;
123*68b2efbdSKonstantin Belousov 	error = make_dev_s(&mda, &sc->sc_devnode, "smb%d", mda.mda_unit);
124*68b2efbdSKonstantin Belousov 	return (error);
125c17d4340SNicolas Souchu }
126c17d4340SNicolas Souchu 
127c17d4340SNicolas Souchu static int
128c17d4340SNicolas Souchu smb_detach(device_t dev)
129c17d4340SNicolas Souchu {
130*68b2efbdSKonstantin Belousov 	struct smb_softc *sc;
131c17d4340SNicolas Souchu 
132*68b2efbdSKonstantin Belousov 	sc = device_get_softc(dev);
133c17d4340SNicolas Souchu 	destroy_dev(sc->sc_devnode);
134d70424edSNicolas Souchu 	return (0);
135d70424edSNicolas Souchu }
136d70424edSNicolas Souchu 
137d70424edSNicolas Souchu static int
13889c9c53dSPoul-Henning Kamp smbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
139d70424edSNicolas Souchu {
140add37e1eSJoerg Wunsch 	char buf[SMB_MAXBLOCKSIZE];
141b3dd2b7bSJoerg Wunsch 	device_t parent;
142d70424edSNicolas Souchu 	struct smbcmd *s = (struct smbcmd *)data;
143697218c9SJohn Baldwin 	struct smb_softc *sc = dev->si_drv1;
144697218c9SJohn Baldwin 	device_t smbdev = sc->sc_dev;
145b3dd2b7bSJoerg Wunsch 	int error;
146202379afSMichael Gmelin 	int unit;
147202379afSMichael Gmelin 	u_char bcount;
148202379afSMichael Gmelin 
149202379afSMichael Gmelin 	/*
150202379afSMichael Gmelin 	 * If a specific slave device is being used, override any passed-in
151202379afSMichael Gmelin 	 * slave.
152202379afSMichael Gmelin 	 */
153202379afSMichael Gmelin 	unit = dev2unit(dev);
154202379afSMichael Gmelin 	if (unit & 0x0400)
155202379afSMichael Gmelin 		s->slave = unit & 0x03ff;
156d70424edSNicolas Souchu 
157b3dd2b7bSJoerg Wunsch 	parent = device_get_parent(smbdev);
158b3dd2b7bSJoerg Wunsch 
159bb6bb7feSJohn Baldwin 	/* Make sure that LSB bit is cleared. */
160bb6bb7feSJohn Baldwin 	if (s->slave & 0x1)
161bb6bb7feSJohn Baldwin 		return (EINVAL);
162bb6bb7feSJohn Baldwin 
163b3dd2b7bSJoerg Wunsch 	/* Allocate the bus. */
164ba81c311SNicolas Souchu 	if ((error = smbus_request_bus(parent, smbdev,
165ba81c311SNicolas Souchu 			(flags & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR))))
166ba81c311SNicolas Souchu 		return (error);
167ba81c311SNicolas Souchu 
168d70424edSNicolas Souchu 	switch (cmd) {
169d70424edSNicolas Souchu 	case SMB_QUICK_WRITE:
1704012f363SNicolas Souchu 		error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE));
171ba81c311SNicolas Souchu 		break;
172d70424edSNicolas Souchu 
173d70424edSNicolas Souchu 	case SMB_QUICK_READ:
1744012f363SNicolas Souchu 		error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD));
175ba81c311SNicolas Souchu 		break;
176d70424edSNicolas Souchu 
177d70424edSNicolas Souchu 	case SMB_SENDB:
1784012f363SNicolas Souchu 		error = smbus_error(smbus_sendb(parent, s->slave, s->cmd));
179d70424edSNicolas Souchu 		break;
180d70424edSNicolas Souchu 
181d70424edSNicolas Souchu 	case SMB_RECVB:
1824012f363SNicolas Souchu 		error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd));
183d70424edSNicolas Souchu 		break;
184d70424edSNicolas Souchu 
185d70424edSNicolas Souchu 	case SMB_WRITEB:
1864012f363SNicolas Souchu 		error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
187202379afSMichael Gmelin 						s->wdata.byte));
188d70424edSNicolas Souchu 		break;
189d70424edSNicolas Souchu 
190d70424edSNicolas Souchu 	case SMB_WRITEW:
1914012f363SNicolas Souchu 		error = smbus_error(smbus_writew(parent, s->slave,
192202379afSMichael Gmelin 						s->cmd, s->wdata.word));
193d70424edSNicolas Souchu 		break;
194d70424edSNicolas Souchu 
195f43618f5SAndriy Gapon 	case SMB_OLD_READB:
196d70424edSNicolas Souchu 	case SMB_READB:
197f43618f5SAndriy Gapon 		/* NB: for SMB_OLD_READB the read data goes to rbuf only. */
198202379afSMichael Gmelin 		error = smbus_error(smbus_readb(parent, s->slave, s->cmd,
199202379afSMichael Gmelin 		    &s->rdata.byte));
200add37e1eSJoerg Wunsch 		if (error)
201add37e1eSJoerg Wunsch 			break;
202202379afSMichael Gmelin 		if (s->rbuf && s->rcount >= 1) {
203202379afSMichael Gmelin 			error = copyout(&s->rdata.byte, s->rbuf, 1);
204202379afSMichael Gmelin 			s->rcount = 1;
205add37e1eSJoerg Wunsch 		}
206d70424edSNicolas Souchu 		break;
207d70424edSNicolas Souchu 
208f43618f5SAndriy Gapon 	case SMB_OLD_READW:
209d70424edSNicolas Souchu 	case SMB_READW:
210f43618f5SAndriy Gapon 		/* NB: for SMB_OLD_READW the read data goes to rbuf only. */
211202379afSMichael Gmelin 		error = smbus_error(smbus_readw(parent, s->slave, s->cmd,
212202379afSMichael Gmelin 		    &s->rdata.word));
213202379afSMichael Gmelin 		if (error)
214202379afSMichael Gmelin 			break;
215202379afSMichael Gmelin 		if (s->rbuf && s->rcount >= 2) {
216202379afSMichael Gmelin 			buf[0] = (u_char)s->rdata.word;
217202379afSMichael Gmelin 			buf[1] = (u_char)(s->rdata.word >> 8);
218202379afSMichael Gmelin 			error = copyout(buf, s->rbuf, 2);
219202379afSMichael Gmelin 			s->rcount = 2;
220add37e1eSJoerg Wunsch 		}
221d70424edSNicolas Souchu 		break;
222d70424edSNicolas Souchu 
223f43618f5SAndriy Gapon 	case SMB_OLD_PCALL:
224d70424edSNicolas Souchu 	case SMB_PCALL:
225f43618f5SAndriy Gapon 		/* NB: for SMB_OLD_PCALL the read data goes to rbuf only. */
2264012f363SNicolas Souchu 		error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
227202379afSMichael Gmelin 		    s->wdata.word, &s->rdata.word));
228add37e1eSJoerg Wunsch 		if (error)
229add37e1eSJoerg Wunsch 			break;
230202379afSMichael Gmelin 		if (s->rbuf && s->rcount >= 2) {
231202379afSMichael Gmelin 			buf[0] = (u_char)s->rdata.word;
232202379afSMichael Gmelin 			buf[1] = (u_char)(s->rdata.word >> 8);
233202379afSMichael Gmelin 			error = copyout(buf, s->rbuf, 2);
234202379afSMichael Gmelin 			s->rcount = 2;
235add37e1eSJoerg Wunsch 		}
236add37e1eSJoerg Wunsch 
237d70424edSNicolas Souchu 		break;
238d70424edSNicolas Souchu 
239d70424edSNicolas Souchu 	case SMB_BWRITE:
240202379afSMichael Gmelin 		if (s->wcount < 0) {
241202379afSMichael Gmelin 			error = EINVAL;
242202379afSMichael Gmelin 			break;
243202379afSMichael Gmelin 		}
244202379afSMichael Gmelin 		if (s->wcount > SMB_MAXBLOCKSIZE)
245202379afSMichael Gmelin 			s->wcount = SMB_MAXBLOCKSIZE;
246202379afSMichael Gmelin 		if (s->wcount)
247202379afSMichael Gmelin 			error = copyin(s->wbuf, buf, s->wcount);
248add37e1eSJoerg Wunsch 		if (error)
249add37e1eSJoerg Wunsch 			break;
250202379afSMichael Gmelin 		error = smbus_error(smbus_bwrite(parent, s->slave, s->cmd,
251202379afSMichael Gmelin 		    s->wcount, buf));
252d70424edSNicolas Souchu 		break;
253d70424edSNicolas Souchu 
254d70424edSNicolas Souchu 	case SMB_BREAD:
255202379afSMichael Gmelin 		if (s->rcount < 0) {
256202379afSMichael Gmelin 			error = EINVAL;
257202379afSMichael Gmelin 			break;
258202379afSMichael Gmelin 		}
259202379afSMichael Gmelin 		if (s->rcount > SMB_MAXBLOCKSIZE)
260202379afSMichael Gmelin 			s->rcount = SMB_MAXBLOCKSIZE;
261202379afSMichael Gmelin 		error = smbus_error(smbus_bread(parent, s->slave, s->cmd,
262202379afSMichael Gmelin 		    &bcount, buf));
263add37e1eSJoerg Wunsch 		if (error)
264add37e1eSJoerg Wunsch 			break;
265202379afSMichael Gmelin 		if (s->rcount > bcount)
266202379afSMichael Gmelin 			s->rcount = bcount;
267202379afSMichael Gmelin 		error = copyout(buf, s->rbuf, s->rcount);
268d70424edSNicolas Souchu 		break;
269d70424edSNicolas Souchu 
270d70424edSNicolas Souchu 	default:
271add37e1eSJoerg Wunsch 		error = ENOTTY;
272d70424edSNicolas Souchu 	}
273d70424edSNicolas Souchu 
274ba81c311SNicolas Souchu 	smbus_release_bus(parent, smbdev);
275ba81c311SNicolas Souchu 
276d70424edSNicolas Souchu 	return (error);
277d70424edSNicolas Souchu }
278d70424edSNicolas Souchu 
279c6b92decSPeter Wemm DRIVER_MODULE(smb, smbus, smb_driver, smb_devclass, 0, 0);
280c17d4340SNicolas Souchu MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
281c17d4340SNicolas Souchu MODULE_VERSION(smb, 1);
282