xref: /freebsd/sys/dev/smbus/smb.c (revision 8d1611432f425a97714543af30379f6ad9ed4b7c)
1d70424edSNicolas Souchu /*-
2718cf2ccSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3718cf2ccSPedro F. Giffuni  *
4c17d4340SNicolas Souchu  * Copyright (c) 1998, 2001 Nicolas Souchu
5d70424edSNicolas Souchu  * All rights reserved.
6d70424edSNicolas Souchu  *
7d70424edSNicolas Souchu  * Redistribution and use in source and binary forms, with or without
8d70424edSNicolas Souchu  * modification, are permitted provided that the following conditions
9d70424edSNicolas Souchu  * are met:
10d70424edSNicolas Souchu  * 1. Redistributions of source code must retain the above copyright
11d70424edSNicolas Souchu  *    notice, this list of conditions and the following disclaimer.
12d70424edSNicolas Souchu  * 2. Redistributions in binary form must reproduce the above copyright
13d70424edSNicolas Souchu  *    notice, this list of conditions and the following disclaimer in the
14d70424edSNicolas Souchu  *    documentation and/or other materials provided with the distribution.
15d70424edSNicolas Souchu  *
16d70424edSNicolas Souchu  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17d70424edSNicolas Souchu  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18d70424edSNicolas Souchu  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19d70424edSNicolas Souchu  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20d70424edSNicolas Souchu  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21d70424edSNicolas Souchu  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22d70424edSNicolas Souchu  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23d70424edSNicolas Souchu  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24d70424edSNicolas Souchu  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25d70424edSNicolas Souchu  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26d70424edSNicolas Souchu  * SUCH DAMAGE.
27d70424edSNicolas Souchu  *
28c3aac50fSPeter Wemm  * $FreeBSD$
29d70424edSNicolas Souchu  */
30b3dd2b7bSJoerg Wunsch 
31d70424edSNicolas Souchu #include <sys/param.h>
32d70424edSNicolas Souchu #include <sys/kernel.h>
33d70424edSNicolas Souchu #include <sys/systm.h>
34d70424edSNicolas Souchu #include <sys/module.h>
35d70424edSNicolas Souchu #include <sys/bus.h>
36d70424edSNicolas Souchu #include <sys/conf.h>
37d70424edSNicolas Souchu #include <sys/uio.h>
38ba81c311SNicolas Souchu #include <sys/fcntl.h>
39d70424edSNicolas Souchu 
40d70424edSNicolas Souchu #include <dev/smbus/smbconf.h>
41d70424edSNicolas Souchu #include <dev/smbus/smbus.h>
42531facbaSPeter Wemm #include <dev/smbus/smb.h>
43d70424edSNicolas Souchu 
44d70424edSNicolas Souchu #include "smbus_if.h"
45d70424edSNicolas Souchu 
46f43618f5SAndriy Gapon #define SMB_OLD_READB	_IOW('i', 7, struct smbcmd)
47f43618f5SAndriy Gapon #define SMB_OLD_READW	_IOW('i', 8, struct smbcmd)
48f43618f5SAndriy Gapon #define SMB_OLD_PCALL	_IOW('i', 9, struct smbcmd)
49d70424edSNicolas Souchu 
50d70424edSNicolas Souchu struct smb_softc {
51697218c9SJohn Baldwin 	device_t sc_dev;
5289c9c53dSPoul-Henning Kamp 	struct cdev *sc_devnode;
53d70424edSNicolas Souchu };
54d70424edSNicolas Souchu 
557048a99cSJohn Baldwin static void smb_identify(driver_t *driver, device_t parent);
56d70424edSNicolas Souchu static int smb_probe(device_t);
57d70424edSNicolas Souchu static int smb_attach(device_t);
58c17d4340SNicolas Souchu static int smb_detach(device_t);
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 	{ 0, 0 }
70d70424edSNicolas Souchu };
71d70424edSNicolas Souchu 
72d70424edSNicolas Souchu static driver_t smb_driver = {
73d70424edSNicolas Souchu 	"smb",
74d70424edSNicolas Souchu 	smb_methods,
75d70424edSNicolas Souchu 	sizeof(struct smb_softc),
76d70424edSNicolas Souchu };
77d70424edSNicolas Souchu 
78d70424edSNicolas Souchu static	d_ioctl_t	smbioctl;
79d70424edSNicolas Souchu 
804e2f199eSPoul-Henning Kamp static struct cdevsw smb_cdevsw = {
81dc08ffecSPoul-Henning Kamp 	.d_version =	D_VERSION,
82697218c9SJohn Baldwin 	.d_flags =	D_TRACKCLOSE,
837ac40f5fSPoul-Henning Kamp 	.d_ioctl =	smbioctl,
847ac40f5fSPoul-Henning Kamp 	.d_name =	"smb",
854e2f199eSPoul-Henning Kamp };
86d70424edSNicolas Souchu 
877048a99cSJohn Baldwin static void
887048a99cSJohn Baldwin smb_identify(driver_t *driver, device_t parent)
897048a99cSJohn Baldwin {
907048a99cSJohn Baldwin 
917048a99cSJohn Baldwin 	if (device_find_child(parent, "smb", -1) == NULL)
927048a99cSJohn Baldwin 		BUS_ADD_CHILD(parent, 0, "smb", -1);
937048a99cSJohn Baldwin }
947048a99cSJohn Baldwin 
95d70424edSNicolas Souchu static int
96d70424edSNicolas Souchu smb_probe(device_t dev)
97d70424edSNicolas Souchu {
98202379afSMichael Gmelin 	if (smbus_get_addr(dev) != -1)
99202379afSMichael Gmelin 		return (ENXIO);
100d70424edSNicolas Souchu 
101202379afSMichael Gmelin 	device_set_desc(dev, "SMBus generic I/O");
102202379afSMichael Gmelin 	return (BUS_PROBE_NOWILDCARD);
103d70424edSNicolas Souchu }
104d70424edSNicolas Souchu 
105d70424edSNicolas Souchu static int
106d70424edSNicolas Souchu smb_attach(device_t dev)
107d70424edSNicolas Souchu {
10868b2efbdSKonstantin Belousov 	struct smb_softc *sc;
10968b2efbdSKonstantin Belousov 	struct make_dev_args mda;
11068b2efbdSKonstantin Belousov 	int error;
111c17d4340SNicolas Souchu 
11268b2efbdSKonstantin Belousov 	sc = device_get_softc(dev);
113697218c9SJohn Baldwin 	sc->sc_dev = dev;
114202379afSMichael Gmelin 
11568b2efbdSKonstantin Belousov 	make_dev_args_init(&mda);
11668b2efbdSKonstantin Belousov 	mda.mda_devsw = &smb_cdevsw;
11768b2efbdSKonstantin Belousov 	mda.mda_unit = device_get_unit(dev);
11868b2efbdSKonstantin Belousov 	mda.mda_uid = UID_ROOT;
11968b2efbdSKonstantin Belousov 	mda.mda_gid = GID_WHEEL;
12068b2efbdSKonstantin Belousov 	mda.mda_mode = 0600;
12168b2efbdSKonstantin Belousov 	mda.mda_si_drv1 = sc;
12268b2efbdSKonstantin Belousov 	error = make_dev_s(&mda, &sc->sc_devnode, "smb%d", mda.mda_unit);
12368b2efbdSKonstantin Belousov 	return (error);
124c17d4340SNicolas Souchu }
125c17d4340SNicolas Souchu 
126c17d4340SNicolas Souchu static int
127c17d4340SNicolas Souchu smb_detach(device_t dev)
128c17d4340SNicolas Souchu {
12968b2efbdSKonstantin Belousov 	struct smb_softc *sc;
130c17d4340SNicolas Souchu 
13168b2efbdSKonstantin Belousov 	sc = device_get_softc(dev);
132c17d4340SNicolas Souchu 	destroy_dev(sc->sc_devnode);
133d70424edSNicolas Souchu 	return (0);
134d70424edSNicolas Souchu }
135d70424edSNicolas Souchu 
136d70424edSNicolas Souchu static int
13789c9c53dSPoul-Henning Kamp smbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
138d70424edSNicolas Souchu {
139add37e1eSJoerg Wunsch 	char buf[SMB_MAXBLOCKSIZE];
140b3dd2b7bSJoerg Wunsch 	device_t parent;
141d70424edSNicolas Souchu 	struct smbcmd *s = (struct smbcmd *)data;
142697218c9SJohn Baldwin 	struct smb_softc *sc = dev->si_drv1;
143697218c9SJohn Baldwin 	device_t smbdev = sc->sc_dev;
144b3dd2b7bSJoerg Wunsch 	int error;
145202379afSMichael Gmelin 	int unit;
146202379afSMichael Gmelin 	u_char bcount;
147202379afSMichael Gmelin 
148202379afSMichael Gmelin 	/*
149202379afSMichael Gmelin 	 * If a specific slave device is being used, override any passed-in
150202379afSMichael Gmelin 	 * slave.
151202379afSMichael Gmelin 	 */
152202379afSMichael Gmelin 	unit = dev2unit(dev);
153202379afSMichael Gmelin 	if (unit & 0x0400)
154202379afSMichael Gmelin 		s->slave = unit & 0x03ff;
155d70424edSNicolas Souchu 
156b3dd2b7bSJoerg Wunsch 	parent = device_get_parent(smbdev);
157b3dd2b7bSJoerg Wunsch 
158bb6bb7feSJohn Baldwin 	/* Make sure that LSB bit is cleared. */
159bb6bb7feSJohn Baldwin 	if (s->slave & 0x1)
160bb6bb7feSJohn Baldwin 		return (EINVAL);
161bb6bb7feSJohn Baldwin 
162b3dd2b7bSJoerg Wunsch 	/* Allocate the bus. */
163ba81c311SNicolas Souchu 	if ((error = smbus_request_bus(parent, smbdev,
164ba81c311SNicolas Souchu 			(flags & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR))))
165ba81c311SNicolas Souchu 		return (error);
166ba81c311SNicolas Souchu 
167d70424edSNicolas Souchu 	switch (cmd) {
168d70424edSNicolas Souchu 	case SMB_QUICK_WRITE:
1694012f363SNicolas Souchu 		error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE));
170ba81c311SNicolas Souchu 		break;
171d70424edSNicolas Souchu 
172d70424edSNicolas Souchu 	case SMB_QUICK_READ:
1734012f363SNicolas Souchu 		error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD));
174ba81c311SNicolas Souchu 		break;
175d70424edSNicolas Souchu 
176d70424edSNicolas Souchu 	case SMB_SENDB:
1774012f363SNicolas Souchu 		error = smbus_error(smbus_sendb(parent, s->slave, s->cmd));
178d70424edSNicolas Souchu 		break;
179d70424edSNicolas Souchu 
180d70424edSNicolas Souchu 	case SMB_RECVB:
1814012f363SNicolas Souchu 		error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd));
182d70424edSNicolas Souchu 		break;
183d70424edSNicolas Souchu 
184d70424edSNicolas Souchu 	case SMB_WRITEB:
1854012f363SNicolas Souchu 		error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
186202379afSMichael Gmelin 						s->wdata.byte));
187d70424edSNicolas Souchu 		break;
188d70424edSNicolas Souchu 
189d70424edSNicolas Souchu 	case SMB_WRITEW:
1904012f363SNicolas Souchu 		error = smbus_error(smbus_writew(parent, s->slave,
191202379afSMichael Gmelin 						s->cmd, s->wdata.word));
192d70424edSNicolas Souchu 		break;
193d70424edSNicolas Souchu 
194f43618f5SAndriy Gapon 	case SMB_OLD_READB:
195d70424edSNicolas Souchu 	case SMB_READB:
196f43618f5SAndriy Gapon 		/* NB: for SMB_OLD_READB the read data goes to rbuf only. */
197202379afSMichael Gmelin 		error = smbus_error(smbus_readb(parent, s->slave, s->cmd,
198202379afSMichael Gmelin 		    &s->rdata.byte));
199add37e1eSJoerg Wunsch 		if (error)
200add37e1eSJoerg Wunsch 			break;
201202379afSMichael Gmelin 		if (s->rbuf && s->rcount >= 1) {
202202379afSMichael Gmelin 			error = copyout(&s->rdata.byte, s->rbuf, 1);
203202379afSMichael Gmelin 			s->rcount = 1;
204add37e1eSJoerg Wunsch 		}
205d70424edSNicolas Souchu 		break;
206d70424edSNicolas Souchu 
207f43618f5SAndriy Gapon 	case SMB_OLD_READW:
208d70424edSNicolas Souchu 	case SMB_READW:
209f43618f5SAndriy Gapon 		/* NB: for SMB_OLD_READW the read data goes to rbuf only. */
210202379afSMichael Gmelin 		error = smbus_error(smbus_readw(parent, s->slave, s->cmd,
211202379afSMichael Gmelin 		    &s->rdata.word));
212202379afSMichael Gmelin 		if (error)
213202379afSMichael Gmelin 			break;
214202379afSMichael Gmelin 		if (s->rbuf && s->rcount >= 2) {
215202379afSMichael Gmelin 			buf[0] = (u_char)s->rdata.word;
216202379afSMichael Gmelin 			buf[1] = (u_char)(s->rdata.word >> 8);
217202379afSMichael Gmelin 			error = copyout(buf, s->rbuf, 2);
218202379afSMichael Gmelin 			s->rcount = 2;
219add37e1eSJoerg Wunsch 		}
220d70424edSNicolas Souchu 		break;
221d70424edSNicolas Souchu 
222f43618f5SAndriy Gapon 	case SMB_OLD_PCALL:
223d70424edSNicolas Souchu 	case SMB_PCALL:
224f43618f5SAndriy Gapon 		/* NB: for SMB_OLD_PCALL the read data goes to rbuf only. */
2254012f363SNicolas Souchu 		error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
226202379afSMichael Gmelin 		    s->wdata.word, &s->rdata.word));
227add37e1eSJoerg Wunsch 		if (error)
228add37e1eSJoerg Wunsch 			break;
229202379afSMichael Gmelin 		if (s->rbuf && s->rcount >= 2) {
230202379afSMichael Gmelin 			buf[0] = (u_char)s->rdata.word;
231202379afSMichael Gmelin 			buf[1] = (u_char)(s->rdata.word >> 8);
232202379afSMichael Gmelin 			error = copyout(buf, s->rbuf, 2);
233202379afSMichael Gmelin 			s->rcount = 2;
234add37e1eSJoerg Wunsch 		}
235add37e1eSJoerg Wunsch 
236d70424edSNicolas Souchu 		break;
237d70424edSNicolas Souchu 
238d70424edSNicolas Souchu 	case SMB_BWRITE:
239202379afSMichael Gmelin 		if (s->wcount < 0) {
240202379afSMichael Gmelin 			error = EINVAL;
241202379afSMichael Gmelin 			break;
242202379afSMichael Gmelin 		}
243202379afSMichael Gmelin 		if (s->wcount > SMB_MAXBLOCKSIZE)
244202379afSMichael Gmelin 			s->wcount = SMB_MAXBLOCKSIZE;
245202379afSMichael Gmelin 		if (s->wcount)
246202379afSMichael Gmelin 			error = copyin(s->wbuf, buf, s->wcount);
247add37e1eSJoerg Wunsch 		if (error)
248add37e1eSJoerg Wunsch 			break;
249202379afSMichael Gmelin 		error = smbus_error(smbus_bwrite(parent, s->slave, s->cmd,
250202379afSMichael Gmelin 		    s->wcount, buf));
251d70424edSNicolas Souchu 		break;
252d70424edSNicolas Souchu 
253d70424edSNicolas Souchu 	case SMB_BREAD:
254202379afSMichael Gmelin 		if (s->rcount < 0) {
255202379afSMichael Gmelin 			error = EINVAL;
256202379afSMichael Gmelin 			break;
257202379afSMichael Gmelin 		}
258202379afSMichael Gmelin 		if (s->rcount > SMB_MAXBLOCKSIZE)
259202379afSMichael Gmelin 			s->rcount = SMB_MAXBLOCKSIZE;
260202379afSMichael Gmelin 		error = smbus_error(smbus_bread(parent, s->slave, s->cmd,
261202379afSMichael Gmelin 		    &bcount, buf));
262add37e1eSJoerg Wunsch 		if (error)
263add37e1eSJoerg Wunsch 			break;
264202379afSMichael Gmelin 		if (s->rcount > bcount)
265202379afSMichael Gmelin 			s->rcount = bcount;
266202379afSMichael Gmelin 		error = copyout(buf, s->rbuf, s->rcount);
267d70424edSNicolas Souchu 		break;
268d70424edSNicolas Souchu 
269d70424edSNicolas Souchu 	default:
270add37e1eSJoerg Wunsch 		error = ENOTTY;
271d70424edSNicolas Souchu 	}
272d70424edSNicolas Souchu 
273ba81c311SNicolas Souchu 	smbus_release_bus(parent, smbdev);
274ba81c311SNicolas Souchu 
275d70424edSNicolas Souchu 	return (error);
276d70424edSNicolas Souchu }
277d70424edSNicolas Souchu 
278*8d161143SJohn Baldwin DRIVER_MODULE(smb, smbus, smb_driver, 0, 0);
279c17d4340SNicolas Souchu MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
280c17d4340SNicolas Souchu MODULE_VERSION(smb, 1);
281