This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

How to make GPIO expander and LEDs works (AM335x evm)

Other Parts Discussed in Thread: TCA6408A, TCA6408

Good morning,

I'm working on the LEDs and Gpio expander (TCA6408A) from the AM335x EVM. I was surprised not to find anything in the folder /sys/cladd/leds/. Then, I tried to export the gpio in the directory /sys/class/gpio to turn ON/OFF the leds but the new gpio folder is not created. I thought that it was because the drivers were not in the kernel, so I added (besides the standard ti menuconfig) in  my kernel driver tree:

Device drivers / GPIO support / PCA953x, PCA955x, TCA64xx, and MAX7310 I/O ports

Device drivers / LED support / LED Support for PCA955x I2C chips

I still can't export a gpio from the gpio expander and I can't adjust the brightness of the LED using /sys/class/leds because this directory is empty.

However, I can make it work by writing directly into the register (via i2c) of the TCA6408A.

Is there anyone that was able to make it work?

Thank you for your suggestions.

  • I'll bump this as I'm also stuck with this  right now.

    BTW, Laurent how did you make it work through I2C register?

  • Hi,

    Regarding the issue of exporting the GPIO, I kind of figured that out by tracing with Jtag. When the function probe is executed (driver gpio_pca953x), there is a call to the function pca953x_get_alt_pdata. Here is a sample of the code

    #ifdef CONFIG_OF_GPIO

    /*

    * Translate OpenFirmware node properties into platform_data

    * WARNING: This is DEPRECATED and will be removed eventually!

    */

    static void

    pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, u32 *invert)

    {

            struct device_node *node;

            const __be32 *val;

            int size;

            node = client->dev.of_node;

            if (node == NULL)

                   return;

            *gpio_base = -1;

    ….

    }

    #else

    static void

    pca953x_get_alt_pdata(struct i2c_client *client, int *gpio_base, u32 *invert)

    {

            *gpio_base = -1;

    }

    #endif

    First of all the there is a condition on the choice of which function is going to be executed according to the define variable "CONFIG_OF_GPIO". In both case what we are interested in is *gpio_base = -1 because this is going to define the next gpio number available (instead of defining a fixed number the -1 tell the driver to pick the next gpio number available).

    However in my case and probably yours as well, the first function is executed (which is DEPRECATED by the way) where there is a condition that is not met

    if (node == NULL) {return}, that force the driver to exit without executing *gpio_base = -1 and then throw an error. I forced the driver to execute this line and it works just fine.

    So now, I have two questions for people who might know:

    Why is "CONFIG_OF_GPIO" set?

    Why "node == NULL" ?


    Regarding the I2c register, yes I was able to make it work. Please check this discussion I had with rfc2217, it will probably be helpful making the I2c work.

    http://e2e.ti.com/support/dsp/sitara_arm174_microprocessors/f/791/t/218439.aspx?pi69149=1

    Read the documentation of the tca6408 to understand the register. Here are few lines of code (Qt) to turn a LED ON (LED D1 on am335x evm)

    void MainWindow::on_pushButton_clicked()

    {
        QFile file("/dev/i2c-2");
        int r = file.open(QIODevice::WriteOnly | QIODevice::Text);
        if (r < 0)
        {
            qDebug()<<"Failed to open file i2c-0";
            return;
        }
        if(ioctl(file.handle(), I2C_SLAVE, 0x20) < 0)
        {
            qDebug()<<"Failed to set slave address";
            return;
        }
        if(i2c_smbus_write_byte_data(file.handle(),0x03, 0x00) < 0)
        {
            qDebug()<<"Failed to write Configuration Register";
            return;
        }
        if(i2c_smbus_write_byte_data(file.handle(),0x01, 0x10) < 0)
        {
            qDebug()<<"Failed to write Register Output";
            return;
        }
    }

    Hope this help.



  • Hi I have asked the same questions, but after spending hours on internet I figured out how to control leds. I have controlled them directly with QT. Hope this helps.

    #include <linux/i2c-dev.h>

    #include <sys/ioctl.h>

     

           if ((file = open("/dev/i2c-2",O_RDWR)) < 0) {            exit(1);            }                // there are 2 TCA6408A

           if ((fileb = open("/dev/i2c-2",O_RDWR)) < 0) {            exit(1);            }              // other half of them

     

           int addr = 0x20;        if (ioctl(file, I2C_SLAVE, addr) < 0) {            exit(1);        }   // connect over I2C

            addr = 0x21;        if (ioctl(fileb, I2C_SLAVE, addr) < 0) {            exit(1);        }

          buf[0]=0x03;                                                    // the TCA6408A connected 4 bit input (on expansion) 4 bit output leds

            buf[1]=0x0F;                                                  // first 4 bits input, second 4 bits output,  0x03 means control register

            if (write(file,buf,2) != 2) {            exit(2);        }            // initializing the chips

            if (write(fileb,buf,2) != 2) {            exit(2);        }

     

           buf[0]=0x01;

            buf[1]=0x0;

            if (write(file,buf,2) != 2) {            exit(3);        }               //switch off all leds.

            if (write(fileb,buf,2) != 2) {            exit(3);        }

     

    // to turn on led1

           buf[0]=0x01;        buf[1]=0x10;        if (write(file,buf,2) != 2) {            exit(3);        }        if (write(fileb,buf,2) != 2) {            exit(3);        }

    // to turn on all leds

           buf[0]=0x01;        buf[1]=0xF0;        if (write(file,buf,2) != 2) {            exit(3);        }        if (write(fileb,buf,2) != 2) {            exit(3);        }