I did some further testing with interrupts and set the BME280 device to use
interrupts on setup.  This occurs in the attach phase on system boot.  It
did not work even after I added an early start to the ampintc driver:

sxipio0 at simplebus0: 94 pins
ampintc0 at simplebus0 nirq 160, ncpu 4
sxipio1 at simplebus0: 12 pins

I suspect that interrupts are not set up on system boot so I went back to
the BME280 code that does device setup non-interrupt and cyclic read with
interrupt.

I am not happy to rip out the interrupt code that I have tested and works on
the H3 device.  I looked at the V40 docs and they seem to be identical to
the H3 for the TWI.  If the code does not meet openBSD standards I am quite
willing to work to get it right.  The code I provided also works in
non-interrupt mode.

Could we work together on the interrupt code and get the whole driver
working properly?  It should not be a major effort.

As I mentioned above there is an issue trying to use interrupts on system
boot.

-----Original Message-----
From: owner-...@openbsd.org [mailto:owner-...@openbsd.org] On Behalf Of
Stephen Graf
Sent: Sunday, December 31, 2017 11:57 AM
To: 'Mark Kettenis' <mark.kette...@xs4all.nl>
Cc: arm@openbsd.org; artturi....@gmail.com
Subject: Re: sxitwi on orangepi one (H3) re-visited

Thank you for looking at my code. I will try your code on my orange pi one
in the near future.

Regarding the wait for bus free a on stop condition: After a stop, the only
thing that can be done is a start.  When the start bit is set the controller
"will transmit a START condition on the bus when the bus is free".  So the
wait is done by the hardware in the start routine.  It is necessary for the
start hardware to wait for a free bus as it could be a different independent
master waiting to grab the bus. Have you tested without any stop delay?  It
works, as it should, on my system.

Regarding the bus reset:  When (not if!) an error occurs, it is important to
reset to a known default state.  This incudes not only the hardware but also
flags such as the started flag.  In my testing, I had an issue with the
power lead to the sensor on my breadboard being intermittent.  Without the
reset the sensor would often permanently quit on an error.  With the reset
it would work again on the next read.
My programming experience dates back to writing assembler code on mini
computers to control monstrous paper making machines. Things would often go
wrong and sending an error message to a remote console was never an
acceptable solution.

Regarding the bus clock speed:  Ideally this should be as parameter set
outside of the driver on system startup.  Different applications require
different bus speeds and one bus should be able to run at a different speed
than another. It should not be necessary to recompile the kernel to change
the bus speed.

Does the existing driver work with interrupts on any of your hardware?

-----Original Message-----
From: Mark Kettenis [mailto:mark.kette...@xs4all.nl]
Sent: Sunday, December 31, 2017 4:06 AM
To: s_g...@telus.net
Cc: arm@openbsd.org; mark.kette...@xs4all.nl; artturi....@gmail.com
Subject: Re: sxitwi on orangepi one (H3) re-visited

> From: "Stephen Graf" <s_g...@telus.net>
> Date: Thu, 14 Dec 2017 12:54:02 -0800
> 
> My recent experience with the dwxe driver has emboldened me to look at 
> the sxitwi driver again.  A few months ago I worked with Artturi Alm 
> to get the driver working with a driver he wrote for a BME280 sensor.
> At that time I tested the non-interrupt option (I2C_F_POLL) only.  
> This time I tried the interrupt option as I thought it would be more 
> appropriate in a production situation.  The non-interrupt option can 
> tie up the system for periods of time.
> 
> My environment is an orange pi one (Allwinner H3) with one BME280 
> sensor on i2c bus 0 and another on bus 1.  Most of my testing was done 
> with a scope on the SDA and SCL lines of bus 0 and printf statements 
> in the drivers.  The
> BME280 driver does a lot of device calibration reading and setup in 
> the attach phase and then reads data once a second into sysctl hw.sensors.
> 
> My first issue is that the dtb has the i2c busses disabled.  So I had 
> to dtc the dtb, enable the busses 0 and 1 and add BME280 sections to 
> the i2c busses.  The dtb provides a third i2c bus, but the orange pi 
> one does not bring it out to the header and so it makes no sense to 
> enable
it.
> 
> A serious issue that hindered my testing is that the printf statements 
> in the driver affected the console driver, garbling the output and 
> often stopping all output and input. The output always appeared 
> correctly in the message log file.  Even now it seems that the console 
> is affected when the sxitwi driver is in use and there is a lot of 
> other
console output.
> 
> Another item I noticed is that the i2c busses were running at half the 
> standard speed of 100kHz.  The comments in the driver code, which are 
> taken right out of the Allwinner doc, set the speed for a 48MHz master
clock.
> However, the orange pi one runs at 24MHz.
> 
> The sxitwi driver was sprinkled with delays.  I took most of them out 
> without any side effects.
> 
> I added a bus reset function to help in error recovery.
> 
> In my testing I left the BME280 driver to do device initialization in 
> non-interrupt mode and only changed the cyclic data read to use
interrupts.
> This made testing easier as it is hard to capture something on a scope 
> that happens only on system boot.  Also I am not clear if interrupts 
> are working at system boot.  It seems that the interrupt controller is 
> set up after the sxitwi controller.
> 
> It looks like the device status can change after the interrupt is 
> released in the interrupt service routine, sxitwi_intr.  Therefore I 
> saved the status for later use when the driver wakes up in 
> sxitwi_wait. To make that work I separated the interrupt and 
> non-interrupt parts of sxitwi_wait and added some recovery code.
> 
> I took out a lot of the sxitwi_send_stop code because as the comment 
> says "Interrupt is not generated" and there is no need to do anything.
> The next send start will wait, or for that matter another master on 
> the bus may be trying to do a start and will wait for the stop to
complete.
> 
> I sort of messed up the formatting (tabs spaces?) with all my changes 
> and I hesitate do show a diff.  The working code for sxitwi and BNE280 
> drivers is attached.  Is the sxitwi driver used by any other devices 
> other than Allwinner?  Is anyone interested in testing my code?

Hi Stephen,

The sxitwi(4) driver indeed doesn't work very well on the newer SoCs.
But it works fine on my Allwinner A20 board.

As I now have a Banana Pi M2 Berry with an AXPI221a PMIC connected over I2C
I made some time to look into the issue.  There are some things in your diff
that didn't make a lot of sense, but while trying to fix them in a better
way, I only managed to make things worse it seems.  So I've taken the
radical approach to disable interrupt mode completely for now.  Together
with the removal of the the excessive delays that seems to make things much
better on both the A20 and the
V40 boards that periodically read the PMIC registers to update sensor
values.  My plan is to commit the diff below unless some other developer
objects to it and then see if I can properly fix interrupt mode.

Regarding you conclusions and questions above:

Yes, I came to the conclusion that the bus is running at half the speed as
well.  The proper solution is to calculate the dividers based on the input
frequency instead of hardcoding them.  The input frequency can be found by
calling clock_get_frequency().  I might need to add a few more clocks to
sxiccmu(4) first for this to work, so I'll address that in a future diff.
Running the I2C bus at 50 kHz is probably fine for most I2C devices.

We do need to make sure the bus is idle after sending a STOP before starting
another transfer.  So your change twsi_send_stop() is not ok.

I'm not sure under what circumstances a bus reset would help.  Linux doesn't
seem to implement one.  So I left that bit out for now.

I think it would be ok to add the BME280 driver to the tree, but it needs
some cleanup first.  Normal code should not use symbols that start with an
underscore!  I also don't think we should document registers in the source
code when documentation is publically available.

Cheers,

Mark


Index: dev/fdt/sxitwi.c
===================================================================
RCS file: /cvs/src/sys/dev/fdt/sxitwi.c,v retrieving revision 1.5 diff -u -p
-r1.5 sxitwi.c
--- dev/fdt/sxitwi.c    30 Dec 2017 19:04:00 -0000      1.5
+++ dev/fdt/sxitwi.c    31 Dec 2017 11:36:32 -0000
@@ -128,12 +128,6 @@
 
 #define        SOFTRESET_VAL           0               /* reset value */
 
-#define        TWSI_RETRY_COUNT        1000            /* retry loop count
*/
-#define        TWSI_RETRY_DELAY        1               /* retry delay */
-#define        TWSI_STAT_DELAY         1               /* poll status delay
*/
-#define        TWSI_READ_DELAY         2               /* read delay */
-#define        TWSI_WRITE_DELAY        2               /* write delay */
-
 struct sxitwi_softc {
        struct device            sc_dev;
        bus_space_tag_t          sc_iot;
@@ -291,21 +285,13 @@ sxitwi_bus_scan(struct device *self, str  u_int
sxitwi_read_4(struct sxitwi_softc *sc, u_int reg)  {
-       u_int val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg);
-
-       delay(TWSI_READ_DELAY);
-
-       return val;
+       return bus_space_read_4(sc->sc_iot, sc->sc_ioh, reg);
 }
 
 void
 sxitwi_write_4(struct sxitwi_softc *sc, u_int reg, u_int val)  {
        bus_space_write_4(sc->sc_iot, sc->sc_ioh, reg, val);
-
-       delay(TWSI_WRITE_DELAY);
-
-       return;
 }
 
 int
@@ -317,7 +303,6 @@ sxitwi_intr(void *arg)
        val = sxitwi_read_4(sc, TWSI_CONTROL);
        if (val & CONTROL_IFLG) {
                sxitwi_write_4(sc, TWSI_CONTROL, val & ~CONTROL_INTEN);
-               wakeup(&sc->sc_dev);
                return 1;
        }
        return 0;
@@ -364,19 +349,19 @@ int
 sxitwi_send_stop(void *v, int flags)
 {
        struct sxitwi_softc *sc = v;
-       int retry = TWSI_RETRY_COUNT;
+       int timo;
 
        sc->sc_started = 0;
 
        /* Interrupt is not generated for STAT_NRS. */
        sxitwi_write_4(sc, TWSI_CONTROL, CONTROL_STOP | sc->sc_twsien_iflg);
-       while (--retry > 0) {
+       for (timo = 100; timo > 0; timo--) {
                if (sxitwi_read_4(sc, TWSI_STATUS) == STAT_NRS)
                        return 0;
-               delay(TWSI_STAT_DELAY);
+               delay(1);
        }
 
-       return -1;
+       return ETIMEDOUT;
 }
 
 int
@@ -458,30 +443,21 @@ int
 sxitwi_wait(struct sxitwi_softc *sc, u_int control, u_int expect, int
flags)  {
        u_int status;
-       int timo, error = 0;
+       int timo;
 
-       delay(5);
-       if (!(flags & I2C_F_POLL))
-               control |= CONTROL_INTEN;
        sxitwi_write_4(sc, TWSI_CONTROL, control | sc->sc_twsien_iflg);
 
-       timo = 0;
-       do {
+       for (timo = 10000; timo > 0; timo--) {
                control = sxitwi_read_4(sc, TWSI_CONTROL);
                if (control & CONTROL_IFLG)
                        break;
-               if (flags & I2C_F_POLL)
-                       delay(TWSI_RETRY_DELAY);
-               else {
-                       error = tsleep(&sc->sc_dev, PWAIT, "sxitwi", 100);
-                       if (error)
-                               return error;
-               }
-       } while (++timo < 1000000);
+               delay(1);
+       }
+       if (timo == 0)
+               return ETIMEDOUT;
 
        status = sxitwi_read_4(sc, TWSI_STATUS);
        if (status != expect)
                return EIO;
-
-       return error;
+       return 0;
 }



Reply via email to