0

I'm trying to develop a Linux device driver to manage the GPIO registers of a NanoPI Neo card (Allwinner H3).

I'm using a simple approach to understand the GPIO registers behaviour by means the use of only two driver functions: open and ioctl.

My driver implementation, at now, is able to manage a lot of registers such as RTC and CPU-PORT and to read write some other registers.

But I'm having issues in using/managing TWI registers (I2C).

The blocking me issue is that whathever register I read or read after writing any value to the register itself always returns 0x00000000 and nothing seems to happen at pin HW level (PA11/PA12 see below)

I read on the CPU datasheet (see: paragraph 8.1 of the document) the registers value and base address to manage TWI0, base address should be 0x01C2AC00.

I can't find any AllWinner H3 GPIO programmer's reference and I'm not sure if specific operations are required to activate the TWI register functionality. The only operations I have done are to set registers PA11 and PA12 to be the I/O of TWI0_SCK and TWI0_SDA.

Questions:

  • Do you have any news of an "AllWinner H3 GPIO Programmer's Reference"?

  • Do you know which GPIO register I have to set / modify to get TWI enabled or at least give me "signs of life"?


A very strong simplification, aimed at using only TWI0 registers, of the mapping and ioctl functions of the driver I wrote might be the following code, but my code is much more sophisticated and I know it works with many other registers.

#define TWI_IOBASE(n)           (0x01C2AC00 + 0x400*((n)&3))
#define TWI_PAGESIZE            0x400

#define IO_ADDRESS_MSK          0x0000FFFFUL
#define IO_CMD_MSK              0xF0000000UL
#define IO_CMD_READ             0x10000000UL
#define IO_CMD_WRITE            0x20000000UL

static unsigned char * vmaddr;

inline static int drv_init_twi_vm(void)
{
    vmaddr=ioremap(TWI_IOBASE(0), TWI_PAGESIZE);
}

static long drv_ioctl_twi_rw(struct file *file, unsigned int cmd, unsigned long * arg)
{
    long retval = 1;

    unsigned c;

    uint32_t r;

    void *x;

    r=cmd & IO_ADDRESS_MSK;

    x=vmaddr+r;

    c=cmd & IO_CMD_MSK;

    switch(c)
    {
    case IO_CMD_READ:
        if ( copy_to_user((void *)arg, x, 4) ) {
            retval = -EFAULT;
        }
        break;

    case IO_CMD_WRITE:
        *(unsigned long *)x=*arg;
        break;

    default:
        retval=-EFAULT;
        break;
    }

    return retval;
}
Sir Jo Black
  • 2,024
  • 2
  • 15
  • 22
  • I’m not sure I understand the hardware setup. Do you have a host system to which you connected NanoPi via i2c? And you are trying to create exactly what? – 0andriy Jan 09 '22 at 20:37
  • Otherwise if you have a single system that runs Linux, you chose a completely wrong approach, you need to read about GPIO library in the Linux kernel. – 0andriy Jan 09 '22 at 20:39
  • @0andriy, My goal is simply to personally write a driver to manage at least a master I2C connection to any device, at the moment I would be happy to see an I2C signal at the output with a logic analyzer. – Sir Jo Black Jan 10 '22 at 08:03
  • Can you boot with a DT overlay to enable the I2C controller? – Ian Abbott Jan 10 '22 at 15:47
  • @IanAbbott, I don't know about DT overlay! Why might it be necessary? It is the first time that I try to try my hand with an I2C device driver on an ARM and also on Linux. Until now I had written I2C "drivers" on AVRs and other MCUs where it was enough to manipulate ports and at most interrupt vectors. I don't understand what specific needs to be enabled that I can't enable by setting values on ports via device-driver. – Sir Jo Black Jan 10 '22 at 16:48
  • @SirJoBlack Well the device tree for the board should already contain nodes for the three I2C controllers but they are disabled by default. Some boot loaders (such as U-Boot) allow the default device tree contents to be modified by loading a device tree overlay file before it launches the Linux kernel. According to [this table](https://linux-sunxi.org/Linux_mainlining_effort), the mainline Linux kernel has had drivers for the H3's I2C controllers since kernel version 4.9. – Ian Abbott Jan 10 '22 at 17:11
  • @SirJoBlack Not sure if this is relevant to your system: https://docs.armbian.com/User-Guide_Allwinner_overlays/ . If so, it may be as simple as adding `i2c0` to the `overlays=` line of `/boot/armbianEnv.txt`. – Ian Abbott Jan 10 '22 at 17:19
  • @IanAbbott, Does this mean that there is no way to write to some register or execute particular code for which it is possible to by-pass this disablement and enable the use of TWI registers via device-driver? Thank you for the information, I'll try to study the links you sent me and that I found about it. – Sir Jo Black Jan 10 '22 at 17:23
  • 2
    @SirJoBlack Possibly, but you would also need to access the pin controller to configure the pins for use by the I2C controller. Also, the way you are reading and writing registers is not correct. You should be using the `readl()` and `writel()` functions (or alternatively, the `ioread32()` and `iowrite32()` functions) to access remapped MMIO registers. – Ian Abbott Jan 10 '22 at 17:46
  • @IanAbbott, Thanks, I've already seen the use of writel and readl into the source of some driver to manage SPI e TWI, I'll try to use them ;) – Sir Jo Black Jan 10 '22 at 18:10
  • 1
    It won’t help you, although will make code correct from general point of view. ARM32 will use the same stores and loads (like on x86), if you check the generated code. – 0andriy Jan 10 '22 at 22:32
  • What @IanAbbot is right about is the pin control. Hardware engineers often do not understand the difference between GPIO snd pin control. This is discussed in the documentation: https://www.kernel.org/doc/html/latest/driver-api/pin-control.html. – 0andriy Jan 10 '22 at 22:33
  • @IanAbbot, I do not see any driver for I2C for H3 in v5.16, maybe it was dropped? Or maybe i’m missing something? – 0andriy Jan 10 '22 at 22:49
  • I see pinctrl and GPIO driver is in the upstream, though. – 0andriy Jan 10 '22 at 22:55
  • @0andriy The I2C driver for H3 is "i2c-mv64xxx" ("drivers/i2c/busses/i2c-mv64xxx.c"), `CONFIG_I2C_MV64XXX`. Device tree stuff: "arch/arm/boot/dts/sunxi-h3-h5.dtsi", `compatible = "allwinner,sun6i-a31-i2c";`. – Ian Abbott Jan 11 '22 at 10:07

0 Answers0