0

Right now I am looking to use the Cadence SPI driver to perform some basic reading and writing on Linux. I have just used the I2C driver, but I am still a little confused as to how all of these drivers fit together and if there is a general interface to which they all conform.

Here is the code that I wrote to use the I2C driver

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>

#include <errno.h>
#include <string.h>

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>

#define I2C_ADAPTER                 "/dev/i2c-0"
#define I2C_SWITHC_MUX_ADDRESS      0x74
#define DEVICE_ADDRESS              0x54


int main (int argc, char *argv[])
{
    int file;

    uint8_t reg, value;

    char *end;

    /* Take a value to write */

    printf("The device address on the bus: %d", DEVICE_ADDRESS);

    if(argc == 2) {
        value = strtol(argv[1], &end, 16);
        printf("value to write is: %d\n", value);
    }
    else {
        printf("arg failed\n\n.");
    }


    if((file = open(I2C_ADAPTER, O_RDWR)) < 0) {
        printf("Failed to open the bus\n");
        return -1;
    }

    if(ioctl(file, I2C_SLAVE_FORCE, I2C_SWITHC_MUX_ADDRESS) < 0) {
        printf("Unable to open device as slave \n%s\n", strerror(errno));
        return -1;
    }

    char buf[10];

    reg = DEVICE_ADDRESS;

    buf[0] = reg;
    buf[1] = value;

    if(write(file, buf, 2) != 2) {
        printf("Failed to write to bus %s.\n\n", strerror(errno));
    }
    else {
        printf("Successful write\n");
        printf(buf);
        printf("\n\n");
    }

    if(read(file, buf, 1) != 1) {
        printf("Failed to read from the i2c bus.\n %s\n\n", strerror(errno));
    }
    else {
        printf("Successful read\n");
        printf("Buf = [%02u]\n", buf[0]);
        printf("\n\n");
    }

    return 0;
}

I am confused as to where the driver is actually invoked. Now I am reading the SPI driver docs here:

https://www.kernel.org/doc/html/v4.11/driver-api/spi.html

The corresponding docs for the I2C driver are here:

https://www.kernel.org/doc/html/v4.11/driver-api/i2c.html

The very first struct defined in the I2C documentation is i2c_driver. I do not think when I wrote my I2C program, that I used any of the structs defined in the I2C driver document. Did I ever actually use the I2C Cadence driver? If not, how can my program be rewritten to use the I2C Cadence driver so I can get an idea of how to use drivers in the user space so I can use the SPI driver.

John Frye
  • 255
  • 6
  • 22
  • You used the I2C Cadance driver indirectly via the "i2c-dev" driver. That driver gets notified whenever an I2C adapter (I2C master bus controller) is added or removed from the system, and creates or destroys the "/dev/i2c-n" devices in response. This allows a userspace application to send messages to any slave on the I2C bus (with the potential to confuse any slave devices bound to a device driver). – Ian Abbott Nov 06 '17 at 17:44
  • With SPI, you can use the "spidev" driver to access an SPI slave device from userspace, but unlike I2C, the SPI slave device needs to be defined in the system (e.g. in the device tree or board config code) and bound to the "spidev" driver. – Ian Abbott Nov 06 '17 at 17:46
  • I know how to add it to the device tree. But how do I bind it to the driver? I would like to add an eeprom. – John Frye Nov 06 '17 at 18:03
  • and what are the these "dev" drivers? are they legitimate or should I be going through some other protocol to access the system? – John Frye Nov 06 '17 at 18:04
  • lastly when you use drivers in user space, is the normal flow to 1. define hardware-related macros 2. file drivers structs with macros 3. call driver routines to act on hardware? – John Frye Nov 06 '17 at 18:08
  • "spidev" is probably not the best choice for accessing an eeprom. For a simple (small) eeprom (not a flash memory chip), the [at25](https://www.kernel.org/doc/Documentation/devicetree/bindings/eeprom/at25.txt) driver is probably a better fit. For a flash memory chip, the [spi-nor](https://www.kernel.org/doc/Documentation/devicetree/bindings/mtd/jedec%2Cspi-nor.txt) driver is probably a better fit, unless you actually have the Cadence QSPI Flash Controller which uses the [cadence-quadspi](https://www.kernel.org/doc/Documentation/devicetree/bindings/mtd/cadence-quadspi.txt) driver. – Ian Abbott Nov 06 '17 at 18:37
  • thanks. im just trying to build the most basic application at the moment. I am emulating it on QEMU so I need device in my device tree that have QEMU model counterparts, like EEPROM. My next question is how do you know what driver you should choose? Does Linux have a common repo or do device vendors generally write their own open-source drivers. How would I find out which driver would be best for my app? is there a common source for all Linux driver docs? – John Frye Nov 06 '17 at 18:40
  • Well I see QEMU has emulation for m25p80-style SPI flash memory chips, and the "spi-nor" driver should work with those. – Ian Abbott Nov 06 '17 at 18:53

2 Answers2

0

put my two cents in, you can alternatively used i2c-tools utility. which makes very easy to deal with i2c devices with linux. reference how to use it . https://elinux.org/Interfacing_with_I2C_Devices

Devidas
  • 2,479
  • 9
  • 24
  • for my actual implementation, that is exactly what I did. Just wasn't sure how it would scale and if it was the preferred way – John Frye Nov 08 '17 at 12:54
  • one clarification. the code you provided is user application (with main function). kernel driver code will create. i2c-* device node in /dev. now you have two option. use i2c-* from user space like which you have written. and cross-compile and start when intialising (write some init script). or write kernel driver check some rtc driver to do same ask from kernel. decide for yourself what you want to do ? – Devidas Nov 08 '17 at 13:40
  • also take a look @ this blog http://opensourceforu.com/2015/01/writing-i2c-clients-in-linux/ – Devidas Nov 08 '17 at 13:49
0

This is how call stack look like from userspace via kernel I2C framework to Cadence I2C driver (similar for read()/write()):

  1. Userspace driver ioctl() calls compat_i2cdev_ioctl() in i2c-dev.c
  2. i2cdev_ioctl_rdwr() calls i2c_transfer() in i2c-core-case.c
  3. __i2c_transfer() calls adap->algo->master_xfer()

And this function pointer .master_xfer is initialized with callback defined in i2c-cadence.c when you register I2C driver, in probe() function:

id->adap.algo = &cdns_i2c_algo;

struct i2c_algorithm cdns_i2c_algo = {
.master_xfer    = cdns_i2c_master_xfer,

Hope it helps.

Dražen G.
  • 358
  • 3
  • 10