0
votes

I'm writing a custom IR transmitter driver for lirc on an embedded board. The board has a i2c-to-gpio extender (FXL6408).

The problem is that only one GPIO pin is needed for my driver (and hence LIRC) and the other pins are needed for use by other applications. Something like this:

Driver Connections

I've read LWM, LDD3, and tons of sites about i2c-slave, i2c adaptors, buses, pinctrl, gpio, stacking etc but its not clear how to do what I want:

my-driver needs to control a single pin on the GPIO extender while still allowing other applications to control the other 7 pins via /dev/i2c-0.

Following this SO suggestion was promising but i2c_new_dummy failed, returning NULL:

i2cAdaptor = i2c_get_adapter(ECP_I2CBUS); // 1 means i2c-1 bus
if (i2cAdaptor == NULL)
{
    printk("ecp_gpio: Could not acquire i2c adaptor\n");
    return -EBUSY;
}

i2cClient  = i2c_new_dummy(i2cAdaptor, ECP_I2CDEV); // 0x43 - slave address on i2c bus
if (i2cClient == NULL)
{
    printk("ecp_gpio: Could not acquire i2c client\n");
    return -EBUSY;
}

if ( (rc = i2c_smbus_write_byte(i2cClient, 0xF0)) < 0)
{
    printk("ecp_gpio: Error writing byte - error %d", rc);
    return -EIO;
}

What is the correct way to hook up the plumbing to achieve what I want?

OS Info:

# uname -a
Linux ecp 4.4.127-1.el6.elrepo.i686 #1 SMP Sun Apr 8 09:44:43 EDT 2018 i686 i686 i386 GNU/Linux
1
In Linux kernel everything is delegated thru frameworks. In your case there is a driver for GPIO expander, that provides resources to the system thru GPIO library. In the other driver you just call something like gpiod_get(dev, “pin_name”);, where name of the pin comes thru device properties (they may be gathered in Device Tree, ACPI, or in GPIO tables or software nodes).0andriy
True. But since the GPIO device is at the end of an i2c bus what my driver must do is communicate over i2c. Any existing gpio driver will not be aware of the GPIO device, so the task at hand is to control i2c, hence using the i2c framework to control i2c, no?Danny
No. What you need is to investigate if vanilla kernel has a driver for this expander (or quite similar one which you can modify), or write a new one (plenty examples) and use what I put in the first comment.0andriy
Just in case there is a common library for such chips, called bgpio in drivers/gpio/gpio-mmio.c.0andriy
Thanks, I'll check it out. Unfortunately, I did some testing on the (down voted) solution using i2c_smbux_xfer. It's too slow. HW must be using the 100 kHz i2c bus. So back to to the other approach, "Control USART RTS Pin on Embedded Board". You commented on that question too. Any leads there? I'm stuck...Danny

1 Answers

-1
votes

After trying many different things, I found one that works. I don't know if it is "the right way" to do it, but it does work.

Instead of trying to create a dummy client, just call the i2c_xx functions directly. So the code looks like:

i2cAdaptor = i2c_get_adapter(ECP_I2CBUS); // 1 means i2c-1 bus etc
if (i2cAdaptor == NULL)
{
    printk("ecp_gpio: Could not acquire i2c adaptor\n");
    return -EBUSY;
}


union i2c_smbus_data data;

data.byte = 0xF0;

if ((rc = i2c_smbus_xfer(ecpHardware.i2cAdaptor, ECP_I2CDEV, 0, I2C_SMBUS_WRITE, 0x05, I2C_SMBUS_BYTE_DATA, &data)) < 0)
{
    printk("ecp_gpio: i2c_smbus_xfer failed: %d\n", rc);
    return -EIO;
}

data.byte = 0xE0;

if ((rc = i2c_smbus_xfer(ecpHardware.i2cAdaptor, ECP_I2CDEV, 0, I2C_SMBUS_WRITE, 0x05, I2C_SMBUS_BYTE_DATA, &data)) < 0)
{
    printk("ecp_gpio: i2c_smbus_xfer failed: %d\n", rc);
    return -EIO;
}