Expand SMBUS API to add smbus_trans() function.
Differential Revision: https://reviews.freebsd.org/D1955 Reviewed by: adrian, jhb, wblock Approved by: adrian, jhb
This commit is contained in:
parent
a9467c3c45
commit
202379af84
@ -1,5 +1,6 @@
|
|||||||
.\" Copyright (c) 1998, Nicolas Souchu
|
.\" Copyright (c) 1998, Nicolas Souchu
|
||||||
.\" Copyright (c) 2004, Joerg Wunsch
|
.\" Copyright (c) 2004, Joerg Wunsch
|
||||||
|
.\" Copyright (c) 2015, Michael Gmelin <freebsd@grem.de>
|
||||||
.\" All rights reserved.
|
.\" All rights reserved.
|
||||||
.\"
|
.\"
|
||||||
.\" Redistribution and use in source and binary forms, with or without
|
.\" Redistribution and use in source and binary forms, with or without
|
||||||
@ -25,7 +26,7 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" $FreeBSD$
|
.\" $FreeBSD$
|
||||||
.\"
|
.\"
|
||||||
.Dd February 6, 2009
|
.Dd April 25, 2015
|
||||||
.Dt SMB 4
|
.Dt SMB 4
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@ -49,21 +50,24 @@ as its argument.
|
|||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
struct smbcmd {
|
struct smbcmd {
|
||||||
char cmd;
|
u_char cmd;
|
||||||
int count;
|
u_char reserved;
|
||||||
u_char slave;
|
u_short op;
|
||||||
union {
|
union {
|
||||||
char byte;
|
char byte;
|
||||||
short word;
|
char buf[2];
|
||||||
|
short word;
|
||||||
char *byte_ptr;
|
} wdata;
|
||||||
short *word_ptr;
|
union {
|
||||||
|
char byte;
|
||||||
struct {
|
char buf[2];
|
||||||
short sdata;
|
short word;
|
||||||
short *rdata;
|
} rdata;
|
||||||
} process;
|
int slave;
|
||||||
} data;
|
char *wbuf; /* use wdata if NULL */
|
||||||
|
int wcount;
|
||||||
|
char *rbuf; /* use rdata if NULL */
|
||||||
|
int rcount;
|
||||||
};
|
};
|
||||||
.Ed
|
.Ed
|
||||||
.Pp
|
.Pp
|
||||||
@ -107,14 +111,14 @@ The
|
|||||||
command first sends the byte from the
|
command first sends the byte from the
|
||||||
.Fa cmd
|
.Fa cmd
|
||||||
field to the device, followed by the byte given in
|
field to the device, followed by the byte given in
|
||||||
.Fa data.byte .
|
.Fa wdata.byte .
|
||||||
.It Dv SMB_WRITEW Ta
|
.It Dv SMB_WRITEW Ta
|
||||||
The
|
The
|
||||||
.Em WriteWord
|
.Em WriteWord
|
||||||
command first sends the byte from the
|
command first sends the byte from the
|
||||||
.Fa cmd
|
.Fa cmd
|
||||||
field to the device, followed by the word given in
|
field to the device, followed by the word given in
|
||||||
.Fa data.word .
|
.Fa wdata.word .
|
||||||
Note that the SMBus byte-order is little-endian by definition.
|
Note that the SMBus byte-order is little-endian by definition.
|
||||||
.It Dv SMB_READB Ta
|
.It Dv SMB_READB Ta
|
||||||
The
|
The
|
||||||
@ -123,8 +127,8 @@ command first sends the byte from the
|
|||||||
.Fa cmd
|
.Fa cmd
|
||||||
field to the device, and then reads one byte of data from
|
field to the device, and then reads one byte of data from
|
||||||
the device.
|
the device.
|
||||||
The returned data will be stored in the location pointed to by
|
The returned data will be stored in
|
||||||
.Fa data.byte_ptr .
|
.Fa rdata.byte .
|
||||||
.It Dv SMB_READW Ta
|
.It Dv SMB_READW Ta
|
||||||
The
|
The
|
||||||
.Em ReadWord
|
.Em ReadWord
|
||||||
@ -132,29 +136,33 @@ command first sends the byte from the
|
|||||||
.Fa cmd
|
.Fa cmd
|
||||||
field to the device, and then reads one word of data from
|
field to the device, and then reads one word of data from
|
||||||
the device.
|
the device.
|
||||||
The returned data will be stored in the location pointed to by
|
The returned data will be stored in
|
||||||
.Fa data.word_ptr .
|
.Fa rdata.word .
|
||||||
.It Dv SMB_PCALL Ta
|
.It Dv SMB_PCALL Ta
|
||||||
The
|
The
|
||||||
.Em ProcedureCall
|
.Em ProcedureCall
|
||||||
command first sends the byte from the
|
command first sends the byte from the
|
||||||
.Fa cmd
|
.Fa cmd
|
||||||
field to the device, followed by the word provided in
|
field to the device, followed by the word provided in
|
||||||
.Fa data.process.sdata .
|
.Fa wdata.word .
|
||||||
It then reads one word of data from the device, and returns it
|
It then reads one word of data from the device, and returns it
|
||||||
in the location pointed to by
|
in
|
||||||
.Fa data.process.rdata .
|
.Fa rdata.word .
|
||||||
.It Dv SMB_BWRITE Ta
|
.It Dv SMB_BWRITE Ta
|
||||||
The
|
The
|
||||||
.Em BlockWrite
|
.Em BlockWrite
|
||||||
command first sends the byte from the
|
command first sends the byte from the
|
||||||
.Fa cmd
|
.Fa cmd
|
||||||
field to the device, followed by
|
field to the device, followed by
|
||||||
.Fa count
|
.Fa wcount
|
||||||
bytes of data that are taken from the buffer pointed to by
|
bytes of data that are taken from the buffer pointed to by
|
||||||
.Fa data.byte_ptr .
|
.Fa wbuf .
|
||||||
The SMBus specification mandates that no more than 32 bytes of
|
The SMBus specification mandates that no more than 32 bytes of
|
||||||
data can be transferred in a single block read or write command.
|
data can be transferred in a single block read or write command,
|
||||||
|
but since
|
||||||
|
.Xr smbus 4
|
||||||
|
is also used to access I2C devices, the limit has been increased
|
||||||
|
to 1024.
|
||||||
This value is available in the constant
|
This value is available in the constant
|
||||||
.Dv SMB_MAXBLOCKSIZE .
|
.Dv SMB_MAXBLOCKSIZE .
|
||||||
.It Dv SMB_BREAD Ta
|
.It Dv SMB_BREAD Ta
|
||||||
@ -163,10 +171,38 @@ The
|
|||||||
command first sends the byte from the
|
command first sends the byte from the
|
||||||
.Fa cmd
|
.Fa cmd
|
||||||
field to the device, and then reads
|
field to the device, and then reads
|
||||||
.Fa count
|
.Fa rcount
|
||||||
bytes of data that from the device.
|
bytes of data that from the device.
|
||||||
These data will be returned in the buffer pointed to by
|
These data will be returned in the buffer pointed to by
|
||||||
.Fa data.byte_ptr .
|
.Fa rbuf .
|
||||||
|
.It Dv SMB_TRANS Ta
|
||||||
|
The
|
||||||
|
.Em Trans
|
||||||
|
command sends an SMB roll-up transaction with flags that also allow it to
|
||||||
|
be used for (mostly) I2C pass-through and with with 10-bit addresses.
|
||||||
|
This function can be used to roll up all of the above functions.
|
||||||
|
It first sends the byte from the
|
||||||
|
.Fa cmd
|
||||||
|
field to the device, followed by
|
||||||
|
.Fa wcount
|
||||||
|
bytes of data that are taken from the buffer pointed to by
|
||||||
|
.Fa wbuf ,
|
||||||
|
then reads
|
||||||
|
.Fa rcount
|
||||||
|
bytes of data that from the device.
|
||||||
|
These data will be returned in the buffer pointed to by
|
||||||
|
.Fa rbuf .
|
||||||
|
.Pp
|
||||||
|
The following flags are allowed in
|
||||||
|
.Fa op :
|
||||||
|
.Pp
|
||||||
|
.Bd -literal -compact
|
||||||
|
SMB_TRANS_NOSTOP Do not send STOP at end
|
||||||
|
SMB_TRANS_NOCMD Ignore cmd field (do not tx)
|
||||||
|
SMB_TRANS_NOCNT Do not tx or rx count field
|
||||||
|
SMB_TRANS_7BIT Change address mode to 7-bit
|
||||||
|
SMB_TRANS_10BIT Change address mode to 10-bit
|
||||||
|
.Ed
|
||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
The
|
The
|
||||||
@ -201,4 +237,7 @@ manual page first appeared in
|
|||||||
.Sh AUTHORS
|
.Sh AUTHORS
|
||||||
This
|
This
|
||||||
manual page was written by
|
manual page was written by
|
||||||
.An Nicolas Souchu .
|
.An Nicolas Souchu
|
||||||
|
and extended by
|
||||||
|
.An Michael Gmelin Aq freebsd@grem.de
|
||||||
|
.
|
||||||
|
@ -26,10 +26,6 @@
|
|||||||
* $FreeBSD$
|
* $FreeBSD$
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
|
||||||
#include "opt_compat.h"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
#include <sys/kernel.h>
|
#include <sys/kernel.h>
|
||||||
#include <sys/systm.h>
|
#include <sys/systm.h>
|
||||||
@ -104,19 +100,24 @@ smb_identify(driver_t *driver, device_t parent)
|
|||||||
static int
|
static int
|
||||||
smb_probe(device_t dev)
|
smb_probe(device_t dev)
|
||||||
{
|
{
|
||||||
device_set_desc(dev, "SMBus generic I/O");
|
if (smbus_get_addr(dev) != -1)
|
||||||
|
return (ENXIO);
|
||||||
|
|
||||||
return (0);
|
device_set_desc(dev, "SMBus generic I/O");
|
||||||
|
return (BUS_PROBE_NOWILDCARD);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
smb_attach(device_t dev)
|
smb_attach(device_t dev)
|
||||||
{
|
{
|
||||||
struct smb_softc *sc = device_get_softc(dev);
|
struct smb_softc *sc = device_get_softc(dev);
|
||||||
|
int unit;
|
||||||
|
|
||||||
|
unit = device_get_unit(dev);
|
||||||
sc->sc_dev = dev;
|
sc->sc_dev = dev;
|
||||||
sc->sc_devnode = make_dev(&smb_cdevsw, device_get_unit(dev),
|
|
||||||
UID_ROOT, GID_WHEEL, 0600, "smb%d", device_get_unit(dev));
|
sc->sc_devnode = make_dev(&smb_cdevsw, unit, UID_ROOT, GID_WHEEL,
|
||||||
|
0600, "smb%d", unit);
|
||||||
sc->sc_devnode->si_drv1 = sc;
|
sc->sc_devnode->si_drv1 = sc;
|
||||||
mtx_init(&sc->sc_lock, device_get_nameunit(dev), NULL, MTX_DEF);
|
mtx_init(&sc->sc_lock, device_get_nameunit(dev), NULL, MTX_DEF);
|
||||||
|
|
||||||
@ -174,9 +175,16 @@ smbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t
|
|||||||
struct smb_softc *sc = dev->si_drv1;
|
struct smb_softc *sc = dev->si_drv1;
|
||||||
device_t smbdev = sc->sc_dev;
|
device_t smbdev = sc->sc_dev;
|
||||||
int error;
|
int error;
|
||||||
short w;
|
int unit;
|
||||||
u_char count;
|
u_char bcount;
|
||||||
char c;
|
|
||||||
|
/*
|
||||||
|
* If a specific slave device is being used, override any passed-in
|
||||||
|
* slave.
|
||||||
|
*/
|
||||||
|
unit = dev2unit(dev);
|
||||||
|
if (unit & 0x0400)
|
||||||
|
s->slave = unit & 0x03ff;
|
||||||
|
|
||||||
parent = device_get_parent(smbdev);
|
parent = device_get_parent(smbdev);
|
||||||
|
|
||||||
@ -208,77 +216,101 @@ smbioctl(struct cdev *dev, u_long cmd, caddr_t data, int flags, struct thread *t
|
|||||||
|
|
||||||
case SMB_WRITEB:
|
case SMB_WRITEB:
|
||||||
error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
|
error = smbus_error(smbus_writeb(parent, s->slave, s->cmd,
|
||||||
s->data.byte));
|
s->wdata.byte));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SMB_WRITEW:
|
case SMB_WRITEW:
|
||||||
error = smbus_error(smbus_writew(parent, s->slave,
|
error = smbus_error(smbus_writew(parent, s->slave,
|
||||||
s->cmd, s->data.word));
|
s->cmd, s->wdata.word));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SMB_READB:
|
case SMB_READB:
|
||||||
if (s->data.byte_ptr) {
|
error = smbus_error(smbus_readb(parent, s->slave, s->cmd,
|
||||||
error = smbus_error(smbus_readb(parent, s->slave,
|
&s->rdata.byte));
|
||||||
s->cmd, &c));
|
if (error)
|
||||||
if (error)
|
break;
|
||||||
break;
|
if (s->rbuf && s->rcount >= 1) {
|
||||||
error = copyout(&c, s->data.byte_ptr,
|
error = copyout(&s->rdata.byte, s->rbuf, 1);
|
||||||
sizeof(*(s->data.byte_ptr)));
|
s->rcount = 1;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SMB_READW:
|
case SMB_READW:
|
||||||
if (s->data.word_ptr) {
|
error = smbus_error(smbus_readw(parent, s->slave, s->cmd,
|
||||||
error = smbus_error(smbus_readw(parent, s->slave,
|
&s->rdata.word));
|
||||||
s->cmd, &w));
|
if (error)
|
||||||
if (error == 0) {
|
break;
|
||||||
error = copyout(&w, s->data.word_ptr,
|
if (s->rbuf && s->rcount >= 2) {
|
||||||
sizeof(*(s->data.word_ptr)));
|
buf[0] = (u_char)s->rdata.word;
|
||||||
}
|
buf[1] = (u_char)(s->rdata.word >> 8);
|
||||||
|
error = copyout(buf, s->rbuf, 2);
|
||||||
|
s->rcount = 2;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SMB_PCALL:
|
case SMB_PCALL:
|
||||||
if (s->data.process.rdata) {
|
error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
|
||||||
|
s->wdata.word, &s->rdata.word));
|
||||||
error = smbus_error(smbus_pcall(parent, s->slave, s->cmd,
|
if (error)
|
||||||
s->data.process.sdata, &w));
|
break;
|
||||||
if (error)
|
if (s->rbuf && s->rcount >= 2) {
|
||||||
break;
|
buf[0] = (u_char)s->rdata.word;
|
||||||
error = copyout(&w, s->data.process.rdata,
|
buf[1] = (u_char)(s->rdata.word >> 8);
|
||||||
sizeof(*(s->data.process.rdata)));
|
error = copyout(buf, s->rbuf, 2);
|
||||||
|
s->rcount = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SMB_BWRITE:
|
case SMB_BWRITE:
|
||||||
if (s->count && s->data.byte_ptr) {
|
if (s->wcount < 0) {
|
||||||
if (s->count > SMB_MAXBLOCKSIZE)
|
error = EINVAL;
|
||||||
s->count = SMB_MAXBLOCKSIZE;
|
break;
|
||||||
error = copyin(s->data.byte_ptr, buf, s->count);
|
|
||||||
if (error)
|
|
||||||
break;
|
|
||||||
error = smbus_error(smbus_bwrite(parent, s->slave,
|
|
||||||
s->cmd, s->count, buf));
|
|
||||||
}
|
}
|
||||||
|
if (s->wcount > SMB_MAXBLOCKSIZE)
|
||||||
|
s->wcount = SMB_MAXBLOCKSIZE;
|
||||||
|
if (s->wcount)
|
||||||
|
error = copyin(s->wbuf, buf, s->wcount);
|
||||||
|
if (error)
|
||||||
|
break;
|
||||||
|
error = smbus_error(smbus_bwrite(parent, s->slave, s->cmd,
|
||||||
|
s->wcount, buf));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
#if defined(COMPAT_FREEBSD4) || defined(COMPAT_FREEBSD5) || defined(COMPAT_FREEBSD6)
|
|
||||||
case SMB_OLD_BREAD:
|
|
||||||
#endif
|
|
||||||
case SMB_BREAD:
|
case SMB_BREAD:
|
||||||
if (s->count && s->data.byte_ptr) {
|
if (s->rcount < 0) {
|
||||||
count = min(s->count, SMB_MAXBLOCKSIZE);
|
error = EINVAL;
|
||||||
error = smbus_error(smbus_bread(parent, s->slave,
|
break;
|
||||||
s->cmd, &count, buf));
|
|
||||||
if (error)
|
|
||||||
break;
|
|
||||||
error = copyout(buf, s->data.byte_ptr,
|
|
||||||
min(count, s->count));
|
|
||||||
s->count = count;
|
|
||||||
}
|
}
|
||||||
|
if (s->rcount > SMB_MAXBLOCKSIZE)
|
||||||
|
s->rcount = SMB_MAXBLOCKSIZE;
|
||||||
|
error = smbus_error(smbus_bread(parent, s->slave, s->cmd,
|
||||||
|
&bcount, buf));
|
||||||
|
if (error)
|
||||||
|
break;
|
||||||
|
if (s->rcount > bcount)
|
||||||
|
s->rcount = bcount;
|
||||||
|
error = copyout(buf, s->rbuf, s->rcount);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SMB_TRANS:
|
||||||
|
if (s->rcount < 0 || s->wcount < 0) {
|
||||||
|
error = EINVAL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (s->rcount > SMB_MAXBLOCKSIZE)
|
||||||
|
s->rcount = SMB_MAXBLOCKSIZE;
|
||||||
|
if (s->wcount > SMB_MAXBLOCKSIZE)
|
||||||
|
s->wcount = SMB_MAXBLOCKSIZE;
|
||||||
|
if (s->wcount)
|
||||||
|
error = copyin(s->wbuf, buf, s->wcount);
|
||||||
|
if (error)
|
||||||
|
break;
|
||||||
|
error = smbus_error(smbus_trans(parent, s->slave, s->cmd,
|
||||||
|
s->op, buf, s->wcount, buf, s->rcount, &s->rcount));
|
||||||
|
if (error == 0)
|
||||||
|
error = copyout(buf, s->rbuf, s->rcount);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
error = ENOTTY;
|
error = ENOTTY;
|
||||||
}
|
}
|
||||||
|
@ -32,27 +32,33 @@
|
|||||||
#include <sys/ioccom.h>
|
#include <sys/ioccom.h>
|
||||||
|
|
||||||
struct smbcmd {
|
struct smbcmd {
|
||||||
char cmd;
|
u_char cmd;
|
||||||
int count;
|
u_char reserved;
|
||||||
u_char slave;
|
u_short op;
|
||||||
union {
|
union {
|
||||||
char byte;
|
char byte;
|
||||||
short word;
|
char buf[2];
|
||||||
|
short word;
|
||||||
char *byte_ptr;
|
} wdata;
|
||||||
short *word_ptr;
|
union {
|
||||||
|
char byte;
|
||||||
struct {
|
char buf[2];
|
||||||
short sdata;
|
short word;
|
||||||
short *rdata;
|
} rdata;
|
||||||
} process;
|
int slave;
|
||||||
} data;
|
char *wbuf; /* use wdata if NULL */
|
||||||
|
int wcount;
|
||||||
|
char *rbuf; /* use rdata if NULL */
|
||||||
|
int rcount;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* SMBus spec 2.0 says block transfers may be at most 32 bytes.
|
* SMBus spec 2.0 says block transfers may be at most 32 bytes.
|
||||||
|
* We use SMBus for i2c as well, make the size limit something more
|
||||||
|
* reasonable. Keep in mind that a char buf array is declared on the
|
||||||
|
* kernel stack.
|
||||||
*/
|
*/
|
||||||
#define SMB_MAXBLOCKSIZE 32
|
#define SMB_MAXBLOCKSIZE 1024
|
||||||
|
|
||||||
#define SMB_QUICK_WRITE _IOW('i', 1, struct smbcmd)
|
#define SMB_QUICK_WRITE _IOW('i', 1, struct smbcmd)
|
||||||
#define SMB_QUICK_READ _IOW('i', 2, struct smbcmd)
|
#define SMB_QUICK_READ _IOW('i', 2, struct smbcmd)
|
||||||
@ -66,5 +72,6 @@ struct smbcmd {
|
|||||||
#define SMB_BWRITE _IOW('i', 10, struct smbcmd)
|
#define SMB_BWRITE _IOW('i', 10, struct smbcmd)
|
||||||
#define SMB_OLD_BREAD _IOW('i', 11, struct smbcmd)
|
#define SMB_OLD_BREAD _IOW('i', 11, struct smbcmd)
|
||||||
#define SMB_BREAD _IOWR('i', 11, struct smbcmd)
|
#define SMB_BREAD _IOWR('i', 11, struct smbcmd)
|
||||||
|
#define SMB_TRANS _IOWR('i', 12, struct smbcmd)
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -67,10 +67,31 @@
|
|||||||
#define SMB_QWRITE 0x0
|
#define SMB_QWRITE 0x0
|
||||||
#define SMB_QREAD 0x1
|
#define SMB_QREAD 0x1
|
||||||
|
|
||||||
|
/*
|
||||||
|
* smbus transction op with pass-thru capabilities
|
||||||
|
*
|
||||||
|
* This smbus function is capable of doing a smbus command transaction
|
||||||
|
* (read or write), and can be flagged to not issue the 'cmd' and/or
|
||||||
|
* issue or expect a count field as well as flagged for chaining (no STOP),
|
||||||
|
* which gives it an i2c pass-through capability.
|
||||||
|
*
|
||||||
|
* NOSTOP- Caller chaining transactions, do not issue STOP
|
||||||
|
* NOCMD- Do not transmit the command field
|
||||||
|
* NOCNT- Do not transmit (wr) or expect (rd) the count field
|
||||||
|
*/
|
||||||
|
#define SMB_TRANS_NOSTOP 0x0001 /* do not send STOP at end */
|
||||||
|
#define SMB_TRANS_NOCMD 0x0002 /* ignore cmd field (do not tx) */
|
||||||
|
#define SMB_TRANS_NOCNT 0x0004 /* do not tx or rx count field */
|
||||||
|
#define SMB_TRANS_7BIT 0x0008 /* change address mode to 7-bit */
|
||||||
|
#define SMB_TRANS_10BIT 0x0010 /* change address mode to 10-bit */
|
||||||
|
#define SMB_TRANS_NOREPORT 0x0020 /* do not report errors */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ivars codes
|
* ivars codes
|
||||||
*/
|
*/
|
||||||
#define SMBUS_IVAR_ADDR 0x1 /* slave address of the device */
|
enum smbus_ivars {
|
||||||
|
SMBUS_IVAR_ADDR, /* slave address of the device */
|
||||||
|
};
|
||||||
|
|
||||||
int smbus_request_bus(device_t, device_t, int);
|
int smbus_request_bus(device_t, device_t, int);
|
||||||
int smbus_release_bus(device_t, device_t);
|
int smbus_release_bus(device_t, device_t);
|
||||||
@ -79,7 +100,12 @@ int smbus_error(int error);
|
|||||||
|
|
||||||
void smbus_intr(device_t, u_char, char low, char high, int error);
|
void smbus_intr(device_t, u_char, char low, char high, int error);
|
||||||
|
|
||||||
u_char smbus_get_addr(device_t);
|
#define SMBUS_ACCESSOR(var, ivar, type) \
|
||||||
|
__BUS_ACCESSOR(smbus, var, SMBUS, ivar, type)
|
||||||
|
|
||||||
|
SMBUS_ACCESSOR(addr, ADDR, int)
|
||||||
|
|
||||||
|
#undef SMBUS_ACCESSOR
|
||||||
|
|
||||||
extern driver_t smbus_driver;
|
extern driver_t smbus_driver;
|
||||||
extern devclass_t smbus_devclass;
|
extern devclass_t smbus_devclass;
|
||||||
@ -104,6 +130,9 @@ extern devclass_t smbus_devclass;
|
|||||||
(SMBUS_BWRITE(device_get_parent(bus), slave, cmd, count, buf))
|
(SMBUS_BWRITE(device_get_parent(bus), slave, cmd, count, buf))
|
||||||
#define smbus_bread(bus,slave,cmd,count,buf) \
|
#define smbus_bread(bus,slave,cmd,count,buf) \
|
||||||
(SMBUS_BREAD(device_get_parent(bus), slave, cmd, count, buf))
|
(SMBUS_BREAD(device_get_parent(bus), slave, cmd, count, buf))
|
||||||
|
#define smbus_trans(bus,slave,cmd,op,wbuf,wcount,rbuf,rcount,actualp) \
|
||||||
|
(SMBUS_TRANS(device_get_parent(bus), slave, cmd, op, \
|
||||||
|
wbuf, wcount, rbuf, rcount, actualp))
|
||||||
|
|
||||||
#define SMBUS_MODVER 1
|
#define SMBUS_MODVER 1
|
||||||
#define SMBUS_MINVER 1
|
#define SMBUS_MINVER 1
|
||||||
|
@ -33,11 +33,15 @@ __FBSDID("$FreeBSD$");
|
|||||||
#include <sys/lock.h>
|
#include <sys/lock.h>
|
||||||
#include <sys/module.h>
|
#include <sys/module.h>
|
||||||
#include <sys/mutex.h>
|
#include <sys/mutex.h>
|
||||||
#include <sys/bus.h>
|
#include <sys/bus.h>
|
||||||
|
|
||||||
#include <dev/smbus/smbconf.h>
|
#include <dev/smbus/smbconf.h>
|
||||||
#include <dev/smbus/smbus.h>
|
#include <dev/smbus/smbus.h>
|
||||||
|
|
||||||
|
#include "smbus_if.h"
|
||||||
|
#include "bus_if.h"
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Autoconfiguration and support routines for System Management bus
|
* Autoconfiguration and support routines for System Management bus
|
||||||
*/
|
*/
|
||||||
@ -49,6 +53,13 @@ static int smbus_probe(device_t);
|
|||||||
static int smbus_attach(device_t);
|
static int smbus_attach(device_t);
|
||||||
static int smbus_detach(device_t);
|
static int smbus_detach(device_t);
|
||||||
|
|
||||||
|
static int smbus_child_location_str(device_t parent, device_t child,
|
||||||
|
char *buf, size_t buflen);
|
||||||
|
static int smbus_print_child(device_t parent, device_t child);
|
||||||
|
static void smbus_probe_device(device_t dev, u_char* addr);
|
||||||
|
static int smbus_read_ivar(device_t parent, device_t child, int which,
|
||||||
|
uintptr_t *result);
|
||||||
|
|
||||||
static device_method_t smbus_methods[] = {
|
static device_method_t smbus_methods[] = {
|
||||||
/* device interface */
|
/* device interface */
|
||||||
DEVMETHOD(device_probe, smbus_probe),
|
DEVMETHOD(device_probe, smbus_probe),
|
||||||
@ -57,6 +68,10 @@ static device_method_t smbus_methods[] = {
|
|||||||
|
|
||||||
/* bus interface */
|
/* bus interface */
|
||||||
DEVMETHOD(bus_add_child, bus_generic_add_child),
|
DEVMETHOD(bus_add_child, bus_generic_add_child),
|
||||||
|
DEVMETHOD(bus_child_location_str, smbus_child_location_str),
|
||||||
|
DEVMETHOD(bus_driver_added, bus_generic_driver_added),
|
||||||
|
DEVMETHOD(bus_print_child, smbus_print_child),
|
||||||
|
DEVMETHOD(bus_read_ivar, smbus_read_ivar),
|
||||||
|
|
||||||
DEVMETHOD_END
|
DEVMETHOD_END
|
||||||
};
|
};
|
||||||
@ -87,9 +102,14 @@ static int
|
|||||||
smbus_attach(device_t dev)
|
smbus_attach(device_t dev)
|
||||||
{
|
{
|
||||||
struct smbus_softc *sc = device_get_softc(dev);
|
struct smbus_softc *sc = device_get_softc(dev);
|
||||||
|
unsigned char addr;
|
||||||
|
|
||||||
mtx_init(&sc->lock, device_get_nameunit(dev), "smbus", MTX_DEF);
|
mtx_init(&sc->lock, device_get_nameunit(dev), "smbus", MTX_DEF);
|
||||||
bus_generic_probe(dev);
|
bus_generic_probe(dev);
|
||||||
|
for (addr = SMBUS_ADDR_MIN; addr < SMBUS_ADDR_MAX; ++addr) {
|
||||||
|
sc->addrs[addr] = addr;
|
||||||
|
smbus_probe_device(dev, &sc->addrs[addr]);
|
||||||
|
}
|
||||||
bus_generic_attach(dev);
|
bus_generic_attach(dev);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
@ -114,4 +134,70 @@ smbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
smbus_probe_device(device_t dev, u_char* addr)
|
||||||
|
{
|
||||||
|
device_t child;
|
||||||
|
int error;
|
||||||
|
u_char cmd;
|
||||||
|
u_char buf[2];
|
||||||
|
|
||||||
|
cmd = 0x01;
|
||||||
|
error = smbus_trans(dev, *addr, cmd,
|
||||||
|
SMB_TRANS_NOCNT | SMB_TRANS_NOREPORT,
|
||||||
|
NULL, 0, buf, 1, NULL);
|
||||||
|
if (error == 0) {
|
||||||
|
if (bootverbose)
|
||||||
|
device_printf(dev, "Probed address 0x%02x\n", *addr);
|
||||||
|
child = device_add_child(dev, NULL, -1);
|
||||||
|
device_set_ivars(child, addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smbus_child_location_str(device_t parent, device_t child, char *buf,
|
||||||
|
size_t buflen)
|
||||||
|
{
|
||||||
|
unsigned char *addr;
|
||||||
|
|
||||||
|
addr = device_get_ivars(child);
|
||||||
|
if (addr)
|
||||||
|
snprintf(buf, buflen, "addr=0x%x", *addr);
|
||||||
|
else if (buflen)
|
||||||
|
buf[0] = 0;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smbus_print_child(device_t parent, device_t child)
|
||||||
|
{
|
||||||
|
unsigned char *addr;
|
||||||
|
int retval;
|
||||||
|
|
||||||
|
addr = device_get_ivars(child);
|
||||||
|
retval = bus_print_child_header(parent, child);
|
||||||
|
if (addr)
|
||||||
|
retval += printf(" at addr 0x%x", *addr);
|
||||||
|
retval += bus_print_child_footer(parent, child);
|
||||||
|
|
||||||
|
return (retval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
smbus_read_ivar(device_t parent, device_t child, int which,
|
||||||
|
uintptr_t *result)
|
||||||
|
{
|
||||||
|
unsigned char *addr;
|
||||||
|
|
||||||
|
addr = device_get_ivars(child);
|
||||||
|
switch (which) {
|
||||||
|
case SMBUS_IVAR_ADDR:
|
||||||
|
*result = (addr == NULL) ? -1 : *addr;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return (ENOENT);
|
||||||
|
}
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
MODULE_VERSION(smbus, SMBUS_MODVER);
|
MODULE_VERSION(smbus, SMBUS_MODVER);
|
||||||
|
@ -29,9 +29,13 @@
|
|||||||
#ifndef __SMBUS_H
|
#ifndef __SMBUS_H
|
||||||
#define __SMBUS_H
|
#define __SMBUS_H
|
||||||
|
|
||||||
|
#define SMBUS_ADDR_MIN 0x10
|
||||||
|
#define SMBUS_ADDR_MAX 0x70
|
||||||
|
|
||||||
struct smbus_softc {
|
struct smbus_softc {
|
||||||
device_t owner; /* smbus owner device structure */
|
device_t owner; /* smbus owner device structure */
|
||||||
struct mtx lock;
|
struct mtx lock;
|
||||||
|
unsigned char addrs[SMBUS_ADDR_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
void smbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err);
|
void smbus_generic_intr(device_t dev, u_char devaddr, char low, char high, int err);
|
||||||
|
@ -149,3 +149,20 @@ METHOD int bread {
|
|||||||
u_char *count;
|
u_char *count;
|
||||||
char *buf;
|
char *buf;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#
|
||||||
|
# SMB roll-up transaction with flags that also allow it to be
|
||||||
|
# used for (mostly) i2c pass-through and with 10-bit addresses.
|
||||||
|
# This function can be used to roll-up all of the above functions.
|
||||||
|
#
|
||||||
|
METHOD int trans {
|
||||||
|
device_t dev;
|
||||||
|
int slave;
|
||||||
|
char cmd;
|
||||||
|
int op;
|
||||||
|
char *wbuf;
|
||||||
|
int wcount;
|
||||||
|
char *rbuf;
|
||||||
|
int rcount;
|
||||||
|
int *actualp;
|
||||||
|
};
|
||||||
|
@ -163,7 +163,8 @@ do_io(void)
|
|||||||
}
|
}
|
||||||
if (iflag == 1 && oflag == -1) {
|
if (iflag == 1 && oflag == -1) {
|
||||||
/* command + 1 byte input: read byte op. */
|
/* command + 1 byte input: read byte op. */
|
||||||
c.data.byte_ptr = ibuf;
|
c.rbuf = ibuf;
|
||||||
|
c.rcount = iflag;
|
||||||
if (ioctl(fd, SMB_READB, &c) == -1)
|
if (ioctl(fd, SMB_READB, &c) == -1)
|
||||||
return (-1);
|
return (-1);
|
||||||
printf(fmt, (int)(unsigned char)ibuf[0]);
|
printf(fmt, (int)(unsigned char)ibuf[0]);
|
||||||
@ -171,11 +172,12 @@ do_io(void)
|
|||||||
return (0);
|
return (0);
|
||||||
} else if (iflag == -1 && oflag == 1) {
|
} else if (iflag == -1 && oflag == 1) {
|
||||||
/* command + 1 byte output: write byte op. */
|
/* command + 1 byte output: write byte op. */
|
||||||
c.data.byte = obuf[0];
|
c.wdata.byte = obuf[0];
|
||||||
return (ioctl(fd, SMB_WRITEB, &c));
|
return (ioctl(fd, SMB_WRITEB, &c));
|
||||||
} else if (wflag && iflag == 2 && oflag == -1) {
|
} else if (wflag && iflag == 2 && oflag == -1) {
|
||||||
/* command + 2 bytes input: read word op. */
|
/* command + 2 bytes input: read word op. */
|
||||||
c.data.word_ptr = &iword;
|
c.rbuf = (char*) &iword;
|
||||||
|
c.rcount = iflag;
|
||||||
if (ioctl(fd, SMB_READW, &c) == -1)
|
if (ioctl(fd, SMB_READW, &c) == -1)
|
||||||
return (-1);
|
return (-1);
|
||||||
printf(fmt, (int)(unsigned short)iword);
|
printf(fmt, (int)(unsigned short)iword);
|
||||||
@ -183,15 +185,16 @@ do_io(void)
|
|||||||
return (0);
|
return (0);
|
||||||
} else if (wflag && iflag == -1 && oflag == 2) {
|
} else if (wflag && iflag == -1 && oflag == 2) {
|
||||||
/* command + 2 bytes output: write word op. */
|
/* command + 2 bytes output: write word op. */
|
||||||
c.data.word = oword;
|
c.wdata.word = oword;
|
||||||
return (ioctl(fd, SMB_WRITEW, &c));
|
return (ioctl(fd, SMB_WRITEW, &c));
|
||||||
} else if (wflag && iflag == 2 && oflag == 2) {
|
} else if (wflag && iflag == 2 && oflag == 2) {
|
||||||
/*
|
/*
|
||||||
* command + 2 bytes output + 2 bytes input:
|
* command + 2 bytes output + 2 bytes input:
|
||||||
* "process call" op.
|
* "process call" op.
|
||||||
*/
|
*/
|
||||||
c.data.process.sdata = oword;
|
c.wdata.word = oword;
|
||||||
c.data.process.rdata = &iword;
|
c.rbuf = (char*) &iword;
|
||||||
|
c.rcount = iflag;
|
||||||
if (ioctl(fd, SMB_PCALL, &c) == -1)
|
if (ioctl(fd, SMB_PCALL, &c) == -1)
|
||||||
return (-1);
|
return (-1);
|
||||||
printf(fmt, (int)(unsigned short)iword);
|
printf(fmt, (int)(unsigned short)iword);
|
||||||
@ -199,8 +202,8 @@ do_io(void)
|
|||||||
return (0);
|
return (0);
|
||||||
} else if (iflag > 1 && oflag == -1) {
|
} else if (iflag > 1 && oflag == -1) {
|
||||||
/* command + > 1 bytes of input: block read */
|
/* command + > 1 bytes of input: block read */
|
||||||
c.data.byte_ptr = ibuf;
|
c.rbuf = ibuf;
|
||||||
c.count = iflag;
|
c.rcount = iflag;
|
||||||
if (ioctl(fd, SMB_BREAD, &c) == -1)
|
if (ioctl(fd, SMB_BREAD, &c) == -1)
|
||||||
return (-1);
|
return (-1);
|
||||||
for (i = 0; i < iflag; i++) {
|
for (i = 0; i < iflag; i++) {
|
||||||
@ -212,8 +215,8 @@ do_io(void)
|
|||||||
return (0);
|
return (0);
|
||||||
} else if (iflag == -1 && oflag > 1) {
|
} else if (iflag == -1 && oflag > 1) {
|
||||||
/* command + > 1 bytes of output: block write */
|
/* command + > 1 bytes of output: block write */
|
||||||
c.data.byte_ptr = obuf;
|
c.wbuf = obuf;
|
||||||
c.count = oflag;
|
c.wcount = oflag;
|
||||||
return (ioctl(fd, SMB_BWRITE, &c));
|
return (ioctl(fd, SMB_BWRITE, &c));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user