1998-09-03 20:51:50 +00:00
|
|
|
/*-
|
2017-11-27 14:52:40 +00:00
|
|
|
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
|
|
*
|
1998-09-03 20:51:50 +00:00
|
|
|
* Copyright (c) 1998 Nicolas Souchu
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
2003-08-24 17:55:58 +00:00
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
1998-09-03 20:51:50 +00:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
2008-08-04 21:03:06 +00:00
|
|
|
#include <sys/lock.h>
|
2007-03-23 23:03:54 +00:00
|
|
|
#include <sys/malloc.h>
|
1998-09-03 20:51:50 +00:00
|
|
|
#include <sys/module.h>
|
2008-08-04 21:03:06 +00:00
|
|
|
#include <sys/mutex.h>
|
1998-09-03 20:51:50 +00:00
|
|
|
#include <sys/bus.h>
|
|
|
|
|
|
|
|
#include <dev/iicbus/iiconf.h>
|
|
|
|
#include <dev/iicbus/iicbus.h>
|
|
|
|
#include "iicbus_if.h"
|
|
|
|
|
Create a mechanism for encoding a system errno into the IIC_Exxxxx space.
Errors are communicated between the i2c controller layer and upper layers
(iicbus and slave device drivers) using a set of IIC_Exxxxxx constants which
effectively define a private number space separate from (and having values
that conflict with) the system errno number space. Sometimes it is necessary
to report a plain old system error (especially EINTR) from the controller or
bus layer and have that value make it back across the syscall interface
intact.
I initially considered replicating a few "crucial" errno values with similar
names and new numbers, e.g., IIC_EINTR, IIC_ERESTART, etc. It seemed like
that had the potential to grow over time until many of the errno names were
duplicated into the IIC_Exxxxx space.
So instead, this defines a mechanism to "encode" an errno into the IIC_Exxxx
space by setting the high bit and putting the errno into the lower-order
bits; a new errno2iic() function does this. The existing iic2errno()
recognizes the encoded values and extracts the original errno out of the
encoded value. An interesting wrinkle occurs with the pseudo-error values
such as ERESTART -- they aleady have the high bit set, and turning it off
would be the wrong thing to do. Instead, iic2errno() recognizes that lots of
high bits are on (i.e., it's a negative number near to zero) and just
returns that value as-is.
Thus, existing drivers continue to work without needing any changes, and
there is now a way to return errno values from the lower layers. The first
use of that is in iicbus_poll() which does mtx_sleep() with the PCATCH flag,
and needs to return the errno from that up the call chain.
Differential Revision: https://reviews.freebsd.org/D20975
2019-09-14 19:33:36 +00:00
|
|
|
/*
|
|
|
|
* Encode a system errno value into the IIC_Exxxxx space by setting the
|
|
|
|
* IIC_ERRNO marker bit, so that iic2errno() can turn it back into a plain
|
|
|
|
* system errno value later. This lets controller- and bus-layer code get
|
|
|
|
* important system errno values (such as EINTR/ERESTART) back to the caller.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
errno2iic(int errno)
|
|
|
|
{
|
|
|
|
return ((errno == 0) ? 0 : errno | IIC_ERRNO);
|
|
|
|
}
|
|
|
|
|
2015-10-09 23:20:08 +00:00
|
|
|
/*
|
|
|
|
* Translate IIC_Exxxxx status values to vaguely-equivelent errno values.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iic2errno(int iic_status)
|
|
|
|
{
|
|
|
|
switch (iic_status) {
|
|
|
|
case IIC_NOERR: return (0);
|
|
|
|
case IIC_EBUSERR: return (EALREADY);
|
|
|
|
case IIC_ENOACK: return (EIO);
|
|
|
|
case IIC_ETIMEOUT: return (ETIMEDOUT);
|
|
|
|
case IIC_EBUSBSY: return (EWOULDBLOCK);
|
|
|
|
case IIC_ESTATUS: return (EPROTO);
|
|
|
|
case IIC_EUNDERFLOW: return (EIO);
|
|
|
|
case IIC_EOVERFLOW: return (EOVERFLOW);
|
|
|
|
case IIC_ENOTSUPP: return (EOPNOTSUPP);
|
|
|
|
case IIC_ENOADDR: return (EADDRNOTAVAIL);
|
|
|
|
case IIC_ERESOURCE: return (ENOMEM);
|
Create a mechanism for encoding a system errno into the IIC_Exxxxx space.
Errors are communicated between the i2c controller layer and upper layers
(iicbus and slave device drivers) using a set of IIC_Exxxxxx constants which
effectively define a private number space separate from (and having values
that conflict with) the system errno number space. Sometimes it is necessary
to report a plain old system error (especially EINTR) from the controller or
bus layer and have that value make it back across the syscall interface
intact.
I initially considered replicating a few "crucial" errno values with similar
names and new numbers, e.g., IIC_EINTR, IIC_ERESTART, etc. It seemed like
that had the potential to grow over time until many of the errno names were
duplicated into the IIC_Exxxxx space.
So instead, this defines a mechanism to "encode" an errno into the IIC_Exxxx
space by setting the high bit and putting the errno into the lower-order
bits; a new errno2iic() function does this. The existing iic2errno()
recognizes the encoded values and extracts the original errno out of the
encoded value. An interesting wrinkle occurs with the pseudo-error values
such as ERESTART -- they aleady have the high bit set, and turning it off
would be the wrong thing to do. Instead, iic2errno() recognizes that lots of
high bits are on (i.e., it's a negative number near to zero) and just
returns that value as-is.
Thus, existing drivers continue to work without needing any changes, and
there is now a way to return errno values from the lower layers. The first
use of that is in iicbus_poll() which does mtx_sleep() with the PCATCH flag,
and needs to return the errno from that up the call chain.
Differential Revision: https://reviews.freebsd.org/D20975
2019-09-14 19:33:36 +00:00
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* If the high bit is set, that means it's a system errno value
|
|
|
|
* that was encoded into the IIC_Exxxxxx space by setting the
|
|
|
|
* IIC_ERRNO marker bit. If lots of high-order bits are set,
|
|
|
|
* then it's one of the negative pseudo-errors such as ERESTART
|
|
|
|
* and we return it as-is. Otherwise it's a plain "small
|
|
|
|
* positive integer" errno, so just remove the IIC_ERRNO marker
|
|
|
|
* bit. If it's some unknown number without the high bit set,
|
|
|
|
* there isn't much we can do except call it an I/O error.
|
|
|
|
*/
|
|
|
|
if ((iic_status & IIC_ERRNO) == 0)
|
|
|
|
return (EIO);
|
|
|
|
if ((iic_status & 0xFFFF0000) != 0)
|
|
|
|
return (iic_status);
|
|
|
|
return (iic_status & ~IIC_ERRNO);
|
2015-10-09 23:20:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-09-03 20:51:50 +00:00
|
|
|
/*
|
|
|
|
* iicbus_intr()
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
iicbus_intr(device_t bus, int event, char *buf)
|
|
|
|
{
|
|
|
|
struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
|
|
|
|
|
|
|
|
/* call owner's intr routine */
|
|
|
|
if (sc->owner)
|
|
|
|
IICBUS_INTR(sc->owner, event, buf);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
1998-10-31 11:31:07 +00:00
|
|
|
static int
|
|
|
|
iicbus_poll(struct iicbus_softc *sc, int how)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
2008-08-04 21:03:06 +00:00
|
|
|
IICBUS_ASSERT_LOCKED(sc);
|
2018-01-23 23:30:19 +00:00
|
|
|
switch (how & IIC_INTRWAIT) {
|
2008-08-04 21:03:06 +00:00
|
|
|
case IIC_WAIT | IIC_INTR:
|
|
|
|
error = mtx_sleep(sc, &sc->lock, IICPRI|PCATCH, "iicreq", 0);
|
1998-10-31 11:31:07 +00:00
|
|
|
break;
|
|
|
|
|
2008-08-04 21:03:06 +00:00
|
|
|
case IIC_WAIT | IIC_NOINTR:
|
|
|
|
error = mtx_sleep(sc, &sc->lock, IICPRI, "iicreq", 0);
|
1998-10-31 11:31:07 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2015-10-10 00:12:03 +00:00
|
|
|
return (IIC_EBUSBSY);
|
1998-10-31 11:31:07 +00:00
|
|
|
}
|
|
|
|
|
Create a mechanism for encoding a system errno into the IIC_Exxxxx space.
Errors are communicated between the i2c controller layer and upper layers
(iicbus and slave device drivers) using a set of IIC_Exxxxxx constants which
effectively define a private number space separate from (and having values
that conflict with) the system errno number space. Sometimes it is necessary
to report a plain old system error (especially EINTR) from the controller or
bus layer and have that value make it back across the syscall interface
intact.
I initially considered replicating a few "crucial" errno values with similar
names and new numbers, e.g., IIC_EINTR, IIC_ERESTART, etc. It seemed like
that had the potential to grow over time until many of the errno names were
duplicated into the IIC_Exxxxx space.
So instead, this defines a mechanism to "encode" an errno into the IIC_Exxxx
space by setting the high bit and putting the errno into the lower-order
bits; a new errno2iic() function does this. The existing iic2errno()
recognizes the encoded values and extracts the original errno out of the
encoded value. An interesting wrinkle occurs with the pseudo-error values
such as ERESTART -- they aleady have the high bit set, and turning it off
would be the wrong thing to do. Instead, iic2errno() recognizes that lots of
high bits are on (i.e., it's a negative number near to zero) and just
returns that value as-is.
Thus, existing drivers continue to work without needing any changes, and
there is now a way to return errno values from the lower layers. The first
use of that is in iicbus_poll() which does mtx_sleep() with the PCATCH flag,
and needs to return the errno from that up the call chain.
Differential Revision: https://reviews.freebsd.org/D20975
2019-09-14 19:33:36 +00:00
|
|
|
return (errno2iic(error));
|
1998-10-31 11:31:07 +00:00
|
|
|
}
|
|
|
|
|
1998-09-03 20:51:50 +00:00
|
|
|
/*
|
|
|
|
* iicbus_request_bus()
|
|
|
|
*
|
|
|
|
* Allocate the device to perform transfers.
|
|
|
|
*
|
|
|
|
* how : IIC_WAIT or IIC_DONTWAIT
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iicbus_request_bus(device_t bus, device_t dev, int how)
|
|
|
|
{
|
Add support for i2c bus mux hardware.
An i2c bus can be divided into segments which can be selectively connected
and disconnected from the main bus. This is usually done to enable using
multiple slave devices having the same address, by isolating the devices
onto separate bus segments, only one of which is connected to the main bus
at once.
There are several types of i2c bus muxes, which break down into two general
categories...
- Muxes which are themselves i2c slaves. These devices respond to i2c
commands on their upstream bus, and based on those commands, connect
various downstream buses to the upstream. In newbus terms, they are both
a child of an iicbus and the parent of one or more iicbus instances.
- Muxes which are not i2c devices themselves. Such devices are part of the
i2c bus electrically, but in newbus terms their parent is some other
bus. The association with the upstream bus must be established by
separate metadata (such as FDT data).
In both cases, the mux driver has one or more iicbus child instances
representing the downstream buses. The mux driver implements the iicbus_if
interface, as if it were an iichb host bridge/i2c controller driver. It
services the IO requests sent to it by forwarding them to the iicbus
instance representing the upstream bus, after electrically connecting the
upstream bus to the downstream bus that hosts the i2c slave device which
made the IO request.
The net effect is automatic mux switching which is transparent to slaves on
the downstream buses. They just do i2c IO they way they normally do, and the
bus is electrically connected for the duration of the IO and then idled when
it is complete.
The existing iicbus_if callback() method is enhanced so that the parameter
passed to it can be a struct which contains a device_t for the requesting
bus and slave devices. This change is done by adding a flag that indicates
the extra values are present, and making the flags field the first field of
a new args struct. If the flag is set, the iichb or mux driver can recast
the pointer-to-flags into a pointer-to-struct and access the extra
fields. Thus abi compatibility with older drivers is retained (but a mux
cannot exist on the bus with the older iicbus driver in use.)
A new set of core support routines exists in iicbus.c. This code will help
implement mux drivers for any type of mux hardware by supplying all the
boilerplate code that forwards IO requests upstream. It also has code for
parsing metadata and instantiating the child iicbus instances based on it.
Two new hardware mux drivers are added. The ltc430x driver supports the
LTC4305/4306 mux chips which are controlled via i2c commands. The
iic_gpiomux driver supports any mux hardware which is controlled by
manipulating the state of one or more gpio pins. Test Plan
Tested locally using a variety of mux'd bus configurations involving both
ltc4305 and a homebrew gpio-controlled mux. Tested configurations included
cascaded muxes (unlikely in the real world, but useful to prove that 'it all
just works' in terms of the automatic switching and upstream forwarding of
IO requests).
2020-01-02 17:51:49 +00:00
|
|
|
struct iic_reqbus_data reqdata;
|
1998-09-03 20:51:50 +00:00
|
|
|
struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
|
2008-08-04 21:03:06 +00:00
|
|
|
int error = 0;
|
1998-09-03 20:51:50 +00:00
|
|
|
|
2008-08-04 21:03:06 +00:00
|
|
|
IICBUS_LOCK(sc);
|
1998-09-03 20:51:50 +00:00
|
|
|
|
2018-01-23 23:30:19 +00:00
|
|
|
for (;;) {
|
|
|
|
if (sc->owner == NULL)
|
|
|
|
break;
|
|
|
|
if ((how & IIC_RECURSIVE) && sc->owner == dev)
|
|
|
|
break;
|
|
|
|
if ((error = iicbus_poll(sc, how)) != 0)
|
|
|
|
break;
|
|
|
|
}
|
Fix numerous issues in iic(4) and iicbus(4):
--Allow multiple open iic fds by storing addressing state in cdevpriv
--Fix, as much as possible, the baked-in race conditions in the iic
ioctl interface by requesting bus ownership on I2CSTART, releasing it on
I2CSTOP/I2CRSTCARD, and requiring bus ownership by the current cdevpriv
to use the I/O ioctls
--Reduce internal iic buffer size and remove 1K read/write limit by
iteratively calling iicbus_read/iicbus_write
--Eliminate dynamic allocation in I2CWRITE/I2CREAD
--Move handling of I2CRDWR to separate function and improve error handling
--Add new I2CSADDR ioctl to store address in current cdevpriv so that
I2CSTART is not needed for read(2)/write(2) to work
--Redesign iicbus_request_bus() and iicbus_release_bus():
--iicbus_request_bus() no longer falls through if the bus is already
owned by the requesting device. Multiple threads on the same device may
want exclusive access. Also, iicbus_release_bus() was never
device-recursive anyway.
--Previously, if IICBUS_CALLBACK failed in iicbus_release_bus(), but
the following iicbus_poll() call succeeded, IICBUS_CALLBACK would not be
issued again
--Do not hold iicbus mtx during IICBUS_CALLBACK call. There are
several drivers that may sleep in IICBUS_CALLBACK, if IIC_WAIT is passed.
--Do not loop in iicbus_request_bus if IICBUS_CALLBACK returns
EWOULDBLOCK; instead pass that to the caller so that it can retry if so
desired.
Differential Revision: https://reviews.freebsd.org/D2140
Reviewed by: imp, jhb, loos
Approved by: kib (mentor)
2015-04-21 11:50:31 +00:00
|
|
|
|
|
|
|
if (error == 0) {
|
2017-07-26 21:06:26 +00:00
|
|
|
++sc->owncount;
|
|
|
|
if (sc->owner == NULL) {
|
|
|
|
sc->owner = dev;
|
2019-05-23 14:02:39 +00:00
|
|
|
/*
|
|
|
|
* Mark the device busy while it owns the bus, to
|
|
|
|
* prevent detaching the device, bus, or hardware
|
2019-07-08 20:26:56 +00:00
|
|
|
* controller, until ownership is relinquished. If the
|
|
|
|
* device is doing IO from its probe method before
|
|
|
|
* attaching, it cannot be busied; mark the bus busy.
|
2019-05-23 14:02:39 +00:00
|
|
|
*/
|
2019-07-08 20:26:56 +00:00
|
|
|
if (device_get_state(dev) < DS_ATTACHING)
|
|
|
|
sc->busydev = bus;
|
|
|
|
else
|
|
|
|
sc->busydev = dev;
|
|
|
|
device_busy(sc->busydev);
|
2017-07-26 21:06:26 +00:00
|
|
|
/*
|
|
|
|
* Drop the lock around the call to the bus driver, it
|
|
|
|
* should be allowed to sleep in the IIC_WAIT case.
|
|
|
|
* Drivers might also need to grab locks that would
|
|
|
|
* cause a LOR if our lock is held.
|
|
|
|
*/
|
|
|
|
IICBUS_UNLOCK(sc);
|
|
|
|
/* Ask the underlying layers if the request is ok */
|
Add support for i2c bus mux hardware.
An i2c bus can be divided into segments which can be selectively connected
and disconnected from the main bus. This is usually done to enable using
multiple slave devices having the same address, by isolating the devices
onto separate bus segments, only one of which is connected to the main bus
at once.
There are several types of i2c bus muxes, which break down into two general
categories...
- Muxes which are themselves i2c slaves. These devices respond to i2c
commands on their upstream bus, and based on those commands, connect
various downstream buses to the upstream. In newbus terms, they are both
a child of an iicbus and the parent of one or more iicbus instances.
- Muxes which are not i2c devices themselves. Such devices are part of the
i2c bus electrically, but in newbus terms their parent is some other
bus. The association with the upstream bus must be established by
separate metadata (such as FDT data).
In both cases, the mux driver has one or more iicbus child instances
representing the downstream buses. The mux driver implements the iicbus_if
interface, as if it were an iichb host bridge/i2c controller driver. It
services the IO requests sent to it by forwarding them to the iicbus
instance representing the upstream bus, after electrically connecting the
upstream bus to the downstream bus that hosts the i2c slave device which
made the IO request.
The net effect is automatic mux switching which is transparent to slaves on
the downstream buses. They just do i2c IO they way they normally do, and the
bus is electrically connected for the duration of the IO and then idled when
it is complete.
The existing iicbus_if callback() method is enhanced so that the parameter
passed to it can be a struct which contains a device_t for the requesting
bus and slave devices. This change is done by adding a flag that indicates
the extra values are present, and making the flags field the first field of
a new args struct. If the flag is set, the iichb or mux driver can recast
the pointer-to-flags into a pointer-to-struct and access the extra
fields. Thus abi compatibility with older drivers is retained (but a mux
cannot exist on the bus with the older iicbus driver in use.)
A new set of core support routines exists in iicbus.c. This code will help
implement mux drivers for any type of mux hardware by supplying all the
boilerplate code that forwards IO requests upstream. It also has code for
parsing metadata and instantiating the child iicbus instances based on it.
Two new hardware mux drivers are added. The ltc430x driver supports the
LTC4305/4306 mux chips which are controlled via i2c commands. The
iic_gpiomux driver supports any mux hardware which is controlled by
manipulating the state of one or more gpio pins. Test Plan
Tested locally using a variety of mux'd bus configurations involving both
ltc4305 and a homebrew gpio-controlled mux. Tested configurations included
cascaded muxes (unlikely in the real world, but useful to prove that 'it all
just works' in terms of the automatic switching and upstream forwarding of
IO requests).
2020-01-02 17:51:49 +00:00
|
|
|
reqdata.dev = dev;
|
|
|
|
reqdata.bus = bus;
|
|
|
|
reqdata.flags = how | IIC_REQBUS_DEV;
|
2017-07-26 21:06:26 +00:00
|
|
|
error = IICBUS_CALLBACK(device_get_parent(bus),
|
Add support for i2c bus mux hardware.
An i2c bus can be divided into segments which can be selectively connected
and disconnected from the main bus. This is usually done to enable using
multiple slave devices having the same address, by isolating the devices
onto separate bus segments, only one of which is connected to the main bus
at once.
There are several types of i2c bus muxes, which break down into two general
categories...
- Muxes which are themselves i2c slaves. These devices respond to i2c
commands on their upstream bus, and based on those commands, connect
various downstream buses to the upstream. In newbus terms, they are both
a child of an iicbus and the parent of one or more iicbus instances.
- Muxes which are not i2c devices themselves. Such devices are part of the
i2c bus electrically, but in newbus terms their parent is some other
bus. The association with the upstream bus must be established by
separate metadata (such as FDT data).
In both cases, the mux driver has one or more iicbus child instances
representing the downstream buses. The mux driver implements the iicbus_if
interface, as if it were an iichb host bridge/i2c controller driver. It
services the IO requests sent to it by forwarding them to the iicbus
instance representing the upstream bus, after electrically connecting the
upstream bus to the downstream bus that hosts the i2c slave device which
made the IO request.
The net effect is automatic mux switching which is transparent to slaves on
the downstream buses. They just do i2c IO they way they normally do, and the
bus is electrically connected for the duration of the IO and then idled when
it is complete.
The existing iicbus_if callback() method is enhanced so that the parameter
passed to it can be a struct which contains a device_t for the requesting
bus and slave devices. This change is done by adding a flag that indicates
the extra values are present, and making the flags field the first field of
a new args struct. If the flag is set, the iichb or mux driver can recast
the pointer-to-flags into a pointer-to-struct and access the extra
fields. Thus abi compatibility with older drivers is retained (but a mux
cannot exist on the bus with the older iicbus driver in use.)
A new set of core support routines exists in iicbus.c. This code will help
implement mux drivers for any type of mux hardware by supplying all the
boilerplate code that forwards IO requests upstream. It also has code for
parsing metadata and instantiating the child iicbus instances based on it.
Two new hardware mux drivers are added. The ltc430x driver supports the
LTC4305/4306 mux chips which are controlled via i2c commands. The
iic_gpiomux driver supports any mux hardware which is controlled by
manipulating the state of one or more gpio pins. Test Plan
Tested locally using a variety of mux'd bus configurations involving both
ltc4305 and a homebrew gpio-controlled mux. Tested configurations included
cascaded muxes (unlikely in the real world, but useful to prove that 'it all
just works' in terms of the automatic switching and upstream forwarding of
IO requests).
2020-01-02 17:51:49 +00:00
|
|
|
IIC_REQUEST_BUS, (caddr_t)&reqdata);
|
2017-07-26 21:06:26 +00:00
|
|
|
IICBUS_LOCK(sc);
|
|
|
|
|
|
|
|
if (error != 0) {
|
|
|
|
sc->owner = NULL;
|
|
|
|
sc->owncount = 0;
|
|
|
|
wakeup_one(sc);
|
2019-07-08 20:26:56 +00:00
|
|
|
device_unbusy(sc->busydev);
|
2017-07-26 21:06:26 +00:00
|
|
|
}
|
1998-09-03 20:51:50 +00:00
|
|
|
}
|
|
|
|
}
|
Fix numerous issues in iic(4) and iicbus(4):
--Allow multiple open iic fds by storing addressing state in cdevpriv
--Fix, as much as possible, the baked-in race conditions in the iic
ioctl interface by requesting bus ownership on I2CSTART, releasing it on
I2CSTOP/I2CRSTCARD, and requiring bus ownership by the current cdevpriv
to use the I/O ioctls
--Reduce internal iic buffer size and remove 1K read/write limit by
iteratively calling iicbus_read/iicbus_write
--Eliminate dynamic allocation in I2CWRITE/I2CREAD
--Move handling of I2CRDWR to separate function and improve error handling
--Add new I2CSADDR ioctl to store address in current cdevpriv so that
I2CSTART is not needed for read(2)/write(2) to work
--Redesign iicbus_request_bus() and iicbus_release_bus():
--iicbus_request_bus() no longer falls through if the bus is already
owned by the requesting device. Multiple threads on the same device may
want exclusive access. Also, iicbus_release_bus() was never
device-recursive anyway.
--Previously, if IICBUS_CALLBACK failed in iicbus_release_bus(), but
the following iicbus_poll() call succeeded, IICBUS_CALLBACK would not be
issued again
--Do not hold iicbus mtx during IICBUS_CALLBACK call. There are
several drivers that may sleep in IICBUS_CALLBACK, if IIC_WAIT is passed.
--Do not loop in iicbus_request_bus if IICBUS_CALLBACK returns
EWOULDBLOCK; instead pass that to the caller so that it can retry if so
desired.
Differential Revision: https://reviews.freebsd.org/D2140
Reviewed by: imp, jhb, loos
Approved by: kib (mentor)
2015-04-21 11:50:31 +00:00
|
|
|
|
2008-08-04 21:03:06 +00:00
|
|
|
IICBUS_UNLOCK(sc);
|
1998-09-03 20:51:50 +00:00
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iicbus_release_bus()
|
|
|
|
*
|
|
|
|
* Release the device allocated with iicbus_request_dev()
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iicbus_release_bus(device_t bus, device_t dev)
|
|
|
|
{
|
Add support for i2c bus mux hardware.
An i2c bus can be divided into segments which can be selectively connected
and disconnected from the main bus. This is usually done to enable using
multiple slave devices having the same address, by isolating the devices
onto separate bus segments, only one of which is connected to the main bus
at once.
There are several types of i2c bus muxes, which break down into two general
categories...
- Muxes which are themselves i2c slaves. These devices respond to i2c
commands on their upstream bus, and based on those commands, connect
various downstream buses to the upstream. In newbus terms, they are both
a child of an iicbus and the parent of one or more iicbus instances.
- Muxes which are not i2c devices themselves. Such devices are part of the
i2c bus electrically, but in newbus terms their parent is some other
bus. The association with the upstream bus must be established by
separate metadata (such as FDT data).
In both cases, the mux driver has one or more iicbus child instances
representing the downstream buses. The mux driver implements the iicbus_if
interface, as if it were an iichb host bridge/i2c controller driver. It
services the IO requests sent to it by forwarding them to the iicbus
instance representing the upstream bus, after electrically connecting the
upstream bus to the downstream bus that hosts the i2c slave device which
made the IO request.
The net effect is automatic mux switching which is transparent to slaves on
the downstream buses. They just do i2c IO they way they normally do, and the
bus is electrically connected for the duration of the IO and then idled when
it is complete.
The existing iicbus_if callback() method is enhanced so that the parameter
passed to it can be a struct which contains a device_t for the requesting
bus and slave devices. This change is done by adding a flag that indicates
the extra values are present, and making the flags field the first field of
a new args struct. If the flag is set, the iichb or mux driver can recast
the pointer-to-flags into a pointer-to-struct and access the extra
fields. Thus abi compatibility with older drivers is retained (but a mux
cannot exist on the bus with the older iicbus driver in use.)
A new set of core support routines exists in iicbus.c. This code will help
implement mux drivers for any type of mux hardware by supplying all the
boilerplate code that forwards IO requests upstream. It also has code for
parsing metadata and instantiating the child iicbus instances based on it.
Two new hardware mux drivers are added. The ltc430x driver supports the
LTC4305/4306 mux chips which are controlled via i2c commands. The
iic_gpiomux driver supports any mux hardware which is controlled by
manipulating the state of one or more gpio pins. Test Plan
Tested locally using a variety of mux'd bus configurations involving both
ltc4305 and a homebrew gpio-controlled mux. Tested configurations included
cascaded muxes (unlikely in the real world, but useful to prove that 'it all
just works' in terms of the automatic switching and upstream forwarding of
IO requests).
2020-01-02 17:51:49 +00:00
|
|
|
struct iic_reqbus_data reqdata;
|
1998-09-03 20:51:50 +00:00
|
|
|
struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
|
1998-10-31 11:31:07 +00:00
|
|
|
|
2008-08-04 21:03:06 +00:00
|
|
|
IICBUS_LOCK(sc);
|
|
|
|
|
1998-09-03 20:51:50 +00:00
|
|
|
if (sc->owner != dev) {
|
2008-08-04 21:03:06 +00:00
|
|
|
IICBUS_UNLOCK(sc);
|
2015-10-10 00:12:03 +00:00
|
|
|
return (IIC_EBUSBSY);
|
1998-09-03 20:51:50 +00:00
|
|
|
}
|
|
|
|
|
2017-07-26 21:06:26 +00:00
|
|
|
if (--sc->owncount == 0) {
|
|
|
|
/* Drop the lock while informing the low-level driver. */
|
|
|
|
IICBUS_UNLOCK(sc);
|
Add support for i2c bus mux hardware.
An i2c bus can be divided into segments which can be selectively connected
and disconnected from the main bus. This is usually done to enable using
multiple slave devices having the same address, by isolating the devices
onto separate bus segments, only one of which is connected to the main bus
at once.
There are several types of i2c bus muxes, which break down into two general
categories...
- Muxes which are themselves i2c slaves. These devices respond to i2c
commands on their upstream bus, and based on those commands, connect
various downstream buses to the upstream. In newbus terms, they are both
a child of an iicbus and the parent of one or more iicbus instances.
- Muxes which are not i2c devices themselves. Such devices are part of the
i2c bus electrically, but in newbus terms their parent is some other
bus. The association with the upstream bus must be established by
separate metadata (such as FDT data).
In both cases, the mux driver has one or more iicbus child instances
representing the downstream buses. The mux driver implements the iicbus_if
interface, as if it were an iichb host bridge/i2c controller driver. It
services the IO requests sent to it by forwarding them to the iicbus
instance representing the upstream bus, after electrically connecting the
upstream bus to the downstream bus that hosts the i2c slave device which
made the IO request.
The net effect is automatic mux switching which is transparent to slaves on
the downstream buses. They just do i2c IO they way they normally do, and the
bus is electrically connected for the duration of the IO and then idled when
it is complete.
The existing iicbus_if callback() method is enhanced so that the parameter
passed to it can be a struct which contains a device_t for the requesting
bus and slave devices. This change is done by adding a flag that indicates
the extra values are present, and making the flags field the first field of
a new args struct. If the flag is set, the iichb or mux driver can recast
the pointer-to-flags into a pointer-to-struct and access the extra
fields. Thus abi compatibility with older drivers is retained (but a mux
cannot exist on the bus with the older iicbus driver in use.)
A new set of core support routines exists in iicbus.c. This code will help
implement mux drivers for any type of mux hardware by supplying all the
boilerplate code that forwards IO requests upstream. It also has code for
parsing metadata and instantiating the child iicbus instances based on it.
Two new hardware mux drivers are added. The ltc430x driver supports the
LTC4305/4306 mux chips which are controlled via i2c commands. The
iic_gpiomux driver supports any mux hardware which is controlled by
manipulating the state of one or more gpio pins. Test Plan
Tested locally using a variety of mux'd bus configurations involving both
ltc4305 and a homebrew gpio-controlled mux. Tested configurations included
cascaded muxes (unlikely in the real world, but useful to prove that 'it all
just works' in terms of the automatic switching and upstream forwarding of
IO requests).
2020-01-02 17:51:49 +00:00
|
|
|
reqdata.dev = dev;
|
|
|
|
reqdata.bus = bus;
|
|
|
|
reqdata.flags = IIC_REQBUS_DEV;
|
|
|
|
IICBUS_CALLBACK(device_get_parent(bus), IIC_RELEASE_BUS,
|
|
|
|
(caddr_t)&reqdata);
|
Fix numerous issues in iic(4) and iicbus(4):
--Allow multiple open iic fds by storing addressing state in cdevpriv
--Fix, as much as possible, the baked-in race conditions in the iic
ioctl interface by requesting bus ownership on I2CSTART, releasing it on
I2CSTOP/I2CRSTCARD, and requiring bus ownership by the current cdevpriv
to use the I/O ioctls
--Reduce internal iic buffer size and remove 1K read/write limit by
iteratively calling iicbus_read/iicbus_write
--Eliminate dynamic allocation in I2CWRITE/I2CREAD
--Move handling of I2CRDWR to separate function and improve error handling
--Add new I2CSADDR ioctl to store address in current cdevpriv so that
I2CSTART is not needed for read(2)/write(2) to work
--Redesign iicbus_request_bus() and iicbus_release_bus():
--iicbus_request_bus() no longer falls through if the bus is already
owned by the requesting device. Multiple threads on the same device may
want exclusive access. Also, iicbus_release_bus() was never
device-recursive anyway.
--Previously, if IICBUS_CALLBACK failed in iicbus_release_bus(), but
the following iicbus_poll() call succeeded, IICBUS_CALLBACK would not be
issued again
--Do not hold iicbus mtx during IICBUS_CALLBACK call. There are
several drivers that may sleep in IICBUS_CALLBACK, if IIC_WAIT is passed.
--Do not loop in iicbus_request_bus if IICBUS_CALLBACK returns
EWOULDBLOCK; instead pass that to the caller so that it can retry if so
desired.
Differential Revision: https://reviews.freebsd.org/D2140
Reviewed by: imp, jhb, loos
Approved by: kib (mentor)
2015-04-21 11:50:31 +00:00
|
|
|
IICBUS_LOCK(sc);
|
|
|
|
sc->owner = NULL;
|
|
|
|
wakeup_one(sc);
|
2019-07-08 20:26:56 +00:00
|
|
|
device_unbusy(sc->busydev);
|
Fix numerous issues in iic(4) and iicbus(4):
--Allow multiple open iic fds by storing addressing state in cdevpriv
--Fix, as much as possible, the baked-in race conditions in the iic
ioctl interface by requesting bus ownership on I2CSTART, releasing it on
I2CSTOP/I2CRSTCARD, and requiring bus ownership by the current cdevpriv
to use the I/O ioctls
--Reduce internal iic buffer size and remove 1K read/write limit by
iteratively calling iicbus_read/iicbus_write
--Eliminate dynamic allocation in I2CWRITE/I2CREAD
--Move handling of I2CRDWR to separate function and improve error handling
--Add new I2CSADDR ioctl to store address in current cdevpriv so that
I2CSTART is not needed for read(2)/write(2) to work
--Redesign iicbus_request_bus() and iicbus_release_bus():
--iicbus_request_bus() no longer falls through if the bus is already
owned by the requesting device. Multiple threads on the same device may
want exclusive access. Also, iicbus_release_bus() was never
device-recursive anyway.
--Previously, if IICBUS_CALLBACK failed in iicbus_release_bus(), but
the following iicbus_poll() call succeeded, IICBUS_CALLBACK would not be
issued again
--Do not hold iicbus mtx during IICBUS_CALLBACK call. There are
several drivers that may sleep in IICBUS_CALLBACK, if IIC_WAIT is passed.
--Do not loop in iicbus_request_bus if IICBUS_CALLBACK returns
EWOULDBLOCK; instead pass that to the caller so that it can retry if so
desired.
Differential Revision: https://reviews.freebsd.org/D2140
Reviewed by: imp, jhb, loos
Approved by: kib (mentor)
2015-04-21 11:50:31 +00:00
|
|
|
}
|
2017-07-26 21:06:26 +00:00
|
|
|
IICBUS_UNLOCK(sc);
|
|
|
|
return (0);
|
1998-09-03 20:51:50 +00:00
|
|
|
}
|
|
|
|
|
1999-01-09 18:08:24 +00:00
|
|
|
/*
|
|
|
|
* iicbus_started()
|
|
|
|
*
|
|
|
|
* Test if the iicbus is started by the controller
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iicbus_started(device_t bus)
|
|
|
|
{
|
|
|
|
struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
|
|
|
|
|
|
|
|
return (sc->started);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iicbus_start()
|
|
|
|
*
|
|
|
|
* Send start condition to the slave addressed by 'slave'
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iicbus_start(device_t bus, u_char slave, int timeout)
|
|
|
|
{
|
|
|
|
struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
if (sc->started)
|
2015-10-09 23:58:19 +00:00
|
|
|
return (IIC_ESTATUS); /* protocol error, bus already started */
|
1999-01-09 18:08:24 +00:00
|
|
|
|
|
|
|
if (!(error = IICBUS_START(device_get_parent(bus), slave, timeout)))
|
|
|
|
sc->started = slave;
|
|
|
|
else
|
|
|
|
sc->started = 0;
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
1999-01-28 15:59:15 +00:00
|
|
|
/*
|
|
|
|
* iicbus_repeated_start()
|
|
|
|
*
|
|
|
|
* Send start condition to the slave addressed by 'slave'
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iicbus_repeated_start(device_t bus, u_char slave, int timeout)
|
|
|
|
{
|
|
|
|
struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
if (!sc->started)
|
2015-10-09 23:58:19 +00:00
|
|
|
return (IIC_ESTATUS); /* protocol error, bus not started */
|
1999-01-28 15:59:15 +00:00
|
|
|
|
1999-02-06 10:56:09 +00:00
|
|
|
if (!(error = IICBUS_REPEATED_START(device_get_parent(bus), slave, timeout)))
|
1999-01-28 15:59:15 +00:00
|
|
|
sc->started = slave;
|
|
|
|
else
|
|
|
|
sc->started = 0;
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
1999-01-09 18:08:24 +00:00
|
|
|
/*
|
|
|
|
* iicbus_stop()
|
|
|
|
*
|
|
|
|
* Send stop condition to the bus
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iicbus_stop(device_t bus)
|
|
|
|
{
|
|
|
|
struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
|
|
|
|
int error = 0;
|
|
|
|
|
|
|
|
if (!sc->started)
|
2015-10-09 23:58:19 +00:00
|
|
|
return (IIC_ESTATUS); /* protocol error, bus not started */
|
1999-01-09 18:08:24 +00:00
|
|
|
|
|
|
|
error = IICBUS_STOP(device_get_parent(bus));
|
|
|
|
|
|
|
|
/* refuse any further access */
|
|
|
|
sc->started = 0;
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iicbus_write()
|
|
|
|
*
|
|
|
|
* Write a block of data to the slave previously started by
|
|
|
|
* iicbus_start() call
|
|
|
|
*/
|
|
|
|
int
|
2006-12-05 06:19:36 +00:00
|
|
|
iicbus_write(device_t bus, const char *buf, int len, int *sent, int timeout)
|
1999-01-09 18:08:24 +00:00
|
|
|
{
|
|
|
|
struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
|
|
|
|
|
2011-12-04 11:55:33 +00:00
|
|
|
/* a slave must have been started for writing */
|
|
|
|
if (sc->started == 0 || (sc->strict != 0 && (sc->started & LSB) != 0))
|
2015-10-09 23:58:19 +00:00
|
|
|
return (IIC_ESTATUS);
|
1999-01-09 18:08:24 +00:00
|
|
|
|
|
|
|
return (IICBUS_WRITE(device_get_parent(bus), buf, len, sent, timeout));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iicbus_read()
|
|
|
|
*
|
|
|
|
* Read a block of data from the slave previously started by
|
|
|
|
* iicbus_read() call
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iicbus_read(device_t bus, char *buf, int len, int *read, int last, int delay)
|
|
|
|
{
|
|
|
|
struct iicbus_softc *sc = (struct iicbus_softc *)device_get_softc(bus);
|
|
|
|
|
2011-12-04 11:55:33 +00:00
|
|
|
/* a slave must have been started for reading */
|
|
|
|
if (sc->started == 0 || (sc->strict != 0 && (sc->started & LSB) == 0))
|
2015-10-09 23:58:19 +00:00
|
|
|
return (IIC_ESTATUS);
|
1999-01-09 18:08:24 +00:00
|
|
|
|
|
|
|
return (IICBUS_READ(device_get_parent(bus), buf, len, read, last, delay));
|
1999-01-28 15:59:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iicbus_write_byte()
|
|
|
|
*
|
|
|
|
* Write a byte to the slave previously started by iicbus_start() call
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iicbus_write_byte(device_t bus, char byte, int timeout)
|
|
|
|
{
|
2015-10-09 23:58:19 +00:00
|
|
|
struct iicbus_softc *sc = device_get_softc(bus);
|
1999-01-28 15:59:15 +00:00
|
|
|
char data = byte;
|
|
|
|
int sent;
|
|
|
|
|
2015-10-09 23:58:19 +00:00
|
|
|
/* a slave must have been started for writing */
|
|
|
|
if (sc->started == 0 || (sc->strict != 0 && (sc->started & LSB) != 0))
|
|
|
|
return (IIC_ESTATUS);
|
|
|
|
|
1999-01-28 15:59:15 +00:00
|
|
|
return (iicbus_write(bus, &data, 1, &sent, timeout));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iicbus_read_byte()
|
|
|
|
*
|
|
|
|
* Read a byte from the slave previously started by iicbus_start() call
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iicbus_read_byte(device_t bus, char *byte, int timeout)
|
|
|
|
{
|
2015-10-09 23:58:19 +00:00
|
|
|
struct iicbus_softc *sc = device_get_softc(bus);
|
1999-01-28 15:59:15 +00:00
|
|
|
int read;
|
|
|
|
|
2015-10-09 23:58:19 +00:00
|
|
|
/* a slave must have been started for reading */
|
|
|
|
if (sc->started == 0 || (sc->strict != 0 && (sc->started & LSB) == 0))
|
|
|
|
return (IIC_ESTATUS);
|
|
|
|
|
1999-01-28 15:59:15 +00:00
|
|
|
return (iicbus_read(bus, byte, 1, &read, IIC_LAST_READ, timeout));
|
1999-01-09 18:08:24 +00:00
|
|
|
}
|
|
|
|
|
1998-09-03 20:51:50 +00:00
|
|
|
/*
|
|
|
|
* iicbus_block_write()
|
|
|
|
*
|
|
|
|
* Write a block of data to slave ; start/stop protocol managed
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iicbus_block_write(device_t bus, u_char slave, char *buf, int len, int *sent)
|
|
|
|
{
|
|
|
|
u_char addr = slave & ~LSB;
|
|
|
|
int error;
|
|
|
|
|
1998-10-31 11:31:07 +00:00
|
|
|
if ((error = iicbus_start(bus, addr, 0)))
|
1998-09-03 20:51:50 +00:00
|
|
|
return (error);
|
|
|
|
|
1998-10-31 11:31:07 +00:00
|
|
|
error = iicbus_write(bus, buf, len, sent, 0);
|
1998-09-03 20:51:50 +00:00
|
|
|
|
|
|
|
iicbus_stop(bus);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* iicbus_block_read()
|
|
|
|
*
|
|
|
|
* Read a block of data from slave ; start/stop protocol managed
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iicbus_block_read(device_t bus, u_char slave, char *buf, int len, int *read)
|
|
|
|
{
|
|
|
|
u_char addr = slave | LSB;
|
|
|
|
int error;
|
|
|
|
|
1998-10-31 11:31:07 +00:00
|
|
|
if ((error = iicbus_start(bus, addr, 0)))
|
1998-09-03 20:51:50 +00:00
|
|
|
return (error);
|
|
|
|
|
1998-10-31 11:31:07 +00:00
|
|
|
error = iicbus_read(bus, buf, len, read, IIC_LAST_READ, 0);
|
1998-09-03 20:51:50 +00:00
|
|
|
|
1998-10-31 11:31:07 +00:00
|
|
|
iicbus_stop(bus);
|
1998-09-03 20:51:50 +00:00
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
2006-07-14 23:15:06 +00:00
|
|
|
|
|
|
|
/*
|
2008-08-04 21:03:06 +00:00
|
|
|
* iicbus_transfer()
|
2006-07-14 23:15:06 +00:00
|
|
|
*
|
|
|
|
* Do an aribtrary number of transfers on the iicbus. We pass these
|
|
|
|
* raw requests to the bridge driver. If the bridge driver supports
|
|
|
|
* them directly, then it manages all the details. If not, it can use
|
|
|
|
* the helper function iicbus_transfer_gen() which will do the
|
|
|
|
* transfers at a low level.
|
|
|
|
*
|
|
|
|
* Pointers passed in as part of iic_msg must be kernel pointers.
|
|
|
|
* Callers that have user addresses to manage must do so on their own.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
iicbus_transfer(device_t bus, struct iic_msg *msgs, uint32_t nmsgs)
|
|
|
|
{
|
2015-10-09 23:58:19 +00:00
|
|
|
|
2006-07-14 23:15:06 +00:00
|
|
|
return (IICBUS_TRANSFER(device_get_parent(bus), msgs, nmsgs));
|
|
|
|
}
|
|
|
|
|
2015-10-22 00:54:59 +00:00
|
|
|
int
|
|
|
|
iicbus_transfer_excl(device_t dev, struct iic_msg *msgs, uint32_t nmsgs,
|
|
|
|
int how)
|
|
|
|
{
|
|
|
|
device_t bus;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
bus = device_get_parent(dev);
|
|
|
|
error = iicbus_request_bus(bus, dev, how);
|
|
|
|
if (error == 0)
|
|
|
|
error = IICBUS_TRANSFER(bus, msgs, nmsgs);
|
|
|
|
iicbus_release_bus(bus, dev);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2006-07-14 23:15:06 +00:00
|
|
|
/*
|
|
|
|
* Generic version of iicbus_transfer that calls the appropriate
|
|
|
|
* routines to accomplish this. See note above about acceptable
|
|
|
|
* buffer addresses.
|
|
|
|
*/
|
|
|
|
int
|
2007-03-23 23:03:54 +00:00
|
|
|
iicbus_transfer_gen(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
|
2006-07-14 23:15:06 +00:00
|
|
|
{
|
2010-11-08 19:53:16 +00:00
|
|
|
int i, error, lenread, lenwrote, nkid, rpstart, addr;
|
2007-03-23 23:03:54 +00:00
|
|
|
device_t *children, bus;
|
2019-05-29 09:08:20 +00:00
|
|
|
bool started;
|
2007-03-23 23:03:54 +00:00
|
|
|
|
2008-08-23 07:38:00 +00:00
|
|
|
if ((error = device_get_children(dev, &children, &nkid)) != 0)
|
2015-10-09 23:58:19 +00:00
|
|
|
return (IIC_ERESOURCE);
|
2008-08-23 07:38:00 +00:00
|
|
|
if (nkid != 1) {
|
|
|
|
free(children, M_TEMP);
|
2015-10-09 23:58:19 +00:00
|
|
|
return (IIC_ENOTSUPP);
|
2008-08-23 07:38:00 +00:00
|
|
|
}
|
2007-03-23 23:03:54 +00:00
|
|
|
bus = children[0];
|
2010-11-08 19:53:16 +00:00
|
|
|
rpstart = 0;
|
2007-03-23 23:03:54 +00:00
|
|
|
free(children, M_TEMP);
|
2017-06-29 00:29:15 +00:00
|
|
|
started = false;
|
2006-07-14 23:15:06 +00:00
|
|
|
for (i = 0, error = 0; i < nmsgs && error == 0; i++) {
|
2010-11-08 19:53:16 +00:00
|
|
|
addr = msgs[i].slave;
|
2006-07-14 23:15:06 +00:00
|
|
|
if (msgs[i].flags & IIC_M_RD)
|
2010-11-08 19:53:16 +00:00
|
|
|
addr |= LSB;
|
2006-07-14 23:15:06 +00:00
|
|
|
else
|
2010-11-08 19:53:16 +00:00
|
|
|
addr &= ~LSB;
|
|
|
|
|
|
|
|
if (!(msgs[i].flags & IIC_M_NOSTART)) {
|
|
|
|
if (rpstart)
|
|
|
|
error = iicbus_repeated_start(bus, addr, 0);
|
|
|
|
else
|
|
|
|
error = iicbus_start(bus, addr, 0);
|
2017-06-29 00:29:15 +00:00
|
|
|
if (error != 0)
|
|
|
|
break;
|
|
|
|
started = true;
|
2010-11-08 19:53:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (msgs[i].flags & IIC_M_RD)
|
|
|
|
error = iicbus_read(bus, msgs[i].buf, msgs[i].len,
|
|
|
|
&lenread, IIC_LAST_READ, 0);
|
|
|
|
else
|
|
|
|
error = iicbus_write(bus, msgs[i].buf, msgs[i].len,
|
|
|
|
&lenwrote, 0);
|
2015-10-09 21:34:46 +00:00
|
|
|
if (error != 0)
|
|
|
|
break;
|
2010-11-08 19:53:16 +00:00
|
|
|
|
2019-05-29 09:08:20 +00:00
|
|
|
if (!(msgs[i].flags & IIC_M_NOSTOP)) {
|
2010-11-08 19:53:16 +00:00
|
|
|
rpstart = 0;
|
|
|
|
iicbus_stop(bus);
|
2019-05-29 09:08:20 +00:00
|
|
|
} else {
|
|
|
|
rpstart = 1; /* Next message gets repeated start */
|
2010-11-08 19:53:16 +00:00
|
|
|
}
|
2006-07-14 23:15:06 +00:00
|
|
|
}
|
2017-06-29 00:29:15 +00:00
|
|
|
if (error != 0 && started)
|
2015-10-09 21:34:46 +00:00
|
|
|
iicbus_stop(bus);
|
2006-07-14 23:15:06 +00:00
|
|
|
return (error);
|
|
|
|
}
|
2017-07-26 20:40:24 +00:00
|
|
|
|
|
|
|
int
|
|
|
|
iicdev_readfrom(device_t slavedev, uint8_t regaddr, void *buffer,
|
|
|
|
uint16_t buflen, int waithow)
|
|
|
|
{
|
|
|
|
struct iic_msg msgs[2];
|
|
|
|
uint8_t slaveaddr;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Two transfers back to back with a repeat-start between them; first we
|
|
|
|
* write the address-within-device, then we read from the device.
|
|
|
|
*/
|
|
|
|
slaveaddr = iicbus_get_addr(slavedev);
|
|
|
|
|
|
|
|
msgs[0].slave = slaveaddr;
|
|
|
|
msgs[0].flags = IIC_M_WR | IIC_M_NOSTOP;
|
|
|
|
msgs[0].len = 1;
|
|
|
|
msgs[0].buf = ®addr;
|
|
|
|
|
|
|
|
msgs[1].slave = slaveaddr;
|
|
|
|
msgs[1].flags = IIC_M_RD;
|
|
|
|
msgs[1].len = buflen;
|
|
|
|
msgs[1].buf = buffer;
|
|
|
|
|
|
|
|
return (iicbus_transfer_excl(slavedev, msgs, nitems(msgs), waithow));
|
|
|
|
}
|
|
|
|
|
|
|
|
int iicdev_writeto(device_t slavedev, uint8_t regaddr, void *buffer,
|
|
|
|
uint16_t buflen, int waithow)
|
|
|
|
{
|
2019-11-21 19:13:05 +00:00
|
|
|
struct iic_msg msg;
|
|
|
|
uint8_t local_buffer[32];
|
|
|
|
uint8_t *bufptr;
|
|
|
|
size_t bufsize;
|
|
|
|
int error;
|
2017-07-26 20:40:24 +00:00
|
|
|
|
|
|
|
/*
|
2019-11-21 19:13:05 +00:00
|
|
|
* Ideally, we would do two transfers back to back with no stop or start
|
|
|
|
* between them using an array of 2 iic_msgs; first we'd write the
|
|
|
|
* address byte using the IIC_M_NOSTOP flag, then we write the data
|
|
|
|
* using IIC_M_NOSTART, all in a single transfer. Unfortunately,
|
|
|
|
* several i2c hardware drivers don't support that (perhaps because the
|
|
|
|
* hardware itself can't support it). So instead we gather the
|
|
|
|
* scattered bytes into a single buffer here before writing them using a
|
|
|
|
* single iic_msg. This function is typically used to write a few bytes
|
|
|
|
* at a time, so we try to use a small local buffer on the stack, but
|
|
|
|
* fall back to allocating a temporary buffer when necessary.
|
2017-07-26 20:40:24 +00:00
|
|
|
*/
|
|
|
|
|
2019-11-21 19:13:05 +00:00
|
|
|
bufsize = buflen + 1;
|
|
|
|
if (bufsize <= sizeof(local_buffer)) {
|
|
|
|
bufptr = local_buffer;
|
|
|
|
} else {
|
|
|
|
bufptr = malloc(bufsize, M_DEVBUF,
|
|
|
|
(waithow & IIC_WAIT) ? M_WAITOK : M_NOWAIT);
|
|
|
|
if (bufptr == NULL)
|
|
|
|
return (errno2iic(ENOMEM));
|
|
|
|
}
|
2017-07-26 20:40:24 +00:00
|
|
|
|
2019-11-21 19:13:05 +00:00
|
|
|
bufptr[0] = regaddr;
|
|
|
|
memcpy(&bufptr[1], buffer, buflen);
|
2017-07-26 20:40:24 +00:00
|
|
|
|
2019-11-21 19:13:05 +00:00
|
|
|
msg.slave = iicbus_get_addr(slavedev);
|
|
|
|
msg.flags = IIC_M_WR;
|
|
|
|
msg.len = bufsize;
|
|
|
|
msg.buf = bufptr;
|
|
|
|
|
|
|
|
error = iicbus_transfer_excl(slavedev, &msg, 1, waithow);
|
|
|
|
|
|
|
|
if (bufptr != local_buffer)
|
|
|
|
free(bufptr, M_DEVBUF);
|
|
|
|
|
|
|
|
return (error);
|
2017-07-26 20:40:24 +00:00
|
|
|
}
|