xref: /freebsd/sys/dev/smbus/smb.c (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 1998, 2001 Nicolas Souchu
5  * Copyright (c) 2023 Juniper Networks, Inc.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29 
30 #include <sys/param.h>
31 #include <sys/kernel.h>
32 #include <sys/systm.h>
33 #include <sys/abi_compat.h>
34 #include <sys/module.h>
35 #include <sys/bus.h>
36 #include <sys/conf.h>
37 #include <sys/uio.h>
38 #include <sys/fcntl.h>
39 
40 #include <dev/smbus/smbconf.h>
41 #include <dev/smbus/smbus.h>
42 #include <dev/smbus/smb.h>
43 
44 #include "smbus_if.h"
45 
46 #ifdef COMPAT_FREEBSD32
47 struct smbcmd32 {
48 	u_char cmd;
49 	u_char reserved;
50 	u_short op;
51 	union {
52 		char	byte;
53 		char	buf[2];
54 		short	word;
55 	} wdata;
56 	union {
57 		char	byte;
58 		char	buf[2];
59 		short	word;
60 	} rdata;
61 	int slave;
62 	uint32_t wbuf;
63 	int wcount;
64 	uint32_t rbuf;
65 	int rcount;
66 };
67 
68 #define	SMB_QUICK_WRITE32	_IOW('i', 1, struct smbcmd32)
69 #define	SMB_QUICK_READ32	_IOW('i', 2, struct smbcmd32)
70 #define	SMB_SENDB32		_IOW('i', 3, struct smbcmd32)
71 #define	SMB_RECVB32		_IOWR('i', 4, struct smbcmd32)
72 #define	SMB_WRITEB32		_IOW('i', 5, struct smbcmd32)
73 #define	SMB_WRITEW32		_IOW('i', 6, struct smbcmd32)
74 #define	SMB_READB32		_IOWR('i', 7, struct smbcmd32)
75 #define	SMB_READW32		_IOWR('i', 8, struct smbcmd32)
76 #define	SMB_PCALL32		_IOWR('i', 9, struct smbcmd32)
77 #define	SMB_BWRITE32		_IOW('i', 10, struct smbcmd32)
78 #define	SMB_BREAD32		_IOWR('i', 11, struct smbcmd32)
79 #define	SMB_OLD_READB32		_IOW('i', 7, struct smbcmd32)
80 #define	SMB_OLD_READW32		_IOW('i', 8, struct smbcmd32)
81 #define	SMB_OLD_PCALL32		_IOW('i', 9, struct smbcmd32)
82 #endif
83 
84 #define SMB_OLD_READB	_IOW('i', 7, struct smbcmd)
85 #define SMB_OLD_READW	_IOW('i', 8, struct smbcmd)
86 #define SMB_OLD_PCALL	_IOW('i', 9, struct smbcmd)
87 
88 struct smb_softc {
89 	device_t sc_dev;
90 	struct cdev *sc_devnode;
91 };
92 
93 static void smb_identify(driver_t *driver, device_t parent);
94 static int smb_probe(device_t);
95 static int smb_attach(device_t);
96 static int smb_detach(device_t);
97 
98 static device_method_t smb_methods[] = {
99 	/* device interface */
100 	DEVMETHOD(device_identify,	smb_identify),
101 	DEVMETHOD(device_probe,		smb_probe),
102 	DEVMETHOD(device_attach,	smb_attach),
103 	DEVMETHOD(device_detach,	smb_detach),
104 
105 	/* smbus interface */
106 	DEVMETHOD(smbus_intr,		smbus_generic_intr),
107 	{ 0, 0 }
108 };
109 
110 static driver_t smb_driver = {
111 	"smb",
112 	smb_methods,
113 	sizeof(struct smb_softc),
114 };
115 
116 static	d_ioctl_t	smbioctl;
117 
118 static struct cdevsw smb_cdevsw = {
119 	.d_version =	D_VERSION,
120 	.d_flags =	D_TRACKCLOSE,
121 	.d_ioctl =	smbioctl,
122 	.d_name =	"smb",
123 };
124 
125 static void
126 smb_identify(driver_t *driver, device_t parent)
127 {
128 
129 	if (device_find_child(parent, "smb", -1) == NULL)
130 		BUS_ADD_CHILD(parent, 0, "smb", -1);
131 }
132 
133 static int
134 smb_probe(device_t dev)
135 {
136 	if (smbus_get_addr(dev) != -1)
137 		return (ENXIO);
138 
139 	device_set_desc(dev, "SMBus generic I/O");
140 	return (BUS_PROBE_NOWILDCARD);
141 }
142 
143 static int
144 smb_attach(device_t dev)
145 {
146 	struct smb_softc *sc;
147 	struct make_dev_args mda;
148 	int error;
149 
150 	sc = device_get_softc(dev);
151 	sc->sc_dev = dev;
152 
153 	make_dev_args_init(&mda);
154 	mda.mda_devsw = &smb_cdevsw;
155 	mda.mda_unit = device_get_unit(dev);
156 	mda.mda_uid = UID_ROOT;
157 	mda.mda_gid = GID_WHEEL;
158 	mda.mda_mode = 0600;
159 	mda.mda_si_drv1 = sc;
160 	error = make_dev_s(&mda, &sc->sc_devnode, "smb%d", mda.mda_unit);
161 	return (error);
162 }
163 
164 static int
165 smb_detach(device_t dev)
166 {
167 	struct smb_softc *sc;
168 
169 	sc = device_get_softc(dev);
170 	destroy_dev(sc->sc_devnode);
171 	return (0);
172 }
173 
174 #ifdef COMPAT_FREEBSD32
175 static void
176 smbcopyincmd32(struct smbcmd32 *uaddr, struct smbcmd *kaddr)
177 {
178 	CP(*uaddr, *kaddr, cmd);
179 	CP(*uaddr, *kaddr, op);
180 	CP(*uaddr, *kaddr, wdata.word);
181 	CP(*uaddr, *kaddr, slave);
182 	PTRIN_CP(*uaddr, *kaddr, wbuf);
183 	CP(*uaddr, *kaddr, wcount);
184 	PTRIN_CP(*uaddr, *kaddr, rbuf);
185 	CP(*uaddr, *kaddr, rcount);
186 }
187 #endif
188 
189 static int
190 smbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *td)
191 {
192 	char buf[SMB_MAXBLOCKSIZE];
193 	device_t parent;
194 #ifdef COMPAT_FREEBSD32
195 	struct smbcmd sswab;
196 	struct smbcmd32 *s32 = (struct smbcmd32 *)data;
197 #endif
198 	struct smbcmd *s = (struct smbcmd *)data;
199 	struct smb_softc *sc = dev->si_drv1;
200 	device_t smbdev = sc->sc_dev;
201 	int error;
202 	int unit;
203 	u_char bcount;
204 
205 	/*
206 	 * If a specific slave device is being used, override any passed-in
207 	 * slave.
208 	 */
209 	unit = dev2unit(dev);
210 	if (unit & 0x0400)
211 		s->slave = unit & 0x03ff;
212 
213 	parent = device_get_parent(smbdev);
214 
215 	/* Make sure that LSB bit is cleared. */
216 	if (s->slave & 0x1)
217 		return (EINVAL);
218 
219 	/* Allocate the bus. */
220 	if ((error = smbus_request_bus(parent, smbdev,
221 			(flags & O_NONBLOCK) ? SMB_DONTWAIT : (SMB_WAIT | SMB_INTR))))
222 		return (error);
223 
224 #ifdef COMPAT_FREEBSD32
225 	switch (cmd) {
226 	case SMB_QUICK_WRITE32:
227 	case SMB_QUICK_READ32:
228 	case SMB_SENDB32:
229 	case SMB_RECVB32:
230 	case SMB_WRITEB32:
231 	case SMB_WRITEW32:
232 	case SMB_OLD_READB32:
233 	case SMB_READB32:
234 	case SMB_OLD_READW32:
235 	case SMB_READW32:
236 	case SMB_OLD_PCALL32:
237 	case SMB_PCALL32:
238 	case SMB_BWRITE32:
239 	case SMB_BREAD32:
240 		smbcopyincmd32(s32, &sswab);
241 		s = &sswab;
242 		break;
243 	default:
244 		break;
245 	}
246 #endif
247 
248 	switch (cmd) {
249 	case SMB_QUICK_WRITE:
250 #ifdef COMPAT_FREEBSD32
251 	case SMB_QUICK_WRITE32:
252 #endif
253 		error = smbus_error(smbus_quick(parent, s->slave, SMB_QWRITE));
254 		break;
255 
256 	case SMB_QUICK_READ:
257 #ifdef COMPAT_FREEBSD32
258 	case SMB_QUICK_READ32:
259 #endif
260 		error = smbus_error(smbus_quick(parent, s->slave, SMB_QREAD));
261 		break;
262 
263 	case SMB_SENDB:
264 #ifdef COMPAT_FREEBSD32
265 	case SMB_SENDB32:
266 #endif
267 		error = smbus_error(smbus_sendb(parent, s->slave, s->cmd));
268 		break;
269 
270 	case SMB_RECVB:
271 #ifdef COMPAT_FREEBSD32
272 	case SMB_RECVB32:
273 #endif
274 		error = smbus_error(smbus_recvb(parent, s->slave, &s->cmd));
275 		break;
276 
277 	case SMB_WRITEB:
278 #ifdef COMPAT_FREEBSD32
279 	case SMB_WRITEB32:
280 #endif
281 		error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
282 						s->wdata.byte));
283 		break;
284 
285 	case SMB_WRITEW:
286 #ifdef COMPAT_FREEBSD32
287 	case SMB_WRITEW32:
288 #endif
289 		error = smbus_error(smbus_writew(parent, s->slave,
290 						s->cmd, s->wdata.word));
291 		break;
292 
293 	case SMB_OLD_READB:
294 	case SMB_READB:
295 #ifdef COMPAT_FREEBSD32
296 	case SMB_OLD_READB32:
297 	case SMB_READB32:
298 #endif
299 		/* NB: for SMB_OLD_READB the read data goes to rbuf only. */
300 		error = smbus_error(smbus_readb(parent, s->slave, s->cmd,
301 		    &s->rdata.byte));
302 		if (error)
303 			break;
304 		if (s->rbuf && s->rcount >= 1) {
305 			error = copyout(&s->rdata.byte, s->rbuf, 1);
306 			s->rcount = 1;
307 		}
308 		break;
309 
310 	case SMB_OLD_READW:
311 	case SMB_READW:
312 #ifdef COMPAT_FREEBSD32
313 	case SMB_OLD_READW32:
314 	case SMB_READW32:
315 #endif
316 		/* NB: for SMB_OLD_READW the read data goes to rbuf only. */
317 		error = smbus_error(smbus_readw(parent, s->slave, s->cmd,
318 		    &s->rdata.word));
319 		if (error)
320 			break;
321 		if (s->rbuf && s->rcount >= 2) {
322 			buf[0] = (u_char)s->rdata.word;
323 			buf[1] = (u_char)(s->rdata.word >> 8);
324 			error = copyout(buf, s->rbuf, 2);
325 			s->rcount = 2;
326 		}
327 		break;
328 
329 	case SMB_OLD_PCALL:
330 	case SMB_PCALL:
331 #ifdef COMPAT_FREEBSD32
332 	case SMB_OLD_PCALL32:
333 	case SMB_PCALL32:
334 #endif
335 		/* NB: for SMB_OLD_PCALL the read data goes to rbuf only. */
336 		error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
337 		    s->wdata.word, &s->rdata.word));
338 		if (error)
339 			break;
340 		if (s->rbuf && s->rcount >= 2) {
341 			buf[0] = (u_char)s->rdata.word;
342 			buf[1] = (u_char)(s->rdata.word >> 8);
343 			error = copyout(buf, s->rbuf, 2);
344 			s->rcount = 2;
345 		}
346 
347 		break;
348 
349 	case SMB_BWRITE:
350 #ifdef COMPAT_FREEBSD32
351 	case SMB_BWRITE32:
352 #endif
353 		if (s->wcount < 0) {
354 			error = EINVAL;
355 			break;
356 		}
357 		if (s->wcount > SMB_MAXBLOCKSIZE)
358 			s->wcount = SMB_MAXBLOCKSIZE;
359 		if (s->wcount)
360 			error = copyin(s->wbuf, buf, s->wcount);
361 		if (error)
362 			break;
363 		error = smbus_error(smbus_bwrite(parent, s->slave, s->cmd,
364 		    s->wcount, buf));
365 		break;
366 
367 	case SMB_BREAD:
368 #ifdef COMPAT_FREEBSD32
369 	case SMB_BREAD32:
370 #endif
371 		if (s->rcount < 0) {
372 			error = EINVAL;
373 			break;
374 		}
375 		if (s->rcount > SMB_MAXBLOCKSIZE)
376 			s->rcount = SMB_MAXBLOCKSIZE;
377 		error = smbus_error(smbus_bread(parent, s->slave, s->cmd,
378 		    &bcount, buf));
379 		if (error)
380 			break;
381 		if (s->rcount > bcount)
382 			s->rcount = bcount;
383 		error = copyout(buf, s->rbuf, s->rcount);
384 		break;
385 
386 	default:
387 		error = ENOTTY;
388 	}
389 
390 #ifdef COMPAT_FREEBSD32
391 	switch (cmd) {
392 	case SMB_RECVB32:
393 		CP(*s, *s32, cmd);
394 		break;
395 	case SMB_OLD_READB32:
396 	case SMB_READB32:
397 	case SMB_OLD_READW32:
398 	case SMB_READW32:
399 	case SMB_OLD_PCALL32:
400 	case SMB_PCALL32:
401 		CP(*s, *s32, rdata.word);
402 		break;
403 	case SMB_BREAD32:
404 		if (s->rbuf == NULL)
405 			CP(*s, *s32, rdata.word);
406 		CP(*s, *s32, rcount);
407 		break;
408 	default:
409 		break;
410 	}
411 #endif
412 
413 	smbus_release_bus(parent, smbdev);
414 
415 	return (error);
416 }
417 
418 DRIVER_MODULE(smb, smbus, smb_driver, 0, 0);
419 MODULE_DEPEND(smb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
420 MODULE_VERSION(smb, 1);
421