xref: /freebsd/sys/dev/smbus/smb.c (revision a05a680469a7ac77b195021fed74e3aa58152dd7)
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", DEVICE_UNIT_ANY);
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