1c3e2dc6bSNicolas Souchu /*-
24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni *
4c17d4340SNicolas Souchu * Copyright (c) 1998, 2001 Nicolas Souchu
5c3e2dc6bSNicolas Souchu * All rights reserved.
6c3e2dc6bSNicolas Souchu *
7c3e2dc6bSNicolas Souchu * Redistribution and use in source and binary forms, with or without
8c3e2dc6bSNicolas Souchu * modification, are permitted provided that the following conditions
9c3e2dc6bSNicolas Souchu * are met:
10c3e2dc6bSNicolas Souchu * 1. Redistributions of source code must retain the above copyright
11c3e2dc6bSNicolas Souchu * notice, this list of conditions and the following disclaimer.
12c3e2dc6bSNicolas Souchu * 2. Redistributions in binary form must reproduce the above copyright
13c3e2dc6bSNicolas Souchu * notice, this list of conditions and the following disclaimer in the
14c3e2dc6bSNicolas Souchu * documentation and/or other materials provided with the distribution.
15c3e2dc6bSNicolas Souchu *
16c3e2dc6bSNicolas Souchu * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17c3e2dc6bSNicolas Souchu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18c3e2dc6bSNicolas Souchu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19c3e2dc6bSNicolas Souchu * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20c3e2dc6bSNicolas Souchu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21c3e2dc6bSNicolas Souchu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22c3e2dc6bSNicolas Souchu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23c3e2dc6bSNicolas Souchu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24c3e2dc6bSNicolas Souchu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25c3e2dc6bSNicolas Souchu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26c3e2dc6bSNicolas Souchu * SUCH DAMAGE.
27c3e2dc6bSNicolas Souchu */
28c3e2dc6bSNicolas Souchu
294b7ec270SMarius Strobl #include <sys/cdefs.h>
30c3e2dc6bSNicolas Souchu /*
31c3e2dc6bSNicolas Souchu * I2C to SMB bridge
3204f89a63SNicolas Souchu *
3304f89a63SNicolas Souchu * Example:
3404f89a63SNicolas Souchu *
3504f89a63SNicolas Souchu * smb bttv
3604f89a63SNicolas Souchu * \ /
3704f89a63SNicolas Souchu * smbus
3804f89a63SNicolas Souchu * / \
3904f89a63SNicolas Souchu * iicsmb bti2c
4004f89a63SNicolas Souchu * |
4104f89a63SNicolas Souchu * iicbus
4204f89a63SNicolas Souchu * / | \
4304f89a63SNicolas Souchu * iicbb pcf ...
4404f89a63SNicolas Souchu * |
4504f89a63SNicolas Souchu * lpbb
46c3e2dc6bSNicolas Souchu */
47c3e2dc6bSNicolas Souchu
48c3e2dc6bSNicolas Souchu #include <sys/param.h>
49c3e2dc6bSNicolas Souchu #include <sys/bus.h>
500df8f081SJohn Baldwin #include <sys/kernel.h>
510df8f081SJohn Baldwin #include <sys/lock.h>
520df8f081SJohn Baldwin #include <sys/module.h>
530df8f081SJohn Baldwin #include <sys/mutex.h>
540df8f081SJohn Baldwin #include <sys/systm.h>
55c3e2dc6bSNicolas Souchu #include <sys/uio.h>
56c3e2dc6bSNicolas Souchu
57c3e2dc6bSNicolas Souchu #include <dev/iicbus/iiconf.h>
58c3e2dc6bSNicolas Souchu #include <dev/iicbus/iicbus.h>
59c3e2dc6bSNicolas Souchu
6088cc0bb9SAndriy Gapon #include <dev/smbus/smb.h>
61c3e2dc6bSNicolas Souchu #include <dev/smbus/smbconf.h>
62c3e2dc6bSNicolas Souchu
63c3e2dc6bSNicolas Souchu #include "iicbus_if.h"
64c3e2dc6bSNicolas Souchu #include "smbus_if.h"
65c3e2dc6bSNicolas Souchu
66c3e2dc6bSNicolas Souchu struct iicsmb_softc {
67c3e2dc6bSNicolas Souchu
68c3e2dc6bSNicolas Souchu #define SMB_WAITING_ADDR 0x0
69c3e2dc6bSNicolas Souchu #define SMB_WAITING_LOW 0x1
70c3e2dc6bSNicolas Souchu #define SMB_WAITING_HIGH 0x2
71c3e2dc6bSNicolas Souchu #define SMB_DONE 0x3
72c3e2dc6bSNicolas Souchu int state;
73c3e2dc6bSNicolas Souchu
74c3e2dc6bSNicolas Souchu u_char devaddr; /* slave device address */
75c3e2dc6bSNicolas Souchu
76c3e2dc6bSNicolas Souchu char low; /* low byte received first */
77c3e2dc6bSNicolas Souchu char high; /* high byte */
78c3e2dc6bSNicolas Souchu
79313f8941SJohn Baldwin struct mtx lock;
80c3e2dc6bSNicolas Souchu device_t smbus;
81c3e2dc6bSNicolas Souchu };
82c3e2dc6bSNicolas Souchu
83c3e2dc6bSNicolas Souchu static int iicsmb_probe(device_t);
84c3e2dc6bSNicolas Souchu static int iicsmb_attach(device_t);
85c17d4340SNicolas Souchu static int iicsmb_detach(device_t);
86c17d4340SNicolas Souchu static void iicsmb_identify(driver_t *driver, device_t parent);
87c3e2dc6bSNicolas Souchu
88b23193a5SWarner Losh static int iicsmb_intr(device_t dev, int event, char *buf);
897048a99cSJohn Baldwin static int iicsmb_callback(device_t dev, int index, void *data);
90c3e2dc6bSNicolas Souchu static int iicsmb_quick(device_t dev, u_char slave, int how);
91c3e2dc6bSNicolas Souchu static int iicsmb_sendb(device_t dev, u_char slave, char byte);
92c3e2dc6bSNicolas Souchu static int iicsmb_recvb(device_t dev, u_char slave, char *byte);
93c3e2dc6bSNicolas Souchu static int iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte);
94c3e2dc6bSNicolas Souchu static int iicsmb_writew(device_t dev, u_char slave, char cmd, short word);
95c3e2dc6bSNicolas Souchu static int iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte);
96c3e2dc6bSNicolas Souchu static int iicsmb_readw(device_t dev, u_char slave, char cmd, short *word);
97c3e2dc6bSNicolas Souchu static int iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata);
98c3e2dc6bSNicolas Souchu static int iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf);
997048a99cSJohn Baldwin static int iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf);
100c3e2dc6bSNicolas Souchu
101c3e2dc6bSNicolas Souchu static device_method_t iicsmb_methods[] = {
102c3e2dc6bSNicolas Souchu /* device interface */
103c17d4340SNicolas Souchu DEVMETHOD(device_identify, iicsmb_identify),
104c3e2dc6bSNicolas Souchu DEVMETHOD(device_probe, iicsmb_probe),
105c3e2dc6bSNicolas Souchu DEVMETHOD(device_attach, iicsmb_attach),
106c17d4340SNicolas Souchu DEVMETHOD(device_detach, iicsmb_detach),
107c3e2dc6bSNicolas Souchu
108c3e2dc6bSNicolas Souchu /* iicbus interface */
109c3e2dc6bSNicolas Souchu DEVMETHOD(iicbus_intr, iicsmb_intr),
110c3e2dc6bSNicolas Souchu
111c3e2dc6bSNicolas Souchu /* smbus interface */
11204f89a63SNicolas Souchu DEVMETHOD(smbus_callback, iicsmb_callback),
113c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_quick, iicsmb_quick),
114c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_sendb, iicsmb_sendb),
115c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_recvb, iicsmb_recvb),
116c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_writeb, iicsmb_writeb),
117c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_writew, iicsmb_writew),
118c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_readb, iicsmb_readb),
119c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_readw, iicsmb_readw),
120c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_pcall, iicsmb_pcall),
121c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_bwrite, iicsmb_bwrite),
122c3e2dc6bSNicolas Souchu DEVMETHOD(smbus_bread, iicsmb_bread),
123c3e2dc6bSNicolas Souchu
1244b7ec270SMarius Strobl DEVMETHOD_END
125c3e2dc6bSNicolas Souchu };
126c3e2dc6bSNicolas Souchu
127c3e2dc6bSNicolas Souchu static driver_t iicsmb_driver = {
128c3e2dc6bSNicolas Souchu "iicsmb",
129c3e2dc6bSNicolas Souchu iicsmb_methods,
130c3e2dc6bSNicolas Souchu sizeof(struct iicsmb_softc),
131c3e2dc6bSNicolas Souchu };
132c3e2dc6bSNicolas Souchu
133c17d4340SNicolas Souchu static void
iicsmb_identify(driver_t * driver,device_t parent)134c17d4340SNicolas Souchu iicsmb_identify(driver_t *driver, device_t parent)
135c17d4340SNicolas Souchu {
136313f8941SJohn Baldwin
137313f8941SJohn Baldwin if (device_find_child(parent, "iicsmb", -1) == NULL)
138a05a6804SWarner Losh BUS_ADD_CHILD(parent, 0, "iicsmb", DEVICE_UNIT_ANY);
139c17d4340SNicolas Souchu }
140c17d4340SNicolas Souchu
141c3e2dc6bSNicolas Souchu static int
iicsmb_probe(device_t dev)142c3e2dc6bSNicolas Souchu iicsmb_probe(device_t dev)
143c3e2dc6bSNicolas Souchu {
144c17d4340SNicolas Souchu device_set_desc(dev, "SMBus over I2C bridge");
145789c4b9dSNathan Whitehorn return (BUS_PROBE_NOWILDCARD);
146c3e2dc6bSNicolas Souchu }
147c3e2dc6bSNicolas Souchu
148c3e2dc6bSNicolas Souchu static int
iicsmb_attach(device_t dev)149c3e2dc6bSNicolas Souchu iicsmb_attach(device_t dev)
150c3e2dc6bSNicolas Souchu {
151c3e2dc6bSNicolas Souchu struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
152c3e2dc6bSNicolas Souchu
153313f8941SJohn Baldwin mtx_init(&sc->lock, "iicsmb", NULL, MTX_DEF);
154313f8941SJohn Baldwin
1555b56413dSWarner Losh sc->smbus = device_add_child(dev, "smbus", DEVICE_UNIT_ANY);
156c17d4340SNicolas Souchu
157c3e2dc6bSNicolas Souchu /* probe and attach the smbus */
158*18250ec6SJohn Baldwin bus_attach_children(dev);
159c17d4340SNicolas Souchu
160c17d4340SNicolas Souchu return (0);
161c17d4340SNicolas Souchu }
162c17d4340SNicolas Souchu
163c17d4340SNicolas Souchu static int
iicsmb_detach(device_t dev)164c17d4340SNicolas Souchu iicsmb_detach(device_t dev)
165c17d4340SNicolas Souchu {
166c17d4340SNicolas Souchu struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
167c17d4340SNicolas Souchu
168c17d4340SNicolas Souchu bus_generic_detach(dev);
169313f8941SJohn Baldwin mtx_destroy(&sc->lock);
170c3e2dc6bSNicolas Souchu
171c3e2dc6bSNicolas Souchu return (0);
172c3e2dc6bSNicolas Souchu }
173c3e2dc6bSNicolas Souchu
174c3e2dc6bSNicolas Souchu /*
175c3e2dc6bSNicolas Souchu * iicsmb_intr()
176c3e2dc6bSNicolas Souchu *
177c3e2dc6bSNicolas Souchu * iicbus interrupt handler
178c3e2dc6bSNicolas Souchu */
179b23193a5SWarner Losh static int
iicsmb_intr(device_t dev,int event,char * buf)180c3e2dc6bSNicolas Souchu iicsmb_intr(device_t dev, int event, char *buf)
181c3e2dc6bSNicolas Souchu {
182c3e2dc6bSNicolas Souchu struct iicsmb_softc *sc = (struct iicsmb_softc *)device_get_softc(dev);
183c3e2dc6bSNicolas Souchu
184313f8941SJohn Baldwin mtx_lock(&sc->lock);
185c3e2dc6bSNicolas Souchu switch (event) {
186c3e2dc6bSNicolas Souchu case INTR_GENERAL:
187c3e2dc6bSNicolas Souchu case INTR_START:
188c3e2dc6bSNicolas Souchu sc->state = SMB_WAITING_ADDR;
189c3e2dc6bSNicolas Souchu break;
190c3e2dc6bSNicolas Souchu
191c3e2dc6bSNicolas Souchu case INTR_STOP:
192c3e2dc6bSNicolas Souchu /* call smbus intr handler */
193c3e2dc6bSNicolas Souchu smbus_intr(sc->smbus, sc->devaddr,
194c3e2dc6bSNicolas Souchu sc->low, sc->high, SMB_ENOERR);
195c3e2dc6bSNicolas Souchu break;
196c3e2dc6bSNicolas Souchu
197c3e2dc6bSNicolas Souchu case INTR_RECEIVE:
198c3e2dc6bSNicolas Souchu switch (sc->state) {
199c3e2dc6bSNicolas Souchu case SMB_DONE:
200c3e2dc6bSNicolas Souchu /* XXX too much data, discard */
2016e551fb6SDavid E. O'Brien printf("%s: too much data from 0x%x\n", __func__,
202c3e2dc6bSNicolas Souchu sc->devaddr & 0xff);
203c3e2dc6bSNicolas Souchu goto end;
204c3e2dc6bSNicolas Souchu
205c3e2dc6bSNicolas Souchu case SMB_WAITING_ADDR:
206c3e2dc6bSNicolas Souchu sc->devaddr = (u_char)*buf;
207c3e2dc6bSNicolas Souchu sc->state = SMB_WAITING_LOW;
208c3e2dc6bSNicolas Souchu break;
209c3e2dc6bSNicolas Souchu
210c3e2dc6bSNicolas Souchu case SMB_WAITING_LOW:
211c3e2dc6bSNicolas Souchu sc->low = *buf;
212c3e2dc6bSNicolas Souchu sc->state = SMB_WAITING_HIGH;
213c3e2dc6bSNicolas Souchu break;
214c3e2dc6bSNicolas Souchu
215c3e2dc6bSNicolas Souchu case SMB_WAITING_HIGH:
216c3e2dc6bSNicolas Souchu sc->high = *buf;
217c3e2dc6bSNicolas Souchu sc->state = SMB_DONE;
218c3e2dc6bSNicolas Souchu break;
219c3e2dc6bSNicolas Souchu }
220c3e2dc6bSNicolas Souchu end:
221c3e2dc6bSNicolas Souchu break;
222c3e2dc6bSNicolas Souchu
223c3e2dc6bSNicolas Souchu case INTR_TRANSMIT:
224c3e2dc6bSNicolas Souchu case INTR_NOACK:
225c3e2dc6bSNicolas Souchu break;
226c3e2dc6bSNicolas Souchu
227c3e2dc6bSNicolas Souchu case INTR_ERROR:
228c3e2dc6bSNicolas Souchu switch (*buf) {
229c3e2dc6bSNicolas Souchu case IIC_EBUSERR:
230c3e2dc6bSNicolas Souchu smbus_intr(sc->smbus, sc->devaddr, 0, 0, SMB_EBUSERR);
231c3e2dc6bSNicolas Souchu break;
232c3e2dc6bSNicolas Souchu
233c3e2dc6bSNicolas Souchu default:
2346e551fb6SDavid E. O'Brien printf("%s unknown error 0x%x!\n", __func__,
235c3e2dc6bSNicolas Souchu (int)*buf);
236c3e2dc6bSNicolas Souchu break;
237c3e2dc6bSNicolas Souchu }
238c3e2dc6bSNicolas Souchu break;
239c3e2dc6bSNicolas Souchu
240c3e2dc6bSNicolas Souchu default:
2416e551fb6SDavid E. O'Brien panic("%s: unknown event (%d)!", __func__, event);
242c3e2dc6bSNicolas Souchu }
243313f8941SJohn Baldwin mtx_unlock(&sc->lock);
244c3e2dc6bSNicolas Souchu
245b23193a5SWarner Losh return (0);
246c3e2dc6bSNicolas Souchu }
247c3e2dc6bSNicolas Souchu
248c3e2dc6bSNicolas Souchu static int
iicsmb_callback(device_t dev,int index,void * data)2497048a99cSJohn Baldwin iicsmb_callback(device_t dev, int index, void *data)
25004f89a63SNicolas Souchu {
25104f89a63SNicolas Souchu device_t parent = device_get_parent(dev);
25204f89a63SNicolas Souchu int error = 0;
25304f89a63SNicolas Souchu int how;
25404f89a63SNicolas Souchu
25504f89a63SNicolas Souchu switch (index) {
25604f89a63SNicolas Souchu case SMB_REQUEST_BUS:
25704f89a63SNicolas Souchu /* request underlying iicbus */
25804f89a63SNicolas Souchu how = *(int *)data;
25904f89a63SNicolas Souchu error = iicbus_request_bus(parent, dev, how);
26004f89a63SNicolas Souchu break;
26104f89a63SNicolas Souchu
26204f89a63SNicolas Souchu case SMB_RELEASE_BUS:
26304f89a63SNicolas Souchu /* release underlying iicbus */
26404f89a63SNicolas Souchu error = iicbus_release_bus(parent, dev);
26504f89a63SNicolas Souchu break;
26604f89a63SNicolas Souchu
26704f89a63SNicolas Souchu default:
26804f89a63SNicolas Souchu error = EINVAL;
26904f89a63SNicolas Souchu }
27004f89a63SNicolas Souchu
27104f89a63SNicolas Souchu return (error);
27204f89a63SNicolas Souchu }
27304f89a63SNicolas Souchu
27404f89a63SNicolas Souchu static int
iic2smb_error(int error)275f1519c01SAndriy Gapon iic2smb_error(int error)
276f1519c01SAndriy Gapon {
277f1519c01SAndriy Gapon switch (error) {
278f1519c01SAndriy Gapon case IIC_NOERR:
279f1519c01SAndriy Gapon return (SMB_ENOERR);
280f1519c01SAndriy Gapon case IIC_EBUSERR:
281f1519c01SAndriy Gapon return (SMB_EBUSERR);
282f1519c01SAndriy Gapon case IIC_ENOACK:
283f1519c01SAndriy Gapon return (SMB_ENOACK);
284f1519c01SAndriy Gapon case IIC_ETIMEOUT:
285f1519c01SAndriy Gapon return (SMB_ETIMEOUT);
286f1519c01SAndriy Gapon case IIC_EBUSBSY:
287f1519c01SAndriy Gapon return (SMB_EBUSY);
288f1519c01SAndriy Gapon case IIC_ESTATUS:
289f1519c01SAndriy Gapon return (SMB_EBUSERR);
290f1519c01SAndriy Gapon case IIC_EUNDERFLOW:
291f1519c01SAndriy Gapon return (SMB_EBUSERR);
292f1519c01SAndriy Gapon case IIC_EOVERFLOW:
293f1519c01SAndriy Gapon return (SMB_EBUSERR);
294f1519c01SAndriy Gapon case IIC_ENOTSUPP:
295f1519c01SAndriy Gapon return (SMB_ENOTSUPP);
296f1519c01SAndriy Gapon case IIC_ENOADDR:
297f1519c01SAndriy Gapon return (SMB_EBUSERR);
298f1519c01SAndriy Gapon case IIC_ERESOURCE:
299f1519c01SAndriy Gapon return (SMB_EBUSERR);
300f1519c01SAndriy Gapon default:
301f1519c01SAndriy Gapon return (SMB_EBUSERR);
302f1519c01SAndriy Gapon }
303f1519c01SAndriy Gapon }
304f1519c01SAndriy Gapon
305f1519c01SAndriy Gapon #define TRANSFER_MSGS(dev, msgs) iicbus_transfer(dev, msgs, nitems(msgs))
306f1519c01SAndriy Gapon
307f1519c01SAndriy Gapon static int
iicsmb_quick(device_t dev,u_char slave,int how)308c3e2dc6bSNicolas Souchu iicsmb_quick(device_t dev, u_char slave, int how)
309c3e2dc6bSNicolas Souchu {
310f1519c01SAndriy Gapon struct iic_msg msgs[] = {
311f1519c01SAndriy Gapon { slave, how == SMB_QWRITE ? IIC_M_WR : IIC_M_RD, 0, NULL },
312f1519c01SAndriy Gapon };
313c3e2dc6bSNicolas Souchu int error;
314c3e2dc6bSNicolas Souchu
315c3e2dc6bSNicolas Souchu switch (how) {
316c3e2dc6bSNicolas Souchu case SMB_QWRITE:
317c3e2dc6bSNicolas Souchu case SMB_QREAD:
318c3e2dc6bSNicolas Souchu break;
319c3e2dc6bSNicolas Souchu default:
320f1519c01SAndriy Gapon return (SMB_EINVAL);
321c3e2dc6bSNicolas Souchu }
322c3e2dc6bSNicolas Souchu
323f1519c01SAndriy Gapon error = TRANSFER_MSGS(dev, msgs);
324f1519c01SAndriy Gapon return (iic2smb_error(error));
325c3e2dc6bSNicolas Souchu }
326c3e2dc6bSNicolas Souchu
327c3e2dc6bSNicolas Souchu static int
iicsmb_sendb(device_t dev,u_char slave,char byte)328c3e2dc6bSNicolas Souchu iicsmb_sendb(device_t dev, u_char slave, char byte)
329c3e2dc6bSNicolas Souchu {
330f1519c01SAndriy Gapon struct iic_msg msgs[] = {
331f1519c01SAndriy Gapon { slave, IIC_M_WR, 1, &byte },
332f1519c01SAndriy Gapon };
333f1519c01SAndriy Gapon int error;
334c3e2dc6bSNicolas Souchu
335f1519c01SAndriy Gapon error = TRANSFER_MSGS(dev, msgs);
336f1519c01SAndriy Gapon return (iic2smb_error(error));
337c3e2dc6bSNicolas Souchu }
338c3e2dc6bSNicolas Souchu
339c3e2dc6bSNicolas Souchu static int
iicsmb_recvb(device_t dev,u_char slave,char * byte)340c3e2dc6bSNicolas Souchu iicsmb_recvb(device_t dev, u_char slave, char *byte)
341c3e2dc6bSNicolas Souchu {
342f1519c01SAndriy Gapon struct iic_msg msgs[] = {
343f1519c01SAndriy Gapon { slave, IIC_M_RD, 1, byte },
344f1519c01SAndriy Gapon };
345f1519c01SAndriy Gapon int error;
346c3e2dc6bSNicolas Souchu
347f1519c01SAndriy Gapon error = TRANSFER_MSGS(dev, msgs);
348f1519c01SAndriy Gapon return (iic2smb_error(error));
349c3e2dc6bSNicolas Souchu }
350c3e2dc6bSNicolas Souchu
351c3e2dc6bSNicolas Souchu static int
iicsmb_writeb(device_t dev,u_char slave,char cmd,char byte)352c3e2dc6bSNicolas Souchu iicsmb_writeb(device_t dev, u_char slave, char cmd, char byte)
353c3e2dc6bSNicolas Souchu {
354f1519c01SAndriy Gapon uint8_t bytes[] = { cmd, byte };
355f1519c01SAndriy Gapon struct iic_msg msgs[] = {
356f1519c01SAndriy Gapon { slave, IIC_M_WR, nitems(bytes), bytes },
357f1519c01SAndriy Gapon };
358f1519c01SAndriy Gapon int error;
359c3e2dc6bSNicolas Souchu
360f1519c01SAndriy Gapon error = TRANSFER_MSGS(dev, msgs);
361f1519c01SAndriy Gapon return (iic2smb_error(error));
362c3e2dc6bSNicolas Souchu }
363c3e2dc6bSNicolas Souchu
364c3e2dc6bSNicolas Souchu static int
iicsmb_writew(device_t dev,u_char slave,char cmd,short word)365c3e2dc6bSNicolas Souchu iicsmb_writew(device_t dev, u_char slave, char cmd, short word)
366c3e2dc6bSNicolas Souchu {
367f1519c01SAndriy Gapon uint8_t bytes[] = { cmd, word & 0xff, word >> 8 };
368f1519c01SAndriy Gapon struct iic_msg msgs[] = {
369f1519c01SAndriy Gapon { slave, IIC_M_WR, nitems(bytes), bytes },
370f1519c01SAndriy Gapon };
371f1519c01SAndriy Gapon int error;
372c3e2dc6bSNicolas Souchu
373f1519c01SAndriy Gapon error = TRANSFER_MSGS(dev, msgs);
374f1519c01SAndriy Gapon return (iic2smb_error(error));
375c3e2dc6bSNicolas Souchu }
376c3e2dc6bSNicolas Souchu
377c3e2dc6bSNicolas Souchu static int
iicsmb_readb(device_t dev,u_char slave,char cmd,char * byte)378c3e2dc6bSNicolas Souchu iicsmb_readb(device_t dev, u_char slave, char cmd, char *byte)
379c3e2dc6bSNicolas Souchu {
380f1519c01SAndriy Gapon struct iic_msg msgs[] = {
381f1519c01SAndriy Gapon { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
382f1519c01SAndriy Gapon { slave, IIC_M_RD, 1, byte },
383f1519c01SAndriy Gapon };
384f1519c01SAndriy Gapon int error;
385c3e2dc6bSNicolas Souchu
386f1519c01SAndriy Gapon error = TRANSFER_MSGS(dev, msgs);
387f1519c01SAndriy Gapon return (iic2smb_error(error));
388c3e2dc6bSNicolas Souchu }
389c3e2dc6bSNicolas Souchu
390c3e2dc6bSNicolas Souchu static int
iicsmb_readw(device_t dev,u_char slave,char cmd,short * word)391c3e2dc6bSNicolas Souchu iicsmb_readw(device_t dev, u_char slave, char cmd, short *word)
392c3e2dc6bSNicolas Souchu {
393f1519c01SAndriy Gapon uint8_t buf[2];
394f1519c01SAndriy Gapon struct iic_msg msgs[] = {
395f1519c01SAndriy Gapon { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
396f1519c01SAndriy Gapon { slave, IIC_M_RD, nitems(buf), buf },
397f1519c01SAndriy Gapon };
398f1519c01SAndriy Gapon int error;
399c3e2dc6bSNicolas Souchu
400f1519c01SAndriy Gapon error = TRANSFER_MSGS(dev, msgs);
401f1519c01SAndriy Gapon if (error == 0)
402f1519c01SAndriy Gapon *word = ((uint16_t)buf[1] << 8) | buf[0];
403f1519c01SAndriy Gapon return (iic2smb_error(error));
404c3e2dc6bSNicolas Souchu }
405c3e2dc6bSNicolas Souchu
406c3e2dc6bSNicolas Souchu static int
iicsmb_pcall(device_t dev,u_char slave,char cmd,short sdata,short * rdata)407c3e2dc6bSNicolas Souchu iicsmb_pcall(device_t dev, u_char slave, char cmd, short sdata, short *rdata)
408c3e2dc6bSNicolas Souchu {
409f1519c01SAndriy Gapon uint8_t in[3] = { cmd, sdata & 0xff, sdata >> 8 };
410f1519c01SAndriy Gapon uint8_t out[2];
411f1519c01SAndriy Gapon struct iic_msg msgs[] = {
412f1519c01SAndriy Gapon { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(in), in },
413f1519c01SAndriy Gapon { slave, IIC_M_RD, nitems(out), out },
414f1519c01SAndriy Gapon };
415f1519c01SAndriy Gapon int error;
416c3e2dc6bSNicolas Souchu
417f1519c01SAndriy Gapon error = TRANSFER_MSGS(dev, msgs);
418f1519c01SAndriy Gapon if (error == 0)
419f1519c01SAndriy Gapon *rdata = ((uint16_t)out[1] << 8) | out[0];
420f1519c01SAndriy Gapon return (iic2smb_error(error));
421c3e2dc6bSNicolas Souchu }
422c3e2dc6bSNicolas Souchu
423c3e2dc6bSNicolas Souchu static int
iicsmb_bwrite(device_t dev,u_char slave,char cmd,u_char count,char * buf)424c3e2dc6bSNicolas Souchu iicsmb_bwrite(device_t dev, u_char slave, char cmd, u_char count, char *buf)
425c3e2dc6bSNicolas Souchu {
426f1519c01SAndriy Gapon uint8_t bytes[2] = { cmd, count };
427f1519c01SAndriy Gapon struct iic_msg msgs[] = {
428f1519c01SAndriy Gapon { slave, IIC_M_WR | IIC_M_NOSTOP, nitems(bytes), bytes },
429f1519c01SAndriy Gapon { slave, IIC_M_WR | IIC_M_NOSTART, count, buf },
430f1519c01SAndriy Gapon };
431f1519c01SAndriy Gapon int error;
432c3e2dc6bSNicolas Souchu
43388cc0bb9SAndriy Gapon if (count > SMB_MAXBLOCKSIZE || count == 0)
434f1519c01SAndriy Gapon return (SMB_EINVAL);
435f1519c01SAndriy Gapon error = TRANSFER_MSGS(dev, msgs);
436f1519c01SAndriy Gapon return (iic2smb_error(error));
437c3e2dc6bSNicolas Souchu }
438c3e2dc6bSNicolas Souchu
439c3e2dc6bSNicolas Souchu static int
iicsmb_bread(device_t dev,u_char slave,char cmd,u_char * count,char * buf)4407048a99cSJohn Baldwin iicsmb_bread(device_t dev, u_char slave, char cmd, u_char *count, char *buf)
441c3e2dc6bSNicolas Souchu {
442f1519c01SAndriy Gapon struct iic_msg msgs[] = {
443f1519c01SAndriy Gapon { slave, IIC_M_WR | IIC_M_NOSTOP, 1, &cmd },
444f1519c01SAndriy Gapon { slave, IIC_M_RD | IIC_M_NOSTOP, 1, count },
445f1519c01SAndriy Gapon };
446f1519c01SAndriy Gapon struct iic_msg block_msg[] = {
447f1519c01SAndriy Gapon { slave, IIC_M_RD | IIC_M_NOSTART, 0, buf },
448f1519c01SAndriy Gapon };
449c3e2dc6bSNicolas Souchu device_t parent = device_get_parent(dev);
450f1519c01SAndriy Gapon int error;
45104f89a63SNicolas Souchu
452f1519c01SAndriy Gapon /* Have to do this because the command is split in two transfers. */
45311ba8488SAllan Jude error = iicbus_request_bus(parent, dev, IIC_WAIT | IIC_RECURSIVE);
454f1519c01SAndriy Gapon if (error == 0)
455f1519c01SAndriy Gapon error = TRANSFER_MSGS(dev, msgs);
456f1519c01SAndriy Gapon if (error == 0) {
457f1519c01SAndriy Gapon /*
458f1519c01SAndriy Gapon * If the slave offers an empty or a too long reply,
459f1519c01SAndriy Gapon * read one byte to generate the stop or abort.
460f1519c01SAndriy Gapon */
46188cc0bb9SAndriy Gapon if (*count > SMB_MAXBLOCKSIZE || *count == 0)
462f1519c01SAndriy Gapon block_msg[0].len = 1;
463f1519c01SAndriy Gapon else
464f1519c01SAndriy Gapon block_msg[0].len = *count;
465f1519c01SAndriy Gapon error = TRANSFER_MSGS(dev, block_msg);
46688cc0bb9SAndriy Gapon if (*count > SMB_MAXBLOCKSIZE || *count == 0)
467f1519c01SAndriy Gapon error = SMB_EINVAL;
468f1519c01SAndriy Gapon }
469f1519c01SAndriy Gapon (void)iicbus_release_bus(parent, dev);
470f1519c01SAndriy Gapon return (iic2smb_error(error));
471c3e2dc6bSNicolas Souchu }
472c3e2dc6bSNicolas Souchu
4733a866152SJohn Baldwin DRIVER_MODULE(iicsmb, iicbus, iicsmb_driver, 0, 0);
474c6d39765SJohn Baldwin DRIVER_MODULE(smbus, iicsmb, smbus_driver, 0, 0);
475c17d4340SNicolas Souchu MODULE_DEPEND(iicsmb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
476c17d4340SNicolas Souchu MODULE_DEPEND(iicsmb, smbus, SMBUS_MINVER, SMBUS_PREFVER, SMBUS_MAXVER);
477c17d4340SNicolas Souchu MODULE_VERSION(iicsmb, 1);
478