602 lines
14 KiB
C
Raw Normal View History

/*-
* SPDX-License-Identifier: BSD-2-Clause-FreeBSD
*
* Copyright (c) 1998, 2001 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.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
/*
* Generic I2C bit-banging code
*
* Example:
*
* iicbus
* / \
* iicbb pcf
* | \
* bti2c lpbb
*
* From Linux I2C generic interface
* (c) 1998 Gerd Knorr <kraxel@cs.tu-berlin.de>
*
*/
#include "opt_platform.h"
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/bus.h>
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
#include <sys/sysctl.h>
#include <sys/uio.h>
#ifdef FDT
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
#include <dev/fdt/fdt_common.h>
#endif
#include <dev/iicbus/iiconf.h>
#include <dev/iicbus/iicbus.h>
#include <dev/smbus/smbconf.h>
#include "iicbus_if.h"
#include "iicbb_if.h"
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
/* Based on the SMBus specification. */
#define DEFAULT_SCL_LOW_TIMEOUT (25 * 1000)
struct iicbb_softc {
device_t iicbus;
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
u_int udelay; /* signal toggle delay in usec */
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
u_int io_latency; /* approximate pin toggling latency */
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
u_int scl_low_timeout;
};
static int iicbb_attach(device_t);
static void iicbb_child_detached(device_t, device_t);
static int iicbb_detach(device_t);
static int iicbb_print_child(device_t, device_t);
static int iicbb_probe(device_t);
static int iicbb_callback(device_t, int, caddr_t);
static int iicbb_start(device_t, u_char, int);
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
static int iicbb_repstart(device_t, u_char, int);
static int iicbb_stop(device_t);
static int iicbb_write(device_t, const char *, int, int *, int);
static int iicbb_read(device_t, char *, int, int *, int, int);
static int iicbb_reset(device_t, u_char, u_char, u_char *);
static int iicbb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs);
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
static void iicbb_set_speed(struct iicbb_softc *sc, u_char);
#ifdef FDT
static phandle_t iicbb_get_node(device_t, device_t);
#endif
static device_method_t iicbb_methods[] = {
/* device interface */
DEVMETHOD(device_probe, iicbb_probe),
DEVMETHOD(device_attach, iicbb_attach),
DEVMETHOD(device_detach, iicbb_detach),
/* bus interface */
DEVMETHOD(bus_child_detached, iicbb_child_detached),
DEVMETHOD(bus_print_child, iicbb_print_child),
/* iicbus interface */
DEVMETHOD(iicbus_callback, iicbb_callback),
DEVMETHOD(iicbus_start, iicbb_start),
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
DEVMETHOD(iicbus_repeated_start, iicbb_repstart),
DEVMETHOD(iicbus_stop, iicbb_stop),
DEVMETHOD(iicbus_write, iicbb_write),
DEVMETHOD(iicbus_read, iicbb_read),
DEVMETHOD(iicbus_reset, iicbb_reset),
DEVMETHOD(iicbus_transfer, iicbb_transfer),
#ifdef FDT
/* ofw_bus interface */
DEVMETHOD(ofw_bus_get_node, iicbb_get_node),
#endif
{ 0, 0 }
};
driver_t iicbb_driver = {
"iicbb",
iicbb_methods,
sizeof(struct iicbb_softc),
};
devclass_t iicbb_devclass;
static int
iicbb_probe(device_t dev)
{
device_set_desc(dev, "I2C bit-banging driver");
return (0);
}
static int
iicbb_attach(device_t dev)
{
struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
sc->iicbus = device_add_child(dev, "iicbus", -1);
if (!sc->iicbus)
return (ENXIO);
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
sc->scl_low_timeout = DEFAULT_SCL_LOW_TIMEOUT;
SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
"delay", CTLFLAG_RD, &sc->udelay,
0, "Signal change delay controlled by bus frequency, microseconds");
SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
"scl_low_timeout", CTLFLAG_RWTUN, &sc->scl_low_timeout,
0, "SCL low timeout, microseconds");
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
SYSCTL_ADD_UINT(device_get_sysctl_ctx(dev),
SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO,
"io_latency", CTLFLAG_RWTUN, &sc->io_latency,
0, "Estimate of pin toggling latency, microseconds");
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
bus_generic_attach(dev);
return (0);
}
static int
iicbb_detach(device_t dev)
{
bus_generic_detach(dev);
device_delete_children(dev);
return (0);
}
#ifdef FDT
static phandle_t
iicbb_get_node(device_t bus, device_t dev)
{
/* We only have one child, the I2C bus, which needs our own node. */
return (ofw_bus_get_node(bus));
}
#endif
static void
iicbb_child_detached( device_t dev, device_t child )
{
struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
if (child == sc->iicbus)
sc->iicbus = NULL;
}
static int
iicbb_print_child(device_t bus, device_t dev)
{
int error;
int retval = 0;
u_char oldaddr;
retval += bus_print_child_header(bus, dev);
/* retrieve the interface I2C address */
error = IICBB_RESET(device_get_parent(bus), IIC_FASTEST, 0, &oldaddr);
if (error == IIC_ENOADDR) {
retval += printf(" on %s master-only\n",
device_get_nameunit(bus));
} else {
/* restore the address */
IICBB_RESET(device_get_parent(bus), IIC_FASTEST, oldaddr, NULL);
retval += printf(" on %s addr 0x%x\n",
device_get_nameunit(bus), oldaddr & 0xff);
}
return (retval);
}
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
#define IICBB_DEBUG
#ifdef IICBB_DEBUG
static int i2c_debug = 0;
SYSCTL_DECL(_hw_i2c);
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
SYSCTL_INT(_hw_i2c, OID_AUTO, iicbb_debug, CTLFLAG_RWTUN,
&i2c_debug, 0, "Enable i2c bit-banging driver debug");
#define I2C_DEBUG(x) do { \
if (i2c_debug) (x); \
} while (0)
#else
#define I2C_DEBUG(x)
#endif
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
#define I2C_GETSDA(dev) (IICBB_GETSDA(device_get_parent(dev)))
#define I2C_SETSDA(dev, x) (IICBB_SETSDA(device_get_parent(dev), x))
#define I2C_GETSCL(dev) (IICBB_GETSCL(device_get_parent(dev)))
#define I2C_SETSCL(dev, x) (IICBB_SETSCL(device_get_parent(dev), x))
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
static int
iicbb_waitforscl(device_t dev)
{
struct iicbb_softc *sc = device_get_softc(dev);
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
sbintime_t fast_timeout;
sbintime_t now, timeout;
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/* Spin for up to 1 ms, then switch to pause. */
now = sbinuptime();
fast_timeout = now + SBT_1MS;
timeout = now + sc->scl_low_timeout * SBT_1US;
do {
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
if (I2C_GETSCL(dev))
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
return (0);
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
now = sbinuptime();
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
} while (now < fast_timeout);
do {
I2C_DEBUG(printf("."));
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
pause_sbt("iicbb-scl-low", SBT_1MS, C_PREL(8), 0);
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
if (I2C_GETSCL(dev))
return (0);
now = sbinuptime();
} while (now < timeout);
I2C_DEBUG(printf("*"));
return (IIC_ETIMEOUT);
}
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/* Start the high phase of the clock. */
static int
iicbb_clockin(device_t dev, int sda)
{
/*
* Precondition: SCL is low.
* Action:
* - set SDA to the value;
* - release SCL and wait until it's high.
* The caller is responsible for keeping SCL high for udelay.
*
* There should be a data set-up time, 250 ns minimum, between setting
* SDA and raising SCL. It's expected that the I/O access latency will
* naturally provide that delay.
*/
I2C_SETSDA(dev, sda);
I2C_SETSCL(dev, 1);
return (iicbb_waitforscl(dev));
}
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/*
* End the high phase of the clock and wait out the low phase
* as nothing interesting happens during it anyway.
*/
static void
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
iicbb_clockout(device_t dev)
{
struct iicbb_softc *sc = device_get_softc(dev);
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/*
* Precondition: SCL is high.
* Action:
* - pull SCL low and hold for udelay.
*/
I2C_SETSCL(dev, 0);
DELAY(sc->udelay);
}
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
static int
iicbb_sendbit(device_t dev, int bit)
{
struct iicbb_softc *sc = device_get_softc(dev);
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
int err;
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
err = iicbb_clockin(dev, bit);
if (err != 0)
return (err);
DELAY(sc->udelay);
iicbb_clockout(dev);
return (0);
}
/*
* Waiting for ACKNOWLEDGE.
*
* When a chip is being addressed or has received data it will issue an
* ACKNOWLEDGE pulse. Therefore the MASTER must release the DATA line
* (set it to high level) and then release the CLOCK line.
* Now it must wait for the SLAVE to pull the DATA line low.
* Actually on the bus this looks like a START condition so nothing happens
* because of the fact that the IC's that have not been addressed are doing
* nothing.
*
* When the SLAVE has pulled this line low the MASTER will take the CLOCK
* line low and then the SLAVE will release the SDA (data) line.
*/
static int
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
iicbb_getack(device_t dev)
{
struct iicbb_softc *sc = device_get_softc(dev);
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
int noack, err;
int t;
/* Release SDA so that the slave can drive it. */
err = iicbb_clockin(dev, 1);
if (err != 0) {
I2C_DEBUG(printf("! "));
return (err);
}
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/* Sample SDA until ACK (low) or udelay runs out. */
for (t = 0; t < sc->udelay; t++) {
noack = I2C_GETSDA(dev);
if (!noack)
break;
DELAY(1);
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
}
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
DELAY(sc->udelay - t);
iicbb_clockout(dev);
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
I2C_DEBUG(printf("%c ", noack ? '-' : '+'));
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
return (noack ? IIC_ENOACK : 0);
}
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
static int
iicbb_sendbyte(device_t dev, uint8_t data)
{
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
int err, i;
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
for (i = 7; i >= 0; i--) {
err = iicbb_sendbit(dev, (data & (1 << i)) != 0);
if (err != 0) {
I2C_DEBUG(printf("w!"));
return (err);
}
}
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
I2C_DEBUG(printf("w%02x", data));
return (0);
}
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
static int
iicbb_readbyte(device_t dev, bool last, uint8_t *data)
{
struct iicbb_softc *sc = device_get_softc(dev);
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
int i, err;
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/*
* Release SDA so that the slave can drive it.
* We do not use iicbb_clockin() here because we need to release SDA
* only once and then we just pulse the SCL.
*/
*data = 0;
I2C_SETSDA(dev, 1);
for (i = 7; i >= 0; i--) {
I2C_SETSCL(dev, 1);
err = iicbb_waitforscl(dev);
if (err != 0) {
I2C_DEBUG(printf("r! "));
return (err);
}
DELAY((sc->udelay + 1) / 2);
if (I2C_GETSDA(dev))
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
*data |= 1 << i;
DELAY((sc->udelay + 1) / 2);
iicbb_clockout(dev);
}
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/*
* Send master->slave ACK (low) for more data,
* NoACK (high) otherwise.
*/
iicbb_sendbit(dev, last);
I2C_DEBUG(printf("r%02x%c ", *data, last ? '-' : '+'));
return (0);
}
static int
iicbb_callback(device_t dev, int index, caddr_t data)
{
return (IICBB_CALLBACK(device_get_parent(dev), index, data));
}
static int
iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
{
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
iicbb_set_speed(device_get_softc(dev), speed);
return (IICBB_RESET(device_get_parent(dev), speed, addr, oldaddr));
}
static int
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
iicbb_start_impl(device_t dev, u_char slave, bool repstart)
{
struct iicbb_softc *sc = device_get_softc(dev);
int error;
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
if (!repstart) {
I2C_DEBUG(printf("<<"));
/* SCL must be high on the idle bus. */
if (iicbb_waitforscl(dev) != 0) {
I2C_DEBUG(printf("C!\n"));
return (IIC_EBUSERR);
}
} else {
I2C_DEBUG(printf("<"));
error = iicbb_clockin(dev, 1);
if (error != 0)
return (error);
/* SDA will go low in the middle of the SCL high phase. */
DELAY((sc->udelay + 1) / 2);
}
/*
* SDA must be high after the earlier stop condition or the end
* of Ack/NoAck pulse.
*/
if (!I2C_GETSDA(dev)) {
I2C_DEBUG(printf("D!\n"));
return (IIC_EBUSERR);
}
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/* Start: SDA high->low. */
I2C_SETSDA(dev, 0);
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/* Wait the second half of the SCL high phase. */
DELAY((sc->udelay + 1) / 2);
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/* Pull SCL low to keep the bus reserved. */
iicbb_clockout(dev);
/* send address */
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
error = iicbb_sendbyte(dev, slave);
/* check for ack */
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
if (error == 0)
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
error = iicbb_getack(dev);
if (error != 0)
(void)iicbb_stop(dev);
return (error);
}
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/* NB: the timeout is ignored. */
static int
iicbb_start(device_t dev, u_char slave, int timeout)
{
return (iicbb_start_impl(dev, slave, false));
}
/* NB: the timeout is ignored. */
static int
iicbb_repstart(device_t dev, u_char slave, int timeout)
{
return (iicbb_start_impl(dev, slave, true));
}
static int
iicbb_stop(device_t dev)
{
struct iicbb_softc *sc = device_get_softc(dev);
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
int err = 0;
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/*
* Stop: SDA goes from low to high in the middle of the SCL high phase.
*/
err = iicbb_clockin(dev, 0);
if (err != 0)
return (err);
DELAY((sc->udelay + 1) / 2);
I2C_SETSDA(dev, 1);
DELAY((sc->udelay + 1) / 2);
I2C_DEBUG(printf("%s>>", err != 0 ? "!" : ""));
I2C_DEBUG(printf("\n"));
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
return (err);
}
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/* NB: the timeout is ignored. */
static int
iicbb_write(device_t dev, const char *buf, int len, int *sent, int timeout)
{
int bytes, error = 0;
bytes = 0;
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
while (len > 0) {
/* send byte */
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
iicbb_sendbyte(dev, (uint8_t)*buf++);
/* check for ack */
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
error = iicbb_getack(dev);
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
if (error != 0)
break;
bytes++;
len--;
}
*sent = bytes;
return (error);
}
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
/* NB: whatever delay is, it's ignored. */
static int
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
iicbb_read(device_t dev, char *buf, int len, int *read, int last, int delay)
{
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
int bytes = 0;
int err = 0;
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
while (len > 0) {
err = iicbb_readbyte(dev, (len == 1) ? last : 0,
(uint8_t *)buf);
if (err != 0)
break;
buf++;
bytes++;
len--;
}
*read = bytes;
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
return (err);
}
static int
iicbb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
{
int error;
error = IICBB_PRE_XFER(device_get_parent(dev));
if (error)
return (error);
error = iicbus_transfer_gen(dev, msgs, nmsgs);
IICBB_POST_XFER(device_get_parent(dev));
return (error);
}
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
static void
iicbb_set_speed(struct iicbb_softc *sc, u_char speed)
{
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
u_int busfreq;
int period;
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
/*
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
* udelay is half a period, the clock is held high or low for this long.
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
*/
busfreq = IICBUS_GET_FREQUENCY(sc->iicbus, speed);
iicbb: rebuild the bit-banging algorithms using different primitives I2C_SET was quite inflexible, it used too long delays as well as some unnecessary delays. The new building blocks are iicbb_clockin and iicbb_clockout. The former sets SDA and starts the high period of SCL, the latter executes the low period of SCL. What happens during the high phase depends on the operation. For writes we just hold both lines, for reads we poll SDA. S, Sr and P change SDA in the middle of the high period. Also, the calculation of udelay has been updated, so that the resulting period more closely corresponds the requested bus frequency. There is a new knob, io_delay, that allows to further adjust udelay based on the estimated latency of pin toggling operations. Finally, I slightly changed debug tracing and added error indicators to it. The debug prints are compiled in but disabled by default. This can be of use if there is any fallout from this change. Some ideas for further improvements: - add a function for sub-microsecond delays (e.g., in units of 1/10th of a microsecond) and use it for more precise timing of short delays; - account for the actual time spent in the pin I/O. Some sample debug output with the new code follows. Reading temperature and humidity from HTU21 in the bus hold mode: <<w80+ we3+ <w81+ .....r6d+ rac+ r94- >> <<w80+ we5+ <w81+ .............r47+ re2+ r84- >> where '<<' is S, '<' is Sr, '>>' is P, '.' is one millisecond of clock stretching by the slave. Reading temperature and humidity in the no-hold mode: <<w80+ wf3+ >> <<w81- >> <<w81+ r6d+ r54+ raf- >> <<w80+ wf5+ >> <<w81- >> <<w81+ r48+ r4e+ r9c- >> where '+' is Ack and '-' is NoAck. We see that first read attempts are not acknowledged. MFC after: 4 weeks Differential Revision: https://reviews.freebsd.org/D22206
2020-06-11 05:34:31 +00:00
period = 1000000 / 2 / busfreq; /* Hz -> uS */
period -= sc->io_latency;
iicbb: allow longer SCL low timeout and other improvements First, SCL low timeout is set to 25 milliseconds by default as opposed to 1 millisecond before. The new value is based on the SMBus specification. The timeout can be changed on a per bus basis using dev.iicbb.N.scl_low_timeout sysctl. The driver uses DELAY to wait for high SCL up to 1 millisecond, then it switches to pause_sbt(SBT_1MS) for the rest of the timeout. While here I made a number of other changes. 'udelay' that's used for timing clock and data signals is now calculated based on the requested bus frequency (dev.iicbus.N.frequency) instead of being hardcoded to 10 microseconds. The calculations are done in such a fashion that the default bus frequency of 100000 is converted to udelay of 10 us. This is for backward compatibility. The actual frequency will be less than a quarter (I think) of the requested frequency. Also, I added detection of stuck low SCL in a few places. Previously, the code would just carry on after the SCL low timeout and that might potentially lead to misinterpreted bits. Finally, I fixed several style issues near the code that I changed. Many more are still remaining. Tested by accessing HTU21 temperature and humidity sensor in this setup: superio0: <Nuvoton NCT5104D/NCT6102D/NCT6106D (rev. B+)> at port 0x2e-0x2f on isa0 gpio1: <Nuvoton GPIO controller> at GPIO ldn 0x07 on superio0 pcib0: allocated type 4 (0x220-0x226) for rid 0 of gpio1 gpiobus1: <GPIO bus> on gpio1 gpioiic0: <GPIO I2C bit-banging driver> at pins 14-15 on gpiobus1 gpioiic0: SCL pin: 14, SDA pin: 15 iicbb0: <I2C bit-banging driver> on gpioiic0 iicbus0: <Philips I2C bus> on iicbb0 master-only iic0: <I2C generic I/O> on iicbus0 Discussed with: ian, imp MFC after: 3 weeks Differential Revision: https://reviews.freebsd.org/D22109
2019-10-31 11:31:13 +00:00
sc->udelay = MAX(period, 1);
}
DRIVER_MODULE(iicbus, iicbb, iicbus_driver, iicbus_devclass, 0, 0);
MODULE_DEPEND(iicbb, iicbus, IICBUS_MINVER, IICBUS_PREFVER, IICBUS_MAXVER);
MODULE_VERSION(iicbb, IICBB_MODVER);